man this tutorial level is really screwed up

todo??:

- fix the id system [days since the universe was created is NOT a valid id]
- fix literally everything else although thats probably caused by the discrepancy above
This commit is contained in:
Fireblade 2026-05-02 20:07:41 -04:00
parent d89fadff68
commit 02f1be3bb7
23 changed files with 217 additions and 70 deletions

View file

@ -223,7 +223,7 @@ wstring AbstractTexturePack::getAnimationString(const wstring &textureName, cons
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));
// app.DebugPrintf("AbstractTexturePack::getImageResource - %s, drive is %s\n",pchTexture, wstringtofilename(drive));
return new BufferedImage(TexturePack::getResource(L"/" + File),filenameHasExtension,bTitleUpdateTexture,drive);
}

View file

@ -163,7 +163,7 @@ BufferedImage::BufferedImage(const wstring& File, bool filenameHasExtension /*=f
const char *pchTextureName=wstringtofilename(name);
#ifndef _CONTENT_PACKAGE
app.DebugPrintf("\n--- Loading TEXTURE - %s\n\n",pchTextureName);
// app.DebugPrintf("\n--- Loading TEXTURE - %s\n\n",pchTextureName);
#endif
D3DXIMAGE_INFO ImageInfo;

View file

@ -181,6 +181,7 @@ void Chunk::makeCopyForRebuild(Chunk *source)
void Chunk::rebuild()
{
if (this == nullptr) return;
PIXBeginNamedEvent(0,"Rebuilding chunk %d, %d, %d", x, y, z);
#if defined __PS3__ && !defined DISABLE_SPU_CODE
rebuild_SPU();
@ -404,6 +405,10 @@ void Chunk::rebuild()
}
Tile *tile = Tile::tiles[tileId];
if (tile == nullptr)
{
continue;
}
if (currentLayer == 0 && tile->isEntityTile())
{
shared_ptr<TileEntity> et = region->getTileEntity(x, y, z);

View file

@ -40,6 +40,9 @@ bool ChoiceTask::isCompleted()
return false;
int xboxPad = pMinecraft->player->GetXboxPad();
int tutorialPad = tutorial->getPad();
bool hasValidPad = (tutorialPad >= 0 && tutorialPad < XUSER_MAX_COUNT);
bool menuDisplayed = hasValidPad && ui.GetMenuDisplayed(tutorialPad);
if( m_bConfirmMappingComplete || m_bCancelMappingComplete )
{
@ -48,14 +51,10 @@ bool ChoiceTask::isCompleted()
return true;
}
if(ui.GetMenuDisplayed(tutorial->getPad()))
{
// If a menu is displayed, then we use the handleUIInput to complete the task
}
else
if(!menuDisplayed)
{
// 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 (hasValidPad && pMinecraft->localplayers[tutorialPad] != nullptr && pMinecraft->localplayers[tutorialPad]->isUnderLiquid(Material::water)) return false;
#ifdef _WINDOWS64
if (!m_bConfirmMappingComplete &&
(InputManager.GetValue(xboxPad, m_iConfirmMapping) > 0
@ -85,8 +84,9 @@ bool ChoiceTask::isCompleted()
sendTelemetry();
enableConstraints(false, true);
}
return m_bConfirmMappingComplete || m_bCancelMappingComplete;
}
return m_bConfirmMappingComplete || m_bCancelMappingComplete;
}
eTutorial_CompletionAction ChoiceTask::getCompletionAction()

View file

@ -40,11 +40,13 @@ bool InfoTask::isCompleted()
bool bAllComplete = true;
Minecraft *pMinecraft = Minecraft::GetInstance();
int tutorialPad = tutorial->getPad();
bool hasValidPad = (tutorialPad >= 0 && tutorialPad < XUSER_MAX_COUNT);
// 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( hasValidPad && pMinecraft->localplayers[tutorialPad] != nullptr && pMinecraft->localplayers[tutorialPad]->isUnderLiquid(Material::water) ) return false;
if(ui.GetMenuDisplayed(tutorial->getPad()))
if(hasValidPad && ui.GetMenuDisplayed(tutorialPad))
{
// If a menu is displayed, then we use the handleUIInput to complete the task
bAllComplete = true;

View file

@ -118,10 +118,12 @@ bool TutorialHint::onLookAtEntity(eINSTANCEOF type)
int TutorialHint::tick()
{
int returnVal = -1;
int tutorialPad = m_tutorial->getPad();
bool hasValidPad = (tutorialPad >= 0 && tutorialPad < XUSER_MAX_COUNT);
switch(m_type)
{
case e_Hint_SwimUp:
if( Minecraft::GetInstance()->localplayers[m_tutorial->getPad()]->isUnderLiquid(Material::water) ) returnVal = m_descriptionId;
if( hasValidPad && Minecraft::GetInstance()->localplayers[tutorialPad] != nullptr && Minecraft::GetInstance()->localplayers[tutorialPad]->isUnderLiquid(Material::water) ) returnVal = m_descriptionId;
break;
}
return returnVal;

View file

@ -76,10 +76,11 @@ bool IUIScene_TradingMenu::handleKeyDown(int iPad, int iAction, bool bRepeat)
// Do we have the ingredients?
shared_ptr<ItemInstance> buyAItem = activeRecipe->getBuyAItem();
shared_ptr<ItemInstance> buyBItem = activeRecipe->getBuyBItem();
shared_ptr<ItemInstance> sellItem = activeRecipe->getSellItem();
shared_ptr<MultiplayerLocalPlayer> player = Minecraft::GetInstance()->localplayers[getPad()];
int buyAMatches = player->inventory->countMatches(buyAItem);
int buyBMatches = player->inventory->countMatches(buyBItem);
if( (buyAItem != nullptr && buyAMatches >= buyAItem->count) && (buyBItem == nullptr || buyBMatches >= buyBItem->count) )
if( sellItem != nullptr && (buyAItem != nullptr && buyAMatches >= buyAItem->count) && (buyBItem == nullptr || buyBMatches >= buyBItem->count) )
{
// 4J-JEV: Fix for PS4 #7111: [PATCH 1.12] Trading Librarian villagers for multiple <20>Enchanted Books<6B> will cause the title to crash.
int actualShopItem = m_activeOffers.at(selectedShopItem).second;
@ -91,7 +92,7 @@ bool IUIScene_TradingMenu::handleKeyDown(int iPad, int iAction, bool bRepeat)
player->inventory->removeResources(buyBItem);
// Add the item we have purchased
shared_ptr<ItemInstance> result = activeRecipe->getSellItem()->copy();
shared_ptr<ItemInstance> result = sellItem->copy();
if(!player->inventory->add( result ) )
{
player->drop(result);
@ -238,6 +239,7 @@ void IUIScene_TradingMenu::updateDisplay()
if( selectedShopItem < m_activeOffers.size() )
{
MerchantRecipe *activeRecipe = m_activeOffers.at(selectedShopItem).first;
shared_ptr<ItemInstance> sellItem = activeRecipe ? activeRecipe->getSellItem() : nullptr;
wstring wsTemp;
@ -245,11 +247,11 @@ void IUIScene_TradingMenu::updateDisplay()
wsTemp = app.GetString(IDS_VILLAGER_OFFERS_ITEM);
wsTemp = replaceAll(wsTemp,L"{*VILLAGER_TYPE*}",m_merchant->getDisplayName());
size_t iPos=wsTemp.find(L"%s");
wsTemp.replace(iPos,2,activeRecipe->getSellItem()->getHoverName());
wsTemp.replace(iPos,2,sellItem != nullptr ? sellItem->getHoverName() : L"");
setTitle(wsTemp.c_str());
vector<HtmlString> *offerDescription = GetItemDescription(activeRecipe->getSellItem());
vector<HtmlString> *offerDescription = GetItemDescription(sellItem);
setOfferDescription(offerDescription);
shared_ptr<ItemInstance> buyAItem = activeRecipe->getBuyAItem();
@ -270,8 +272,8 @@ void IUIScene_TradingMenu::updateDisplay()
int buyAMatches = player->inventory->countMatches(buyAItem);
if(buyAMatches > 0)
{
setRequest1RedBox(buyAMatches < buyAItem->count);
canMake = buyAMatches > buyAItem->count;
setRequest1RedBox(buyAItem == nullptr || buyAMatches < buyAItem->count);
canMake = buyAItem != nullptr && buyAMatches > buyAItem->count;
}
else
{
@ -282,8 +284,8 @@ void IUIScene_TradingMenu::updateDisplay()
int buyBMatches = player->inventory->countMatches(buyBItem);
if(buyBMatches > 0)
{
setRequest2RedBox(buyBMatches < buyBItem->count);
canMake = canMake && buyBMatches > buyBItem->count;
setRequest2RedBox(buyBItem == nullptr || buyBMatches < buyBItem->count);
canMake = canMake && buyBItem != nullptr && buyBMatches > buyBItem->count;
}
else
{
@ -369,6 +371,11 @@ void IUIScene_TradingMenu::setTradeItem(int index, shared_ptr<ItemInstance> item
vector<HtmlString> *IUIScene_TradingMenu::GetItemDescription(shared_ptr<ItemInstance> item)
{
if (item == nullptr)
{
return new vector<HtmlString>();
}
bool advanced = false;
if (const Minecraft* pMinecraft = Minecraft::GetInstance())
{

View file

@ -34,6 +34,7 @@
#include "../Minecraft.World/BlockStateDecoderRegistry.h"
#include "../Minecraft.World/BlockStateDecoder.h"
#include <map>
#include <set>
#include <cwctype>
ResourceLocation Gui::PUMPKIN_BLUR_LOCATION = ResourceLocation(TN__BLUR__MISC_PUMPKINBLUR);
@ -1190,6 +1191,14 @@ void Gui::render(float a, bool mouseFree, int xMouse, int yMouse)
if (!decoded.empty())
{
std::map<std::wstring, std::wstring> props;
std::set<std::wstring> shownProps;
auto appendProp = [&](const std::wstring &key) {
auto it = props.find(key);
if (it != props.end()) {
lines.push_back(key + L": " + it->second);
shownProps.insert(key);
}
};
size_t start = 0;
while (start < decoded.size()) {
size_t pos = decoded.find(L'\n', start);
@ -1217,31 +1226,35 @@ void Gui::render(float a, bool mouseFree, int xMouse, int yMouse)
start = pos + 1;
}
lines.push_back(L"State:");
if (props.find(L"age") != props.end()) lines.push_back(L"age: " + props[L"age"]);
if (props.find(L"facing") != props.end()) lines.push_back(L"facing: " + props[L"facing"]);
if (props.find(L"north") != props.end()) lines.push_back(L"north: " + props[L"north"]);
if (props.find(L"south") != props.end()) lines.push_back(L"south: " + props[L"south"]);
if (props.find(L"east") != props.end()) lines.push_back(L"east: " + props[L"east"]);
if (props.find(L"west") != props.end()) lines.push_back(L"west: " + props[L"west"]);
if (props.find(L"type") != props.end()) lines.push_back(L"type: " + props[L"type"]);
if (props.find(L"variant") != props.end()) lines.push_back(L"variant: " + props[L"variant"]);
if (props.find(L"axis") != props.end()) lines.push_back(L"axis: " + props[L"axis"]);
if (props.find(L"facing") != props.end()) lines.push_back(L"facing: " + props[L"facing"]);
if (props.find(L"hinge") != props.end()) lines.push_back(L"hinge: " + props[L"hinge"]);
if (props.find(L"half") != props.end()) lines.push_back(L"half: " + props[L"half"]);
if (props.find(L"shape") != props.end()) lines.push_back(L"shape: " + props[L"shape"]);
if (props.find(L"up") != props.end()) lines.push_back(L"up: " + props[L"up"]);
if (props.find(L"extended") != props.end()) lines.push_back(L"extended: " + props[L"extended"]);
if (props.find(L"open") != props.end()) lines.push_back(L"open: " + props[L"open"]);
if (props.find(L"attached") != props.end()) lines.push_back(L"attached: " + props[L"attached"]);
if (props.find(L"powered") != props.end()) lines.push_back(L"powered: " + props[L"powered"]);
if (props.find(L"delay") != props.end()) lines.push_back(L"delay: " + props[L"delay"]);
if (props.find(L"enabled") != props.end()) lines.push_back(L"enabled: " + props[L"enabled"]);
if (props.find(L"eye") != props.end()) lines.push_back(L"eye: " + props[L"eye"]);
if (props.find(L"bottle_0") != props.end()) lines.push_back(L"bottle_0: " + props[L"bottle_0"]);
if (props.find(L"bottle_1") != props.end()) lines.push_back(L"bottle_1: " + props[L"bottle_1"]);
if (props.find(L"bottle_2") != props.end()) lines.push_back(L"bottle_2: " + props[L"bottle_2"]);
if (props.find(L"has_record") != props.end()) lines.push_back(L"has_record: " + props[L"has_record"]);
appendProp(L"age");
appendProp(L"facing");
appendProp(L"north");
appendProp(L"south");
appendProp(L"east");
appendProp(L"west");
appendProp(L"type");
appendProp(L"variant");
appendProp(L"axis");
appendProp(L"hinge");
appendProp(L"half");
appendProp(L"shape");
appendProp(L"up");
appendProp(L"extended");
appendProp(L"open");
appendProp(L"attached");
appendProp(L"powered");
appendProp(L"delay");
appendProp(L"enabled");
appendProp(L"eye");
appendProp(L"bottle_0");
appendProp(L"bottle_1");
appendProp(L"bottle_2");
appendProp(L"has_record");
for (const auto &entry : props) {
if (shownProps.find(entry.first) == shownProps.end()) {
lines.push_back(entry.first + L": " + entry.second);
}
}
}
else
{

View file

@ -186,17 +186,18 @@ static std::wstring jukeboxPropsToString(int composite)
return ss.str();
}
static std::wstring daylightDetectorPropsToString(int composite)
static std::wstring daylightDetectorPropsToString(bool inverted)
{
std::wstringstream ss;
ss << L"power: " << (composite & 0xF);
ss << L"inverted: " << (inverted ? L"true" : L"false");
return ss.str();
}
static std::wstring snowPropsToString(int composite)
{
std::wstringstream ss;
int layers = (composite & 0x7) + 1;
int layers = composite & 0x7;
if (layers == 0) layers = 8;
ss << L"layers: " << layers;
return ss.str();
}
@ -605,12 +606,6 @@ static bool registerPlantDecoders()
registerDecoder(Tile::sapling_Id, [](int composite)->std::wstring { return saplingPropsToString(composite); });
registerDecoder(Tile::tallgrass_Id, [](int composite)->std::wstring { return tallGrassPropsToString(composite); });
registerDecoder(Tile::tallgrass2_Id, [](int composite)->std::wstring { return tallGrass2PropsToString(composite); });
registerDecoder(Tile::fenceGate_Id, [](int composite)->std::wstring { return fenceGatePropsToString(composite); });
registerDecoder(Tile::spruceGate_Id, [](int composite)->std::wstring { return fenceGatePropsToString(composite); });
registerDecoder(Tile::birchGate_Id, [](int composite)->std::wstring { return fenceGatePropsToString(composite); });
registerDecoder(Tile::jungleGate_Id, [](int composite)->std::wstring { return fenceGatePropsToString(composite); });
registerDecoder(Tile::darkGate_Id, [](int composite)->std::wstring { return fenceGatePropsToString(composite); });
registerDecoder(Tile::acaciaGate_Id, [](int composite)->std::wstring { return fenceGatePropsToString(composite); });
registerDecoder(Tile::fence_Id, [](int composite)->std::wstring { return fencePropsToString(composite); });
registerDecoder(Tile::netherFence_Id, [](int composite)->std::wstring { return fencePropsToString(composite); });
registerDecoder(Tile::spruceFence_Id, [](int composite)->std::wstring { return fencePropsToString(composite); });
@ -642,8 +637,14 @@ static bool registerPlantDecoders()
registerDecoder(Tile::hugeMushroom_red_Id, [](int composite)->std::wstring { return hugeMushroomPropsToString(composite); });
registerDecoder(Tile::hopper_Id, [](int composite)->std::wstring { return hopperPropsToString(composite); });
registerDecoder(Tile::jukebox_Id, [](int composite)->std::wstring { return jukeboxPropsToString(composite); });
registerDecoder(Tile::daylightDetector_Id, [](int composite)->std::wstring { return daylightDetectorPropsToString(composite); });
registerDecoder(Tile::invertedDaylightDetector_Id, [](int composite)->std::wstring { return daylightDetectorPropsToString(composite); });
registerDecoder(Tile::fenceGate_Id, [](int composite)->std::wstring { return fenceGatePropsToString(composite); });
registerDecoder(Tile::spruceGate_Id, [](int composite)->std::wstring { return fenceGatePropsToString(composite); });
registerDecoder(Tile::birchGate_Id, [](int composite)->std::wstring { return fenceGatePropsToString(composite); });
registerDecoder(Tile::jungleGate_Id, [](int composite)->std::wstring { return fenceGatePropsToString(composite); });
registerDecoder(Tile::darkGate_Id, [](int composite)->std::wstring { return fenceGatePropsToString(composite); });
registerDecoder(Tile::acaciaGate_Id, [](int composite)->std::wstring { return fenceGatePropsToString(composite); });
registerDecoder(Tile::daylightDetector_Id, [](int composite)->std::wstring { (void)composite; return daylightDetectorPropsToString(false); });
registerDecoder(Tile::invertedDaylightDetector_Id, [](int composite)->std::wstring { (void)composite; return daylightDetectorPropsToString(true); });
registerDecoder(Tile::snow_Id, [](int composite)->std::wstring { return snowPropsToString(composite); });
registerDecoder(Tile::topSnow_Id, [](int composite)->std::wstring { return snowPropsToString(composite); });
registerDecoder(Tile::cauldron_Id, [](int composite)->std::wstring { return cauldronPropsToString(composite); });

View file

@ -142,7 +142,15 @@ void ChestTileEntity::load(CompoundTag *base)
{
CompoundTag *tag = inventoryList->get(i);
unsigned int slot = tag->getByte(L"Slot") & 0xff;
if (slot >= 0 && slot < items->length) (*items)[slot] = ItemInstance::fromTag(tag);
if (slot < items->length)
{
shared_ptr<ItemInstance> loadedItem = ItemInstance::fromTag(tag);
if (loadedItem == nullptr)
{
app.DebugPrintf("[ChestTileEntity] Missing chest item at %d,%d,%d slot=%u id=%d count=%d damage=%d\n", x, y, z, slot, tag->getShort(L"id"), tag->getByte(L"Count"), tag->getShort(L"Damage"));
}
(*items)[slot] = loadedItem;
}
}
isBonusChest = base->getBoolean(L"bonus");
}

View file

@ -14,6 +14,32 @@ DaylightDetectorTile::DaylightDetectorTile(int id, bool inverted) : BaseEntityTi
updateDefaultShape();
}
void DaylightDetectorTile::createBlockStateDefinition()
{
if (!m_blockStateDefinition)
m_blockStateDefinition = new BlockStateDefinition(this);
}
int DaylightDetectorTile::defaultBlockState()
{
return 0;
}
int DaylightDetectorTile::convertBlockStateToLegacyData(BlockState *state)
{
return state ? (state->value & 0xF) : 0;
}
Tile::BlockState DaylightDetectorTile::getBlockState(int data)
{
return Tile::BlockState(data & 0xF);
}
Tile::BlockState DaylightDetectorTile::getBlockState(LevelSource *level, int x, int y, int z)
{
return Tile::BlockState(level->getData(x, y, z) & 0xF);
}
void DaylightDetectorTile::updateDefaultShape()
{
setShape(0, 0, 0, 1, 6.0f / 16.0f, 1);

View file

@ -12,6 +12,12 @@ private:
public:
DaylightDetectorTile(int id, bool inverted);
virtual void createBlockStateDefinition() override;
virtual int defaultBlockState() override;
virtual int convertBlockStateToLegacyData(BlockState *state) override;
virtual Tile::BlockState getBlockState(int data);
virtual Tile::BlockState getBlockState(LevelSource *level, int x, int y, int z) override;
virtual void updateDefaultShape(); // 4J Added override
virtual void updateShape(LevelSource *level, int x, int y, int z, int forceData = -1, shared_ptr<TileEntity> forceEntity = shared_ptr<TileEntity>());
virtual int getSignal(LevelSource *level, int x, int y, int z, int dir);

View file

@ -298,7 +298,7 @@ void FileHeader::ReadHeader( LPVOID saveMem, ESavePlatform plat /*= SAVE_FILE_PL
default:
#ifndef _CONTENT_PACKAGE
app.DebugPrintf("********** Invalid save version %d\n",m_saveVersion);
DEBUG_BREAK();
// DEBUG_BREAK();
#endif
break;
}

View file

@ -299,9 +299,11 @@ void HangingEntity::readAdditionalSaveData(CompoundTag *tag)
break;
}
}
xTile = tag->getInt(L"TileX");
yTile = tag->getInt(L"TileY");
zTile = tag->getInt(L"TileZ");
xTile = static_cast<int>(floor(x + 0.5f));
yTile = static_cast<int>(floor(y + 0.5f));
zTile = static_cast<int>(floor(z + 0.5f));
setDir(dir);
}

View file

@ -13,6 +13,7 @@
#include "Item.h"
#include "ItemInstance.h"
#include "HtmlString.h"
#include "../Minecraft.Client/Common/Consoles_App.h"
const wstring ItemInstance::ATTRIBUTE_MODIFIER_FORMAT = L"#.###";
@ -79,9 +80,22 @@ ItemInstance::ItemInstance(int id, int count, int damage)
shared_ptr<ItemInstance> ItemInstance::fromTag(CompoundTag *itemTag)
{
if (!itemTag)
{
app.DebugPrintf("[ItemInstance] NULL itemTag\n");
return nullptr;
}
shared_ptr<ItemInstance> itemInstance = shared_ptr<ItemInstance>(new ItemInstance());
itemInstance->load(itemTag);
return itemInstance->getItem() != nullptr ? itemInstance : nullptr;
Item *item = itemInstance->getItem();
if (item == nullptr)
{
app.DebugPrintf("[ItemInstance] Missing item while loading: id=%d count=%d damage=%d\n", itemInstance->id, itemInstance->count, itemInstance->auxValue);
}
return item != nullptr ? itemInstance : nullptr;
}
ItemInstance::~ItemInstance()
@ -105,6 +119,11 @@ shared_ptr<ItemInstance> ItemInstance::remove(int count)
Item *ItemInstance::getItem() const
{
if (id < 0 || id >= Item::items.length)
{
return nullptr;
}
return Item::items[id];
}

View file

@ -963,7 +963,9 @@ Material *Level::getMaterial(int x, int y, int z)
{
int t = getTile(x, y, z);
if (t == 0) return Material::air;
return Tile::tiles[t]->material;
Tile *tile = Tile::tiles[t];
if (tile == nullptr) return Material::air;
return tile->material;
}
int Level::getData(int x, int y, int z)

View file

@ -2096,6 +2096,9 @@ void LevelChunk::setBiomes(byteArray biomes)
// 4J - optimisation brought forward from 1.8.2
int LevelChunk::getTopRainBlock(int x, int z)
{
// check if sent data is not malformed causing a crash
if (x < 0 || x >= 16 || z < 0 || z >= 16) return 0;
int slot = x | (z << 4);
int h = rainHeights[slot];
@ -2106,7 +2109,8 @@ int LevelChunk::getTopRainBlock(int x, int z)
while (y > 0 && h == -1)
{
int t = getTile(x, y, z);
Material *m = t == 0 ? Material::air : Tile::tiles[t]->material;
Tile *tile = (t == 0) ? nullptr : Tile::tiles[t];
Material *m = (tile == nullptr) ? Material::air : tile->material;
if (!m->blocksMotion() && !m->isLiquid())
{
y--;

View file

@ -64,6 +64,10 @@ shared_ptr<ItemInstance> MerchantRecipe::getSellItem()
bool MerchantRecipe::isSame(MerchantRecipe *other)
{
if (!other || !buyA || !sell || !other->buyA || !other->sell)
{
return false;
}
if (buyA->id != other->buyA->id || sell->id != other->sell->id)
{
return false;
@ -74,7 +78,11 @@ bool MerchantRecipe::isSame(MerchantRecipe *other)
bool MerchantRecipe::isSameSameButBetter(MerchantRecipe *other)
{
// same deal, but cheaper
return isSame(other) && (buyA->count < other->buyA->count || (buyB != nullptr && buyB->count < other->buyB->count));
if (!isSame(other) || !buyA || !other || !other->buyA)
{
return false;
}
return buyA->count < other->buyA->count || (buyB != nullptr && buyB->count < other->buyB->count);
}
int MerchantRecipe::getUses()
@ -110,9 +118,9 @@ void MerchantRecipe::enforceDeprecated()
void MerchantRecipe::load(CompoundTag *tag)
{
CompoundTag *buyTag = tag->getCompound(L"buy");
buyA = ItemInstance::fromTag(buyTag);
buyA = buyTag ? ItemInstance::fromTag(buyTag) : nullptr;
CompoundTag *sellTag = tag->getCompound(L"sell");
sell = ItemInstance::fromTag(sellTag);
sell = sellTag ? ItemInstance::fromTag(sellTag) : nullptr;
if (tag->contains(L"buyB"))
{
buyB = ItemInstance::fromTag(tag->getCompound(L"buyB"));
@ -134,8 +142,14 @@ void MerchantRecipe::load(CompoundTag *tag)
CompoundTag *MerchantRecipe::createTag()
{
CompoundTag *tag = new CompoundTag();
tag->putCompound(L"buy", buyA->save(new CompoundTag(L"buy")));
tag->putCompound(L"sell", sell->save(new CompoundTag(L"sell")));
if (buyA != nullptr)
{
tag->putCompound(L"buy", buyA->save(new CompoundTag(L"buy")));
}
if (sell != nullptr)
{
tag->putCompound(L"sell", sell->save(new CompoundTag(L"sell")));
}
if (buyB != nullptr)
{
tag->putCompound(L"buyB", buyB->save(new CompoundTag(L"buyB")));

View file

@ -246,7 +246,9 @@ Material *Region::getMaterial(int x, int y, int z)
{
int t = getTile(x, y, z);
if (t == 0) return Material::air;
return Tile::tiles[t]->material;
Tile *tile = Tile::tiles[t];
if (tile == nullptr) return Material::air;
return tile->material;
}

View file

@ -13,11 +13,38 @@ const int TopSnowTile::HEIGHT_MASK = 7; // max 8 steps
TopSnowTile::TopSnowTile(int id) : Tile(id, Material::topSnow,isSolidRender())
{
m_defaultBlockState = 1;
setShape(0, 0, 0, 1, 2 / 16.0f, 1);
setTicking(true);
updateShape(0);
}
void TopSnowTile::createBlockStateDefinition()
{
if (!m_blockStateDefinition)
m_blockStateDefinition = new BlockStateDefinition(this);
}
int TopSnowTile::defaultBlockState()
{
return 1;
}
int TopSnowTile::convertBlockStateToLegacyData(BlockState *state)
{
return state && state->value > 0 ? ((state->value - 1) & HEIGHT_MASK) : 0;
}
Tile::BlockState TopSnowTile::getBlockState(int data)
{
return Tile::BlockState((data & HEIGHT_MASK) + 1);
}
Tile::BlockState TopSnowTile::getBlockState(LevelSource *level, int x, int y, int z)
{
return Tile::BlockState((level->getData(x, y, z) & HEIGHT_MASK) + 1);
}
void TopSnowTile::registerIcons(IconRegister *iconRegister)
{
icon = iconRegister->registerIcon(L"snow");

View file

@ -14,6 +14,13 @@ public:
protected:
TopSnowTile(int id);
public:
virtual void createBlockStateDefinition() override;
virtual int defaultBlockState() override;
virtual int convertBlockStateToLegacyData(BlockState *state) override;
virtual Tile::BlockState getBlockState(int data);
virtual Tile::BlockState getBlockState(LevelSource *level, int x, int y, int z) override;
public:
void registerIcons(IconRegister *iconRegister);
AABB *getAABB(Level *level, int x, int y, int z);