From 8c687e0cc188c5abba28105c742e43919e29b472 Mon Sep 17 00:00:00 2001 From: hellosammu Date: Sun, 15 Feb 2026 09:33:45 -0500 Subject: [PATCH 01/63] clean branch --- source/CMakeLists.txt | 16 +- source/client/app/NinecraftApp.cpp | 4 +- .../gui/screens/inventory/FurnaceScreen.cpp | 57 +++++ .../gui/screens/inventory/FurnaceScreen.hpp | 20 ++ source/client/player/LocalPlayer.cpp | 5 +- source/client/player/LocalPlayer.hpp | 2 +- source/client/renderer/Chunk.cpp | 49 +++- source/client/renderer/Chunk.hpp | 5 +- source/client/renderer/LevelRenderer.cpp | 19 +- source/client/renderer/LevelRenderer.hpp | 1 + source/client/sound/SoundRepository.cpp | 2 +- source/client/sound/sound_list.h | 12 + source/common/Mth.cpp | 4 +- source/world/Facing.cpp | 19 ++ source/world/Facing.hpp | 4 +- source/world/entity/Player.cpp | 5 + source/world/entity/Player.hpp | 3 +- source/world/inventory/FurnaceMenu.cpp | 100 ++++++++ source/world/inventory/FurnaceMenu.hpp | 24 ++ source/world/inventory/FurnaceResultSlot.cpp | 19 ++ source/world/inventory/FurnaceResultSlot.hpp | 16 ++ source/world/inventory/SimpleContainer.cpp | 6 +- source/world/inventory/SimpleContainer.hpp | 31 +-- source/world/item/CoalItem.cpp | 12 + source/world/item/CoalItem.hpp | 12 + source/world/item/Item.cpp | 17 +- source/world/item/crafting/FurnaceRecipes.cpp | 2 + source/world/item/crafting/Recipes.cpp | 26 +-- source/world/level/Level.cpp | 74 ++++++ source/world/level/Level.hpp | 8 + source/world/level/Region.cpp | 5 + source/world/level/Region.hpp | 1 + .../level/levelgen/chunk/ChunkTilePos.hpp | 9 +- .../world/level/levelgen/chunk/LevelChunk.cpp | 69 ++++++ .../world/level/levelgen/chunk/LevelChunk.hpp | 7 + .../storage/ExternalFileLevelStorage.cpp | 30 +++ source/world/level/storage/LevelSource.hpp | 3 + source/world/particle/BubbleParticle.cpp | 10 +- source/world/particle/ExplodeParticle.cpp | 10 +- source/world/particle/FlameParticle.cpp | 14 +- source/world/particle/LavaParticle.cpp | 16 +- source/world/particle/NoteParticle.cpp | 47 ++++ source/world/particle/Particle.cpp | 30 +-- source/world/particle/Particle.hpp | 22 +- source/world/particle/RedDustParticle.cpp | 14 +- source/world/particle/SmokeParticle.cpp | 14 +- source/world/particle/TerrainParticle.cpp | 18 +- source/world/phys/Vec3.hpp | 7 + source/world/tile/ChestTile.cpp | 217 ++++++++++++++++++ source/world/tile/ChestTile.hpp | 20 ++ source/world/tile/CropsTile.cpp | 2 +- source/world/tile/FurnaceTile.cpp | 194 ++++++++++++++++ source/world/tile/FurnaceTile.hpp | 33 +++ source/world/tile/MusicTile.cpp | 98 ++++++++ source/world/tile/MusicTile.hpp | 17 ++ source/world/tile/Tile.cpp | 55 ++++- source/world/tile/Tile.hpp | 9 +- source/world/tile/entity/ChestTileEntity.cpp | 31 +++ source/world/tile/entity/ChestTileEntity.hpp | 17 ++ .../world/tile/entity/FurnaceTileEntity.cpp | 163 +++++++++++++ .../world/tile/entity/FurnaceTileEntity.hpp | 33 +++ source/world/tile/entity/MusicTileEntity.cpp | 46 ++++ source/world/tile/entity/MusicTileEntity.hpp | 21 ++ source/world/tile/entity/TileEntity.cpp | 106 +++++++++ source/world/tile/entity/TileEntity.hpp | 50 ++++ source/world/tile/entity/TileEntityType.cpp | 51 ++++ source/world/tile/entity/TileEntityType.hpp | 50 ++++ 67 files changed, 1978 insertions(+), 135 deletions(-) create mode 100644 source/client/gui/screens/inventory/FurnaceScreen.cpp create mode 100644 source/client/gui/screens/inventory/FurnaceScreen.hpp create mode 100644 source/world/Facing.cpp create mode 100644 source/world/inventory/FurnaceMenu.cpp create mode 100644 source/world/inventory/FurnaceMenu.hpp create mode 100644 source/world/inventory/FurnaceResultSlot.cpp create mode 100644 source/world/inventory/FurnaceResultSlot.hpp create mode 100644 source/world/item/CoalItem.cpp create mode 100644 source/world/item/CoalItem.hpp create mode 100644 source/world/particle/NoteParticle.cpp create mode 100644 source/world/tile/ChestTile.cpp create mode 100644 source/world/tile/ChestTile.hpp create mode 100644 source/world/tile/FurnaceTile.cpp create mode 100644 source/world/tile/FurnaceTile.hpp create mode 100644 source/world/tile/MusicTile.cpp create mode 100644 source/world/tile/MusicTile.hpp create mode 100644 source/world/tile/entity/ChestTileEntity.cpp create mode 100644 source/world/tile/entity/ChestTileEntity.hpp create mode 100644 source/world/tile/entity/FurnaceTileEntity.cpp create mode 100644 source/world/tile/entity/FurnaceTileEntity.hpp create mode 100644 source/world/tile/entity/MusicTileEntity.cpp create mode 100644 source/world/tile/entity/MusicTileEntity.hpp create mode 100644 source/world/tile/entity/TileEntity.cpp create mode 100644 source/world/tile/entity/TileEntity.hpp create mode 100644 source/world/tile/entity/TileEntityType.cpp create mode 100644 source/world/tile/entity/TileEntityType.hpp diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index 36493fd02..afe085a83 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -124,8 +124,9 @@ add_library(reminecraftpe-core STATIC client/gui/screens/inventory/ContainerScreen.cpp client/gui/screens/inventory/InventoryScreen.cpp client/gui/screens/inventory/CraftingScreen.cpp - client/gui/screens/inventory/ClassicCraftingScreen_Console.cpp + client/gui/screens/inventory/FurnaceScreen.cpp client/gui/screens/inventory/ChestScreen.cpp + client/gui/screens/inventory/ClassicCraftingScreen_Console.cpp client/gui/components/ScrolledSelectionList.cpp client/gui/components/AvailableGamesList.cpp client/gui/components/RolledSelectionList.cpp @@ -350,6 +351,7 @@ add_library(reminecraftpe-core STATIC world/item/TilePlanterItem.cpp world/item/CameraItem.cpp world/item/TileItem.cpp + world/item/CoalItem.cpp world/item/Inventory.cpp world/item/crafting/Recipes.cpp world/item/crafting/FurnaceRecipes.cpp @@ -360,10 +362,12 @@ add_library(reminecraftpe-core STATIC world/inventory/ContainerMenu.cpp world/inventory/InventoryMenu.cpp world/inventory/CraftingMenu.cpp + world/inventory/FurnaceMenu.cpp world/inventory/ChestMenu.cpp world/inventory/Slot.cpp world/inventory/ResultSlot.cpp world/inventory/ArmorSlot.cpp + world/inventory/FurnaceResultSlot.cpp world/inventory/CraftingContainer.cpp world/inventory/ResultContainer.cpp world/inventory/SimpleContainer.cpp @@ -386,6 +390,7 @@ add_library(reminecraftpe-core STATIC world/item/SlabItem.cpp world/particle/RedDustParticle.cpp world/particle/TerrainParticle.cpp + world/particle/NoteParticle.cpp world/particle/BubbleParticle.cpp world/particle/ExplodeParticle.cpp world/particle/ParticleEngine.cpp @@ -421,6 +426,9 @@ add_library(reminecraftpe-core STATIC world/tile/OreTile.cpp world/tile/StairTile.cpp world/tile/SandStoneTile.cpp + world/tile/ChestTile.cpp + world/tile/FurnaceTile.cpp + world/tile/MusicTile.cpp world/tile/FireTile.cpp world/tile/StoneSlabTile.cpp world/tile/LiquidTile.cpp @@ -442,6 +450,12 @@ add_library(reminecraftpe-core STATIC world/tile/Web.cpp world/tile/FenceTile.cpp world/tile/CraftingTableTile.cpp + world/tile/entity/TileEntity.cpp + world/tile/entity/TileEntityType.cpp + world/tile/entity/ChestTileEntity.cpp + world/tile/entity/FurnaceTileEntity.cpp + world/tile/entity/MusicTileEntity.cpp + world/Facing.cpp renderer/GL/GL.cpp renderer/Attribute.cpp renderer/ConstantBufferMetaData.cpp diff --git a/source/client/app/NinecraftApp.cpp b/source/client/app/NinecraftApp.cpp index a591064f5..b805ddcd1 100644 --- a/source/client/app/NinecraftApp.cpp +++ b/source/client/app/NinecraftApp.cpp @@ -10,6 +10,7 @@ #include "world/item/Item.hpp" #include "world/entity/MobCategory.hpp" #include "world/entity/MobFactory.hpp" +#include "world/tile/entity/TileEntityType.hpp" #include "client/player/input/GameControllerHandler.hpp" #include "client/player/input/Multitouch.hpp" #include "client/gui/screens/StartMenuScreen.hpp" @@ -182,7 +183,7 @@ void NinecraftApp::_initAll() Tile::initTiles(); Item::initItems(); Biome::initBiomes(); - //TileEntity::initTileEntities(); + TileEntityType::InitTileEntities(); } _initOptions(); @@ -340,6 +341,7 @@ void NinecraftApp::onGraphicsReset() void NinecraftApp::teardown() { + TileEntityType::TeardownTileEntities(); teardownRenderer(); Resource::teardownLoaders(); // Stop our SoundSystem before we nuke our sound buffers and cause it to implode diff --git a/source/client/gui/screens/inventory/FurnaceScreen.cpp b/source/client/gui/screens/inventory/FurnaceScreen.cpp new file mode 100644 index 000000000..b28b6d538 --- /dev/null +++ b/source/client/gui/screens/inventory/FurnaceScreen.cpp @@ -0,0 +1,57 @@ +#include "FurnaceScreen.hpp" +#include "world/inventory/FurnaceMenu.hpp" +#include "renderer/ShaderConstants.hpp" + +FurnaceScreen::FurnaceScreen(Inventory* inventory, FurnaceTileEntity* container) + : ContainerScreen(new FurnaceMenu(inventory, container)), m_inventory(inventory), m_furnace(container) +{ + m_uiTheme = UI_JAVA; +} + +void FurnaceScreen::tick() +{ + ContainerScreen::tick(); +} + +void FurnaceScreen::_renderLabels() +{ + m_pFont->draw(m_furnace->getName(), 66, 6, 0x404040); + m_pFont->draw(m_inventory->getName(), 8, m_imageHeight - 96 + 2, 0x404040); +} + +void FurnaceScreen::_renderBg(float a) +{ + currentShaderColor = Color::WHITE; + + m_pMinecraft->m_pTextures->loadAndBindTexture("gui/furnace.png"); + + blit(m_leftPos, m_topPos, 0, 0, m_imageWidth, m_imageHeight, 0, 0); + + int p; + if (m_furnace->isLit()) + { + p = m_furnace->getLitProgress(12); + blit(m_leftPos + 56, m_topPos + 36 + 12 - p, 176, 12 - p, 14, p + 2, 0, 0); + } + + p = m_furnace->getBurnProgress(24); + blit(m_leftPos + 79, m_topPos + 34, 176, 14, p + 1, 16, 0, 0); +} + +SlotDisplay FurnaceScreen::_createSlotDisplay(const Slot& slot) +{ + constexpr int slotSize = 18; + switch (slot.m_group) + { + case Slot::INPUT: + return SlotDisplay(56, 17 + (slot.m_slot % 2) * (slotSize * 2)); + case Slot::OUTPUT: + return SlotDisplay(116, 35); + case Slot::INVENTORY: + return SlotDisplay(8 + (slot.m_slot % 9) * slotSize, 84 + ((slot.m_slot / 9) - 1) * slotSize, slotSize); + case Slot::HOTBAR: + return SlotDisplay(8 + (slot.m_slot % 9) * slotSize, 142, slotSize); + default: + return SlotDisplay(); + } +} \ No newline at end of file diff --git a/source/client/gui/screens/inventory/FurnaceScreen.hpp b/source/client/gui/screens/inventory/FurnaceScreen.hpp new file mode 100644 index 000000000..5b84e36d3 --- /dev/null +++ b/source/client/gui/screens/inventory/FurnaceScreen.hpp @@ -0,0 +1,20 @@ +#pragma once + +#include "ContainerScreen.hpp" +#include "world/tile/entity/FurnaceTileEntity.hpp" + +class FurnaceScreen : public ContainerScreen +{ +public: + FurnaceScreen(Inventory* inventory, FurnaceTileEntity* container); + void tick() override; + +protected: + void _renderLabels() override; + void _renderBg(float a) override; + SlotDisplay _createSlotDisplay(const Slot&) override; + +private: + Inventory* m_inventory; + FurnaceTileEntity* m_furnace; +}; diff --git a/source/client/player/LocalPlayer.cpp b/source/client/player/LocalPlayer.cpp index ec62ed15b..8db2a1dcb 100644 --- a/source/client/player/LocalPlayer.cpp +++ b/source/client/player/LocalPlayer.cpp @@ -13,6 +13,7 @@ #include "network/packets/PlayerEquipmentPacket.hpp" #include "client/gui/screens/inventory/CraftingScreen.hpp" #include "client/gui/screens/inventory/ChestScreen.hpp" +#include "client/gui/screens/inventory/FurnaceScreen.hpp" int dword_250ADC, dword_250AE0; @@ -134,11 +135,11 @@ void LocalPlayer::startCrafting(const TilePos& pos) m_pMinecraft->getScreenChooser()->pushCraftingScreen(this, pos); } -/*void LocalPlayer::openFurnace(FurnaceTileEntity* furnace) +void LocalPlayer::openFurnace(FurnaceTileEntity* furnace) { // PE 0.3.2 doesn't let you cook in creative mode m_pMinecraft->setScreen(new FurnaceScreen(m_pInventory, furnace)); -}*/ +} void LocalPlayer::openContainer(Container* container) { diff --git a/source/client/player/LocalPlayer.hpp b/source/client/player/LocalPlayer.hpp index 1eecb1288..076c5abe1 100644 --- a/source/client/player/LocalPlayer.hpp +++ b/source/client/player/LocalPlayer.hpp @@ -39,7 +39,7 @@ class LocalPlayer : public Player void setPlayerGameType(GameType gameType) override; void swing() override; void startCrafting(const TilePos&) override; - //void openFurnace(FurnaceTileEntity* furnace) override; + void openFurnace(FurnaceTileEntity* furnace) override; void openContainer(Container* container) override; void closeContainer() override; //void openTrap(DispenserTileEntity* tileEntity) override; diff --git a/source/client/renderer/Chunk.cpp b/source/client/renderer/Chunk.cpp index 86ece3261..ec7d6db5e 100644 --- a/source/client/renderer/Chunk.cpp +++ b/source/client/renderer/Chunk.cpp @@ -133,6 +133,9 @@ void Chunk::rebuild() LevelChunk::touchedSky = false; + std::set tmpSet(m_renderableTileEntities.begin(), m_renderableTileEntities.end()); + m_renderableTileEntities.clear(); + for (int i = Tile::RENDER_LAYERS_MIN; i <= Tile::RENDER_LAYERS_MAX; i++) { m_empty[i] = true; @@ -169,6 +172,16 @@ void Chunk::rebuild() t.setOffset(-m_pos); } + if (!layer && Tile::isEntityTile[tile]) + { + /* + // @TODO: ADD TILE ENTITY RENDER DISPATCHER + TileEntity* et = region.getTileEntity(tp); + if (TileEntityRenderDispatcher::getInstance()->hasRenderer(et)) + m_renderableTileEntities.push_back(et); + */ + } + Tile* pTile = Tile::tiles[tile]; if (layer == pTile->getRenderLayer()) @@ -201,11 +214,45 @@ void Chunk::rebuild() break; } + std::set newSet(m_renderableTileEntities.begin(), m_renderableTileEntities.end()); + std::vector toAdd, toRemove; + + std::set_difference( + newSet.begin(), newSet.end(), + tmpSet.begin(), tmpSet.end(), + std::back_inserter(toAdd) + ); + + std::set_difference( + tmpSet.begin(), tmpSet.end(), + newSet.begin(), newSet.end(), + std::back_inserter(toRemove) + ); + + // Add + for (std::vector::iterator it = toAdd.begin(); it != toAdd.end(); ++it) + { + m_globalRenderableTileEntities.push_back(*it); + } + + // Remove + for (std::vector::iterator it = toRemove.begin(); it != toRemove.end(); ++it) + { + std::vector::iterator f = + std::find(m_globalRenderableTileEntities.begin(), + m_globalRenderableTileEntities.end(), + *it); + + if (f != m_globalRenderableTileEntities.end()) + m_globalRenderableTileEntities.erase(f); + } + field_54 = LevelChunk::touchedSky; m_bCompiled = true; } -Chunk::Chunk(Level* level, const TilePos& pos, int size, int lists) +Chunk::Chunk(Level* level, std::vector& renderableTileEntities, const TilePos& pos, int size, int lists) + : m_globalRenderableTileEntities(renderableTileEntities) { m_bOcclusionVisible = true; m_bOcclusionQuerying = false; diff --git a/source/client/renderer/Chunk.hpp b/source/client/renderer/Chunk.hpp index 5f575c0d8..957ef0446 100644 --- a/source/client/renderer/Chunk.hpp +++ b/source/client/renderer/Chunk.hpp @@ -15,11 +15,12 @@ class Level; class Entity; +class TileEntity; class Chunk { public: - Chunk(Level*, const TilePos& pos, int, int); + Chunk(Level*, std::vector& renderableTileEntities, const TilePos& pos, int, int); public: float distanceToSqr(const Entity& entity) const; @@ -42,6 +43,8 @@ class Chunk public: Level* m_pLevel; + std::vector& m_globalRenderableTileEntities; + std::vector m_renderableTileEntities; TilePos m_pos; TilePos m_posS; bool m_empty[Tile::RENDER_LAYERS_COUNT]; diff --git a/source/client/renderer/LevelRenderer.cpp b/source/client/renderer/LevelRenderer.cpp index c7202fe54..98a311c52 100644 --- a/source/client/renderer/LevelRenderer.cpp +++ b/source/client/renderer/LevelRenderer.cpp @@ -617,7 +617,7 @@ void LevelRenderer::allChanged() m_zMinChunk = 0; m_dirtyChunks.clear(); - //m_renderableTileEntities.clear(); + m_renderableTileEntities.clear(); m_xMaxChunk = m_xChunks; m_yMaxChunk = m_yChunks; @@ -638,7 +638,7 @@ void LevelRenderer::allChanged() { int index = (cp.z * m_yChunks + cp.y) * m_xChunks + cp.x; - Chunk* pChunk = new Chunk(m_pLevel, cp * 16, 16, id + m_chunkLists); + Chunk* pChunk = new Chunk(m_pLevel, m_renderableTileEntities, cp * 16, 16, id + m_chunkLists); if (m_bOcclusionCheck) pChunk->m_occlusionId = 0; // m_occlusionCheckIds.get(count) @@ -1421,6 +1421,11 @@ void LevelRenderer::addParticle(const std::string& name, const Vec3& pos, const pe->add(new SmokeParticle(m_pLevel, pos, dir, 1.0f)); return; } + if (name == "note") + { + pe->add(new NoteParticle(m_pLevel, pos, dir)); + return; + } if (name == "explode") { pe->add(new ExplodeParticle(m_pLevel, pos, dir)); @@ -1599,6 +1604,16 @@ void LevelRenderer::renderEntities(Vec3 pos, Culler* culler, float f) EntityRenderDispatcher::getInstance()->render(*entity, f); } } + + /* + // @TODO: TileEntityRenderDispatcher + for (std::vector::const_iterator it = m_renderableTileEntities.begin(); + it != m_renderableTileEntities.end(); ++it) + { + TileEntity* tileEntity = *it; + TileEntityRenderDispatcher::getInstance()->render(tileEntity, f); + } + */ } void LevelRenderer::renderShadow(const Entity& entity, const Vec3& pos, float r, float pow, float a) diff --git a/source/client/renderer/LevelRenderer.hpp b/source/client/renderer/LevelRenderer.hpp index 1e2fbbc60..18947ff8c 100644 --- a/source/client/renderer/LevelRenderer.hpp +++ b/source/client/renderer/LevelRenderer.hpp @@ -223,4 +223,5 @@ class LevelRenderer : public LevelListener, public AppPlatformListener mce::Mesh m_darkMesh; //... Textures* m_pTextures; + std::vector m_renderableTileEntities; }; diff --git a/source/client/sound/SoundRepository.cpp b/source/client/sound/SoundRepository.cpp index 32a24a2dd..17342dc52 100644 --- a/source/client/sound/SoundRepository.cpp +++ b/source/client/sound/SoundRepository.cpp @@ -33,7 +33,7 @@ bool SoundRepository::get(const std::string& name, SoundDesc& sd) std::map >::iterator iter = m_repo.find(name); if (iter == m_repo.end()) { - LOG_E("Couldn't find a sound with id: %s", name.c_str()); + LOG_W("Couldn't find a sound with id: %s", name.c_str()); return false; } diff --git a/source/client/sound/sound_list.h b/source/client/sound/sound_list.h index e20026c34..ee6906caf 100644 --- a/source/client/sound/sound_list.h +++ b/source/client/sound/sound_list.h @@ -48,6 +48,12 @@ SOUND(ui, press) SOUND(ui, scroll) SOUND(fire, fire) +SOUND(fire, ignite) +SOUND_NUM(fire, fire_crackle, 1) +SOUND_NUM(fire, fire_crackle, 2) +SOUND_NUM(fire, fire_crackle, 3) +SOUND_NUM(fire, fire_crackle, 4) +SOUND_NUM(fire, fire_crackle, 5) SOUND_NUM(damage, fallbig, 1) SOUND_NUM(damage, fallbig, 2) @@ -103,3 +109,9 @@ SOUND_NUM(mob, zombie, 3) SOUND_NUM(mob, zombiehurt, 1) SOUND_NUM(mob, zombiehurt, 2) SOUND(mob, zombiedeath) + +SOUND(note, harp) +SOUND(note, bd) +SOUND(note, hat) +SOUND(note, snare) +SOUND(note, bassattack) \ No newline at end of file diff --git a/source/common/Mth.cpp b/source/common/Mth.cpp index d3a20ddb0..5126313ab 100644 --- a/source/common/Mth.cpp +++ b/source/common/Mth.cpp @@ -48,13 +48,13 @@ float Mth::invSqrt(float number) // they just stole it from Quake. float x2, y; - const float threehalfs = 1.5F; + const float threehalfs = 1.5f; union { float f; int32_t i; } un; - x2 = number * 0.5F; + x2 = number * 0.5f; un.f = number; // evil floating point bit level hacking un.i = 0x5f3759df - ( un.i >> 1 ); // what the fuck? y = un.f; diff --git a/source/world/Facing.cpp b/source/world/Facing.cpp new file mode 100644 index 000000000..0893ffc57 --- /dev/null +++ b/source/world/Facing.cpp @@ -0,0 +1,19 @@ +#include "Facing.hpp" + +const Facing::Name Facing::OPPOSITE[6] = +{ + Facing::UP, // DOWN -> UP + Facing::DOWN, // UP -> DOWN + Facing::SOUTH, // NORTH -> SOUTH + Facing::NORTH, // SOUTH -> NORTH + Facing::EAST, // WEST -> EAST + Facing::WEST // EAST -> WEST +}; + +const Facing::Name Facing::HORIZONTAL[4] = +{ + Facing::NORTH, + Facing::SOUTH, + Facing::EAST, + Facing::WEST +}; \ No newline at end of file diff --git a/source/world/Facing.hpp b/source/world/Facing.hpp index 740f1ce37..2875da6d5 100644 --- a/source/world/Facing.hpp +++ b/source/world/Facing.hpp @@ -12,4 +12,6 @@ class Facing WEST, // -X EAST // +X }; -}; + static const Name OPPOSITE[6]; + static const Name HORIZONTAL[4]; +}; \ No newline at end of file diff --git a/source/world/entity/Player.cpp b/source/world/entity/Player.cpp index 444cb5254..d7e0cff66 100644 --- a/source/world/entity/Player.cpp +++ b/source/world/entity/Player.cpp @@ -540,6 +540,11 @@ void Player::startCrafting(const TilePos& pos) } +void Player::openFurnace(FurnaceTileEntity* tileEntity) +{ + +} + void Player::startStonecutting(const TilePos& pos) { diff --git a/source/world/entity/Player.hpp b/source/world/entity/Player.hpp index 2d88c21ed..1a25f7228 100644 --- a/source/world/entity/Player.hpp +++ b/source/world/entity/Player.hpp @@ -16,6 +16,7 @@ #include "world/inventory/InventoryMenu.hpp" class Inventory; // in case we're included from Inventory.hpp +class FurnaceTileEntity; class Player : public Mob { @@ -72,7 +73,7 @@ class Player : public Mob virtual void startStonecutting(const TilePos& pos); virtual void startDestroying(); virtual void stopDestroying(); - //virtual void openFurnace(FurnaceTileEntity* tileEntity); + virtual void openFurnace(FurnaceTileEntity* tileEntity); virtual void openContainer(Container* container) {} virtual void closeContainer() {} //virtual void openTrap(DispenserTileEntity* tileEntity); diff --git a/source/world/inventory/FurnaceMenu.cpp b/source/world/inventory/FurnaceMenu.cpp new file mode 100644 index 000000000..e8f6678ac --- /dev/null +++ b/source/world/inventory/FurnaceMenu.cpp @@ -0,0 +1,100 @@ +#include "FurnaceMenu.hpp" +#include "Slot.hpp" +#include "FurnaceResultSlot.hpp" +#include "world/ContainerListener.hpp" + +FurnaceMenu::FurnaceMenu(Inventory* inventory, FurnaceTileEntity* furnace) + : ContainerMenu(Container::FURNACE), m_furnace(furnace), m_lastCookTime(0), m_lastBurnTime(0), m_lastLitDuration(0) +{ + addSlot(new Slot(m_furnace, 0, Slot::INPUT)); + addSlot(new Slot(m_furnace, 1, Slot::INPUT)); + addSlot(new FurnaceResultSlot(inventory->m_pPlayer, m_furnace, 2)); + + for (int y = 0; y < 3; ++y) + { + for (int x = 0; x < 9; ++x) + addSlot(new Slot(inventory, x + (y + 1) * 9, Slot::INVENTORY)); + } + + for (int i = 0; i < 9; ++i) + { + addSlot(new Slot(inventory, i, Slot::HOTBAR)); + } +} + +bool FurnaceMenu::stillValid(Player* player) const +{ + return m_furnace->stillValid(player); +} + +void FurnaceMenu::addSlotListener(ContainerListener* listener) +{ + ContainerMenu::addSlotListener(listener); + listener->setContainerData(this, 0, m_furnace->m_tickCount); + listener->setContainerData(this, 1, m_furnace->m_litTime); + listener->setContainerData(this, 2, m_furnace->m_litDuration); +} + +void FurnaceMenu::broadcastChanges() +{ + ContainerMenu::broadcastChanges(); + + for (std::vector::iterator it = m_listeners.begin(); it != m_listeners.end(); ++it) + { + ContainerListener* listener = *it; + + if (m_lastCookTime != m_furnace->m_tickCount) + listener->setContainerData(this, 0, m_furnace->m_tickCount); + + if (m_lastBurnTime != m_furnace->m_litTime) + listener->setContainerData(this, 1, m_furnace->m_litTime); + + if (m_lastLitDuration != m_furnace->m_litDuration) + listener->setContainerData(this, 2, m_furnace->m_litDuration); + } + + m_lastCookTime = m_furnace->m_tickCount; + m_lastBurnTime = m_furnace->m_litTime; + m_lastLitDuration = m_furnace->m_litDuration; +} + +void FurnaceMenu::setData(int index, int value) +{ + if (index == 0) + m_furnace->m_tickCount = value; + else if (index == 1) + m_furnace->m_litTime = value; + else if (index == 2) + m_furnace->m_litDuration = value; +} + +ItemStack FurnaceMenu::quickMoveStack(int index) +{ + ItemStack item; + Slot* slot = getSlot(index); + if (slot && slot->hasItem()) + { + ItemStack& slotItem = slot->getItem(); + item = slotItem.copy(); + if (index == 2) + moveItemStackTo(slotItem, 3, 39, true); + else if (index >= 3 && index < 30) + moveItemStackTo(slotItem, 30, 39, false); + else if (index >= 30 && index < 39) + moveItemStackTo(slotItem, 3, 30, false); + else + moveItemStackTo(slotItem, 3, 39, false); + + if (slotItem.m_count == 0) + slot->set(ItemStack::EMPTY); + else + slot->setChanged(); + + if (slotItem.m_count == item.m_count) + return ItemStack::EMPTY; + + slot->onTake(slotItem); + } + + return item; +} diff --git a/source/world/inventory/FurnaceMenu.hpp b/source/world/inventory/FurnaceMenu.hpp new file mode 100644 index 000000000..42025cf41 --- /dev/null +++ b/source/world/inventory/FurnaceMenu.hpp @@ -0,0 +1,24 @@ +#pragma once + +#include "ContainerMenu.hpp" +#include "world/entity/Player.hpp" +#include + +class FurnaceMenu : public ContainerMenu +{ +public: + FurnaceMenu(Inventory* inventory, FurnaceTileEntity* container); + +public: + bool stillValid(Player* player) const override; + void addSlotListener(ContainerListener* listener) override; + void broadcastChanges() override; + void setData(int, int) override; + ItemStack quickMoveStack(int index) override; + +private: + FurnaceTileEntity* m_furnace; + int m_lastCookTime; + int m_lastBurnTime; + int m_lastLitDuration; +}; diff --git a/source/world/inventory/FurnaceResultSlot.cpp b/source/world/inventory/FurnaceResultSlot.cpp new file mode 100644 index 000000000..84d628de0 --- /dev/null +++ b/source/world/inventory/FurnaceResultSlot.cpp @@ -0,0 +1,19 @@ +#include "FurnaceResultSlot.hpp" +#include "../item/Item.hpp" +#include "world/entity/Player.hpp" + +FurnaceResultSlot::FurnaceResultSlot(Player* player, Container* container, int slotIndex) + : Slot(container, slotIndex, OUTPUT), m_pPlayer(player) +{ +} + +bool FurnaceResultSlot::mayPlace(const ItemStack&) const +{ + return false; +} + +void FurnaceResultSlot::onTake(ItemStack& inst) +{ + inst.onCraftedBy(m_pPlayer, m_pPlayer->m_pLevel); + Slot::onTake(inst); +} diff --git a/source/world/inventory/FurnaceResultSlot.hpp b/source/world/inventory/FurnaceResultSlot.hpp new file mode 100644 index 000000000..4c1e277a1 --- /dev/null +++ b/source/world/inventory/FurnaceResultSlot.hpp @@ -0,0 +1,16 @@ +#pragma once + +#include "Slot.hpp" +#include "source/world/Container.hpp" + +class FurnaceResultSlot : public Slot +{ +public: + FurnaceResultSlot(Player* player, Container* container, int slotIdx); + + bool mayPlace(const ItemStack&) const override; + void onTake(ItemStack&) override; + +private: + Player* m_pPlayer; +}; \ No newline at end of file diff --git a/source/world/inventory/SimpleContainer.cpp b/source/world/inventory/SimpleContainer.cpp index 6799cd433..74b8c4c5d 100644 --- a/source/world/inventory/SimpleContainer.cpp +++ b/source/world/inventory/SimpleContainer.cpp @@ -64,7 +64,7 @@ bool SimpleContainer::stillValid(Player* player) const return true; } -void SimpleContainer::load(CompoundTag& tag) +void SimpleContainer::load(const CompoundTag& tag) { clear(); const ListTag* list = tag.getList("Items"); @@ -78,14 +78,14 @@ void SimpleContainer::load(CompoundTag& tag) { uint8_t slot = itemTag->getInt8("Slot") & 255; ItemStack item = ItemStack::fromTag(*itemTag); - if (itemTag->isEmpty() && slot >= 0 && slot < m_items.size()) + if (!itemTag->isEmpty() && slot >= 0 && slot < m_items.size()) m_items[slot] = item; } } } -void SimpleContainer::save(CompoundTag& tag) +void SimpleContainer::save(CompoundTag& tag) const { ListTag* list = new ListTag; diff --git a/source/world/inventory/SimpleContainer.hpp b/source/world/inventory/SimpleContainer.hpp index 2e676e564..a371d2532 100644 --- a/source/world/inventory/SimpleContainer.hpp +++ b/source/world/inventory/SimpleContainer.hpp @@ -9,28 +9,21 @@ class SimpleContainer : public Container public: SimpleContainer(int size, const std::string& name); - virtual uint16_t getContainerSize() const override; - - virtual ItemStack& getItem(int index) override; - - virtual ItemStack removeItem(int index, int count) override; - - virtual void setItem(int index, const ItemStack& item) override; - - virtual std::string getName() const override; - - virtual void setChanged() override; - - virtual bool stillValid(Player* player) const override; +public: + uint16_t getContainerSize() const override; + ItemStack& getItem(int index) override; + ItemStack removeItem(int index, int count) override; + void setItem(int index, const ItemStack& item) override; + std::string getName() const override; + void setChanged() override; + bool stillValid(Player* player) const override; +public: virtual void clear(); - - virtual void load(CompoundTag& tag); - virtual void save(CompoundTag& tag); + virtual void load(const CompoundTag& tag); + virtual void save(CompoundTag& tag) const; private: std::vector m_items; std::string m_name; -}; - - +}; \ No newline at end of file diff --git a/source/world/item/CoalItem.cpp b/source/world/item/CoalItem.cpp new file mode 100644 index 000000000..4d3f6669e --- /dev/null +++ b/source/world/item/CoalItem.cpp @@ -0,0 +1,12 @@ +#include "CoalItem.hpp" + +CoalItem::CoalItem(int id) : Item(id) +{ + m_maxDamage = 0; + m_bStackedByData = true; +} + +std::string CoalItem::getDescriptionId(ItemStack* inst) const +{ + return (inst->getAuxValue() == 1) ? "item.charcoal" : "item.coal"; +} \ No newline at end of file diff --git a/source/world/item/CoalItem.hpp b/source/world/item/CoalItem.hpp new file mode 100644 index 000000000..fc1474401 --- /dev/null +++ b/source/world/item/CoalItem.hpp @@ -0,0 +1,12 @@ +#pragma once + +#include "Item.hpp" + +class CoalItem : public Item +{ +public: + CoalItem(int id); + + std::string getDescriptionId(ItemStack* inst) const override; + +}; \ No newline at end of file diff --git a/source/world/item/Item.cpp b/source/world/item/Item.cpp index 88fe95939..7377fca9f 100644 --- a/source/world/item/Item.cpp +++ b/source/world/item/Item.cpp @@ -11,19 +11,20 @@ #include "common/Logger.hpp" #include "common/Util.hpp" +#include "ArmorItem.hpp" +#include "BowItem.hpp" #include "CameraItem.hpp" +#include "CoalItem.hpp" #include "DoorItem.hpp" -#include "TileItem.hpp" -#include "TilePlanterItem.hpp" -#include "RocketItem.hpp" -#include "ToolItem.hpp" -#include "BowItem.hpp" #include "DyePowderItem.hpp" -#include "WeaponItem.hpp" #include "FoodItem.hpp" -#include "ArmorItem.hpp" #include "HoeItem.hpp" +#include "RocketItem.hpp" #include "SeedItem.hpp" +#include "TileItem.hpp" +#include "ToolItem.hpp" +#include "TilePlanterItem.hpp" +#include "WeaponItem.hpp" #define ITEM(x) ((x) - 256) @@ -339,7 +340,7 @@ void Item::initItems() ->setIcon(5, 2) ->setDescriptionId("arrow"); - Item::coal = NEW_ITEM(ITEM_COAL) + Item::coal = NEW_X_ITEMN(CoalItem, ITEM_COAL) ->setIcon(7, 0) ->setDescriptionId("coal"); diff --git a/source/world/item/crafting/FurnaceRecipes.cpp b/source/world/item/crafting/FurnaceRecipes.cpp index 0c170f30d..36f99a069 100644 --- a/source/world/item/crafting/FurnaceRecipes.cpp +++ b/source/world/item/crafting/FurnaceRecipes.cpp @@ -1,6 +1,8 @@ #include "FurnaceRecipes.hpp" #include "common/Logger.hpp" +FurnaceRecipes* FurnaceRecipes::instance; + FurnaceRecipes::FurnaceRecipes() { addFurnaceRecipe(Tile::ironOre, ItemStack(Item::ironIngot)); diff --git a/source/world/item/crafting/Recipes.cpp b/source/world/item/crafting/Recipes.cpp index 05f0e32c0..fe375b83b 100644 --- a/source/world/item/crafting/Recipes.cpp +++ b/source/world/item/crafting/Recipes.cpp @@ -120,14 +120,14 @@ Recipes::Recipes() addOre(ItemStack(Item::dye_powder, 1, 4), Tile::lapisBlock); // StructureRecipes - //add(ShapedRecipeBuilder("###", - // "# #", - // "###", ItemStack(Tile::chest)) - // .add('#', Tile::wood)); - //add(ShapedRecipeBuilder("###", - // "# #", - // "###", ItemStack(Tile::furnace)) - // .add('#', Tile::stoneBrick)); + add(ShapedRecipeBuilder("###", + "# #", + "###", ItemStack(Tile::chest)) + .add('#', Tile::wood)); + add(ShapedRecipeBuilder("###", + "# #", + "###", ItemStack(Tile::furnace)) + .add('#', Tile::stoneBrick)); add(ShapedRecipeBuilder("##", "##", ItemStack(Tile::craftingTable)) @@ -169,11 +169,11 @@ Recipes::Recipes() // .add('#', Tile::wood) // .add('X', Item::emerald)); - //add(ShapedRecipeBuilder("###", - // "#X#", - // "###", ItemStack(Tile::musicBlock, 1)) - // .add('#', Tile::wood) - // .add('X', Item::redStone)); + add(ShapedRecipeBuilder("###", + "#X#", + "###", ItemStack(Tile::musicBlock, 1)) + .add('#', Tile::wood) + .add('X', Item::redStone)); add(ShapedRecipeBuilder("###", "XXX", diff --git a/source/world/level/Level.cpp b/source/world/level/Level.cpp index e91a3c8f2..efec2255d 100644 --- a/source/world/level/Level.cpp +++ b/source/world/level/Level.cpp @@ -18,6 +18,7 @@ #include "network/packets/ExplodePacket.hpp" #include "world/level/levelgen/chunk/ChunkCache.hpp" #include "world/entity/MobSpawner.hpp" +#include "world/tile/entity/TileEntity.hpp" #include "Explosion.hpp" #include "Region.hpp" @@ -38,6 +39,8 @@ Level::Level(LevelStorage* pStor, const std::string& name, const LevelSettings& m_bCalculatingInitialSpawn = false; m_pChunkSource = nullptr; m_pLevelStorage = pStor; + m_tileEntityList = TileEntityVector(); + m_bUpdatingTileEntities = false; m_randValue = 42184323; m_addend = 1013904223; m_bUpdateLights = true; @@ -258,6 +261,54 @@ int Level::getRawBrightness(const TilePos& pos, bool b) const return pChunk->getRawBrightness(pos, m_skyDarken); } +TileEntity* Level::getTileEntity(const TilePos& pos) const +{ + LevelChunk* pChunk = getChunk(pos); + return pChunk ? pChunk->getTileEntity(pos) : nullptr; +} + +const TileEntityVector* Level::getAllTileEntities() const +{ + return &m_tileEntityList; +} + +void Level::setTileEntity(const TilePos& pos, TileEntity* tileEntity) +{ + + if (tileEntity->isRemoved()) + return; + + if (m_bUpdatingTileEntities) + { + tileEntity->m_pos = pos; + m_pendingTileEntities.push_back(tileEntity); + return; + } + + m_tileEntityList.push_back(tileEntity); + LevelChunk* pChunk = getChunk(pos); + if (pChunk) + pChunk->setTileEntity(pos, tileEntity); +} + +void Level::removeTileEntity(const TilePos& pos) +{ + TileEntity* old = getTileEntity(pos); + + if (old != nullptr && m_bUpdatingTileEntities) + { + old->setRemoved(); + return; + } + + if (old) + Util::remove(m_tileEntityList, old); + + LevelChunk* pChunk = getChunk(pos); + if (pChunk) + pChunk->removeTileEntity(pos); +} + void Level::swap(const TilePos& pos1, const TilePos& pos2) { TileID tile1 = getTile(pos1); @@ -1706,6 +1757,29 @@ void Level::tickEntities() delete pEnt; } } + + m_bUpdatingTileEntities = true; + for (size_t i = 0; i < m_tileEntityList.size(); i++) + { + TileEntity* tileEnt = m_tileEntityList[i]; + + if (!tileEnt->isRemoved()) + { + tileEnt->tick(); + } + else + { + LevelChunk* ch = getChunk(tileEnt->m_pos); + if (ch) + ch->removeTileEntity(tileEnt->m_pos); + + m_entities.erase(m_entities.begin() + i); + i--; + + delete tileEnt; + } + } + m_bUpdatingTileEntities = false; } HitResult Level::clip(Vec3 v1, Vec3 v2, bool flag) const diff --git a/source/world/level/Level.hpp b/source/world/level/Level.hpp index 993830cf6..95ae707ea 100644 --- a/source/world/level/Level.hpp +++ b/source/world/level/Level.hpp @@ -38,6 +38,7 @@ class Packet; class MobSpawner; typedef std::vector EntityVector; +typedef std::vector TileEntityVector; typedef std::vector AABBVector; struct Brightness @@ -72,6 +73,10 @@ class Level : public LevelSource LevelChunk* getChunkAt(const TilePos& pos) const; int getRawBrightness(const TilePos& pos) const; int getRawBrightness(const TilePos& pos, bool b) const; + TileEntity* getTileEntity(const TilePos& pos) const override; + const TileEntityVector* getAllTileEntities() const; + void setTileEntity(const TilePos& pos, TileEntity* tileEntity); + void removeTileEntity(const TilePos& pos); int getBrightness(const LightLayer&, const TilePos& pos) const; void setBrightness(const LightLayer&, const TilePos& pos, int brightness); int getSeaLevel() const { return 63; } @@ -203,6 +208,7 @@ class Level : public LevelSource private: LevelData* m_pLevelData; + bool m_bUpdatingTileEntities; protected: int m_randValue; @@ -237,5 +243,7 @@ class Level : public LevelSource MobSpawner* m_pMobSpawner; std::map m_entityCountsByCategory; + TileEntityVector m_tileEntityList; + TileEntityVector m_pendingTileEntities; }; diff --git a/source/world/level/Region.cpp b/source/world/level/Region.cpp index 9a7109ce6..8d55fcd48 100644 --- a/source/world/level/Region.cpp +++ b/source/world/level/Region.cpp @@ -111,6 +111,11 @@ BiomeSource* Region::getBiomeSource() const return m_pLevel->getBiomeSource(); } +TileEntity* Region::getTileEntity(const TilePos& pos) const +{ + return m_pLevel->getTileEntity(pos); +} + Region::~Region() { delete[] field_C; diff --git a/source/world/level/Region.hpp b/source/world/level/Region.hpp index ca0101edb..e0eeea90a 100644 --- a/source/world/level/Region.hpp +++ b/source/world/level/Region.hpp @@ -21,6 +21,7 @@ class Region : public LevelSource Material* getMaterial(const TilePos& pos) const override; bool isSolidTile(const TilePos& pos) const override; BiomeSource* getBiomeSource() const override; + TileEntity* getTileEntity(const TilePos& pos) const override; virtual ~Region(); Region(const Level* level, const TilePos& min, const TilePos& max); diff --git a/source/world/level/levelgen/chunk/ChunkTilePos.hpp b/source/world/level/levelgen/chunk/ChunkTilePos.hpp index 923a430f3..258f9010d 100644 --- a/source/world/level/levelgen/chunk/ChunkTilePos.hpp +++ b/source/world/level/levelgen/chunk/ChunkTilePos.hpp @@ -19,4 +19,11 @@ struct ChunkTilePos { return ChunkTilePos(x + other.x, y + other.y, z + other.z); } -}; + + bool operator<(const ChunkTilePos& other) const + { + if (x != other.x) return x < other.x; + if (y != other.y) return y < other.y; + return z < other.z; + } +}; \ No newline at end of file diff --git a/source/world/level/levelgen/chunk/LevelChunk.cpp b/source/world/level/levelgen/chunk/LevelChunk.cpp index e0de339b4..598a4ef8a 100644 --- a/source/world/level/levelgen/chunk/LevelChunk.cpp +++ b/source/world/level/levelgen/chunk/LevelChunk.cpp @@ -8,6 +8,7 @@ #include "common/Logger.hpp" #include "world/level/Level.hpp" +#include "world/tile/entity/TileEntity.hpp" #include "world/phys/AABB.hpp" bool LevelChunk::touchedSky = false; @@ -683,6 +684,74 @@ bool LevelChunk::setTileAndData(const ChunkTilePos& pos, TileID tile, TileData d return true; } +TileEntity* LevelChunk::getTileEntity(const ChunkTilePos& pos) +{ + std::map::iterator it = m_tileEntities.find(pos); + if (it == m_tileEntities.end()) + { + int tileId = getTile(pos); + if (tileId <= TILE_AIR || !Tile::isEntityTile[tileId]) + return nullptr; + + TilePos tilePos(m_chunkPos, pos.y); + tilePos += TilePos(pos.x, 0, pos.z); + + Tile* pTile = Tile::tiles[tileId]; + pTile->onPlace(m_pLevel, tilePos); + + // do a recheck to see if a tile entity was actually added. + it = m_tileEntities.find(pos); + return (it == m_tileEntities.end()) ? nullptr : it->second; + } + + if (!it->second || it->second->isRemoved()) + { + m_tileEntities.erase(it); + return nullptr; + } + + return it->second; +} + +void LevelChunk::addTileEntity(TileEntity* tileEntity) +{ + setTileEntity(tileEntity->m_pos, tileEntity); + if (m_bLoaded) + m_pLevel->m_tileEntityList.push_back(tileEntity); +} + +void LevelChunk::setTileEntity(const ChunkTilePos& pos, TileEntity* tileEntity) +{ + tileEntity->m_pLevel = m_pLevel; + TileID tile = getTile(pos); + TilePos tilePos(m_chunkPos, pos.y); + tilePos.x += pos.x; + tilePos.z += pos.z; + tileEntity->m_pos = tilePos; + if (tile > 0 && Tile::isEntityTile[tile]) + { + tileEntity->clearRemoved(); + m_tileEntities[pos] = tileEntity; + return; + } + + LOG_I("Attempted to place a tile entity where there was no entity tile! %d", tile); +} + +void LevelChunk::removeTileEntity(const ChunkTilePos& pos) +{ + if (!m_bLoaded) + return; + + std::map::iterator it = m_tileEntities.find(pos); + if (it != m_tileEntities.end()) + { + if (it->second) + it->second->setRemoved(); + m_tileEntities.erase(it); + } +} + TileData LevelChunk::getData(const ChunkTilePos& pos) { CheckPosition(pos); diff --git a/source/world/level/levelgen/chunk/LevelChunk.hpp b/source/world/level/levelgen/chunk/LevelChunk.hpp index f25faf097..d103eb18a 100644 --- a/source/world/level/levelgen/chunk/LevelChunk.hpp +++ b/source/world/level/levelgen/chunk/LevelChunk.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include "common/Random.hpp" #include "common/Utils.hpp" #include "client/renderer/LightLayer.hpp" @@ -21,6 +22,7 @@ class Level; class AABB; class Entity; +class TileEntity; class LevelChunk { @@ -69,6 +71,10 @@ class LevelChunk virtual void setBlocks(uint8_t* pData, int y); virtual int getBlocksAndData(uint8_t* pData, int, int, int, int, int, int, int); virtual int setBlocksAndData(uint8_t* pData, int, int, int, int, int, int, int); + virtual TileEntity* getTileEntity(const ChunkTilePos& pos); + virtual void addTileEntity(TileEntity* tileEntity); + virtual void setTileEntity(const ChunkTilePos& pos, TileEntity* tileEntity); + virtual void removeTileEntity(const ChunkTilePos& pos); virtual Random getRandom(int32_t l); virtual void recalcHeight(const ChunkTilePos& pos); virtual bool isEmpty(); @@ -96,4 +102,5 @@ class LevelChunk int field_23C; TileID* m_pBlockData; std::vector m_entities[128 / 16]; + std::map m_tileEntities; }; diff --git a/source/world/level/storage/ExternalFileLevelStorage.cpp b/source/world/level/storage/ExternalFileLevelStorage.cpp index 18b1e297c..681d04db0 100644 --- a/source/world/level/storage/ExternalFileLevelStorage.cpp +++ b/source/world/level/storage/ExternalFileLevelStorage.cpp @@ -16,6 +16,7 @@ #include "network/RakIO.hpp" #include "world/entity/EntityFactory.hpp" #include "world/level/Level.hpp" +#include "world/tile/entity/TileEntity.hpp" #include "thirdparty/raknet/GetTime.h" #ifndef DEMO @@ -335,6 +336,22 @@ void ExternalFileLevelStorage::loadEntities(Level* level, LevelChunk* chunk) level->addEntity(entity); } } + + const ListTag* tileEntitiesTag = tag->getList("TileEntities"); + if (tileEntitiesTag) + { + const std::vector& tileEntities = tileEntitiesTag->rawView(); + for (std::vector::const_iterator it = tileEntities.begin(); it != tileEntities.end(); it++) + { + const Tag* betterTag = *it; + if (!betterTag || betterTag->getId() != Tag::TAG_TYPE_COMPOUND) + continue; + + TileEntity* tileEntity = TileEntity::LoadTileEntity(*(CompoundTag*)betterTag); + if (tileEntity) + level->setTileEntity(tileEntity->m_pos, tileEntity); + } + } } tag->deleteChildren(); @@ -395,8 +412,21 @@ void ExternalFileLevelStorage::saveEntities(Level* level, LevelChunk* chunk) entitiesTag->add(tag); } + ListTag* tileEntitiesTag = new ListTag(); + + const TileEntityVector* tileEntities = level->getAllTileEntities(); + for (TileEntityVector::const_iterator it = tileEntities->begin(); it != tileEntities->end(); it++) + { + const TileEntity* tileEntity = *it; + CompoundTag* tag = new CompoundTag(); + + tileEntity->save(*tag); + tileEntitiesTag->add(tag); + } + CompoundTag tag = CompoundTag(); tag.put("Entities", entitiesTag); + tag.put("TileEntities", tileEntitiesTag); RakNet::BitStream bs; RakDataOutput dos = RakDataOutput(bs); NbtIo::write(tag, dos); diff --git a/source/world/level/storage/LevelSource.hpp b/source/world/level/storage/LevelSource.hpp index 97cc026b2..7cdbc8d2b 100644 --- a/source/world/level/storage/LevelSource.hpp +++ b/source/world/level/storage/LevelSource.hpp @@ -12,6 +12,8 @@ #include "world/level/Material.hpp" #include "world/level/levelgen/biome/BiomeSource.hpp" +class TileEntity; + class LevelSource { public: @@ -22,5 +24,6 @@ class LevelSource virtual Material* getMaterial(const TilePos& pos) const = 0; virtual bool isSolidTile(const TilePos& pos) const = 0; virtual BiomeSource* getBiomeSource() const = 0; + virtual TileEntity* getTileEntity(const TilePos& pos) const = 0; }; diff --git a/source/world/particle/BubbleParticle.cpp b/source/world/particle/BubbleParticle.cpp index 234917b45..96642b64a 100644 --- a/source/world/particle/BubbleParticle.cpp +++ b/source/world/particle/BubbleParticle.cpp @@ -13,14 +13,14 @@ BubbleParticle::BubbleParticle(Level* level, const Vec3& pos, const Vec3& dir) : Particle(level, pos, dir) { m_rCol = m_gCol = m_bCol = 1.0f; - field_DC = PTI_BUBBLE; + m_tex = PTI_BUBBLE; setSize(0.02f, 0.02f); - field_F0 *= 0.2f + 0.6f * sharedRandom.nextFloat(); + m_size *= 0.2f + 0.6f * sharedRandom.nextFloat(); m_vel.x = dir.x * 0.2f + 0.02f * (2.0f * Mth::random() - 1.0f); m_vel.y = dir.y * 0.2f + 0.02f * (2.0f * Mth::random() - 1.0f); m_vel.z = dir.z * 0.2f + 0.02f * (2.0f * Mth::random() - 1.0f); - field_EC = int(8.0f / (Mth::random() * 0.8f + 0.2f)); + m_lifetime = int(8.0f / (Mth::random() * 0.8f + 0.2f)); } void BubbleParticle::tick() @@ -35,7 +35,7 @@ void BubbleParticle::tick() if (m_pLevel->getMaterial(m_pos) != Material::water) remove(); - field_EC--; - if (field_EC <= 0) + m_lifetime--; + if (m_lifetime <= 0) remove(); } diff --git a/source/world/particle/ExplodeParticle.cpp b/source/world/particle/ExplodeParticle.cpp index e5149e29a..c13d15f45 100644 --- a/source/world/particle/ExplodeParticle.cpp +++ b/source/world/particle/ExplodeParticle.cpp @@ -18,20 +18,20 @@ ExplodeParticle::ExplodeParticle(Level* level, const Vec3& pos, const Vec3& dir) m_vel.z = dir.z + 0.05f * (2.0f * Mth::random() - 1.0f); m_rCol = m_gCol = m_bCol = 0.7f + 0.3f * sharedRandom.nextFloat(); - field_F0 = 1.0f + 6.0f * sharedRandom.nextFloat() * sharedRandom.nextFloat(); - field_EC = int(16.0f / (0.2f + 0.8f * sharedRandom.nextFloat())) + 2; + m_size = 1.0f + 6.0f * sharedRandom.nextFloat() * sharedRandom.nextFloat(); + m_lifetime = int(16.0f / (0.2f + 0.8f * sharedRandom.nextFloat())) + 2; } void ExplodeParticle::tick() { m_oPos = m_pos; - field_E8++; - if (field_E8 > field_EC) + m_timer++; + if (m_timer > m_lifetime) remove(); m_vel.y += 0.004f; - field_DC = -8 * field_E8 / field_EC + 7; + m_tex = -8 * m_timer / m_lifetime + 7; move(m_vel); diff --git a/source/world/particle/FlameParticle.cpp b/source/world/particle/FlameParticle.cpp index 39a63d158..5e17d316e 100644 --- a/source/world/particle/FlameParticle.cpp +++ b/source/world/particle/FlameParticle.cpp @@ -23,10 +23,10 @@ FlameParticle::FlameParticle(Level* level, const Vec3& pos, const Vec3& dir) : sharedRandom.genrand_int32(); sharedRandom.genrand_int32(); - field_104 = field_F0; + field_104 = m_size; m_rCol = m_gCol = m_bCol = 1.0f; - field_EC = int(8.0f / (0.2f + 0.8f * Mth::random())) + 4; - field_DC = PTI_FLAME; + m_lifetime = int(8.0f / (0.2f + 0.8f * Mth::random())) + 4; + m_tex = PTI_FLAME; } float FlameParticle::getBrightness(float unused) const @@ -38,8 +38,8 @@ void FlameParticle::tick() { m_oPos = m_pos; - field_E8++; - if (field_E8 > field_EC) + m_timer++; + if (m_timer > m_lifetime) remove(); move(m_vel); @@ -55,7 +55,7 @@ void FlameParticle::tick() void FlameParticle::render(Tesselator& t, float f, float a, float b, float c, float d, float e) { - float mult = float(field_E8 + f) / float(field_EC); - field_F0 = field_104 * (1.0f - 0.5f * mult * mult); + float mult = float(m_timer + f) / float(m_lifetime); + m_size = field_104 * (1.0f - 0.5f * mult * mult); Particle::render(t, f, a, b, c, d, e); } diff --git a/source/world/particle/LavaParticle.cpp b/source/world/particle/LavaParticle.cpp index d76ad60bd..413eedd87 100644 --- a/source/world/particle/LavaParticle.cpp +++ b/source/world/particle/LavaParticle.cpp @@ -17,9 +17,9 @@ LavaParticle::LavaParticle(Level* level, const Vec3& pos) : m_vel *= 0.8f; m_vel.y = sharedRandom.nextFloat() * 0.4f + 0.05f; m_rCol = m_gCol = m_bCol = 1.0f; - field_104 = field_F0 = field_F0 * (0.2f + 2 * sharedRandom.nextFloat()); - field_DC = PTI_LAVA; - field_EC = int(16.0f / (0.2f + 0.8f * Mth::random())); + field_104 = m_size = m_size * (0.2f + 2 * sharedRandom.nextFloat()); + m_tex = PTI_LAVA; + m_lifetime = int(16.0f / (0.2f + 0.8f * Mth::random())); } float LavaParticle::getBrightness(float unused) const @@ -31,11 +31,11 @@ void LavaParticle::tick() { m_oPos = m_pos; - field_E8++; - if (field_E8 > field_EC) + m_timer++; + if (m_timer > m_lifetime) remove(); - float a = float(field_E8) / float(field_EC); + float a = float(m_timer) / float(m_lifetime); float b = sharedRandom.nextFloat(); if (a < b) { @@ -55,7 +55,7 @@ void LavaParticle::tick() void LavaParticle::render(Tesselator& t, float f, float a, float b, float c, float d, float e) { - float mult = float(field_E8 + f) / float(field_EC); - field_F0 = field_104 * (1.0f - mult * mult); + float mult = float(m_timer + f) / float(m_lifetime); + m_size = field_104 * (1.0f - mult * mult); Particle::render(t, f, a, b, c, d, e); } diff --git a/source/world/particle/NoteParticle.cpp b/source/world/particle/NoteParticle.cpp new file mode 100644 index 000000000..149dee931 --- /dev/null +++ b/source/world/particle/NoteParticle.cpp @@ -0,0 +1,47 @@ +#include "Particle.hpp" + +NoteParticle::NoteParticle(Level* level, const Vec3& pos, const Vec3& dir, float scale) : + Particle(level, pos, Vec3::ZERO) +{ + m_vel.x *= 0.01f; + m_vel.y *= 0.01f; + m_vel.z *= 0.01f; + m_vel.y += 0.2f; + m_rCol = Mth::sin((dir.x + 0.0f) * M_PI * 2.0f) * 0.65f + 0.35f; + m_gCol = Mth::sin((dir.x + (1.0f / 3.0f)) * M_PI * 2.0f) * 0.65f + 0.35f; + m_bCol = Mth::sin((dir.x + (2.0f / 3.0f)) * M_PI * 2.0f) * 0.65f + 0.35f; + m_size *= 0.75f * scale; + m_oSize = m_size; + m_lifetime = 6; + m_bNoPhysics = false; + m_tex = PTI_NOTE; +} + +void NoteParticle::tick() +{ + m_oPos = m_pos; + + m_timer++; + if (m_timer > m_lifetime) + remove(); + + move(m_vel); + if (m_pos.y == m_oPos.y) + { + m_vel *= Vec3(1.1f, 1.0f, 1.1f); + } + + m_vel *= 0.66f; + + if (m_bOnGround) + { + m_vel *= Vec3(0.7f, 1.0f, 0.7f); + } +} + +void NoteParticle::render(Tesselator& t, float f, float a, float b, float c, float d, float e) +{ + float mult = float(m_timer + f) / float(m_lifetime) * 32.0f; + m_size = m_oSize * Mth::clamp(mult, 0.0f, 1.0f); + Particle::render(t, f, a, b, c, d, e); +} diff --git a/source/world/particle/Particle.cpp b/source/world/particle/Particle.cpp index 8b276c1db..e29863500 100644 --- a/source/world/particle/Particle.cpp +++ b/source/world/particle/Particle.cpp @@ -12,12 +12,12 @@ float Particle::xOff, Particle::yOff, Particle::zOff; void Particle::_init() { - field_DC = 0; + m_tex = 0; field_E0 = 0.0f; field_E4 = 0.0f; - field_E8 = 0; - field_EC = 0; - field_F0 = 0.0f; + m_timer = 0; + m_lifetime = 0; + m_size = 0.0f; field_F4 = 0.0f; m_rCol = 1.0f; m_gCol = 1.0f; @@ -45,8 +45,8 @@ Particle::Particle(Level* level, const Vec3& pos, const Vec3& dir) : Entity(leve field_E0 = 3.0f * sharedRandom.nextFloat(); field_E4 = 3.0f * sharedRandom.nextFloat(); - field_F0 = 2.0f * (0.5f + 0.5f * sharedRandom.nextFloat()); - field_EC = int(4.0f / (0.1f + 0.9f * sharedRandom.nextFloat())); + m_size = 2.0f * (0.5f + 0.5f * sharedRandom.nextFloat()); + m_lifetime = int(4.0f / (0.1f + 0.9f * sharedRandom.nextFloat())); } int Particle::getParticleTexture() @@ -57,7 +57,7 @@ int Particle::getParticleTexture() Particle* Particle::scale(float f) { setSize(0.2f * f, 0.2f * f); - field_F0 *= f; + m_size *= f; return this; } @@ -73,7 +73,7 @@ void Particle::render(Tesselator& t, float f, float a4, float a5, float a6, floa { constexpr float C_MAGIC_1 = 0.062438f; // @BUG: Slightly bigger than 1/16.0f - int texture = field_DC; + int texture = m_tex; int texX = texture % 16; if (texture < 0) texture += 15; @@ -86,11 +86,11 @@ void Particle::render(Tesselator& t, float f, float a4, float a5, float a6, floa float posZ = Mth::Lerp(m_oPos.z, m_pos.z, f) - zOff; float fBright = m_bIsUnlit ? 1.0f : getBrightness(f); - float sizeX = a4 * field_F0 * 0.1f; - float sizeY = a5 * field_F0 * 0.1f; - float sizeZ = a6 * field_F0 * 0.1f; - float siz2X = a7 * field_F0 * 0.1f; - float siz2Z = a8 * field_F0 * 0.1f; + float sizeX = a4 * m_size * 0.1f; + float sizeY = a5 * m_size * 0.1f; + float sizeZ = a6 * m_size * 0.1f; + float siz2X = a7 * m_size * 0.1f; + float siz2Z = a8 * m_size * 0.1f; t.color(m_rCol * fBright, m_gCol * fBright, m_bCol * fBright); t.vertexUV(posX - sizeX - siz2X, posY - sizeY, posZ - sizeZ - siz2Z, texU_1 + C_MAGIC_1, texV_1 + C_MAGIC_1); @@ -102,8 +102,8 @@ void Particle::render(Tesselator& t, float f, float a4, float a5, float a6, floa void Particle::tick() { m_oPos = m_pos; - field_E8++; - if (field_E8 >= field_EC) + m_timer++; + if (m_timer >= m_lifetime) remove(); m_vel.y -= field_F4 * 0.04f; diff --git a/source/world/particle/Particle.hpp b/source/world/particle/Particle.hpp index 85d4814bc..9d58ab074 100644 --- a/source/world/particle/Particle.hpp +++ b/source/world/particle/Particle.hpp @@ -23,7 +23,8 @@ enum eParticleTextureIndex { PTI_BUBBLE = 32, PTI_FLAME = 48, - PTI_LAVA + PTI_LAVA, + PTI_NOTE = 64 }; class Particle : public Entity @@ -45,12 +46,12 @@ class Particle : public Entity Particle* setPower(float); public: - int field_DC; + int m_tex; float field_E0; float field_E4; - int field_E8; - int field_EC; - float field_F0; + int m_timer; + int m_lifetime; + float m_size; float field_F4; float m_rCol; float m_gCol; @@ -138,3 +139,14 @@ class LavaParticle : public Particle public: float field_104; }; + +class NoteParticle : public Particle +{ +public: + NoteParticle(Level*, const Vec3& pos, const Vec3& dir, float scale = 2.0f); + void tick() override; + void render(Tesselator&, float, float, float, float, float, float) override; + +public: + float m_oSize; +}; \ No newline at end of file diff --git a/source/world/particle/RedDustParticle.cpp b/source/world/particle/RedDustParticle.cpp index bae587260..b7edf53a4 100644 --- a/source/world/particle/RedDustParticle.cpp +++ b/source/world/particle/RedDustParticle.cpp @@ -20,21 +20,21 @@ RedDustParticle::RedDustParticle(Level* level, const Vec3& pos, const Vec3& dir) m_gCol = f * dir.y * (Mth::random() * 0.2f + 0.8f); m_bCol = f * dir.z * (Mth::random() * 0.2f + 0.8f); - field_104 = field_F0 = field_F0 * 0.75f; + field_104 = m_size = m_size * 0.75f; m_bNoPhysics = false; - field_EC = int(8.0f / (0.2f + 0.8f * Mth::random())); + m_lifetime = int(8.0f / (0.2f + 0.8f * Mth::random())); } void RedDustParticle::render(Tesselator& t, float f, float a, float b, float c, float d, float e) { - float mult = 32.0f * (float(field_E8 + f) / float(field_EC)); + float mult = 32.0f * (float(m_timer + f) / float(m_lifetime)); if (mult < 0.0f) mult = 0.0f; if (mult > 1.0f) mult = 1.0f; - field_F0 = field_104 * mult; + m_size = field_104 * mult; Particle::render(t, f, a, b, c, d, e); } @@ -42,12 +42,12 @@ void RedDustParticle::tick() { m_oPos = m_pos; - field_E8++; - if (field_E8 > field_EC) + m_timer++; + if (m_timer > m_lifetime) remove(); m_vel.y += 0.004f; - field_DC = -8 * field_E8 / field_EC + 7; + m_tex = -8 * m_timer / m_lifetime + 7; move(m_vel); diff --git a/source/world/particle/SmokeParticle.cpp b/source/world/particle/SmokeParticle.cpp index 2c781a660..812e291ee 100644 --- a/source/world/particle/SmokeParticle.cpp +++ b/source/world/particle/SmokeParticle.cpp @@ -17,21 +17,21 @@ SmokeParticle::SmokeParticle(Level* level, const Vec3& pos, const Vec3& dir, flo m_bCol = m_gCol = m_rCol = Mth::random() * 0.5f; - field_104 = field_F0 = field_F0 * 0.75f * a9; + field_104 = m_size = m_size * 0.75f * a9; m_bNoPhysics = false; - field_EC = int((a9 * 8.0f) / (0.2f + 0.8f * Mth::random())); + m_lifetime = int((a9 * 8.0f) / (0.2f + 0.8f * Mth::random())); } void SmokeParticle::render(Tesselator& t, float f, float a, float b, float c, float d, float e) { - float mult = 32.0f * (float(field_E8 + f) / float(field_EC)); + float mult = 32.0f * (float(m_timer + f) / float(m_lifetime)); if (mult < 0.0f) mult = 0.0f; if (mult > 1.0f) mult = 1.0f; - field_F0 = field_104 * mult; + m_size = field_104 * mult; Particle::render(t, f, a, b, c, d, e); } @@ -39,12 +39,12 @@ void SmokeParticle::tick() { m_oPos = m_pos; - field_E8++; - if (field_E8 > field_EC) + m_timer++; + if (m_timer > m_lifetime) remove(); m_vel.y += 0.004f; - field_DC = -8 * field_E8 / field_EC + 7; + m_tex = -8 * m_timer / m_lifetime + 7; move(m_vel); diff --git a/source/world/particle/TerrainParticle.cpp b/source/world/particle/TerrainParticle.cpp index 3235e2b95..cf6b02f60 100644 --- a/source/world/particle/TerrainParticle.cpp +++ b/source/world/particle/TerrainParticle.cpp @@ -12,10 +12,10 @@ void TerrainParticle::_init(Tile* tile) { m_pTile = tile; - field_DC = tile->m_TextureFrame; + m_tex = tile->m_TextureFrame; field_F4 = tile->field_28; m_rCol = m_gCol = m_bCol = 0.6f; - field_F0 *= 0.5f; + m_size *= 0.5f; } TerrainParticle::TerrainParticle(Level* level, const Vec3& pos, Tile* tile) : @@ -36,7 +36,7 @@ TerrainParticle* TerrainParticle::init(const TilePos& tilePos, Facing::Name face face = Facing::DOWN; #endif - field_DC = m_pTile->getTexture(m_pLevel, tilePos, face); + m_tex = m_pTile->getTexture(m_pLevel, tilePos, face); if (m_pTile == Tile::grass && face != Facing::UP) return this; @@ -58,7 +58,7 @@ void TerrainParticle::render(Tesselator& t, float f, float a4, float a5, float a { constexpr float C_MAGIC_1 = 0.015609f; // @BUG: Slightly bigger than 1/64.0f - int texture = field_DC; + int texture = m_tex; int texX = texture % 16; if (texture < 0) texture += 15; @@ -71,11 +71,11 @@ void TerrainParticle::render(Tesselator& t, float f, float a4, float a5, float a float posZ = Mth::Lerp(m_oPos.z, m_pos.z, f) - zOff; float fBright = getBrightness(f); - float sizeX = a4 * field_F0 * 0.1f; - float sizeY = a5 * field_F0 * 0.1f; - float sizeZ = a6 * field_F0 * 0.1f; - float siz2X = a7 * field_F0 * 0.1f; - float siz2Z = a8 * field_F0 * 0.1f; + float sizeX = a4 * m_size * 0.1f; + float sizeY = a5 * m_size * 0.1f; + float sizeZ = a6 * m_size * 0.1f; + float siz2X = a7 * m_size * 0.1f; + float siz2Z = a8 * m_size * 0.1f; t.color(m_rCol * fBright, m_gCol * fBright, m_bCol * fBright); t.vertexUV(posX - sizeX - siz2X, posY - sizeY, posZ - sizeZ - siz2Z, texU_1 + C_MAGIC_1, texV_1 + C_MAGIC_1); diff --git a/source/world/phys/Vec3.hpp b/source/world/phys/Vec3.hpp index a3725336e..efb4333ae 100644 --- a/source/world/phys/Vec3.hpp +++ b/source/world/phys/Vec3.hpp @@ -123,6 +123,13 @@ class Vec3 (*this) += -b; } + void operator*=(const Vec3& b) + { + x *= b.x; + y *= b.y; + z *= b.z; + } + void operator+=(float f) { x += f; diff --git a/source/world/tile/ChestTile.cpp b/source/world/tile/ChestTile.cpp new file mode 100644 index 000000000..327bd0bf6 --- /dev/null +++ b/source/world/tile/ChestTile.cpp @@ -0,0 +1,217 @@ +#include "ChestTile.hpp" +#include "world/level/Level.hpp" +#include "world/CompoundContainer.hpp" +#include "world/tile/entity/ChestTileEntity.hpp" + +ChestTile::ChestTile(int id, int texture) : Tile(id, texture, Material::wood) +{ + setTicking(true); +} + +int ChestTile::getTexture(const LevelSource* level, const TilePos& pos, Facing::Name face) const +{ + if (face == Facing::UP || face == Facing::DOWN) { + return m_TextureFrame - 1; + } + + int id_north = level->getTile(pos.north()); + int id_south = level->getTile(pos.south()); + int id_west = level->getTile(pos.west()); + int id_east = level->getTile(pos.east()); + + bool isDoubleNS = (id_north == m_ID || id_south == m_ID); + bool isDoubleWE = (id_west == m_ID || id_east == m_ID); + + if (!isDoubleNS && !isDoubleWE) { + Facing::Name front = Facing::SOUTH; + + if (Tile::solid[id_north] && !Tile::solid[id_south]) + front = Facing::SOUTH; + else if (Tile::solid[id_south] && !Tile::solid[id_north]) + front = Facing::NORTH; + else if (Tile::solid[id_west] && !Tile::solid[id_east]) + front = Facing::EAST; + else if (Tile::solid[id_east] && !Tile::solid[id_west]) + front = Facing::WEST; + + return (face == front) ? m_TextureFrame + 1 : m_TextureFrame; + } + + bool left = false; + Facing::Name front = Facing::SOUTH; + + if (isDoubleWE) { + left = id_west == m_ID; + TilePos side = left ? pos.west() : pos.east(); + + int id_behind = level->getTile(side.north()); + int id_infront = level->getTile(side.south()); + + if ((Tile::solid[id_north] || Tile::solid[id_behind]) && !Tile::solid[id_south] && !Tile::solid[id_infront]) + front = Facing::SOUTH; + else if ((Tile::solid[id_south] || Tile::solid[id_infront]) && !Tile::solid[id_north] && !Tile::solid[id_behind]) + front = Facing::NORTH; + + if (front == Facing::SOUTH) left = !left; + if (face == front) return m_TextureFrame + (left ? 15 : 16); + if (face == Facing::OPPOSITE[front]) return m_TextureFrame + (left ? 32 : 31); + return m_TextureFrame; + } + + if (isDoubleNS) + { + front = Facing::EAST; + left = id_north == m_ID; + TilePos side = left ? pos.north() : pos.south(); + + int id_behind = level->getTile(side.west()); + int id_infront = level->getTile(side.east()); + + if ((Tile::solid[id_west] || Tile::solid[id_behind]) && !Tile::solid[id_east] && !Tile::solid[id_infront]) { + front = Facing::EAST; + } + else if ((Tile::solid[id_east] || Tile::solid[id_infront]) && !Tile::solid[id_west] && !Tile::solid[id_behind]) { + front = Facing::WEST; + left = !left; + } + + if (face == front) + return m_TextureFrame + (left ? 15 : 16); + if (face == Facing::OPPOSITE[front]) + return m_TextureFrame + (left ? 32 : 31); + return m_TextureFrame; + } + + return m_TextureFrame; +} + +int ChestTile::getTexture(Facing::Name face) const +{ + switch (face) { + case Facing::UP: + case Facing::DOWN: + return m_TextureFrame - 1; + case Facing::SOUTH: + return m_TextureFrame + 1; + default: + return m_TextureFrame; + } +} + +bool ChestTile::mayPlace(const Level* level, const TilePos& pos) const +{ + return !hasNeighbors(level, pos, 1); +} + +bool ChestTile::hasNeighbors(const Level* level, const TilePos& pos, int count) const +{ + int neighbors = 0; + for (int i = 0; i < 4; ++i) + { + Facing::Name f = static_cast(Facing::HORIZONTAL[i]); + TilePos relative = pos.relative(f); + if (level->getTile(relative) == m_ID) + { + neighbors++; + if (neighbors > count) return true; + + if (hasNeighbors(level, relative, 0)) return true; + } + } + + return false; +} + +void ChestTile::onRemove(Level* level, const TilePos& pos){ + ChestTileEntity* ent = static_cast(level->getTileEntity(pos)); + + if (!ent) + { + Tile::onRemove(level, pos); + } + + for (int slot = 0; slot < ent->getContainerSize(); ++slot) + { + ItemStack& item = ent->getItem(slot); + if (!item.isEmpty()) + { + TilePos offset( + (m_chestRandom.nextFloat() * 0.8f) + 0.1f, + (m_chestRandom.nextFloat() * 0.8f) + 0.1f, + (m_chestRandom.nextFloat() * 0.8f) + 0.1f + ); + + while (item.m_count > 0) + { + int toRemove = std::min(m_chestRandom.nextInt(21) + 10, static_cast(item.m_count)); + item.m_count -= toRemove; + ItemEntity* itemEnt = new ItemEntity(level, pos + offset, ItemStack(item.getItem()->m_itemID, toRemove, item.getAuxValue())); + float spread = 0.05f; + itemEnt->m_vel.x = (double)((float)m_chestRandom.nextGaussian() * spread); + itemEnt->m_vel.y = (double)((float)m_chestRandom.nextGaussian() * spread + 0.2f); + itemEnt->m_vel.z = (double)((float)m_chestRandom.nextGaussian() * spread); + level->addEntity(itemEnt); + } + } + } + + Tile::onRemove(level, pos); +} + +bool ChestTile::use(Level* level, const TilePos& pos, Player* player) +{ + if (level->m_bIsClientSide) + return true; + + if (level->isSolidTile(pos.above())) + return true; + + if (level->getTile(pos.west()) == m_ID && level->isSolidTile(pos + TilePos(-1, 1, 0))) + return true; + + if (level->getTile(pos.east()) == m_ID && level->isSolidTile(pos + TilePos(1, 1, 0))) + return true; + + if (level->getTile(pos.north()) == m_ID && level->isSolidTile(pos + TilePos(0, 1, -1))) + return true; + + if (level->getTile(pos.south()) == m_ID && level->isSolidTile(pos + TilePos(0, 1, 1))) + return true; + + TileEntity* tileEnt = level->getTileEntity(pos); + if (!tileEnt) + return false; + + ChestTileEntity* chest = static_cast(tileEnt); + Container* container = static_cast(chest); + for (int rel = Facing::NORTH; rel <= Facing::EAST; rel++) + { + TilePos relPos = pos.relative(static_cast(rel)); + if (level->getTile(pos.relative(static_cast(rel))) == m_ID) + { + TileEntity* nearbyTileEntity = level->getTileEntity(relPos); + if (!nearbyTileEntity) + continue; + + Container* nearbyContainer = static_cast(static_cast(nearbyTileEntity)); + if (rel % 2 == 0) + container = new CompoundContainer("Large chest", nearbyContainer, container); + else + container = new CompoundContainer("Large chest", container, nearbyContainer); + break; + } + } + + player->openContainer(container); + return true; +} + +bool ChestTile::hasTileEntity() const +{ + return true; +} + +TileEntity* ChestTile::newTileEntity() +{ + return new ChestTileEntity(); +} diff --git a/source/world/tile/ChestTile.hpp b/source/world/tile/ChestTile.hpp new file mode 100644 index 000000000..051081631 --- /dev/null +++ b/source/world/tile/ChestTile.hpp @@ -0,0 +1,20 @@ +#pragma once + +#include "Tile.hpp" + +class ChestTile : public Tile +{ +public: + ChestTile(int id, int texture); +public: + int getTexture(const LevelSource*, const TilePos& pos, Facing::Name face) const override; + int getTexture(Facing::Name face) const override; + bool mayPlace(const Level* level, const TilePos& pos) const override; + bool hasNeighbors(const Level* level, const TilePos& pos, int count) const; + void onRemove(Level* level, const TilePos& pos) override; + bool use(Level* level, const TilePos& pos, Player* var5) override; + bool hasTileEntity() const override; + TileEntity* newTileEntity() override; +public: + Random m_chestRandom; +}; \ No newline at end of file diff --git a/source/world/tile/CropsTile.cpp b/source/world/tile/CropsTile.cpp index cb361a031..9cd432ede 100644 --- a/source/world/tile/CropsTile.cpp +++ b/source/world/tile/CropsTile.cpp @@ -85,7 +85,7 @@ float CropsTile::getGrowthRate(Level* level, const TilePos& pos) if (diag || (hor && vert)) { - rate /= 2.0F; + rate /= 2.0f; } return rate; diff --git a/source/world/tile/FurnaceTile.cpp b/source/world/tile/FurnaceTile.cpp new file mode 100644 index 000000000..e5ea21f65 --- /dev/null +++ b/source/world/tile/FurnaceTile.cpp @@ -0,0 +1,194 @@ +#include "FurnaceTile.hpp" +#include "world/level/Level.hpp" +#include "entity/FurnaceTileEntity.hpp" + +bool FurnaceTile::keepInventory = false; + +FurnaceTile::FurnaceTile(int id, bool active) : Tile(id, TEXTURE_FURNACE_SIDE, Material::stone) +{ + setTicking(true); + m_active = active; +} + +int FurnaceTile::getTexture(const LevelSource* level, const TilePos& pos, Facing::Name face) const +{ + switch (face) + { + case Facing::UP: + case Facing::DOWN: + return m_TextureFrame + 17; + default: + { + int meta = level->getData(pos); + return face != meta ? m_TextureFrame : (m_active ? m_TextureFrame + 16 : m_TextureFrame - 1); + } + } +} + +void FurnaceTile::animateTick(Level* level, const TilePos& pos, Random* random) { + if (!m_active) + return; + + int data = level->getData(pos); + Vec3 particlePos(pos.x + 0.5f, pos.y + 0.0f + random->nextFloat() * 6.0f / 16.0f, pos.z + 0.5f); + const float outward = 0.52f; + float randomOffset = random->nextFloat() * 0.6f - 0.3f; + + if (random->nextFloat() < 0.1f) + level->playSound(Vec3(particlePos.x + 0.5f, particlePos.y + 0.5f, particlePos.z + 0.5f), "fire.fire_crackle", 1.0f, 1.0f); + + if (data == 4 || data == 5) + { + particlePos.x += data == 4 ? -outward : outward; + particlePos.z += randomOffset; + } + else if (data == 2 || data == 3) + { + particlePos.x += randomOffset; + particlePos.z += data == 2 ? -outward : outward; + } + + level->addParticle("smoke", particlePos); + level->addParticle("flame", particlePos); +} + +int FurnaceTile::getTexture(Facing::Name face) const +{ + switch (face) { + case Facing::UP: + case Facing::DOWN: + return m_TextureFrame + 17; + case Facing::SOUTH: + return (m_active) ? m_TextureFrame + 16 : m_TextureFrame - 1; + default: + return m_TextureFrame; + } +} + +void FurnaceTile::onPlace(Level* level, const TilePos& pos) +{ + Tile::onPlace(level, pos); + RecalculateLookDirection(level, pos); +} + +bool FurnaceTile::use(Level* level, const TilePos& pos, Player* player) +{ + if (player->isSneaking() && player->getSelectedItem()) + return false; + + if (level->m_bIsClientSide) + return true; + + player->openFurnace(static_cast(level->getTileEntity(pos))); + return true; +} + +void FurnaceTile::setPlacedBy(Level* level, const TilePos& pos, Mob* mob) +{ + int rot = Mth::floor(0.5f + (mob->m_rot.y * 4.0f / 360.0f)) & 3; + int data = 4; + + switch (rot) + { + case 0: + data = 2; + break; + case 1: + data = 5; + break; + case 2: + data = 3; + break; + } + + level->setData(pos, data); +} + +void FurnaceTile::onRemove(Level* level, const TilePos& pos) +{ + if (keepInventory) + return; + + TileEntity* tileEnt = level->getTileEntity(pos); + + if (!tileEnt) + { + return; // this has to be wrapped in a guard or the compiler screams, thanks -Wmisleading-indentation + } + + FurnaceTileEntity* furnace = static_cast(tileEnt); + for (int i = 0; i < furnace->getContainerSize(); ++i) + { + ItemStack& item = furnace->getItem(i); + if (item.isEmpty()) + continue; + + TilePos splitSource( + m_random.nextFloat() * 0.8f + 0.1f, + m_random.nextFloat() * 0.8f + 0.1f, + m_random.nextFloat() * 0.8f + 0.1f + ); + + while (item.m_count > 0) + { + int splitCount = std::min(m_random.nextInt(21) + 10, static_cast(item.m_count)); + item.m_count -= splitCount; + ItemEntity* itemEnt = new ItemEntity(level, splitSource + pos, ItemStack(item.getId(), splitCount, item.getAuxValue())); + + float deviation = 0.05f; + itemEnt->m_vel.x = (double)((float)m_random.nextGaussian() * deviation); + itemEnt->m_vel.y = (double)((float)m_random.nextGaussian() * deviation + 0.2f); + itemEnt->m_vel.z = (double)((float)m_random.nextGaussian() * deviation); + level->addEntity(itemEnt); + } + } +} + +void FurnaceTile::SetLit(bool lit, Level* level, const TilePos& pos) +{ + TileEntity* tileEntity = level->getTileEntity(pos); + int data = level->getData(pos); + + keepInventory = true; + level->setTile(pos, (lit) ? Tile::furnaceLit->m_ID : Tile::furnace->m_ID); + keepInventory = false; + + level->setData(pos, data); + tileEntity->clearRemoved(); + level->setTileEntity(pos, tileEntity); +} + +bool FurnaceTile::hasTileEntity() const +{ + return true; +} + +TileEntity* FurnaceTile::newTileEntity() +{ + return new FurnaceTileEntity(); +} + +int FurnaceTile::getResource(TileData, Random*) const +{ + return Tile::furnace->m_ID; +} + +void FurnaceTile::RecalculateLookDirection(Level* level, const TilePos& pos) +{ + TileID n = level->getTile(pos.north()); + TileID s = level->getTile(pos.south()); + TileID w = level->getTile(pos.west()); + TileID e = level->getTile(pos.east()); + uint8_t lookData = 3; + + if (Tile::solid[e] && !Tile::solid[w]) + lookData = 4; + else if (Tile::solid[w] && !Tile::solid[e]) + lookData = 5; + else if (Tile::solid[s] && !Tile::solid[n]) + lookData = 2; + else if (Tile::solid[n] && !Tile::solid[s]) + lookData = 3; + + level->setData(pos, lookData); +} diff --git a/source/world/tile/FurnaceTile.hpp b/source/world/tile/FurnaceTile.hpp new file mode 100644 index 000000000..bb0ac9359 --- /dev/null +++ b/source/world/tile/FurnaceTile.hpp @@ -0,0 +1,33 @@ +#pragma once + +#include "Tile.hpp" + +class FurnaceTile : public Tile +{ +public: + FurnaceTile(int id, bool active); + +public: + int getTexture(const LevelSource*, const TilePos& pos, Facing::Name face) const override; + void animateTick(Level* level, const TilePos& pos, Random* random) override; + int getTexture(Facing::Name face) const override; + void onPlace(Level* level, const TilePos& pos) override; + bool use(Level* level, const TilePos& pos, Player* player) override; + void setPlacedBy(Level*, const TilePos& pos, Mob*) override; + void onRemove(Level* level, const TilePos& pos) override; + bool hasTileEntity() const override; + TileEntity* newTileEntity() override; + int getResource(TileData, Random*) const override; + +public: + static void SetLit(bool lit, Level* level, const TilePos& pos); + static void RecalculateLookDirection(Level* level, const TilePos& pos); + +private: + Random m_random; + static bool keepInventory; + +public: + bool m_active; +}; + diff --git a/source/world/tile/MusicTile.cpp b/source/world/tile/MusicTile.cpp new file mode 100644 index 000000000..7842f458e --- /dev/null +++ b/source/world/tile/MusicTile.cpp @@ -0,0 +1,98 @@ +#include "MusicTile.hpp" +#include "world/level/Level.hpp" +#include "entity/MusicTileEntity.hpp" + +MusicTile::MusicTile(TileID id, int texture) : Tile(id, texture, Material::wood) +{ +} + +void MusicTile::neighborChanged(Level* level, const TilePos& pos, TileID tile) +{ + if (tile <= TILE_AIR || !Tile::tiles[tile]->isSignalSource()) + return; + + TileEntity* tileEnt = level->getTileEntity(pos); + if (!tileEnt) + return; + + bool signalProvided = level->hasDirectSignal(pos); + MusicTileEntity* musEnt = static_cast(tileEnt); + if (musEnt->m_bOn == signalProvided) + return; + + if (signalProvided) + musEnt->play(level, pos); + + musEnt->m_bOn = signalProvided; +} + +bool MusicTile::use(Level* level, const TilePos& pos, Player* player) +{ + if (player->isSneaking() && player->getSelectedItem()) + return false; + + if (level->m_bIsClientSide) + return true; + + TileEntity* tileEnt = level->getTileEntity(pos); + if (!tileEnt) + return false; + + MusicTileEntity* musEnt = static_cast(tileEnt); + + musEnt->tune(); + musEnt->play(level, pos); + return true; +} + +void MusicTile::attack(Level* level, const TilePos& pos, Player* player) +{ + if (level->m_bIsClientSide) + return; + + TileEntity* tileEnt = level->getTileEntity(pos); + if (!tileEnt) + return; + + (static_cast(tileEnt))->play(level, pos); +} + +bool MusicTile::hasTileEntity() const +{ + return true; +} + +TileEntity* MusicTile::newTileEntity() +{ + return new MusicTileEntity(); +} + +void MusicTile::triggerEvent(Level* level, const TileEvent& event) +{ + int instrument = event.b0; + int note = event.b1; + + + std::string soundType; + switch (instrument) + { + default: + soundType = "harp"; + break; + case 1: + soundType = "bd"; + break; + case 2: + soundType = "snare"; + break; + case 3: + soundType = "hat"; + break; + case 4: + soundType = "bassattack"; + break; + } + + level->playSound(event.pos + 0.5f, "note." + soundType, 3.0f, std::pow(2.0f, (note - 12) / 12.0f)); + level->addParticle("note", Vec3(event.pos.x + 0.5f, event.pos.y + 1.2f, event.pos.z + 0.5f), Vec3(note / 24.0f, 0, 0)); +} diff --git a/source/world/tile/MusicTile.hpp b/source/world/tile/MusicTile.hpp new file mode 100644 index 000000000..fcddfba82 --- /dev/null +++ b/source/world/tile/MusicTile.hpp @@ -0,0 +1,17 @@ +#pragma once + +#include "Tile.hpp" + +class MusicTile : public Tile +{ +public: + MusicTile(TileID id, int textureID); + +public: + void neighborChanged(Level*, const TilePos& pos, TileID tile) override; + bool use(Level*, const TilePos&, Player*) override; + void attack(Level*, const TilePos&, Player*) override; + bool hasTileEntity() const override; + void triggerEvent(Level*, const TileEvent&) override; + TileEntity* newTileEntity() override; +}; \ No newline at end of file diff --git a/source/world/tile/Tile.cpp b/source/world/tile/Tile.cpp index 7c5646745..4aea4c4b7 100644 --- a/source/world/tile/Tile.cpp +++ b/source/world/tile/Tile.cpp @@ -55,12 +55,12 @@ #include "RocketLauncherTile.hpp" //#include "RedStoneDustTile.hpp" #include "CraftingTableTile.hpp" -//#include "FurnaceTile.hpp" +#include "FurnaceTile.hpp" #include "TallGrass.hpp" #include "DeadBush.hpp" //#include "Fern.hpp" #include "CactusTile.hpp" -//#include "ChestTile.hpp" +#include "ChestTile.hpp" #include "PumpkinTile.hpp" #include "SoulSandTile.hpp" #include "GlowstoneTile.hpp" @@ -79,7 +79,7 @@ //#include "RedstoneTorchTile.hpp" //#include "CakeTile.hpp" //#include "DispenserTile.hpp" -//#include "MusicTile.hpp" +#include "MusicTile.hpp" //#include "RecordPlayerTile.hpp" //#include "TrapDoorTile.hpp" //#include "PortalTile.hpp" @@ -205,7 +205,7 @@ Tile* Tile::init() solid[m_ID] = isSolidRender(); lightBlock[m_ID] = isSolidRender() ? 255 : 0; translucent[m_ID] = m_pMaterial->blocksLight(); - isEntityTile[m_ID] = 0; + isEntityTile[m_ID] = hasTileEntity(); m_toolMask = Tool::NONE; m_requiredToolLevel = 0; @@ -263,6 +263,11 @@ bool Tile::mayPick(TileData data, bool y) const return mayPick(); } +bool Tile::hasTileEntity() const +{ + return false; +} + int Tile::getResource(TileData data, Random* pRandom) const { return m_ID; @@ -278,6 +283,11 @@ int Tile::getSpawnResourcesAuxValue(int x) const return 0; } +TileEntity* Tile::newTileEntity() +{ + return nullptr; +} + Tile* Tile::setToolTypes(unsigned int toolMask) { m_toolMask |= toolMask; @@ -786,6 +796,31 @@ void Tile::initTiles() ->setSoundType(Tile::SOUND_GRASS) ->setDescriptionId("crops"); + Tile::musicBlock = (new MusicTile(TILE_NOTE_BLOCK, TEXTURE_JUKEBOX_SIDE)) + ->init() + ->setSoundType(Tile::SOUND_WOOD) + ->setDestroyTime(0.8f) + ->setDescriptionId("musicBlock"); + + Tile::furnace = (new FurnaceTile(TILE_FURNACE, false)) + ->init() + ->setDestroyTime(3.5f) + ->setSoundType(Tile::SOUND_STONE) + ->setDescriptionId("furnace"); + + Tile::furnaceLit = (new FurnaceTile(TILE_FURNACE_LIT, true)) + ->init() + ->setLightEmission(14.0f / 16.0f) + ->setDestroyTime(3.5f) + ->setSoundType(Tile::SOUND_STONE) + ->setDescriptionId("furnace"); + + Tile::chest = (new ChestTile(TILE_CHEST, TEXTURE_CHEST_ONE_SIDE)) + ->init() + ->setDestroyTime(2.5f) + ->setSoundType(Tile::SOUND_WOOD) + ->setDescriptionId("chest"); + // Great Item::items[Tile::cloth->m_ID] = (new ClothItem(Tile::cloth->m_ID - C_MAX_TILES)) ->setDescriptionId("cloth"); @@ -963,12 +998,14 @@ void Tile::neighborChanged(Level* pLevel, const TilePos& pos, TileID tile) void Tile::onPlace(Level* pLevel, const TilePos& pos) { - + if (hasTileEntity()) + pLevel->setTileEntity(pos, newTileEntity()); } void Tile::onRemove(Level* pLevel, const TilePos& pos) { - + if (hasTileEntity()) + pLevel->removeTileEntity(pos); } bool Tile::containsX(const Vec3& v) @@ -1279,4 +1316,8 @@ Tile *Tile::web, *Tile::fence, *Tile::craftingTable, - *Tile::crops; + *Tile::crops, + *Tile::musicBlock, + *Tile::furnace, + *Tile::furnaceLit, + *Tile::chest; \ No newline at end of file diff --git a/source/world/tile/Tile.hpp b/source/world/tile/Tile.hpp index aad6af4c2..2a9bc0ad3 100644 --- a/source/world/tile/Tile.hpp +++ b/source/world/tile/Tile.hpp @@ -29,6 +29,7 @@ class Entity; class Mob; class Player; class LiquidTile; +class TileEntity; class Tile { @@ -76,6 +77,7 @@ class Tile virtual bool isSolidRender() const; virtual bool mayPick() const; virtual bool mayPick(TileData, bool) const; + virtual bool hasTileEntity() const; virtual bool mayPlace(const Level*, const TilePos& pos) const; virtual int getTickDelay() const; virtual void tick(Level*, const TilePos& pos, Random*); @@ -121,6 +123,7 @@ class Tile virtual Tile* setDestroyTime(float); virtual Tile* setTicking(bool); virtual int getSpawnResourcesAuxValue(int) const; + virtual TileEntity* newTileEntity(); Tile* setToolTypes(unsigned int toolMask); Tile* setToolLevel(int toolLevel); Tile* setToolTypesAndLevel(unsigned int toolMask, int toolLevel = 0); @@ -241,7 +244,11 @@ class Tile * web, * fence, * craftingTable, - * crops; + * crops, + * furnace, + * furnaceLit, + * musicBlock, + * chest; public: int m_TextureFrame; diff --git a/source/world/tile/entity/ChestTileEntity.cpp b/source/world/tile/entity/ChestTileEntity.cpp new file mode 100644 index 000000000..f72f78a4c --- /dev/null +++ b/source/world/tile/entity/ChestTileEntity.cpp @@ -0,0 +1,31 @@ +#include "ChestTileEntity.hpp" + +ChestTileEntity::ChestTileEntity() : SimpleContainer(27, "Chest") +{ + m_pType = TileEntityType::chest; +} + +void ChestTileEntity::load(const CompoundTag& tag) +{ + TileEntity::load(tag); + SimpleContainer::load(tag); +} + +void ChestTileEntity::save(CompoundTag& tag) const +{ + TileEntity::save(tag); + SimpleContainer::save(tag); +} + +bool ChestTileEntity::stillValid(Player* player) const +{ + if (m_pLevel->getTileEntity(m_pos) != this) + return false; + + return player->distanceToSqr(m_pos + 0.5f) <= 64.0f; +} + +void ChestTileEntity::setChanged() +{ + TileEntity::setChanged(); +} diff --git a/source/world/tile/entity/ChestTileEntity.hpp b/source/world/tile/entity/ChestTileEntity.hpp new file mode 100644 index 000000000..38137f631 --- /dev/null +++ b/source/world/tile/entity/ChestTileEntity.hpp @@ -0,0 +1,17 @@ +#pragma once + +#include "TileEntity.hpp" +#include "world/level/Level.hpp" +#include "world/inventory/SimpleContainer.hpp" + +class ChestTileEntity : public TileEntity, public SimpleContainer { +public: + ChestTileEntity(); + + void load(const CompoundTag& tag) override; + void save(CompoundTag& tag) const override; + + bool stillValid(Player* player) const override; + + virtual void setChanged() override; +}; diff --git a/source/world/tile/entity/FurnaceTileEntity.cpp b/source/world/tile/entity/FurnaceTileEntity.cpp new file mode 100644 index 000000000..1134d2736 --- /dev/null +++ b/source/world/tile/entity/FurnaceTileEntity.cpp @@ -0,0 +1,163 @@ +#include "FurnaceTileEntity.hpp" +#include "world/item/crafting/FurnaceRecipes.hpp" +#include "world/tile/FurnaceTile.hpp" + +#define C_BURN_TIME (200) + +FurnaceTileEntity::FurnaceTileEntity() + : SimpleContainer(3, "gui.furnace"), m_litTime(0), m_litDuration(0), m_tickCount(0) +{ + m_pType = TileEntityType::furnace; +} + +bool FurnaceTileEntity::_canBurn() +{ + if (getItem(0).isEmpty()) + return false; + + const ItemStack& result = FurnaceRecipes::singleton().getItemFor(this); + + // Not a furnace recipe + if (result.isEmpty()) + return false; + + // Nothing has started burning yet + if (getItem(2).isEmpty()) + return true; + + // Potential result/current result mismatch + if (getItem(2).getItem()->m_itemID != result.getItem()->m_itemID) + return false; + + if (getItem(2).m_count < getMaxStackSize() && getItem(2).m_count < getItem(2).getMaxStackSize()) + return true; + + // Return if result slot is full + return getItem(2).m_count < result.getMaxStackSize(); +} + +void FurnaceTileEntity::_burn() +{ + if (!_canBurn()) + return; + + const ItemStack& result = FurnaceRecipes::singleton().getItemFor(this); + + // Either set the item in the result slot to the item, or add what's already there + if (getItem(2).isEmpty()) + setItem(2, result); + else if (getItem(2).getItem()->m_itemID == result.getItem()->m_itemID) + ++getItem(2).m_count; + + // Decrement burning item + if (getItem(0).m_count > 0) + --getItem(0).m_count; + + // No more burning item + if (getItem(0).m_count <= 0) + setItem(0, ItemStack::EMPTY); +} + +void FurnaceTileEntity::tick() +{ + if (m_pLevel->m_bIsClientSide) + return; + + bool wasBurning = m_litTime > 0; + bool changed = false; + + if (m_litTime > 0) + --m_litTime; + + if (m_litTime == 0 && _canBurn()) + { + m_litDuration = m_litTime = FurnaceRecipes::singleton().getBurnDuration(getItem(1)); + if (m_litTime > 0) + { + changed = true; + if (!getItem(1).isEmpty()) + { + --getItem(1).m_count; + if (!getItem(1).m_count) + setItem(1, ItemStack::EMPTY); + } + } + } + + if (isLit() && _canBurn()) + { + ++m_tickCount; + if (m_tickCount == C_BURN_TIME) + { + m_tickCount = 0; + _burn(); + changed = true; + } + } + else + m_tickCount = 0; + + bool isBurning = m_litTime > 0; + if (isBurning != wasBurning) + { + changed = true; + FurnaceTile::SetLit(isBurning, m_pLevel, m_pos); + } + + if (changed) + setChanged(); +} + +bool FurnaceTileEntity::stillValid(Player* player) const +{ + if (m_pLevel->getTileEntity(m_pos) != this) + return false; + + return player->distanceToSqr(m_pos + 0.5f) <= 64.0; +} + +void FurnaceTileEntity::setChanged() +{ + TileEntity::setChanged(); +} + +void FurnaceTileEntity::load(const CompoundTag& tag) +{ + TileEntity::load(tag); + SimpleContainer::load(tag); + + m_litTime = tag.getInt16("BurnTime"); + m_tickCount = tag.getInt16("CookTime"); + m_litDuration = FurnaceRecipes::singleton().getBurnDuration(getItem(1)); +} + +void FurnaceTileEntity::save(CompoundTag& tag) const +{ + TileEntity::save(tag); + tag.putInt16("BurnTime", m_litTime); + tag.putInt16("CookTime", m_tickCount); + SimpleContainer::save(tag); +} + +std::string FurnaceTileEntity::getName() const +{ + return "Furnace"; +} + +int FurnaceTileEntity::getBurnProgress(int height) +{ + return m_tickCount * height / C_BURN_TIME; +} + +int FurnaceTileEntity::getLitProgress(int height) +{ + if (m_litDuration == 0) + m_litDuration = C_BURN_TIME; + + return m_litTime * height / m_litDuration; +} + +bool FurnaceTileEntity::isLit() +{ + return m_litTime > 0; +} \ No newline at end of file diff --git a/source/world/tile/entity/FurnaceTileEntity.hpp b/source/world/tile/entity/FurnaceTileEntity.hpp new file mode 100644 index 000000000..55108282f --- /dev/null +++ b/source/world/tile/entity/FurnaceTileEntity.hpp @@ -0,0 +1,33 @@ +#pragma once + +#include "TileEntity.hpp" +#include "world/inventory/SimpleContainer.hpp" +#include "world/level/Level.hpp" + +class FurnaceTileEntity : public SimpleContainer, public TileEntity +{ +public: + FurnaceTileEntity(); + +private: + bool _canBurn(); + void _burn(); + +public: + void tick() override; + bool stillValid(Player* player) const override; + void setChanged() override; + void load(const CompoundTag& tag) override; + void save(CompoundTag& tag) const override; + std::string getName() const override; + +public: + int getBurnProgress(int height); + int getLitProgress(int height); + bool isLit(); + +public: + int m_litTime; + int m_litDuration; + int m_tickCount; +}; diff --git a/source/world/tile/entity/MusicTileEntity.cpp b/source/world/tile/entity/MusicTileEntity.cpp new file mode 100644 index 000000000..d38630bd7 --- /dev/null +++ b/source/world/tile/entity/MusicTileEntity.cpp @@ -0,0 +1,46 @@ +#include "MusicTileEntity.hpp" +#include "world/level/Level.hpp" + +MusicTileEntity::MusicTileEntity() : TileEntity(), m_note(0), m_bOn(false) +{ + m_pType = TileEntityType::noteblock; +} + +void MusicTileEntity::load(const CompoundTag& tag) +{ + TileEntity::load(tag); + m_note = static_cast(Mth::clamp(tag.getInt8("note"), 0, 24)); +} + +void MusicTileEntity::save(CompoundTag& tag) const +{ + TileEntity::save(tag); + tag.putInt8("note", m_note); +} + +void MusicTileEntity::tune() +{ + m_note = (m_note + 1) % 25; + setChanged(); +} + +void MusicTileEntity::play(Level* pLevel, const TilePos& pos) +{ + // noteblocks only play if the block above them is air + if (pLevel->getMaterial(TilePos(pos.x, pos.y + 1, pos.z)) != Material::air) + return; + + int instrument = 0; + Material* below = pLevel->getMaterial(TilePos(pos.x, pos.y - 1, pos.z)); + + if (below == Material::stone) + instrument = 1; + else if (below == Material::sand) + instrument = 2; + else if (below == Material::glass) + instrument = 3; + else if (below == Material::wood) + instrument = 4; + + pLevel->tileEvent(TileEvent(pos, instrument, m_note)); +} \ No newline at end of file diff --git a/source/world/tile/entity/MusicTileEntity.hpp b/source/world/tile/entity/MusicTileEntity.hpp new file mode 100644 index 000000000..9a8a1a60a --- /dev/null +++ b/source/world/tile/entity/MusicTileEntity.hpp @@ -0,0 +1,21 @@ +#pragma once + +#include "TileEntity.hpp" + +class MusicTileEntity : public TileEntity +{ +public: + MusicTileEntity(); + +public: + void load(const CompoundTag& tag) override; + void save(CompoundTag& tag) const override; + +public: + void tune(); + void play(Level* pLevel, const TilePos& pos); + +public: + uint8_t m_note; + bool m_bOn; +}; \ No newline at end of file diff --git a/source/world/tile/entity/TileEntity.cpp b/source/world/tile/entity/TileEntity.cpp new file mode 100644 index 000000000..9cd7b79ad --- /dev/null +++ b/source/world/tile/entity/TileEntity.cpp @@ -0,0 +1,106 @@ +#include "TileEntity.hpp" +#include "common/Logger.hpp" +#include "world/level/Level.hpp" + +TileEntity::TileEntity() : m_bRemove(false), m_pLevel(nullptr) +{ +} + +TileEntity::~TileEntity() +{ +} + +TileEntity* TileEntity::LoadTileEntity(const CompoundTag& tag) +{ + if (!tag.contains("id")) + { + LOG_W("Skipping TileEntity with no id!"); + return nullptr; + } + + std::string id = tag.getString("id"); + const TileEntityType* type = TileEntityType::GetType(id); + + if (!type) + { + LOG_I("Skipping TileEntity with id %s", id.c_str()); + return nullptr; + } + + TileEntity* newEnt = type->newTileEntity(); + newEnt->load(tag); + return newEnt; +} + +void TileEntity::load(const CompoundTag& tag) +{ + m_pos.x = tag.getInt32("x"); + m_pos.y = tag.getInt32("y"); + m_pos.z = tag.getInt32("z"); +} + +void TileEntity::save(CompoundTag& tag) const +{ + tag.putString("id", getId()); + tag.putInt32("x", m_pos.x); + tag.putInt32("y", m_pos.y); + tag.putInt32("z", m_pos.z); +} + +void TileEntity::tick() +{ +} + +Packet* TileEntity::getUpdatePacket() +{ + return nullptr; +} + +bool TileEntity::isRemoved() const +{ + return m_bRemove; +} + +void TileEntity::setRemoved() +{ + m_bRemove = true; +} + +void TileEntity::clearRemoved() +{ + m_bRemove = false; +} + +int TileEntity::getData() const +{ + return m_pLevel->getData(m_pos); +} + +void TileEntity::setData(int data) +{ + m_pLevel->setData(m_pos, data); +} + +void TileEntity::setChanged() +{ + // @TODO: tileEntityChanged + /* + if (m_pLevel) + m_pLevel->tileEntityChanged(m_pos, this); + */ +} + +float TileEntity::distanceToSqr(const Vec3& vec) const +{ + return (m_pos + 0.5f).distanceToSqr(vec); +} + +Tile* TileEntity::getTile() const +{ + return Tile::tiles[m_pLevel->getTile(m_pos)]; +} + +std::string TileEntity::getId() const +{ + return (m_pType) ? m_pType->getName() : "Unknown"; +} \ No newline at end of file diff --git a/source/world/tile/entity/TileEntity.hpp b/source/world/tile/entity/TileEntity.hpp new file mode 100644 index 000000000..2a3666fc7 --- /dev/null +++ b/source/world/tile/entity/TileEntity.hpp @@ -0,0 +1,50 @@ +#pragma once + +#include "TileEntityType.hpp" +#include "world/level/TilePos.hpp" +#include "world/phys/Vec3.hpp" +#include "nbt/CompoundTag.hpp" + +class Tile; +class Level; +class Packet; + +class TileEntity +{ +public: + TileEntity(); + virtual ~TileEntity(); + +protected: + virtual std::string getId() const; + +public: + virtual void load(const CompoundTag& tag); + virtual void save(CompoundTag& tag) const; + virtual void tick(); + virtual Packet* getUpdatePacket(); + const TileEntityType* getType() const; + virtual int getData() const; + virtual Tile* getTile() const; + +public: + static TileEntity* LoadTileEntity(const CompoundTag& tag); + +public: + float distanceToSqr(const Vec3& vec) const; + bool isRemoved() const; + void setRemoved(); + void clearRemoved(); + + void setData(int data); + void setChanged(); + +protected: + const TileEntityType* m_pType; + bool m_bRemove; + +public: + Level* m_pLevel; + TilePos m_pos; +}; + diff --git a/source/world/tile/entity/TileEntityType.cpp b/source/world/tile/entity/TileEntityType.cpp new file mode 100644 index 000000000..8ee14b727 --- /dev/null +++ b/source/world/tile/entity/TileEntityType.cpp @@ -0,0 +1,51 @@ +#include "TileEntityType.hpp" +#include "FurnaceTileEntity.hpp" +#include "ChestTileEntity.hpp" +#include "MusicTileEntity.hpp" +// #include "MobSpawnerTileEntity.hpp" +// #include "DispenserTileEntity.hpp" +// #include "SignTileEntity.hpp" +// #include "RecordPlayerTileEntity.hpp" +// #include "PistonMovingTileEntity.hpp" + +std::map TileEntityType::_types; + +TileEntityType* TileEntityType::furnace; +TileEntityType* TileEntityType::chest; +TileEntityType* TileEntityType::noteblock; + +TileEntityType::TileEntityType(const std::string& name, CreateFunction func) : _name(name), _function(func) +{ +} + +const std::string& TileEntityType::getName() const +{ + return _name; +} + +TileEntity* TileEntityType::newTileEntity() const +{ + return _function(); +} + +void TileEntityType::InitTileEntities() +{ + furnace = RegisterTileEntity("Furnace"); + chest = RegisterTileEntity("Chest"); + noteblock = RegisterTileEntity("Music"); +} + +const TileEntityType* TileEntityType::GetType(const std::string& name) +{ + return _types[name]; +} + +void TileEntityType::TeardownTileEntities() +{ + // delete all heap allocated tile entity types (furnace, chest, etc.) + for (std::map::const_iterator it = _types.begin(); it != _types.end(); ++it) + { + SAFE_DELETE(it->second); + } + _types.clear(); +} \ No newline at end of file diff --git a/source/world/tile/entity/TileEntityType.hpp b/source/world/tile/entity/TileEntityType.hpp new file mode 100644 index 000000000..3a981cf6c --- /dev/null +++ b/source/world/tile/entity/TileEntityType.hpp @@ -0,0 +1,50 @@ +#pragma once + +#include +#include +#include + +class Level; +class TileEntity; + +class TileEntityType +{ +public: + typedef TileEntity* (*CreateFunction)(); + +public: + TileEntityType(const std::string& name, CreateFunction func); + +public: + const std::string& getName() const; + TileEntity* newTileEntity() const; + +public: + static void InitTileEntities(); + static const TileEntityType* GetType(const std::string& name); + static void TeardownTileEntities(); + +private: + static std::map _types; + +private: + std::string _name; + CreateFunction _function; + +public: + static TileEntityType* furnace; + static TileEntityType* chest; + static TileEntityType* noteblock; + + template + static TileEntity* CreateType() { return new T(); } + +public: + template + static TileEntityType* RegisterTileEntity(const std::string& name) + { + TileEntityType* type = new TileEntityType(name, &TileEntityType::CreateType); + _types[type->_name] = type; + return type; + } +}; \ No newline at end of file From 3054cbd410c68aa6f1ea5ce8c885caf3657b998b Mon Sep 17 00:00:00 2001 From: hellosammu Date: Sun, 15 Feb 2026 17:59:03 -0500 Subject: [PATCH 02/63] furnace rot fix, useTouchscreen/isTouchscreen --- source/client/app/Minecraft.cpp | 21 ++++++----- source/client/app/Minecraft.hpp | 6 ++-- source/client/app/NinecraftApp.cpp | 2 +- source/client/gui/Gui.cpp | 17 +++++---- source/client/gui/Gui.hpp | 1 + source/client/gui/Screen.cpp | 2 +- source/client/gui/components/OptionList.cpp | 3 +- source/client/gui/components/TextBox.cpp | 19 ++++++---- .../gui/screens/inventory/ContainerScreen.cpp | 6 ++-- source/client/model/models/SpiderModel.cpp | 6 ++-- source/client/options/Options.cpp | 16 +++++++-- source/client/options/Options.hpp | 14 ++++++-- source/client/player/input/Keyboard.cpp | 3 +- source/client/renderer/Chunk.hpp | 2 ++ source/client/renderer/Font.cpp | 6 ++-- source/client/renderer/GameRenderer.cpp | 2 +- source/client/renderer/LevelRenderer.cpp | 3 +- source/client/renderer/LogoRenderer.cpp | 6 ++-- source/client/renderer/TileRenderer.cpp | 6 ++-- .../renderer/entity/HumanoidMobRenderer.cpp | 6 ++++ .../renderer/entity/HumanoidMobRenderer.hpp | 1 + .../client/renderer/entity/ItemRenderer.cpp | 6 ++-- .../client/renderer/entity/SheepRenderer.cpp | 2 +- source/client/sound/SoundData.cpp | 6 ++-- source/client/sound/SoundEngine.hpp | 1 + source/common/Logger.cpp | 5 ++- source/common/Random.cpp | 9 +++-- source/network/packets/AddMobPacket.hpp | 2 ++ .../network/packets/SetEntityDataPacket.hpp | 2 ++ source/renderer/GL/GL.cpp | 24 ++++++++----- source/world/entity/Creeper.cpp | 3 +- source/world/entity/MobSpawner.hpp | 2 +- source/world/entity/Player.cpp | 12 ++++--- source/world/entity/Player.hpp | 2 +- source/world/inventory/ContainerMenu.cpp | 12 ++++--- source/world/item/BowItem.cpp | 3 +- source/world/item/ItemStack.hpp | 1 + .../levelgen/chunk/RandomLevelSource.cpp | 6 ++-- .../level/levelgen/chunk/TestChunkSource.cpp | 6 ++-- source/world/level/path/BinaryHeap.cpp | 30 ++++++++-------- source/world/level/path/BinaryHeap.hpp | 12 ++++--- source/world/tile/ChestTile.cpp | 35 +++++++++++-------- source/world/tile/ChestTile.hpp | 2 ++ source/world/tile/CraftingTableTile.cpp | 3 +- source/world/tile/FurnaceTile.cpp | 23 ++++++------ source/world/tile/MusicTile.cpp | 2 +- source/world/tile/PumpkinTile.cpp | 6 ++-- 47 files changed, 231 insertions(+), 134 deletions(-) diff --git a/source/client/app/Minecraft.cpp b/source/client/app/Minecraft.cpp index cd3f8986a..749a6a493 100644 --- a/source/client/app/Minecraft.cpp +++ b/source/client/app/Minecraft.cpp @@ -173,12 +173,12 @@ GameMode* Minecraft::_createGameMode(GameType gameType, Level& level) } } -void Minecraft::_reloadInput() +void Minecraft::reloadInput() { if (m_pInputHolder) delete m_pInputHolder; - if (isTouchscreen()) + if (useTouchscreen()) { m_pInputHolder = new TouchInputHolder(this, getOptions()); } @@ -206,7 +206,7 @@ void Minecraft::_reloadInput() m_pLocalPlayer->m_pMoveInput = m_pInputHolder->getMoveInput(); } - getOptions()->field_19 = !isTouchscreen(); + getOptions()->m_bUseMouseToBreak = !useTouchscreen(); } int Minecraft::getLicenseId() @@ -246,7 +246,7 @@ void Minecraft::grabMouse() // This will call grabMouse again, so why are we calling it here? //setScreen(nullptr); - if (useController() || isTouchscreen()) + if (useController() || useTouchscreen()) return; // don't actually try to grab the mouse platform()->setMouseGrabbed(true); @@ -254,7 +254,7 @@ void Minecraft::grabMouse() void Minecraft::recenterMouse() { - if (useController() || isTouchscreen()) + if (useController() || useTouchscreen()) return; platform()->recenterMouse(); @@ -364,9 +364,14 @@ bool Minecraft::isTouchscreen() const return m_bIsTouchscreen; } +bool Minecraft::useTouchscreen() const +{ + return isTouchscreen() && !getOptions()->m_bUseController.get(); +} + bool Minecraft::useSplitControls() const { - return !m_bIsTouchscreen || getOptions()->m_splitControls.get(); + return !useTouchscreen() || getOptions()->m_splitControls.get(); } bool Minecraft::useController() const @@ -625,7 +630,7 @@ void Minecraft::tickInput() #endif } - if (getOptions()->field_19) + if (!getOptions()->m_bUseMouseToBreak) continue; // @TODO: Replace with KeyboardBuildInput @@ -672,7 +677,7 @@ void Minecraft::tickMouse() * This is exactly what Minecraft Java does too **/ - if (useController() || isTouchscreen()) + if (useController() || useTouchscreen()) return; // don't actually try to recenter the mouse if (platform()->getRecenterMouseEveryTick()) // just for SDL1 diff --git a/source/client/app/Minecraft.hpp b/source/client/app/Minecraft.hpp index edd594852..6a112aaad 100644 --- a/source/client/app/Minecraft.hpp +++ b/source/client/app/Minecraft.hpp @@ -44,9 +44,6 @@ class Minecraft : public App void _resetPlayer(Player* player); GameMode* _createGameMode(GameType gameType, Level& level); -protected: - void _reloadInput(); - public: int getLicenseId(); void setScreen(Screen * pScreen); @@ -79,11 +76,13 @@ class Minecraft : public App void handlePointerPressedButtonRelease(); void handleKeyboardClosed(); void resetInput(); + void reloadInput(); void sendMessage(const std::string& message); void respawnPlayer(); void freeResources(bool bCopyMap); std::string getVersionString(const std::string& str = Util::EMPTY_STRING) const; bool isTouchscreen() const; + bool useTouchscreen() const; bool useSplitControls() const; bool useController() const; @@ -118,6 +117,7 @@ class Minecraft : public App private: // Value provided by the OS static float _renderScaleMultiplier; + public: static float getRenderScaleMultiplier() { return _renderScaleMultiplier; } static void setRenderScaleMultiplier(float value) { _renderScaleMultiplier = value; } diff --git a/source/client/app/NinecraftApp.cpp b/source/client/app/NinecraftApp.cpp index b805ddcd1..0a16d0d3d 100644 --- a/source/client/app/NinecraftApp.cpp +++ b/source/client/app/NinecraftApp.cpp @@ -105,7 +105,7 @@ void NinecraftApp::_initInput() m_bIsTouchscreen = platform()->isTouchscreen(); getOptions()->m_bUseController.set(platform()->hasGamepad()); getOptions()->loadControls(); - _reloadInput(); + reloadInput(); } void NinecraftApp::_updateStats() diff --git a/source/client/gui/Gui.cpp b/source/client/gui/Gui.cpp index d8e94fcdf..5e4784d4f 100644 --- a/source/client/gui/Gui.cpp +++ b/source/client/gui/Gui.cpp @@ -259,8 +259,6 @@ void Gui::renderSlot(int slot, int x, int y, float f) ItemRenderer::singleton().renderGuiItem(m_pMinecraft->m_pFont, m_pMinecraft->m_pTextures, item, x, y, true); } - - //ItemRenderer::renderGuiItemDecorations(m_pMinecraft->m_pFont, m_pMinecraft->m_pTextures, item, x, y); } void Gui::renderSlotOverlay(int slot, int x, int y, float f) @@ -311,7 +309,8 @@ void Gui::handleClick(int clickID, int mouseX, int mouseY) if (slot == -1) return; - if (m_pMinecraft->isTouchscreen() && slot == getNumSlots() - 1) + // Final slot on touch opens inventory + if (m_pMinecraft->useTouchscreen() && slot == getNumSlots() - 1) { if (m_pMinecraft->m_pGameMode->isSurvivalType()) m_pMinecraft->setScreen(new InventoryScreen(m_pMinecraft->m_pLocalPlayer)); @@ -360,7 +359,7 @@ void Gui::handleKeyPressed(int keyCode) if (slotL || slotR) { int maxItems = getNumSlots() - 1; - if (m_pMinecraft->isTouchscreen()) + if (m_pMinecraft->useTouchscreen()) maxItems--; SlotID* slot = &m_pMinecraft->m_pLocalPlayer->m_pInventory->m_selectedSlot; @@ -672,7 +671,7 @@ void Gui::renderToolBar(float f, float alpha) textures->loadAndBindTexture(C_BLOCKS_NAME); - int diff = mc->isTouchscreen(); + int diff = mc->useTouchscreen(); int slotX = -hotbarWidth / 2 + 3; for (int i = 0; i < nSlots - diff; i++) @@ -694,8 +693,8 @@ void Gui::renderToolBar(float f, float alpha) field_A3C = false; - // blit the "more items" button - if (mc->isTouchscreen()) + // blit the "more items" button if using touch + if (mc->useTouchscreen()) { textures->loadAndBindTexture(C_TERRAIN_NAME); blit(hotbarWidth / 2 - 19, -19, 208, 208, 16, 16, 0, 0); @@ -704,7 +703,7 @@ void Gui::renderToolBar(float f, float alpha) int Gui::getNumSlots() { - if (m_pMinecraft->isTouchscreen()) + if (m_pMinecraft->useTouchscreen()) return 6; return 9; @@ -712,7 +711,7 @@ int Gui::getNumSlots() int Gui::getNumUsableSlots() { - return getNumSlots() - m_pMinecraft->isTouchscreen(); + return getNumSlots() - m_pMinecraft->useTouchscreen(); } RectangleArea Gui::getRectangleArea(bool b) diff --git a/source/client/gui/Gui.hpp b/source/client/gui/Gui.hpp index 9957d2f65..156f13788 100644 --- a/source/client/gui/Gui.hpp +++ b/source/client/gui/Gui.hpp @@ -42,6 +42,7 @@ class Gui : public GuiComponent private: static bool _isVignetteAvailable; + public: static bool isVignetteAvailable() { return _isVignetteAvailable; } static void setIsVignetteAvailable(bool value) { _isVignetteAvailable = value; } diff --git a/source/client/gui/Screen.cpp b/source/client/gui/Screen.cpp index 230a85921..7aec2e65e 100644 --- a/source/client/gui/Screen.cpp +++ b/source/client/gui/Screen.cpp @@ -393,7 +393,7 @@ void Screen::pointerPressed(const MenuPointer& pointer, MouseButtonType btn) // { m_pClickedElement = element; - if (!m_pMinecraft->isTouchscreen()) + if (!m_pMinecraft->useTouchscreen()) { if (_useController()) m_pMinecraft->m_pSoundEngine->playUI(C_SOUND_UI_PRESS); diff --git a/source/client/gui/components/OptionList.cpp b/source/client/gui/components/OptionList.cpp index 062f32f5e..26e7afa1f 100644 --- a/source/client/gui/components/OptionList.cpp +++ b/source/client/gui/components/OptionList.cpp @@ -173,7 +173,8 @@ void OptionList::initControlsMenu() if (!m_pMinecraft->isTouchscreen()) m_items[idxSplit]->setEnabled(false); - m_items[idxController]->setEnabled(false); + if (!m_pMinecraft->m_pPlatform->hasGamepad()) + m_items[idxController]->setEnabled(false); } void OptionList::initVideoMenu() diff --git a/source/client/gui/components/TextBox.cpp b/source/client/gui/components/TextBox.cpp index f18371498..36d64344d 100644 --- a/source/client/gui/components/TextBox.cpp +++ b/source/client/gui/components/TextBox.cpp @@ -149,7 +149,8 @@ bool TextBox::pointerPressed(Minecraft* pMinecraft, const MenuPointer& pointer) #ifndef HANDLE_CHARS_SEPARATELY -char TextBox::guessCharFromKey(int key) { +char TextBox::guessCharFromKey(int key) +{ bool bShiftPressed = m_pParent->m_pMinecraft->platform()->shiftPressed(); char chr = '\0'; if (key >= AKEYCODE_A && key <= AKEYCODE_Z) @@ -218,13 +219,15 @@ void TextBox::handleButtonPress(Minecraft* pMinecraft, int key) #ifndef HANDLE_CHARS_SEPARATELY char guess = guessCharFromKey(key); - if (guess != '\0') { + if (guess != '\0') + { handleTextChar(guess); return; } #endif - switch (key) { + switch (key) + { case AKEYCODE_DEL: { // handled elsewhere, do not dupe @@ -298,7 +301,8 @@ void TextBox::handleTextChar(Minecraft* pMinecraft, int k) if (!hasFocus()) return; - switch (k) { + switch (k) + { case '\b': // BACKSPACE case '\x7f': // DELETE { @@ -546,10 +550,13 @@ void TextBox::recalculateScroll() } else { - if (m_scrollPos == int(m_text.length())) { + if (m_scrollPos == int(m_text.length())) + { LOG_W("Text Box Is Too Small"); break; - } else { + } + else + { m_scrollPos++; } } diff --git a/source/client/gui/screens/inventory/ContainerScreen.cpp b/source/client/gui/screens/inventory/ContainerScreen.cpp index 991e74357..98d0b447d 100644 --- a/source/client/gui/screens/inventory/ContainerScreen.cpp +++ b/source/client/gui/screens/inventory/ContainerScreen.cpp @@ -234,14 +234,14 @@ void ContainerScreen::render(float partialTicks) void ContainerScreen::pointerPressed(const MenuPointer& pointer, MouseButtonType button) { Screen::pointerPressed(pointer, button); - if (m_pMinecraft->isTouchscreen()) return; + if (m_pMinecraft->useTouchscreen()) return; slotClicked(pointer, button); } void ContainerScreen::pointerReleased(const MenuPointer& pointer, MouseButtonType button) { Screen::pointerReleased(pointer, button); - if (m_pMinecraft->isTouchscreen() && m_timeSlotDragged < 5) + if (m_pMinecraft->useTouchscreen() && m_timeSlotDragged < 5) slotClicked(pointer, button); m_timeSlotDragged = 0; } @@ -253,7 +253,7 @@ void ContainerScreen::handlePointerPressed(bool isPressed) m_timeSlotDragged++; else m_timeSlotDragged = 0; - if (m_pMinecraft->isTouchscreen() && m_timeSlotDragged % 5 == 0) + if (m_pMinecraft->useTouchscreen() && m_timeSlotDragged % 5 == 0) { slotClicked(m_menuPointer, MOUSE_BUTTON_RIGHT); } diff --git a/source/client/model/models/SpiderModel.cpp b/source/client/model/models/SpiderModel.cpp index a3293ecb4..677798049 100644 --- a/source/client/model/models/SpiderModel.cpp +++ b/source/client/model/models/SpiderModel.cpp @@ -57,7 +57,8 @@ SpiderModel::~SpiderModel() { } -void SpiderModel::render(float time, float r, float bob, float yRot, float xRot, float scale) { +void SpiderModel::render(float time, float r, float bob, float yRot, float xRot, float scale) +{ setupAnim(time, r, bob, yRot, xRot, scale); m_head.render(scale); @@ -74,7 +75,8 @@ void SpiderModel::render(float time, float r, float bob, float yRot, float xRot, m_leg7.render(scale); } -void SpiderModel::setupAnim(float time, float r, float bob, float yRot, float xRot, float scale) { +void SpiderModel::setupAnim(float time, float r, float bob, float yRot, float xRot, float scale) +{ m_head.m_rot.y = yRot / 57.295776f; m_head.m_rot.x = xRot / 57.295776f; diff --git a/source/client/options/Options.cpp b/source/client/options/Options.cpp index 6cbc4bead..546f53a58 100644 --- a/source/client/options/Options.cpp +++ b/source/client/options/Options.cpp @@ -41,11 +41,11 @@ void Options::_initDefaultValues() field_16 = 0; field_240 = 1; field_1C = "Default"; - field_19 = 1; + m_bUseMouseToBreak = true; #ifdef ORIGINAL_CODE m_viewDistance.set(2); m_thirdPerson.set(0); - field_19 = 0; + m_bUseMouseToBreak = false; #endif // Force this on until we get a proper UI @@ -59,7 +59,7 @@ static UITheme GetDefaultUiTheme(Minecraft* mc) #if MC_PLATFORM_XBOX360 return UI_CONSOLE; #else - return mc->isTouchscreen() ? UI_POCKET : UI_JAVA; + return (mc->useTouchscreen() || mc->isTouchscreen()) ? UI_POCKET : UI_JAVA; #endif } @@ -126,6 +126,7 @@ Options::Options(Minecraft* mc, const std::string& folderPath) : add(m_playerName); add(m_debugText); add(m_lang); + add(m_bUseController); add(m_uiTheme); add(m_logoType); add(m_hudSize); @@ -803,3 +804,12 @@ void UIThemeOption::apply() m_pMinecraft->getOptions()->m_logoType.apply(); } } + +void ControllerOption::apply() +{ + // @TODO: This works but ultimately needs to be a multi-select (KBM, controller, touch) instead of a single option. + // Either that or figure out an automatic way to switch between them based on input like how Minecraft Bedrock does + // For now, I just wanted to be able to switch to controller input on mobile devices. + if (m_pMinecraft && m_pMinecraft->m_pInputHolder) + m_pMinecraft->reloadInput(); +} \ No newline at end of file diff --git a/source/client/options/Options.hpp b/source/client/options/Options.hpp index 99bacdf1a..1fe458dae 100644 --- a/source/client/options/Options.hpp +++ b/source/client/options/Options.hpp @@ -214,6 +214,14 @@ class GraphicsOption : public BoolOption void apply() override; }; +class ControllerOption : public BoolOption +{ +public: + ControllerOption(const std::string& key, const std::string& name, bool initial = true) : BoolOption(key, name, initial) {} + + void apply() override; +}; + class FancyGraphicsOption : public GraphicsOption { public: @@ -329,6 +337,7 @@ class Options { public: struct KeyBind; + private: static bool _hasResourcePack(const ResourcePack& pack, ResourcePackStack& packs); static void _tryAddResourcePack(const std::string& name, ResourcePackStack& packs); @@ -358,6 +367,7 @@ class Options void _initDefaultValues(); void _load(); AsyncTask _saveAsync(); + public: Options(Minecraft*, const std::string& folderPath = ""); @@ -399,7 +409,7 @@ class Options uint8_t field_16; FancyGraphicsOption m_fancyGraphics; AOOption m_ambientOcclusion; - uint8_t field_19; // use Mouse as input for breaking + bool m_bUseMouseToBreak; std::string field_1C; ValuesOption m_difficulty; BoolOption m_hideGui; @@ -419,7 +429,7 @@ class Options GraphicsOption m_fancyGrass; GraphicsOption m_biomeColors; BoolOption m_splitControls; - BoolOption m_bUseController; + ControllerOption m_bUseController; BoolOption m_dynamicHand; BoolOption m_menuPanorama; GuiScaleOption m_guiScale; diff --git a/source/client/player/input/Keyboard.cpp b/source/client/player/input/Keyboard.cpp index 562003650..0f82cc3e8 100644 --- a/source/client/player/input/Keyboard.cpp +++ b/source/client/player/input/Keyboard.cpp @@ -18,9 +18,8 @@ Keyboard::KeyState Keyboard::_states[KEYBOARD_STATES_SIZE]; void Keyboard::feed(KeyState state, int key) { // Prevent Crashes - if (key >= KEYBOARD_STATES_SIZE || key < 0) { + if (key >= KEYBOARD_STATES_SIZE || key < 0) return; - } _inputs.push_back(KeyboardAction(key, state)); diff --git a/source/client/renderer/Chunk.hpp b/source/client/renderer/Chunk.hpp index 957ef0446..aea9643c1 100644 --- a/source/client/renderer/Chunk.hpp +++ b/source/client/renderer/Chunk.hpp @@ -59,8 +59,10 @@ class Chunk bool field_54; RenderChunk m_renderChunks[Tile::RENDER_LAYERS_COUNT]; Tesselator* m_pTesselator; + private: int m_lists; + public: bool m_bCompiled; bool m_bDirty; diff --git a/source/client/renderer/Font.cpp b/source/client/renderer/Font.cpp index 6ec81196f..e66f98b6e 100644 --- a/source/client/renderer/Font.cpp +++ b/source/client/renderer/Font.cpp @@ -138,7 +138,8 @@ void Font::drawOutlinedString(const std::string& str, int x, int y, const Color& for (int yi = 0; yi < 3; ++yi) { int t1 = translations[yi]; - if (t != 0 || t1 != 0) { + if (t != 0 || t1 != 0) + { MatrixStack::Ref matrix = MatrixStack::World.push(); matrix->translate(Vec3(t, t1, 0)); drawScalable(str, x, y, outlineColor, scale, false); @@ -290,7 +291,8 @@ std::vector Font::split(const std::string& text, int maxWidth) std::istringstream iss(paragraph); std::string word; - while (iss >> word) { + while (iss >> word) + { std::string testLine = currentLine.empty() ? word : currentLine + " " + word; if (width(testLine) <= maxWidth) diff --git a/source/client/renderer/GameRenderer.cpp b/source/client/renderer/GameRenderer.cpp index d71fd3d4e..c265d6b5e 100644 --- a/source/client/renderer/GameRenderer.cpp +++ b/source/client/renderer/GameRenderer.cpp @@ -643,7 +643,7 @@ void GameRenderer::render(const Timer& timer) int mouseY = -9999; bool bMouseData = false; - if (m_pMinecraft->isTouchscreen()) + if (m_pMinecraft->useTouchscreen()) { int pointerId = Multitouch::getFirstActivePointerIdExThisUpdate(); if (pointerId >= 0) diff --git a/source/client/renderer/LevelRenderer.cpp b/source/client/renderer/LevelRenderer.cpp index 98a311c52..f9c47bc95 100644 --- a/source/client/renderer/LevelRenderer.cpp +++ b/source/client/renderer/LevelRenderer.cpp @@ -1163,7 +1163,8 @@ bool LevelRenderer::updateDirtyChunks(const Entity& camera, bool b) if (--j <= 0) continue; - for (int k = j; --k != 0;) { + for (int k = j; --k != 0;) + { pChunks[k - 1] = pChunks[k]; } diff --git a/source/client/renderer/LogoRenderer.cpp b/source/client/renderer/LogoRenderer.cpp index c5dac8dc3..e9cdbbfeb 100644 --- a/source/client/renderer/LogoRenderer.cpp +++ b/source/client/renderer/LogoRenderer.cpp @@ -414,8 +414,10 @@ Tile* TitleTile::getRandomTile(Tile* except1, Tile* except2) for (;;) { id = _random.nextInt(256); - for (int i = 0; i < _tileBlockListSize; i++) { - if (_tileBlockList[i] == id) { + for (int i = 0; i < _tileBlockListSize; i++) + { + if (_tileBlockList[i] == id) + { // N.B. Air does not have a tile id = TILE_AIR; break; diff --git a/source/client/renderer/TileRenderer.cpp b/source/client/renderer/TileRenderer.cpp index 575742689..ed07fc37b 100644 --- a/source/client/renderer/TileRenderer.cpp +++ b/source/client/renderer/TileRenderer.cpp @@ -1168,7 +1168,8 @@ bool TileRenderer::tesselateFenceInWorld(Tile* tile, const TilePos& pos) bool connectsHorizontally = tileWest || tileEast; bool connectsVertically = tileNorth || tileSouth; - if (!connectsHorizontally && !connectsVertically) { + if (!connectsHorizontally && !connectsVertically) + { connectsHorizontally = true; } @@ -2793,7 +2794,8 @@ void TileRenderer::renderTile(const FullTile& tile, const mce::MaterialPtr& mate float v6 = v5 * 2.0f; for (int i = 0; i < 4; i++) { - switch (i) { + switch (i) + { case 0: tileType->setShape(0.5f - v6, 0.0f, 0.0f, 0.5f + v6, 1.0f, v6 * 2.0f); break; case 1: tileType->setShape(0.5f - v6, 0.0f, 1.0f - (v6 * 2.0f), 0.5f + v6, 1.0f, 1.0f); break; case 2: tileType->setShape(0.5f - v5, 1.0f - v5 * 3.0f, -v5 * 2.0f, 0.5f + v5, 1.0f - v5, 1.0f + v5 * 2.0f); break; diff --git a/source/client/renderer/entity/HumanoidMobRenderer.cpp b/source/client/renderer/entity/HumanoidMobRenderer.cpp index c448e50c7..31f1d6c26 100644 --- a/source/client/renderer/entity/HumanoidMobRenderer.cpp +++ b/source/client/renderer/entity/HumanoidMobRenderer.cpp @@ -203,3 +203,9 @@ void HumanoidMobRenderer::renderHand(const Entity& entity, float a) } #endif } + +void HumanoidMobRenderer::scale(const Mob &mob, Matrix &matrix, float a) +{ + // players are actually 15/16ths the size than any other bipedal mob + matrix.scale((mob.isPlayer()) ? (15.0f / 16.0f) : 1.0f); +} diff --git a/source/client/renderer/entity/HumanoidMobRenderer.hpp b/source/client/renderer/entity/HumanoidMobRenderer.hpp index bcb072c89..62b900098 100644 --- a/source/client/renderer/entity/HumanoidMobRenderer.hpp +++ b/source/client/renderer/entity/HumanoidMobRenderer.hpp @@ -21,6 +21,7 @@ class HumanoidMobRenderer : public MobRenderer virtual void onGraphicsReset() override; void renderHand(const Entity& entity, float a); + void scale(const Mob& mob, Matrix& matrix, float a); public: HumanoidModel* m_pHumanoidModel; diff --git a/source/client/renderer/entity/ItemRenderer.cpp b/source/client/renderer/entity/ItemRenderer.cpp index aa3d9973f..e55794ba5 100644 --- a/source/client/renderer/entity/ItemRenderer.cpp +++ b/source/client/renderer/entity/ItemRenderer.cpp @@ -192,7 +192,8 @@ void ItemRenderer::renderGuiItemOverlay(Font* font, Textures* textures, ItemStac return; // Draw damage amount - if (item.isDamaged()) { + if (item.isDamaged()) + { int duraWidth = ceilf(13.0f - static_cast(item.getDamageValue()) * 13.0f / static_cast(item.getMaxDamage())); int duraPercent = ceilf(255.0f - static_cast(item.getDamageValue()) * 255.0f / static_cast(item.getMaxDamage())); @@ -207,7 +208,8 @@ void ItemRenderer::renderGuiItemOverlay(Font* font, Textures* textures, ItemStac blitRect(t, x + 2, y + 13, duraWidth, 1, duraColor); } - if (item.m_count <= 1) { + if (item.m_count <= 1) + { return; } diff --git a/source/client/renderer/entity/SheepRenderer.cpp b/source/client/renderer/entity/SheepRenderer.cpp index 3e864a296..ed2ef91b8 100644 --- a/source/client/renderer/entity/SheepRenderer.cpp +++ b/source/client/renderer/entity/SheepRenderer.cpp @@ -15,7 +15,7 @@ int SheepRenderer::prepareArmor(const Mob& mob, int layer, float a) const Sheep& sheep = (const Sheep&)mob; if (layer == 0 && !sheep.isSheared()) { - bindTexture("/mob/sheep_fur.png"); + bindTexture("mob/sheep_fur.png"); float brightness = sheep.getBrightness(a); int color = sheep.getColor(); currentShaderColor = Sheep::COLOR[color]; diff --git a/source/client/sound/SoundData.cpp b/source/client/sound/SoundData.cpp index 44c6bb1ed..23ab30a8c 100644 --- a/source/client/sound/SoundData.cpp +++ b/source/client/sound/SoundData.cpp @@ -81,11 +81,13 @@ bool SoundDesc::_load(const char* category, const char *name) } location.path = "sound/" + std::string(name) + ".pcm"; ret = _loadPcm(location); - if (!ret) { + if (!ret) + { m_codecType = AudioCodec::NONE; LOG_W("Failed to load sound \"%s\"!", name); return false; - } else + } + else return true; } diff --git a/source/client/sound/SoundEngine.hpp b/source/client/sound/SoundEngine.hpp index 620a108e0..e01b33f4e 100644 --- a/source/client/sound/SoundEngine.hpp +++ b/source/client/sound/SoundEngine.hpp @@ -43,6 +43,7 @@ class SoundEngine public: SoundSystem* m_pSoundSystem; + private: SoundRepository m_sounds; SoundPathRepository m_songs; diff --git a/source/common/Logger.cpp b/source/common/Logger.cpp index a98c4559f..d0115bc6e 100644 --- a/source/common/Logger.cpp +++ b/source/common/Logger.cpp @@ -15,11 +15,10 @@ Logger* Logger::singleton() void Logger::setSingleton(Logger* logger) { // Stick with the first output handle we get - if (!m_singleton) { + if (!m_singleton) m_singleton = logger; - } else { + else m_singleton->print(LOG_ERR, "Logging already setup!"); - } } Logger::~Logger() diff --git a/source/common/Random.cpp b/source/common/Random.cpp index 66c9b30f8..8ea1c3763 100644 --- a/source/common/Random.cpp +++ b/source/common/Random.cpp @@ -35,7 +35,8 @@ void Random::setSeed(int32_t seed) void Random::init_genrand(uint32_t s) { mt[0] = s & 0xffffffffUL; - for (mti = 1; mti < N; mti++) { + for (mti = 1; mti < N; mti++) + { mt[mti] = (1812433253UL * (mt[mti - 1] ^ (mt[mti - 1] >> 30)) + mti); /* See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier. */ @@ -65,11 +66,13 @@ uint32_t Random::genrand_int32() if (mti == N+1) /* if init_genrand() has not been called, */ init_genrand(5489UL); /* a default initial seed is used */ - for (kk=0;kk> 1) ^ mag01[y & 0x1UL]; } - for (;kk> 1) ^ mag01[y & 0x1UL]; } diff --git a/source/network/packets/AddMobPacket.hpp b/source/network/packets/AddMobPacket.hpp index 0fc9d397e..8d311e986 100644 --- a/source/network/packets/AddMobPacket.hpp +++ b/source/network/packets/AddMobPacket.hpp @@ -17,11 +17,13 @@ class AddMobPacket : public Packet void write(RakNet::BitStream&) override; void read(RakNet::BitStream&) override; const SynchedEntityData::ItemsArray& getUnpackedData() const { return m_unpack; } + public: int32_t m_entityId; int32_t m_entityTypeId; Vec3 m_pos; Vec2 m_rot; + private: SynchedEntityData m_entityData; SynchedEntityData::ItemsArray m_unpack; diff --git a/source/network/packets/SetEntityDataPacket.hpp b/source/network/packets/SetEntityDataPacket.hpp index 917e42e2b..cd2b18e4a 100644 --- a/source/network/packets/SetEntityDataPacket.hpp +++ b/source/network/packets/SetEntityDataPacket.hpp @@ -17,9 +17,11 @@ class SetEntityDataPacket : public Packet void write(RakNet::BitStream&) override; void read(RakNet::BitStream&) override; const SynchedEntityData::ItemsArray& getUnpackedData() const { return m_packedItems; } + public: int32_t m_entityId; bool m_bIsIncoming; + private: SynchedEntityData::ItemsArray m_packedItems; }; diff --git a/source/renderer/GL/GL.cpp b/source/renderer/GL/GL.cpp index 230895f46..12ffd16ad 100644 --- a/source/renderer/GL/GL.cpp +++ b/source/renderer/GL/GL.cpp @@ -157,25 +157,29 @@ int glhInvertMatrixf2(float* m, float* out) r2[3] -= m2 * s; r3[3] -= m3 * s; s = r0[4]; - if (s != 0.0f) { + if (s != 0.0f) + { r1[4] -= m1 * s; r2[4] -= m2 * s; r3[4] -= m3 * s; } s = r0[5]; - if (s != 0.0f) { + if (s != 0.0f) + { r1[5] -= m1 * s; r2[5] -= m2 * s; r3[5] -= m3 * s; } s = r0[6]; - if (s != 0.0f) { + if (s != 0.0f) + { r1[6] -= m1 * s; r2[6] -= m2 * s; r3[6] -= m3 * s; } s = r0[7]; - if (s != 0.0f) { + if (s != 0.0f) + { r1[7] -= m1 * s; r2[7] -= m2 * s; r3[7] -= m3 * s; @@ -195,22 +199,26 @@ int glhInvertMatrixf2(float* m, float* out) r2[3] -= m2 * r1[3]; r3[3] -= m3 * r1[3]; s = r1[4]; - if (0.0f != s) { + if (0.0f != s) + { r2[4] -= m2 * s; r3[4] -= m3 * s; } s = r1[5]; - if (0.0f != s) { + if (0.0f != s) + { r2[5] -= m2 * s; r3[5] -= m3 * s; } s = r1[6]; - if (0.0f != s) { + if (0.0f != s) + { r2[6] -= m2 * s; r3[6] -= m3 * s; } s = r1[7]; - if (0.0f != s) { + if (0.0f != s) + { r2[7] -= m2 * s; r3[7] -= m3 * s; } diff --git a/source/world/entity/Creeper.cpp b/source/world/entity/Creeper.cpp index 5046ff694..e55485e5b 100644 --- a/source/world/entity/Creeper.cpp +++ b/source/world/entity/Creeper.cpp @@ -76,7 +76,8 @@ void Creeper::checkHurtTarget(Entity* pEnt, float f) { setSwellDir(-1); m_swell--; - if (m_swell < 0) { + if (m_swell < 0) + { m_swell = 0; } } diff --git a/source/world/entity/MobSpawner.hpp b/source/world/entity/MobSpawner.hpp index fd29acaf3..3d7316255 100644 --- a/source/world/entity/MobSpawner.hpp +++ b/source/world/entity/MobSpawner.hpp @@ -24,7 +24,7 @@ class MobSpawner { public: TilePos getRandomPosWithin(Level& level, int chunkX, int chunkZ); void tick(Level& level, bool allowHostile, bool allowFriendly); -private: +private: std::set chunksToPoll; }; \ No newline at end of file diff --git a/source/world/entity/Player.cpp b/source/world/entity/Player.cpp index d7e0cff66..34c37381b 100644 --- a/source/world/entity/Player.cpp +++ b/source/world/entity/Player.cpp @@ -318,7 +318,8 @@ void Player::readAdditionalSaveData(const CompoundTag& tag) m_dimension = tag.getInt32("Dimension"); //m_sleepTimer = tag.getInt32("SleepTimer"); - if (tag.contains("SpawnX") && tag.contains("SpawnY") && tag.contains("SpawnZ")) { + if (tag.contains("SpawnX") && tag.contains("SpawnY") && tag.contains("SpawnZ")) + { setRespawnPos(TilePos( static_cast(tag.getInt32("SpawnX")), static_cast(tag.getInt32("SpawnY")), static_cast(tag.getInt32("SpawnZ")))); @@ -383,7 +384,8 @@ void Player::attack(Entity* pEnt) if (!item.isEmpty() && isMob) { item.hurtEnemy((Mob*)pEnt, this); - if (item.m_count <= 0) { + if (item.m_count <= 0) + { item.snap(this); removeSelectedItem(); } @@ -575,9 +577,11 @@ void Player::interact(Entity* pEnt) return; ItemStack& item = getSelectedItem(); - if (!item.isEmpty()) { + if (!item.isEmpty()) + { item.interactEnemy(static_cast(pEnt)); - if (item.m_count <= 0) { + if (item.m_count <= 0) + { item.snap(this); removeSelectedItem(); } diff --git a/source/world/entity/Player.hpp b/source/world/entity/Player.hpp index 1a25f7228..ed63daee9 100644 --- a/source/world/entity/Player.hpp +++ b/source/world/entity/Player.hpp @@ -45,7 +45,7 @@ class Player : public Mob public: void reset() override; void remove() override; - float getHeadHeight() const override { return 0.12f; /*@HUH: what ?*/ } + float getHeadHeight() const override { return 0.12f; } int getMaxHealth() const override { return 20; } bool isShootable() const override { return true; } bool isPlayer() const override { return true; } diff --git a/source/world/inventory/ContainerMenu.cpp b/source/world/inventory/ContainerMenu.cpp index 8c083c908..7a549e7b0 100644 --- a/source/world/inventory/ContainerMenu.cpp +++ b/source/world/inventory/ContainerMenu.cpp @@ -338,17 +338,21 @@ void ContainerMenu::setAll(const std::vector& items) } } -void ContainerMenu::setData(int id, int value) { +void ContainerMenu::setData(int id, int value) +{ } -uint16_t ContainerMenu::backup(Inventory*) { +uint16_t ContainerMenu::backup(Inventory*) +{ return ++m_changeUid; } -void ContainerMenu::deleteBackup(uint16_t) { +void ContainerMenu::deleteBackup(uint16_t) +{ } -void ContainerMenu::rollbackToBackup(uint16_t) { +void ContainerMenu::rollbackToBackup(uint16_t) +{ } bool ContainerMenu::isSynched(Player* player) const diff --git a/source/world/item/BowItem.cpp b/source/world/item/BowItem.cpp index 53ebdce5d..5b7ef84f1 100644 --- a/source/world/item/BowItem.cpp +++ b/source/world/item/BowItem.cpp @@ -13,9 +13,8 @@ ItemStack* BowItem::use(ItemStack* inst, Level* level, Mob* user) const if (!user->isPlayer() || static_cast(user)->isCreative() || static_cast(user)->m_pInventory->removeResource(Item::arrow->m_itemID)) { level->playSound(user, "random.bow", 1.0f, 1.0f / (level->m_random.nextFloat() * 0.4f + 0.8f)); - if (!level->m_bIsClientSide) { + if (!level->m_bIsClientSide) level->addEntity(new Arrow(level, user)); - } } return inst; diff --git a/source/world/item/ItemStack.hpp b/source/world/item/ItemStack.hpp index 53db80153..39586fe5a 100644 --- a/source/world/item/ItemStack.hpp +++ b/source/world/item/ItemStack.hpp @@ -125,6 +125,7 @@ class ItemStack public: int16_t m_count; int m_popTime; + private: int16_t m_auxValue; CompoundTag* m_userData; diff --git a/source/world/level/levelgen/chunk/RandomLevelSource.cpp b/source/world/level/levelgen/chunk/RandomLevelSource.cpp index 1908668f1..222a48533 100644 --- a/source/world/level/levelgen/chunk/RandomLevelSource.cpp +++ b/source/world/level/levelgen/chunk/RandomLevelSource.cpp @@ -320,11 +320,13 @@ void RandomLevelSource::buildSurfaces(const ChunkPos& pos, TileID* tiles, Biome* { byte1 = pBiome->field_20; byte2 = pBiome->field_21; - if (flag1) { + if (flag1) + { byte1 = 0; byte2 = Tile::gravel->m_ID; } - if (flag) { + if (flag) + { byte1 = Tile::sand->m_ID; byte2 = Tile::sand->m_ID; } diff --git a/source/world/level/levelgen/chunk/TestChunkSource.cpp b/source/world/level/levelgen/chunk/TestChunkSource.cpp index d0f5bde69..48b72e22f 100644 --- a/source/world/level/levelgen/chunk/TestChunkSource.cpp +++ b/source/world/level/levelgen/chunk/TestChunkSource.cpp @@ -59,7 +59,8 @@ LevelChunk* TestChunkSource::generateChunk(const ChunkPos& pos) *p = TILE_BEDROCK; //else if (j == 0 || k == 0) // *p = TILE_AIR; - else if (i == 65) { + else if (i == 65) + { /*if (rand() % 10 == 0) *p = TILE_ROSE; else if (rand() % 10 == 0) @@ -69,7 +70,8 @@ LevelChunk* TestChunkSource::generateChunk(const ChunkPos& pos) } else if (i > 64) *p = TILE_AIR; - else if (i == 64) { + else if (i == 64) + { //if ((j + k) % 2 == 0) *p = TILE_GRASS; } diff --git a/source/world/level/path/BinaryHeap.cpp b/source/world/level/path/BinaryHeap.cpp index 5eed80453..07faf7fd7 100644 --- a/source/world/level/path/BinaryHeap.cpp +++ b/source/world/level/path/BinaryHeap.cpp @@ -39,12 +39,12 @@ void BinaryHeap::inlined0(int num) Node* var2 = m_items[num]; int var4; - for (float var3 = var2->field_C; num > 0; num = var4) { + for (float var3 = var2->field_C; num > 0; num = var4) + { var4 = (num - 1) >> 1; Node* var5 = m_items[var4]; - if (var3 >= var5->field_C) { + if (var3 >= var5->field_C) break; - } m_items[num] = var5; var5->field_0 = num; @@ -59,39 +59,41 @@ void BinaryHeap::downHeap(int num) Node* var2 = m_items[num]; float var3 = var2->field_C; - while (true) { + while (true) + { int var4 = 1 + (num << 1); int var5 = var4 + 1; - if (var4 >= m_count) { + if (var4 >= m_count) break; - } Node* var6 = m_items[var4]; float var7 = var6->field_C; Node* var8; float var9; - if (var5 >= m_count) { + if (var5 >= m_count) + { var8 = nullptr; var9 = std::numeric_limits::infinity(); } - else { + else + { var8 = m_items[var5]; var9 = var8->field_C; } - if (var7 < var9) { - if (var7 >= var3) { + if (var7 < var9) + { + if (var7 >= var3) break; - } m_items[num] = var6; var6->field_0 = num; num = var4; } - else { - if (var9 >= var3) { + else + { + if (var9 >= var3) break; - } m_items[num] = var8; var8->field_0 = num; diff --git a/source/world/level/path/BinaryHeap.hpp b/source/world/level/path/BinaryHeap.hpp index e0142b685..57fb67525 100644 --- a/source/world/level/path/BinaryHeap.hpp +++ b/source/world/level/path/BinaryHeap.hpp @@ -29,7 +29,8 @@ class BinaryHeap void inlined0(int i); void downHeap(int i); - Node* removeTop() { + Node* removeTop() + { Node* pNode = m_items[0]; m_items[0] = m_items[--m_count]; m_items[m_count] = 0; @@ -41,15 +42,18 @@ class BinaryHeap return pNode; } - void clear() { + void clear() + { m_count = 0; } - int size() const { + int size() const + { return m_count; } - void setDistance(Node* pNode, float distance) { + void setDistance(Node* pNode, float distance) + { float oldDistance = pNode->field_C; pNode->field_C = distance; diff --git a/source/world/tile/ChestTile.cpp b/source/world/tile/ChestTile.cpp index 327bd0bf6..ca8f42ad9 100644 --- a/source/world/tile/ChestTile.cpp +++ b/source/world/tile/ChestTile.cpp @@ -10,9 +10,8 @@ ChestTile::ChestTile(int id, int texture) : Tile(id, texture, Material::wood) int ChestTile::getTexture(const LevelSource* level, const TilePos& pos, Facing::Name face) const { - if (face == Facing::UP || face == Facing::DOWN) { + if (face == Facing::UP || face == Facing::DOWN) return m_TextureFrame - 1; - } int id_north = level->getTile(pos.north()); int id_south = level->getTile(pos.south()); @@ -22,7 +21,8 @@ int ChestTile::getTexture(const LevelSource* level, const TilePos& pos, Facing:: bool isDoubleNS = (id_north == m_ID || id_south == m_ID); bool isDoubleWE = (id_west == m_ID || id_east == m_ID); - if (!isDoubleNS && !isDoubleWE) { + if (!isDoubleNS && !isDoubleWE) + { Facing::Name front = Facing::SOUTH; if (Tile::solid[id_north] && !Tile::solid[id_south]) @@ -40,7 +40,8 @@ int ChestTile::getTexture(const LevelSource* level, const TilePos& pos, Facing:: bool left = false; Facing::Name front = Facing::SOUTH; - if (isDoubleWE) { + if (isDoubleWE) + { left = id_west == m_ID; TilePos side = left ? pos.west() : pos.east(); @@ -52,9 +53,12 @@ int ChestTile::getTexture(const LevelSource* level, const TilePos& pos, Facing:: else if ((Tile::solid[id_south] || Tile::solid[id_infront]) && !Tile::solid[id_north] && !Tile::solid[id_behind]) front = Facing::NORTH; - if (front == Facing::SOUTH) left = !left; - if (face == front) return m_TextureFrame + (left ? 15 : 16); - if (face == Facing::OPPOSITE[front]) return m_TextureFrame + (left ? 32 : 31); + if (front == Facing::SOUTH) + left = !left; + if (face == front) + return m_TextureFrame + (left ? 15 : 16); + if (face == Facing::OPPOSITE[front]) + return m_TextureFrame + (left ? 32 : 31); return m_TextureFrame; } @@ -67,10 +71,10 @@ int ChestTile::getTexture(const LevelSource* level, const TilePos& pos, Facing:: int id_behind = level->getTile(side.west()); int id_infront = level->getTile(side.east()); - if ((Tile::solid[id_west] || Tile::solid[id_behind]) && !Tile::solid[id_east] && !Tile::solid[id_infront]) { + if ((Tile::solid[id_west] || Tile::solid[id_behind]) && !Tile::solid[id_east] && !Tile::solid[id_infront]) front = Facing::EAST; - } - else if ((Tile::solid[id_east] || Tile::solid[id_infront]) && !Tile::solid[id_west] && !Tile::solid[id_behind]) { + else if ((Tile::solid[id_east] || Tile::solid[id_infront]) && !Tile::solid[id_west] && !Tile::solid[id_behind]) + { front = Facing::WEST; left = !left; } @@ -87,7 +91,8 @@ int ChestTile::getTexture(const LevelSource* level, const TilePos& pos, Facing:: int ChestTile::getTexture(Facing::Name face) const { - switch (face) { + switch (face) + { case Facing::UP: case Facing::DOWN: return m_TextureFrame - 1; @@ -122,13 +127,12 @@ bool ChestTile::hasNeighbors(const Level* level, const TilePos& pos, int count) return false; } -void ChestTile::onRemove(Level* level, const TilePos& pos){ +void ChestTile::onRemove(Level* level, const TilePos& pos) +{ ChestTileEntity* ent = static_cast(level->getTileEntity(pos)); if (!ent) - { Tile::onRemove(level, pos); - } for (int slot = 0; slot < ent->getContainerSize(); ++slot) { @@ -160,6 +164,9 @@ void ChestTile::onRemove(Level* level, const TilePos& pos){ bool ChestTile::use(Level* level, const TilePos& pos, Player* player) { + if (player->isSneaking() && !player->getSelectedItem().isEmpty()) + return false; + if (level->m_bIsClientSide) return true; diff --git a/source/world/tile/ChestTile.hpp b/source/world/tile/ChestTile.hpp index 051081631..8ade330a0 100644 --- a/source/world/tile/ChestTile.hpp +++ b/source/world/tile/ChestTile.hpp @@ -6,6 +6,7 @@ class ChestTile : public Tile { public: ChestTile(int id, int texture); + public: int getTexture(const LevelSource*, const TilePos& pos, Facing::Name face) const override; int getTexture(Facing::Name face) const override; @@ -15,6 +16,7 @@ class ChestTile : public Tile bool use(Level* level, const TilePos& pos, Player* var5) override; bool hasTileEntity() const override; TileEntity* newTileEntity() override; + public: Random m_chestRandom; }; \ No newline at end of file diff --git a/source/world/tile/CraftingTableTile.cpp b/source/world/tile/CraftingTableTile.cpp index f6cd821ab..dc5c26e95 100644 --- a/source/world/tile/CraftingTableTile.cpp +++ b/source/world/tile/CraftingTableTile.cpp @@ -24,7 +24,8 @@ bool CraftingTableTile::use(Level* level, const TilePos& pos, Player* player) int CraftingTableTile::getTexture(Facing::Name face) const { - switch (face) { + switch (face) + { case Facing::UP: return m_TextureFrame - 16; case Facing::DOWN: return Tile::wood->getTexture(face); case Facing::NORTH: case Facing::SOUTH: return m_TextureFrame + 1; diff --git a/source/world/tile/FurnaceTile.cpp b/source/world/tile/FurnaceTile.cpp index e5ea21f65..85ff6a4cb 100644 --- a/source/world/tile/FurnaceTile.cpp +++ b/source/world/tile/FurnaceTile.cpp @@ -25,7 +25,8 @@ int FurnaceTile::getTexture(const LevelSource* level, const TilePos& pos, Facing } } -void FurnaceTile::animateTick(Level* level, const TilePos& pos, Random* random) { +void FurnaceTile::animateTick(Level* level, const TilePos& pos, Random* random) +{ if (!m_active) return; @@ -54,7 +55,8 @@ void FurnaceTile::animateTick(Level* level, const TilePos& pos, Random* random) int FurnaceTile::getTexture(Facing::Name face) const { - switch (face) { + switch (face) + { case Facing::UP: case Facing::DOWN: return m_TextureFrame + 17; @@ -73,7 +75,7 @@ void FurnaceTile::onPlace(Level* level, const TilePos& pos) bool FurnaceTile::use(Level* level, const TilePos& pos, Player* player) { - if (player->isSneaking() && player->getSelectedItem()) + if (player->isSneaking() && !player->getSelectedItem().isEmpty()) return false; if (level->m_bIsClientSide) @@ -85,20 +87,15 @@ bool FurnaceTile::use(Level* level, const TilePos& pos, Player* player) void FurnaceTile::setPlacedBy(Level* level, const TilePos& pos, Mob* mob) { - int rot = Mth::floor(0.5f + (mob->m_rot.y * 4.0f / 360.0f)) & 3; + int rot = Mth::floor(0.5f + (mob->m_rot.x * 4.0f / 360.0f)) & 3; int data = 4; switch (rot) { - case 0: - data = 2; - break; - case 1: - data = 5; - break; - case 2: - data = 3; - break; + case 0: data = 2; break; + case 1: data = 5; break; + case 2: data = 3; break; + case 3: data = 4; break; } level->setData(pos, data); diff --git a/source/world/tile/MusicTile.cpp b/source/world/tile/MusicTile.cpp index 7842f458e..5d50c6a1d 100644 --- a/source/world/tile/MusicTile.cpp +++ b/source/world/tile/MusicTile.cpp @@ -28,7 +28,7 @@ void MusicTile::neighborChanged(Level* level, const TilePos& pos, TileID tile) bool MusicTile::use(Level* level, const TilePos& pos, Player* player) { - if (player->isSneaking() && player->getSelectedItem()) + if (player->isSneaking() && !player->getSelectedItem().isEmpty()) return false; if (level->m_bIsClientSide) diff --git a/source/world/tile/PumpkinTile.cpp b/source/world/tile/PumpkinTile.cpp index c9481e442..cab8de26f 100644 --- a/source/world/tile/PumpkinTile.cpp +++ b/source/world/tile/PumpkinTile.cpp @@ -7,7 +7,8 @@ PumpkinTile::PumpkinTile(TileID id, bool lantern) : Tile(id, TEXTURE_PUMPKIN_TOP int PumpkinTile::getTexture(Facing::Name face, TileData data) const { - switch (face) { + switch (face) + { case Facing::UP: case Facing::DOWN: return m_TextureFrame; default: return (face == 2 && data == 2) || (face == 5 && data == 3) || (face == 3 && data == 0) || (face == 4 && data == 1) ? m_TextureFrame + (m_bLantern ? 18 : 17) : m_TextureFrame + 16; @@ -16,7 +17,8 @@ int PumpkinTile::getTexture(Facing::Name face, TileData data) const int PumpkinTile::getTexture(Facing::Name face) const { - switch (face) { + switch (face) + { case Facing::UP: case Facing::DOWN: return m_TextureFrame; case Facing::SOUTH: return m_TextureFrame + 17; default: return m_TextureFrame + 16; From 26e7f2e3746fc5b0114e446c3601b708dbcfde0f Mon Sep 17 00:00:00 2001 From: hellosammu Date: Mon, 16 Feb 2026 11:05:15 -0500 Subject: [PATCH 03/63] Virtual EntityTile class; fix save/load of entity tiles (far safer) --- source/CMakeLists.txt | 1 + source/world/level/Level.cpp | 21 +++++++++----- .../world/level/levelgen/chunk/LevelChunk.cpp | 28 +++++++++++-------- source/world/tile/ChestTile.cpp | 6 ++-- source/world/tile/ChestTile.hpp | 4 +-- source/world/tile/EntityTile.cpp | 23 +++++++++++++++ source/world/tile/EntityTile.hpp | 17 +++++++++++ source/world/tile/FurnaceTile.cpp | 10 +++++-- source/world/tile/FurnaceTile.hpp | 4 +-- source/world/tile/MusicTile.cpp | 2 +- source/world/tile/MusicTile.hpp | 4 +-- source/world/tile/Tile.cpp | 9 ------ source/world/tile/Tile.hpp | 1 - 13 files changed, 89 insertions(+), 41 deletions(-) create mode 100644 source/world/tile/EntityTile.cpp create mode 100644 source/world/tile/EntityTile.hpp diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index afe085a83..a91b654c5 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -426,6 +426,7 @@ add_library(reminecraftpe-core STATIC world/tile/OreTile.cpp world/tile/StairTile.cpp world/tile/SandStoneTile.cpp + world/tile/EntityTile.cpp world/tile/ChestTile.cpp world/tile/FurnaceTile.cpp world/tile/MusicTile.cpp diff --git a/source/world/level/Level.cpp b/source/world/level/Level.cpp index efec2255d..7460bbb08 100644 --- a/source/world/level/Level.cpp +++ b/source/world/level/Level.cpp @@ -293,20 +293,27 @@ void Level::setTileEntity(const TilePos& pos, TileEntity* tileEntity) void Level::removeTileEntity(const TilePos& pos) { - TileEntity* old = getTileEntity(pos); + TileEntity* tileEntity = getTileEntity(pos); - if (old != nullptr && m_bUpdatingTileEntities) + if (tileEntity == nullptr) { - old->setRemoved(); + LOG_W("Tried to remove a tile entity at %d, %d, %d, but there was no tile entity there!", pos.x, pos.y, pos.z); return; } - if (old) - Util::remove(m_tileEntityList, old); + // During a tile entity update, just mark it for potential removal + if (m_bUpdatingTileEntities) + { + tileEntity->setRemoved(); + return; + } - LevelChunk* pChunk = getChunk(pos); - if (pChunk) + Util::remove(m_tileEntityList, tileEntity); + + if (LevelChunk* pChunk = getChunk(pos)) + { pChunk->removeTileEntity(pos); + } } void Level::swap(const TilePos& pos1, const TilePos& pos2) diff --git a/source/world/level/levelgen/chunk/LevelChunk.cpp b/source/world/level/levelgen/chunk/LevelChunk.cpp index 598a4ef8a..357a805c1 100644 --- a/source/world/level/levelgen/chunk/LevelChunk.cpp +++ b/source/world/level/levelgen/chunk/LevelChunk.cpp @@ -589,7 +589,7 @@ bool LevelChunk::setTile(const ChunkTilePos& pos, TileID tile) tilePos.x += pos.x; tilePos.z += pos.z; m_pBlockData[index] = tile; - if (oldTile) + if (oldTile && Tile::tiles[oldTile]) { Tile::tiles[oldTile]->onRemove(m_pLevel, tilePos); } @@ -722,20 +722,24 @@ void LevelChunk::addTileEntity(TileEntity* tileEntity) void LevelChunk::setTileEntity(const ChunkTilePos& pos, TileEntity* tileEntity) { - tileEntity->m_pLevel = m_pLevel; - TileID tile = getTile(pos); TilePos tilePos(m_chunkPos, pos.y); - tilePos.x += pos.x; - tilePos.z += pos.z; - tileEntity->m_pos = tilePos; - if (tile > 0 && Tile::isEntityTile[tile]) - { - tileEntity->clearRemoved(); - m_tileEntities[pos] = tileEntity; - return; + + if (tileEntity) + { + tileEntity->m_pLevel = m_pLevel; + TileID tile = getTile(pos); + tilePos.x += pos.x; + tilePos.z += pos.z; + tileEntity->m_pos = tilePos; + if (tile > 0 && Tile::isEntityTile[tile]) + { + tileEntity->clearRemoved(); + m_tileEntities[pos] = tileEntity; + return; + } } - LOG_I("Attempted to place a tile entity where there was no entity tile! %d", tile); + LOG_W("Attempted to place a tile entity at %d, %d, %d where there was no entity tile!", tilePos.x, tilePos.y, tilePos.z); } void LevelChunk::removeTileEntity(const ChunkTilePos& pos) diff --git a/source/world/tile/ChestTile.cpp b/source/world/tile/ChestTile.cpp index ca8f42ad9..e7aad4afa 100644 --- a/source/world/tile/ChestTile.cpp +++ b/source/world/tile/ChestTile.cpp @@ -3,7 +3,7 @@ #include "world/CompoundContainer.hpp" #include "world/tile/entity/ChestTileEntity.hpp" -ChestTile::ChestTile(int id, int texture) : Tile(id, texture, Material::wood) +ChestTile::ChestTile(int id, int texture) : EntityTile(id, texture, Material::wood) { setTicking(true); } @@ -132,7 +132,7 @@ void ChestTile::onRemove(Level* level, const TilePos& pos) ChestTileEntity* ent = static_cast(level->getTileEntity(pos)); if (!ent) - Tile::onRemove(level, pos); + EntityTile::onRemove(level, pos); for (int slot = 0; slot < ent->getContainerSize(); ++slot) { @@ -159,7 +159,7 @@ void ChestTile::onRemove(Level* level, const TilePos& pos) } } - Tile::onRemove(level, pos); + EntityTile::onRemove(level, pos); } bool ChestTile::use(Level* level, const TilePos& pos, Player* player) diff --git a/source/world/tile/ChestTile.hpp b/source/world/tile/ChestTile.hpp index 8ade330a0..e154d454e 100644 --- a/source/world/tile/ChestTile.hpp +++ b/source/world/tile/ChestTile.hpp @@ -1,8 +1,8 @@ #pragma once -#include "Tile.hpp" +#include "EntityTile.hpp" -class ChestTile : public Tile +class ChestTile : public EntityTile { public: ChestTile(int id, int texture); diff --git a/source/world/tile/EntityTile.cpp b/source/world/tile/EntityTile.cpp new file mode 100644 index 000000000..ed5e75f84 --- /dev/null +++ b/source/world/tile/EntityTile.cpp @@ -0,0 +1,23 @@ +#include "EntityTile.hpp" +#include "world/level/Level.hpp" + +EntityTile::EntityTile(TileID id, int textureID, Material *material) : Tile(id, textureID, material) +{ +} + +bool EntityTile::hasTileEntity() const +{ + return true; +} + +void EntityTile::onPlace(Level *level, const TilePos &pos) +{ + Tile::onPlace(level, pos); + level->setTileEntity(pos, newTileEntity()); +} + +void EntityTile::onRemove(Level *level, const TilePos &pos) +{ + Tile::onRemove(level, pos); + level->removeTileEntity(pos); +} \ No newline at end of file diff --git a/source/world/tile/EntityTile.hpp b/source/world/tile/EntityTile.hpp new file mode 100644 index 000000000..d79c2cc1a --- /dev/null +++ b/source/world/tile/EntityTile.hpp @@ -0,0 +1,17 @@ +#pragma once + +#include "Tile.hpp" + +class EntityTile : public Tile +{ +public: + EntityTile(TileID id, int textureID, Material* material); + +public: + virtual TileEntity* newTileEntity() = 0; + +public: + bool hasTileEntity() const override; + void onPlace(Level* level, const TilePos& pos) override; + void onRemove(Level* level, const TilePos& pos) override; +}; \ No newline at end of file diff --git a/source/world/tile/FurnaceTile.cpp b/source/world/tile/FurnaceTile.cpp index 85ff6a4cb..ccf3e1c28 100644 --- a/source/world/tile/FurnaceTile.cpp +++ b/source/world/tile/FurnaceTile.cpp @@ -4,7 +4,7 @@ bool FurnaceTile::keepInventory = false; -FurnaceTile::FurnaceTile(int id, bool active) : Tile(id, TEXTURE_FURNACE_SIDE, Material::stone) +FurnaceTile::FurnaceTile(int id, bool active) : EntityTile(id, TEXTURE_FURNACE_SIDE, Material::stone) { setTicking(true); m_active = active; @@ -69,7 +69,7 @@ int FurnaceTile::getTexture(Facing::Name face) const void FurnaceTile::onPlace(Level* level, const TilePos& pos) { - Tile::onPlace(level, pos); + EntityTile::onPlace(level, pos); RecalculateLookDirection(level, pos); } @@ -110,6 +110,7 @@ void FurnaceTile::onRemove(Level* level, const TilePos& pos) if (!tileEnt) { + EntityTile::onRemove(level, pos); return; // this has to be wrapped in a guard or the compiler screams, thanks -Wmisleading-indentation } @@ -139,11 +140,16 @@ void FurnaceTile::onRemove(Level* level, const TilePos& pos) level->addEntity(itemEnt); } } + + EntityTile::onRemove(level, pos); } void FurnaceTile::SetLit(bool lit, Level* level, const TilePos& pos) { TileEntity* tileEntity = level->getTileEntity(pos); + if (!tileEntity) + return; + int data = level->getData(pos); keepInventory = true; diff --git a/source/world/tile/FurnaceTile.hpp b/source/world/tile/FurnaceTile.hpp index bb0ac9359..0a79e01aa 100644 --- a/source/world/tile/FurnaceTile.hpp +++ b/source/world/tile/FurnaceTile.hpp @@ -1,8 +1,8 @@ #pragma once -#include "Tile.hpp" +#include "EntityTile.hpp" -class FurnaceTile : public Tile +class FurnaceTile : public EntityTile { public: FurnaceTile(int id, bool active); diff --git a/source/world/tile/MusicTile.cpp b/source/world/tile/MusicTile.cpp index 5d50c6a1d..372077028 100644 --- a/source/world/tile/MusicTile.cpp +++ b/source/world/tile/MusicTile.cpp @@ -2,7 +2,7 @@ #include "world/level/Level.hpp" #include "entity/MusicTileEntity.hpp" -MusicTile::MusicTile(TileID id, int texture) : Tile(id, texture, Material::wood) +MusicTile::MusicTile(TileID id, int texture) : EntityTile(id, texture, Material::wood) { } diff --git a/source/world/tile/MusicTile.hpp b/source/world/tile/MusicTile.hpp index fcddfba82..7383a0694 100644 --- a/source/world/tile/MusicTile.hpp +++ b/source/world/tile/MusicTile.hpp @@ -1,8 +1,8 @@ #pragma once -#include "Tile.hpp" +#include "EntityTile.hpp" -class MusicTile : public Tile +class MusicTile : public EntityTile { public: MusicTile(TileID id, int textureID); diff --git a/source/world/tile/Tile.cpp b/source/world/tile/Tile.cpp index 4aea4c4b7..14878f2d2 100644 --- a/source/world/tile/Tile.cpp +++ b/source/world/tile/Tile.cpp @@ -283,11 +283,6 @@ int Tile::getSpawnResourcesAuxValue(int x) const return 0; } -TileEntity* Tile::newTileEntity() -{ - return nullptr; -} - Tile* Tile::setToolTypes(unsigned int toolMask) { m_toolMask |= toolMask; @@ -998,14 +993,10 @@ void Tile::neighborChanged(Level* pLevel, const TilePos& pos, TileID tile) void Tile::onPlace(Level* pLevel, const TilePos& pos) { - if (hasTileEntity()) - pLevel->setTileEntity(pos, newTileEntity()); } void Tile::onRemove(Level* pLevel, const TilePos& pos) { - if (hasTileEntity()) - pLevel->removeTileEntity(pos); } bool Tile::containsX(const Vec3& v) diff --git a/source/world/tile/Tile.hpp b/source/world/tile/Tile.hpp index 2a9bc0ad3..61388b225 100644 --- a/source/world/tile/Tile.hpp +++ b/source/world/tile/Tile.hpp @@ -123,7 +123,6 @@ class Tile virtual Tile* setDestroyTime(float); virtual Tile* setTicking(bool); virtual int getSpawnResourcesAuxValue(int) const; - virtual TileEntity* newTileEntity(); Tile* setToolTypes(unsigned int toolMask); Tile* setToolLevel(int toolLevel); Tile* setToolTypesAndLevel(unsigned int toolMask, int toolLevel = 0); From f64f96a392f9124127306d3274585aa59c11ad3a Mon Sep 17 00:00:00 2001 From: hellosammu Date: Mon, 16 Feb 2026 12:18:45 -0500 Subject: [PATCH 04/63] base client riding logic --- source/client/renderer/entity/MobRenderer.cpp | 2 +- source/world/entity/Entity.cpp | 139 ++++++++++++++++++ source/world/entity/Entity.hpp | 19 ++- source/world/entity/Mob.cpp | 20 ++- source/world/entity/Mob.hpp | 6 +- source/world/entity/Pig.cpp | 31 ++++ source/world/entity/Pig.hpp | 2 +- source/world/entity/Player.cpp | 4 +- source/world/entity/Player.hpp | 3 +- source/world/entity/Spider.hpp | 2 +- source/world/level/Level.cpp | 56 ++++++- source/world/tile/FurnaceTile.cpp | 2 + 12 files changed, 267 insertions(+), 19 deletions(-) diff --git a/source/client/renderer/entity/MobRenderer.cpp b/source/client/renderer/entity/MobRenderer.cpp index 13d7ba76e..c3af5f015 100644 --- a/source/client/renderer/entity/MobRenderer.cpp +++ b/source/client/renderer/entity/MobRenderer.cpp @@ -90,7 +90,7 @@ void MobRenderer::render(const Entity& entity, const Vec3& pos, float rot, float MatrixStack::Ref matrix = MatrixStack::World.push(); m_pModel->m_attackTime = getAttackAnim(mob, a); - m_pModel->m_bRiding = false; + m_pModel->m_bRiding = mob.isRiding(); m_pModel->m_bIsBaby = mob.isBaby(); if (m_pArmorModel != nullptr) diff --git a/source/world/entity/Entity.cpp b/source/world/entity/Entity.cpp index 49d7112a0..f7edc64a2 100644 --- a/source/world/entity/Entity.cpp +++ b/source/world/entity/Entity.cpp @@ -28,6 +28,9 @@ void Entity::_init() field_28 = 0; field_30 = 1.0f; m_dimensionId = DIMENSION_OVERWORLD; + m_riderId = 0; + m_ridingId = 0; + m_bRiding = false; m_bBlocksBuilding = false; m_pLevel = nullptr; m_tintColor = Color::WHITE; @@ -469,6 +472,14 @@ void Entity::tick() void Entity::baseTick() { //@TODO: untangle the gotos + if (const Entity* riding = getRiding()) + { + if ((!riding && m_riderId > 0) || riding->m_bRemoved) + { + m_riderId = 0; + setSharedFlag(C_ENTITY_FLAG_RIDING, false); + } + } field_90 = m_walkDist; m_oPos = m_pos; @@ -937,6 +948,46 @@ AABB* Entity::getCollideAgainstBox(Entity* ent) const return nullptr; } +void Entity::rideTick() +{ + Entity* riding = getRiding(); + if (!riding || riding->m_bRemoved) + { + m_riderId = 0; + setSharedFlag(C_ENTITY_FLAG_RIDING, false); + } + + // we don't move + m_vel = Vec3::ZERO; + + tick(); + + riding->positionRider(); + m_rideRot.x += riding->m_rot.x - riding->m_oRot.x; + m_rideRot.y += riding->m_rot.y - riding->m_oRot.y; + while (m_rideRot.y >= 180.0f) + m_rideRot.y -= 360.0f; + while (m_rideRot.y < -180.0f) + m_rideRot.y += 360.0f; + while (m_rideRot.x >= 180.0f) + m_rideRot.x -= 360.0f; + while (m_rideRot.x < -180.0f) + m_rideRot.x += 360.0f; + + float rotX = m_rideRot.x * 0.5f; + float rotY = m_rideRot.y * 0.5f; + + float lookLimiter = 10.0f; + rotX = Mth::clamp(rotX, -lookLimiter, lookLimiter); + rotY = Mth::clamp(rotY, -lookLimiter, lookLimiter); + + m_rideRot.x -= rotX; + m_rideRot.y -= rotY; + + m_rot.x += rotX; + m_rot.y += rotY; +} + void Entity::handleInsidePortal() { } @@ -946,6 +997,94 @@ void Entity::handleEntityEvent(EventType::ID eventId) LOG_W("Unknown EntityEvent ID: %d, EntityType: %s", eventId, getDescriptor().getEntityType().getName().c_str()); } +void Entity::positionRider() +{ + Entity* rider = getRider(); + if (!rider) + return; + + rider->setPos(Vec3(m_pos.x, m_pos.y + getRideHeight() + rider->getRidingHeight(), m_pos.z)); +} + +void Entity::ride(Entity* newRiding) +{ + m_rideRot = Vec2::ZERO; + Entity* oldRiding = getRiding(); + + // Dismount current ride if nullptr is fed in + if (newRiding == nullptr) + { + if (oldRiding) + { + moveTo(oldRiding->m_pos); + setRot(oldRiding->m_rot); + oldRiding->m_riderId = 0; // Let them know you dismounted them + } + + // Let yourself know you aren't riding anything + m_ridingId = 0; + setSharedFlag(C_ENTITY_FLAG_RIDING, false); + + return; + } + + // Dismount if the same entity is fed in + if (oldRiding && oldRiding == newRiding) + { + oldRiding->m_riderId = 0; + + m_ridingId = 0; + setSharedFlag(C_ENTITY_FLAG_RIDING, false); + + moveTo(oldRiding->m_pos); + setRot(oldRiding->m_rot); + return; + } + + // if (this.riding != null) this.riding.rider = null; + if (oldRiding) + { + oldRiding->m_riderId = 0; + } + + // if (newRiding.rider != null) newRiding.rider.riding = null; + // i hate this name but it's literally what it is + if (Entity* newRidesOldRider = newRiding->getRider()) + { + newRidesOldRider->m_ridingId = 0; + newRidesOldRider->setSharedFlag(C_ENTITY_FLAG_RIDING, false); + } + + // Tell yourself that you're riding the new ride + m_ridingId = newRiding->m_EntityID; + setSharedFlag(C_ENTITY_FLAG_RIDING, true); + + // Tell the new ride that it's being ridden by you + newRiding->m_riderId = m_EntityID; +} + +Entity* Entity::getRiding() const +{ + if (m_ridingId <= 0) + return nullptr; + + if (Entity* riding = m_pLevel->getEntity(m_ridingId)) + return riding; + + return nullptr; +} + +Entity* Entity::getRider() const +{ + if (m_riderId <= 0) + return nullptr; + + if (Entity* rider = m_pLevel->getEntity(m_riderId)) + return rider; + + return nullptr; +} + /*void Entity::thunderHit(LightningBolt* bolt) { burn(5); diff --git a/source/world/entity/Entity.hpp b/source/world/entity/Entity.hpp index 07a0baf01..e104b01de 100644 --- a/source/world/entity/Entity.hpp +++ b/source/world/entity/Entity.hpp @@ -112,11 +112,9 @@ class Entity Entity(Level*); virtual ~Entity(); -protected: +public: virtual bool getSharedFlag(SharedFlag flag) const; virtual void setSharedFlag(SharedFlag flag, bool value); - -public: virtual void reset(); virtual void setLevel(Level*); virtual void removed(); @@ -165,7 +163,7 @@ class Entity virtual bool isPushable() const { return false; } virtual bool isShootable() const { return false; } virtual bool isOnFire() const { return m_fireTicks > 0 || getSharedFlag(C_ENTITY_FLAG_ONFIRE); } - virtual bool isRiding() const { return /*m_pRiding != nullptr ||*/ getSharedFlag(C_ENTITY_FLAG_RIDING); } + virtual bool isRiding() const { return getRiding() || getSharedFlag(C_ENTITY_FLAG_RIDING); } virtual bool isSneaking() const { return getSharedFlag(C_ENTITY_FLAG_SNEAKING); } virtual void setSneaking(bool value) { setSharedFlag(C_ENTITY_FLAG_SNEAKING, value); } virtual bool isAlive() const { return m_bRemoved; } @@ -196,9 +194,18 @@ class Entity virtual RenderType queryEntityRenderer() const; virtual const AABB* getCollideBox() const; virtual AABB* getCollideAgainstBox(Entity* ent) const; + virtual void rideTick(); virtual void handleInsidePortal(); virtual void handleEntityEvent(EventType::ID eventId); //virtual void thunderHit(LightningBolt*); + virtual void positionRider(); + virtual void ride(Entity*); + virtual float getRideHeight() const { return m_bbHeight * 0.75f; } + virtual float getRidingHeight() const { return m_heightOffset; } + Entity* getRiding() const; + Entity* getRider() const; + // void setRiding(Entity* newRiding); + // void setRider(Entity* newRider); void load(const CompoundTag& tag); bool save(CompoundTag& tag) const; void saveWithoutId(CompoundTag& tag) const; @@ -240,12 +247,16 @@ class Entity float field_30; //TileSource* m_pTileSource; DimensionId m_dimensionId; + Entity::ID m_ridingId; + Entity::ID m_riderId; + bool m_bRiding; bool m_bBlocksBuilding; Level* m_pLevel; Vec3 m_oPos; // "o" in Java or "xo" "yo" "zo" Vec3 m_vel; Vec2 m_rot; Vec2 m_oRot; // "RotO" in Java or "xRotO" "yRotO" + Vec2 m_rideRot; Color m_tintColor; AABB m_hitbox; bool m_bOnGround; diff --git a/source/world/entity/Mob.cpp b/source/world/entity/Mob.cpp index a336c5654..a3967ce12 100644 --- a/source/world/entity/Mob.cpp +++ b/source/world/entity/Mob.cpp @@ -56,7 +56,7 @@ void Mob::_init() m_lPos = Vec3::ZERO; m_lRot = Vec2::ZERO; m_lastHurt = 0; - m_pEntLookedAt = nullptr; + m_entLookedAtId = 0; m_bSwinging = false; m_swingTime = 0; m_ambientSoundTime = 0; @@ -786,6 +786,13 @@ void Mob::lookAt(Entity* pEnt, float a3, float a4) -rotlerp(m_rot.y, x2 * 180.0f / float(M_PI), a3))); } +Entity *Mob::getLookingAt() const +{ + if (m_entLookedAtId == 0) + return nullptr; + return m_pLevel->getEntity(m_entLookedAtId); +} + bool Mob::canSpawn() { return m_pLevel->getCubes(this, m_hitbox)->empty(); @@ -832,7 +839,7 @@ void Mob::updateAi() Entity* nearestPlayer = m_pLevel->getNearestPlayer(*this, 8.0f); if (nearestPlayer) { - m_pEntLookedAt = nearestPlayer; + m_entLookedAtId = nearestPlayer->m_EntityID; field_120 = m_random.nextInt(20) + 10; } @@ -843,17 +850,18 @@ void Mob::updateAi() } // @TODO: we get a crash here when a Player leaves - if (m_pEntLookedAt) + if (m_entLookedAtId > 0) { - lookAt(m_pEntLookedAt, 10.0f, getMaxHeadXRot()); + Entity* pEnt = m_pLevel->getEntity(m_entLookedAtId); + lookAt(pEnt, 10.0f, getMaxHeadXRot()); // gaze timer field_120--; // if the entity was removed, or we're too far away, or our gaze timer is up - if (field_120 < 0 || m_pEntLookedAt->m_bRemoved || m_pEntLookedAt->distanceToSqr(this) > 64.0f) + if (field_120 < 0 || pEnt->m_bRemoved || pEnt->distanceToSqr(this) > 64.0f) // stop staring - m_pEntLookedAt = nullptr; + m_entLookedAtId = 0; } else { diff --git a/source/world/entity/Mob.hpp b/source/world/entity/Mob.hpp index 2f3909617..b98687817 100644 --- a/source/world/entity/Mob.hpp +++ b/source/world/entity/Mob.hpp @@ -63,9 +63,9 @@ class Mob : public Entity virtual void updateWalkAnim(); virtual void aiStep(); virtual void lookAt(Entity* pEnt, float, float); - virtual bool isLookingAtAnEntity() { return m_pEntLookedAt != nullptr; } + virtual bool isLookingAtAnEntity() { return m_entLookedAtId > 0; } virtual bool isSlowedByLiquids() const { return true; } - virtual Entity* getLookingAt() const { return m_pEntLookedAt; } + virtual Entity* getLookingAt() const; virtual void beforeRemove() {} virtual bool canSpawn(); virtual float getAttackAnim(float f) const; @@ -145,7 +145,7 @@ class Mob : public Entity Vec3 m_lPos; Vec2 m_lRot; int m_lastHurt; - Entity* m_pEntLookedAt; + Entity::ID m_entLookedAtId; float v020_field_104; diff --git a/source/world/entity/Pig.cpp b/source/world/entity/Pig.cpp index 153163f49..0f160d6d0 100644 --- a/source/world/entity/Pig.cpp +++ b/source/world/entity/Pig.cpp @@ -6,6 +6,8 @@ SPDX-License-Identifier: BSD-1-Clause ********************************************************************/ #include "Pig.hpp" +#include "Player.hpp" +#include "world/level/Level.hpp" Pig::Pig(Level* pLevel) : Animal(pLevel) { @@ -14,6 +16,7 @@ Pig::Pig(Level* pLevel) : Animal(pLevel) m_texture = "mob/pig.png"; setSize(0.9f, 0.9f); // some dataitem stuff + setSaddle(true); } int Pig::getDeathLoot() const { @@ -23,6 +26,34 @@ int Pig::getDeathLoot() const return Item::porkChop_raw->m_itemID; } +bool Pig::interact(Player* pPlayer) +{ + return false; + // @TODO: add saddles + /* + if (m_pLevel->m_bIsClientSide) + { + return false; + } + + if (!m_bSaddled) + { + return false; + } + + Entity* rider = getRider(); + + // already being ridden by someone else + if (rider && rider != pPlayer) + { + return false; + } + + pPlayer->ride(this); + return true; + */ +} + void Pig::setSaddle(bool b) { // @TODO: this diff --git a/source/world/entity/Pig.hpp b/source/world/entity/Pig.hpp index fb1d9ae77..0e93310d9 100644 --- a/source/world/entity/Pig.hpp +++ b/source/world/entity/Pig.hpp @@ -19,7 +19,7 @@ class Pig : public Animal std::string getHurtSound() const override { return "mob.pig"; } int getDeathLoot() const override; int getMaxHealth() const override { return 10; } - bool interact(Player*) override { return false; } + bool interact(Player*) override; bool hasSaddle() const { return false; } void setSaddle(bool b); diff --git a/source/world/entity/Player.cpp b/source/world/entity/Player.cpp index 34c37381b..a22fb7e90 100644 --- a/source/world/entity/Player.cpp +++ b/source/world/entity/Player.cpp @@ -472,7 +472,9 @@ void Player::respawn() void Player::rideTick() { - + Mob::rideTick(); + m_oBob = m_bob; + m_bob = 0.0f; } void Player::setDefaultHeadHeight() diff --git a/source/world/entity/Player.hpp b/source/world/entity/Player.hpp index ed63daee9..9e982f7dd 100644 --- a/source/world/entity/Player.hpp +++ b/source/world/entity/Player.hpp @@ -94,7 +94,8 @@ class Player : public Mob Dimension* getDimension() const; void prepareCustomTextures(); void respawn(); - void rideTick(); + void rideTick() override; + float getRidingHeight() const override { return m_heightOffset - 0.5f; } void setDefaultHeadHeight(); void setRespawnPos(const TilePos& pos); inline const Abilities& getAbilities() const { return m_abilities; } diff --git a/source/world/entity/Spider.hpp b/source/world/entity/Spider.hpp index e176275ca..1b2662fb5 100644 --- a/source/world/entity/Spider.hpp +++ b/source/world/entity/Spider.hpp @@ -11,7 +11,7 @@ class Spider : public Monster std::string getDeathSound() const override { return "mob.spiderdeath"; } std::string getHurtSound() const override { return "mob.spider"; } int getDeathLoot() const override { return ITEM_STRING; } - float getRideHeight() const { return m_bbHeight * 0.75f - 0.5f; } + float getRideHeight() const override { return m_bbHeight * 0.75f - 0.5f; } Entity* findAttackTarget() override; void checkHurtTarget(Entity* ent, float var2) override; diff --git a/source/world/level/Level.cpp b/source/world/level/Level.cpp index 7460bbb08..c5b170c09 100644 --- a/source/world/level/Level.cpp +++ b/source/world/level/Level.cpp @@ -1253,6 +1253,17 @@ void Level::removeAllPendingEntityRemovals() for (EntityVector::iterator it = m_pendingEntityRemovals.begin(); it != m_pendingEntityRemovals.end(); it++) { Entity* ent = *it; + if (Entity* riding = ent->getRiding()) + { + if (riding->m_bRemoved || riding->getRider() != ent) + { + riding->m_riderId = 0; + ent->m_ridingId = 0; + ent->setSharedFlag(C_ENTITY_FLAG_RIDING, false); + } + else + continue; + } ent->removed(); LevelChunk* chunk = getChunk(ent->m_chunkPos); @@ -1273,6 +1284,14 @@ void Level::removeEntities(const EntityVector& vec) bool Level::removeEntity(Entity* pEnt) { + // kick off rider before disappearing + if (Entity* rider = pEnt->getRider()) + rider->ride(nullptr); + + // kick self off mount before disappearing + if (Entity* mount = pEnt->getRiding()) + mount->ride(nullptr); + pEnt->remove(); if (pEnt->isPlayer()) @@ -1678,7 +1697,12 @@ void Level::tick(Entity* pEnt, bool shouldTick) pEnt->m_oRot = pEnt->m_rot; if (pEnt->m_bInAChunk) - pEnt->tick(); + { + if (pEnt->getRiding()) + pEnt->rideTick(); + else + pEnt->tick(); + } } else { @@ -1712,6 +1736,24 @@ void Level::tick(Entity* pEnt, bool shouldTick) getChunk(cp)->updateEntity(pEnt); } } + if (shouldTick && pEnt->m_bInAChunk) + { + Entity* rider = pEnt->getRider(); + if (rider) + { + if (rider->m_bRemoved || rider->getRiding() != pEnt) + { + rider->m_riderId = 0; + + pEnt->m_ridingId = 0; + pEnt->setSharedFlag(C_ENTITY_FLAG_RIDING, false); + } + else + { + tick(rider); + } + } + } } void Level::tick(Entity* pEnt) @@ -1748,6 +1790,18 @@ void Level::tickEntities() { Entity* pEnt = m_entities[i]; + if (Entity* riding = pEnt->getRiding()) + { + if (riding->m_bRemoved || riding->getRider() != pEnt) + { + riding->m_riderId = 0; + pEnt->m_ridingId = 0; + pEnt->setSharedFlag(C_ENTITY_FLAG_RIDING, false); + } + else + continue; + } + if (!pEnt->m_bRemoved) { tick(pEnt); diff --git a/source/world/tile/FurnaceTile.cpp b/source/world/tile/FurnaceTile.cpp index ccf3e1c28..e17ea782d 100644 --- a/source/world/tile/FurnaceTile.cpp +++ b/source/world/tile/FurnaceTile.cpp @@ -148,7 +148,9 @@ void FurnaceTile::SetLit(bool lit, Level* level, const TilePos& pos) { TileEntity* tileEntity = level->getTileEntity(pos); if (!tileEntity) + { return; + } int data = level->getData(pos); From bd18e03f60f3f899b7fb18ea0a96b1ee697fb97c Mon Sep 17 00:00:00 2001 From: hellosammu Date: Mon, 16 Feb 2026 19:30:15 -0500 Subject: [PATCH 05/63] naming convention adjustment to match other classes better --- source/client/app/NinecraftApp.cpp | 4 +- source/world/tile/entity/TileEntity.cpp | 2 +- source/world/tile/entity/TileEntityType.cpp | 40 ++++++++-------- source/world/tile/entity/TileEntityType.hpp | 52 ++++++++++++--------- 4 files changed, 53 insertions(+), 45 deletions(-) diff --git a/source/client/app/NinecraftApp.cpp b/source/client/app/NinecraftApp.cpp index 0a16d0d3d..1b336d72c 100644 --- a/source/client/app/NinecraftApp.cpp +++ b/source/client/app/NinecraftApp.cpp @@ -180,10 +180,10 @@ void NinecraftApp::_initAll() EntityTypeDescriptor::initDescriptors(); // custom MobCategory::initMobCategories(); MobFactory::initMobLists(); + TileEntityFactory::InitTileEntities(); Tile::initTiles(); Item::initItems(); Biome::initBiomes(); - TileEntityType::InitTileEntities(); } _initOptions(); @@ -341,7 +341,7 @@ void NinecraftApp::onGraphicsReset() void NinecraftApp::teardown() { - TileEntityType::TeardownTileEntities(); + TileEntityFactory::TeardownTileEntities(); teardownRenderer(); Resource::teardownLoaders(); // Stop our SoundSystem before we nuke our sound buffers and cause it to implode diff --git a/source/world/tile/entity/TileEntity.cpp b/source/world/tile/entity/TileEntity.cpp index 9cd7b79ad..a5b11be86 100644 --- a/source/world/tile/entity/TileEntity.cpp +++ b/source/world/tile/entity/TileEntity.cpp @@ -19,7 +19,7 @@ TileEntity* TileEntity::LoadTileEntity(const CompoundTag& tag) } std::string id = tag.getString("id"); - const TileEntityType* type = TileEntityType::GetType(id); + const TileEntityType* type = TileEntityFactory::GetType(id); if (!type) { diff --git a/source/world/tile/entity/TileEntityType.cpp b/source/world/tile/entity/TileEntityType.cpp index 8ee14b727..1391e41ac 100644 --- a/source/world/tile/entity/TileEntityType.cpp +++ b/source/world/tile/entity/TileEntityType.cpp @@ -8,44 +8,44 @@ // #include "RecordPlayerTileEntity.hpp" // #include "PistonMovingTileEntity.hpp" -std::map TileEntityType::_types; - TileEntityType* TileEntityType::furnace; TileEntityType* TileEntityType::chest; TileEntityType* TileEntityType::noteblock; -TileEntityType::TileEntityType(const std::string& name, CreateFunction func) : _name(name), _function(func) +std::map TileEntityFactory::_types; + +void TileEntityFactory::InitTileEntities() { + TileEntityType::furnace = RegisterTileEntity("Furnace"); + TileEntityType::chest = RegisterTileEntity("Chest"); + TileEntityType::noteblock = RegisterTileEntity("Music"); } -const std::string& TileEntityType::getName() const +void TileEntityFactory::TeardownTileEntities() { - return _name; + // delete all heap allocated tile entity types (furnace, chest, etc.) + for (std::map::const_iterator it = _types.begin(); it != _types.end(); ++it) + { + SAFE_DELETE(it->second); + } + _types.clear(); } -TileEntity* TileEntityType::newTileEntity() const +const TileEntityType* TileEntityFactory::GetType(const std::string& name) { - return _function(); + return _types[name]; } -void TileEntityType::InitTileEntities() +TileEntityType::TileEntityType(const std::string& name, CreateFunction func) : _name(name), _function(func) { - furnace = RegisterTileEntity("Furnace"); - chest = RegisterTileEntity("Chest"); - noteblock = RegisterTileEntity("Music"); } -const TileEntityType* TileEntityType::GetType(const std::string& name) +const std::string& TileEntityType::getName() const { - return _types[name]; + return _name; } -void TileEntityType::TeardownTileEntities() +TileEntity* TileEntityType::newTileEntity() const { - // delete all heap allocated tile entity types (furnace, chest, etc.) - for (std::map::const_iterator it = _types.begin(); it != _types.end(); ++it) - { - SAFE_DELETE(it->second); - } - _types.clear(); + return _function(); } \ No newline at end of file diff --git a/source/world/tile/entity/TileEntityType.hpp b/source/world/tile/entity/TileEntityType.hpp index 3a981cf6c..0baa6b857 100644 --- a/source/world/tile/entity/TileEntityType.hpp +++ b/source/world/tile/entity/TileEntityType.hpp @@ -6,10 +6,32 @@ class Level; class TileEntity; +class TileEntityType; + +class TileEntityFactory +{ +private: + static std::map _types; + +public: + static void InitTileEntities(); + static void TeardownTileEntities(); + static const TileEntityType* GetType(const std::string& name); + +public: + template + static TileEntityType* RegisterTileEntity(const std::string& name); +}; class TileEntityType { public: + static TileEntityType* furnace; + static TileEntityType* chest; + static TileEntityType* noteblock; + +public: + friend class TileEntityFactory; typedef TileEntity* (*CreateFunction)(); public: @@ -19,32 +41,18 @@ class TileEntityType const std::string& getName() const; TileEntity* newTileEntity() const; -public: - static void InitTileEntities(); - static const TileEntityType* GetType(const std::string& name); - static void TeardownTileEntities(); - -private: - static std::map _types; - private: std::string _name; CreateFunction _function; -public: - static TileEntityType* furnace; - static TileEntityType* chest; - static TileEntityType* noteblock; - template static TileEntity* CreateType() { return new T(); } +}; -public: - template - static TileEntityType* RegisterTileEntity(const std::string& name) - { - TileEntityType* type = new TileEntityType(name, &TileEntityType::CreateType); - _types[type->_name] = type; - return type; - } -}; \ No newline at end of file +template +TileEntityType* TileEntityFactory::RegisterTileEntity(const std::string& name) +{ + TileEntityType* type = new TileEntityType(name, &TileEntityType::CreateType); + _types[type->_name] = type; + return type; +} \ No newline at end of file From ecdaaea97942f004e67b06d95b8a88594d702ae5 Mon Sep 17 00:00:00 2001 From: hellosammu Date: Mon, 16 Feb 2026 20:52:43 -0500 Subject: [PATCH 06/63] Entities should be hashmaps. Can open furnace now --- source/client/gui/ScreenChooser.cpp | 6 ++ source/client/gui/ScreenChooser.hpp | 2 + .../client/multiplayer/MultiPlayerLevel.cpp | 2 +- .../network/ClientSideNetworkHandler.cpp | 3 +- source/client/player/LocalPlayer.cpp | 2 +- source/client/renderer/LevelRenderer.cpp | 8 +- source/server/ServerPlayer.cpp | 20 ++++- source/server/ServerPlayer.hpp | 1 + source/server/ServerSideNetworkHandler.cpp | 4 +- source/world/entity/Entity.hpp | 2 - source/world/entity/Pig.cpp | 7 +- source/world/entity/Player.cpp | 3 - source/world/level/Level.cpp | 84 ++++++++++++------- source/world/level/Level.hpp | 6 +- .../storage/ExternalFileLevelStorage.cpp | 6 +- 15 files changed, 104 insertions(+), 52 deletions(-) diff --git a/source/client/gui/ScreenChooser.cpp b/source/client/gui/ScreenChooser.cpp index d2620083f..2ce71d3a7 100644 --- a/source/client/gui/ScreenChooser.cpp +++ b/source/client/gui/ScreenChooser.cpp @@ -5,6 +5,7 @@ #include "screens/PauseScreen.hpp" #include "screens/inventory/CraftingScreen.hpp" #include "screens/inventory/ChestScreen.hpp" +#include "screens/inventory/FurnaceScreen.hpp" #include "screens/OptionsScreen.hpp" #include "screens/OptionsScreen_Console.hpp" #include "screens/CreateWorldScreen.hpp" @@ -53,6 +54,11 @@ void ScreenChooser::pushCraftingScreen(Player* player, const TilePos& pos) m_pMinecraft->setScreen(new CraftingScreen(player->m_pInventory, pos, player->m_pLevel)); } +void ScreenChooser::pushFurnaceScreen(Player* player, FurnaceTileEntity* furnace) +{ + m_pMinecraft->setScreen(new FurnaceScreen(player->m_pInventory, furnace)); +} + void ScreenChooser::pushChestScreen(Player* player, Container* container) { m_pMinecraft->setScreen(new ChestScreen(player->m_pInventory, container)); diff --git a/source/client/gui/ScreenChooser.hpp b/source/client/gui/ScreenChooser.hpp index c052f6233..e78800ade 100644 --- a/source/client/gui/ScreenChooser.hpp +++ b/source/client/gui/ScreenChooser.hpp @@ -7,6 +7,7 @@ class Player; class Minecraft; class Container; class Screen; +class FurnaceTileEntity; //@NOTE: This is just based on MCPE, not really a decompilation, make it accurate if necessary class ScreenChooser @@ -22,6 +23,7 @@ class ScreenChooser virtual void pushOptionsScreen(Screen*); virtual void pushProgressScreen(); virtual void pushCraftingScreen(Player*, const TilePos&); // originally pushWorkbenchScreen + virtual void pushFurnaceScreen(Player*, FurnaceTileEntity*); virtual void pushChestScreen(Player*, Container*); virtual void pushCreditsScreen(Screen*); diff --git a/source/client/multiplayer/MultiPlayerLevel.cpp b/source/client/multiplayer/MultiPlayerLevel.cpp index 1890674d5..66c6bcfa4 100644 --- a/source/client/multiplayer/MultiPlayerLevel.cpp +++ b/source/client/multiplayer/MultiPlayerLevel.cpp @@ -13,7 +13,7 @@ void MultiPlayerLevel::tick() for (size_t i = 0; i < 10 && i < m_reEntries.size(); i++) { Entity* pEntity = m_reEntries[i]; - if (std::find(m_entities.begin(), m_entities.end(), pEntity) != m_entities.end()) + if (m_entitiesById.find(pEntity->hashCode()) == m_entitiesById.end()) { addEntity(pEntity); } diff --git a/source/client/network/ClientSideNetworkHandler.cpp b/source/client/network/ClientSideNetworkHandler.cpp index 7b5fd9559..56f55f0df 100644 --- a/source/client/network/ClientSideNetworkHandler.cpp +++ b/source/client/network/ClientSideNetworkHandler.cpp @@ -17,6 +17,7 @@ #include "world/entity/MobFactory.hpp" #include "world/level/Explosion.hpp" #include "world/inventory/SimpleContainer.hpp" +#include "world/tile/entity/FurnaceTileEntity.hpp" // This lets you make the client shut up and not log events in the debug console. //#define VERBOSE_CLIENT @@ -683,7 +684,7 @@ void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& guid, ContainerO pLocalPlayer->openContainer(new SimpleContainer(packet->m_size, packet->m_title.C_String())); break; case Container::FURNACE: - //pLocalPlayer->openFurnace(new FurnaceTileEntity()); + pLocalPlayer->openFurnace(new FurnaceTileEntity); break; case Container::DISPENSER: //pLocalPlayer->openTrap(new DispenserTileEntity()); diff --git a/source/client/player/LocalPlayer.cpp b/source/client/player/LocalPlayer.cpp index 8db2a1dcb..6ca19d378 100644 --- a/source/client/player/LocalPlayer.cpp +++ b/source/client/player/LocalPlayer.cpp @@ -138,7 +138,7 @@ void LocalPlayer::startCrafting(const TilePos& pos) void LocalPlayer::openFurnace(FurnaceTileEntity* furnace) { // PE 0.3.2 doesn't let you cook in creative mode - m_pMinecraft->setScreen(new FurnaceScreen(m_pInventory, furnace)); + m_pMinecraft->getScreenChooser()->pushFurnaceScreen(this, furnace); } void LocalPlayer::openContainer(Container* container) diff --git a/source/client/renderer/LevelRenderer.cpp b/source/client/renderer/LevelRenderer.cpp index f9c47bc95..e30b75164 100644 --- a/source/client/renderer/LevelRenderer.cpp +++ b/source/client/renderer/LevelRenderer.cpp @@ -1584,12 +1584,12 @@ void LevelRenderer::renderEntities(Vec3 pos, Culler* culler, float f) EntityRenderDispatcher::off = camera->m_posPrev + (camera->m_pos - camera->m_posPrev) * f; - const EntityVector* pVec = m_pLevel->getAllEntities(); + const EntityMap* pVec = m_pLevel->getAllEntities(); m_totalEntities = int(pVec->size()); - for (int i = 0; i < m_totalEntities; i++) - { - const Entity* entity = (*pVec)[i]; + for (EntityMap::const_iterator it = pVec->begin(); it != pVec->end(); ++it) + { + const Entity* entity = it->second; if (!entity->shouldRender(pos)) continue; diff --git a/source/server/ServerPlayer.cpp b/source/server/ServerPlayer.cpp index c48af8dd7..21ed28339 100644 --- a/source/server/ServerPlayer.cpp +++ b/source/server/ServerPlayer.cpp @@ -9,8 +9,10 @@ #include "network/packets/ContainerSetContentPacket.hpp" #include "network/RakNetInstance.hpp" #include "world/inventory/CraftingMenu.hpp" +#include "world/inventory/FurnaceMenu.hpp" #include "world/inventory/ChestMenu.hpp" #include "world/level/Level.hpp" +#include "world/tile/entity/FurnaceTileEntity.hpp" ServerPlayer::ServerPlayer(Level* pLevel, GameType playerGameType) : Player(pLevel, playerGameType) @@ -58,7 +60,7 @@ void ServerPlayer::openContainer(Container* container) #if NETWORK_PROTOCOL_VERSION >= 5 m_pLevel->m_pRakNetInstance->send( new ContainerOpenPacket( - m_pContainerMenu->m_containerId, Container::CONTAINER, + m_containerId, Container::CONTAINER, container->getName(), container->getContainerSize() ) ); @@ -76,6 +78,22 @@ void ServerPlayer::closeContainer() doCloseContainer(); } +void ServerPlayer::openFurnace(FurnaceTileEntity* furnace) +{ + _nextContainerCounter(); + +#if NETWORK_PROTOCOL_VERSION >= 5 + m_pLevel->m_pRakNetInstance->send( + new ContainerOpenPacket( + m_containerId, Container::FURNACE, + furnace->getName(), furnace->getContainerSize() + ) + ); +#endif + + setContainerMenu(new FurnaceMenu(m_pInventory, furnace)); +} + void ServerPlayer::take(Entity* pEnt, int count) { m_pLevel->m_pRakNetInstance->send(new TakeItemEntityPacket(pEnt->m_EntityID, m_EntityID)); diff --git a/source/server/ServerPlayer.hpp b/source/server/ServerPlayer.hpp index ddc2b7d53..11152a123 100644 --- a/source/server/ServerPlayer.hpp +++ b/source/server/ServerPlayer.hpp @@ -14,6 +14,7 @@ class ServerPlayer : public Player, public ContainerListener void startCrafting(const TilePos& pos) override; void openContainer(Container* container) override; void closeContainer() override; + void openFurnace(FurnaceTileEntity* tileEntity); void take(Entity* pEnt, int count) override; void refreshContainer(ContainerMenu* menu, const std::vector& items) override; diff --git a/source/server/ServerSideNetworkHandler.cpp b/source/server/ServerSideNetworkHandler.cpp index 840207710..3cb71fd4e 100644 --- a/source/server/ServerSideNetworkHandler.cpp +++ b/source/server/ServerSideNetworkHandler.cpp @@ -218,9 +218,9 @@ void ServerSideNetworkHandler::handle(const RakNet::RakNetGUID& guid, ReadyPacke #if NETWORK_PROTOCOL_VERSION >= 3 // send the connecting player info about all entities in the world - for (size_t i = 0; i < m_pLevel->m_entities.size(); i++) + for (EntityMap::iterator it = m_pLevel->m_entities.begin(); it != m_pLevel->m_entities.end(); ++it) { - Entity* entity = m_pLevel->m_entities[i]; + Entity* entity = it->second; if (canReplicateEntity(entity)) { AddMobPacket packet(*((Mob*)entity)); diff --git a/source/world/entity/Entity.hpp b/source/world/entity/Entity.hpp index e104b01de..0869cc71b 100644 --- a/source/world/entity/Entity.hpp +++ b/source/world/entity/Entity.hpp @@ -204,8 +204,6 @@ class Entity virtual float getRidingHeight() const { return m_heightOffset; } Entity* getRiding() const; Entity* getRider() const; - // void setRiding(Entity* newRiding); - // void setRider(Entity* newRider); void load(const CompoundTag& tag); bool save(CompoundTag& tag) const; void saveWithoutId(CompoundTag& tag) const; diff --git a/source/world/entity/Pig.cpp b/source/world/entity/Pig.cpp index 0f160d6d0..7ba77895e 100644 --- a/source/world/entity/Pig.cpp +++ b/source/world/entity/Pig.cpp @@ -20,10 +20,9 @@ Pig::Pig(Level* pLevel) : Animal(pLevel) } int Pig::getDeathLoot() const { - if (isOnFire()) - return Item::porkChop_cooked->m_itemID; - else - return Item::porkChop_raw->m_itemID; + return (isOnFire()) ? + Item::porkChop_cooked->m_itemID : + Item::porkChop_raw->m_itemID; } bool Pig::interact(Player* pPlayer) diff --git a/source/world/entity/Player.cpp b/source/world/entity/Player.cpp index a22fb7e90..9d0b19685 100644 --- a/source/world/entity/Player.cpp +++ b/source/world/entity/Player.cpp @@ -541,17 +541,14 @@ void Player::drop(const ItemStack& item, bool randomly) void Player::startCrafting(const TilePos& pos) { - } void Player::openFurnace(FurnaceTileEntity* tileEntity) { - } void Player::startStonecutting(const TilePos& pos) { - } void Player::startDestroying() diff --git a/source/world/level/Level.cpp b/source/world/level/Level.cpp index c5b170c09..0c1efd729 100644 --- a/source/world/level/Level.cpp +++ b/source/world/level/Level.cpp @@ -80,10 +80,9 @@ Level::~Level() SAFE_DELETE(m_pPathFinder); SAFE_DELETE(m_pMobSpawner); - const size_t size = m_entities.size(); - for (size_t i = 0; i < size; i++) + for (EntityMap::iterator it = m_entities.begin(); it != m_entities.end(); it++) { - Entity* pEnt = m_entities.at(i); + Entity* pEnt = it->second; //you better HOPE this is freed by Minecraft! (or a NetworkHandler) //Really should have used shared pointers and stuff. @@ -358,22 +357,18 @@ Material* Level::getMaterial(const TilePos& pos) const Entity* Level::getEntity(Entity::ID id) const { - // @TODO: wtf? no map?? - // prioritize players first. for (std::vector::const_iterator it = m_players.begin(); it != m_players.end(); it++) { Player* pEnt = *it; - if (pEnt->m_EntityID == id) - return pEnt; - } - for (std::vector::const_iterator it = m_entities.begin(); it != m_entities.end(); it++) - { - Entity* pEnt = *it; - if (pEnt->m_EntityID == id) + if (pEnt->hashCode() == id) return pEnt; } + EntityMap::const_iterator it = m_entities.find(id); + if (it != m_entities.end()) + return it->second; + return nullptr; } @@ -386,7 +381,7 @@ unsigned int Level::getEntityCount(const EntityCategories& category) const return it->second; } -const EntityVector* Level::getAllEntities() const +const EntityMap* Level::getAllEntities() const { return &m_entities; } @@ -1248,7 +1243,15 @@ void Level::validateSpawn() void Level::removeAllPendingEntityRemovals() { - Util::removeAll(m_entities, m_pendingEntityRemovals); + for (EntityVector::iterator it = m_pendingEntityRemovals.begin(); it != m_pendingEntityRemovals.end(); it++) + { + Entity* ent = *it; + if (m_entities.find(ent->hashCode()) != m_entities.end()) + { + m_entities.erase(ent->hashCode()); + } + + } for (EntityVector::iterator it = m_pendingEntityRemovals.begin(); it != m_pendingEntityRemovals.end(); it++) { @@ -1326,7 +1329,7 @@ bool Level::addEntity(Entity* pEnt) m_players.push_back((Player*)pEnt); } - m_entities.push_back(pEnt); + m_entities.insert(std::make_pair(pEnt->hashCode(), pEnt)); entityAdded(pEnt); @@ -1392,9 +1395,9 @@ void Level::sendEntityData() return; // Inlined on 0.2.1, god bless PerfTimer - for (EntityVector::iterator it = m_entities.begin(); it != m_entities.end(); it++) + for (EntityMap::iterator it = m_entities.begin(); it != m_entities.end(); it++) { - Entity* ent = *it; + Entity* ent = it->second; SynchedEntityData& data = ent->getEntityData(); if (data.isDirty()) m_pRakNetInstance->send(new SetEntityDataPacket(ent->m_EntityID, data)); @@ -1786,9 +1789,9 @@ void Level::tickEntities() // inlined in the original removeAllPendingEntityRemovals(); - for (size_t i = 0; i < m_entities.size(); i++) + for (EntityMap::iterator it = m_entities.begin(); it != m_entities.end();) { - Entity* pEnt = m_entities[i]; + Entity* pEnt = it->second; if (Entity* riding = pEnt->getRiding()) { @@ -1799,24 +1802,36 @@ void Level::tickEntities() pEnt->setSharedFlag(C_ENTITY_FLAG_RIDING, false); } else + { + ++it; continue; + } } if (!pEnt->m_bRemoved) { tick(pEnt); + ++it; + + continue; } - else if (!pEnt->isPlayer() || pEnt->m_bForceRemove) + + if (!pEnt->isPlayer() || pEnt->m_bForceRemove) { if (pEnt->m_bInAChunk && hasChunk(pEnt->m_chunkPos)) getChunk(pEnt->m_chunkPos)->removeEntity(pEnt); - m_entities.erase(m_entities.begin() + i); - i--; + EntityMap::iterator itErase = it; + it++; + m_entities.erase(itErase); entityRemoved(pEnt); delete pEnt; + + continue; } + + ++it; } m_bUpdatingTileEntities = true; @@ -1834,7 +1849,7 @@ void Level::tickEntities() if (ch) ch->removeTileEntity(tileEnt->m_pos); - m_entities.erase(m_entities.begin() + i); + m_tileEntityList.erase(m_tileEntityList.begin() + i); i--; delete tileEnt; @@ -2076,11 +2091,24 @@ void Level::explode(Entity* entity, const Vec3& pos, float power, bool bIsFiery) void Level::addEntities(const EntityVector& entities) { - m_entities.insert(m_entities.end(), entities.begin(), entities.end()); - - for (EntityVector::iterator it = m_entities.begin(); it != m_entities.end(); it++) + for (EntityVector::const_iterator it = entities.begin(); it != entities.end(); it++) { Entity* pEnt = *it; + EntityMap::iterator result = m_entities.find(pEnt->hashCode()); + + if (result != m_entities.end()) + { + if (result->second == pEnt) + { + LOG_W("Entity %d already exists. Skipping...", pEnt->hashCode()); + continue; + } + + removeEntity(result->second); + continue; + } + + m_entities.insert(std::make_pair(pEnt->hashCode(), pEnt)); entityAdded(pEnt); } } @@ -2097,9 +2125,9 @@ void Level::ensureAdded(Entity* entity) for (cp.z = chunkPos.z - 2; cp.z <= chunkPos.z + 2; cp.z++) getChunk(cp); - EntityVector::iterator result = std::find(m_entities.begin(), m_entities.end(), entity); + EntityMap::iterator result = m_entities.find(entity->hashCode()); if (result == m_entities.end()) - m_entities.push_back(entity); + m_entities.insert(std::make_pair(entity->hashCode(), entity)); } bool Level::extinguishFire(Player* player, const TilePos& pos, Facing::Name face) diff --git a/source/world/level/Level.hpp b/source/world/level/Level.hpp index 95ae707ea..9caf0153e 100644 --- a/source/world/level/Level.hpp +++ b/source/world/level/Level.hpp @@ -14,6 +14,7 @@ #define _USE_MATH_DEFINES #endif #include +#include #include "client/renderer/LightUpdate.hpp" #include "world/tile/Tile.hpp" @@ -38,6 +39,7 @@ class Packet; class MobSpawner; typedef std::vector EntityVector; +typedef std::map EntityMap; typedef std::vector TileEntityVector; typedef std::vector AABBVector; @@ -183,7 +185,7 @@ class Level : public LevelSource HitResult clip(Vec3 a, Vec3 b, bool c) const; Entity* getEntity(Entity::ID id) const; unsigned int getEntityCount(const EntityCategories&) const; - const EntityVector* getAllEntities() const; + const EntityMap* getAllEntities() const; EntityVector getEntities(Entity* pAvoid, const AABB&) const; BiomeSource* getBiomeSource() const override; LevelStorage* getLevelStorage() const { return m_pLevelStorage; } @@ -219,7 +221,7 @@ class Level : public LevelSource bool m_bInstantTicking; bool m_bIsClientSide; // if the level is controlled externally by a server. bool m_bPostProcessing; - EntityVector m_entities; + EntityMap m_entities; std::vector m_players; int m_skyDarken; uint8_t field_30; diff --git a/source/world/level/storage/ExternalFileLevelStorage.cpp b/source/world/level/storage/ExternalFileLevelStorage.cpp index 681d04db0..31e8fcb35 100644 --- a/source/world/level/storage/ExternalFileLevelStorage.cpp +++ b/source/world/level/storage/ExternalFileLevelStorage.cpp @@ -400,10 +400,10 @@ void ExternalFileLevelStorage::saveEntities(Level* level, LevelChunk* chunk) //getTimeS(); ListTag* entitiesTag = new ListTag(); - const EntityVector* entities = level->getAllEntities(); - for (EntityVector::const_iterator it = entities->begin(); it != entities->end(); it++) + const EntityMap* entities = level->getAllEntities(); + for (EntityMap::const_iterator it = entities->begin(); it != entities->end(); it++) { - const Entity* entity = *it; + const Entity* entity = it->second; CompoundTag* tag = new CompoundTag(); if (!entity->save(*tag)) From d0699c4839197b656c468e8191fcb0589f975ef4 Mon Sep 17 00:00:00 2001 From: hellosammu Date: Tue, 17 Feb 2026 08:22:55 -0500 Subject: [PATCH 07/63] bounds check fix --- source/world/inventory/ContainerMenu.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/world/inventory/ContainerMenu.cpp b/source/world/inventory/ContainerMenu.cpp index 7a549e7b0..f618561e3 100644 --- a/source/world/inventory/ContainerMenu.cpp +++ b/source/world/inventory/ContainerMenu.cpp @@ -332,7 +332,8 @@ void ContainerMenu::setItem(int index, ItemStack item) void ContainerMenu::setAll(const std::vector& items) { - for (size_t i = 0; i < items.size(); ++i) + size_t n = std::min(items.size(), m_slots.size()); + for (size_t i = 0; i < n; ++i) { m_slots[i]->set(items[i]); } From 22170d2907952956801216a66f102eacb0c294b8 Mon Sep 17 00:00:00 2001 From: hellosammu Date: Tue, 17 Feb 2026 13:44:35 -0500 Subject: [PATCH 08/63] vsync, top snow fix --- platforms/android/AppPlatform_android.cpp | 9 ++++++ platforms/android/AppPlatform_android.hpp | 1 + platforms/sdl/base/AppPlatform_sdl.cpp | 7 +++++ platforms/sdl/base/AppPlatform_sdl.hpp | 1 + platforms/sdl/sdl2/main.cpp | 14 +++------ platforms/windows/AppPlatform_win32.cpp | 7 +++++ platforms/windows/AppPlatform_win32.hpp | 1 + platforms/windows/main.cpp | 2 ++ platforms/xdk360/AppPlatform_xdk360.cpp | 6 ++++ platforms/xdk360/AppPlatform_xdk360.hpp | 1 + platforms/xdk360/main.cpp | 2 ++ source/client/app/AppPlatform.cpp | 4 +++ source/client/app/AppPlatform.hpp | 2 ++ source/client/options/Options.cpp | 12 +++++-- source/client/options/Options.hpp | 12 ++++++- source/client/renderer/LevelRenderer.cpp | 4 +-- source/client/renderer/TileRenderer.cpp | 38 +++++++++++------------ source/world/tile/TopSnowTile.cpp | 18 ++++++++++- source/world/tile/TopSnowTile.hpp | 1 + 19 files changed, 107 insertions(+), 35 deletions(-) diff --git a/platforms/android/AppPlatform_android.cpp b/platforms/android/AppPlatform_android.cpp index ceb1f9872..a6cedc81b 100644 --- a/platforms/android/AppPlatform_android.cpp +++ b/platforms/android/AppPlatform_android.cpp @@ -141,6 +141,15 @@ void AppPlatform_android::setScreenSize(int width, int height) m_ScreenHeight = height; } +void AppPlatform_android::setVSyncEnabled(bool enabled) +{ + EGLDisplay display = eglGetCurrentDisplay(); + if (display == EGL_NO_DISPLAY) + return; + + glSwapInterval(display, enabled ? 1 : 0); +} + void AppPlatform_android::initAndroidApp(android_app* ptr) { m_app = ptr; diff --git a/platforms/android/AppPlatform_android.hpp b/platforms/android/AppPlatform_android.hpp index ce7f219a4..fecbbc46c 100644 --- a/platforms/android/AppPlatform_android.hpp +++ b/platforms/android/AppPlatform_android.hpp @@ -55,6 +55,7 @@ class AppPlatform_android : public AppPlatform void setExternalStoragePath(const std::string& path); AssetFile readAssetFile(const std::string&, bool) const override; + void setVSyncEnabled(bool enabled) override; private: void changeKeyboardVisibility(bool bShown); diff --git a/platforms/sdl/base/AppPlatform_sdl.cpp b/platforms/sdl/base/AppPlatform_sdl.cpp index 30de0ac72..aae613798 100644 --- a/platforms/sdl/base/AppPlatform_sdl.cpp +++ b/platforms/sdl/base/AppPlatform_sdl.cpp @@ -149,6 +149,13 @@ void AppPlatform_sdl::_setDefaultIcon() _setIcon(data); } +void AppPlatform_sdl::setVSyncEnabled(bool enabled) +{ +#if MCE_GFX_API_OGL + SDL_GL_SetSwapInterval(enabled ? 1 : 0); +#endif +} + void AppPlatform_sdl::initSoundSystem() { if (m_pSoundSystem) diff --git a/platforms/sdl/base/AppPlatform_sdl.hpp b/platforms/sdl/base/AppPlatform_sdl.hpp index c93d4f4b2..d72a97b5b 100644 --- a/platforms/sdl/base/AppPlatform_sdl.hpp +++ b/platforms/sdl/base/AppPlatform_sdl.hpp @@ -38,6 +38,7 @@ class AppPlatform_sdl : public AppPlatform int getUserInputStatus() override; void saveScreenshot(const std::string& fileName, int width, int height) override; SoundSystem* getSoundSystem() const override { return m_pSoundSystem; } + void setVSyncEnabled(bool enabled) override; // Also add these to allow proper turning within the game. void setMouseGrabbed(bool b) override; diff --git a/platforms/sdl/sdl2/main.cpp b/platforms/sdl/sdl2/main.cpp index fa228b636..0fc0713a4 100644 --- a/platforms/sdl/sdl2/main.cpp +++ b/platforms/sdl/sdl2/main.cpp @@ -63,16 +63,11 @@ static void initGraphics() exit(EXIT_FAILURE); } - // Enable V-Sync - // Not setting this explicitly results in undefined behavior - if (SDL_GL_SetSwapInterval(-1) == -1) // Try adaptive + // Vsync is controlled through the AppPlatform, + // default to no vsync here, let platform set it when needed + if (SDL_GL_SetSwapInterval(0) == -1) { - LOG_W("Adaptive V-Sync is not supported on this platform. Falling back to standard V-Sync..."); - // fallback to standard - if (SDL_GL_SetSwapInterval(1) == -1) - { - LOG_W("Setting the swap interval for V-Sync is not supported on this platform!"); - } + LOG_W("Setting the swap interval is not supported on this platform!"); } if (!mce::Platform::OGL::InitBindings()) @@ -430,6 +425,7 @@ int main(int argc, char *argv[]) // Start MCPE g_pAppPlatform = new UsedAppPlatform(storagePath, window); g_pAppPlatform->m_externalStorageDir = storagePath; + g_pAppPlatform->setVSyncEnabled(true); g_pApp = new NinecraftApp; g_pApp->m_pPlatform = g_pAppPlatform; g_pApp->init(); diff --git a/platforms/windows/AppPlatform_win32.cpp b/platforms/windows/AppPlatform_win32.cpp index f54c64812..674b5bf3b 100644 --- a/platforms/windows/AppPlatform_win32.cpp +++ b/platforms/windows/AppPlatform_win32.cpp @@ -484,6 +484,13 @@ bool AppPlatform_win32::initGraphics(int width, int height) return true; } +void AppPlatform_win32::setVSyncEnabled(bool enabled) +{ +#if MCE_GFX_API_OGL + xglSwapIntervalEXT(enabled ? 1 : 0); +#endif +} + void AppPlatform_win32::createWindowSizeDependentResources(const Vec2& logicalSize, const Vec2& compositionScale) { #if MCE_GFX_API_D3D9 diff --git a/platforms/windows/AppPlatform_win32.hpp b/platforms/windows/AppPlatform_win32.hpp index 1dc3bb7e0..d047243e9 100644 --- a/platforms/windows/AppPlatform_win32.hpp +++ b/platforms/windows/AppPlatform_win32.hpp @@ -82,6 +82,7 @@ class AppPlatform_win32 : public AppPlatform bool initGraphics(int width, int height); void createWindowSizeDependentResources(const Vec2& logicalSize, const Vec2& compositionScale); void swapBuffers(); + void setVSyncEnabled(bool enabled) override; static MouseButtonType GetMouseButtonType(UINT iMsg); static bool GetMouseButtonState(UINT iMsg, WPARAM wParam); diff --git a/platforms/windows/main.cpp b/platforms/windows/main.cpp index 00f703ea1..b614ca405 100644 --- a/platforms/windows/main.cpp +++ b/platforms/windows/main.cpp @@ -152,6 +152,8 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine if (!g_AppPlatform.initGraphics(Minecraft::width, Minecraft::height)) goto _cleanup; + g_AppPlatform.setVSyncEnabled(true); + g_pApp = new NinecraftApp; g_pApp->m_pPlatform = &g_AppPlatform; diff --git a/platforms/xdk360/AppPlatform_xdk360.cpp b/platforms/xdk360/AppPlatform_xdk360.cpp index 0cf1313db..33fea93f4 100644 --- a/platforms/xdk360/AppPlatform_xdk360.cpp +++ b/platforms/xdk360/AppPlatform_xdk360.cpp @@ -319,6 +319,12 @@ const std::string& AppPlatform_xdk360::getKeyboardText() const return m_keyboardText; } +void AppPlatform_xdk360::setVSyncEnabled(bool enabled) +{ + // XDK360 V-Sync control would go here + // For Xbox 360, this is typically handled by the D3D device settings +} + bool AppPlatform_xdk360::initGraphics(unsigned int width, unsigned int height) { m_bHasGraphics = true; diff --git a/platforms/xdk360/AppPlatform_xdk360.hpp b/platforms/xdk360/AppPlatform_xdk360.hpp index d1409aaff..d2934aa71 100644 --- a/platforms/xdk360/AppPlatform_xdk360.hpp +++ b/platforms/xdk360/AppPlatform_xdk360.hpp @@ -52,6 +52,7 @@ class AppPlatform_xdk360 : public AppPlatform bool initGraphics(unsigned int width, unsigned int height); void createWindowSizeDependentResources(unsigned int width, unsigned int height); void swapBuffers(); + void setVSyncEnabled(bool enabled) override; private: int m_ScreenWidth; diff --git a/platforms/xdk360/main.cpp b/platforms/xdk360/main.cpp index 2af6b639a..06d09f0b7 100644 --- a/platforms/xdk360/main.cpp +++ b/platforms/xdk360/main.cpp @@ -45,6 +45,8 @@ void __cdecl main() if (!g_AppPlatform.initGraphics(Minecraft::width, Minecraft::height)) goto _cleanup; + g_AppPlatform.setVSyncEnabled(true); + g_pApp = new NinecraftApp; g_pApp->m_pPlatform = &g_AppPlatform; g_AppPlatform.m_externalStorageDir = "savedrive:"; diff --git a/source/client/app/AppPlatform.cpp b/source/client/app/AppPlatform.cpp index ef28f0f4b..74713c1ca 100644 --- a/source/client/app/AppPlatform.cpp +++ b/source/client/app/AppPlatform.cpp @@ -320,6 +320,10 @@ std::string AppPlatform::getExternalStoragePath(const std::string& path) const return m_externalStorageDir + C_HOME_PATH + path; } +void AppPlatform::setVSyncEnabled(bool enabled) +{ +} + bool AppPlatform::hasAssetFile(const std::string& path) const { return isRegularFile(path.c_str()); diff --git a/source/client/app/AppPlatform.hpp b/source/client/app/AppPlatform.hpp index 9cf1b6cc6..26d74bbd6 100644 --- a/source/client/app/AppPlatform.hpp +++ b/source/client/app/AppPlatform.hpp @@ -107,6 +107,8 @@ class AppPlatform virtual void vibrate(int milliSeconds); virtual bool getRecenterMouseEveryTick(); virtual std::string getClipboardText(); + // Graphics settings + virtual void setVSyncEnabled(bool enabled); void _fireLowMemory(); void _fireAppSuspended(); diff --git a/source/client/options/Options.cpp b/source/client/options/Options.cpp index 546f53a58..c6d24cb34 100644 --- a/source/client/options/Options.cpp +++ b/source/client/options/Options.cpp @@ -98,7 +98,7 @@ Options::Options(Minecraft* mc, const std::string& folderPath) : //, m_limitFramerate("gfx_fpslimit", "options.framerateLimit", 0, ValuesBuilder().add(performance.max").add("performance.balanced").add("performance.powersaver")) //, m_bMipmaps("gfx_mipmaps", "options.mipmaps") //, m_moreWorldOptions("misc_moreworldoptions", "options.moreWorldOptions", true) - //, m_vSync("enableVsync", "options.enableVsync") + , m_vSync("enableVsync", "options.enableVsync") { add(m_musicVolume); add(m_masterVolume); @@ -127,10 +127,11 @@ Options::Options(Minecraft* mc, const std::string& folderPath) : add(m_debugText); add(m_lang); add(m_bUseController); + add(m_hudSize); add(m_uiTheme); add(m_logoType); - add(m_hudSize); add(m_classicCrafting); + add(m_vSync); _initDefaultValues(); if (folderPath.empty()) return; m_filePath = folderPath + "/options.txt"; @@ -812,4 +813,9 @@ void ControllerOption::apply() // For now, I just wanted to be able to switch to controller input on mobile devices. if (m_pMinecraft && m_pMinecraft->m_pInputHolder) m_pMinecraft->reloadInput(); -} \ No newline at end of file +} + +void VsyncOption::apply() +{ + m_pMinecraft->platform()->setVSyncEnabled(get()); +} diff --git a/source/client/options/Options.hpp b/source/client/options/Options.hpp index 1fe458dae..72cce0703 100644 --- a/source/client/options/Options.hpp +++ b/source/client/options/Options.hpp @@ -222,6 +222,14 @@ class ControllerOption : public BoolOption void apply() override; }; +class VsyncOption : public BoolOption +{ +public: + VsyncOption(const std::string& key, const std::string& name, bool initial = true) : BoolOption(key, name, initial) {} + + void apply() override; +}; + class FancyGraphicsOption : public GraphicsOption { public: @@ -326,7 +334,7 @@ class UIThemeOption : public ValuesOption class HUDSizeOption : public MinMaxOption { public: - HUDSizeOption(const std::string& key, const std::string& name, int initial) : MinMaxOption(key, name, initial, HUD_SIZE_1, HUD_SIZE_3 + 1) + HUDSizeOption(const std::string& key, const std::string& name, int initial) : MinMaxOption(key, name, initial, HUD_SIZE_1, HUD_SIZE_MAX + 1) { } @@ -438,6 +446,7 @@ class Options LogoTypeOption m_logoType; HUDSizeOption m_hudSize; BoolOption m_classicCrafting; + VsyncOption m_vSync; ResourcePackStack m_resourcePacks; }; @@ -483,6 +492,7 @@ class Options OPTION(m_viewBobbing); \ OPTION(m_anaglyphs); \ OPTION(m_blockOutlines); \ + OPTION(m_vSync); \ OPTION(m_fancyGrass); \ OPTION(m_biomeColors); \ OPTION(m_dynamicHand); \ diff --git a/source/client/renderer/LevelRenderer.cpp b/source/client/renderer/LevelRenderer.cpp index e30b75164..ef388e89f 100644 --- a/source/client/renderer/LevelRenderer.cpp +++ b/source/client/renderer/LevelRenderer.cpp @@ -1137,7 +1137,7 @@ void LevelRenderer::tick() typedef std::vector ChunkVector; typedef ChunkVector::iterator ChunkVectorIterator; -bool LevelRenderer::updateDirtyChunks(const Entity& camera, bool b) +bool LevelRenderer::updateDirtyChunks(const Entity& camera, bool force) { constexpr int C_MAX = 3; DirtyChunkSorter dcs(camera); @@ -1148,7 +1148,7 @@ bool LevelRenderer::updateDirtyChunks(const Entity& camera, bool b) for (size_t i = 0; i < pendingChunkSize; i++) { Chunk* pChunk = m_dirtyChunks[i]; - if (!b) + if (!force) { if (pChunk->distanceToSqr(camera) > 1024.0f) { diff --git a/source/client/renderer/TileRenderer.cpp b/source/client/renderer/TileRenderer.cpp index ed07fc37b..026c42d68 100644 --- a/source/client/renderer/TileRenderer.cpp +++ b/source/client/renderer/TileRenderer.cpp @@ -204,7 +204,7 @@ void TileRenderer::renderEast(Tile* tile, const Vec3& pos, int texture) texV_d = C_RATIO * (texY + aabb.max.y * 16.0f - 0.01f); } - Tesselator& t = Tesselator::instance; + Tesselator& t = m_tessellator; if (m_ambientOcclusion) { @@ -281,7 +281,7 @@ void TileRenderer::renderWest(Tile* tile, const Vec3& pos, int texture) texV_d = C_RATIO * (texY + aabb.max.y * 16.0f - 0.01f); } - Tesselator& t = Tesselator::instance; + Tesselator& t = m_tessellator; if (m_ambientOcclusion) { @@ -358,7 +358,7 @@ void TileRenderer::renderSouth(Tile* tile, const Vec3& pos, int texture) texV_d = C_RATIO * (texY + aabb.max.y * 16.0f - 0.01f); } - Tesselator& t = Tesselator::instance; + Tesselator& t = m_tessellator; if (m_ambientOcclusion) { @@ -435,7 +435,7 @@ void TileRenderer::renderNorth(Tile* tile, const Vec3& pos, int texture) texV_d = C_RATIO * (texY + aabb.max.y * 16.0f - 0.01f); } - Tesselator& t = Tesselator::instance; + Tesselator& t = m_tessellator; if (m_ambientOcclusion) { @@ -506,7 +506,7 @@ void TileRenderer::renderFaceDown(Tile* tile, const Vec3& pos, int texture) texV_2 = C_RATIO * (texY + 15.99f); } - Tesselator& t = Tesselator::instance; + Tesselator& t = m_tessellator; if (m_ambientOcclusion) { @@ -577,7 +577,7 @@ void TileRenderer::renderFaceUp(Tile* tile, const Vec3& pos, int texture) texV_2 = C_RATIO * (texY + 15.99f); } - Tesselator& t = Tesselator::instance; + Tesselator& t = m_tessellator; if (m_ambientOcclusion) { @@ -641,7 +641,7 @@ void TileRenderer::tesselateCrossTexture(const FullTile& tile, const Vec3& pos, float x1 = cenX - 0.45f, x2 = cenX + 0.45f; float z1 = cenZ - 0.45f, z2 = cenZ + 0.45f; - Tesselator& t = Tesselator::instance; + Tesselator& t = m_tessellator; // face 1 t.vertexUV(x1, newY + 1, z1, texU_l, texV_u); t.vertexUV(x1, newY + 0, z1, texU_l, texV_d); @@ -693,7 +693,7 @@ void TileRenderer::tesselateRowTexture(Tile* tile, int data, const Vec3& pos) float x0 = pos.x + 0.25f, x1 = pos.x + 0.75f; float z0 = pos.z, z1 = pos.z + 1.0f; - Tesselator& t = Tesselator::instance; + Tesselator& t = m_tessellator; t.vertexUV(x0, pos.y + 1.0, z0, u0, v0); t.vertexUV(x0, pos.y + 0.0, z0, u0, v1); t.vertexUV(x0, pos.y + 0.0, z1, u1, v1); @@ -739,7 +739,7 @@ bool TileRenderer::tesselateBlockInWorld(Tile* tile, const TilePos& pos, float r if (tile == Tile::grass) r = g = b = 1.0f; - Tesselator& t = Tesselator::instance; + Tesselator& t = m_tessellator; float fLightHere = tile->getBrightness(m_pTileSource, pos); bool bDrewAnything = false; @@ -871,7 +871,7 @@ bool TileRenderer::tesselateBlockInWorld(Tile* tile, const TilePos& pos) bool TileRenderer::tesselateCrossInWorld(Tile* tile, const TilePos& pos) { - Tesselator& t = Tesselator::instance; + Tesselator& t = m_tessellator; float bright = tile->getBrightness(m_pTileSource, pos); int color = getTileColor(tile, pos); @@ -888,7 +888,7 @@ bool TileRenderer::tesselateCrossInWorld(Tile* tile, const TilePos& pos) bool TileRenderer::tesselateRowInWorld(Tile* tile, const TilePos& pos) { - Tesselator& t = Tesselator::instance; + Tesselator& t = m_tessellator; Color color = getTileColor(tile, pos); color.a = 1.0f; @@ -906,7 +906,7 @@ bool TileRenderer::tesselateWaterInWorld(Tile* tile1, const TilePos& pos) LiquidTile* tile = (LiquidTile*)tile1; bool bRenderFaceDown, bRenderFaceUp, bRenderSides[4]; - Tesselator& t = Tesselator::instance; + Tesselator& t = m_tessellator; bRenderFaceDown = tile->shouldRenderFace(m_pTileSource, pos.above(), Facing::UP); bRenderFaceUp = tile->shouldRenderFace(m_pTileSource, pos.below(), Facing::DOWN); @@ -1217,7 +1217,7 @@ bool TileRenderer::tesselateFenceInWorld(Tile* tile, const TilePos& pos) bool TileRenderer::tesselateDoorInWorld(Tile* tile, const TilePos& pos) { - Tesselator& t = Tesselator::instance; + Tesselator& t = m_tessellator; float fBrightHere = tile->getBrightness(m_pTileSource, pos), fBright; int texture; @@ -1298,7 +1298,7 @@ void TileRenderer::tesselateTorch(Tile* tile, const Vec3& pos, float a, float b) float x2 = x1 + (float)(a * C_TOP_SKEW_RATIO); float z2 = z1 + (float)(b * C_TOP_SKEW_RATIO); - Tesselator& t = Tesselator::instance; + Tesselator& t = m_tessellator; // Top side (flame) float x_1 = x2 - C_ONE_PIXEL; @@ -1371,7 +1371,7 @@ bool TileRenderer::tesselateTorchInWorld(Tile* tile, const TilePos& pos) if (Tile::lightEmission[tile->m_ID] > 0) bright = 1.0f; - Tesselator& t = Tesselator::instance; + Tesselator& t = m_tessellator; t.color(bright, bright, bright); switch (data) @@ -1400,7 +1400,7 @@ bool TileRenderer::tesselateLadderInWorld(Tile* tile, const TilePos& pos) { constexpr float C_RATIO = 1.0f / 256.0f; - Tesselator& t = Tesselator::instance; + Tesselator& t = m_tessellator; int texture = tile->getTexture(Facing::DOWN); @@ -1453,7 +1453,7 @@ bool TileRenderer::tesselateFireInWorld(Tile* tile, const TilePos& pos) { constexpr float C_RATIO = 1.0f / 256.0f; - Tesselator& t = Tesselator::instance; + Tesselator& t = m_tessellator; int texture = tile->getTexture(Facing::DOWN); float bright = tile->getBrightness(m_pTileSource, pos); @@ -2691,7 +2691,7 @@ bool TileRenderer::tesselateBlockInWorldWithAmbienceOcclusion(Tile* a2, const Ti void TileRenderer::renderTile(const FullTile& tile, const mce::MaterialPtr& material, float bright, bool preshade) { - Tesselator& t = Tesselator::instance; + Tesselator& t = m_tessellator; Tile* tileType = tile.getType(); #ifndef ENH_SHADE_HELD_TILES @@ -2926,7 +2926,7 @@ bool TileRenderer::tesselateBlockInWorldWithAmbienceOcclusionV2(Tile* tile, cons if (tile == Tile::grass) r = g = b = 1.0f; - //Tesselator& t = Tesselator::instance; + //Tesselator& t = m_tessellator; //float fLightHere = tile->getBrightness(m_pTileSource, pos); diff --git a/source/world/tile/TopSnowTile.cpp b/source/world/tile/TopSnowTile.cpp index b974b308b..9bec5bc70 100644 --- a/source/world/tile/TopSnowTile.cpp +++ b/source/world/tile/TopSnowTile.cpp @@ -32,7 +32,7 @@ bool TopSnowTile::isSolidRender() const int TopSnowTile::getResource(TileData data, Random* random) const { - return 0; + return Item::snowBall->m_itemID; } int TopSnowTile::getResourceCount(Random* random) const @@ -84,3 +84,19 @@ void TopSnowTile::tick(Level* level, const TilePos& pos, Random* random) level->setTile(pos, TILE_AIR); } } + +void TopSnowTile::playerDestroy(Level* level, Player* player, const TilePos& pos, TileData data) +{ + float dispersion = 0.7f; + + Vec3 offset ( + (level->m_random.nextFloat() * dispersion) + (1.0f - dispersion) * 0.5f, + (level->m_random.nextFloat() * dispersion) + (1.0f - dispersion) * 0.5f, + (level->m_random.nextFloat() * dispersion) + (1.0f - dispersion) * 0.5f + ); + + ItemEntity* pItemEntity = new ItemEntity(level, Vec3(pos) + offset, ItemStack(getResource(data, &level->m_random), 1, 0)); + pItemEntity->m_throwTime = 10; + level->addEntity(pItemEntity); + level->setTile(pos, TILE_AIR); +} diff --git a/source/world/tile/TopSnowTile.hpp b/source/world/tile/TopSnowTile.hpp index 82af8e659..cafd89ea2 100644 --- a/source/world/tile/TopSnowTile.hpp +++ b/source/world/tile/TopSnowTile.hpp @@ -24,6 +24,7 @@ class TopSnowTile : public Tile void neighborChanged(Level*, const TilePos& pos, TileID tile) override; bool shouldRenderFace(const LevelSource*, const TilePos& pos, Facing::Name face) const override; void tick(Level*, const TilePos& pos, Random*) override; + virtual void playerDestroy(Level*, Player*, const TilePos& pos, TileData data); bool checkCanSurvive(Level*, const TilePos& pos); }; From 837b7412c8900d3c6cb3c1151bfa81efb0a360d9 Mon Sep 17 00:00:00 2001 From: hellosammu Date: Tue, 17 Feb 2026 13:58:15 -0500 Subject: [PATCH 09/63] drop sand --- platforms/xdk360/AppPlatform_xdk360.cpp | 4 ++-- source/client/options/Options.hpp | 2 +- source/world/entity/FallingTile.cpp | 5 ++++- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/platforms/xdk360/AppPlatform_xdk360.cpp b/platforms/xdk360/AppPlatform_xdk360.cpp index 33fea93f4..f9fe54f86 100644 --- a/platforms/xdk360/AppPlatform_xdk360.cpp +++ b/platforms/xdk360/AppPlatform_xdk360.cpp @@ -321,8 +321,8 @@ const std::string& AppPlatform_xdk360::getKeyboardText() const void AppPlatform_xdk360::setVSyncEnabled(bool enabled) { - // XDK360 V-Sync control would go here - // For Xbox 360, this is typically handled by the D3D device settings + // You have to reset/recreate the D3D device to change the vsync setting + // @TODO: Someone with a windows machine or xbox please do and test this. } bool AppPlatform_xdk360::initGraphics(unsigned int width, unsigned int height) diff --git a/source/client/options/Options.hpp b/source/client/options/Options.hpp index 72cce0703..b15eb695b 100644 --- a/source/client/options/Options.hpp +++ b/source/client/options/Options.hpp @@ -334,7 +334,7 @@ class UIThemeOption : public ValuesOption class HUDSizeOption : public MinMaxOption { public: - HUDSizeOption(const std::string& key, const std::string& name, int initial) : MinMaxOption(key, name, initial, HUD_SIZE_1, HUD_SIZE_MAX + 1) + HUDSizeOption(const std::string& key, const std::string& name, int initial) : MinMaxOption(key, name, initial, HUD_SIZE_1, HUD_SIZE_3 + 1) { } diff --git a/source/world/entity/FallingTile.cpp b/source/world/entity/FallingTile.cpp index 8fb89b6a9..23c028573 100644 --- a/source/world/entity/FallingTile.cpp +++ b/source/world/entity/FallingTile.cpp @@ -66,7 +66,10 @@ void FallingTile::tick() if (!m_bOnGround) { if (field_E0 > 100 && !m_pLevel->m_bIsClientSide) + { + spawnAtLocation(m_id, 1); remove(); + } return; } @@ -81,7 +84,7 @@ void FallingTile::tick() } else { - // @TODO: spawn resources? + spawnAtLocation(m_id, 1); } } From da33efab2a2597efa36c6dd445b51f8c488d4e21 Mon Sep 17 00:00:00 2001 From: hellosammu Date: Tue, 17 Feb 2026 19:43:33 -0500 Subject: [PATCH 10/63] containers shouldnt look stupid --- platforms/android/AppPlatform_android.cpp | 5 +++++ platforms/android/AppPlatform_android.hpp | 1 + platforms/sdl/base/AppPlatform_sdl.cpp | 9 +++++++++ platforms/sdl/base/AppPlatform_sdl.hpp | 1 + platforms/windows/AppPlatform_win32.cpp | 9 +++++++++ platforms/windows/AppPlatform_win32.hpp | 1 + platforms/xdk360/AppPlatform_xdk360.cpp | 5 +++++ platforms/xdk360/AppPlatform_xdk360.hpp | 1 + source/client/app/AppPlatform.cpp | 5 +++++ source/client/app/AppPlatform.hpp | 1 + source/client/gui/screens/inventory/ChestScreen.cpp | 7 +++---- source/client/options/Options.cpp | 5 ++++- source/world/inventory/ChestMenu.cpp | 6 ++++-- 13 files changed, 49 insertions(+), 7 deletions(-) diff --git a/platforms/android/AppPlatform_android.cpp b/platforms/android/AppPlatform_android.cpp index a6cedc81b..a2aacf87c 100644 --- a/platforms/android/AppPlatform_android.cpp +++ b/platforms/android/AppPlatform_android.cpp @@ -150,6 +150,11 @@ void AppPlatform_android::setVSyncEnabled(bool enabled) glSwapInterval(display, enabled ? 1 : 0); } +bool AppPlatform_android::isVsyncSwitchable() const +{ + return eglGetCurrentDisplay() != EGL_NO_DISPLAY; +} + void AppPlatform_android::initAndroidApp(android_app* ptr) { m_app = ptr; diff --git a/platforms/android/AppPlatform_android.hpp b/platforms/android/AppPlatform_android.hpp index fecbbc46c..b18433f5c 100644 --- a/platforms/android/AppPlatform_android.hpp +++ b/platforms/android/AppPlatform_android.hpp @@ -56,6 +56,7 @@ class AppPlatform_android : public AppPlatform AssetFile readAssetFile(const std::string&, bool) const override; void setVSyncEnabled(bool enabled) override; + bool isVsyncSwitchable() const override; private: void changeKeyboardVisibility(bool bShown); diff --git a/platforms/sdl/base/AppPlatform_sdl.cpp b/platforms/sdl/base/AppPlatform_sdl.cpp index aae613798..1d21c36c8 100644 --- a/platforms/sdl/base/AppPlatform_sdl.cpp +++ b/platforms/sdl/base/AppPlatform_sdl.cpp @@ -156,6 +156,15 @@ void AppPlatform_sdl::setVSyncEnabled(bool enabled) #endif } +bool AppPlatform_sdl::isVsyncSwitchable() const +{ +#if MCE_GFX_API_OGL + return true; +#else + return false; +#endif +} + void AppPlatform_sdl::initSoundSystem() { if (m_pSoundSystem) diff --git a/platforms/sdl/base/AppPlatform_sdl.hpp b/platforms/sdl/base/AppPlatform_sdl.hpp index d72a97b5b..976668744 100644 --- a/platforms/sdl/base/AppPlatform_sdl.hpp +++ b/platforms/sdl/base/AppPlatform_sdl.hpp @@ -39,6 +39,7 @@ class AppPlatform_sdl : public AppPlatform void saveScreenshot(const std::string& fileName, int width, int height) override; SoundSystem* getSoundSystem() const override { return m_pSoundSystem; } void setVSyncEnabled(bool enabled) override; + bool isVsyncSwitchable() const override; // Also add these to allow proper turning within the game. void setMouseGrabbed(bool b) override; diff --git a/platforms/windows/AppPlatform_win32.cpp b/platforms/windows/AppPlatform_win32.cpp index 674b5bf3b..b177fef9a 100644 --- a/platforms/windows/AppPlatform_win32.cpp +++ b/platforms/windows/AppPlatform_win32.cpp @@ -491,6 +491,15 @@ void AppPlatform_win32::setVSyncEnabled(bool enabled) #endif } +bool AppPlatform_win32::isVsyncSwitchable() const +{ +#if MCE_GFX_API_OGL + return true; +#else + return false; +#endif +} + void AppPlatform_win32::createWindowSizeDependentResources(const Vec2& logicalSize, const Vec2& compositionScale) { #if MCE_GFX_API_D3D9 diff --git a/platforms/windows/AppPlatform_win32.hpp b/platforms/windows/AppPlatform_win32.hpp index d047243e9..bac0f7eda 100644 --- a/platforms/windows/AppPlatform_win32.hpp +++ b/platforms/windows/AppPlatform_win32.hpp @@ -83,6 +83,7 @@ class AppPlatform_win32 : public AppPlatform void createWindowSizeDependentResources(const Vec2& logicalSize, const Vec2& compositionScale); void swapBuffers(); void setVSyncEnabled(bool enabled) override; + bool isVsyncSwitchable() const override; static MouseButtonType GetMouseButtonType(UINT iMsg); static bool GetMouseButtonState(UINT iMsg, WPARAM wParam); diff --git a/platforms/xdk360/AppPlatform_xdk360.cpp b/platforms/xdk360/AppPlatform_xdk360.cpp index f9fe54f86..8b4ec16a6 100644 --- a/platforms/xdk360/AppPlatform_xdk360.cpp +++ b/platforms/xdk360/AppPlatform_xdk360.cpp @@ -325,6 +325,11 @@ void AppPlatform_xdk360::setVSyncEnabled(bool enabled) // @TODO: Someone with a windows machine or xbox please do and test this. } +bool AppPlatform_xdk360::isVsyncSwitchable() const +{ + return false; +} + bool AppPlatform_xdk360::initGraphics(unsigned int width, unsigned int height) { m_bHasGraphics = true; diff --git a/platforms/xdk360/AppPlatform_xdk360.hpp b/platforms/xdk360/AppPlatform_xdk360.hpp index d2934aa71..d1fb3b859 100644 --- a/platforms/xdk360/AppPlatform_xdk360.hpp +++ b/platforms/xdk360/AppPlatform_xdk360.hpp @@ -53,6 +53,7 @@ class AppPlatform_xdk360 : public AppPlatform void createWindowSizeDependentResources(unsigned int width, unsigned int height); void swapBuffers(); void setVSyncEnabled(bool enabled) override; + bool isVsyncSwitchable() const override; private: int m_ScreenWidth; diff --git a/source/client/app/AppPlatform.cpp b/source/client/app/AppPlatform.cpp index 74713c1ca..7eba59212 100644 --- a/source/client/app/AppPlatform.cpp +++ b/source/client/app/AppPlatform.cpp @@ -324,6 +324,11 @@ void AppPlatform::setVSyncEnabled(bool enabled) { } +bool AppPlatform::isVsyncSwitchable() const +{ + return false; +} + bool AppPlatform::hasAssetFile(const std::string& path) const { return isRegularFile(path.c_str()); diff --git a/source/client/app/AppPlatform.hpp b/source/client/app/AppPlatform.hpp index 26d74bbd6..b7852bd21 100644 --- a/source/client/app/AppPlatform.hpp +++ b/source/client/app/AppPlatform.hpp @@ -109,6 +109,7 @@ class AppPlatform virtual std::string getClipboardText(); // Graphics settings virtual void setVSyncEnabled(bool enabled); + virtual bool isVsyncSwitchable() const; void _fireLowMemory(); void _fireAppSuspended(); diff --git a/source/client/gui/screens/inventory/ChestScreen.cpp b/source/client/gui/screens/inventory/ChestScreen.cpp index 312618638..2df77fff9 100644 --- a/source/client/gui/screens/inventory/ChestScreen.cpp +++ b/source/client/gui/screens/inventory/ChestScreen.cpp @@ -31,15 +31,14 @@ SlotDisplay ChestScreen::_createSlotDisplay(const Slot& slot) { constexpr int slotSize = 18; int rows = m_pContainer->getContainerSize() / 9; - int verticalOffset = (rows - 4) * slotSize; switch (slot.m_group) { case Slot::CONTAINER: - return SlotDisplay(8 + (slot.m_slot % 9) * slotSize, 18 + verticalOffset + ((slot.m_slot / 9) - 1) * slotSize, slotSize); + return SlotDisplay(8 + (slot.m_slot % 9) * slotSize, 18 + (slot.m_slot / 9) * slotSize, slotSize); case Slot::INVENTORY: - return SlotDisplay(8 + (slot.m_slot % 9) * slotSize, 103 + verticalOffset + ((slot.m_slot / 9) - 1) * slotSize, slotSize); + return SlotDisplay(8 + (slot.m_slot % 9) * slotSize, (rows * 18) + 13 + (slot.m_slot / 9) * slotSize, slotSize); case Slot::HOTBAR: - return SlotDisplay(8 + (slot.m_slot % 9) * slotSize, 142, slotSize); + return SlotDisplay(8 + (slot.m_slot % 9) * slotSize, 89 + rows * slotSize, slotSize); default: return SlotDisplay(); } diff --git a/source/client/options/Options.cpp b/source/client/options/Options.cpp index c6d24cb34..f99e36c2f 100644 --- a/source/client/options/Options.cpp +++ b/source/client/options/Options.cpp @@ -98,7 +98,7 @@ Options::Options(Minecraft* mc, const std::string& folderPath) : //, m_limitFramerate("gfx_fpslimit", "options.framerateLimit", 0, ValuesBuilder().add(performance.max").add("performance.balanced").add("performance.powersaver")) //, m_bMipmaps("gfx_mipmaps", "options.mipmaps") //, m_moreWorldOptions("misc_moreworldoptions", "options.moreWorldOptions", true) - , m_vSync("enableVsync", "options.enableVsync") + , m_vSync("enableVsync", "options.enableVsync", true) { add(m_musicVolume); add(m_masterVolume); @@ -676,6 +676,9 @@ void Options::initResourceDependentOptions() if (!Screen::isMenuPanoramaAvailable()) m_menuPanorama.set(false); + + if (!m_pMinecraft->platform()->isVsyncSwitchable()) + m_vSync.set(false); } const std::string& OptionEntry::getDisplayName() const diff --git a/source/world/inventory/ChestMenu.cpp b/source/world/inventory/ChestMenu.cpp index 95a78894f..7766dd63c 100644 --- a/source/world/inventory/ChestMenu.cpp +++ b/source/world/inventory/ChestMenu.cpp @@ -7,16 +7,18 @@ ChestMenu::ChestMenu(Container* inventory, Container* container) { int rows = m_pContainer->getContainerSize() / 9; + // Chest slots for (int row = 0; row < rows; ++row) { for (int col = 0; col < 9; ++col) - addSlot(new Slot(m_pContainer, col + row * 9)); + addSlot(new Slot(m_pContainer, col + row * 9, Slot::CONTAINER)); } + // Inventory slots for (int row = 0; row < 3; ++row) { for (int col = 0; col < 9; ++col) - addSlot(new Slot(inventory, col + row * 9 + 9)); + addSlot(new Slot(inventory, col + row * 9 + 9, Slot::INVENTORY)); } for (int col = 0; col < 9; ++col) From fa895e26b5439b840badc5477853909e003ee321 Mon Sep 17 00:00:00 2001 From: hellosammu Date: Tue, 17 Feb 2026 20:15:46 -0500 Subject: [PATCH 11/63] naming conventions, not done yet --- source/client/renderer/Chunk.cpp | 30 ++++++++++----------- source/client/renderer/Chunk.hpp | 6 ++--- source/client/renderer/LevelRenderer.cpp | 2 +- source/client/renderer/LevelRenderer.hpp | 2 +- source/world/entity/FallingTile.cpp | 4 +-- source/world/tile/entity/TileEntityType.cpp | 12 ++++----- source/world/tile/entity/TileEntityType.hpp | 10 +++---- 7 files changed, 33 insertions(+), 33 deletions(-) diff --git a/source/client/renderer/Chunk.cpp b/source/client/renderer/Chunk.cpp index ec7d6db5e..aed0ab5c7 100644 --- a/source/client/renderer/Chunk.cpp +++ b/source/client/renderer/Chunk.cpp @@ -133,8 +133,8 @@ void Chunk::rebuild() LevelChunk::touchedSky = false; - std::set tmpSet(m_renderableTileEntities.begin(), m_renderableTileEntities.end()); - m_renderableTileEntities.clear(); + std::set tmpSet(m_tileEntities.begin(), m_tileEntities.end()); + m_tileEntities.clear(); for (int i = Tile::RENDER_LAYERS_MIN; i <= Tile::RENDER_LAYERS_MAX; i++) { @@ -178,7 +178,7 @@ void Chunk::rebuild() // @TODO: ADD TILE ENTITY RENDER DISPATCHER TileEntity* et = region.getTileEntity(tp); if (TileEntityRenderDispatcher::getInstance()->hasRenderer(et)) - m_renderableTileEntities.push_back(et); + m_tileEntities.push_back(et); */ } @@ -214,8 +214,8 @@ void Chunk::rebuild() break; } - std::set newSet(m_renderableTileEntities.begin(), m_renderableTileEntities.end()); - std::vector toAdd, toRemove; + std::set newSet(m_tileEntities.begin(), m_tileEntities.end()); + TileEntityVector toAdd, toRemove; std::set_difference( newSet.begin(), newSet.end(), @@ -230,29 +230,29 @@ void Chunk::rebuild() ); // Add - for (std::vector::iterator it = toAdd.begin(); it != toAdd.end(); ++it) + for (TileEntityVector::iterator it = toAdd.begin(); it != toAdd.end(); ++it) { - m_globalRenderableTileEntities.push_back(*it); + m_globalTileEntities.push_back(*it); } // Remove - for (std::vector::iterator it = toRemove.begin(); it != toRemove.end(); ++it) + for (TileEntityVector::iterator it = toRemove.begin(); it != toRemove.end(); ++it) { - std::vector::iterator f = - std::find(m_globalRenderableTileEntities.begin(), - m_globalRenderableTileEntities.end(), + TileEntityVector::iterator f = + std::find(m_globalTileEntities.begin(), + m_globalTileEntities.end(), *it); - if (f != m_globalRenderableTileEntities.end()) - m_globalRenderableTileEntities.erase(f); + if (f != m_globalTileEntities.end()) + m_globalTileEntities.erase(f); } field_54 = LevelChunk::touchedSky; m_bCompiled = true; } -Chunk::Chunk(Level* level, std::vector& renderableTileEntities, const TilePos& pos, int size, int lists) - : m_globalRenderableTileEntities(renderableTileEntities) +Chunk::Chunk(Level* level, TileEntityVector& tileEntities, const TilePos& pos, int size, int lists) + : m_globalTileEntities(tileEntities) { m_bOcclusionVisible = true; m_bOcclusionQuerying = false; diff --git a/source/client/renderer/Chunk.hpp b/source/client/renderer/Chunk.hpp index aea9643c1..58d6d4793 100644 --- a/source/client/renderer/Chunk.hpp +++ b/source/client/renderer/Chunk.hpp @@ -20,7 +20,7 @@ class TileEntity; class Chunk { public: - Chunk(Level*, std::vector& renderableTileEntities, const TilePos& pos, int, int); + Chunk(Level*, std::vector& tileEntities, const TilePos& pos, int, int); public: float distanceToSqr(const Entity& entity) const; @@ -43,8 +43,8 @@ class Chunk public: Level* m_pLevel; - std::vector& m_globalRenderableTileEntities; - std::vector m_renderableTileEntities; + std::vector& m_globalTileEntities; + std::vector m_tileEntities; TilePos m_pos; TilePos m_posS; bool m_empty[Tile::RENDER_LAYERS_COUNT]; diff --git a/source/client/renderer/LevelRenderer.cpp b/source/client/renderer/LevelRenderer.cpp index ef388e89f..a8dc66049 100644 --- a/source/client/renderer/LevelRenderer.cpp +++ b/source/client/renderer/LevelRenderer.cpp @@ -1608,7 +1608,7 @@ void LevelRenderer::renderEntities(Vec3 pos, Culler* culler, float f) /* // @TODO: TileEntityRenderDispatcher - for (std::vector::const_iterator it = m_renderableTileEntities.begin(); + for (TileEntityVector::const_iterator it = m_renderableTileEntities.begin(); it != m_renderableTileEntities.end(); ++it) { TileEntity* tileEntity = *it; diff --git a/source/client/renderer/LevelRenderer.hpp b/source/client/renderer/LevelRenderer.hpp index 18947ff8c..63520dc24 100644 --- a/source/client/renderer/LevelRenderer.hpp +++ b/source/client/renderer/LevelRenderer.hpp @@ -223,5 +223,5 @@ class LevelRenderer : public LevelListener, public AppPlatformListener mce::Mesh m_darkMesh; //... Textures* m_pTextures; - std::vector m_renderableTileEntities; + TileEntityVector m_renderableTileEntities; }; diff --git a/source/world/entity/FallingTile.cpp b/source/world/entity/FallingTile.cpp index 23c028573..aa249e52d 100644 --- a/source/world/entity/FallingTile.cpp +++ b/source/world/entity/FallingTile.cpp @@ -80,11 +80,11 @@ void FallingTile::tick() remove(); if (m_pLevel->mayPlace(m_id, tilePos, true)) { - m_pLevel->setTile(tilePos, m_id); + m_pLevel->setTile(tilePos, static_cast(m_id)); } else { - spawnAtLocation(m_id, 1); + spawnAtLocation(static_cast(m_id), 1); } } diff --git a/source/world/tile/entity/TileEntityType.cpp b/source/world/tile/entity/TileEntityType.cpp index 1391e41ac..18589f526 100644 --- a/source/world/tile/entity/TileEntityType.cpp +++ b/source/world/tile/entity/TileEntityType.cpp @@ -14,14 +14,14 @@ TileEntityType* TileEntityType::noteblock; std::map TileEntityFactory::_types; -void TileEntityFactory::InitTileEntities() +void TileEntityFactory::initTileEntities() { - TileEntityType::furnace = RegisterTileEntity("Furnace"); - TileEntityType::chest = RegisterTileEntity("Chest"); - TileEntityType::noteblock = RegisterTileEntity("Music"); + TileEntityType::furnace = registerTileEntity("Furnace"); + TileEntityType::chest = registerTileEntity("Chest"); + TileEntityType::noteblock = registerTileEntity("Music"); } -void TileEntityFactory::TeardownTileEntities() +void TileEntityFactory::teardownTileEntities() { // delete all heap allocated tile entity types (furnace, chest, etc.) for (std::map::const_iterator it = _types.begin(); it != _types.end(); ++it) @@ -31,7 +31,7 @@ void TileEntityFactory::TeardownTileEntities() _types.clear(); } -const TileEntityType* TileEntityFactory::GetType(const std::string& name) +const TileEntityType* TileEntityFactory::getType(const std::string& name) { return _types[name]; } diff --git a/source/world/tile/entity/TileEntityType.hpp b/source/world/tile/entity/TileEntityType.hpp index 0baa6b857..d1ad2b6ff 100644 --- a/source/world/tile/entity/TileEntityType.hpp +++ b/source/world/tile/entity/TileEntityType.hpp @@ -14,13 +14,13 @@ class TileEntityFactory static std::map _types; public: - static void InitTileEntities(); - static void TeardownTileEntities(); - static const TileEntityType* GetType(const std::string& name); + static void initTileEntities(); + static void teardownTileEntities(); + static const TileEntityType* getType(const std::string& name); public: template - static TileEntityType* RegisterTileEntity(const std::string& name); + static TileEntityType* registerTileEntity(const std::string& name); }; class TileEntityType @@ -50,7 +50,7 @@ class TileEntityType }; template -TileEntityType* TileEntityFactory::RegisterTileEntity(const std::string& name) +TileEntityType* TileEntityFactory::registerTileEntity(const std::string& name) { TileEntityType* type = new TileEntityType(name, &TileEntityType::CreateType); _types[type->_name] = type; From 732374d48ab2f66399efb8839019ec531052fff8 Mon Sep 17 00:00:00 2001 From: hellosammu Date: Tue, 17 Feb 2026 20:17:21 -0500 Subject: [PATCH 12/63] wrong way of doing this but i have to merge --- source/world/entity/FallingTile.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/world/entity/FallingTile.cpp b/source/world/entity/FallingTile.cpp index aa249e52d..dd9edfdc8 100644 --- a/source/world/entity/FallingTile.cpp +++ b/source/world/entity/FallingTile.cpp @@ -80,11 +80,11 @@ void FallingTile::tick() remove(); if (m_pLevel->mayPlace(m_id, tilePos, true)) { - m_pLevel->setTile(tilePos, static_cast(m_id)); + m_pLevel->setTile(tilePos, getTile()); } else { - spawnAtLocation(static_cast(m_id), 1); + spawnAtLocation(getTile()); } } From 67a9b26d4ca7a09d6e14013cd7ad61aaa093ebc8 Mon Sep 17 00:00:00 2001 From: hellosammu Date: Tue, 17 Feb 2026 20:19:38 -0500 Subject: [PATCH 13/63] syntax bitch --- source/client/app/NinecraftApp.cpp | 4 ++-- source/world/entity/FallingTile.cpp | 4 ++-- source/world/tile/entity/TileEntity.cpp | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/source/client/app/NinecraftApp.cpp b/source/client/app/NinecraftApp.cpp index 1b336d72c..4994bbfe2 100644 --- a/source/client/app/NinecraftApp.cpp +++ b/source/client/app/NinecraftApp.cpp @@ -180,7 +180,7 @@ void NinecraftApp::_initAll() EntityTypeDescriptor::initDescriptors(); // custom MobCategory::initMobCategories(); MobFactory::initMobLists(); - TileEntityFactory::InitTileEntities(); + TileEntityFactory::initTileEntities(); Tile::initTiles(); Item::initItems(); Biome::initBiomes(); @@ -341,7 +341,7 @@ void NinecraftApp::onGraphicsReset() void NinecraftApp::teardown() { - TileEntityFactory::TeardownTileEntities(); + TileEntityFactory::teardownTileEntities(); teardownRenderer(); Resource::teardownLoaders(); // Stop our SoundSystem before we nuke our sound buffers and cause it to implode diff --git a/source/world/entity/FallingTile.cpp b/source/world/entity/FallingTile.cpp index 5b23d84ac..838b63a0a 100644 --- a/source/world/entity/FallingTile.cpp +++ b/source/world/entity/FallingTile.cpp @@ -79,7 +79,7 @@ void FallingTile::tick() { if (field_E0 > 100 && !m_pLevel->m_bIsClientSide) { - spawnAtLocation(m_id, 1); + spawnAtLocation(getTile(), 1); remove(); } @@ -96,7 +96,7 @@ void FallingTile::tick() } else { - spawnAtLocation(getTile()); + spawnAtLocation(getTile(), 1); } } diff --git a/source/world/tile/entity/TileEntity.cpp b/source/world/tile/entity/TileEntity.cpp index a5b11be86..8cf5078c1 100644 --- a/source/world/tile/entity/TileEntity.cpp +++ b/source/world/tile/entity/TileEntity.cpp @@ -19,7 +19,7 @@ TileEntity* TileEntity::LoadTileEntity(const CompoundTag& tag) } std::string id = tag.getString("id"); - const TileEntityType* type = TileEntityFactory::GetType(id); + const TileEntityType* type = TileEntityFactory::getType(id); if (!type) { From 7506a6cf273ac381f307509974696229ce39b3cc Mon Sep 17 00:00:00 2001 From: hellosammu Date: Tue, 17 Feb 2026 20:24:46 -0500 Subject: [PATCH 14/63] sorry SDL1 no vsync yet --- platforms/sdl/base/AppPlatform_sdl.cpp | 16 ---------------- platforms/sdl/base/AppPlatform_sdl.hpp | 2 -- 2 files changed, 18 deletions(-) diff --git a/platforms/sdl/base/AppPlatform_sdl.cpp b/platforms/sdl/base/AppPlatform_sdl.cpp index 1d21c36c8..30de0ac72 100644 --- a/platforms/sdl/base/AppPlatform_sdl.cpp +++ b/platforms/sdl/base/AppPlatform_sdl.cpp @@ -149,22 +149,6 @@ void AppPlatform_sdl::_setDefaultIcon() _setIcon(data); } -void AppPlatform_sdl::setVSyncEnabled(bool enabled) -{ -#if MCE_GFX_API_OGL - SDL_GL_SetSwapInterval(enabled ? 1 : 0); -#endif -} - -bool AppPlatform_sdl::isVsyncSwitchable() const -{ -#if MCE_GFX_API_OGL - return true; -#else - return false; -#endif -} - void AppPlatform_sdl::initSoundSystem() { if (m_pSoundSystem) diff --git a/platforms/sdl/base/AppPlatform_sdl.hpp b/platforms/sdl/base/AppPlatform_sdl.hpp index 976668744..c93d4f4b2 100644 --- a/platforms/sdl/base/AppPlatform_sdl.hpp +++ b/platforms/sdl/base/AppPlatform_sdl.hpp @@ -38,8 +38,6 @@ class AppPlatform_sdl : public AppPlatform int getUserInputStatus() override; void saveScreenshot(const std::string& fileName, int width, int height) override; SoundSystem* getSoundSystem() const override { return m_pSoundSystem; } - void setVSyncEnabled(bool enabled) override; - bool isVsyncSwitchable() const override; // Also add these to allow proper turning within the game. void setMouseGrabbed(bool b) override; From b39ae301d75c329e3949a1d521860206d3d67ea3 Mon Sep 17 00:00:00 2001 From: hellosammu Date: Tue, 17 Feb 2026 20:26:19 -0500 Subject: [PATCH 15/63] android --- platforms/android/AppPlatform_android.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platforms/android/AppPlatform_android.cpp b/platforms/android/AppPlatform_android.cpp index a2aacf87c..f3547ef58 100644 --- a/platforms/android/AppPlatform_android.cpp +++ b/platforms/android/AppPlatform_android.cpp @@ -147,7 +147,7 @@ void AppPlatform_android::setVSyncEnabled(bool enabled) if (display == EGL_NO_DISPLAY) return; - glSwapInterval(display, enabled ? 1 : 0); + eglSwapInterval(display, enabled ? 1 : 0); } bool AppPlatform_android::isVsyncSwitchable() const From 4d117c3db6398f09f1835674dac8e64cfdfa03f6 Mon Sep 17 00:00:00 2001 From: Brent Da Mage Date: Tue, 17 Feb 2026 19:30:35 -0600 Subject: [PATCH 16/63] Updated Visual Studio Projects --- .../windows/projects/Client/Client.vcxproj | 2 + .../projects/Client/Client.vcxproj.filters | 6 ++ .../windows/projects/World/World.vcxproj | 26 ++++++ .../projects/World/World.vcxproj.filters | 93 +++++++++++++++++-- source/server/ServerPlayer.cpp | 10 ++ 5 files changed, 128 insertions(+), 9 deletions(-) diff --git a/platforms/windows/projects/Client/Client.vcxproj b/platforms/windows/projects/Client/Client.vcxproj index c046d534d..8da4fa551 100644 --- a/platforms/windows/projects/Client/Client.vcxproj +++ b/platforms/windows/projects/Client/Client.vcxproj @@ -258,6 +258,7 @@ + @@ -434,6 +435,7 @@ + diff --git a/platforms/windows/projects/Client/Client.vcxproj.filters b/platforms/windows/projects/Client/Client.vcxproj.filters index 55cd0f467..35b451268 100644 --- a/platforms/windows/projects/Client/Client.vcxproj.filters +++ b/platforms/windows/projects/Client/Client.vcxproj.filters @@ -677,6 +677,9 @@ Header Files\GUI\Screens\Inventory + + Header Files\GUI\Screens\Inventory + @@ -1204,5 +1207,8 @@ Source Files\GUI\Screens\Inventory + + Source Files\GUI\Screens\Inventory + \ No newline at end of file diff --git a/platforms/windows/projects/World/World.vcxproj b/platforms/windows/projects/World/World.vcxproj index 8a8312eca..7247155f5 100644 --- a/platforms/windows/projects/World/World.vcxproj +++ b/platforms/windows/projects/World/World.vcxproj @@ -228,6 +228,20 @@ + + + + + + + + + + + + + + @@ -397,6 +411,18 @@ + + + + + + + + + + + + diff --git a/platforms/windows/projects/World/World.vcxproj.filters b/platforms/windows/projects/World/World.vcxproj.filters index 18a19e701..6c9bbd804 100644 --- a/platforms/windows/projects/World/World.vcxproj.filters +++ b/platforms/windows/projects/World/World.vcxproj.filters @@ -105,6 +105,12 @@ {3a97a5e5-77e6-43eb-9890-3fa79b287cd1} + + {53363441-c079-4adf-a246-481be4b68199} + + + {9dca9e2a-e126-4598-a852-105be172d198} + @@ -659,6 +665,48 @@ Source Files\Item + + Source Files\Inventory + + + Source Files\Inventory + + + Source Files\Item + + + Source Files\Particle + + + Source Files + + + Source Files\Tile + + + Source Files\Tile + + + Source Files\Tile + + + Source Files\Tile + + + Source Files\Tile\Entity + + + Source Files\Tile\Entity + + + Source Files\Tile\Entity + + + Source Files\Tile\Entity + + + Source Files\Tile\Entity + @@ -1126,15 +1174,6 @@ Header Files\Item - - Header Files\Item - - - Header Files\Item - - - Header Files\Item - Header Files\Item @@ -1171,5 +1210,41 @@ Header Files\Item + + Header Files\Inventory + + + Header Files\Inventory + + + Header Files\Item + + + Header Files\Tile\Entity + + + Header Files\Tile\Entity + + + Header Files\Tile\Entity + + + Header Files\Tile\Entity + + + Header Files\Tile\Entity + + + Header Files\Tile + + + Header Files\Tile + + + Header Files\Tile + + + Header Files\Tile + \ No newline at end of file diff --git a/source/server/ServerPlayer.cpp b/source/server/ServerPlayer.cpp index 21ed28339..8a0aad136 100644 --- a/source/server/ServerPlayer.cpp +++ b/source/server/ServerPlayer.cpp @@ -1,4 +1,5 @@ #include "ServerPlayer.hpp" +#include "common/Logger.hpp" #include "network/packets/SetHealthPacket.hpp" #include "network/packets/TakeItemEntityPacket.hpp" #include "network/packets/SendInventoryPacket.hpp" @@ -55,6 +56,8 @@ void ServerPlayer::startCrafting(const TilePos& pos) void ServerPlayer::openContainer(Container* container) { + LOG_I("Client is opening a container"); + _nextContainerCounter(); #if NETWORK_PROTOCOL_VERSION >= 5 @@ -71,6 +74,8 @@ void ServerPlayer::openContainer(Container* container) void ServerPlayer::closeContainer() { + LOG_I("Client is closing a container"); + #if NETWORK_PROTOCOL_VERSION >= 5 m_pLevel->m_pRakNetInstance->send(new ContainerClosePacket(m_pContainerMenu->m_containerId)); #endif @@ -80,6 +85,8 @@ void ServerPlayer::closeContainer() void ServerPlayer::openFurnace(FurnaceTileEntity* furnace) { + LOG_I("Client is opening a furnace"); + _nextContainerCounter(); #if NETWORK_PROTOCOL_VERSION >= 5 @@ -131,6 +138,9 @@ void ServerPlayer::doCloseContainer() { if (m_pContainerMenu) m_pContainerMenu->removed(this); + else + LOG_W("Container is missing @ doCloseContainer!"); + setContainerMenu(nullptr); // m_pInventoryMenu on Java, nullptr on Pocket } From d2bdb19429320f21a2ac526564b4a7d244d01a87 Mon Sep 17 00:00:00 2001 From: Sam Date: Tue, 17 Feb 2026 20:47:38 -0500 Subject: [PATCH 17/63] tim apple --- .../Minecraft.xcodeproj/project.pbxproj | 220 ++++++++++++++---- 1 file changed, 170 insertions(+), 50 deletions(-) diff --git a/platforms/macos/projects/Minecraft/Minecraft.xcodeproj/project.pbxproj b/platforms/macos/projects/Minecraft/Minecraft.xcodeproj/project.pbxproj index 7a625e3e8..dcc3c8727 100644 --- a/platforms/macos/projects/Minecraft/Minecraft.xcodeproj/project.pbxproj +++ b/platforms/macos/projects/Minecraft/Minecraft.xcodeproj/project.pbxproj @@ -194,6 +194,34 @@ 8426107D2AE989730065905F /* UpdateBlockPacket.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 840DD64C2AC810620006A435 /* UpdateBlockPacket.cpp */; }; 8426107E2AE989730065905F /* RakNetInstance.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 840DD64E2AC810620006A435 /* RakNetInstance.cpp */; }; 842610882AE98A4C0065905F /* libRakNet.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 84FFBD7E2ACA2876005A8CCF /* libRakNet.a */; }; + 84358AFE2F4550B30077EA44 /* TileEntityType.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84358AFC2F4550B30077EA44 /* TileEntityType.cpp */; }; + 84358AFF2F4550B30077EA44 /* ChestTileEntity.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84358AF42F4550B30077EA44 /* ChestTileEntity.cpp */; }; + 84358B002F4550B30077EA44 /* FurnaceTileEntity.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84358AF62F4550B30077EA44 /* FurnaceTileEntity.cpp */; }; + 84358B012F4550B30077EA44 /* TileEntity.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84358AFA2F4550B30077EA44 /* TileEntity.cpp */; }; + 84358B022F4550B30077EA44 /* MusicTileEntity.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84358AF82F4550B30077EA44 /* MusicTileEntity.cpp */; }; + 84358B032F4550B30077EA44 /* TileEntity.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84358AF92F4550B30077EA44 /* TileEntity.hpp */; }; + 84358B042F4550B30077EA44 /* FurnaceTileEntity.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84358AF52F4550B30077EA44 /* FurnaceTileEntity.hpp */; }; + 84358B052F4550B30077EA44 /* ChestTileEntity.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84358AF32F4550B30077EA44 /* ChestTileEntity.hpp */; }; + 84358B062F4550B30077EA44 /* MusicTileEntity.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84358AF72F4550B30077EA44 /* MusicTileEntity.hpp */; }; + 84358B072F4550B30077EA44 /* TileEntityType.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84358AFB2F4550B30077EA44 /* TileEntityType.hpp */; }; + 84358B102F4550CC0077EA44 /* FurnaceTile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84358B0D2F4550CC0077EA44 /* FurnaceTile.cpp */; }; + 84358B112F4550CC0077EA44 /* EntityTile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84358B0B2F4550CC0077EA44 /* EntityTile.cpp */; }; + 84358B122F4550CC0077EA44 /* ChestTile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84358B092F4550CC0077EA44 /* ChestTile.cpp */; }; + 84358B132F4550CC0077EA44 /* MusicTile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84358B0F2F4550CC0077EA44 /* MusicTile.cpp */; }; + 84358B142F4550CC0077EA44 /* ChestTile.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84358B082F4550CC0077EA44 /* ChestTile.hpp */; }; + 84358B152F4550CC0077EA44 /* FurnaceTile.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84358B0C2F4550CC0077EA44 /* FurnaceTile.hpp */; }; + 84358B162F4550CC0077EA44 /* EntityTile.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84358B0A2F4550CC0077EA44 /* EntityTile.hpp */; }; + 84358B172F4550CC0077EA44 /* MusicTile.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84358B0E2F4550CC0077EA44 /* MusicTile.hpp */; }; + 84358B192F45511C0077EA44 /* NoteParticle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84358B182F45511C0077EA44 /* NoteParticle.cpp */; }; + 84358B1B2F4551290077EA44 /* Facing.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84358B1A2F4551290077EA44 /* Facing.cpp */; }; + 84358B1E2F4551500077EA44 /* CoalItem.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84358B1D2F4551500077EA44 /* CoalItem.cpp */; }; + 84358B1F2F4551500077EA44 /* CoalItem.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84358B1C2F4551500077EA44 /* CoalItem.hpp */; }; + 84358B242F4551730077EA44 /* FurnaceResultSlot.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84358B232F4551730077EA44 /* FurnaceResultSlot.cpp */; }; + 84358B252F4551730077EA44 /* FurnaceMenu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84358B212F4551730077EA44 /* FurnaceMenu.cpp */; }; + 84358B262F4551730077EA44 /* FurnaceMenu.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84358B202F4551730077EA44 /* FurnaceMenu.hpp */; }; + 84358B272F4551730077EA44 /* FurnaceResultSlot.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84358B222F4551730077EA44 /* FurnaceResultSlot.hpp */; }; + 84358B2A2F45519B0077EA44 /* FurnaceScreen.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84358B282F45519B0077EA44 /* FurnaceScreen.hpp */; }; + 84358B2B2F45519B0077EA44 /* FurnaceScreen.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84358B292F45519B0077EA44 /* FurnaceScreen.cpp */; }; 8435BB192DCD47F400D38282 /* SoundStreamOAL.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8435BB182DCD47F400D38282 /* SoundStreamOAL.cpp */; }; 8435BB1A2DCD47F400D38282 /* SoundStreamOAL.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8435BB182DCD47F400D38282 /* SoundStreamOAL.cpp */; }; 8441F98F2DB4E911005977BD /* SoundSystemOAL.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8441F9862DB4E911005977BD /* SoundSystemOAL.cpp */; }; @@ -2234,6 +2262,34 @@ 84336BA52B1EB57E00097DB0 /* Settings_iOS_Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Settings_iOS_Debug.xcconfig; path = ../Configuration/Settings_iOS_Debug.xcconfig; sourceTree = ""; }; 84336BA82B1EB88500097DB0 /* Settings_macOS.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Settings_macOS.xcconfig; path = ../Configuration/Settings_macOS.xcconfig; sourceTree = ""; }; 84336BA92B1EB9C200097DB0 /* Settings_macOS_Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Settings_macOS_Debug.xcconfig; path = ../Configuration/Settings_macOS_Debug.xcconfig; sourceTree = ""; }; + 84358AF32F4550B30077EA44 /* ChestTileEntity.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ChestTileEntity.hpp; sourceTree = ""; }; + 84358AF42F4550B30077EA44 /* ChestTileEntity.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ChestTileEntity.cpp; sourceTree = ""; }; + 84358AF52F4550B30077EA44 /* FurnaceTileEntity.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = FurnaceTileEntity.hpp; sourceTree = ""; }; + 84358AF62F4550B30077EA44 /* FurnaceTileEntity.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FurnaceTileEntity.cpp; sourceTree = ""; }; + 84358AF72F4550B30077EA44 /* MusicTileEntity.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = MusicTileEntity.hpp; sourceTree = ""; }; + 84358AF82F4550B30077EA44 /* MusicTileEntity.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = MusicTileEntity.cpp; sourceTree = ""; }; + 84358AF92F4550B30077EA44 /* TileEntity.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = TileEntity.hpp; sourceTree = ""; }; + 84358AFA2F4550B30077EA44 /* TileEntity.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = TileEntity.cpp; sourceTree = ""; }; + 84358AFB2F4550B30077EA44 /* TileEntityType.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = TileEntityType.hpp; sourceTree = ""; }; + 84358AFC2F4550B30077EA44 /* TileEntityType.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = TileEntityType.cpp; sourceTree = ""; }; + 84358B082F4550CC0077EA44 /* ChestTile.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ChestTile.hpp; sourceTree = ""; }; + 84358B092F4550CC0077EA44 /* ChestTile.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ChestTile.cpp; sourceTree = ""; }; + 84358B0A2F4550CC0077EA44 /* EntityTile.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = EntityTile.hpp; sourceTree = ""; }; + 84358B0B2F4550CC0077EA44 /* EntityTile.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = EntityTile.cpp; sourceTree = ""; }; + 84358B0C2F4550CC0077EA44 /* FurnaceTile.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = FurnaceTile.hpp; sourceTree = ""; }; + 84358B0D2F4550CC0077EA44 /* FurnaceTile.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FurnaceTile.cpp; sourceTree = ""; }; + 84358B0E2F4550CC0077EA44 /* MusicTile.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = MusicTile.hpp; sourceTree = ""; }; + 84358B0F2F4550CC0077EA44 /* MusicTile.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = MusicTile.cpp; sourceTree = ""; }; + 84358B182F45511C0077EA44 /* NoteParticle.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = NoteParticle.cpp; sourceTree = ""; }; + 84358B1A2F4551290077EA44 /* Facing.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Facing.cpp; sourceTree = ""; }; + 84358B1C2F4551500077EA44 /* CoalItem.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = CoalItem.hpp; sourceTree = ""; }; + 84358B1D2F4551500077EA44 /* CoalItem.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = CoalItem.cpp; sourceTree = ""; }; + 84358B202F4551730077EA44 /* FurnaceMenu.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = FurnaceMenu.hpp; sourceTree = ""; }; + 84358B212F4551730077EA44 /* FurnaceMenu.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FurnaceMenu.cpp; sourceTree = ""; }; + 84358B222F4551730077EA44 /* FurnaceResultSlot.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = FurnaceResultSlot.hpp; sourceTree = ""; }; + 84358B232F4551730077EA44 /* FurnaceResultSlot.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FurnaceResultSlot.cpp; sourceTree = ""; }; + 84358B282F45519B0077EA44 /* FurnaceScreen.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = FurnaceScreen.hpp; sourceTree = ""; }; + 84358B292F45519B0077EA44 /* FurnaceScreen.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FurnaceScreen.cpp; sourceTree = ""; }; 8435BB172DCD47F400D38282 /* SoundStreamOAL.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = SoundStreamOAL.hpp; sourceTree = ""; }; 8435BB182DCD47F400D38282 /* SoundStreamOAL.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = SoundStreamOAL.cpp; sourceTree = ""; }; 8441F9852DB4E911005977BD /* CustomSoundSystem.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = CustomSoundSystem.hpp; sourceTree = ""; }; @@ -3756,6 +3812,7 @@ 84E7BF2D2F286A20002D3936 /* ContainerListener.cpp */, 84E7BF2E2F286A20002D3936 /* ContainerListener.hpp */, 840DD6572AC810620006A435 /* entity */, + 84358B1A2F4551290077EA44 /* Facing.cpp */, 8477B3A82C4DC3B3004E1AC5 /* Facing.hpp */, 840DD6682AC810620006A435 /* gamemode */, 84E7BF092F286A08002D3936 /* inventory */, @@ -3852,6 +3909,8 @@ children = ( 84B9D8962F3BE0A900FD67C4 /* ArmorItem.hpp */, 84B9D8972F3BE0A900FD67C4 /* ArmorItem.cpp */, + 84358B1C2F4551500077EA44 /* CoalItem.hpp */, + 84358B1D2F4551500077EA44 /* CoalItem.cpp */, 84B9D8982F3BE0A900FD67C4 /* HoeItem.hpp */, 84B9D8992F3BE0A900FD67C4 /* HoeItem.cpp */, 84B9D89A2F3BE0A900FD67C4 /* SeedItem.hpp */, @@ -4042,6 +4101,7 @@ 840DD6CE2AC810620006A435 /* particle */ = { isa = PBXGroup; children = ( + 84358B182F45511C0077EA44 /* NoteParticle.cpp */, 840DD6CF2AC810620006A435 /* BubbleParticle.cpp */, 840DD6D02AC810620006A435 /* ExplodeParticle.cpp */, 8470AF3B2BE9B6FA00BCA54E /* FireworkParticle.hpp */, @@ -4077,104 +4137,113 @@ 840DD6E12AC810620006A435 /* tile */ = { isa = PBXGroup; children = ( - 84B9D8A22F3BE0B200FD67C4 /* CropsTile.hpp */, - 84B9D8A32F3BE0B200FD67C4 /* CropsTile.cpp */, - 840DD6E22AC810620006A435 /* BookshelfTile.cpp */, + 84358AFD2F4550B30077EA44 /* entity */, 840DD6E32AC810620006A435 /* BookshelfTile.hpp */, - 840DD6E42AC810620006A435 /* Bush.cpp */, + 840DD6E22AC810620006A435 /* BookshelfTile.cpp */, 840DD6E52AC810620006A435 /* Bush.hpp */, - 84E1C9BF2E7FDC26007D2F5D /* CactusTile.cpp */, + 840DD6E42AC810620006A435 /* Bush.cpp */, 84E1C9C02E7FDC26007D2F5D /* CactusTile.hpp */, - 840DD6E62AC810620006A435 /* ClayTile.cpp */, + 84E1C9BF2E7FDC26007D2F5D /* CactusTile.cpp */, + 84358B082F4550CC0077EA44 /* ChestTile.hpp */, + 84358B092F4550CC0077EA44 /* ChestTile.cpp */, 840DD6E72AC810620006A435 /* ClayTile.hpp */, - 840DD6E82AC810620006A435 /* ClothTile.cpp */, + 840DD6E62AC810620006A435 /* ClayTile.cpp */, 840DD6E92AC810620006A435 /* ClothTile.hpp */, - 84DED1B52F30970A004001C5 /* CraftingTableTile.cpp */, + 840DD6E82AC810620006A435 /* ClothTile.cpp */, 84DED1B62F30970A004001C5 /* CraftingTableTile.hpp */, - 84E1C9C12E7FDC26007D2F5D /* DeadBush.cpp */, + 84DED1B52F30970A004001C5 /* CraftingTableTile.cpp */, + 84B9D8A22F3BE0B200FD67C4 /* CropsTile.hpp */, + 84B9D8A32F3BE0B200FD67C4 /* CropsTile.cpp */, 84E1C9C22E7FDC26007D2F5D /* DeadBush.hpp */, - 840DD6EA2AC810620006A435 /* DirtTile.cpp */, + 84E1C9C12E7FDC26007D2F5D /* DeadBush.cpp */, 840DD6EB2AC810620006A435 /* DirtTile.hpp */, - 840DD6EC2AC810620006A435 /* DoorTile.cpp */, + 840DD6EA2AC810620006A435 /* DirtTile.cpp */, 840DD6ED2AC810620006A435 /* DoorTile.hpp */, - 840DD6EE2AC810620006A435 /* FarmTile.cpp */, + 840DD6EC2AC810620006A435 /* DoorTile.cpp */, + 84358B0A2F4550CC0077EA44 /* EntityTile.hpp */, + 84358B0B2F4550CC0077EA44 /* EntityTile.cpp */, 840DD6EF2AC810620006A435 /* FarmTile.hpp */, - 84E1C9C32E7FDC26007D2F5D /* FenceTile.cpp */, + 840DD6EE2AC810620006A435 /* FarmTile.cpp */, 84E1C9C42E7FDC26007D2F5D /* FenceTile.hpp */, - 840DD6F02AC810620006A435 /* FireTile.cpp */, + 84E1C9C32E7FDC26007D2F5D /* FenceTile.cpp */, 840DD6F12AC810620006A435 /* FireTile.hpp */, - 840DD6F22AC810620006A435 /* GlassTile.cpp */, + 840DD6F02AC810620006A435 /* FireTile.cpp */, + 84358B0C2F4550CC0077EA44 /* FurnaceTile.hpp */, + 84358B0D2F4550CC0077EA44 /* FurnaceTile.cpp */, 840DD6F32AC810620006A435 /* GlassTile.hpp */, - 84E1C9C52E7FDC26007D2F5D /* GlowstoneTile.cpp */, + 840DD6F22AC810620006A435 /* GlassTile.cpp */, 84E1C9C62E7FDC26007D2F5D /* GlowstoneTile.hpp */, - 840DD6F42AC810620006A435 /* GrassTile.cpp */, + 84E1C9C52E7FDC26007D2F5D /* GlowstoneTile.cpp */, 840DD6F52AC810620006A435 /* GrassTile.hpp */, - 840DD6F62AC810620006A435 /* GravelTile.cpp */, + 840DD6F42AC810620006A435 /* GrassTile.cpp */, 840DD6F72AC810620006A435 /* GravelTile.hpp */, - 840DD6F82AC810620006A435 /* HalfTransparentTile.cpp */, + 840DD6F62AC810620006A435 /* GravelTile.cpp */, 840DD6F92AC810620006A435 /* HalfTransparentTile.hpp */, - 840DD6FA2AC810620006A435 /* IceTile.cpp */, + 840DD6F82AC810620006A435 /* HalfTransparentTile.cpp */, 840DD6FB2AC810620006A435 /* IceTile.hpp */, - 840DD6FC2AC810620006A435 /* InvisibleTile.cpp */, + 840DD6FA2AC810620006A435 /* IceTile.cpp */, 840DD6FD2AC810620006A435 /* InvisibleTile.hpp */, - 840DD6FE2AC810620006A435 /* LadderTile.cpp */, + 840DD6FC2AC810620006A435 /* InvisibleTile.cpp */, 840DD6FF2AC810620006A435 /* LadderTile.hpp */, - 840DD7002AC810620006A435 /* LeafTile.cpp */, + 840DD6FE2AC810620006A435 /* LadderTile.cpp */, 840DD7012AC810620006A435 /* LeafTile.hpp */, - 840DD7022AC810620006A435 /* LiquidTile.cpp */, + 840DD7002AC810620006A435 /* LeafTile.cpp */, 840DD7032AC810620006A435 /* LiquidTile.hpp */, - 840DD7042AC810620006A435 /* LiquidTileDynamic.cpp */, + 840DD7022AC810620006A435 /* LiquidTile.cpp */, 840DD7052AC810620006A435 /* LiquidTileDynamic.hpp */, - 840DD7062AC810620006A435 /* LiquidTileStatic.cpp */, + 840DD7042AC810620006A435 /* LiquidTileDynamic.cpp */, 840DD7072AC810620006A435 /* LiquidTileStatic.hpp */, - 840DD7082AC810620006A435 /* MetalTile.cpp */, + 840DD7062AC810620006A435 /* LiquidTileStatic.cpp */, 840DD7092AC810620006A435 /* MetalTile.hpp */, - 840DD70A2AC810620006A435 /* ObsidianTile.cpp */, + 840DD7082AC810620006A435 /* MetalTile.cpp */, + 84358B0E2F4550CC0077EA44 /* MusicTile.hpp */, + 84358B0F2F4550CC0077EA44 /* MusicTile.cpp */, 840DD70B2AC810620006A435 /* ObsidianTile.hpp */, - 840DD70C2AC810620006A435 /* OreTile.cpp */, + 840DD70A2AC810620006A435 /* ObsidianTile.cpp */, 840DD70D2AC810620006A435 /* OreTile.hpp */, - 84E1C9C72E7FDC26007D2F5D /* PumpkinTile.cpp */, + 840DD70C2AC810620006A435 /* OreTile.cpp */, 84E1C9C82E7FDC26007D2F5D /* PumpkinTile.hpp */, - 840DD70E2AC810620006A435 /* RedStoneOreTile.cpp */, + 84E1C9C72E7FDC26007D2F5D /* PumpkinTile.cpp */, 840DD70F2AC810620006A435 /* RedStoneOreTile.hpp */, - 840DD7102AC810620006A435 /* ReedTile.cpp */, + 840DD70E2AC810620006A435 /* RedStoneOreTile.cpp */, 840DD7112AC810620006A435 /* ReedTile.hpp */, - 84E78C852B58B66B00D515EF /* RocketLauncherTile.cpp */, + 840DD7102AC810620006A435 /* ReedTile.cpp */, 84E78C862B58B66B00D515EF /* RocketLauncherTile.hpp */, - 840DD7122AC810620006A435 /* SandStoneTile.cpp */, + 84E78C852B58B66B00D515EF /* RocketLauncherTile.cpp */, 840DD7132AC810620006A435 /* SandStoneTile.hpp */, - 840DD7142AC810620006A435 /* SandTile.cpp */, + 840DD7122AC810620006A435 /* SandStoneTile.cpp */, 840DD7152AC810620006A435 /* SandTile.hpp */, - 840DD7162AC810620006A435 /* Sapling.cpp */, + 840DD7142AC810620006A435 /* SandTile.cpp */, 840DD7172AC810620006A435 /* Sapling.hpp */, - 84E1C9C92E7FDC26007D2F5D /* SoulSandTile.cpp */, + 840DD7162AC810620006A435 /* Sapling.cpp */, 84E1C9CA2E7FDC26007D2F5D /* SoulSandTile.hpp */, - 840DD7182AC810620006A435 /* SpongeTile.cpp */, + 84E1C9C92E7FDC26007D2F5D /* SoulSandTile.cpp */, 840DD7192AC810620006A435 /* SpongeTile.hpp */, - 840DD71A2AC810620006A435 /* StairTile.cpp */, + 840DD7182AC810620006A435 /* SpongeTile.cpp */, 840DD71B2AC810620006A435 /* StairTile.hpp */, - 840DD71C2AC810620006A435 /* StoneSlabTile.cpp */, + 840DD71A2AC810620006A435 /* StairTile.cpp */, 840DD71D2AC810620006A435 /* StoneSlabTile.hpp */, - 840DD71E2AC810620006A435 /* StoneTile.cpp */, + 840DD71C2AC810620006A435 /* StoneSlabTile.cpp */, 840DD71F2AC810620006A435 /* StoneTile.hpp */, - 84E1C9CB2E7FDC26007D2F5D /* TallGrass.cpp */, + 840DD71E2AC810620006A435 /* StoneTile.cpp */, 84E1C9CC2E7FDC26007D2F5D /* TallGrass.hpp */, - 840DD7202AC810620006A435 /* Tile.cpp */, + 84E1C9CB2E7FDC26007D2F5D /* TallGrass.cpp */, 840DD7212AC810620006A435 /* Tile.hpp */, - 840DD7222AC810620006A435 /* TntTile.cpp */, + 840DD7202AC810620006A435 /* Tile.cpp */, 840DD7232AC810620006A435 /* TntTile.hpp */, - 840DD7242AC810620006A435 /* TopSnowTile.cpp */, + 840DD7222AC810620006A435 /* TntTile.cpp */, 840DD7252AC810620006A435 /* TopSnowTile.hpp */, - 840DD7262AC810620006A435 /* TorchTile.cpp */, + 840DD7242AC810620006A435 /* TopSnowTile.cpp */, 840DD7272AC810620006A435 /* TorchTile.hpp */, - 840DD7282AC810620006A435 /* TransparentTile.cpp */, + 840DD7262AC810620006A435 /* TorchTile.cpp */, 840DD7292AC810620006A435 /* TransparentTile.hpp */, - 840DD72A2AC810620006A435 /* TreeTile.cpp */, + 840DD7282AC810620006A435 /* TransparentTile.cpp */, 840DD72B2AC810620006A435 /* TreeTile.hpp */, - 84E1C9CD2E7FDC26007D2F5D /* Web.cpp */, + 840DD72A2AC810620006A435 /* TreeTile.cpp */, 84E1C9CE2E7FDC26007D2F5D /* Web.hpp */, - 840DD72C2AC810620006A435 /* WireTile.cpp */, + 84E1C9CD2E7FDC26007D2F5D /* Web.cpp */, 840DD72D2AC810620006A435 /* WireTile.hpp */, + 840DD72C2AC810620006A435 /* WireTile.cpp */, ); path = tile; sourceTree = ""; @@ -4566,6 +4635,23 @@ name = macOS; sourceTree = ""; }; + 84358AFD2F4550B30077EA44 /* entity */ = { + isa = PBXGroup; + children = ( + 84358AF32F4550B30077EA44 /* ChestTileEntity.hpp */, + 84358AF42F4550B30077EA44 /* ChestTileEntity.cpp */, + 84358AF52F4550B30077EA44 /* FurnaceTileEntity.hpp */, + 84358AF62F4550B30077EA44 /* FurnaceTileEntity.cpp */, + 84358AF72F4550B30077EA44 /* MusicTileEntity.hpp */, + 84358AF82F4550B30077EA44 /* MusicTileEntity.cpp */, + 84358AF92F4550B30077EA44 /* TileEntity.hpp */, + 84358AFA2F4550B30077EA44 /* TileEntity.cpp */, + 84358AFB2F4550B30077EA44 /* TileEntityType.hpp */, + 84358AFC2F4550B30077EA44 /* TileEntityType.cpp */, + ); + path = entity; + sourceTree = ""; + }; 8441F9872DB4E911005977BD /* openal */ = { isa = PBXGroup; children = ( @@ -4949,6 +5035,8 @@ 84E022E82F2A0211003C8FFE /* inventory */ = { isa = PBXGroup; children = ( + 84358B282F45519B0077EA44 /* FurnaceScreen.hpp */, + 84358B292F45519B0077EA44 /* FurnaceScreen.cpp */, 84E022E92F2A0211003C8FFE /* ChestScreen.cpp */, 84E022EA2F2A0211003C8FFE /* ChestScreen.hpp */, 844337192F405E1C00EB3115 /* ClassicCraftingScreen_Console.cpp */, @@ -5006,6 +5094,10 @@ 84E7BF0F2F286A08002D3936 /* CraftingContainer.hpp */, 84DED19A2F3095FD004001C5 /* CraftingMenu.cpp */, 84DED19B2F3095FD004001C5 /* CraftingMenu.hpp */, + 84358B202F4551730077EA44 /* FurnaceMenu.hpp */, + 84358B212F4551730077EA44 /* FurnaceMenu.cpp */, + 84358B222F4551730077EA44 /* FurnaceResultSlot.hpp */, + 84358B232F4551730077EA44 /* FurnaceResultSlot.cpp */, 84E7BF102F286A08002D3936 /* InventoryMenu.cpp */, 84E7BF112F286A08002D3936 /* InventoryMenu.hpp */, 84E7BF122F286A08002D3936 /* ResultContainer.cpp */, @@ -5806,6 +5898,7 @@ 84A2FF182DB61D440090CE3E /* SoundPathRepository.hpp in Headers */, 84EB09FB2EE2CCA8008FB007 /* TextureData.hpp in Headers */, 84B1E0302E04FD4500ED000A /* ArrowRenderer.hpp in Headers */, + 84358B2A2F45519B0077EA44 /* FurnaceScreen.hpp in Headers */, 84AA8C2B2B32F3F3003F5B82 /* LightUpdate.hpp in Headers */, 84AA8C2D2B32F3F3003F5B82 /* PatchManager.hpp in Headers */, 84AA8C2F2B32F3F3003F5B82 /* RenderChunk.hpp in Headers */, @@ -5845,6 +5938,10 @@ 84E1C9D42E7FDC26007D2F5D /* FenceTile.hpp in Headers */, 84BF637B2AF186C8008A9995 /* ItemEntity.hpp in Headers */, 84BF637D2AF186C8008A9995 /* Mob.hpp in Headers */, + 84358B142F4550CC0077EA44 /* ChestTile.hpp in Headers */, + 84358B152F4550CC0077EA44 /* FurnaceTile.hpp in Headers */, + 84358B162F4550CC0077EA44 /* EntityTile.hpp in Headers */, + 84358B172F4550CC0077EA44 /* MusicTile.hpp in Headers */, 84BF637E2AF186C8008A9995 /* Player.hpp in Headers */, 8477B3A92C4DC3B3004E1AC5 /* Facing.hpp in Headers */, 84BF637F2AF186C8008A9995 /* PrimedTnt.hpp in Headers */, @@ -5915,6 +6012,8 @@ 84BF63A92AF186C8008A9995 /* RegionFile.hpp in Headers */, 84BF63AA2AF186C8008A9995 /* TickNextTickData.hpp in Headers */, 84BF63AB2AF186C8008A9995 /* Particle.hpp in Headers */, + 84358B262F4551730077EA44 /* FurnaceMenu.hpp in Headers */, + 84358B272F4551730077EA44 /* FurnaceResultSlot.hpp in Headers */, 84E7BF232F286A08002D3936 /* ResultContainer.hpp in Headers */, 84E1C9E62E7FDC72007D2F5D /* AuxTileItem.hpp in Headers */, 84E7BF272F286A08002D3936 /* SimpleContainer.hpp in Headers */, @@ -6001,6 +6100,7 @@ 84E78C842B58B5FB00D515EF /* RocketItem.hpp in Headers */, 84E78C882B58B66B00D515EF /* RocketLauncherTile.hpp in Headers */, 8477B3B72C4DC414004E1AC5 /* EmptyLevelChunk.hpp in Headers */, + 84358B1F2F4551500077EA44 /* CoalItem.hpp in Headers */, 84E1C9E82E7FDC72007D2F5D /* ClothItem.hpp in Headers */, 8470AF2B2BE9B60A00BCA54E /* EntityType.hpp in Headers */, 8445E7A42D769329008DC834 /* EntityTypeDescriptor.hpp in Headers */, @@ -6009,6 +6109,11 @@ 84B9D8A12F3BE0A900FD67C4 /* SeedItem.hpp in Headers */, 8470AF2D2BE9B60A00BCA54E /* MobFactory.hpp in Headers */, 8470AF392BE9B6D100BCA54E /* GameType.hpp in Headers */, + 84358B032F4550B30077EA44 /* TileEntity.hpp in Headers */, + 84358B042F4550B30077EA44 /* FurnaceTileEntity.hpp in Headers */, + 84358B052F4550B30077EA44 /* ChestTileEntity.hpp in Headers */, + 84358B062F4550B30077EA44 /* MusicTileEntity.hpp in Headers */, + 84358B072F4550B30077EA44 /* TileEntityType.hpp in Headers */, 8470AF3D2BE9B6FA00BCA54E /* FireworkParticle.hpp in Headers */, 84E7BF302F286A20002D3936 /* CompoundContainer.hpp in Headers */, ); @@ -6785,6 +6890,7 @@ 84B8AEFD2AF1896F008DE93D /* InvalidLicenseScreen.cpp in Sources */, 84B8AEFE2AF1896F008DE93D /* JoinGameScreen.cpp in Sources */, 844336FB2F405DCA00EB3115 /* VerticalLayout.cpp in Sources */, + 84358B2B2F45519B0077EA44 /* FurnaceScreen.cpp in Sources */, 84B8AEFF2AF1896F008DE93D /* OptionsScreen.cpp in Sources */, 84B8AF002AF1896F008DE93D /* PauseScreen.cpp in Sources */, 84B8AF012AF1896F008DE93D /* ProgressScreen.cpp in Sources */, @@ -6976,6 +7082,11 @@ 84BF63162AF18631008A9995 /* SurvivalMode.cpp in Sources */, 84BF63172AF18631008A9995 /* CameraItem.cpp in Sources */, 84DED1AF2F309611004001C5 /* ShapedRecipe.cpp in Sources */, + 84358AFE2F4550B30077EA44 /* TileEntityType.cpp in Sources */, + 84358AFF2F4550B30077EA44 /* ChestTileEntity.cpp in Sources */, + 84358B002F4550B30077EA44 /* FurnaceTileEntity.cpp in Sources */, + 84358B012F4550B30077EA44 /* TileEntity.cpp in Sources */, + 84358B022F4550B30077EA44 /* MusicTileEntity.cpp in Sources */, 84E1C9E52E7FDC72007D2F5D /* AuxTileItem.cpp in Sources */, 84E1C9D52E7FDC26007D2F5D /* GlowstoneTile.cpp in Sources */, 84BF63182AF18631008A9995 /* DoorItem.cpp in Sources */, @@ -6983,6 +7094,7 @@ 84B9D8A42F3BE0B200FD67C4 /* CropsTile.cpp in Sources */, 8445E7A22D769329008DC834 /* EntityType.cpp in Sources */, 84BF631A2AF18631008A9995 /* Item.cpp in Sources */, + 84358B1B2F4551290077EA44 /* Facing.cpp in Sources */, 84BF631C2AF18631008A9995 /* TileItem.cpp in Sources */, 84E1C9F02E7FDC89007D2F5D /* VegetationFeature.cpp in Sources */, 84BF631D2AF18631008A9995 /* TilePlanterItem.cpp in Sources */, @@ -6995,8 +7107,11 @@ 84BF63222AF18631008A9995 /* BiomeSource.cpp in Sources */, 84BF63232AF18631008A9995 /* ChunkCache.cpp in Sources */, 84E1C9CF2E7FDC26007D2F5D /* CactusTile.cpp in Sources */, + 84358B242F4551730077EA44 /* FurnaceResultSlot.cpp in Sources */, + 84358B252F4551730077EA44 /* FurnaceMenu.cpp in Sources */, 84E7BF1A2F286A08002D3936 /* ArmorSlot.cpp in Sources */, 84BF63242AF18631008A9995 /* ChunkSource.cpp in Sources */, + 84358B192F45511C0077EA44 /* NoteParticle.cpp in Sources */, 84BF63252AF18631008A9995 /* LevelChunk.cpp in Sources */, 84BF63262AF18631008A9995 /* PerformanceTestChunkSource.cpp in Sources */, 84EE51312E8DCC9000D3DCA2 /* DataLayer.cpp in Sources */, @@ -7026,6 +7141,10 @@ 84B9D89C2F3BE0A900FD67C4 /* ArmorItem.cpp in Sources */, 84B9D89D2F3BE0A900FD67C4 /* HoeItem.cpp in Sources */, 84B9D89E2F3BE0A900FD67C4 /* SeedItem.cpp in Sources */, + 84358B102F4550CC0077EA44 /* FurnaceTile.cpp in Sources */, + 84358B112F4550CC0077EA44 /* EntityTile.cpp in Sources */, + 84358B122F4550CC0077EA44 /* ChestTile.cpp in Sources */, + 84358B132F4550CC0077EA44 /* MusicTile.cpp in Sources */, 84BF63382AF18631008A9995 /* LevelListener.cpp in Sources */, 84E7BF2F2F286A20002D3936 /* CompoundContainer.cpp in Sources */, 84BF63392AF18631008A9995 /* Material.cpp in Sources */, @@ -7097,6 +7216,7 @@ 84BF63662AF18631008A9995 /* MetalTile.cpp in Sources */, 84BF63672AF18631008A9995 /* ObsidianTile.cpp in Sources */, 84CCBC932E61880600E251AF /* EntityFactory.cpp in Sources */, + 84358B1E2F4551500077EA44 /* CoalItem.cpp in Sources */, 84BF63682AF18631008A9995 /* OreTile.cpp in Sources */, 84B1E0392E04FD7900ED000A /* Arrow.cpp in Sources */, 84BF63692AF18631008A9995 /* RedStoneOreTile.cpp in Sources */, From da4f8640e5a7cc22e72d8186ddaf0df8add4428a Mon Sep 17 00:00:00 2001 From: Sam <31021951+samdotjpg@users.noreply.github.com> Date: Tue, 17 Feb 2026 20:53:59 -0500 Subject: [PATCH 18/63] add to lang file --- game/assets/lang/en_US.lang | 1 + 1 file changed, 1 insertion(+) diff --git a/game/assets/lang/en_US.lang b/game/assets/lang/en_US.lang index d85d3c0b3..1002d7955 100644 --- a/game/assets/lang/en_US.lang +++ b/game/assets/lang/en_US.lang @@ -104,6 +104,7 @@ options.guiScale.small=Small options.guiScale.normal=Normal options.guiScale.large=Large options.advancedOpengl=Advanced OpenGL +options.enableVsync=Enable Vsync performance.max=Max FPS performance.balanced=Balanced From d46806d9cf49edd66fd97cd83ad806874be0d36a Mon Sep 17 00:00:00 2001 From: Sam Date: Tue, 17 Feb 2026 22:18:30 -0500 Subject: [PATCH 19/63] Make riding / rider code not suck really badly --- source/world/entity/Entity.cpp | 60 +++++++++++++++++++--------------- source/world/entity/Entity.hpp | 8 +++-- source/world/entity/Mob.cpp | 2 +- source/world/level/Level.cpp | 17 ++++------ 4 files changed, 48 insertions(+), 39 deletions(-) diff --git a/source/world/entity/Entity.cpp b/source/world/entity/Entity.cpp index b70616ba1..8e8ba64a9 100644 --- a/source/world/entity/Entity.cpp +++ b/source/world/entity/Entity.cpp @@ -28,8 +28,8 @@ void Entity::_init() field_28 = 0; field_30 = 1.0f; m_dimensionId = DIMENSION_OVERWORLD; - m_riderId = 0; - m_ridingId = 0; + _riderId = 0; + _ridingId = 0; m_bRiding = false; m_bBlocksBuilding = false; m_pLevel = nullptr; @@ -474,10 +474,10 @@ void Entity::baseTick() //@TODO: untangle the gotos if (const Entity* riding = getRiding()) { - if ((!riding && m_riderId > 0) || riding->m_bRemoved) + // if you were riding an entity and they no longer exist, stop + if ((!riding && _ridingId > 0) || riding->m_bRemoved) { - m_riderId = 0; - setSharedFlag(C_ENTITY_FLAG_RIDING, false); + setRiding(nullptr); } } @@ -756,6 +756,9 @@ void Entity::playerTouch(Player* player) void Entity::push(Entity* bud) { + if (bud == getRider() || bud == getRiding()) + return; + float diffX = bud->m_pos.x - m_pos.x; float diffZ = bud->m_pos.z - m_pos.z; float maxDiff = Mth::absMax(diffX, diffZ); @@ -963,8 +966,8 @@ void Entity::rideTick() Entity* riding = getRiding(); if (!riding || riding->m_bRemoved) { - m_riderId = 0; - setSharedFlag(C_ENTITY_FLAG_RIDING, false); + setRiding(nullptr); + return; } // we don't move @@ -1028,12 +1031,11 @@ void Entity::ride(Entity* newRiding) { moveTo(oldRiding->m_pos); setRot(oldRiding->m_rot); - oldRiding->m_riderId = 0; // Let them know you dismounted them + oldRiding->setRider(nullptr); } // Let yourself know you aren't riding anything - m_ridingId = 0; - setSharedFlag(C_ENTITY_FLAG_RIDING, false); + setRiding(nullptr); return; } @@ -1041,10 +1043,9 @@ void Entity::ride(Entity* newRiding) // Dismount if the same entity is fed in if (oldRiding && oldRiding == newRiding) { - oldRiding->m_riderId = 0; + oldRiding->setRider(nullptr); - m_ridingId = 0; - setSharedFlag(C_ENTITY_FLAG_RIDING, false); + setRiding(nullptr); moveTo(oldRiding->m_pos); setRot(oldRiding->m_rot); @@ -1054,31 +1055,27 @@ void Entity::ride(Entity* newRiding) // if (this.riding != null) this.riding.rider = null; if (oldRiding) { - oldRiding->m_riderId = 0; + oldRiding->setRider(nullptr); } // if (newRiding.rider != null) newRiding.rider.riding = null; // i hate this name but it's literally what it is if (Entity* newRidesOldRider = newRiding->getRider()) { - newRidesOldRider->m_ridingId = 0; - newRidesOldRider->setSharedFlag(C_ENTITY_FLAG_RIDING, false); + setRiding(nullptr); + newRidesOldRider->setRider(nullptr); } - // Tell yourself that you're riding the new ride - m_ridingId = newRiding->m_EntityID; - setSharedFlag(C_ENTITY_FLAG_RIDING, true); - - // Tell the new ride that it's being ridden by you - newRiding->m_riderId = m_EntityID; + setRiding(newRiding); + newRiding->setRider(this); } Entity* Entity::getRiding() const { - if (m_ridingId <= 0) + if (_ridingId <= 0) return nullptr; - if (Entity* riding = m_pLevel->getEntity(m_ridingId)) + if (Entity* riding = m_pLevel->getEntity(_ridingId)) return riding; return nullptr; @@ -1086,15 +1083,26 @@ Entity* Entity::getRiding() const Entity* Entity::getRider() const { - if (m_riderId <= 0) + if (_riderId <= 0) return nullptr; - if (Entity* rider = m_pLevel->getEntity(m_riderId)) + if (Entity* rider = m_pLevel->getEntity(_riderId)) return rider; return nullptr; } +void Entity::setRider(Entity* rider) +{ + _riderId = (rider) ? rider->m_EntityID : 0; +} + +void Entity::setRiding(Entity* riding) +{ + _ridingId = (riding) ? riding->m_EntityID : 0; + setSharedFlag(C_ENTITY_FLAG_RIDING, riding); +} + /*void Entity::thunderHit(LightningBolt* bolt) { burn(5); diff --git a/source/world/entity/Entity.hpp b/source/world/entity/Entity.hpp index e3b53d6a1..9fec6801f 100644 --- a/source/world/entity/Entity.hpp +++ b/source/world/entity/Entity.hpp @@ -207,6 +207,8 @@ class Entity virtual float getRidingHeight() const { return m_heightOffset; } Entity* getRiding() const; Entity* getRider() const; + void setRiding(Entity* ent); + void setRider(Entity* ent); void load(const CompoundTag& tag); bool save(CompoundTag& tag) const; void saveWithoutId(CompoundTag& tag) const; @@ -231,6 +233,10 @@ class Entity (m_pos.z - pos.z) * (m_pos.z - pos.z); } +private: + Entity::ID _ridingId; + Entity::ID _riderId; + protected: SynchedEntityData m_entityData; bool m_bMakeStepSound; @@ -248,8 +254,6 @@ class Entity float field_30; //TileSource* m_pTileSource; DimensionId m_dimensionId; - Entity::ID m_ridingId; - Entity::ID m_riderId; bool m_bRiding; bool m_bBlocksBuilding; Level* m_pLevel; diff --git a/source/world/entity/Mob.cpp b/source/world/entity/Mob.cpp index 6dede3f9b..459ef5571 100644 --- a/source/world/entity/Mob.cpp +++ b/source/world/entity/Mob.cpp @@ -758,7 +758,7 @@ void Mob::aiStep() { Entity* pEnt = *it; if (pEnt->isPushable()) - pEnt->push(this); + pEnt->push(this); } } diff --git a/source/world/level/Level.cpp b/source/world/level/Level.cpp index 0c1efd729..08d495c18 100644 --- a/source/world/level/Level.cpp +++ b/source/world/level/Level.cpp @@ -1260,9 +1260,8 @@ void Level::removeAllPendingEntityRemovals() { if (riding->m_bRemoved || riding->getRider() != ent) { - riding->m_riderId = 0; - ent->m_ridingId = 0; - ent->setSharedFlag(C_ENTITY_FLAG_RIDING, false); + riding->setRider(nullptr); + ent->setRiding(nullptr); } else continue; @@ -1742,14 +1741,13 @@ void Level::tick(Entity* pEnt, bool shouldTick) if (shouldTick && pEnt->m_bInAChunk) { Entity* rider = pEnt->getRider(); + // someone is riding this entity if (rider) { if (rider->m_bRemoved || rider->getRiding() != pEnt) { - rider->m_riderId = 0; - - pEnt->m_ridingId = 0; - pEnt->setSharedFlag(C_ENTITY_FLAG_RIDING, false); + rider->setRiding(nullptr); + pEnt->setRider(nullptr); } else { @@ -1797,9 +1795,8 @@ void Level::tickEntities() { if (riding->m_bRemoved || riding->getRider() != pEnt) { - riding->m_riderId = 0; - pEnt->m_ridingId = 0; - pEnt->setSharedFlag(C_ENTITY_FLAG_RIDING, false); + riding->setRider(nullptr); + pEnt->setRiding(nullptr); } else { From 6bea080d8b33da01a06726233cb517584222a2f7 Mon Sep 17 00:00:00 2001 From: Brent Da Mage Date: Wed, 18 Feb 2026 02:23:38 -0600 Subject: [PATCH 20/63] Fixed container listening and networking --- .../windows/projects/World/World.vcxproj | 14 ++-- .../projects/World/World.vcxproj.filters | 42 +++++++---- .../multiplayer/MultiplayerLocalPlayer.cpp | 12 ++- .../multiplayer/MultiplayerLocalPlayer.hpp | 3 +- .../network/ClientSideNetworkHandler.cpp | 6 ++ source/client/player/LocalPlayer.cpp | 10 +++ source/network/Packet.hpp | 1 + source/network/PacketUtil.cpp | 5 ++ .../network/packets/ContainerOpenPacket.hpp | 2 +- .../packets/ContainerSetContentPacket.cpp | 2 +- source/server/ServerPlayer.cpp | 6 ++ source/server/ServerPlayer.hpp | 3 +- source/world/Container.hpp | 37 ---------- source/world/entity/Player.cpp | 26 +++++-- source/world/entity/Player.hpp | 5 +- source/world/inventory/ArmorSlot.hpp | 2 +- source/world/inventory/ChestMenu.hpp | 2 +- .../{ => inventory}/CompoundContainer.cpp | 6 +- .../{ => inventory}/CompoundContainer.hpp | 2 +- source/world/inventory/Container.hpp | 55 ++++++++++++++ .../ContainerContentChangeListener.cpp | 2 + .../ContainerContentChangeListener.hpp | 12 +++ .../{ => inventory}/ContainerListener.cpp | 2 +- .../{ => inventory}/ContainerListener.hpp | 0 source/world/inventory/ContainerMenu.cpp | 74 ++++++++++++++----- source/world/inventory/ContainerMenu.hpp | 22 ++++-- .../inventory/ContainerSizeChangeListener.cpp | 1 + .../inventory/ContainerSizeChangeListener.hpp | 12 +++ source/world/inventory/CraftingContainer.cpp | 2 +- source/world/inventory/CraftingContainer.hpp | 4 +- source/world/inventory/CraftingMenu.cpp | 3 + source/world/inventory/FurnaceMenu.cpp | 4 +- source/world/inventory/FurnaceResultSlot.hpp | 2 +- source/world/inventory/InventoryMenu.cpp | 3 + source/world/inventory/ResultContainer.cpp | 2 +- source/world/inventory/ResultContainer.hpp | 4 +- source/world/inventory/ResultSlot.hpp | 2 +- source/world/inventory/SimpleContainer.cpp | 51 ++++++++++--- source/world/inventory/SimpleContainer.hpp | 15 +++- source/world/inventory/Slot.hpp | 8 +- source/world/item/Inventory.hpp | 6 +- source/world/item/crafting/Recipe.hpp | 2 +- source/world/tile/ChestTile.cpp | 2 +- source/world/tile/entity/ChestTileEntity.cpp | 2 +- source/world/tile/entity/ChestTileEntity.hpp | 2 +- .../world/tile/entity/FurnaceTileEntity.cpp | 3 +- .../world/tile/entity/FurnaceTileEntity.hpp | 2 +- 47 files changed, 346 insertions(+), 139 deletions(-) delete mode 100644 source/world/Container.hpp rename source/world/{ => inventory}/CompoundContainer.cpp (90%) rename source/world/{ => inventory}/CompoundContainer.hpp (92%) create mode 100644 source/world/inventory/Container.hpp create mode 100644 source/world/inventory/ContainerContentChangeListener.cpp create mode 100644 source/world/inventory/ContainerContentChangeListener.hpp rename source/world/{ => inventory}/ContainerListener.cpp (81%) rename source/world/{ => inventory}/ContainerListener.hpp (100%) create mode 100644 source/world/inventory/ContainerSizeChangeListener.cpp create mode 100644 source/world/inventory/ContainerSizeChangeListener.hpp diff --git a/platforms/windows/projects/World/World.vcxproj b/platforms/windows/projects/World/World.vcxproj index 7247155f5..7b610602c 100644 --- a/platforms/windows/projects/World/World.vcxproj +++ b/platforms/windows/projects/World/World.vcxproj @@ -202,7 +202,7 @@ - + @@ -210,7 +210,7 @@ - + @@ -242,6 +242,8 @@ + + @@ -385,8 +387,8 @@ - - + + @@ -394,7 +396,7 @@ - + @@ -423,6 +425,8 @@ + + diff --git a/platforms/windows/projects/World/World.vcxproj.filters b/platforms/windows/projects/World/World.vcxproj.filters index 6c9bbd804..85317d225 100644 --- a/platforms/windows/projects/World/World.vcxproj.filters +++ b/platforms/windows/projects/World/World.vcxproj.filters @@ -587,9 +587,6 @@ Source Files\Level\LevelGen\Chunk - - Source Files - Source Files\Inventory @@ -614,9 +611,6 @@ Source Files\Inventory - - Source Files - Source Files\Inventory @@ -707,6 +701,18 @@ Source Files\Tile\Entity + + Source Files\Inventory + + + Source Files\Inventory + + + Source Files\Inventory + + + Source Files\Inventory + @@ -1132,12 +1138,6 @@ Header Files\Level - - Header Files - - - Header Files - Header Files\Inventory @@ -1159,9 +1159,6 @@ Header Files\Inventory - - Header Files - Header Files\Inventory @@ -1246,5 +1243,20 @@ Header Files\Tile + + Header Files\Inventory + + + Header Files\Inventory + + + Header Files\Inventory + + + Header Files\Inventory + + + Header Files\Inventory + \ No newline at end of file diff --git a/source/client/multiplayer/MultiplayerLocalPlayer.cpp b/source/client/multiplayer/MultiplayerLocalPlayer.cpp index 85742cbbf..36750209d 100644 --- a/source/client/multiplayer/MultiplayerLocalPlayer.cpp +++ b/source/client/multiplayer/MultiplayerLocalPlayer.cpp @@ -15,6 +15,14 @@ void MultiplayerLocalPlayer::reallyDrop(ItemEntity* itemEntity) { } +void MultiplayerLocalPlayer::_handleOpenedContainerMenu() +{ + if (m_pContainerMenu) + m_pContainerMenu->addSlotListener(this); + else + LOG_W("Tried to add MultiplayerLocalPlayer as ContainerListener for NULL container!"); +} + bool MultiplayerLocalPlayer::hurt(Entity* pAttacker, int damage) { // Java returns false @@ -135,5 +143,7 @@ void MultiplayerLocalPlayer::refreshContainer(ContainerMenu* menu, const std::ve void MultiplayerLocalPlayer::slotChanged(ContainerMenu* menu, int index, ItemStack& item, bool isResultSlot) { - // @TODO: Replicate ContainerSetSlotPacket +#if NETWORK_PROTOCOL_VERSION >= 5 + m_pMinecraft->m_pRakNetInstance->send(new ContainerSetSlotPacket(m_pContainerMenu->m_containerId, index, item)); +#endif } diff --git a/source/client/multiplayer/MultiplayerLocalPlayer.hpp b/source/client/multiplayer/MultiplayerLocalPlayer.hpp index d1a7e2bec..6ab1a580b 100644 --- a/source/client/multiplayer/MultiplayerLocalPlayer.hpp +++ b/source/client/multiplayer/MultiplayerLocalPlayer.hpp @@ -1,7 +1,7 @@ #pragma once #include "client/player/LocalPlayer.hpp" -#include "world/ContainerListener.hpp" +#include "world/inventory/ContainerListener.hpp" class MultiplayerLocalPlayer : public LocalPlayer, public ContainerListener { @@ -10,6 +10,7 @@ class MultiplayerLocalPlayer : public LocalPlayer, public ContainerListener protected: void reallyDrop(ItemEntity* itemEntity) override; + void _handleOpenedContainerMenu() override; public: bool hurt(Entity*, int) override; diff --git a/source/client/network/ClientSideNetworkHandler.cpp b/source/client/network/ClientSideNetworkHandler.cpp index a1392ef6e..5079976d5 100644 --- a/source/client/network/ClientSideNetworkHandler.cpp +++ b/source/client/network/ClientSideNetworkHandler.cpp @@ -777,7 +777,9 @@ void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& guid, ContainerS if (pContainerMenu->m_containerId != packet->m_containerId) return; + pContainerMenu->m_bBroadcastChanges = false; pContainerMenu->setItem(packet->m_slot, packet->m_item); + pContainerMenu->m_bBroadcastChanges = true; } void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& guid, ContainerSetDataPacket* packet) @@ -798,7 +800,9 @@ void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& guid, ContainerS if (pContainerMenu->m_containerId != packet->m_containerId) return; + pContainerMenu->m_bBroadcastChanges = false; pContainerMenu->setData(packet->m_slot, packet->m_value); + pContainerMenu->m_bBroadcastChanges = true; } void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& guid, ContainerSetContentPacket* packet) @@ -819,7 +823,9 @@ void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& guid, ContainerS if (pContainerMenu->m_containerId != packet->m_containerId) return; + pContainerMenu->m_bBroadcastChanges = false; pContainerMenu->setAll(packet->m_items); + pContainerMenu->m_bBroadcastChanges = true; } void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& guid, LevelDataPacket* packet) diff --git a/source/client/player/LocalPlayer.cpp b/source/client/player/LocalPlayer.cpp index 6ca19d378..787d65806 100644 --- a/source/client/player/LocalPlayer.cpp +++ b/source/client/player/LocalPlayer.cpp @@ -133,18 +133,24 @@ void LocalPlayer::swing() void LocalPlayer::startCrafting(const TilePos& pos) { m_pMinecraft->getScreenChooser()->pushCraftingScreen(this, pos); + + Player::startCrafting(pos); } void LocalPlayer::openFurnace(FurnaceTileEntity* furnace) { // PE 0.3.2 doesn't let you cook in creative mode m_pMinecraft->getScreenChooser()->pushFurnaceScreen(this, furnace); + + Player::openFurnace(furnace); } void LocalPlayer::openContainer(Container* container) { // PE 0.3.2 doesn't let you open chests in creative mode m_pMinecraft->getScreenChooser()->pushChestScreen(this, container); + + Player::openContainer(container); } void LocalPlayer::closeContainer() @@ -157,11 +163,15 @@ void LocalPlayer::closeContainer() /*void LocalPlayer::openTrap(DispenserTileEntity* tileEntity) { m_pMinecraft->setScreen(new TrapScreen(m_pInventory, tileEntity)); + + Player::openTrap(tileEntity); }*/ /*void LocalPlayer::openTextEdit(SignTileEntity* tileEntity) { m_pMinecraft->setScreen(new TextEditScreen(tileEntity)); + + Player::openTextEdit(tileEntity); }*/ void LocalPlayer::reset() diff --git a/source/network/Packet.hpp b/source/network/Packet.hpp index 4825bee6f..4d7a538d5 100644 --- a/source/network/Packet.hpp +++ b/source/network/Packet.hpp @@ -19,6 +19,7 @@ //#define NETWORK_PROTOCOL_VERSION 4 // 0.3.0 //#define NETWORK_PROTOCOL_VERSION 5 // 0.3.2 #define NETWORK_PROTOCOL_VERSION 6 // 0.3.3 +//#define NETWORK_PROTOCOL_VERSION 29 // 0.12.1 class NetEventCallback; class Level; diff --git a/source/network/PacketUtil.cpp b/source/network/PacketUtil.cpp index f6000cb85..8be6f9bbe 100644 --- a/source/network/PacketUtil.cpp +++ b/source/network/PacketUtil.cpp @@ -1,4 +1,5 @@ #include "PacketUtil.hpp" +#include "Packet.hpp" #include "nbt/NbtIo.hpp" char PacketUtil::Rot_degreesToChar(float degrees) @@ -146,12 +147,14 @@ void PacketUtil::WriteItemStack(const ItemStack& item, RakNet::BitStream& bs, bo int16_t itemId = item.getId(); int8_t count = item.m_count; int16_t auxValue = item.getAuxValue(); +#if NETWORK_PROTOCOL_VERSION >= 29 if (itemId <= 0) { itemId = -1; bs.Write(itemId); return; } +#endif bs.Write(itemId); bs.Write(count); @@ -166,8 +169,10 @@ ItemStack PacketUtil::ReadItemStack(RakNet::BitStream& bs, bool doUserData) if (!bs.Read(itemId)) return ItemStack(); +#if NETWORK_PROTOCOL_VERSION >= 29 if (itemId == ItemStack::EMPTY.getId()) return ItemStack(); +#endif uint8_t count; int16_t auxValue; diff --git a/source/network/packets/ContainerOpenPacket.hpp b/source/network/packets/ContainerOpenPacket.hpp index 11fc81f6c..648d6569b 100644 --- a/source/network/packets/ContainerOpenPacket.hpp +++ b/source/network/packets/ContainerOpenPacket.hpp @@ -2,7 +2,7 @@ #include #include "../Packet.hpp" -#include "world/Container.hpp" +#include "world/inventory/Container.hpp" class ContainerOpenPacket : public Packet { diff --git a/source/network/packets/ContainerSetContentPacket.cpp b/source/network/packets/ContainerSetContentPacket.cpp index 02b5b07b5..cc1552065 100644 --- a/source/network/packets/ContainerSetContentPacket.cpp +++ b/source/network/packets/ContainerSetContentPacket.cpp @@ -30,7 +30,7 @@ void ContainerSetContentPacket::read(RakNet::BitStream& bs) bs.Read(m_containerId); int16_t size = 0; bs.Read(size); - m_items.resize(size); + m_items.reserve(size); for (uint16_t i = 0; i < size; i++) { diff --git a/source/server/ServerPlayer.cpp b/source/server/ServerPlayer.cpp index 8a0aad136..a2a5d7cc5 100644 --- a/source/server/ServerPlayer.cpp +++ b/source/server/ServerPlayer.cpp @@ -24,6 +24,11 @@ ServerPlayer::ServerPlayer(Level* pLevel, GameType playerGameType) m_pInventoryMenu->addSlotListener(this); } +ServerPlayer::~ServerPlayer() +{ + doCloseContainer(); +} + void ServerPlayer::_nextContainerCounter() { m_containerId++; @@ -156,5 +161,6 @@ void ServerPlayer::setContainerMenu(ContainerMenu* menu) { m_pContainerMenu->m_containerId = m_containerId; m_pContainerMenu->addSlotListener(this); + refreshContainer(m_pContainerMenu, m_pContainerMenu->cloneItems()); } } \ No newline at end of file diff --git a/source/server/ServerPlayer.hpp b/source/server/ServerPlayer.hpp index 11152a123..874a0f1cc 100644 --- a/source/server/ServerPlayer.hpp +++ b/source/server/ServerPlayer.hpp @@ -1,10 +1,11 @@ #include "world/entity/Player.hpp" -#include "world/ContainerListener.hpp" +#include "world/inventory/ContainerListener.hpp" class ServerPlayer : public Player, public ContainerListener { public: ServerPlayer(Level* pLevel, GameType playerGameType); + ~ServerPlayer(); protected: void _nextContainerCounter(); diff --git a/source/world/Container.hpp b/source/world/Container.hpp deleted file mode 100644 index 8c18f3a6c..000000000 --- a/source/world/Container.hpp +++ /dev/null @@ -1,37 +0,0 @@ -#pragma once - -#include "world/item/ItemStack.hpp" - -#define C_MAX_CONTAINER_STACK_SIZE (64) - -class Container -{ -public: - enum Type - { - CONTAINER, - CRAFTING, - FURNACE, - DISPENSER - }; - -public: - virtual uint16_t getContainerSize() const = 0; - virtual ItemStack& getItem(int index) = 0; - virtual ItemStack* tryGetItem(int index) - { - if (index >= 0 && index < getContainerSize()) - return &getItem(index); - else - return nullptr; - } - virtual ItemStack removeItem(int index, int count) = 0; - virtual void setItem(int index, const ItemStack& item) = 0; - virtual std::string getName() const = 0; - virtual int getMaxStackSize() - { - return C_MAX_CONTAINER_STACK_SIZE; - } - virtual void setChanged() = 0; - virtual bool stillValid(Player* player) const = 0; -}; \ No newline at end of file diff --git a/source/world/entity/Player.cpp b/source/world/entity/Player.cpp index 95193e007..2ce2c9012 100644 --- a/source/world/entity/Player.cpp +++ b/source/world/entity/Player.cpp @@ -63,8 +63,8 @@ Player::Player(Level* pLevel, GameType playerGameType) : Mob(pLevel) Player::~Player() { - delete m_pInventory; delete m_pInventoryMenu; + delete m_pInventory; } void Player::reallyDrop(ItemEntity* pEnt) @@ -72,6 +72,10 @@ void Player::reallyDrop(ItemEntity* pEnt) m_pLevel->addEntity(pEnt); } +void Player::_handleOpenedContainerMenu() +{ +} + void Player::reset() { Mob::reset(); @@ -563,14 +567,12 @@ void Player::drop(const ItemStack& item, bool randomly) void Player::startCrafting(const TilePos& pos) { -} - -void Player::openFurnace(FurnaceTileEntity* tileEntity) -{ + _handleOpenedContainerMenu(); } void Player::startStonecutting(const TilePos& pos) { + _handleOpenedContainerMenu(); } void Player::startDestroying() @@ -583,6 +585,20 @@ void Player::stopDestroying() m_destroyingBlock = false; } +void Player::openFurnace(FurnaceTileEntity* tileEntity) +{ + _handleOpenedContainerMenu(); +} + +void Player::openContainer(Container* container) +{ + _handleOpenedContainerMenu(); +} + +void Player::closeContainer() +{ +} + void Player::touch(Entity* pEnt) { pEnt->playerTouch(this); diff --git a/source/world/entity/Player.hpp b/source/world/entity/Player.hpp index 3ba5345a2..c2c5dcdd2 100644 --- a/source/world/entity/Player.hpp +++ b/source/world/entity/Player.hpp @@ -41,6 +41,7 @@ class Player : public Mob protected: virtual void reallyDrop(ItemEntity* pEnt); + virtual void _handleOpenedContainerMenu(); public: void reset() override; @@ -74,8 +75,8 @@ class Player : public Mob virtual void startDestroying(); virtual void stopDestroying(); virtual void openFurnace(FurnaceTileEntity* tileEntity); - virtual void openContainer(Container* container) {} - virtual void closeContainer() {} + virtual void openContainer(Container* container); + virtual void closeContainer(); //virtual void openTrap(DispenserTileEntity* tileEntity); //virtual void openTextEdit(SignTileEntity* tileEntity); virtual bool isLocalPlayer() const { return false; } diff --git a/source/world/inventory/ArmorSlot.hpp b/source/world/inventory/ArmorSlot.hpp index 2b679e8d9..0e54f8ab5 100644 --- a/source/world/inventory/ArmorSlot.hpp +++ b/source/world/inventory/ArmorSlot.hpp @@ -1,7 +1,7 @@ #pragma once #include "Slot.hpp" -#include "world/Container.hpp" +#include "Container.hpp" class ArmorSlot : public Slot { diff --git a/source/world/inventory/ChestMenu.hpp b/source/world/inventory/ChestMenu.hpp index ea787bea3..04eb68da5 100644 --- a/source/world/inventory/ChestMenu.hpp +++ b/source/world/inventory/ChestMenu.hpp @@ -1,7 +1,7 @@ #pragma once #include "ContainerMenu.hpp" -#include "world/Container.hpp" +#include "Container.hpp" #include "world/entity/Player.hpp" class ChestMenu : public ContainerMenu diff --git a/source/world/CompoundContainer.cpp b/source/world/inventory/CompoundContainer.cpp similarity index 90% rename from source/world/CompoundContainer.cpp rename to source/world/inventory/CompoundContainer.cpp index 80e339b90..ea4d4dd1c 100644 --- a/source/world/CompoundContainer.cpp +++ b/source/world/inventory/CompoundContainer.cpp @@ -44,10 +44,10 @@ int CompoundContainer::getMaxStackSize() return m_pLeftContainer->getMaxStackSize(); } -void CompoundContainer::setChanged() +void CompoundContainer::setContainerChanged(SlotID slot) { - m_pLeftContainer->setChanged(); - m_pRightContainer->setChanged(); + m_pLeftContainer->setContainerChanged(slot); + m_pRightContainer->setContainerChanged(slot); } bool CompoundContainer::stillValid(Player* player) const diff --git a/source/world/CompoundContainer.hpp b/source/world/inventory/CompoundContainer.hpp similarity index 92% rename from source/world/CompoundContainer.hpp rename to source/world/inventory/CompoundContainer.hpp index 5a4884591..1c3fd0fea 100644 --- a/source/world/CompoundContainer.hpp +++ b/source/world/inventory/CompoundContainer.hpp @@ -25,7 +25,7 @@ class CompoundContainer : public Container int getMaxStackSize() override; - void setChanged() override; + void setContainerChanged(SlotID slot) override; bool stillValid(Player* player) const override; }; diff --git a/source/world/inventory/Container.hpp b/source/world/inventory/Container.hpp new file mode 100644 index 000000000..6e6956689 --- /dev/null +++ b/source/world/inventory/Container.hpp @@ -0,0 +1,55 @@ +#pragma once + +#include + +#include "world/item/ItemStack.hpp" + +#define C_MAX_CONTAINER_STACK_SIZE (64) + +class ContainerContentChangeListener; +class ContainerSizeChangeListener; + +class Container +{ +protected: + typedef std::set ContentChangeListeners; + typedef std::set SizeChangeListeners; + +public: + typedef uint16_t Size; + +public: + enum Type + { + CONTAINER, + CRAFTING, + FURNACE, + DISPENSER + }; + +public: + virtual Size getContainerSize() const = 0; + virtual ItemStack& getItem(int index) = 0; + virtual ItemStack* tryGetItem(int index) + { + if (index >= 0 && index < getContainerSize()) + return &getItem(index); + else + return nullptr; + } + virtual ItemStack removeItem(int index, int count) = 0; + virtual void setItem(int index, const ItemStack& item) = 0; + virtual std::string getName() const = 0; + virtual int getMaxStackSize() + { + return C_MAX_CONTAINER_STACK_SIZE; + } + // Was called setChanged in Java + virtual void setContainerChanged(SlotID slot) = 0; + virtual bool stillValid(Player* player) const = 0; + + virtual void addContentChangeListener(ContainerContentChangeListener* listener) {} + virtual void addSizeChangeListener(ContainerSizeChangeListener* listener) {} + virtual void removeContentChangeListener(ContainerContentChangeListener* listener) {} + virtual void removeSizeChangeListener(ContainerSizeChangeListener* listener) {} +}; \ No newline at end of file diff --git a/source/world/inventory/ContainerContentChangeListener.cpp b/source/world/inventory/ContainerContentChangeListener.cpp new file mode 100644 index 000000000..bb72498aa --- /dev/null +++ b/source/world/inventory/ContainerContentChangeListener.cpp @@ -0,0 +1,2 @@ +#include "ContainerContentChangeListener.hpp" + diff --git a/source/world/inventory/ContainerContentChangeListener.hpp b/source/world/inventory/ContainerContentChangeListener.hpp new file mode 100644 index 000000000..675f1b221 --- /dev/null +++ b/source/world/inventory/ContainerContentChangeListener.hpp @@ -0,0 +1,12 @@ +#pragma once +#include +#include "Container.hpp" + +class ContainerContentChangeListener +{ +public: + virtual ~ContainerContentChangeListener() {} + +public: + virtual void containerContentChanged(SlotID slot) = 0; +}; diff --git a/source/world/ContainerListener.cpp b/source/world/inventory/ContainerListener.cpp similarity index 81% rename from source/world/ContainerListener.cpp rename to source/world/inventory/ContainerListener.cpp index ddfc041fa..d0377bd7c 100644 --- a/source/world/ContainerListener.cpp +++ b/source/world/inventory/ContainerListener.cpp @@ -7,5 +7,5 @@ ContainerListener::~ContainerListener() void ContainerListener::refreshContainerItems(ContainerMenu* menu) { - refreshContainer(menu, menu->copyItems()); + refreshContainer(menu, menu->cloneItems()); } diff --git a/source/world/ContainerListener.hpp b/source/world/inventory/ContainerListener.hpp similarity index 100% rename from source/world/ContainerListener.hpp rename to source/world/inventory/ContainerListener.hpp diff --git a/source/world/inventory/ContainerMenu.cpp b/source/world/inventory/ContainerMenu.cpp index f618561e3..7e52498df 100644 --- a/source/world/inventory/ContainerMenu.cpp +++ b/source/world/inventory/ContainerMenu.cpp @@ -1,14 +1,15 @@ #include "ContainerMenu.hpp" -#include "Slot.hpp" #include "world/item/ItemStack.hpp" #include "world/item/Inventory.hpp" -#include "world/Container.hpp" -#include "world/ContainerListener.hpp" +#include "Slot.hpp" +#include "Container.hpp" +#include "ContainerListener.hpp" ContainerMenu::ContainerMenu(Container::Type containerType) : m_changeUid(0) , m_containerId(0) , m_containerType(containerType) + , m_bBroadcastChanges(true) { } @@ -18,8 +19,24 @@ ContainerMenu::~ContainerMenu() /*for (std::vector::iterator it = m_listeners.begin(); it != m_listeners.end(); ++it) delete (*it);*/ + _clearSlots(); +} + +void ContainerMenu::_clearSlots() +{ for (std::vector::iterator it = m_slots.begin(); it != m_slots.end(); ++it) - delete (*it); + { + Slot* slot = *it; + + // @HACK: I don't like this + Container* pContainer = slot->m_pContainer; + if (pContainer) + pContainer->removeContentChangeListener(this); + + delete slot; + } + + m_slots.clear(); } void ContainerMenu::addSlot(Slot* slot) @@ -27,35 +44,45 @@ void ContainerMenu::addSlot(Slot* slot) slot->m_index = m_slots.size(); m_slots.push_back(slot); m_lastSlots.push_back(ItemStack::EMPTY); + + // @HACK: holy hack + Container* pContainer = slot->m_pContainer; + if (pContainer) + pContainer->addContentChangeListener(this); } void ContainerMenu::addSlotListener(ContainerListener* listener) { - m_listeners.push_back(listener); + m_listeners.insert(listener); // Not done on PE - /*std::vector snapshot = copyItems(); + /*std::vector snapshot = cloneItems(); listener->refreshContainer(this, snapshot); broadcastChanges();*/ } void ContainerMenu::sendData(int id, int value) { - for (std::vector::iterator it = m_listeners.begin(); it != m_listeners.end(); ++it) + for (ContainerListeners::iterator it = m_listeners.begin(); it != m_listeners.end(); ++it) (*it)->setContainerData(this, id, value); } +void ContainerMenu::broadcastChanges(SlotID slot) +{ + ItemStack& current = m_slots[slot]->getItem(); + if (m_lastSlots[slot] != current) + { + m_lastSlots[slot] = ItemStack(current); + for (ContainerListeners::iterator it = m_listeners.begin(); it != m_listeners.end(); ++it) + (*it)->slotChanged(this, slot, m_lastSlots[slot], isResultSlot()); + } +} + void ContainerMenu::broadcastChanges() { for (size_t i = 0; i < m_slots.size(); ++i) { - ItemStack& current = m_slots[i]->getItem(); - if (m_lastSlots[i] != current) - { - m_lastSlots[i] = ItemStack(current); - for (std::vector::iterator it = m_listeners.begin(); it != m_listeners.end(); ++it) - (*it)->slotChanged(this, i, m_lastSlots[i], isResultSlot()); - } + broadcastChanges(i); } } @@ -74,12 +101,15 @@ void ContainerMenu::slotsChanged(Container*) broadcastChanges(); } -std::vector ContainerMenu::copyItems() +std::vector ContainerMenu::cloneItems() { std::vector content; for (std::vector::iterator it = m_slots.begin(); it != m_slots.end(); ++it) - content.push_back((*it)->getItem()); + { + const ItemStack& item = (*it)->getItem(); + content.push_back(item); + } return content; } @@ -358,13 +388,19 @@ void ContainerMenu::rollbackToBackup(uint16_t) bool ContainerMenu::isSynched(Player* player) const { - return unsynchedPlayers.find(player) == unsynchedPlayers.end(); + return m_unsynchedPlayers.find(player) == m_unsynchedPlayers.end(); } void ContainerMenu::setSynched(Player* player, bool isSynched) { if (isSynched) - unsynchedPlayers.erase(player); + m_unsynchedPlayers.erase(player); else - unsynchedPlayers.insert(player); + m_unsynchedPlayers.insert(player); +} + +void ContainerMenu::containerContentChanged(SlotID slot) +{ + if (m_bBroadcastChanges) + broadcastChanges(slot); } diff --git a/source/world/inventory/ContainerMenu.hpp b/source/world/inventory/ContainerMenu.hpp index f7b299806..481d199be 100644 --- a/source/world/inventory/ContainerMenu.hpp +++ b/source/world/inventory/ContainerMenu.hpp @@ -3,30 +3,38 @@ #include #include #include "world/item/ItemStack.hpp" -#include "world/Container.hpp" #include "client/player/input/MouseDevice.hpp" +#include "Container.hpp" +#include "ContainerContentChangeListener.hpp" class Player; class Inventory; class Slot; class ContainerListener; -class ContainerMenu +class ContainerMenu : public ContainerContentChangeListener { +protected: + typedef std::set ContainerListeners; + public: ContainerMenu(Container::Type containerType); virtual ~ContainerMenu(); +protected: + void _clearSlots(); + public: void addSlot(Slot* slot); virtual void addSlotListener(ContainerListener* listener); void sendData(int id, int value); + virtual void broadcastChanges(SlotID slot); virtual void broadcastChanges(); virtual void removed(Player* player); virtual void slotsChanged(Container* container); // Called getItems in PE and Java - std::vector copyItems(); + std::vector cloneItems(); Slot* getSlotFor(Container* container, int index); Slot* getSlot(int index); virtual ItemStack clicked(int slotIndex, MouseButtonType mouseButton, bool quickMove, Player* player); @@ -50,14 +58,18 @@ class ContainerMenu //Unused virtual bool isPauseScreen() const { return false; } +public: + void containerContentChanged(SlotID slot) override; + protected: std::vector m_lastSlots; uint16_t m_changeUid; - std::vector m_listeners; - std::set unsynchedPlayers; + ContainerListeners m_listeners; + std::set m_unsynchedPlayers; public: int m_containerId; Container::Type m_containerType; std::vector m_slots; + bool m_bBroadcastChanges; }; diff --git a/source/world/inventory/ContainerSizeChangeListener.cpp b/source/world/inventory/ContainerSizeChangeListener.cpp new file mode 100644 index 000000000..671ca58bd --- /dev/null +++ b/source/world/inventory/ContainerSizeChangeListener.cpp @@ -0,0 +1 @@ +#include "ContainerSizeChangeListener.hpp" diff --git a/source/world/inventory/ContainerSizeChangeListener.hpp b/source/world/inventory/ContainerSizeChangeListener.hpp new file mode 100644 index 000000000..f39a00780 --- /dev/null +++ b/source/world/inventory/ContainerSizeChangeListener.hpp @@ -0,0 +1,12 @@ +#pragma once +#include +#include "Container.hpp" + +class ContainerSizeChangeListener +{ +public: + virtual ~ContainerSizeChangeListener() {} + +public: + virtual void containerSizeChanged(Container::Size size) = 0; +}; diff --git a/source/world/inventory/CraftingContainer.cpp b/source/world/inventory/CraftingContainer.cpp index 548c6db7b..edfc5a04c 100644 --- a/source/world/inventory/CraftingContainer.cpp +++ b/source/world/inventory/CraftingContainer.cpp @@ -73,7 +73,7 @@ void CraftingContainer::setItem(int index, const ItemStack& item) } } -void CraftingContainer::setChanged() +void CraftingContainer::setContainerChanged(SlotID slot) { } diff --git a/source/world/inventory/CraftingContainer.hpp b/source/world/inventory/CraftingContainer.hpp index 5fecc7d91..f450e2c9f 100644 --- a/source/world/inventory/CraftingContainer.hpp +++ b/source/world/inventory/CraftingContainer.hpp @@ -1,8 +1,8 @@ #pragma once #include -#include "world/Container.hpp" #include "world/item/ItemStack.hpp" +#include "Container.hpp" #include "ContainerMenu.hpp" class ContainerMenu; @@ -25,7 +25,7 @@ class CraftingContainer : public Container ItemStack removeItem(int index, int amount) override; void setItem(int index, const ItemStack& item) override; - void setChanged() override; + void setContainerChanged(SlotID slot) override; bool stillValid(Player* player) const override; private: diff --git a/source/world/inventory/CraftingMenu.cpp b/source/world/inventory/CraftingMenu.cpp index aeb9d016b..728aad397 100644 --- a/source/world/inventory/CraftingMenu.cpp +++ b/source/world/inventory/CraftingMenu.cpp @@ -36,6 +36,9 @@ CraftingMenu::CraftingMenu(Inventory* inventory, const TilePos& tilePos, Level* CraftingMenu::~CraftingMenu() { + _clearSlots(); + + // clearSlots must be called before these are deleted delete m_pCraftSlots; delete m_pResultSlots; } diff --git a/source/world/inventory/FurnaceMenu.cpp b/source/world/inventory/FurnaceMenu.cpp index e8f6678ac..b3c3cb2f3 100644 --- a/source/world/inventory/FurnaceMenu.cpp +++ b/source/world/inventory/FurnaceMenu.cpp @@ -1,7 +1,7 @@ #include "FurnaceMenu.hpp" #include "Slot.hpp" #include "FurnaceResultSlot.hpp" -#include "world/ContainerListener.hpp" +#include "ContainerListener.hpp" FurnaceMenu::FurnaceMenu(Inventory* inventory, FurnaceTileEntity* furnace) : ContainerMenu(Container::FURNACE), m_furnace(furnace), m_lastCookTime(0), m_lastBurnTime(0), m_lastLitDuration(0) @@ -39,7 +39,7 @@ void FurnaceMenu::broadcastChanges() { ContainerMenu::broadcastChanges(); - for (std::vector::iterator it = m_listeners.begin(); it != m_listeners.end(); ++it) + for (ContainerListeners::iterator it = m_listeners.begin(); it != m_listeners.end(); ++it) { ContainerListener* listener = *it; diff --git a/source/world/inventory/FurnaceResultSlot.hpp b/source/world/inventory/FurnaceResultSlot.hpp index 4c1e277a1..912b17000 100644 --- a/source/world/inventory/FurnaceResultSlot.hpp +++ b/source/world/inventory/FurnaceResultSlot.hpp @@ -1,7 +1,7 @@ #pragma once #include "Slot.hpp" -#include "source/world/Container.hpp" +#include "Container.hpp" class FurnaceResultSlot : public Slot { diff --git a/source/world/inventory/InventoryMenu.cpp b/source/world/inventory/InventoryMenu.cpp index 6c5d2a76f..1e5b2c37e 100644 --- a/source/world/inventory/InventoryMenu.cpp +++ b/source/world/inventory/InventoryMenu.cpp @@ -42,6 +42,9 @@ InventoryMenu::InventoryMenu(Inventory* inventory, bool active) InventoryMenu::~InventoryMenu() { + _clearSlots(); + + // clearSlots must be called before these are deleted delete m_pCraftSlots; delete m_pResultSlots; } diff --git a/source/world/inventory/ResultContainer.cpp b/source/world/inventory/ResultContainer.cpp index 3f6763d53..1cf3be2d6 100644 --- a/source/world/inventory/ResultContainer.cpp +++ b/source/world/inventory/ResultContainer.cpp @@ -40,7 +40,7 @@ void ResultContainer::setItem(int index, const ItemStack& item) m_item = item; } -void ResultContainer::setChanged() +void ResultContainer::setContainerChanged(SlotID slot) { } diff --git a/source/world/inventory/ResultContainer.hpp b/source/world/inventory/ResultContainer.hpp index a3d95dc65..14228c1fb 100644 --- a/source/world/inventory/ResultContainer.hpp +++ b/source/world/inventory/ResultContainer.hpp @@ -1,7 +1,7 @@ #pragma once -#include "world/Container.hpp" #include "world/item/ItemStack.hpp" +#include "Container.hpp" class Player; @@ -19,7 +19,7 @@ class ResultContainer : public Container ItemStack removeItem(int index, int amount) override; void setItem(int index, const ItemStack& item) override; - void setChanged() override; + void setContainerChanged(SlotID slot) override; bool stillValid(Player* player) const override; private: diff --git a/source/world/inventory/ResultSlot.hpp b/source/world/inventory/ResultSlot.hpp index 92dd109e6..7da681e83 100644 --- a/source/world/inventory/ResultSlot.hpp +++ b/source/world/inventory/ResultSlot.hpp @@ -1,7 +1,7 @@ #pragma once #include "Slot.hpp" -#include "world/Container.hpp" +#include "Container.hpp" class ResultSlot : public Slot { diff --git a/source/world/inventory/SimpleContainer.cpp b/source/world/inventory/SimpleContainer.cpp index 74b8c4c5d..62d57425c 100644 --- a/source/world/inventory/SimpleContainer.cpp +++ b/source/world/inventory/SimpleContainer.cpp @@ -1,8 +1,10 @@ #include "SimpleContainer.hpp" +#include "ContainerContentChangeListener.hpp" +#include "ContainerSizeChangeListener.hpp" -SimpleContainer::SimpleContainer(int size, const std::string& name) : - m_items(size), - m_name(name) +SimpleContainer::SimpleContainer(int size, const std::string& name) + : m_items(size) + , m_name(name) { } @@ -25,7 +27,7 @@ ItemStack SimpleContainer::removeItem(int index, int count) { result = m_items[index]; m_items[index] = ItemStack::EMPTY; - setChanged(); + setContainerChanged(index); return result; } else @@ -34,7 +36,7 @@ ItemStack SimpleContainer::removeItem(int index, int count) if (!m_items[index].m_count) m_items[index] = ItemStack::EMPTY; - setChanged(); + setContainerChanged(index); return result; } } @@ -47,7 +49,7 @@ void SimpleContainer::setItem(int index, const ItemStack& item) if (!item.isEmpty() && item.m_count > getMaxStackSize()) m_items[index].m_count = getMaxStackSize(); - setChanged(); + setContainerChanged(index); } std::string SimpleContainer::getName() const @@ -55,8 +57,13 @@ std::string SimpleContainer::getName() const return m_name; } -void SimpleContainer::setChanged() +void SimpleContainer::setContainerChanged(SlotID slot) { + for (ContentChangeListeners::iterator it = m_contentChangeListeners.begin(); it != m_contentChangeListeners.end(); it++) + { + ContainerContentChangeListener* pListener = *it; + pListener->containerContentChanged(slot); + } } bool SimpleContainer::stillValid(Player* player) const @@ -64,6 +71,31 @@ bool SimpleContainer::stillValid(Player* player) const return true; } +void SimpleContainer::addContentChangeListener(ContainerContentChangeListener* listener) +{ + m_contentChangeListeners.insert(listener); +} + +void SimpleContainer::addSizeChangeListener(ContainerSizeChangeListener* listener) +{ + m_sizeChangeListeners.insert(listener); +} + +void SimpleContainer::removeContentChangeListener(ContainerContentChangeListener* listener) +{ + m_contentChangeListeners.erase(listener); +} + +void SimpleContainer::removeSizeChangeListener(ContainerSizeChangeListener* listener) +{ + m_sizeChangeListeners.erase(listener); +} + +void SimpleContainer::clear() +{ + std::fill(m_items.begin(), m_items.end(), ItemStack::EMPTY); +} + void SimpleContainer::load(const CompoundTag& tag) { clear(); @@ -102,8 +134,3 @@ void SimpleContainer::save(CompoundTag& tag) const tag.put("Items", list); } - -void SimpleContainer::clear() -{ - std::fill(m_items.begin(), m_items.end(), ItemStack::EMPTY); -} \ No newline at end of file diff --git a/source/world/inventory/SimpleContainer.hpp b/source/world/inventory/SimpleContainer.hpp index a371d2532..2df4756a4 100644 --- a/source/world/inventory/SimpleContainer.hpp +++ b/source/world/inventory/SimpleContainer.hpp @@ -1,9 +1,10 @@ #pragma once -#include "world/Container.hpp" -#include "nbt/CompoundTag.hpp" #include +#include "Container.hpp" +#include "nbt/CompoundTag.hpp" + class SimpleContainer : public Container { public: @@ -15,15 +16,21 @@ class SimpleContainer : public Container ItemStack removeItem(int index, int count) override; void setItem(int index, const ItemStack& item) override; std::string getName() const override; - void setChanged() override; + void setContainerChanged(SlotID slot) override; bool stillValid(Player* player) const override; + void addContentChangeListener(ContainerContentChangeListener* listener) override; + void addSizeChangeListener(ContainerSizeChangeListener* listener) override; + void removeContentChangeListener(ContainerContentChangeListener* listener) override; + void removeSizeChangeListener(ContainerSizeChangeListener* listener) override; public: virtual void clear(); virtual void load(const CompoundTag& tag); virtual void save(CompoundTag& tag) const; -private: +protected: + ContentChangeListeners m_contentChangeListeners; + SizeChangeListeners m_sizeChangeListeners; std::vector m_items; std::string m_name; }; \ No newline at end of file diff --git a/source/world/inventory/Slot.hpp b/source/world/inventory/Slot.hpp index 12a033823..f4286639d 100644 --- a/source/world/inventory/Slot.hpp +++ b/source/world/inventory/Slot.hpp @@ -1,6 +1,6 @@ #pragma once -#include "world/Container.hpp" +#include "Container.hpp" class ItemStack; @@ -32,7 +32,7 @@ class Slot virtual void set(const ItemStack& item); - virtual void setChanged() { m_pContainer->setChanged(); } + virtual void setChanged() { m_pContainer->setContainerChanged(m_slot); } virtual int getMaxStackSize() const { return m_pContainer->getMaxStackSize(); } @@ -42,7 +42,7 @@ class Slot public: Container* m_pContainer; - int m_slot; - int m_index; + int m_slot; // the position in the attached container + int m_index; // the position in the ContainerMenu::m_slots vector Group m_group; }; \ No newline at end of file diff --git a/source/world/item/Inventory.hpp b/source/world/item/Inventory.hpp index 568a1e54a..876d06183 100644 --- a/source/world/item/Inventory.hpp +++ b/source/world/item/Inventory.hpp @@ -1,8 +1,8 @@ #pragma once #include -#include "world/Container.hpp" #include "GameMods.hpp" +#include "world/inventory/Container.hpp" #include "world/item/ItemStack.hpp" #include "world/entity/Player.hpp" #include "world/gamemode/GameType.hpp" @@ -80,9 +80,9 @@ class Inventory : public Container return "Inventory"; } - void setChanged() override { } + void setContainerChanged(SlotID slot) override { } - bool stillValid(Player* player) const override { return true; } + bool stillValid(Player* player) const override { return true; } private: GameType _getGameMode() const; diff --git a/source/world/item/crafting/Recipe.hpp b/source/world/item/crafting/Recipe.hpp index 6bb7a40dd..169d83a17 100644 --- a/source/world/item/crafting/Recipe.hpp +++ b/source/world/item/crafting/Recipe.hpp @@ -1,6 +1,6 @@ #pragma once -#include "world/Container.hpp" +#include "world/inventory/Container.hpp" class Recipe { diff --git a/source/world/tile/ChestTile.cpp b/source/world/tile/ChestTile.cpp index e7aad4afa..6db79d413 100644 --- a/source/world/tile/ChestTile.cpp +++ b/source/world/tile/ChestTile.cpp @@ -1,6 +1,6 @@ #include "ChestTile.hpp" #include "world/level/Level.hpp" -#include "world/CompoundContainer.hpp" +#include "world/inventory/CompoundContainer.hpp" #include "world/tile/entity/ChestTileEntity.hpp" ChestTile::ChestTile(int id, int texture) : EntityTile(id, texture, Material::wood) diff --git a/source/world/tile/entity/ChestTileEntity.cpp b/source/world/tile/entity/ChestTileEntity.cpp index f72f78a4c..323cfa7eb 100644 --- a/source/world/tile/entity/ChestTileEntity.cpp +++ b/source/world/tile/entity/ChestTileEntity.cpp @@ -25,7 +25,7 @@ bool ChestTileEntity::stillValid(Player* player) const return player->distanceToSqr(m_pos + 0.5f) <= 64.0f; } -void ChestTileEntity::setChanged() +void ChestTileEntity::setContainerChanged(SlotID slot) { TileEntity::setChanged(); } diff --git a/source/world/tile/entity/ChestTileEntity.hpp b/source/world/tile/entity/ChestTileEntity.hpp index 38137f631..0fb177245 100644 --- a/source/world/tile/entity/ChestTileEntity.hpp +++ b/source/world/tile/entity/ChestTileEntity.hpp @@ -13,5 +13,5 @@ class ChestTileEntity : public TileEntity, public SimpleContainer { bool stillValid(Player* player) const override; - virtual void setChanged() override; + void setContainerChanged(SlotID slot) override; }; diff --git a/source/world/tile/entity/FurnaceTileEntity.cpp b/source/world/tile/entity/FurnaceTileEntity.cpp index 1134d2736..28e7ead28 100644 --- a/source/world/tile/entity/FurnaceTileEntity.cpp +++ b/source/world/tile/entity/FurnaceTileEntity.cpp @@ -116,8 +116,9 @@ bool FurnaceTileEntity::stillValid(Player* player) const return player->distanceToSqr(m_pos + 0.5f) <= 64.0; } -void FurnaceTileEntity::setChanged() +void FurnaceTileEntity::setContainerChanged(SlotID slot) { + SimpleContainer::setContainerChanged(slot); TileEntity::setChanged(); } diff --git a/source/world/tile/entity/FurnaceTileEntity.hpp b/source/world/tile/entity/FurnaceTileEntity.hpp index 55108282f..0a50b691e 100644 --- a/source/world/tile/entity/FurnaceTileEntity.hpp +++ b/source/world/tile/entity/FurnaceTileEntity.hpp @@ -16,7 +16,7 @@ class FurnaceTileEntity : public SimpleContainer, public TileEntity public: void tick() override; bool stillValid(Player* player) const override; - void setChanged() override; + void setContainerChanged(SlotID slot) override; void load(const CompoundTag& tag) override; void save(CompoundTag& tag) const override; std::string getName() const override; From 5a4f4dbd5507763ec45b8fa16bf27b5c9b33158c Mon Sep 17 00:00:00 2001 From: Sam Date: Wed, 18 Feb 2026 08:49:39 -0500 Subject: [PATCH 21/63] CMakeLists & Xcode --- .../Minecraft.xcodeproj/project.pbxproj | 56 ++++++++++++------- source/CMakeLists.txt | 6 +- 2 files changed, 40 insertions(+), 22 deletions(-) diff --git a/platforms/macos/projects/Minecraft/Minecraft.xcodeproj/project.pbxproj b/platforms/macos/projects/Minecraft/Minecraft.xcodeproj/project.pbxproj index dcc3c8727..7600da5f0 100644 --- a/platforms/macos/projects/Minecraft/Minecraft.xcodeproj/project.pbxproj +++ b/platforms/macos/projects/Minecraft/Minecraft.xcodeproj/project.pbxproj @@ -989,11 +989,6 @@ 84E7BF272F286A08002D3936 /* SimpleContainer.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84E7BF172F286A08002D3936 /* SimpleContainer.hpp */; }; 84E7BF282F286A08002D3936 /* Slot.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84E7BF182F286A08002D3936 /* Slot.cpp */; }; 84E7BF292F286A08002D3936 /* Slot.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84E7BF192F286A08002D3936 /* Slot.hpp */; }; - 84E7BF2F2F286A20002D3936 /* CompoundContainer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84E7BF2A2F286A20002D3936 /* CompoundContainer.cpp */; }; - 84E7BF302F286A20002D3936 /* CompoundContainer.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84E7BF2B2F286A20002D3936 /* CompoundContainer.hpp */; }; - 84E7BF312F286A20002D3936 /* Container.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84E7BF2C2F286A20002D3936 /* Container.hpp */; }; - 84E7BF322F286A20002D3936 /* ContainerListener.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84E7BF2D2F286A20002D3936 /* ContainerListener.cpp */; }; - 84E7BF332F286A20002D3936 /* ContainerListener.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84E7BF2E2F286A20002D3936 /* ContainerListener.hpp */; }; 84E7BF362F286A6A002D3936 /* ItemStack.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84E7BF342F286A6A002D3936 /* ItemStack.cpp */; }; 84E7BF372F286A6A002D3936 /* ItemStack.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84E7BF352F286A6A002D3936 /* ItemStack.hpp */; }; 84E8DCE52E84ACBC00B30789 /* libRenderer.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 8406FD292AF1814500B09C1D /* libRenderer.a */; }; @@ -1287,6 +1282,15 @@ 84EEED642F0A01B700F741B6 /* ViewportOrigin.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84EEED622F0A01B700F741B6 /* ViewportOrigin.hpp */; }; 84EEED682F0A02F300F741B6 /* ErrorHandlerOGL.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84EEED662F0A02F300F741B6 /* ErrorHandlerOGL.cpp */; }; 84EEED692F0A02F300F741B6 /* ErrorHandlerOGL.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84EEED672F0A02F300F741B6 /* ErrorHandlerOGL.hpp */; }; + 84F0DA762F45FAE100906960 /* ContainerContentChangeListener.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84F0DA702F45FAE100906960 /* ContainerContentChangeListener.hpp */; }; + 84F0DA772F45FAE100906960 /* ContainerListener.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84F0DA722F45FAE100906960 /* ContainerListener.hpp */; }; + 84F0DA782F45FAE100906960 /* Container.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84F0DA6F2F45FAE100906960 /* Container.hpp */; }; + 84F0DA792F45FAE100906960 /* CompoundContainer.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84F0DA6D2F45FAE100906960 /* CompoundContainer.hpp */; }; + 84F0DA7A2F45FAE100906960 /* ContainerSizeChangeListener.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84F0DA742F45FAE100906960 /* ContainerSizeChangeListener.hpp */; }; + 84F0DA7B2F45FAE100906960 /* ContainerContentChangeListener.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84F0DA712F45FAE100906960 /* ContainerContentChangeListener.cpp */; }; + 84F0DA7C2F45FAE100906960 /* CompoundContainer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84F0DA6E2F45FAE100906960 /* CompoundContainer.cpp */; }; + 84F0DA7D2F45FAE100906960 /* ContainerListener.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84F0DA732F45FAE100906960 /* ContainerListener.cpp */; }; + 84F0DA7E2F45FAE100906960 /* ContainerSizeChangeListener.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84F0DA752F45FAE100906960 /* ContainerSizeChangeListener.cpp */; }; 84F4880E2EEA4F5C00309B1E /* FixedPipelineStateNull.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84F487F02EEA4F5C00309B1E /* FixedPipelineStateNull.cpp */; }; 84F4880F2EEA4F5C00309B1E /* FixedPipelineStateNull.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84F487F12EEA4F5C00309B1E /* FixedPipelineStateNull.hpp */; }; 84F488102EEA4F5C00309B1E /* FogStateNull.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84F487F22EEA4F5C00309B1E /* FogStateNull.cpp */; }; @@ -2766,11 +2770,6 @@ 84E7BF172F286A08002D3936 /* SimpleContainer.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = SimpleContainer.hpp; sourceTree = ""; }; 84E7BF182F286A08002D3936 /* Slot.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Slot.cpp; sourceTree = ""; }; 84E7BF192F286A08002D3936 /* Slot.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Slot.hpp; sourceTree = ""; }; - 84E7BF2A2F286A20002D3936 /* CompoundContainer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CompoundContainer.cpp; sourceTree = ""; }; - 84E7BF2B2F286A20002D3936 /* CompoundContainer.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = CompoundContainer.hpp; sourceTree = ""; }; - 84E7BF2C2F286A20002D3936 /* Container.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Container.hpp; sourceTree = ""; }; - 84E7BF2D2F286A20002D3936 /* ContainerListener.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ContainerListener.cpp; sourceTree = ""; }; - 84E7BF2E2F286A20002D3936 /* ContainerListener.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ContainerListener.hpp; sourceTree = ""; }; 84E7BF342F286A6A002D3936 /* ItemStack.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ItemStack.cpp; sourceTree = ""; }; 84E7BF352F286A6A002D3936 /* ItemStack.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ItemStack.hpp; sourceTree = ""; }; 84EAE8DD2AF1EAA1000894E8 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; @@ -3069,6 +3068,15 @@ 84EEED622F0A01B700F741B6 /* ViewportOrigin.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ViewportOrigin.hpp; sourceTree = ""; }; 84EEED662F0A02F300F741B6 /* ErrorHandlerOGL.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ErrorHandlerOGL.cpp; sourceTree = ""; }; 84EEED672F0A02F300F741B6 /* ErrorHandlerOGL.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ErrorHandlerOGL.hpp; sourceTree = ""; }; + 84F0DA6D2F45FAE100906960 /* CompoundContainer.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = CompoundContainer.hpp; sourceTree = ""; }; + 84F0DA6E2F45FAE100906960 /* CompoundContainer.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = CompoundContainer.cpp; sourceTree = ""; }; + 84F0DA6F2F45FAE100906960 /* Container.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Container.hpp; sourceTree = ""; }; + 84F0DA702F45FAE100906960 /* ContainerContentChangeListener.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ContainerContentChangeListener.hpp; sourceTree = ""; }; + 84F0DA712F45FAE100906960 /* ContainerContentChangeListener.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ContainerContentChangeListener.cpp; sourceTree = ""; }; + 84F0DA722F45FAE100906960 /* ContainerListener.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ContainerListener.hpp; sourceTree = ""; }; + 84F0DA732F45FAE100906960 /* ContainerListener.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ContainerListener.cpp; sourceTree = ""; }; + 84F0DA742F45FAE100906960 /* ContainerSizeChangeListener.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ContainerSizeChangeListener.hpp; sourceTree = ""; }; + 84F0DA752F45FAE100906960 /* ContainerSizeChangeListener.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ContainerSizeChangeListener.cpp; sourceTree = ""; }; 84F487F02EEA4F5C00309B1E /* FixedPipelineStateNull.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FixedPipelineStateNull.cpp; sourceTree = ""; }; 84F487F12EEA4F5C00309B1E /* FixedPipelineStateNull.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = FixedPipelineStateNull.hpp; sourceTree = ""; }; 84F487F22EEA4F5C00309B1E /* FogStateNull.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FogStateNull.cpp; sourceTree = ""; }; @@ -3806,11 +3814,6 @@ 840DD6562AC810620006A435 /* world */ = { isa = PBXGroup; children = ( - 84E7BF2A2F286A20002D3936 /* CompoundContainer.cpp */, - 84E7BF2B2F286A20002D3936 /* CompoundContainer.hpp */, - 84E7BF2C2F286A20002D3936 /* Container.hpp */, - 84E7BF2D2F286A20002D3936 /* ContainerListener.cpp */, - 84E7BF2E2F286A20002D3936 /* ContainerListener.hpp */, 840DD6572AC810620006A435 /* entity */, 84358B1A2F4551290077EA44 /* Facing.cpp */, 8477B3A82C4DC3B3004E1AC5 /* Facing.hpp */, @@ -5083,6 +5086,15 @@ 84E7BF092F286A08002D3936 /* inventory */ = { isa = PBXGroup; children = ( + 84F0DA6D2F45FAE100906960 /* CompoundContainer.hpp */, + 84F0DA6E2F45FAE100906960 /* CompoundContainer.cpp */, + 84F0DA6F2F45FAE100906960 /* Container.hpp */, + 84F0DA702F45FAE100906960 /* ContainerContentChangeListener.hpp */, + 84F0DA712F45FAE100906960 /* ContainerContentChangeListener.cpp */, + 84F0DA722F45FAE100906960 /* ContainerListener.hpp */, + 84F0DA732F45FAE100906960 /* ContainerListener.cpp */, + 84F0DA742F45FAE100906960 /* ContainerSizeChangeListener.hpp */, + 84F0DA752F45FAE100906960 /* ContainerSizeChangeListener.cpp */, 84E7BF0A2F286A08002D3936 /* ArmorSlot.cpp */, 84E7BF0B2F286A08002D3936 /* ArmorSlot.hpp */, 84E022FE2F2A0245003C8FFE /* ChestMenu.cpp */, @@ -5955,7 +5967,6 @@ 84BF63862AF186C8008A9995 /* Inventory.hpp in Headers */, 8477B3B52C4DC414004E1AC5 /* ChunkTilePos.hpp in Headers */, 84BF63872AF186C8008A9995 /* Item.hpp in Headers */, - 84E7BF332F286A20002D3936 /* ContainerListener.hpp in Headers */, 84BF63892AF186C8008A9995 /* TileItem.hpp in Headers */, 84BF638A2AF186C8008A9995 /* TilePlanterItem.hpp in Headers */, 84E7BF292F286A08002D3936 /* Slot.hpp in Headers */, @@ -5969,6 +5980,11 @@ 84BF638F2AF186C8008A9995 /* BiomeSource.hpp in Headers */, 84BF63902AF186C8008A9995 /* ChunkCache.hpp in Headers */, 84BF63912AF186C8008A9995 /* ChunkSource.hpp in Headers */, + 84F0DA762F45FAE100906960 /* ContainerContentChangeListener.hpp in Headers */, + 84F0DA772F45FAE100906960 /* ContainerListener.hpp in Headers */, + 84F0DA782F45FAE100906960 /* Container.hpp in Headers */, + 84F0DA792F45FAE100906960 /* CompoundContainer.hpp in Headers */, + 84F0DA7A2F45FAE100906960 /* ContainerSizeChangeListener.hpp in Headers */, 84BF63922AF186C8008A9995 /* LevelChunk.hpp in Headers */, 84BF63932AF186C8008A9995 /* PerformanceTestChunkSource.hpp in Headers */, 84E023012F2A0245003C8FFE /* ChestMenu.hpp in Headers */, @@ -6074,7 +6090,6 @@ 84BF63D22AF186C9008A9995 /* TorchTile.hpp in Headers */, 84BF63D32AF186C9008A9995 /* TransparentTile.hpp in Headers */, 84BF63D42AF186C9008A9995 /* TreeTile.hpp in Headers */, - 84E7BF312F286A20002D3936 /* Container.hpp in Headers */, 84BF63D52AF186C9008A9995 /* WireTile.hpp in Headers */, 84AA8B512B32F39A003F5B82 /* BinaryHeap.hpp in Headers */, 84AA8B532B32F39A003F5B82 /* Node.hpp in Headers */, @@ -6115,7 +6130,6 @@ 84358B062F4550B30077EA44 /* MusicTileEntity.hpp in Headers */, 84358B072F4550B30077EA44 /* TileEntityType.hpp in Headers */, 8470AF3D2BE9B6FA00BCA54E /* FireworkParticle.hpp in Headers */, - 84E7BF302F286A20002D3936 /* CompoundContainer.hpp in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -7067,7 +7081,6 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 84E7BF322F286A20002D3936 /* ContainerListener.cpp in Sources */, 84BF630C2AF18631008A9995 /* Entity.cpp in Sources */, 84BF630D2AF18631008A9995 /* FallingTile.cpp in Sources */, 84BF630E2AF18631008A9995 /* ItemEntity.cpp in Sources */, @@ -7146,7 +7159,6 @@ 84358B122F4550CC0077EA44 /* ChestTile.cpp in Sources */, 84358B132F4550CC0077EA44 /* MusicTile.cpp in Sources */, 84BF63382AF18631008A9995 /* LevelListener.cpp in Sources */, - 84E7BF2F2F286A20002D3936 /* CompoundContainer.cpp in Sources */, 84BF63392AF18631008A9995 /* Material.cpp in Sources */, 84BF633A2AF18631008A9995 /* Region.cpp in Sources */, 84BF633B2AF18631008A9995 /* ChunkStorage.cpp in Sources */, @@ -7260,6 +7272,10 @@ 84E78C7B2B58B3E000D515EF /* Rocket.cpp in Sources */, 84E78C832B58B5FB00D515EF /* RocketItem.cpp in Sources */, 845BBCB62F2EA81700C7232C /* ToolItem.cpp in Sources */, + 84F0DA7B2F45FAE100906960 /* ContainerContentChangeListener.cpp in Sources */, + 84F0DA7C2F45FAE100906960 /* CompoundContainer.cpp in Sources */, + 84F0DA7D2F45FAE100906960 /* ContainerListener.cpp in Sources */, + 84F0DA7E2F45FAE100906960 /* ContainerSizeChangeListener.cpp in Sources */, 84E78C872B58B66B00D515EF /* RocketLauncherTile.cpp in Sources */, 8470AF2C2BE9B60A00BCA54E /* MobFactory.cpp in Sources */, 84E1C9E92E7FDC72007D2F5D /* SlabItem.cpp in Sources */, diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index 853b0897c..057f3ac44 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -360,7 +360,10 @@ add_library(reminecraftpe-core STATIC world/item/crafting/SingleInputRecipe.cpp world/item/crafting/ShapedRecipe.cpp world/item/crafting/ShapelessRecipe.cpp - world/ContainerListener.cpp + world/inventory/ContainerListener.cpp + world/inventory/CompoundContainer.cpp + world/inventory/ContainerContentChangeListener.cpp + world/inventory/ContainerSizeChangeListener.cpp world/inventory/ContainerMenu.cpp world/inventory/InventoryMenu.cpp world/inventory/CraftingMenu.cpp @@ -373,7 +376,6 @@ add_library(reminecraftpe-core STATIC world/inventory/CraftingContainer.cpp world/inventory/ResultContainer.cpp world/inventory/SimpleContainer.cpp - world/CompoundContainer.cpp world/item/DoorItem.cpp world/item/ItemStack.cpp world/item/RocketItem.cpp From 4b8fe5c28e88bf4dcf92fab06d358b3b0c2760b4 Mon Sep 17 00:00:00 2001 From: Vimdo Date: Wed, 18 Feb 2026 03:36:51 -0500 Subject: [PATCH 22/63] Chat & Pause buttons for touch screens (#485) --- ...b725a90e35b4da109644cc57d0c013d7.patch.txt | 185 + ...fa9bb9e1ec79591b9d92700b524777e0.patch.txt | 1554 +++ ...65261fe97c2e41196d3b636e0171a1e9.patch.txt | 190 + ...bc5bfe4a76c2c89dfa94d7ac1e7a26df.patch.txt | 501 + ...3bc6fa100cd978fda4b14d6cc0cec4e0.patch.txt | 6842 ++++++++++ 478.patch.txt | 10726 ++++++++++++++++ source/client/gui/Gui.cpp | 13 + 7 files changed, 20011 insertions(+) create mode 100644 01_02783ae6b725a90e35b4da109644cc57d0c013d7.patch.txt create mode 100644 02_4d5d1bbffa9bb9e1ec79591b9d92700b524777e0.patch.txt create mode 100644 03_14bb937365261fe97c2e41196d3b636e0171a1e9.patch.txt create mode 100644 04_d80620fdbc5bfe4a76c2c89dfa94d7ac1e7a26df.patch.txt create mode 100644 05_a55606e83bc6fa100cd978fda4b14d6cc0cec4e0.patch.txt create mode 100644 478.patch.txt diff --git a/01_02783ae6b725a90e35b4da109644cc57d0c013d7.patch.txt b/01_02783ae6b725a90e35b4da109644cc57d0c013d7.patch.txt new file mode 100644 index 000000000..279d343ce --- /dev/null +++ b/01_02783ae6b725a90e35b4da109644cc57d0c013d7.patch.txt @@ -0,0 +1,185 @@ +From 02783ae6b725a90e35b4da109644cc57d0c013d7 Mon Sep 17 00:00:00 2001 +From: Sam +Date: Wed, 18 Feb 2026 08:49:39 -0500 +Subject: [PATCH] CMakeLists & Xcode + +--- + .../Minecraft.xcodeproj/project.pbxproj | 56 ++++++++++++------- + source/CMakeLists.txt | 6 +- + 2 files changed, 40 insertions(+), 22 deletions(-) + +diff --git a/platforms/macos/projects/Minecraft/Minecraft.xcodeproj/project.pbxproj b/platforms/macos/projects/Minecraft/Minecraft.xcodeproj/project.pbxproj +index dcc3c8727..7600da5f0 100644 +--- a/platforms/macos/projects/Minecraft/Minecraft.xcodeproj/project.pbxproj ++++ b/platforms/macos/projects/Minecraft/Minecraft.xcodeproj/project.pbxproj +@@ -989,11 +989,6 @@ + 84E7BF272F286A08002D3936 /* SimpleContainer.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84E7BF172F286A08002D3936 /* SimpleContainer.hpp */; }; + 84E7BF282F286A08002D3936 /* Slot.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84E7BF182F286A08002D3936 /* Slot.cpp */; }; + 84E7BF292F286A08002D3936 /* Slot.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84E7BF192F286A08002D3936 /* Slot.hpp */; }; +- 84E7BF2F2F286A20002D3936 /* CompoundContainer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84E7BF2A2F286A20002D3936 /* CompoundContainer.cpp */; }; +- 84E7BF302F286A20002D3936 /* CompoundContainer.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84E7BF2B2F286A20002D3936 /* CompoundContainer.hpp */; }; +- 84E7BF312F286A20002D3936 /* Container.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84E7BF2C2F286A20002D3936 /* Container.hpp */; }; +- 84E7BF322F286A20002D3936 /* ContainerListener.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84E7BF2D2F286A20002D3936 /* ContainerListener.cpp */; }; +- 84E7BF332F286A20002D3936 /* ContainerListener.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84E7BF2E2F286A20002D3936 /* ContainerListener.hpp */; }; + 84E7BF362F286A6A002D3936 /* ItemStack.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84E7BF342F286A6A002D3936 /* ItemStack.cpp */; }; + 84E7BF372F286A6A002D3936 /* ItemStack.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84E7BF352F286A6A002D3936 /* ItemStack.hpp */; }; + 84E8DCE52E84ACBC00B30789 /* libRenderer.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 8406FD292AF1814500B09C1D /* libRenderer.a */; }; +@@ -1287,6 +1282,15 @@ + 84EEED642F0A01B700F741B6 /* ViewportOrigin.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84EEED622F0A01B700F741B6 /* ViewportOrigin.hpp */; }; + 84EEED682F0A02F300F741B6 /* ErrorHandlerOGL.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84EEED662F0A02F300F741B6 /* ErrorHandlerOGL.cpp */; }; + 84EEED692F0A02F300F741B6 /* ErrorHandlerOGL.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84EEED672F0A02F300F741B6 /* ErrorHandlerOGL.hpp */; }; ++ 84F0DA762F45FAE100906960 /* ContainerContentChangeListener.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84F0DA702F45FAE100906960 /* ContainerContentChangeListener.hpp */; }; ++ 84F0DA772F45FAE100906960 /* ContainerListener.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84F0DA722F45FAE100906960 /* ContainerListener.hpp */; }; ++ 84F0DA782F45FAE100906960 /* Container.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84F0DA6F2F45FAE100906960 /* Container.hpp */; }; ++ 84F0DA792F45FAE100906960 /* CompoundContainer.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84F0DA6D2F45FAE100906960 /* CompoundContainer.hpp */; }; ++ 84F0DA7A2F45FAE100906960 /* ContainerSizeChangeListener.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84F0DA742F45FAE100906960 /* ContainerSizeChangeListener.hpp */; }; ++ 84F0DA7B2F45FAE100906960 /* ContainerContentChangeListener.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84F0DA712F45FAE100906960 /* ContainerContentChangeListener.cpp */; }; ++ 84F0DA7C2F45FAE100906960 /* CompoundContainer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84F0DA6E2F45FAE100906960 /* CompoundContainer.cpp */; }; ++ 84F0DA7D2F45FAE100906960 /* ContainerListener.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84F0DA732F45FAE100906960 /* ContainerListener.cpp */; }; ++ 84F0DA7E2F45FAE100906960 /* ContainerSizeChangeListener.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84F0DA752F45FAE100906960 /* ContainerSizeChangeListener.cpp */; }; + 84F4880E2EEA4F5C00309B1E /* FixedPipelineStateNull.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84F487F02EEA4F5C00309B1E /* FixedPipelineStateNull.cpp */; }; + 84F4880F2EEA4F5C00309B1E /* FixedPipelineStateNull.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84F487F12EEA4F5C00309B1E /* FixedPipelineStateNull.hpp */; }; + 84F488102EEA4F5C00309B1E /* FogStateNull.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84F487F22EEA4F5C00309B1E /* FogStateNull.cpp */; }; +@@ -2766,11 +2770,6 @@ + 84E7BF172F286A08002D3936 /* SimpleContainer.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = SimpleContainer.hpp; sourceTree = ""; }; + 84E7BF182F286A08002D3936 /* Slot.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Slot.cpp; sourceTree = ""; }; + 84E7BF192F286A08002D3936 /* Slot.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Slot.hpp; sourceTree = ""; }; +- 84E7BF2A2F286A20002D3936 /* CompoundContainer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CompoundContainer.cpp; sourceTree = ""; }; +- 84E7BF2B2F286A20002D3936 /* CompoundContainer.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = CompoundContainer.hpp; sourceTree = ""; }; +- 84E7BF2C2F286A20002D3936 /* Container.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Container.hpp; sourceTree = ""; }; +- 84E7BF2D2F286A20002D3936 /* ContainerListener.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ContainerListener.cpp; sourceTree = ""; }; +- 84E7BF2E2F286A20002D3936 /* ContainerListener.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ContainerListener.hpp; sourceTree = ""; }; + 84E7BF342F286A6A002D3936 /* ItemStack.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ItemStack.cpp; sourceTree = ""; }; + 84E7BF352F286A6A002D3936 /* ItemStack.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ItemStack.hpp; sourceTree = ""; }; + 84EAE8DD2AF1EAA1000894E8 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; +@@ -3069,6 +3068,15 @@ + 84EEED622F0A01B700F741B6 /* ViewportOrigin.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ViewportOrigin.hpp; sourceTree = ""; }; + 84EEED662F0A02F300F741B6 /* ErrorHandlerOGL.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ErrorHandlerOGL.cpp; sourceTree = ""; }; + 84EEED672F0A02F300F741B6 /* ErrorHandlerOGL.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ErrorHandlerOGL.hpp; sourceTree = ""; }; ++ 84F0DA6D2F45FAE100906960 /* CompoundContainer.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = CompoundContainer.hpp; sourceTree = ""; }; ++ 84F0DA6E2F45FAE100906960 /* CompoundContainer.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = CompoundContainer.cpp; sourceTree = ""; }; ++ 84F0DA6F2F45FAE100906960 /* Container.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Container.hpp; sourceTree = ""; }; ++ 84F0DA702F45FAE100906960 /* ContainerContentChangeListener.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ContainerContentChangeListener.hpp; sourceTree = ""; }; ++ 84F0DA712F45FAE100906960 /* ContainerContentChangeListener.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ContainerContentChangeListener.cpp; sourceTree = ""; }; ++ 84F0DA722F45FAE100906960 /* ContainerListener.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ContainerListener.hpp; sourceTree = ""; }; ++ 84F0DA732F45FAE100906960 /* ContainerListener.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ContainerListener.cpp; sourceTree = ""; }; ++ 84F0DA742F45FAE100906960 /* ContainerSizeChangeListener.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ContainerSizeChangeListener.hpp; sourceTree = ""; }; ++ 84F0DA752F45FAE100906960 /* ContainerSizeChangeListener.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ContainerSizeChangeListener.cpp; sourceTree = ""; }; + 84F487F02EEA4F5C00309B1E /* FixedPipelineStateNull.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FixedPipelineStateNull.cpp; sourceTree = ""; }; + 84F487F12EEA4F5C00309B1E /* FixedPipelineStateNull.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = FixedPipelineStateNull.hpp; sourceTree = ""; }; + 84F487F22EEA4F5C00309B1E /* FogStateNull.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FogStateNull.cpp; sourceTree = ""; }; +@@ -3806,11 +3814,6 @@ + 840DD6562AC810620006A435 /* world */ = { + isa = PBXGroup; + children = ( +- 84E7BF2A2F286A20002D3936 /* CompoundContainer.cpp */, +- 84E7BF2B2F286A20002D3936 /* CompoundContainer.hpp */, +- 84E7BF2C2F286A20002D3936 /* Container.hpp */, +- 84E7BF2D2F286A20002D3936 /* ContainerListener.cpp */, +- 84E7BF2E2F286A20002D3936 /* ContainerListener.hpp */, + 840DD6572AC810620006A435 /* entity */, + 84358B1A2F4551290077EA44 /* Facing.cpp */, + 8477B3A82C4DC3B3004E1AC5 /* Facing.hpp */, +@@ -5083,6 +5086,15 @@ + 84E7BF092F286A08002D3936 /* inventory */ = { + isa = PBXGroup; + children = ( ++ 84F0DA6D2F45FAE100906960 /* CompoundContainer.hpp */, ++ 84F0DA6E2F45FAE100906960 /* CompoundContainer.cpp */, ++ 84F0DA6F2F45FAE100906960 /* Container.hpp */, ++ 84F0DA702F45FAE100906960 /* ContainerContentChangeListener.hpp */, ++ 84F0DA712F45FAE100906960 /* ContainerContentChangeListener.cpp */, ++ 84F0DA722F45FAE100906960 /* ContainerListener.hpp */, ++ 84F0DA732F45FAE100906960 /* ContainerListener.cpp */, ++ 84F0DA742F45FAE100906960 /* ContainerSizeChangeListener.hpp */, ++ 84F0DA752F45FAE100906960 /* ContainerSizeChangeListener.cpp */, + 84E7BF0A2F286A08002D3936 /* ArmorSlot.cpp */, + 84E7BF0B2F286A08002D3936 /* ArmorSlot.hpp */, + 84E022FE2F2A0245003C8FFE /* ChestMenu.cpp */, +@@ -5955,7 +5967,6 @@ + 84BF63862AF186C8008A9995 /* Inventory.hpp in Headers */, + 8477B3B52C4DC414004E1AC5 /* ChunkTilePos.hpp in Headers */, + 84BF63872AF186C8008A9995 /* Item.hpp in Headers */, +- 84E7BF332F286A20002D3936 /* ContainerListener.hpp in Headers */, + 84BF63892AF186C8008A9995 /* TileItem.hpp in Headers */, + 84BF638A2AF186C8008A9995 /* TilePlanterItem.hpp in Headers */, + 84E7BF292F286A08002D3936 /* Slot.hpp in Headers */, +@@ -5969,6 +5980,11 @@ + 84BF638F2AF186C8008A9995 /* BiomeSource.hpp in Headers */, + 84BF63902AF186C8008A9995 /* ChunkCache.hpp in Headers */, + 84BF63912AF186C8008A9995 /* ChunkSource.hpp in Headers */, ++ 84F0DA762F45FAE100906960 /* ContainerContentChangeListener.hpp in Headers */, ++ 84F0DA772F45FAE100906960 /* ContainerListener.hpp in Headers */, ++ 84F0DA782F45FAE100906960 /* Container.hpp in Headers */, ++ 84F0DA792F45FAE100906960 /* CompoundContainer.hpp in Headers */, ++ 84F0DA7A2F45FAE100906960 /* ContainerSizeChangeListener.hpp in Headers */, + 84BF63922AF186C8008A9995 /* LevelChunk.hpp in Headers */, + 84BF63932AF186C8008A9995 /* PerformanceTestChunkSource.hpp in Headers */, + 84E023012F2A0245003C8FFE /* ChestMenu.hpp in Headers */, +@@ -6074,7 +6090,6 @@ + 84BF63D22AF186C9008A9995 /* TorchTile.hpp in Headers */, + 84BF63D32AF186C9008A9995 /* TransparentTile.hpp in Headers */, + 84BF63D42AF186C9008A9995 /* TreeTile.hpp in Headers */, +- 84E7BF312F286A20002D3936 /* Container.hpp in Headers */, + 84BF63D52AF186C9008A9995 /* WireTile.hpp in Headers */, + 84AA8B512B32F39A003F5B82 /* BinaryHeap.hpp in Headers */, + 84AA8B532B32F39A003F5B82 /* Node.hpp in Headers */, +@@ -6115,7 +6130,6 @@ + 84358B062F4550B30077EA44 /* MusicTileEntity.hpp in Headers */, + 84358B072F4550B30077EA44 /* TileEntityType.hpp in Headers */, + 8470AF3D2BE9B6FA00BCA54E /* FireworkParticle.hpp in Headers */, +- 84E7BF302F286A20002D3936 /* CompoundContainer.hpp in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +@@ -7067,7 +7081,6 @@ + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( +- 84E7BF322F286A20002D3936 /* ContainerListener.cpp in Sources */, + 84BF630C2AF18631008A9995 /* Entity.cpp in Sources */, + 84BF630D2AF18631008A9995 /* FallingTile.cpp in Sources */, + 84BF630E2AF18631008A9995 /* ItemEntity.cpp in Sources */, +@@ -7146,7 +7159,6 @@ + 84358B122F4550CC0077EA44 /* ChestTile.cpp in Sources */, + 84358B132F4550CC0077EA44 /* MusicTile.cpp in Sources */, + 84BF63382AF18631008A9995 /* LevelListener.cpp in Sources */, +- 84E7BF2F2F286A20002D3936 /* CompoundContainer.cpp in Sources */, + 84BF63392AF18631008A9995 /* Material.cpp in Sources */, + 84BF633A2AF18631008A9995 /* Region.cpp in Sources */, + 84BF633B2AF18631008A9995 /* ChunkStorage.cpp in Sources */, +@@ -7260,6 +7272,10 @@ + 84E78C7B2B58B3E000D515EF /* Rocket.cpp in Sources */, + 84E78C832B58B5FB00D515EF /* RocketItem.cpp in Sources */, + 845BBCB62F2EA81700C7232C /* ToolItem.cpp in Sources */, ++ 84F0DA7B2F45FAE100906960 /* ContainerContentChangeListener.cpp in Sources */, ++ 84F0DA7C2F45FAE100906960 /* CompoundContainer.cpp in Sources */, ++ 84F0DA7D2F45FAE100906960 /* ContainerListener.cpp in Sources */, ++ 84F0DA7E2F45FAE100906960 /* ContainerSizeChangeListener.cpp in Sources */, + 84E78C872B58B66B00D515EF /* RocketLauncherTile.cpp in Sources */, + 8470AF2C2BE9B60A00BCA54E /* MobFactory.cpp in Sources */, + 84E1C9E92E7FDC72007D2F5D /* SlabItem.cpp in Sources */, +diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt +index 853b0897c..057f3ac44 100644 +--- a/source/CMakeLists.txt ++++ b/source/CMakeLists.txt +@@ -360,7 +360,10 @@ add_library(reminecraftpe-core STATIC + world/item/crafting/SingleInputRecipe.cpp + world/item/crafting/ShapedRecipe.cpp + world/item/crafting/ShapelessRecipe.cpp +- world/ContainerListener.cpp ++ world/inventory/ContainerListener.cpp ++ world/inventory/CompoundContainer.cpp ++ world/inventory/ContainerContentChangeListener.cpp ++ world/inventory/ContainerSizeChangeListener.cpp + world/inventory/ContainerMenu.cpp + world/inventory/InventoryMenu.cpp + world/inventory/CraftingMenu.cpp +@@ -373,7 +376,6 @@ add_library(reminecraftpe-core STATIC + world/inventory/CraftingContainer.cpp + world/inventory/ResultContainer.cpp + world/inventory/SimpleContainer.cpp +- world/CompoundContainer.cpp + world/item/DoorItem.cpp + world/item/ItemStack.cpp + world/item/RocketItem.cpp diff --git a/02_4d5d1bbffa9bb9e1ec79591b9d92700b524777e0.patch.txt b/02_4d5d1bbffa9bb9e1ec79591b9d92700b524777e0.patch.txt new file mode 100644 index 000000000..a16d982be --- /dev/null +++ b/02_4d5d1bbffa9bb9e1ec79591b9d92700b524777e0.patch.txt @@ -0,0 +1,1554 @@ +From 0b8e1b54f12d104d4b0255986a08a36a8da74293 Mon Sep 17 00:00:00 2001 +From: Vimdo +Date: Wed, 18 Feb 2026 03:36:51 -0500 +Subject: [PATCH] Chat & Pause buttons for touch screens (#485) + +--- + game/assets/gui/touchgui.png | Bin 33357 -> 37494 bytes + source/client/gui/Gui.cpp | 43 ++++++++++++++++-- + .../screens/IngameBlockSelectionScreen.cpp | 38 ++++------------ + .../screens/IngameBlockSelectionScreen.hpp | 2 - + 4 files changed, 48 insertions(+), 35 deletions(-) + +diff --git a/game/assets/gui/touchgui.png b/game/assets/gui/touchgui.png +index 9b97bf25ce9e971331a7903e501324e790c002af..46cd63191bd60e3b49e663aa25c6f2528dc0ad5a 100644 +GIT binary patch +literal 37494 +zcmV*4Ky|-~P)3vmGHUM;pLnrF*(7^kaHHvK~$njl$AVythucD|DAI~-Kwt6)o=PS@66Zpx~p#8x^?TGb5B0(h=UeeFb&gS1H*66 +zzq-DxDLHb*68Uv{T{*!qucM=*UTyR_WBEF3YisjmP(kXfqX!l*Spr?%UA}ZWRiLG% +z1-g5>;j8!FO(Q=1$YWvd{Q1z{-o9VWLsL^Dw6}M_r#|s9U;Eux>#A1Xt9xGm`q~Cy +zjN`RsS#BcWCYV+}BB1wE2@nB@#-gxk(6C(wm~=) +z+OOuJt}YCbNW}Mk_tmgTV|n`glAJ!C(Y7zwyR?H4jEG0G6-Iio?EISGDq9z2+5>-_ +z@Q(iEF_SLh@6*bF#j&3+MgWbNfOAY1zs3@H%rCR3J+t)nkpWBH(o}6MZS2*8V%F8) +zo_|(-KkUe3@{Xj~w(j?ebD+F+6F`SHNY`eK0fCN>|a*az2wtX +zs!Z~NV%F7QBn92gTu1QYU!Q}+k2=n?-3x#DlTv&QOeFL6>3*+f#u6WW=IytKe_!9c +z1D4O9MQ5J`1!AY1HCQv^xLcL-=6<75fX&G<8itsA7s5(OAvJ{ +z`|ZiUS6p@peCvBpz_NKW`O%DP|IPqBry>8^ExYKNe1wxrR?hQoA|R1jM|=s|v8?vK +ziU5YeIHe94g|B!M0Shp-us|)8>gMUV%DUP!7KfSbjr9G+=bwcmjy|5g|Mj^)DrDX@5-qB) +zO5W?bt-E3IoL>6=>>qyzM;~_*eMf>IVZ-*laO5$C`_kw2SATpyFY8|b+@?SxCdf?` +z+d(dH=>YaG4uEzjZFk9-ruytpkU_C0hi0O(`7M53z3uEX;eVg{1cVr+h9CdzDVW{W;yAuzaGcH+3zfdt%ieh*eDDAL7#7ZS +zdN6L7~H~|P4#4_z~Iso_LG)+xgY(Oq`y#um%vm)9shvwed +zQdt}f@hg{duE_DJw%~FP&_?FgR{I6ztgClia4tOm=Raf}D+ThtSBF){HAmWfUUK0D +z@cgsRao{m!NpM(WoYoorzH(mQdi;^hEd~@}>XXP{iTIEghyVmgRtxNZIsh~mm->C$G1xriJ%Ny+scC +z?A^Yc?N>X-9Y*YDHcb;ui+{*-j=KVC4l|akJJ}JCgOJ56+bK^-XG}d9(-uRvdpF}Z +zbk2ixFN5sKb&Lq_Ycb7usJ#=djXLcZKEns6-O6vsT2~I!d-jXL9XFS_pueYrE7Ie= +z;!ccznf@-+I3)01y^vq;3-mc>R@Pv6w@mnxI;P^wqV;7GFjkQZk`r)OYAG|wV?m@v_ady-GtL} +zcV>CsS>43TL&5%*y-4komT+yVmJYzRu9*JU`C{~Uf$}zjl*7>)pmPEFUc1+OHH9Al +ze`K8)WiR5iP&*=NB2KJ_%r +z5WWh}5fM1SDPpoJ%=Z`CTm6U+)%{SrscF--%fPGa$1?1a!hd_gYs2 +z<)*uI?hHC2+dH)apw=3_SLS@Lx<)=51j35l8l95PKl600J%9h_oq0O^UI$}ms;yv9Xv8BF$R +z6BowpzL@BbNjZ@TlacmUcOYN??`JS|PTnhftjw`fSV9>lS-1V=lS5fm +z`QnoASiBp)ZVbnoLWP@D6kd7xrCDo1_Hotam#KF4HHQ6unY|v$d9TQYNUi<3r=OOy +z9iEfz@S1#m=9*XCgRcmL&T3ns^N<4^Ism3*HRrDH8b=9Ax?HotNL91AT<`?rjW+aFzltDyvBhE06E;MqUetpA!VJK@+9?RH-8d+pX;uzVhm*h3<4 +z%n2tu+NgC<;(OJ#{sWOz!S}y+JOSNJJiCxr%z(OY{&{6WmM;|aldG*Sz;ys53Ga{j +z09dvaV809os*WE(p>?%p%P#f(+V{MxV1L-=k#9e)ejj&Y9>QM2dmV@*)%UYcdrQH& +zIQz6y;i>=owWEJe$TP13!5L&n_&Ky&?&v?iMF8OkV5raj1hi1J9mceHG`+c>5dgDF +zM6b}gdhZqQF1gQQt||4sp81wjwYJ60LowI5=ke~3^EOF +z@QVeVURO?_c`di#O0^(ZQ=^%NS^&Qhw-~P}w}03)OcD?QXlm(zg^LzJZ(nc4cYWb6 +z&%*o#3!$^S=S}er`}+D|?!3A1ng93%{XXQdBVpeB1<=viQE}f-KlxKwaquB9YxW!; +zM!N5ztE&t8X3T&O-g!GLUAzcdI{V0d_snxI$UC-Q{Oo@z1JG;hH$rT9&zoW{rV0=N +z7#|;l&6_vTko_P6U7elK(AWUcXl%+&2t|xBuE9p+pEY|H^!N9x^CQDUD&3SHM4+>? +z3mO_5Ar_5R+}C7-bdyCpKO%p3PY;X?535m(5A7ku-mzn6-jN}Jc;-*f(f3+ok>Bh< +z1R#-!!`LYJLIhIDBs4ZQRmHWm63`JlA8g*R9#ZKP^!4_^VTT_E!^6Wc&_AHANW`Kr +zJZS%}gb3igV;#F%la_VF&Id33?FFj*(>G%#EL*-D#>d8dk5-$8>Vnb%_(cScIpHMe +z?Cyd+d;5!MJ$ZnO`HhVmV8P-gd2QZ{mM((>4mbdwe(GlskHsCxuja}pB5?lOFM{3~ +zGogQPZLz(WP~+tn{{{yic6i>oI_!v}Xy^ao_rD9VXv`D&Uw`dYSg>eu-uroR2)n+{ltf_b)~ywB0o%6k@MCPg11{x`_BI$A&c90qLqVvktApJ;cM#$53h9(Y +zAQ%inXIE$0L*2P!JN>P>nhXq}y`#gy(tAODeLZn}_#E{O4OIy%o`&jy$_OMOnM^=5 +zG665W_|lth+9Ttm@Z7V{L8O3gqM@Myg252%+O-qr&R+;c#6w2mrKo^uXdJOQ5T} +ztBBUM3#jku?&*fF-g|e=jL}yDFfld)&8_|hEBFQDqeCUtK5TAoArT-{g_`onMQq>t +z1{^SNf&UNc18m;7zNF`FYwv)~8#j_TR1Jv6qOfVxCJ5JsYjVDdh5Cki7#|z2sWD}V +z0N{~QsSw1YQ5YW?g1W{=Xl`zTNDY~^&?*s&PLO+OZ(mnY&xZ5S*wh5kXcX$|!oJGq +zg9Cd>8QIm{L;8r}k?Q1oF9#+j#-V@DZV~}ktXx%8*f)rsEiy4t6=DhE9VX&&Shsdv +zRr;SS!1>9U9cdpy#Uwj2J_e~|0(xi7Ci{Opku2gWC!pd8gEdmAl;?T0yQ5rQa=)1j +zj1CPzb88!!s!*|4tZzbHeLd+z0s-d~WhL({A(VFK_H8gRF$RYoaTLs-JC6uusZn_7 +z(>@W25 +zsi!(rOaL01nxLzzn~-Z{Y#h?*bjf|-4*QL7JxmANCFmLMBsO@qs`f0O!8#!m3r4db3oo +zuEp_l5g;pj6e|5nGB?(5!!)DNh*R13>VGU9{rX|8%1d(upfibZi3Zp9(()= +zxcXg}LLwF;jdRh!c%~yWoq@E;6SNgGKJG9C(Aw4tJ9g~Ik?LIJ({m+#v>n<_UM>b7 +zKqMYT77&p)o){8@$UbL-smjh+lum;?=I}8Xl-}x{7hg==4b?Buvaw>HcinZj%3!#6 +zU;ui%I>BOwc*DGJeT+PvCXd-i0uYNuz+?sy68)K&92Y(>GweK^0@^z|Vb7l3#lM?r +zs0nbaK1TUXOHU4AmRVQ=cjQle=5s_qK$+N>`dEap1_VO^cAM@gdOw#9!?^a!8p>TKsmNa~=cHkEwFnVP@O2&OeW*+71S2 +z=cOt*BApu&(cAA>O=1Fa2q-u69QDq=^)Z4rhItDXL32wRW%G4ElNy(t1CX`$OjQF9 +zW%X*v04fLNRR6HjbZe6VgcXXt5(zppsU&d$dPbLOe1=EIVAG~8!3_km5>0GKV5I=h%=-t1J29nNr*%vkV@HvjTa>` +zAsh-rkHg``;w-+&AdyT#BoZOyMn#_vr0}#X$UsIaC#XM+$>{5~L$eXVah}k*skr;^ +zdypySr+UsxdX6le&%TA6TUNNf5fbqjn5iTL!gU4NTzo-a`^KYUw=%&9@S>B~UU>yX +zA`?_0HWUtXL`|n55DdaMzx!RtAkt$~LUfBN$#8|1#S1VB-9Ugbc<_;L!u9XHnq+TF +zRG-B@Nk+CTb$?z9j_Wh7z2ZF(v!bOb5A#4)p%)9Ft+N}(#>OBzJ`PQ-ZFzMSIJR&o +z1aTt(V=-Hkk3<2-+|twpyLay;qDb!{9D>G%24d}J&gg?wI$Z=;W)G%2Gn5y4KPS$67cY&-y((44cA^nhHGnfHu?HTe4t)UQKXqj}92JXcL;IX?Oc#z5= +zgDI+^1_HFRbI&kF3-@b}KBnyQrUjyU3za3}<(f^4N@Sy)XNi{{6a)d=1keyAj0A>A +z`~1~MzCmNu?Zkut$bH~Fa2HLb;o)yS2G?D6B_-XcoD<;X!l@LvC4ch(A|ycZSd>^j +zL~=_bBPLh3)Aw`V#xvwlNjlTku-|&?&Gh%H4?INT)H^S^gf#zXYQ<+mTYVtl9=}!> +zPbdea84>%#v0z^hJ@^piIe6zK7ir2)d4FD=_>S9eg}c6dFIW@&_rTx)ba!^C3fig! +zxue7zH##!Qs+xdXO+SFgzV}4du`9F6llcRsY$}rl+zR0SXcHc)tAmO0aUrMM$7Q9} +z`yPCVu0eT{c5j0tt)auE1vt`XVRUlHB +z$`JYGJ?+w2+?fVHIA=HBbOXHOl6TU)p{T@7vwXgps->q_ov|F}@Pi6Te0IDC_Je&Q +z@}s4IBa)@sGinnc0*yofWBVr}6J$N<9~@M-veXw#mA~eb0OrP{_HjvIr<=4NPWX@UCsI)@#0 +zd}0DV^sgVM-Gwo}qX$W2pHT>yN@tX9pD%ho4FQ956H>m%Yt!TuB=!_>E-hG8Sz<{N +z1|Z_W<$U}bFgSOBM8~epCWuVNR5b|id+1>jM+(`KL2jb@rwC~8=z@;UE|Rq+$Q>4u +zH0hGyo25&5`jb+uHC+a{Ufe`_aUbUdyt2sVL&iYxv#o=htqdGR7m1Zs{8m$ +zuH@JgPb3b6Y1MG`omh!5hQMr3wzs&!D(Av^0Ned8uF=0IrR2D4? +zsZ5%7Hd%?-G6lv<3DuAs58fe3|; +zIMVS+BCqi7Rl7F!iz&)7RwgGMP-!{@vZL76P+!jp4%6>t|CRRzp%cJoLyiF-AruVI +zd$zz*1L8EpVEtibu+$7CUo+_`=4w+0;n%Jy7Ah0@^Q +zFc`)glokpJfV9~wKYk_`Bsg|#^p8g~6)FiadT`4dZ>WWDiZ}rGUlch|u3flbJ}L9i +z4&Kz%1U$r_k?9c$2zmS&Twj1}Ead=@KCcYThLwh_TJ}SU-&y!*Y3D?94AdDL8znbg +zG?6yBl#!AfP(2Ie#OJ~1!010Tr=mt5>{jw>$1(0Xm?5OXIYJM=26BfEWGM~#4lcYT +zQFIVI8O9%|J7~{qX^PLRd2s<|1x6z9```U8?~I;!^2s@oAB6qf5T)NI1sGK2UOeVv +zif{mhCID^O|Mu~Jh0lNC3*=OS=Bq}L6E2qkHKaafZ~P(<9Kg$t=aT;Rwp7>2#0k!;Ou2O@sY3>!tBBRqHOrI_`M3Fn=A9>tI!d*l&NPnljcncm~hYu0aokN@jO +ziFN+qhyR88VQeuWKRUYrfRgp)Ju{gUvBBtSjL&oY(MLfj7*d^k-3L5gR1uq9PGH0d +zS+Hl@Hkh+;5h*Rl$0L+J3&nwaPCYXGm*zPVg+Kq{_mIQ@pCW|rp4oF@czA?3h>rGl +zr8KmOfa8hzd6SE6*#Gyx`#or=Zy*=sT+glBA#%+LW(AH;jOHD1V{;pWP<`$Ol6#bT +z4aa>g8(9VRGfCAHp|`k?h-SJ3TZ+p9Wp!Z6$Z7LfmPQ;bOXvzD7bx1{rofmWM3xh; +zYRnoSL;z1TD0nfl@Spg%e}&I}?(@Xr#^P~k!K^2S(`P`k_PCI7zD_*x1UUYvBXg#Z +z^1Ac#C5!2Ugy56^{t5V(kNqp{j7yg+A$GT|t(Dqw?K&qw*uSg?aQv~y!HLHn3@83Q9Jf`^0n*Cghyh4yaS@?L{)FRBfRm3qwrpAGz@itqTB$rNM%q{LlZW +zG0DIAo&$9S0YTT15De_v4&5{609eC14agz_r0f&K6LayWY70{Qn(d>lUY>Cf<}KA~`9(y!m-2#D$@ME;YGKZcu} +zq=wMlr>$@X2WO`>S-zMTn)uA8KM5cH$VYivs_;Ri4- +zqdLqjY}o7`=?&y>%h|#;+`=q5DcJ+StTb@}D93knv_o^Ei3ork1>b3Hh2Bqo{9~u( +zMVaq%-;W{^1Dp`>ds@)HYa8^;oC7c{WmnF+A`u|Ow}dw!je#d=LBh_>rum;uIzi=w +z=U-j_O#T)Hrf;${0*{I2ki3t+`IdCn +z$-qe`ArcA&oa`)zMNV{ +z=?|-z&I;E8`cec89fm0^e`kNW}-gh4-;(!X;I)8Tm-G{cCRny~ +zDYUh=@Nwc8&wN`=iLPXYT+hWs$R^@@^K7%tW!H%cBGuJ$sd-0j2%C7$W$od+c15P;lD35z6 +zH`4Tw4F>rE +z7&VttTf(oO`2)Q5oYVaAPQ@Z2h71%I3HU`(;a5KsCh_dJ=iYzADwb52A1Ai!umvH&jwb{i5 +zag_NH_cILJ&j67>B{B?=ORJXmkaoq437`7h=UgthdeDbA=s`H32%XKoQ?_1_DD?cd +zzg-041Vzq#OUd0J6^#}bA@De+M;jd-r2rf~y3XsPqVe*y(Veh}jtP;2q9`52OT=Bs +zyn+Ug43yEFt8cr7I01C_LfwF)UvS>5DuDs+pdPosN(mxDbwI-#6>MaiE|ngs29=8% +z0nj!(6)9!soa-MPrWD!3LqmKQWL#^FGB!^sZ9p=W78#?tVvC~rs29WTPu<55!FJ-W +z3P_afo_>EjXc4sQ=zno8B~pT6-{gS^me}=86>y{+lqgI%2pQ?9k);ci2MpI=CnzGI +z9W{j;?lC8vXeaO#>B;R-+}sNRiB>!jC-1#{V3XEjz~cIcJgtcXFnIVeg(edbU?TId +z0k_|FYf-^T)!+r(N?|7=l@=&mEwKF(z{YlM@UvnaP>PC4RM +z84txZ3~5}(47`R45aAhq05T{*v+VvH-#rm5KM~Nc!l@;$Lqr}*xmr3TN$VOpB#Gdn +zAjS<~P?97;E~|qrZ#u`8FB%Xu0?lm!8?b~wxSAEd@ce7l1wZI>o +zI7S4*zpTpTKq;{R2xq?K)Qa!vM;~smUF>TIO0vrZMXIe-QUutLaxlZa?gBh1hpwZS +zvgc%gbX(H(4ndJ-JY{Ae7!2_I1G-aB8O2NraRBx%%cJs4u|v|l$jM=LL@~3nk*`{c +zHGOPe;tM$3kQ|Jx3V(5rO(tupDonBsod`}f?!}lL*H9x+k{ctI`r_fM%7{2nw|$5yw<* +z+!jnzRRt2m@W5Tm^4eHu{gnHGHn3` +zWKoo;)-EsMqH9G@%q6mkza(-p%h`?40Wd8`gu1K-l>jyeP+&gL;bvf9h--`k0qE}P +zfad0AQOJWo8OpvG@yB;sOL+z6cKOS(4HW}+94j}gNhc(M@I3Pn)0A!;>CrrB2?r%q +z5@H^F&|k7PK5ride#8VDYi%UsUVNtfn@K}78lj*c?81iBjSckgNIai9dSUfbhMT!99<2;{Uds{mI4AXbHkR6$NT&6`yyatDcD0hHg +zK+%M9|8wON)W}rlh-$#Ta0IHxy^MT$CiW3{R|obW +z04onVkdA%%uK$96{J=lMmaSWfr9!`QEMBo>@gkL=1H@uVq__g`Lm&GneC|`9u8J6d +znTc*%y&76uT1nw12>=qFwD1QoDIrG;wrtq~^^M%TcG+c@!bGX(b*MN)O6uSoc;%9RK0o-)cjqtz&UnR7~#H)%6@Z_xvp_~)>@g1Ca%3Fv?BjLo|-Qm!L(Vg`T +z5DLX$aBzq?0VI&WdHQLZQ|S}nb^l4SUrd9~z&QZ7W-^b)b>%_J=_BEl*Ve#2U%m_e +z`GX%JVt~Hy;ZRVd +z(4g$xxFch2KWv9VL{@_ZL&GD`(cV_kJlR$m4C)C`YyiF^bTw9Fw155dFC6a6 +zy4$kX98??jg)4AX?p3p{z!d-zgXK$?Kqwp}_BEM^^THs4fP|1Or>wLFeBeVLpWul*rm|i+(NMdlh0u}2@H@l#O|nYLZG9H_fk+Og_FHQmTPq3HSprG9GE6_|N_N_aS2IKxB3 +zuyNC77#ka>>rojf7at~;2vVxB0KESLAL7w)(MWkOO!Rr%ogbink?X+w=#~e(H}_by +z$o`L3f|Bmz@aOhs25j@4hfh8_OW`vIL+Zo*?G2#>3=vYHg&-D>0f^+Byh4yc#1r%K +z<1<7eQS!eZ8Xk4{;Csgh*vD~>^>Y_qbP>0Ngo4o6SWok!TLQ>SGLxq4%j1yrWHqm4%&&fuQ{o0enuuR%v3x5=YalglA1RzNryBBc_V(=quc&w#@nh442z;7CNk +z6?fz6+v(f`_udQ9iTp$Wp8 +zw*C@;kIf1Wz?GcC~r(mJp +zpple=UgN{{RTr+KJOfC0kqBIT*`+pd)T0okKZ7Zv7r}zZzw=%4S;(ZlU4JLhvC{$7 +z%%=0fJ8y@dJoyXf{i@^%NCctAQW|(#0gyFK?M^}8j9DIMb%PH#Gev|ge(|COv~+ED +zoG0Iqzd=j_)}U)>2tqoQf`7Q_<}3vsY4H=73*R1I#$u#TK)ICH0br^<2#Y|_(vn;< +z?&yOeg0ep7E?JIT_x3X}uvfVU!>?^0w;CAij^iS*#8U?RCa7$V12BmkyNJr&?c91^>BTEzF)g#$d#ptvjL +z9SGP6kGnoDe(Foq7)&HHu=5J)P30V?h&gwDBM;RN~tZCBrE$KS&=V5GeSOW78d43NAbbaO*8MscVPH^5BHA +zk>P!y$O=knM>+z7iEAuMHo`dtLuvPwvMZ^7 +zPbq@HM3rFlKd1}w;+EIka1&R4xl2X^gJ>iIdv|UZWzO9cI-3aSLav! +zR62So0#V!<8ycYWaFeS9GzxqQw*k-J#q_PU2l-pO4obK_uWo7IfP(LGU5a4txkNJLaw795Yo6Df6$Q4mtbMmT{W +z*d`>10wTX;`(;`(8bg3DXbk&rs1N7V3?DFmA>{PlC2Xd-cOaO7UWLe?xfd70YTe!CgtX^_CkQp=nTDwWO3Nng(rvA6#Likc}=j!W1kW8i_ +z91g0w|0$76Pg)D`nqV;C5E&#f;qpEgEhO^ef|cyQ%m;{n>%&2aM3Xt61oH!S_VmI0 +z1&iRhXa5Lo?dW=YXznK$e(;uwmX09p;+U4a5KxJA%oNn$Pc1R$K4UBk;cenL$vXup +zX9JY*Ri-O<=S#qytiG`YI=gyd^TxH%5RY>uPjyuQ4&ptfA?I$g??6QZPK-~GyxG~= +z1!EHvP~Y4NwgOxhQ?v^+%9@90r{3E?NIwFil1xKGqap$+p#!jlXC5Z$z!C6gRkTAOImmIKG@z +z47b#XW4|Ngl-lw{ +zG6`d2V?+ROz8V{yJFw0P$jCfY0D46c=Dt^H*P@OAm5ms9%uJS-okx8S`g?Tu&8A`_ +zzyHnu!KyD{k7NOwbx#UiLo&X)kQG?<$B}{l8F>){<#Rj +zuL%bY;s}s%y!`i9;Q1F`BKbXm0XC^LCm>pkE6V>ogOLeWUUe1s1z@r$q_&$#rw-tM +z4_tXDTz~Uw_|Xr(Pa=WKK%j~gAo8Pz9SLA@z_smYYU>{uq^inTy*Cnxi4a?Nh%Te& +znwW@CJV1Z{APfx-=WJihwdh*xeXZ1s@UY*O=H@K_e=optV^>^rE&S@|Kcn>O)q-+7 +zMr-m@tA#<(PCjGCOnT|KqqTbi{yoG6VIY07PR6QVQO5 +z(=9}>uDa%GShs#1%$+xn!d4L>+uNFHhfXHZDTv$puejn0IRDHuN{R*8wsk8Uar80J +z+dG5&2$D%#gg^or3Uh8EmPkS{TvwFhOCsCW4eMdn`~_NtUw$o`5xE$XQ4x#il!I== +z+jneNQH12Si-ANQMMXj5pwPO2q1t#YT5~!(I_X?fQ-TVAI`a`o9|#M6{KNl8qFh=i +z0Sm$A&2PYpgAak3vu4r#kU;wlG%sexml%?L2^%+TfQ5?|*3_7Y0Ce~ELVJ4)43CWQ +z`syWmT^)MxO4=pA^UX)$Jy%{$yD`e{qvI1~7Q`@J>~3Ii2yS1!8s2u^+3?z$weXFv +zKLR)1d@C4K)7>J4ABvp;D(%h9esv)OV(}zkO^tWI>pk%H^Ur}-Uta@{Jp6UI?tRx2 +zOU;ZRELpq|BGCydYt`A=L0rd`S6l@boPB1=fk230M;vtwi6?0hv!K8TQrRqu)+<5d +zO*U+b_5;}Y#%7p_L||xG$=a!Ov*1$DC*KmqK`i*yQ%{kFqVIrtTsJ^PJsY91K_dWS +zcr+f|;Td;f(ytw1BXli_PmT5UkZf)uF$60Vlfl8V;D=8$<4rfgx(%D5vA&LM@e3Es +zhZ}F;+T{z+KZpKef)U)AT3VVQk;teW5_f$jstut?fO0)9=CQE|y!Dim=p0^)JLNaN +z{xB{2>uv=V-pc|wAcimsGYacJWLoB +zZ96w_f?4wy5@&=l66WYQ5lZw8NMw>QGBOIkeDX;?e#T(umd&vEz=Mc8GTq5crMF+k +zq@5DsJCA*fa|w9u8=GMM(&aEB;s{t*7jan4%@z>=e25=>|NB`=$>KO0t}7TA7${?g +zi-S#@N~_g34aI|~3*g-C4OX7l)!_%PqU*l)&_fPo6S}6p?VPhnWA3<*Velj!8I)Hi +zAY5lV#iWE1FP=!!A|_KF)f(XAI_%(8^m*~+S3K``(FNy~y$p;hX`HDvSUQawPQeKv +zVlE^3IT2@6s}IGTpa0~i!e!bJ&Qk{Mf~d&j{tmpFZ?;0u61tW&aVQuUfMOla9%P!{-Gu2*mZk{c)lC|jclB}HC1C^GvP +zkTwGl@?c9#xt|}9ZMHK-U{Y~FG4{eI0v#T=tY7Oe4cS?DELFFkN_UIz7^s6G!dThY +zooJM2ur)-kffU5zQPPhn|BH<2c|Wf3D~|y1TmjXMa5?X)eNc+$y79(fq9yy|LL +zw=Mv)=gc9GLfnBdj~?m|+FRQQ0j|F0TDaisvqWsV-H$z#9OJWOq8rmr$mkAhvtyeC +z7%DrC<3>J#&Iv${-gPZ@MwD2am>7p-GC?sHj4CfTsoME6fhYM*1Ps#TOMe2?C!{Iv +z!25pt#P6U4z~|nvZ5t^@r!t_U9Qw=D5;!;j=K7J-|5-ck5+1|>?;XJzX=lJ;ha60Z +z_SiQcg{!Z5FJ%=%n>|Ieg@RxNgRpe*Lb&CYTj9cU&Q|g~cCm5&8koOmi7Ho>b9~zU +zydN|VerMSLv$O6{{(&;wUs*ub;`Ihg<2FnoXTPyw9n7A$h@y%pst_HZkiZ2Dp1`xd +zf!h7(2R|fT2$}-%+R>o_Xzl2vd;__loM(VZiUv$35)g|_r5U~Ju#503fWE{fv^^`tsss`@!>`&!!TD1i93^kTN +zd7pCt<-qP;JK&U4Pv>>?CwWn1DFIEZtQv!*01M+u7MFv9uOx`*h5!AJQk%Gh~}nd$|X1r +z)8Ga{0Q59pP8b^-kRdkPTZEK?DCKTXpC*lKe` +ztdXHX`nhw*Ht6c=hICUi&+uCVFvbA(?9L9Q^$T8l@kLm)Xc4q`WZRwfdoqy{eN^nO +zsRZu0^_26lmJrS>?AR~(!!y5!gI277?w;(Nh|^H};M}ShPZu4FL?8y-UWw+HU^hTy +ze3X)J)Dj?)?b)*nX3m`L>!_%>NjeQnmo4)o0-)LHG+T2(b7P|a*ZlL+0t5ZkiwBs7>VV1$ +z>{SPV^45iTRhwFsAu)g9(C{$Ko;e$0K8B*&L;#2rkdO=cnbak)$*-MPEFb|u&IX;V +z(Aml_(1`$Y0=|!1SX!!KZTb9{@5ws$Pq(l3{XQU`qt;je!g@0YyNdEH5&#)tR*Qi7 +z^>Cckm{>^!)~{RFSRf8y +zZ~tD;=D3q#W}nS(Y=U@=$^SYaQf}Y5mC}e;h5gpTQ5);M+-wzCTbkbpVo}t8frY231zWXQKAF!@L5a%&(u$)VBGBJHB8}LblK3 +z@9DXknn0#xM=;p_<aFUeK2?4T=>j? +ze1e|lki(9IdGi-QM`uUHPxthbKc#e9vu4kEQ_NLYR~Pinm_haS{Df+90F*S3oR0E!3oQFgs^3R$*OQrHg7#SW?QHg#K +z0nEbO(AY@k)~PpF86Ib87=-*-A!%edJBv~+L!pXJZ+0L8z!f;)3lT^qlhD}MRCD%D +z9kKJl<_+t~bFQzq4-Py0Fc=;lc9^%YAja^Z{ksw(fb)(e!fK7{)Db%$y!5vhC{(v^ +z#!OhYd^wDdjZxY$-=T^e04b1NpjT4%6D+HS{Qx)-7$qXGcaOJHuoyVzgp;7NyQ}6L +zVQ?{T*|e!(l;fhM%ixT&&VfC9c2kJ0mu*`TfvsD&P#W}d;rzE>1idq63O2NM01Je? +z`uCR#&edT@90liIa3Q?0c_YN4vFadyfzoD`evkm*Cnf$$f>4$Fxl2LU063I@k`vgv +zb!$ajz_#r>YU&{+;M8}tw^6SAe1M4?Fw}VW&K<(Em#r+5M1U#_b#<0Krky*s)8CrQ +z$io2IJ30!o^q7SRIX--j`i6!n7juBz-hQU{bAX5iuooW&v|#@AmtLIu_FOlhJM(kT +zK1T^N^8sec#nR=wcI||@^A}R`4GB7H7>`D%V(yyP*A}Ggg@d0;X;Eq@Rq5C4-P;dq +zUVRltM#u8@g?=Ka!`QZU3mmj+6~wVzn9oqnd;lpg^R`ufhNOHjoey9NL32wBvG+@t +z*EyZRBghW)Y2ng>GR*z#Wpx}1I~to-hp~4`ZWwIxeXrH +zKLlH4p>%5;)uvs}rs6@VDa8miLFn&*%Fg|U_?K_)bHBWHV1*e=Vqh;@TZ@0y<05Oa +z4M#`Dh|;I}ZD`dFe<%hESVFN506BKJ8yn(E5U@P^U#JbVSifF@?7zjmLMQEPG&{X= +zr=;KpipTIG986|P2mMng6t9-*4XLl{&ERt~+#gX3nxp;1!6(V8EVCnBAgO>=yUET? +zEANMbbSHDJ-#-3)WyZl%YnZ9!Xi3(x?eMTVGnlH83vs<)n0h7NcR4dc#U7h1E +zCAe}hL8Mfr__xnfa~{|jgN!!qI`pwR!A?3}z~aH)S4&Bwlu~ZUG_HcCnt3nQ*o+`$ +zSgp~9&4veNxWz?L>MU9h>Dlh~e3_s@c@dc_ZEYPvu7SLQ&?lp8#lPz|?FSR8zWeFb +z<+?Wax67scFE+;mG&DwmL(QSAcHQLWwiX!okTc8gLst8@BW&R_1|{_89ffFskB+;4 +z-|_!NTt+}?B5dl}_+ubdLk2O%iN+_7#@|}f_2^(u%vmz}RqlzsYf>lfsoYW1;^H9% +zli|z3X>9Fis#Pb$7vHCIm(0I~1{8A(i@r%g;;kkVunl%gb*+Mu$xZ^V74Cni{s!o>__c$j596g0?Si +zE{h>_5EiA|S*NC4#S870HEpSl6tPP{f;1gNFKeiuV>`jT4>Yt3@GyP0)AOsap1*8E +z13f&*w5MF$@V^2_LGlcLHvygMccCl3?#)vy +z?8`WR9_v&kZSZ4oaqLd@e2TD1tETV~f-TrNj!lO9sbc>XsmOZR&rxu4a(2jK3+lr= +zejl0-14=yg)DYsFd}o|>#?MHxsWLXu4{gTIkAr(woI>&Edle(2_a3mLy6APoqUbMT +z*ZHk*K$jj`KdxewQLu@Qs1?T@PBrq@&xzaw$M>&wyORk9Ke^m}0*qOuJyE2ZjdqK! +zo_Hv9m{Ba=cGqQqRr^EecbdZtX>8LB2G_x- +zZ~B#Ch+doQr)l57R5rSW_Y+sWZ@)9(M0fWv0ysNNU@*5ekfv@Aw*ZXD$LCSupXS!q +zLH-}@tX31cVc(S^_qF|AdUmoR67u+QgKEOzWhgQBm_jb*_gkQ_nYivs +zKury7wWKz1pR1i|#!h@A1;xzX8uRfYcXm0LVmoR@v%Mgzs|EVE`}U|(13k4V89#>Hc&}lf6(~vZ@=7W(9@{Cz8?iDe`JPK$MyQ<%!(U02^(VWd;{No +zG(^hD)u`0w@f{ne{v$=zQyPcN*;_9Y+_dqJcTA&hI~{pMi|d(Ocilt;eIF!&@1D?W +z#Ud-_-V$`R-9#YD)Wbyl&hR6XU0hKu!SC1^yQIoiDu?a>A+nVk5$fv#Ffuy&dw2B1 +z!!O6uE>0Ew&jnX=h|1O*K}-R5ydVIiPHaUMpA~i@MvR&{`WQWGW&4~Gue@zd|I5>E +zQ>?gltF0iTPD{|i(F4Ca2F}%f>!p8qI2_Z!K@s&*jTKIiIN4DpIa$|{G*X&&og3Tp +z1J167uvQV1r+9<+bCXKm{?*g2!Kcv-nM$X+Q`guYx#Lme-0LN9)kU%xR{@BRp-k{X#`9Y +z@1S&OuHArj>B?`3@)WZnGnhKYzhbiV#e2W|iKrwcLK+Rns9@P?5TlGU!Xyl=tqH+l +zCK*OnEg0L@OpE}}Fp97#?=ZBp4d`tH+!i`S5XNr(rJ`d?gx#a2{faSL?tFaS*v2}) +z;dWjjrZ}4rs6Nw#3mJE@l}!Q9%%b=^6{rgnAAp5qBFrMq$YX57*O@v&(V|i~4*J3g +z$WwT@Ev6_{WLOoQbtZO=tjB%_lmZ2Ah6~^a2=9J|Sji%5i@@L}!K3=Tr*}bMw_To; +zmGyu~Z$iagFp)Tg*tS)wmhp4TT~A6f{;^D2ngJuDVnCxYP-LX`bdY#W3ieR6`0^4I +z&TY-`28S2x5foFGxu*~Ui)pPDejkGTB^kUwMTv9KO#hOPmgv?Be5u+Q!Pb>>nF;W; +z4^q2bepH7bZZ?vg=JEWPzwfZrc5(##sE7CsZOg`*IF5nC>Vv8&-?XcZ1_#q6pI#v7 +zvtp$pBP3YGg8n*m_&Zxl%>LD;44&#zYTAz%f*C(H8UQcq`giK#P)yQ6 +zDrF)_&+pOhr@>%?Q3r +z0{M%@gN&&>91K!vTHqfvq^lE7XU0r_5muef0J&f=%{RQ +z67NVQtIM+>e}7>5kVW#rNpN!yDgDaXzhCl?nGfl +zP)YEQYu?#2T%=@6^;W1fP<$8(^bv*Y$-NU5I<))_iW)p;Yqf*Vk?Oy2UM3lP#kq3P +zkl^rjI1JV= +z8!Ouy+(OCRCNKP0UKyxfH_sVRHGt_&ekH)O;{~mzyHWR3C`>+*A6`_nr*E=955)Tv +z+e=p#lMjV``<8}t_p4}Bzy$rTO4hK<53A)mWeOCSagd`FZ6F0%R}g_n5Wt|G66$dc +z5!@Facm1P;eLw@X%*xG;K$GEx5NY6;OwJ^TP=1o$P-+1!!uxSVQG2OH#UMf^=9jU= +z97m_!Q&h2ftEUXYEQDg*nGVtqzeC%GQ*?)5FDjKv2PIlKOcUU14;K(fFhXRYqtH)>JB*C745ldb)m^5Y(vL2GePv=+`tTEChfEQ51qbd`^8tde!*XoDA&1_{ +zpeqc~7BS)Q6EZ?OV3EFr5_T)_tc$!r%z<)PMA=gV^gJKBwv;SVyV{iG8Y#Yf~jFOsWiA}RI4F~AA4w6Trbn2+?^4T=oOnHKzo?|wE +z^aaG4vSZ8{JL +zvlMi=DkdX&6SueuHa@8fltq)v{dfl2np0;47iqKzEn@3PxuXDHICB{u5@%``V&FVE +z>{ck;)|m(y&+z@w0W1XJvSeoy7=4UU{>n!1TxbU%7lvF-Lo)(iuLmiycKw~Mrai3+ +z(+ZjNIqobz?|i!zrkm^;V22a!&;`6`9}00L^=6FBJ`fPBfBV)cVFLNV +z?*BK8JII(*cqBbvQGi(p6EVSzv>#K3#G`Twfwd@>Bspd6olmpwb@y2U9VAQ(Cmk}{ +zVYF^4Gj4|#CtJjRm7V=b?f{?tG!!ZWA0jVptb%wnvCL^%bWZy+qTV+-6xX7oOYUto +z;yzojx7n!}W1MZYCYQrxY6W#!ne-Eu5N8R|H4JM +z<)t`DQ=>Sj`;6*U94iUk?h`e(y8=+>CIb3|L6Xfdl5e0rvX!^8oQ +z!6p|jzt?;mGzK5g6Lw<_gbOiPVf=@le$l5#Ak{mfr! +zhyWKCkswEo86PuR(hx%bpr`1CGR}WbxqzBS6K1yNaKzUxN&*SO6eO2@Bk)^w&ziD< +zR6L}cy;Tz)`!RCxPr}9+YB3Q?jy&3Obb`4E6sHVaj8nuj+(qYSSZT<9228Sg~S5sJ={cSMO5Ga%f&P?QL2Cemq{4ri(rJSGUq<`d3$ouM*@;;e`_sA7`3?DQAR}hmB@NHU4Uq +z=Vkv0qzqZcdNc&NPNli~Kfr(^>)PM!_-gZhV8f(6==^J)V +zF`(bj(0qQkLCMLmpAcBg!Nxar50@Ew$Uh@Nz!&>7*&6_|c&wTqkm9)jru=2=aQ2NQIYb)Dg(cFP+x(kPr&EE~?DaF!3(x?-*} +zP?MB=zOtS>(Li22yxM7U!<*f7!rovXUIzkpYdX@RNJuadXls!>ioE-0PIw(cY +z%m{+H;|U!fGRb-%AUWFbXOss1drE#BQs{)`4=hklil*NPDanp8Q*4vc#-heu*T%d*gBk>a88Pu5g)8OU7{#j{eT9#o +zLog{EA^;tx{3|8`Yd0Glas#WrtQLu0$Ti`=r=gQuPpYjMIhI0)Y=;v%gzHg!;Zf}0 +zlx`;gXAOgO#}rq76K5iCP8vYsi%!);*(g+{Q?!alSJF=-i9YGd@K$A=ptZ24;``72 +zKZ@Vuv8nS4%Oi*L8|uu)$B&jLl~5r0kY9;QHO9W}HA(vd>LzE&?#_BSmmrS!A^vE4 +ziI)j>xj?+NVxlVH;VM#+{rLvkF;+rb_|<)V$vN++i>{ulxVILz%4Ht$jr18|0Mj +z;dMIhE_QExV0yMmYp$O(Og%y8tRhr&Ot9OeeI=E?X;$|b@Z|Iw9fx1VrZ~Q(zY*!@ +zg{BKMaXn6uY#9~oyVmPz$OCW4gbrYz<|ln8#(f3oAlH|tK*FOpTaK6W-W+$@&lgss +ze&&n_>D)E}_L;&(D_?Uua*4s_AvM%`0d8SZX|dQfc6&CF?apH)i{0?^T5u4D>f_gnCRy3NGR4XQ_VlVsc-<7&gIYM=i@=Y9Qpy^FLFPbt8tvtozy@rOWL +z(JKph7B$s3n|C>|{^VXhH4jutl?_weXqwib5wg;VvB&w{pVF(CKLfYzm*NB(k_Hc8 +zRgr(26jw>0={g@xhyD~eCNMBbF%p$6NtO{s!}1H~O~KwY-MOaK^`2K#GrS-5*LHv! +znLIg^@;dN;N}P}`^d3GMjp+N9-9-eiGirPb>Bi{yyL9U^VkRIz+da)HWB6Qy{;tmm +z9|i+$U?r?*~%VJ}qQgSz_p$63oV) +z>3#k*;Yd4r#TP!em#l*vT)0F*W;;gszn}EtAdQ +z0l67rEnLd|FJGDYuiUW!IEts#d$vbhG=gZrm*6EZ!y^7w&~`#jlo}({s+)wDTz*8o03zUzH>23oHyV@(Tqy!UD#;!A)W7w#zZ>bNVf@DOZ +zh3J0J(PAqGq>4)B*i3j{)-O)iA7*^0UlL^>`=-2BmsK#dO}-1Bw$%!U^J?+8TGi*9 +z=7FpI_yni2qdyRqEV`XA{KUdOqb +z-UMi12)J#?P2vX;WBU0}ItSF@e6G_syS422`llmE__2QIG!mm|w-nIt+8f|uXn87K +zvzbi*3;dH&9FwjJ)zoZ6Jf5zrY&CS|UUqG>C$3CLn+Q!AeJRm#I$r3)Xm+kTPB#W! +z-#ESn8ju#dSo2pgP>X!#HD7vI?7Iv|9OBgN@J@Al;(%+dd|E#Zn0eB5SIG{|TRc7B +z$lim*$~z?9A~fI84XsUZwO3ZWJ7I%^M>EwKc}ocXVKz4wQ@}~T>>okc$_O$Tmn-g8q1)n-6vDR-eR+Cc7#w};V?1iicR^&}>ZlX#Pdcc)y +zb|7^;KRs@jj&b0^}Q2t2yJ6r0zRS1Z@|(SpA`-311NSQl|zQE%|7c`c~xiCWCY +z@@m{4^L_);76kr;goMC+RSn2cE@`buCILi9nm-hne?{g0V9EAyR8H{x;kPrfGzH*n +zbWz%h{Xj|tVE9l-btwd8UPd){2)<-qny%lUxauFYplk&h$K>s50?m5|6oL%?WwzR} +zW|&@&5D}O&-8hv$VEKAqQ@Pv?>%YTH2<~#a-QU-9$MMiJEFVInK=^^Yz~&|t?HY8W +zYpA2cuaMV6Fo6>6&=?rs&5Nr7WiZQb2etMqMyZ9SASy} +zF02LIBZr=23vCj}L$-N8HyD^h%|5J?@3lJ(^T`q1qBWklRZ(e3Tp8OQBV1KnEUl$& +zGAsC@Qhrz=_eRX^o7wnT%O0=?e)P)liY~x%ivs&oxW0S4=E^ouNjUYoStoptjo`=40*r?@wTsCQ~@9jO9y(fmz^mIa?lT%xU +z!)>RFh~nc +zIUUVd*3eCbCOA}hX3f#?%OUcmg%r?=oQ1~eH}MzUY-H>=$e-8t#;gZmn$g$ +z7e{%cXNt^(z=k7mcVwJpw&8l87`^#7%U*!!^_=7b>Xh4>w9m0IZqYD%^6glQcRy(N +ztOoZRMpL#i15~@w;pXZls!oG#AmXIpDxad1>47^7DOkSMZw_vH-`%W#$?^;NnM_&{ +zzRFy)=AOkC!R>iBZC6}9b36Hk<1l)%ZsK2))}1Hs<0pznuR2@awl +z$6=RgvOa?T%b9P_^JNnp{s<=g*-PlgUhpcZhv(_1JhwVEI?2Vr9xZ25U2@SDg +z=e4$VezwzHjiLC2(>#8<2FcA^fVR=|D&6wH&@E_vx +zl;H#)R`1+j1Eaxa9YLDV8|m(0g0wl~W*YuK=;I>4QWwr3w}tLpW7~g$j-Xx +znWs)^O?&D3|A7}lWa3V?C4LCpV_L-wD{Ix(!|3F*c6`rJE(ZgrmHzsl7pQmM=P&mN +zc-Kv>$XSaqqfV#-Z^%2?CY=}S9cjk1+tR|`=o`-sw_E+4oe-hKZSfP<8~?CS_)QW8 +zlOQr=_(a-W{yz#*V)y%wtIOly`z@q>-}gZgJ@59fRW_Z$q-rz$)q7v#;xXlcKOR{P +znz$yV6fRzV*l&%f32>;SE~>l_3Xq_U5m381H1--lHSc>bcHa~VXu*)zw?-{GwA($e +zvO=IxmOU(8@nj7W?H4BNQ>l;=4;l}FZArS|{ZrKrQk-Jq%gZc)f}%I}>zDJ_usbmS +zr*z8p52`?D;D4V}*;+XL^w-{e +zoa&8sx_PSdd5whCse0U1Z&P!%b~_6v`DD?3c|e^1nV-G-G?0{Gd>aJ-XT{HZA39^G +z(Bp?<9)D+rSo)FKoCox~_(DaS5_LXyS4|Ikzcmn4{gqogL|Z7=NnT%=Z~W+>F}6hT|(P(Z;UASEOq` +zx5)Ne|4i@Jy^*uTrzVGu6+d4ZqZ`cH@0q~5Yvd!4>mSaBd=z2ML&3e~@|tHr+Z5_D +zA2K`p$R(z87iWjx^7-C#xs^=w#kBgRz8>sJYb~V3gJ(Z@YTM=%`6E1dKP%(BR?0;E+oDX)}KptzW7nYApo|C3G?}cN#nn}F2F)u%d +z!AySJ=4FR(jr$Bx6euy9F}@iS{D1hk@k68Epa%{JfU#SK%aHrnmmT$g^@Fz8)jK8h +z%$islVm#MI&kdTvFx)@cRlNh;Y#-rC*!gU>Nk3>esw>utl@BD;hu@K7A2GA-&&h>2 +zTq$BGMIS4OpCsT?*Z2?pT6Lm9w1x +zB3)e>jO)VEP(OExuCn#pMxE-i+Zp)P9uOG&$8p=hFl${tvrRmI(qQ68*?(>nQ0D}m +zD%UmBAh^YOQE_kaABrshA?xTB_rP_40sCVDvejLN; +zJjtnz=`5FToirV2@@CxhzZO*?T-Q8UBMlVD2#x2{%Y+4PJ$cRh5$H9)bF4WZ$`&%Mi8`HjcAoE{l~+Qf_dtw34U?Rh`haK2Q{6a$!a-=8nJzz1U|%;E{_G7$9T)$|3J +zwEK#QpH7mK2s`{MURv;Jvjug8Ll?bZ>fjpNSl>v5ExS{_F24HdIKb4Eip_3 +zijEK5Z#2h8VpBTp&E0T+PG%t2u#0_n@=u48847-*Kc*;KpVFfQ3@r!MLx9T~V5Nfq +z^K8M{$VYqo4UjJlSsN=yn>d|B1PU-JMbK>x;rAbe?~c4o4KDPQG?Tb8A_~lQ)mzQO3BsKr{6ywF5HA@geF0wC3KZhR}p +zFe77-7P?fviPyv~{^$?-! +zj3hp^YR$f<{e%Z=loKdWx>3pfv{DH035M6c%y&_)qbVl&55-``ONE5W`9vJwZ1s!I +zj|4hFqkw(MtikHW#wz3enMYxnsN8m%idjzH?cVlF+o9hZHvB(;E|TQf!P}y*u2d3$ +zL!#xDh=@}Sp!5I8lEtDs@Hh;YZ}wS*xM3(vn+O%b(}1!Depw};bg}$b=`CvJigUKS +zsxNUm45>S2XT1hM#AX?`D$=QQ#v`=v0$$yz2nV_`UWoW`X$XP*Z#R8FcM~b_@1Boy +zGpgTjhLiQ*2nfBFw%rd;swtxLGCxB%KbgV6-oIFqx|Am`pNor|%|wxoneF9hzu-Y` +zEu|=0wNP}W|I&ZzebCmc9lbF%&nQg^{41*l=DeR`$RPokmTjJgD26Me>PfQXB8kB* +zUq*>fCWuy#?Z$)kIs6`?qXQVhoaO&IlTDw0W9kHWPWM1lJ)Xlb5`CN@vh>vC-9#U1 +zQkMucm22z5nH*n%S84ITL|>*~@(;%KES*!oi?K<;S3(JdBkJlcGgjOHz1CknYtM)! +z_7PCt(vi{>s%);~r&gc7p8EI{!!E=R^V5T4cZB?l7ml(Q@uU0kqk0kBR44ZGt2bnH +z=yA9*5=60ndy3H{@DL1N8HXYa$*PeEL>WiFK|uaHgM%Z7icZuN*^yDDJSC_5UgUPh +z?3%oHamMGWsHjMy#`4th*r8EXTGmolR%Yf|_#AfdOQYT6Qcc18ah{{T#Pvo+nBqmD +z@Z>Y?bkVM>4D78$HfkLYb|5$JZF)K&u-}=ZvjnWN6xeh}tv!WHlPu`@yaH>vc%AN- +zxV}WVI1>{%duKt$YP2-vzM&p>gnrL?NGgY|CQwSv2&$jwYI^K&3DYd~ +zG#Lb}HZ3UM%0ZGOZ#T3wz#g`nLhHiDk?8#KJzPHABC_*ltm8?4G(JG?($f!~dE!;2$50|5OUCw~I$713b8&U})GhVwJ!BJN6RoJMR>ZT(8W1xce=I3XylC}m^Cz)>T=8j$|m>?iP6yP;^6 +z(<$U(3vcl-X>ar6!|7^*hInzv`a*#VbP{tKS1b)9H2~!J;ehZwAPWsgpMND@bKPpBpVPYTI +zneWrevBuWJ>SfR$8by$D{>P@3uUm^MF~u}VJFJ%N-)!u)Ks4i9?Lqc*mvrM78`Lb6 +zYE!-j^3Ma;Y~*@QSQqB;jnD;RFErfrl$-#vFx}I1wXdZ3SO(L!K0d?(`dvR=80r)8 +zt2|#P1>nCW7sbB!^n5^Ksz*?@RBkkZEWdcv<|@T9jYJc-6L?i`z1&26cPQaEaGdkp +z_*6aUaIw&~4`kmk)$46*SQXG(3HC-pX^pdGbB&`ou&mb86y +z_LnZb&-6y+_o8LmCpj)nW!KwP;yUl;ZtC%mrOhu~g3xkua^W2sqJ&eIyfEzk_##W9 +z`!c3%cAV+evzlr_LosZOCjJinO#S-Y%hn92nRj#!xt +zGf)EVJ&?UdVJ3$x(7c43gf>_@g5vsrK8*L&TP<617FD+K%c$wzu5{2}AIuR?AW#T5 +zLVXT2c4E}g#zZb7+xL1VR7rIj9n&tRr)Q-G8Z|?0e52r!<{%p>#M`bxM?-HLE-66) +zp9RpKPUP%6)}y1NJb!h3B!8ldQATl1x|SB-={yZwvl@;1?$`9X5(SU!aazwSaap)d +z^e*1K-(&?*%1=v`f*XX0wgv+U-^n?u&bbwn9=6^qV~G4~6nGszYO9Vfg+CiNmtCR_ +z>tHWgA@aP(thf?KDM)OiWrEn|djZZ{IhsKoM@s~Zn1-zzO1WLf#97u<3-24#+*6kG +zhAz)wiyGsB5p5s)q0TCoIciBBnh@lhf&8u)ln1O}ONw3?g?YrgL7yd@pOPF4BRN@+-Og2qKY1!za!B1O|ah4(k&oyC;x`IPmpCTAWyRkgM@52(v +z1#{dWL@wO$Hh{ffmBm-qX?%L6%7IkI7RE>SA<8ruJp2{dFAM*XJ@x +zmvM{`qZQJNSc`!*qvC#GAT(!?U!KZTr}wrE +zu69&vn!npG`&7JcubHWZ)Fb1Mf^IM3B;?;ca4oUWXD7WcZYkoHi~^PeS{XEBGs#jH +zf2ID_@w^fRti~H#U3I<;d?I>(7H0dhzdPZJd0t5*sWHZ=FmZOyL;ck}+1gMpzpS{; +zWw3E_!q{-zC0}ztJSSzmprn9gEg(?fl5tKwMI?eU3LwNF+(HtTFI0Fn2#TPVQ^xc7 +zLDDY=WEJFBd<@fbqb@2EANnoTsa>%!%O7yR5i4R;cD(yY3^{3axZwbY?0E!pFG#Y6 +zNzR0BRKkvF;KSTO3gZIX2)kxB63HLYuX0}h`S|HAMdG`EcN|^wNZ>*^Zk4~uk0Wb~ +zIuUE?4Pz}Cy=eT~@ofVKDOb?Vi!^q}&p45;*tW9Qf7Jv`_nfic?hAi9nb)s~vdKd> +zSuw&Wd7Ynbk_JhcNJU$eH4GA-$Yw#VDl-+$39ck1Kz#AKAg|M~mAfxw@x`-l1VoD# +zYW4NI>)t`Z$&SY%`kV!l$YZ%?VcVlR{eInE*_X37G6XfK9kE8-OV+)$GOISWXs8W9 +zl=F6|uZDm_tlb~)ze&rz3IOpLUKX*|dESHnaq_lT-`ANNQ +zq-0_z{Xys1pS8YX6^(JRuWT@y5QuT-I75hmxk8^&H-iCU&r!%0`ifw&xY#Kx9`SK1*7S?z +zLmxnl(2f0H0;GFae8d$ODt!@AA(uU|u)mQ{MQ)Slg{d-Ia#j&Pu(_D7Ewb#d6?CL? +z+2X>4Xr~|ODs`b0xhaNzBGV3QLO4^v%9Hjy*AU^XFjS{8Cc)MUkYs(>+tmyIQl5~1dXiI +z{v(Kr!d`2kz-|~^fHcvuJF6SLx+$yrm5ILU#IaR>aKA>p)&x(4J{R|#3>JfYzWPor +zzIY(lXGMPHzEDc8=ecNI+;9Y>FT#5x^};?Rt(K|}J6aR#>u>t;Y_=6N)wQkMqD^ksGZv`=N{3;7gbrS7V?RIRddJ2#MWbs3dx#`$BvaFysA8sx +z+`UPB80S1=&LHmceDDi_tDHQQDkRX+AoKck`i*@YTn-ui3D8H2Rr3JJ5=qK(Ng=o+@ll-=u**nK +znBlRcO6Jum?zBodPO&x3c;+DecI#P$lB_6{P4{5)Zg!}hk-HP`Js+Zh9y?19^saSM +zDr(pVTHDNTjU<+_7u1cms829w@g8;f*niT-U#N%Q`hX}9-}XPB%MxyBJb}N=-tWLq +zlgCR1E6avsMl3R5$nyLcCn)w0<6ZG1fr1#_k^*hIpI?$8WOa(MU*yECm;UML)B4xo3DsX%Q<|&XV|e@b07~2>39q>V>yJuRv=R@b1LI3`R9kz&y_+FEzJX1F(2%bA +z`Fe?(tlgvhY=N!}m6!N^8h9`AqwQC@owM>5WC-wv4oPxdOD2LAHmi?#QP6Ixqt8Y` +zL`hFB)&B921nbNN!EO(LC#f~LDt7LP`!-3=L!P$NAfO&x9P&0n%SC%(TPuoIFs)Q< +zuSbb3gQOJmD~h&!%M@vV9axh%(j_I6d*EcPQv&iidv|%OX~{^IgH<31hLbP|Qg20O +zir`0kNezQTA7~@8B=&(HQzS+kqaoFl!##y$shCf@`tkgOQlWm1gLN)IUh*p?_!vw? +zyN%~(1qeDIsTOn-Ho&V#s%s(1tmJTVh81aq +z9tYcdC?6RZglsNG7Qzg?`XSbO(dK`}yX%vR*7xHN=*6O25UwCK`&_vD^6i29h;SPG +zAHvZ^8q4#$cn`%_dHN*7`}>BcnMFc`wx^Tg@;Nj@!^~yN7$jUd7aPJvcqK3eR#0vK +zP@xQ8`=24+tveJlK)YyQrC(?o4nrDucZlF6^law5ZufT*DhL5{JW=03jXw&zGuqxhIm(Qx?(r$#8^;Os1yg3M;l3 +zqy=K#%Fp?!i0OB!N9=MEyvYN4m2277MnVyg-&D3L0b#rZ@u(Xet)^tSMvyN89vWSo +ztj>u8?}*5fdXWDP6d^-j_|gzg6D)N7D|w$rlCO2UWKdsX{}-x7q&A4NN^1r&LmhvQ +zfI-?GrSKKX6GLKgZBcL)UazN)?mFEmPfoWK@Lb=EtSF}T+-t!`T(&m&!#$UGJ +zAPocB{OV%n2yu`F$gJGOSP`=20_VO%5j8+fMKz@u9}$y26LaO_0v^DJ+rZp<*h^EW +zGeEzojm|(XEKrCYTZE#s3yvp7!sTIow>3c74=D*WWm|*X4I7hi*D$uNvFyoPwC}lv +zNog4S3D2{YbdhzYL%B%u?zjYXz|q6o{S_n=-S{MAmL&6eW;Dl*Gk^ +z&LIoB;P1rq08dan&%0;4r&a=8SI8lurXKWmq3aTwqBFAN)?A3jyMy3#DDG0 +zLXtAP2&e>go{Q^1byGjBN(Ma^5)5d_N|a|dc2l^beFY@_6C(eoqrGN^LcQ3#D?`Xg +z0ks@q^xL5^i5w!p*R>|OMwA0(>}a44-4Bpkg$P4H9Eui_{s;PzxK9|4=$)43{xu2k +zM2&F?f^mhssE6%zN4tcs{?aLhXT)2%x!B=83Wo<{K$#Hd^#(!lFC;)7b5Js%i>k?M +z;e9o}Y3n`*4jvti`vRH|$}un{d#lC&GpIMbFlZ{iTgHLM@`HOVv!?eY1pMn-=Hfvm +zcIN4zbpcx>)M}$@zIrIuH!&6(5>N8}VnKta>)OrndE8hnXwo3d`jVIoq!kb)JSNiK +zt!hwqVSqQUR|1lgx_xBaj0#!j5nVN9Z~PBPGO-#!*j^I~u@&cPOOl6N3E=G?2Mz3x +zKLPP?{t|D|NP>wi8JtNI_qjm7@V~LFLcm3g!zdk!LYS_{sdHk(E^CqR=bgDntlepA +zJTtE&7|Q*?K6f{3+q9e}Msodb7ndGHojo>W^`h5O;}N%^4=<#&Yj1s6ZOx+9Z+d~; +zkc$nu!Jq)r^+@wE@m3&4q*QI;dQ46}98*0D)^((|WNUWvd_2peU!;=6ha5-9=P2Y$ +zCjaIA?pAYh0rP83jCat-dd=WV7G#-xMaBK%WiHMdn=lr>g9BeB$IJ!fRrkfnR}laq9TO&c)oF1^BQV>0O#=H;ua;P~u(Yqa`RtdZ +zx}^jdoV}lfg1)ejUb$O!b~cBixSALkB3&k~|B-!6u_ghdsr(LkRSZ6F`Rf+?u8(oO +z%@dC?AJ@VPZ1jGtWvI^e2`3c6hxlsafs>dFc<+Ud7& +zo(L3#iZ^b8UfP|HjY?0!;NXYq?hp6P4+dYsUxkZvwxLAVlve +z7lUIj;Lb|c6x=19eofU53@=oa@gr!P_hOSwzr89xfHqr65_9BKk8whT3(C7xQrcE< +zbx~51H(UlzpMX*}SVMRKp#X~teaswz6CG@Wr +z-F7T2BhqtB%>;|ts0G6f&BSm)>8%b)Zi$di=8v`hD!@J3=?RELuC{i`{)w1!wMeSU +zs~yQ1h>(K@iD+Btfa<-k(f|o +zZMzDM5t)mZg?Q`^-wL!YIX=cN#+NiDumzTqo}tW2Y1LL~E+@PM(do9XS~_Gvq)`$+ +zf8Mq`mE*8@7a1MOnaBuo)cBD&pShtO!v6-TT|y1|F{B=mpcE5_oDiKt;={Z4HRq^h +zxG#WAjC0IVR(1#p6g1#y$}y|r!Ngf$2u)u53G{G1I6UmKM5TFiMwNnPH-+3md}{5y +z4^fgkszVIU0 +zJB-C?!PX7p0bw&^Z)c2Z==AEkiHOs(jJSCc#^lONoG7Ve0aH;#c(YQIBagYd^y0ai +zp^5V%Bm*zO^OjgBBz#D-Dk^ohjh=)=-NM8_+_s^=d`yX>Mr|C}vYW&Mh?_2Xz4NYC +zf)Tfrdm|gh=ERvVTdoFu-?C-T^!4=M3nkd6pZ5L=A@iT~b2N=wc|{(XkQ1n1p%nD5NtTsVO|khEpNi6~ +z%fpQZ$5D&h>;znideBY;(S-cpI%M^B#vTn?)oH|JC%cE1#7n=IEyAbP6vLq-Qv$wt +zO#K)=1)mAfZ-`a;-x5S*GX9ooLO{-p<&of0spH1uH!;Y#jm}Z5j(qSP?Qsz>&5fdz4xb! +z#}+x19IbXzn>>gB-3LSG^uBXZ!k}6I`ru9#h^CGVjYdh!E?T;J`A$=oBrn) +zmvW|-hK8e6Xq<>(a_jjMrPazfX<$@nnA?-G?+%ZUWTmVZm7J}=Dr|YRaehGq2(5*AxdD|-t* +zjXxpII_pRO{uFbS{lOEqZ|iglY#WpK#QVInVj9XF&= +zHt8h293f%$+n@H+QYw!GMXn62x@wg6)gG#n4CJnx1Tej|%F4Fdj#jEzTHEBo2QC>y(-x> +z*>B5JU|f}EYbcK2z}xuuU%MDS0013XRzg%Y(mO-9kUKZfh1mDaVP=&`e5R*fU0vP4 +z$qDgmA-T6b)7ceJPPM +zSo`A{biyyr1n)m>k?3z?K!-iSi-tBfQ7LHyvUzbs%Bl(DLc^;C1t%(lTb1aJ~$vesymEhWu@5bW6;Uf +zZQ9wDI2WQ(3vAh1uwyga?5ql_uy9%b1Eq+BkbMqBdb*m}j{QNtM3-yF?s>^A;g!3g +zFYF+b^6u}8K9f}Sp(QC9TX6n+dpkD@vrjT?* +z*@iBB%*U**sVNDECbyav5%Zb`v8}fJ%8@wAJGZ=;Y=%e>Dn|GpUjudZPPS#qY5dG1zrjK +zp8%N08qz9-PBd(jq$}J#4WfwL(fs=yzn@`EIba+b6}8V1!1~e +z4xDoZqB#aK*PjcCSSo)&iv4YsbHHK8t|}~wEDev~JY{3RSOzq1^$Vt8 +zY(g|DAMYVehaWj^8?IcD?ZE7AWv_L70;gf~aa-C=oVa40@oP%YgDQGy+a3Bpi}XSf +z_#j4s1GtUK$T>z$bPMq{p&J*^J3*)$qz(OzLfR1DVM`VX1wrCmgA(C5N>BQRr3g-y +zc#N2POGyWw>ITHp0Z0CU!AV(-;Fe!}oQVY&0Hx?Z$JSASTrmhG1tu=Ep6^j|kmI(X +zP;_-6QlSWfqiVe4e$mwyr|(;k>;`1ek@%5lKOMy%O6ut{iE^dYYFn_nnzkKS=Y*8N +zloAbnRkj?W`;1#bg!id#I>E+{WKg)088F5H+*p|SpKgyq!P5Bq9Tdub +zz5f{;fJ!8NH*bih2mhpj%ms*)$c`(U%|(tmCZ8K{AO}W{i^xT-qzl+Zs6|NGA~isz +z?J74N1+@ri?f0qPS`&&TmtICP@?l7P3E%D%p;M<5G^NZnr+{qU6p +zgK-5?#FrI>J!)FQ^$C5|k}S4Fc-Q*tohT-ZC_`(9}50PiGtU`Iq^5nNd!P}ZvgqSH=9zwC4Jwv^uF*n +z5(HLFkhoyP9_#z&eQFzsZVC}V6lt(xEJi6}AP<)T_Y)91J#h#d+yLUW1G8d4_j8@| +zg@TB>e0=j9h|oRCXW#kRgIfne5&%c_VN%KMx8EV-^Nj$x(2Ig%xGi*No+63-Ey@Kj +z=?QaRDIqx8x{ucsR3MRlB^9%6DAAoH2ea^o>NkK!e#Z`4kemA=4~4Kv`taC@=y?=H +z*lOb*WD!$TCveQljq5N`nG7X1DH33a`w1*g5xn_@1*-iMq;ER$ci!h#Zrl4=$K|59*ic0 +zvH>Xuz1e^h51fR$%FYWA!qTodssBsvEQ4?y?UyE`RC+n-_=Xk3Blkz~-SdOy9_-iWdV`FRQX3KE0z?AEO+W-?BRVt>1`%1b9k2~RBA-wNKh0zn>3%CH4$c8~T+<)8$GOT! +z-)RI0%|<#T0y3IVssStw@ONM3A&`Y%N$0p;fUtq2!8HOMWt?OiL$f8Nm3A8_P-l8( +z9~`;sD185W-=^;XU6Mys9K7(`RLBGgA;#G2)hW+D>euTT2yhc2?H&ciT@=e@ICSU` +zTsU_c#;{(2*p>DHv9PhRL7SeQp6(#m(R0z?`F4H^V10d^)|r`^>AJwENJ=^zaI3mS +z;j*){bFjI!1?9>V1O{*}{^`NmMineO2(hGO8-;|RZtU`vtFXAZpEx#g?Lv?*z{x&n +z2cYB1S8uCwVTjde5Nx+Rasx8U-f6a>Ty$wZ{I{v%=F7^%Kq>&L6rK;4mzH2^W=5@N +zYYBq-7O1`8eR9`VbRDi&t8o1Olkmz*KZ39R&%b~t-u@0)SllnI0LI@+DCEa$vFsU^ +zMH{b72IoV4^?=kG%%O;bX0t(dim0kEY%teaTU#e}ZePM4v6_(dv?wF5=Tj2V{Qg65 +zx8^pH<1_+0jSsOw28%q +zMao`7@qeshZr_4Nt3?mBSS-MX5o*byz=)VQN4Zphi*LLR^YaT(EZFp6F&+`S-jMbC +z8QBpxksH?=lH->#CIF;55VBbdBhUw=biqMH_=1wr&+-YGUwiEw?3)cD>Ns8j_klzJ +z_ju*XRd~yTC!v5wEw=Pq7TkaGA$afm-Uq+=$xp!3ANeS$1YtLTB0S|WvVvpYL!2Ot +z2!lJ_sc)03Fb*UEXs47G@`PZmdc8){*Jg~w2TCo(L?th#JlMj)+uv0l`#d62MQn3hs@+V&i +zS<90EE#gnS?QNRNpvcV@ef}2|CqfR1K||~jU}DIG1r3jguy~zN4L=2i{R(i^SPCF2 +zxH>BF^MCY*Iv>6p;5|L{?qBH`aq-PJ;gQGR3X6*epzXCoCAN@27O +z$&l``nyT#MILXh8B>*|c&5;0h>J4xlm#PNE0UpQf!y`xTgvrW;F8R)YnV%0l_!c;L +z+ihe}Z8UbTjV6loEZTSBxO6Nknw!7*J419#^tcJ;m~LW!nz?QU5H`*)F2c;rB&@A( +zlDr$&IOCxQPtqq1yzt_WNk?aI07nUGW1#ByN6~&oS2YNcRc`7#O+t8z?Kc8>7)(Q1 +zOIi4JR-zjJ{$Ke;n4Fr<{5UZ%@R7&f3Z-%x+S|3TzG%BpE|s9RT~83*q}Sx~>*rM& +zuw+1YD~jI_KJwU2lK@a!LqfQI<9gRCp8wu=VX{(*MezGio|5$mhPdM#Aw(#mh?#VZ +zkDG`a9oOx;UUT(=M;|ygl!u|&@$zcC4wx0NIt{vw&>n!qpr|-FyAvhHX5YT#k}|2s +zm9YYjt&!iNaASE)1{yhrXP`w@Z;*H#y6pg1t?d5zD6Fq{_J+g#Y;M(z-(eljG7O!) +z{<@a(M5M77I)FwgPr1q=muCm3d>wAEiI9YkaNwOEm_INZ1l;Yy$mcJP$Z!W-7Ojh +zMnQV1Px?Qr2W(VaU0Hz>C+>q%sWj03?E#vHV$qJ?%7+9ppnG;IdqJLH>h&o8qpK7P +zh2Dz5B0Spx;`IM+V2lezR3{yef;rAyzmIYK8YOcTv@(?i?*+&u{_KliOl2>~eeF^a +z{@#1vJ+%2d{r!HZCt`ndrMDO438r3OnHX24$g|&r>>xD~5Jir(h?O*M(Cf2~>k8BR +z7T}&^$6#(go9AV6auVhj=HWkn?mw^_Feo!=soxeBzP1b(m=$S}r3Qz|mwXu>e#=RC +z_$?21EUaB>bRCA{+8vLCj?=gBTEYsGV$+!;zb5QYsU31&J9I8I7R+NdUld+wNO0-= +zS-3PLzjQZv_B#$4_67-K-hT^1;hm7%&>r7Jsf0n8s3M35q8a71|s +zw-bYyOZ0V}$ghvbTo?_|J$Lq1ICAtDS^GN(hPjTpUhIwbqW_(#ztP`*R@1!&yCNlR +ztgXP*%)U@h2H@Z=54{?N%=a(~;moEQ0DXx6d4TDHOcMX0k8x=LjmKD`|LmD;t-?`4 +z=M?y%r~tzTbf4`sc3^RS4%Rj{WS5&;5K#Y4Z7|3Po+{EZJ39+owe6p#SpA`jRHpl| +z8$x#*mz7W<)1azW8C6EKx!I08RFoUomqXv +zYnj2{as&YL_c6$4VSWxSUD=(o&$lX|Td`0oL1m%>t1+DECjlbFr)TIzkKeuKNqc-y +zuR>Qq)e&9guA2>gZH%LFLJ~kS@!Z^4r=0gO>i3@vdWM*(ES*jyBFEALKe^fY1vrGo +zN*WE)Z)#1&6WGke$@quK8!%sCYI>SVrTIOYj_kp$1Az)KfDOQu +z0CaCW^#c)WeRYL?US7Hgb93|1o~V$;cobmM0=Rb78k*?&qZeL)qeqXz%)U+|O(^P* +zsvEaqLvhzu137M)x*UxX!f`3}VZdL1^ILG@fd|O1ya&4j4jBL)3gF5DwknMe9V&n& +zbqg&0R0kREihb(cd4O7VLw5uk6+k4rcI^rrJa{PA9bq&cM$6rM{CF%8V5Gv&&JLEd +zmL;ui)qJGw)klU{n&5yBUV@7@D`4m07hil4PMtcnM+9~WujPO%VC+ZKqy`wUrxi=JaV;kBQBXYo2QE$s18|8`ZFK +zF<A{of&M<0jtS9KfjesBgKVEwpcrv$WOYcaU%0xq +zMpcaRgd`%6g!~rU60DnB;Gr8J0YJ%yn!4l&dqiM&LB;_%V7pQkkVZXQ|913mN2&|1 +zt;K%)ZP9nb27rY95hJsMCfmZ05U8;XyciNNVs1L8*|#u91`a7y#2 +zf>6m<#X|l;4on38=5Ky9viJX!0*LS!*WudP2)S!H;Yh&i3Ax}@|IcyY!t3X$hW@Am +zBITvaZ$jG>!x6vFzox&Am*eBF`B`DrBjFD@yl()YT8WMYmdc{cck&ig*H?D;hJFfR +zP%nU`)CW)mfY`yuKlNKl<1w`kt3Z5h#2@uPRMxmJ5Ap{V +z=k3g)z_u>p1nWO9t;hhdnsGO${vKls0btuAGsSUl}VVGoJ{$YY*o5h{(H|o +z_rT=zluQTS`vdL49^6`hHYlNNR9WEMx$|)M(Yxu&TQV155BA`v8nCxTV+WcU>#t&| +uzH?`#U2Bg3?7<$~VsPFtN)H48@c#i|X5sYUzOw8900003vmGHS$-Tm^LH}f7dLxxGjHNcR=A`%7He^&t|2MK~BK}5u~u8N4Li0CQ^h$yn^ +zCf8qG!x~t^z<}~AGE71SW`;>GcfU?m_y5khp>9=IcUN~;zv;*H8H!h3b?e?+r|v!X +z+!NWM2QK2!hBkP>@NC8NtLs9^(t{SO)AqRXgAcD`V`E`uOq{VwmCc$pE0#_rsIu9+ +zz@o*AVQ^?LmP%&|^!4?@&@Myp&AacS9v`ywNZ4ziePCc1Ub#LYDaW+?D4pJp;0CEXY*d-b29Xz9`RRyPR3FtgM5{cDQJVfO-9A +z9H?#Tm#!24lkWkR9#srvMNQbT@2GJV{H{Rg72lO9JTUc*Epc6YjRz>UV%~CZ3}QWWUt8@%d@6ficcF +zfU5`!7tgz*MPr~l^y2=}7(WGwGOqrxV!1wl^U@QNgVDf`|AbxT2j3e*M=e^QQ5E{DfJx}-PnwY)Zn9)rC +z-wkPx0a)C)#mt`Qd)&O{tAm54kzn4G$C>(a=vErU8BhQ|G(wqD%|GE*D +z9$|bFVg@)Z{zzj9uf{hX7#Lt-TtHyrGvhiBBLr~@GX)30m_BwUU;zS(lYkD#_(o1S +zgp`^5`Wf@wEbYIpuJPb=HA^)o^E +z!-^+~kRWWC$kH0iCplTH~{CLa~6E}Uw#Pt?Y*nG7eWxt$^YWZo9G!| +zIaROv%S-DC5&<=t&55tTxK>g=)-8Z#F+r(qtNIhKBVabu0}D(7(1g{nrJ+gh9Ccis +ze$t8X>{CzB`5&KI4u>4>=K@TkfMu&ez2Eh`v)`pB`BP7LT!2}RS+*+90&SL)0fbHd +za-cYZxVQjG0Bo==3+$b33&3n(#z29clmLi#j=$bo#?|KWEbKbaP3M1Hu^bLP{AfCV +z=BdZ^WZpEIiBwl3-*wgc*I>~ebLo8fm)I}G5m;JCjyz}qjk$?SnShV{t^c#tQ32QcNg?GR6?<3U}1+TTN +z{vpt?AW#qh#&${qP_jSeYOlC+5dcs95HHj^uHNys)8IdU`g7QQu+MjW!}bZf7Zn=) +zuIK#2JK$gb?LT3`u6_%~b(^-+y~yJuCcL8AZyD3!hCvVj+ai`}XOjU02dC?Dk`{Bv +zt)c;dFs>{Dn9nd^hIp@hZN?ofy)M;2fT2M!%bK-7nKp#9y2e+D0QHQkzkmDNV8xS< +zm0YW--7?=*YXL$ai2t+R@pf3T+}{tp=G75+0__f5?z>}Z0p5D_QWo3>L{Rmq$=}HK +zp&}3guxYPm?|d=D|3TWZf0$UXWG9iEmPE&dM8d +zgyKOO14%P7Y1+rVE*pIiuOrJcQI?E@j92tG$fSiI_wb;A8=cUNFxfn(c7)-PY2_x3 +zWuywwofb8q_btuO2(rt#;+ESkSS5x_C9V0sweP#qH+AW6X+OM?qWTdw`4wP2+Ps(h +z>k`+DZ^7L#^{Q{=A~C4M;)kvNNHF5n0?=N~RGm~NEUe@t>_SB1#47&u7gAq|g+J^6 +zt+c`3MHc8DzOy+{xHO1ydBD0Z1oW+vWjt`fn9Sw^#LWxxIqp3Y(jYgg4;JFSawTnZ +z9^MDtyjv$nQyEIf75KiT?{fquE$+7w)INO0Q;$dB0>ty!p6|O#u8y0y0fi#N%0gat +z4FMm@9}tvW;z>g@5UeY3BH9e#HB1$#Ci$#cvzU1;j3`9eHq-)w13zSS{4sAXI|{e;K*uidZ}-t(@%r}IaC^zX1xO5{-`c-T?Zw*rlP*A>R< +zPnriQeEjiGf37tYs2NxqVM|ggUc*-VFu-j?yoNx`BHgJi9T>_Ar4$<;xTAzprEGR( +z(H7{tCY$eTtn?|91?Fm<=Oyg8=!+pPlWZ^;wOZ)V1BS4OyYizsrhiV&c!*Lq91mfy +zUgp_Ay`gOz<@~0|+*d2TcN1g)Vq951&%BcElWqN@dc{T^`?+VmQ +z&7*)&tiXC7M}y9M9~*3P@=+kZt3yXwU;rzg@PvM^jb`A?(@xgU;Q3E}{!5rIZ55s( +zB5+K__jgThvPD6916qO|wmh#VC`V#g_#Mq~m+S%}ZYO+fe(7D`wcu93H2yRmQ4TuH +zf5Ce{`Q8d}zs98uAiyk!K?gW +zP@91wVg3NPpD$_Lfar`mT#3BS@5yzQhD +z%a+4?svJI3sm^kEH7)oeOz4ub4H^&mK*|6tsk1E+n*{`E{&xLWgJoFgQ>AqWuR>^v +zFkoeBt6rnxRxB{NQxe(iq^7`45dtdWOXG@_jEtuq^H~3tFK>h+j}3eu6W>x57pI+g0{r}ce&ee@6e{3VPH+aL9)3RMcF_AT?k#|{1Mpbpoe5~5 +z7%PX%Wjvsri~zVzB6`)v)urd3TXUUJo~iY_p7NFxjIz=25apR5UJpHRQIu%Q)4B?4 +zcVLuOPG#Z`4B5)CcFkH?uyBFVLk>={2${^}PKAd*|M^dFz<~$EUhj??SFQ40n}ydn +zfd~MKD)YH4}x|?_WRcpmEhI +z-!%-ctp@GHAZ-XjTr6mNT={|UT5rOYWE*D`z6)T^&@M9@GZN;_n+JRCy%*f}=}*yxHm=HpiK``x7DC^g +zd1StO%G=&v@ws38^uH(q&|g-sg{hIvJN6}ZG9UslF)&4;`hMn*R0h_qTL-C*R8#7oF6iv)go*Kqmg-Z< +z763v*DpiAQCIb_r!_d*)4ZXcRkj`YL^(86+O4_F~lVlz`civ#6cSC%1_w+y}lYx$o +zln*~`T=}6AuHx)hE}Ml_FRqHc?&$<1kYUl0b|f^+0i-9!p^(qP+}(C3opv@?yFd%o +zPUx&rC=^2PW6%fOYt~$^}y9JneJi;&Ic +zz;Q~$Td9Di$wUJB`}#tkF9)rLt2RszH2lT7dU{}RaEJ!X==cN_i^ZDjz*Xac?|xqf +zj=#p_BL`{3=Z-$^1h8Wal-e+^IH$>7 +zunRDiEuEZ&LXqCDtFsfvCnjnl0e&a1nim<}DXQ;Ip6ooJf;h +z1HR(MRU29#0x|#*X%(fF!`n8)z~J1bT6f_|<4;0g&I?$e9LWTX6iQi4CLzu#9LS$-q} +zxqJcA=`;~eR3ZBMg+e^e!NIJcDq9r?7#$xcJS)I&gc)*>U60y+y|{`C6gN22V^-38 +zzzk6XraHSJmz@H)kOw=}QFX72ALv`(eozns#|6vAbjdDUa>4nKPES&X*kmdtCTg(= +zb|L}a`TqC8!AXxr9l_?>l8>c*GLso!I0u_CxaWcIz!jHXMCP|#W}n46DegPxx_y-k +z4)^GnoPQon@l0*P!y2I$T;V~=F|6iDg3md0cXqXY4N!7c0`(a{y +z6z1=?C#_hO2K7IG>wXa()v_RETQornjsy2U_+4gm8;RJ*LXwfn +zk4XxNs}fFZyn?H~ov>&nW6XwB3gCf<9u`3!og!R$=_OR>*S>W>)!{O+FUAbINPmij +z0^IxHLvY2#7nbE5%0aoD1dZSd@uZ%A-}~QBY`r2DIQc$z>*wfMTH$+opy(7Koz75C +zu6qCbY0^$jWy#jVs +zjTn^(RLkGcD5%7tuLm_?tGz=-JAhhr;dlMYpHHm=Bq$}F2EuP_Z| +zG;qK5;6qy1jElQ%Wah;IL~1b$GzWm(o(&eb4i^$ss@|eeuCfY!^MP+uA5A^6C?axT +z<2_v2?)%O|aM^_yz*MGroIo{PnZGZ+;tKKJ%*#lOw{Q9AM`)XK`{(ZZ=t>vi+Q-?3tyr-ue|1J17H63KmM_gz^6a+ +zIRMESvn^4vRv>3DCVIVx&(E*ba6LO#KU00b_zjn{P +z^bAfkyoM7P|60bL&8Eylwp5&XlfjXB_>fpA8GvHp9r>5Z6oABBFiR2$Ahnm8{Ekff +zr1rGTQ<;OEuyKP7_dfU?;u??>FyEu>5%{N|R$S@lPE&?rX4eTp}Oa#$wI3<Is#Byy2T3se?IVFkB9KFq;A|H1 +zg#wICOw^65*r23J=KB-8SGET%nq2gWr=JP5Wg3En=bX~N!y1U#?v4OCo0#uV`bs>y +zdwZd$uMavqJA8WBiOEU0`4gWc7E=x81WD{2Jpl_vN9*?0ibLy@C00nazsF~bWE3RO +zr0x|Zi>gvADZ&6mJj6a{1U1(Ve +zdj1n1|0o$?ArYYOTQ9C+0+m?I=Akdca+hpZ~b(QN=pxxFZ +z02Kgzy;0H~+}Ca&8G)Byeg(E|+pY(dn#^j_GMVz*`z=;|JQQ(IdE4+ghcpR}Ui~e} +zN~2Iw4PKnkG^tghQdvLgcNo3l_~d-NU9$r#0Wb-2joZ^;#t1bs}%2FAw6U^1Ph +z32c+_P_#k%XeKE?$_>x}#8!M1~!g$CWUa-K0;JA +zU?f~I@h)2_Toxmzrta=8soL}EAp&Ws)xx|k?vWGFR2Cf9sUB!sNKI)gf>M3b8U*Sz +z>lUk*C09>Q90Sr&6h>CVSEc(H75IQT^CJ22cXo78RNzW&OxNU5t9b17wxA(E-OfO0 +zX1aa*Fb%Gb&JH2~N@uU^_=PM^@T%s%6*&V`L7|W^m&?QYmtWSCR|o@H$zR-Z;9hpY +zzWY$BDb$1a^z`U$Ly?SznB)$GUc6XzX)xnTM{6BJwPAC7)~f;TtH;O3DMJmaNPD`w +z!JNr7I0BPea6CogijWmYb*kUo))WSMzmZ5F9&l12jygIGLSU3r!b-%CT4gP?!2l#; +zjlVNS1x6z9zrX)|#d60Ue|*`=4@$Nn(}Yh8Flb6!lqz+mZ~)a1kV+=uA3ymC_`)4` +z5En3+PLs}A7z7CliWAby%<8Q+c#Z=aC&RnXIR{2YM@br89t1o4v5$WeR_1f$O}B8t +z0;&%OxZ?;7f@*{I*32^?5^Xwg)DcI*F-II;_1$Dn+wgle;z~>?4wI7;Mxf|!SL{K#l8at(3qm{FnjiF$mX)t79SWO&cjmn!bTe&N5D1r +zfHH%D{(ebN%fDYaT&-HOmJoI5;q!2UM;Z0yWp{OQ0K8JA)jCtIEs3Ad20|J%FSWEK +zpueMG$TmuFfc+LOBFX>cWCm8RSqG_9CnW6zB$7!(79fk$gcoMLxT2%c&lzt!1G3pH +z9ChfS#6gq;)#}RCYv7Zg_!zOyAO6TkrN6);n*4pVB~aXhrQ39z0>CYgS +zn<{H)q+O{PgN6Ga1S2D(G=XOi3}}I&M+AItCGL}8RkVBmJ>P=P&Q3Da^4_mDT!D-V +zoS7V}D0%7boy8#8S@ymXzJZeQGtFN5x)Yp$L1k&l{4CQey#?S<%48|I0ST5scYsUI +zmdh=i?k|^eFfasyEd&YgrK>?HW^poo>K{J=pTGSJkjv*tYTSoWPb|M4T(R~zkP&~! +z9(xQNeb`cM?N$j?tNSlrLcD7dPG +z(A}49n^W1u-b9;`{gSz#COFYcvz+? +z_z0~hUkQH7Fkt0wSL=VJcQs@@`VR}4-rClsH6fA^upHoo6noPQy!hfOc*DU5%hi~X +zl%J7g+#)K81KDO49COSuaQsn6*1Z=xV98<{ytjV(pWxu_`M~Ho~k9td9&g_ho@`KT~=pcvv7cZorx83?__{c{;MkPB^2a!Ag +zEeFv6rdV)@MMHh|amOA9#~pQq_fBdb7pPZtN`I&zi&-bGP!Nd(03CHuh_`)sI}FaA +zL#ea7H31MjI@e4hhesa%KJCA$588~lGOp0I8psT~2xta?rcp1iT?KnDTmmpM>`?$q +z2qKvgZ-7gV0}YUH@{>vspl)0#Sn>SoN9!G+umiLJ>b-Ub2m}Pt824Re#!be|4GzGf +z1^dD=haV0HA9Mii8A)%CHvDoMAdGb!IPUo4;kcuu0Ip=9cm^~6cKvO8KN#${coDUI +z+vjeDTsB86H9FCVIl}cTWV3l#`o=dD`Zes4eNV}B#y3KMpqk&jIVK^75#Og0O{BO?@ul$@7bhe!whrE}MT +zZ{Pm_wS|NVositX{hg_FJ++(2H0 +z3dl{kyQgvkAy5mh(3EycnjYh7o9alAC*L;0 +zF@W0-wI#sqz=oIB!r}uDCOOIEL>f9eI>i=53II{%xP5qp1gE$Kz;iCQQ0Rw%!o(c` +zrqx|F_{M$TZn%dpu3l49`KvCwI8wQ$nMyYR{uycNgvz}HV{q&3chHJ~7JJBY;-Kv^ +zB>6>GxbNb-=JN$O`Q($~n8OdN+DpY@mIVo_oUfjb;fqc_AJY@Gr-ATkP(KkuX+G6p&mrb!Oz})yEabp9hHW26wjU001BWNkl=xq15gPUg(_ZOC +zX$b#7MK&gfAmN*!1ktw$P3xlRIAr!w_sers +zY9;W3%1ObJW&S4b1>xegb^8c~eMU<(E}eWR5Tz|%5Lehj!I7(>ScS=(Pcer^eHK`< +zE$NJCbVK_mtLp)LSq2MWp5tT}x@=Ash}Z|Q>bsh~CE=hYf|cNE>~Qfxyan7+{6;=$gkj&?ytFt>+T%{6KxCu+RS|eV_DT#=H +za1KJkibR0Q=zd4RRXm`Hj23>D3MO~#v@#{6%-=_KC#bk-5g?w_3Lw>DfdM7o34Q5N +z07wEO5MF)4jaBG?5=llIPnRoM*f6i0y@L@7>O$!JM> +z<=3Xa%9U7Y669Z(fuo#*co9;>N!i9;yfUuh0*~RUK7E#5c{%*@x4(~QS_vX+0Z1T3Y(CIr6OK00uOD^XT>e%XX)fx>=L(_q#R-Cy7EVB}Vy`XH +zUo>R1B+^kvkVuG(LpfAcmI*ag=|fe+mHJHqJSWp46jyIgiI_~`DZ~RH@?ftOf_>GY +zM9PF0qpcN3j!6X%E$!QM3~JNIJ_?-jmJ=FoGmbifptcFE1U2d9gm1kNu&Mp7Q{_!h&ozyl<5#byN;OBIAua$_>qqVuj5RNoZm?{%o;)c->}36}P4I#xB$_KZp5ZE%`~leu=r2_iTkoK=yP +zDv>q@p24MO1<6Rpq=_OkYT>d6^1dk?3^02!Y=HVJGr4mWqt5%!+9tYsR7`7^+QOg~7 +z+z!`XcfIhjU$Tf)RupW*WiWf7k8JVtg`x-o%2aMY@jM3c=++UaKq0}kdNP@vRGAzi43D<+pK +zOw5VFP6lS7&U}Q+A|_$i`E!LSvRxV+#3>di@(|v~JOW)^oz$;i{rtZv-8fnX +zvcPKEbp8A5C?wd776Kl0!2a|x?)>r>#lXctA5IaLec(FU3LLQie$Z{$*CW?JjwX&U +zenDG&lM84R92xyLVJFGvKy68c6uwAu2omBD-M5{5Qf&P&L0HrwT8l8c +z3Tz>g`;lBIs7hJ&hVHg^m({P_$89xB)TzceD +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 +zf8uf979@AV0r&@}b$PCr%d +zD~)z6ORP{1?pG%v)!`Y%6r>O@o6FORMyfpEx?gX8!{5;H$3Oi!w7qw}{fxSYfmK1` +z+z77FCjp57mUmEPS`Y_EtUrJXu-BjjJ}aKE#`@S8@W18>j}jUqnIJ +z>NV?VuRDA809ajJ(q5g@>MtVuSWt9rNTy1idTME}6+m+W2@Jmlz;hWG-7=+nwr$f! +zY0c+P-k~D;IMPrDGy4VujZ*AcCh{R7VMVvzIkRU84ueV*L))_xu@8e-;gF?A!e0CA +z6PlQGCIKdh85o!ipZ(OwTW36iw6r4Nu9W)9;Du!fdoXmoxaP{=78qJDC`k=q+{6{_ +zfd}q`3og0{UR-U%9((LT>M@IC05)`{QZUdzK!U=HF1`fLJndAYuSO?~I(f+@SDS|F +zM=)I&v-Ew+8^jIg8nBcOOqua%hyz!+EpmtxutRXAtO=Dx8BqR3?9=4r1O*B~p9@AR +z*dCobUj^_ay9wKZIdcYO!FTcF9Zm9R>2Mg9TOJve4u#Rd4@8(w*ZA}5}Q_3U}59;A#Z#G4M5C?f6>L4lEV=0dGq8xoB%74fF+9- +zz%|#rAKr2LX}DzNrZO1{ +z>N=AlkxaDQc4>$6ez=bZ5*~gh)JO&Q^Y8k*xBIT)z{XHnbB6|DWNgeQ6ni}a3FI)w +zVG$tZI;7*Dl7U7Q<@BB8(&?&!*CTBi0DsT=Y5>1 +zK=06EmXb3v#`9~NHo%ho4j^^ejt*EkU4X(vQt7#tjeVo$G#@Y@0~&Hy&QR_aO{7d-dJ=V0N&g)lH%>77kA +zLmO8nRK!&qS{?#wK%ozwN~d88guW8hFNq<5^u!ni-)JR3OTo>XH^HvE?jCEeD7i_o +z2ut?c&luPNXyb|?60RDAun0yo5k&08*6O$~jBYR#G&!!2MPAB_dDlXKLNW65=kfL} +zJ2eF(+qc517hiO#Z?Wh#S7}m9Li6_@Qf;#oi9c_h>a1fJU-gX1|Ilyl_w} +z53^PTjIT$@IGcc2HLzb?`GUUI##K0k;cBMB|NX@;XZ$-v1>yjhB?lw~f;1a3)$6dAOg_SHyai#TnKaL&26~3XP;gU +z`|P`bbkMH{m^W`8g&n)?)1QJit|~(iS8+izIRK~3maZu)bqi`TNCq%5F%B=iv<|jx +z4rgy`g~2&<$X9(Pg2+7}Rs|f;yY0T44&~j(l^?3%Y9;{6M1sf81jGSgWSH?Wpn;9h +z$m&+e=b^j1r{(mWB6on{&tC>!TC?>n58iypn_*;R1h#G6rVF=mLLZD0&<~Q`!rrf^Gruk$5$**RNmSP|sd@bwg9a +zLpKVD`q=}ss@8&m8_<3BwT&CfCgEt~N35>sv +zHr}r{Koi}kmOlmQ%v8lU!$`U4dADiPM%Zhg1(b(CK^s?osD!JypqcRiLMJ?jrnJ?H +z@c?F=JMeTtZ(ko}Ia;!RXHy3C>j|2UD`KIW;|)kzkj>m@S_aSr6=@541IEg;O^&y& +zE%?OPa82cVd;4H?WLT%~jvLyzY7Hpl3y?45Azvs!J}-`)2}-sAAd^Mbk{KwQ$-unq%)Zo`oMXZ%1qKeZ|=OoNbg43cY1mtlgUsdR}(*NT=}6AuHu4P>;SH~ +z<^#38bI2Cphd=t)n(DacYj;H|*NWbN4Q~O`6XQ_G=V0z`yOU0v5_z-;pxyr1@HXh} +zp9S;g?@Gd|OqD&Y0!(#_Q}GpdBH^`{n=q4o*7Z_^Rbv(HW@W +zFV@x5LqRZbz>JPhw3HzQM#)9V^w7`{#n#7Mg;72A@VTRpJAu-a#|3R%H6L10qtePC +zLxKg^T8N(R%CZxr$Hv5v7;?IvlwwT-bltTraElJb&<7r^e=5>#|Ezx4uwjEQ>TMfW +zC6ATGRa{WZ767*~UdSep8J2?4kVO&`l@yi9IB#6Ok4|P|BtY?!p-6uL((9|y5Up=5 +z?+nD0OGJ&GY*qhvgjoZFkm~FJCz~s4_gx9fttf3=l|r*t-&zO&_JN~gmQE2RCMrOH +zE+`_PC%2g4bcN|@E$RXAD1xd9>oqbgMH&Y!=nGIH1VovSQ^*V2_Bu%Vm`J7|GnJF( +zt91W9)@54{?W)S-}#lh3?d-t!*$ozCPRJ3u^TSJn^_=Blj$q%aulY<^U%iAH5EU$N{vXFF*+g +zfYwUPPC#F8FJ#l>(9=JANA_=r0IQJlx{WJ8RKitU&`ft=(i->@rq;K$ivq3a3s6=I +zkm~G)Ty_fFLLTf?M?0%b1!&`{x!_7inr4{ihRH3O)mAH}Hw!lel2pB`w;v|PM`8YM +zBDuuQ1mz;bv~g7m(-TF9tN$d+uB7o&SSOQ`h%;`ivnrLbc%IUsBj?717ND%%fD{av +z>E=mD$h>~291*IN5gt5;!y|$a +z%$i<85GStU0^Uq#pfcWo&1j1QFCkAm8UzPmg|M~qT6b?Rq!J0}?Cc=VsVFc!HVRKa +z^9(FKdj5MLlbL=(FgCg!dV2dPXI~^KH>=<#6(g>=;}Tw$U4I=E +z3psEK4t(a;TWJe{gTA|~3$~9;FCmB(S22N#(9;wlV6j+2zM2~mXoh(M;!1#$?ppV? +zW256RGCWL^-Lh;_6+iIZhvD7_A0lUy^yDO^(rPxKzdo+CyY99dq?+ulRx7S>pcS1W +zytrx=>EM~o;A0=V1#bTEP1I-P3eZ^ySE+5=hhcPVys7=4PF&Rva^fg|02j;+frQd4 +zzB?g{x>GJDU|9yhYomloa8>_g8Mm}=tp*i^fcm{$R~m;zWAzkEIGLV;fq~f+ej0V= +zxKeV*f&0GuFmTs}%P+d1sapf2wi9C`FmL|uU|Tj#%qZ^=RSg(cwiaBeXD27qFfl#> +zUEN*el;bi7KK9X%07k6-+-`28>ONLr-rHO+;JAr*PnwTW+R7^tszU4^vZFO$fGb +z-ySO=XlYz2Az(AYf`?^tYMRY{xE5gPL5m|dw&MBKu~OTz96(t9yQrX~;I1T(IDlO_ +zu(4ho#CF04x9F0})V;m)001BWNkl(jX=xON1a23Y1-TUu< +zFgf0!pe^bKtjD#eFayXP1NMz?(Q1g-Q6t^+#$HT+u#HU?gaPX +z?l8DJ1b2daaNT*o-93Bu2TY%5ZujlJUAL-Q%T}$d>b?EN6dN5?WU|%O?yQ#sT?Wv_ +z<2|Mc;hLCiaa^lG6!Ht}ujNaUG?HoU7r240=UskEp57Lm!25;ISd!K(Kt^w>YfrD>8a0m!V&t*r&3}@z?OE82HRY2T(P9QmcA299FdM^hr4ee?8}TOi2)&BM15w9V}Far#%JC_nq=@MDNad +z8dGHbt;qPfO}1PA_p9uw&C_9F6K8J6{4pciRW`zz_B#Opyew2cMsmG5zLD9%bi+i| +zts@CDqmeGPFpIx4@jrmkN%2sTgN)oC?AfqA>IcrtyjNcwce5B-A}0@|aN>Z^A9NYx +z#k(j@LSd9kQP?`eAdBE5T^kP!cC~J)uou2ZGDxQWM2jDqVer8g3nbXn+wKdIQRx1= +zNXB7Go(kxY8-(`jDte*UQ9TRsm3;Y`dVsJZG-Ey2YF#~`Z9hnv$f#r>%;U_s`bmij +zT^=5Pcc~bynb72WfAUr5J|+ZU%?aT3(dWBy +zxyNKo$FF;eoB$vn$Wdj3f|c|`!ob_C0#&`NX4cF}&rc>wo+5@}axD4b(S@&m~=4&fc&63C&AyY?qS(DO;e +zc|m_J)}zpok&%@I!So$V0Vrk0ra&=tk3F*6$+nZChufysS~6e14%62^)nMU_Hf;CK +z;M#sPIJ_TASft@WxJ7-ja^n4}Oo-wXTapsMSL6DCx(elG3+E%=k4!!&YUq=?YVia^ +zlpclacYk}w(c@~i{C&_;)dvLrfRGsuA8@`#`@gT6MF~OozfmdRRt+;*=b0y*B*oA0 +z1jCme6s637+Z*{6hna3udvkml*_H*6B;?L`x2Rrfg@8+5W}Q8=iwd-ob_h*&nE5aL>yp}l9N6cd6KP9b+}7B8gG<*f)z_z$XS>RgWH>tj1&0O2+SGk` +zJ{M@Rw|sD1KIU_B>+1B!;gjf?h%<2ghagxg4)kY|YqXs8xhWH1anP$7>Ocx#0?q92 +z<1R^8EH3>!l>LG(D`Xb0rB8wiREzr*2_dLdA*-#wSptJHFw6&1En9I{o!y@L4ZScR +zo<~uZBi*1qy5yVB>i2$E5~VTm=f`5#RnaTvdyZk~CBXjTF4>h$%mdtnHoNhxaYS3Q6LvA2p#!Dfx+|{bT^q +zo(1ad5PKNnXo1D0BM`@^>D|?P!SxFKTd5~AJJ4Yo!$p_HDM(0B4!#_SbLIq>Vo80k +z(>eg`nb^JK9FP4Zs|DKkIK+&xoDFG`K308?*-;L(AQC$q*s;M%noK)lp~h+vD;lCt +zs=gZtp9wmd(0C}~4Up5v^;{N%cYkC=Il_0bK%AwF2K-*8Z-t#)AzVp&nv{aCc?ay? +zFnR@O>f=7Rdkv&HNa*0FqI+L7dwp;c9N>!SXa{Gg=Ssnq+=zTkb5r->rh5G47N8Z; +z5Q;a_;(JP1!U^3-^}PKKf_DJI@R+&Y#uOB51tpYR<8ICTo%8sU7B)a3QP?5P0aZ28 +zLSgK@<7WIsiH(Hne_OPPov5%Tz=X(kAj9KQXRit|#2yiU$nKm= +zC{Z~Y7&xu?NrG2esz>`%Px$&R4@?h76#`?**m=0**ZwQfGPdIDg~CELhv6Y+O|&#T +zTdlAhRStWZT_`~*NQ@F=6kq6qNK~dxGj0$XznBO2(c`BH%<-vd9eQ>o80a+u_=43$Oypz4 +ze!WRIeW}ajsS#&oF&GOg>|2t8PAN#FvPTj4Mv~HsD(TtG0L>394T;?;YK4^&!wM69 +zvs^Z6MgDj`+qS1a6QRuu|AVAuB#7NWFshU?gZOiODe+?A?8hoLDj;f&zX|fljgkXZ +z*|7;F0fNA-owELoZ)}M_T^|A)^1BZv<+!Giclqkz8q!ihe))=WABHm}X3d!@lf=e| +zWuq?P;+5}_F7{D3E^2-fr>a34qR7P5&=wYVN(Py248Xw!|C>@t8)R*4yWg&BtxSa0 +ztpKLR0jwthVaf-=93PNoAv#B5TrOEu^u3la6AFIWGJQKPDZ29bxG&gV^zuC!K~dp$ +zs92g&++~Uam$VwdUwcgj1KQxri) +z{RXZ5ih%7xu3$#4t^X{i<6RY-ddB)IfsLkk3`_3*&4JiZ7 +zkP{)PG#qUU?UF25aRrq?2r%DP +zvq&e;6jQT>WWu13t$5|)?TZ?c)gt==+&|bkZ=5L&%=7QxexV7RdbcX6>UngWT7jf9 +zwW**0Xs<)u3-(*HPpEPrPClJI(FXFQN4g83e7>%4!xIy=zuWngDS)^1Gs%T8AfKyC +zxU3(53$jqLd|r{}u2?%vT>gfcDpzC-nrjz*Jye539BP>!*PQQy@~@}h(J!~}nNNa< +z*ssP~@S~Dm5IB9+ZF^HBY&o;lbN?zFGO*cI9Z_3pzre;IkU^)F$;A0a&?# +zbUCd}1!&ed`TV0Uh5OvUHl7qrPa5mv{OiR|T2;Nnlefk#cQ+n?DLUl1?jh8x_*?54OXU51)$O9A6DaldTXg3EnX7GB8YLbt5t9b>YKC!V>+aMLj+{3oS5= +z*van&gIBeK)Es!6z +z6MeK`-a1%!?=^4XU!xej{gV|7mNzPM~cJxs~(ZUl4nNgud9U9_|l; +zDhigT`4wJBJDUt8^kvF7T;XdMI^rGTGZQwmzBB4ZI1+1D6wQZ?ldy{~ZD6>+m&=|) +z7)m&e{n0+H$vR~@0r7t)Wp-q*mq*P7niT2_ce8RCFS +z=#?wi0F*5|8EDHB)bsSA?;kz=c0L)rL1;KOo1M(ei%q6T-ne#_#~#C7JES#nNT2`- +zk(BeIQ?zhji5zXf$XNdn9+K8)Q?g15)G87~;eORCvTBo$!pX#FUcoTKAzINd4yWZS +zi|nE|U)PI{ +zbOm_Gt$4^qyn$!>xE@W&bG!%cmENoOYvYr(TdPlaSyJDq<@5|UHvCJ_*3j4wmleNl +z7&f*_wBh2}K3VUOo9&Dn**L_4Y%dsE@>b~LWR@r;arIRp3pAAyA&;$+bOV|H%&hdG +z7<>AXUJU%eXL%E&}s{vb+%e=3Ix<*Lcd~YZ~yMQwfX>nl@x>8h_w}7A^7KR{rO=~ +z`F&b+AaK)2Ucl?B2V2MGl1wa^i8&fR3SP7y0CNGzK!w~B6Ua$zd4Xxz^SiQ0_hS@M +z_IK;v3l68P6R8KBK@8I&>rFw&9;m^zT1?2`6KByv#vJ2j)DEe3E`u3XuuD1t+&9cw +zPlY6t?a*oMdsij~($I<>fidLuGRDC+`j(FHM8Y3KjYMhz8k-`gbtUhV#_hg($>U-}V`5nI2Vtqh%r+_zY=& +z9}?wn+smeE9K3FF4`AR;R_Od~B=I?|RN6PzizxZetEeZnCZO+LRK{TJy|E#UTKyQY +z<)wTi$Z=?a;#+{0>Cm=ITQs;`qB|@Yw!?g|9E$d-D364f`YGCKCxZ&Da;23Ibc7OBk>vJFOzb_|$Cf%3 +z>{H8=Ac-dYehZWFYf?+g%z$i6V#f)4$(eTFH3@&o*Tr15v({Ps4c}k@1T3&f(WZ} +zc0mM;dZFH<|IsE^vY&RQPn<7{rLd`%&s`aW>+azW +z0<&o(m#EUCUj66KI#H$Fe6}^Uqkjb5#^pUB){(h+X2OBn7wBL+557sl3>1LV?|8j4ZL$Fk~mko%36*hLoPN!KNc8vfG_TODso!bjXmCH5ECtf<+dw?m3 +zd_0ojMWSDv@Mg(avrB@CO%o_T>enDEx~o1M&OiAC&JOHbYg1Ur|q^7D=CL(Di^i`0_s +zp8%xO***h$)z#u0PC8u-30CU5l(dYOwwYd?b +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 +zoK-~74%^CymA^DW!sRi@1y5qqPjw;2*@ +zrinieTn02d5qP9jILU$!D!8^cStz0FF3IFSG^u$yUG4EhNBJ$N3KBE-xDv|VU)pIP +z3#nfs0y_5Fe7}B0f=O$(SMW}(gDFI;p*q7f8>=91a$un=9HLl(x)j`3J_7VdA)yJ= +zU)VL~r(tjW6ia!*>H8NrvfL_j2LHf}N;d(aaVt)$ +z%h;i})TcAHbc*I$$jAct6!_*T<}qeX7rl?p*fXoSKc3rw_01tdA64;A`e7lKPI9Jk +z+(;97e8i{{M%z2*oODu?)Eq>V%0P&HSsDqI@R{li_?3zaGj)+VqW#5S3DP%+hyuoo +z?cH-_y#drdHpG=h`j&is)0);C34HCOc6X(^*9d{)pcU4_VTH?3ZsCsKfR(HHIuj;CMDp@4aG(>CtG_7 +zoq~iZyOUqY@@W6;$h<52%g?;jYXhVjuAx5@wXIcW4#9!uzI*^w8ROWktR@*3uMj@Y +z0lyj^18JC2E#OYxtnbi}>q%=7P`7&dacw?ydA(<*k*lhk@C6r+I+o_Q$L3?q1VT8H +zWc@LuqK*V3xonaHry=TRJkP89CT3-!(bO0Zh+;@ztxKDZl9zlvEi{#e +z%gY*x9KMNC!~jjH1Oq(`hTPa0vKCX!5n5_Fj1e}G<~}Y3BIOK;Rd?2QL=zi)<%x#{ +z*H}i%b()Z_sm<26f)T;-buhEZc}4V9R9_xK43Eh35-EyP_2MSXQ>}4h{hkWVKkJn! +zt`drnt-iei2ZNE)lvbfkiGH(2cWVWFdnAce+?y4mFnQ{xOI~Xm=~mp(@tCAta>CU~ +zS=txp;h?iCS2F10h<t6(Zzf7WO^~MDSL@aRJNob7g!P0~X}X&jRR<%P);B2GY!&gbQx!JC +z(J^bOTudT{`!*I}tdd9 +ziI)Z;%HJ#X-t9n|8sM;OyP(6v7plBTZndH$J-&Iq*Zi5{95BZRal$f;G6V5-YOKa9 +z9~1lOxdrbz(9eJc*Mo~(|KMuxBY +zv;1B@kKMlar<(VbM#J|p441xV0cBg=j~k6|abzNf>Q&M5KVyv;n_+jjtAjcdH8EL4 +zKFsl~HankodV^9bxbx4x06fYgByTP$pWwJ56i@tF;;$FH>(HDfNeB*`OtSly+|wi9 +zzQdJ`4MQDrU%#s|0X$v1&>Sn5NNlX_uD<}icSAFCx09_}FJv4pY!PP15JBIqnR7{y)Nhgn1T3HE?MdDG`W} +zzQm3gc%)E{1!aK+H%xqaVEranlvwt)H)KNJW-!x6oA|I$ybC-&Q>QW&Q5aF7%CNXN +zvO-GOL0BGZM~h=?x0|j_ux8FDUgR;m{D^0?O6}%pMvVndC|S_xp9a?MJIZ(cCH>@- +zCddXQ2YYv|fqW7b?0%b>R;^af?hnWHF9&HjS=<2jJ=!p0TZJm`?p@VLuKT)ERzXs) +z$KAFaX9w}NL!nE|MLHv3_1AD7U}zXV{z|XXlI%zxsyz*FIa-Hm^nwu&O(L(0& +z`IhoW4n1~J!A+E{A(A2g&nkhTb|gFjf&I|O$5QI`vG!G^+DE_MPdNHG#B181H`mff +z?LICbOBw*R>qc9~s-l=;-BpDrn$C`4paXUida^K~<|ND5%Ki69b3p(DO +zBj_$#&9d@n@gI&QSsaNYQS{iDpZ_)yfQ5B3U*0%N8n+!pc^9zzP_1#4BN`;KF{ALkvziT!J-yS3-q>u}#@kse3DuWN +zh|tneGD3eYJA*7P#kx>gIK5T4eaYQ_!XWBK(8^1PMfN=kxBmhKj~;|V{}W_Lv?DCC +zH&I^H<%yUiUlezu{B>-gbsD>7d#JpY{$;J1YHiC|NwTbAO;m|Y7*RHk_(%6=j-c1h +z<*lowgWu(|6KJVYd-o-EdOigW(i$*3P->TUg6P}WObn@KaXvZp4Z)F_Nr6$&Q{cQ! +z=awOlET5pPZ_~*h)`|y0{HcQGMO7=GB?-6^82jLsNfwV4eyZw4Y>TS*;_~ivW8JSx +zo^bZ<&@d&!&eAt45>k@7NmDx`&ArAZDy7Of!;ZG0|I?p2!{e;Ya`S*2qFoWI9SZi= +z$LF0ZRJu&Hwk#>B4p}+U_O(+X?O^bUkEqQsp#kwA)ky0V;rva0O)3ovm@$QR0hq18 +z1NdClh3>M4K10aFRoK*NZop17?evE?I&Xsj*@-BJI}itikkFySvpeW) +z0uYCRm?u4UXGE$D%hqD5N5v=oT-^md`=jPWhqB9~V(xzz&P?=aq$wzEMo%|2`P>e< +zRy=DQ$WiPT+s3INXu!DHlte<<;rFiI8Tgyg4 +zp*Pd*lLE#pnkHo$5&m0{l8tR@^zbXIERLZyna(Fjcu{^eMvrY;W5}MR7Gm`}5#izM +zlS{{J%apbNyUI%EhdY8ojlvbkbi+lrv+HcuF0ot)UWtKlC<$7ns?Nx-+z(9Cx^#!` +z#ORw5P|51aY+QWs4|Vi5gN32tV(zo}(dgO39+pw=$G*)pL9DTzppL*6&EV#H4+2}& +zjn&|b3(Co1g+EcaVlQ0JJIPpFFfvopJb6ghV5u9r0;)ruu+)jFo|6)ddiU$9+w>#XyN)t;8WC`N{dP5k|d +zw|tKLCi}ns4u@;jBgysIbHT?aK2=Vk_|YRzL+un!lAg=xEXXyopW~E +zQlwq1fe4G_846n0sn7yd2ud+vF5aZ#4}j8EZdt?05V-%rc*pKuHF2a7eC%rr +zPAgf-(Tl(VoU3Z(V*XZnsJfjhFhN{i(&K4B`vcxx{Kr9k2{317ASYGxo1L=yzPLmP +z#tK~7?!d(M8;y4^Y}yzHoQrOLHVyktT8srdtD03)AwZfBA|6?4K-p72e0 +z`K<+7Rwsk_zwZ8IJGn{*@)hH8Rj<~hOK9=MP(gsDa+ftVLmzFP)1}h^=f}uXHEqa* +z#FGo2$@aLY8bqj74i55AL{u^kpfR^29iqh)qnc^KTqx1!YpT6#Jm{i5BQy~{#j~KQ +zIp95><3^YKa^>=AU6*V6LDpcRs@>v=7byW@bQe*zg@@{__Pah+kAU+epsu3>_NwQ# +z_xkS$S&@?PsdEb8Kz#(}W1sgFrLDUSzOfMvXi8ALMTwUM!C#0pq9p2iWlGPKGv_li +zB?8j$CuD;3Wi?$QBP2+3Fi#EEA)0&Qj1>*v@r6X`FTTUun!e3_JUCsy5S_f&pxZ03HJ +z7>6}l%`{~Uv~LLG^}NS_ZfNAt$ZG`<`@5S%5BdUHomfFP`;T*8yT)*&&$LDnM7)mu +z!^5ykC}Id0>}BbRQQSM%du(hesiFE!HQHR*=LO?i3|W5qN=jG5B6{H7vqd)agjFl}*iv +z`%#I3bbWivQQdBa^oAbZK5+(XpiImJ$;R3@I-1bW4$onrot7@QDyzl=6$;fP#i6H~ +z1Xm9gn1O1LIs!qAJv)C`L-V(lp#=KF^()8ig!79 +zVJp-Qzl0Mk-u8iO+7W_E42RXX!{~<0w+c`2V4y8S*x(PHCM;*?PwHv!`d#f8Cc8SD +zRd#GUJl_mC8g#!-gv{nirpXP#yv9>sU{|AhM9CK|)L47!YVvew(-za{en}_bZdpVB +z50$BYM4k6nEp7yV5zv)%3S|?*jbAc?DtK>ssUYp +z9#3IyuxxVRE4o{)%LVA~5^%6;A`LlM4JpMUjPChMOH7}{QI*)Dj%507cVUFEE#i0Q +zf>$3Ff@kDLh%^TU%5QmS@ZM+!qGo=X6l{mO6n!WwGFtP@X#y}AB&rM-pcmgYSCc8c +z;!OU(i(k7wIRxpHi3WUR3XYT$G|Qe2isBpERfjWrpLI*|&MjQOmL1DMU3({?u|PQg +z!4t9lX>~rwR3%lS) +zZb8+0Joq0p!&0PNk#k=Q@-!7b{)}(9$~T}o3a+li6zVy-a(nuyU+Q$NBLErCSEree +zM);`=MF@|a@M=p5ih1E%K;n-h`P{%?F75|>A`NDz#nKv; +zD5LAKS2N;|#2-I}>^<;Y`9mAf=u +z^>%n4_0+;>uWMVAaf%?Xm}q+NhygZN%LvE16qL2dZKroU*S+^g#NlB +z5KegTx!!%KK075sGA-I@bh0B%b7V#{e+*4q5aMElY1nz_MOH3*`Q-M>5e+{4&Cg>qSUlK%j|^h1AX!=UH*uF!y~EU%_TIAoDeuXwQnC`@Tk +zNQXFyRk#cS!Q_XpK!jHQmK|b;FQ3AjRLU`@_uU+0fY}OsHGUBp43I*1`VEtp +zv&Rz)Mh>4=;$H)%A8R95fI1V` +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 +N0#8>zmvv4FO#tfi_}2gc + +diff --git a/source/client/gui/Gui.cpp b/source/client/gui/Gui.cpp +index d8e94fcdf..38ccbf65c 100644 +--- a/source/client/gui/Gui.cpp ++++ b/source/client/gui/Gui.cpp +@@ -10,6 +10,7 @@ + #include "client/app/Minecraft.hpp" + #include "client/gui/screens/IngameBlockSelectionScreen.hpp" + #include "client/gui/screens/ChatScreen.hpp" ++#include "client/gui/screens/PauseScreen.hpp" + #include "client/gui/screens/inventory/InventoryScreen.hpp" + #include "client/renderer/entity/ItemRenderer.hpp" + #include "client/renderer/renderer/RenderMaterialGroup.hpp" +@@ -307,6 +308,29 @@ void Gui::handleClick(int clickID, int mouseX, int mouseY) + if (clickID != 1) + return; + ++ // @TODO: add InGamePlayScreen at some point ++ if (m_pMinecraft->isTouchscreen()) ++ { ++ int cenX = GuiWidth / 2; ++ int scaledMouseX = int(mouseX * GuiScale); ++ int scaledMouseY = int(mouseY * GuiScale); ++ ++ if (scaledMouseY >= 1 && scaledMouseY < 19 && scaledMouseX >= cenX - 19 && scaledMouseX < cenX - 1) ++ { ++ m_pMinecraft->setScreen(new ChatScreen(false)); ++ return; ++ } ++ ++ if (scaledMouseY >= 1 && scaledMouseY < 19 && scaledMouseX >= cenX && scaledMouseX < cenX + 18) ++ { ++ if (m_pMinecraft->isGamePaused()) ++ m_pMinecraft->resumeGame(); ++ else ++ m_pMinecraft->pauseGame(); ++ return; ++ } ++ } ++ + int slot = getSlotIdAt(mouseX, mouseY); + if (slot == -1) + return; +@@ -646,9 +670,6 @@ void Gui::renderToolBar(float f, float alpha) + + m_blitOffset = -90.0f; + +- // chat +- //blit(width - 18, 0, 200, 82, 18, 18, 18, 18); +- + int nSlots = getNumSlots(); + int hotbarWidth = 2 + nSlots * 20; + +@@ -670,6 +691,19 @@ void Gui::renderToolBar(float f, float alpha) + // selection mark + blit(-1 - hotbarWidth / 2 + 20 * inventory->m_selectedSlot, -23, 0, 22, 24, 22, 0, 0); + ++ // chat and pause button for mobile devices ++ if (mc->isTouchscreen()) ++ { ++ textures->loadAndBindTexture("gui/gui2.png"); ++ ++ currentShaderColor.a = 0.5f; ++ ++ blit(-19, -GuiHeight + 1, 200, 82, 18, 18, 0, 0); // chat ++ blit(0, -GuiHeight + 1, 200, 64, 18, 18, 0, 0); // pause ++ ++ currentShaderColor.a = alpha; ++ } ++ + textures->loadAndBindTexture(C_BLOCKS_NAME); + + int diff = mc->isTouchscreen(); +@@ -704,7 +738,8 @@ void Gui::renderToolBar(float f, float alpha) + + int Gui::getNumSlots() + { +- if (m_pMinecraft->isTouchscreen()) ++ Minecraft& mc = *m_pMinecraft; ++ if (mc.getOptions()->getUiTheme() == UI_POCKET) + return 6; + + return 9; +diff --git a/source/client/gui/screens/IngameBlockSelectionScreen.cpp b/source/client/gui/screens/IngameBlockSelectionScreen.cpp +index 16a91edf3..1875b5ba9 100644 +--- a/source/client/gui/screens/IngameBlockSelectionScreen.cpp ++++ b/source/client/gui/screens/IngameBlockSelectionScreen.cpp +@@ -7,8 +7,6 @@ + ********************************************************************/ + + #include "IngameBlockSelectionScreen.hpp" +-#include "PauseScreen.hpp" +-#include "ChatScreen.hpp" + //#include "CraftingScreen.hpp" + //#include "ArmorScreen.hpp" + #include "client/app/Minecraft.hpp" +@@ -18,8 +16,6 @@ + std::string g_sNotAvailableInDemoVersion = "Not available in the demo version"; + + IngameBlockSelectionScreen::IngameBlockSelectionScreen() : +- m_btnPause("\xF0"), // 3 lined bars +- m_btnChat("\x01\x27"), // face and comma + m_btnCraft("Craft"), + m_btnArmor("Armor") + { +@@ -187,31 +183,21 @@ bool IngameBlockSelectionScreen::isInsideSelectionArea(int x, int y) + + void IngameBlockSelectionScreen::init() + { +- m_btnPause.m_width = 25; +- m_btnPause.m_xPos = m_width - m_btnPause.m_width / 1.05; +- m_btnPause.m_yPos = 0; +- if (m_pMinecraft->isTouchscreen()) +- { +- _addElement(m_btnPause); +- } +- +- m_btnChat.m_width = 25; +- m_btnChat.m_xPos = 0; +- m_btnChat.m_yPos = 0; +- if (m_pMinecraft->isTouchscreen()) +- { +- _addElement(m_btnChat); +- } +- + /*m_btnCraft.m_width = 40; + m_btnCraft.m_xPos = 0; + m_btnCraft.m_yPos = 0; +- _addElement(m_btnCraft);*/ ++ if (m_pMinecraft->isTouchscreen()) ++ { ++ _addElement(m_btnCraft); ++ }*/ + + /*m_btnArmor.m_width = 40; + m_btnArmor.m_xPos = m_btnCraft.m_width; + m_btnArmor.m_yPos = 0; +- _addElement(m_btnArmor);*/ ++ if (m_pMinecraft->isTouchscreen()) ++ { ++ _addElement(m_btnArmor); ++ }*/ + + Inventory* pInv = getInventory(); + +@@ -288,13 +274,7 @@ void IngameBlockSelectionScreen::render(float f) + + void IngameBlockSelectionScreen::_buttonClicked(Button* pButton) + { +- if (pButton->getId() == m_btnPause.getId()) +- m_pMinecraft->getScreenChooser()->pushPauseScreen(); +- +- if (pButton->getId() == m_btnChat.getId()) +- m_pMinecraft->setScreen(new ChatScreen(true)); +- +- /*if (pButton->getId() == m_btnCraft.getId()) ++ /*if (pButton->getId() == m_btnCraft.getId()) + m_pMinecraft->setScreen(new CraftingScreen(m_pMinecraft->m_pLocalPlayer));*/ + + /*if (pButton->getId() == m_btnArmor.getId()) +diff --git a/source/client/gui/screens/IngameBlockSelectionScreen.hpp b/source/client/gui/screens/IngameBlockSelectionScreen.hpp +index a14e41fe0..097af0f64 100644 +--- a/source/client/gui/screens/IngameBlockSelectionScreen.hpp ++++ b/source/client/gui/screens/IngameBlockSelectionScreen.hpp +@@ -46,8 +46,6 @@ class IngameBlockSelectionScreen : public Screen + SlotID m_selectedSlot; + bool m_bReleased; + bool m_bClickedOnSlot; +- Button m_btnPause; +- Button m_btnChat; + Button m_btnCraft; + Button m_btnArmor; + std::vector m_items; diff --git a/03_14bb937365261fe97c2e41196d3b636e0171a1e9.patch.txt b/03_14bb937365261fe97c2e41196d3b636e0171a1e9.patch.txt new file mode 100644 index 000000000..536565452 --- /dev/null +++ b/03_14bb937365261fe97c2e41196d3b636e0171a1e9.patch.txt @@ -0,0 +1,190 @@ +From dafd4c772a1b4bf86379ede612487cd0ace01691 Mon Sep 17 00:00:00 2001 +From: Un1q32 +Date: Wed, 18 Feb 2026 18:58:15 -0500 +Subject: [PATCH 1/2] Fixes in macOS artifact build (#491) + +--- + platforms/macos/build.sh | 12 +++++++----- + 1 file changed, 7 insertions(+), 5 deletions(-) + +diff --git a/platforms/macos/build.sh b/platforms/macos/build.sh +index aac05da6d..441fcd2a8 100755 +--- a/platforms/macos/build.sh ++++ b/platforms/macos/build.sh +@@ -199,6 +199,7 @@ for target in $targets; do + arch="${target%%-*}" + case $arch in + (i386) ++ target_cflags="$cflags -march=pentium-m" + export REMCPE_SDK="$old_sdk" + set -- -DCMAKE_EXE_LINKER_FLAGS='-framework IOKit -framework Carbon -framework AudioUnit -undefined dynamic_lookup' + platform='sdl1' +@@ -213,9 +214,9 @@ for target in $targets; do + tar -xzf ../sdl1src.tar.gz + cd "SDL-1.2-$sdl1_commit" + if [ -n "$DEBUG" ]; then +- opt='-O2' +- else + opt='-O0' ++ else ++ opt='-O2' + fi + ./configure \ + --host="$arch-apple-darwin" \ +@@ -235,9 +236,10 @@ for target in $targets; do + printf '%s' "$sdl1ver" > sdl/sdl1ver + rm -rf "SDL-1.2-$sdl1_commit" + fi +- cflags="$cflags -I$PWD/sdl/include" ++ target_cflags="$target_cflags -I$PWD/sdl/include" + ;; + (arm64*|x86_64*) ++ target_cflags="$cflags" + case $arch in + (arm64*) + export REMCPE_SDK="$arm64_sdk" +@@ -269,8 +271,8 @@ for target in $targets; do + -DCMAKE_CXX_COMPILER="$platformdir/macos-c++" \ + -DCMAKE_FIND_ROOT_PATH="$REMCPE_SDK/usr;$PWD/sdl" \ + -DCMAKE_SYSROOT="$REMCPE_SDK" \ +- -DCMAKE_C_FLAGS="$cflags" \ +- -DCMAKE_CXX_FLAGS="$cflags" \ ++ -DCMAKE_C_FLAGS="$target_cflags" \ ++ -DCMAKE_CXX_FLAGS="$target_cflags" \ + -DWERROR="${WERROR:-OFF}" \ + "$@" + make -j"$ncpus" + +From 6bb698128e88be6516de290484538b414ca845da Mon Sep 17 00:00:00 2001 +From: Brent <43089001+BrentDaMage@users.noreply.github.com> +Date: Wed, 18 Feb 2026 21:56:37 -0800 +Subject: [PATCH 2/2] Xbox 360 / Console Bug Fixes (#492) + +* Added 720p fallback resolution for Direct3D 9 device initialization. This fixes crashes that would occur when launching the game in 1080p on the Xbox 360. +* Added a hack to fix screens being shrunk infinitely when using Java screen scaling with the Console UI theme. +* Removed gameTips.animalFollow from tips, since the localization string was missing +--- + game/assets/lang/en_us.json | 2 +- + game/assets/texts/tips.json | 3 --- + platforms/xdk360/main.cpp | 12 +++++++++-- + source/client/app/Minecraft.cpp | 10 ++++++--- + .../renderer/hal/d3d9/RenderContextD3D9.cpp | 21 ++++++++++++++++--- + 5 files changed, 36 insertions(+), 12 deletions(-) + +diff --git a/game/assets/lang/en_us.json b/game/assets/lang/en_us.json +index f4499f2d2..337c2ace6 100644 +--- a/game/assets/lang/en_us.json ++++ b/game/assets/lang/en_us.json +@@ -28,7 +28,7 @@ + "loadingTip.junkboysFace": "No-one at Mojang has ever seen junkboy's face.", + "loadingTip.largeChest": "Placing two chests side by side will make one large chest.", + "loadingTip.lavaSmelt": "A single bucket of lava can be used in a furnace to smelt 100 blocks.", +- "loadingTip.legacyInfo": "You'll get the latest info on this game from minecraft.net!", ++ "loadingTip.legacyInfo": "You'll get the latest info on this game from our Discord!", + "loadingTip.lightMelt": "Blocks that can be used as a light source will melt snow and ice. This includes torches, glowstone, and Jack O'Lanterns.", + "loadingTip.minecart": "Get to destinations faster with a minecart and rail!", + "loadingTip.mineconCalifornia": "Minecon 2016 was in Anaheim, California, USA!", +diff --git a/game/assets/texts/tips.json b/game/assets/texts/tips.json +index 2117623b4..341869de0 100644 +--- a/game/assets/texts/tips.json ++++ b/game/assets/texts/tips.json +@@ -191,9 +191,6 @@ + { + "translate": "loadingTip.updateInfo" + }, +- { +- "translate": "loadingTip.animalFollow" +- }, + { + "translate": "loadingTip.mineconLondon" + }, +diff --git a/platforms/xdk360/main.cpp b/platforms/xdk360/main.cpp +index 2af6b639a..6e932bce3 100644 +--- a/platforms/xdk360/main.cpp ++++ b/platforms/xdk360/main.cpp +@@ -12,8 +12,16 @@ void _setSize() + { + XVIDEO_MODE VideoMode; + XGetVideoMode(&VideoMode); +- Minecraft::width = Mth::Max(VideoMode.dwDisplayWidth, 1280); +- Minecraft::height = Mth::Max(VideoMode.dwDisplayHeight, 720); ++ Minecraft::width = Mth::Max(VideoMode.dwDisplayWidth, 640); ++ Minecraft::height = Mth::Max(VideoMode.dwDisplayHeight, 480); ++ ++ // Hardcoded 1080p check to avoid failed D3D device creation attempt ++ if (Minecraft::width == 1920 && Minecraft::height == 1080) ++ { ++ // too big, D3D9 Device creation will fail ++ Minecraft::width = 1280; ++ Minecraft::height = 720; ++ } + } + + void _onKeyboardClosed() +diff --git a/source/client/app/Minecraft.cpp b/source/client/app/Minecraft.cpp +index 7eb11009f..3b7cf56e9 100644 +--- a/source/client/app/Minecraft.cpp ++++ b/source/client/app/Minecraft.cpp +@@ -1059,11 +1059,15 @@ float Minecraft::getBestScaleForThisScreenSize(int width, int height) + #define USE_JAVA_SCREEN_SCALING + #endif + #ifdef USE_JAVA_SCREEN_SCALING +- int scale; +- for (scale = 1; width / (scale + 1) >= 320 && height / (scale + 1) >= 240; ++scale) ++ // @HACK: the scaling code for Java/Pocket Screens when using the Console theme is pretty broken ++ if (m_pOptions->getUiTheme() != UI_CONSOLE) + { ++ int scale; ++ for (scale = 1; width / (scale + 1) >= 320 && height / (scale + 1) >= 240; ++scale) ++ { ++ } ++ return scale; + } +- return scale; + #endif + + if (height > 1800) +diff --git a/source/renderer/hal/d3d9/RenderContextD3D9.cpp b/source/renderer/hal/d3d9/RenderContextD3D9.cpp +index 67a496aba..3c69065f8 100644 +--- a/source/renderer/hal/d3d9/RenderContextD3D9.cpp ++++ b/source/renderer/hal/d3d9/RenderContextD3D9.cpp +@@ -167,12 +167,12 @@ void RenderContextD3D9::createWindowSizeDependentResources(HWND hWnd, unsigned i + { + d3dpp.BackBufferWidth = width; + d3dpp.BackBufferHeight = height; +- d3dpp.BackBufferFormat = D3DFMT_X8R8G8B8; ++ d3dpp.BackBufferFormat = D3DFMT_A8R8G8B8; + d3dpp.BackBufferCount = 1; + d3dpp.EnableAutoDepthStencil = TRUE; + d3dpp.AutoDepthStencilFormat = D3DFMT_D24S8; + d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD; +- d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_ONE; ++ d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_ONE; // basically V-Sync + if (hWnd) + { + d3dpp.hDeviceWindow = hWnd; +@@ -181,8 +181,23 @@ void RenderContextD3D9::createWindowSizeDependentResources(HWND hWnd, unsigned i + } + + DWORD flags = D3DCREATE_HARDWARE_VERTEXPROCESSING; ++ LOG_I("Creating IDirect3DDevice9 with %dx%d size...", width, height); + HRESULT hResult = m_pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, NULL, flags, &d3dpp, *m_d3dDevice); +- ErrorHandlerD3D9::checkForErrors(hResult); ++ ++ if (hResult == E_OUTOFMEMORY) ++ { ++ width = 1280; ++ height = 720; ++ d3dpp.BackBufferWidth = width; ++ d3dpp.BackBufferHeight = height; ++ LOG_I("Failed to create IDirect3DDevice9! Falling back to (%dx%d)...", width, height); ++ HRESULT hResult = m_pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, NULL, flags, &d3dpp, *m_d3dDevice); ++ ErrorHandlerD3D9::checkForErrors(hResult); ++ } ++ else ++ { ++ ErrorHandlerD3D9::checkForErrors(hResult); ++ } + + m_d3dDevice->SetRenderState(D3DRS_SEPARATEALPHABLENDENABLE, TRUE); + m_d3dDevice->SetRenderState(D3DRS_TWOSIDEDSTENCILMODE, TRUE); diff --git a/04_d80620fdbc5bfe4a76c2c89dfa94d7ac1e7a26df.patch.txt b/04_d80620fdbc5bfe4a76c2c89dfa94d7ac1e7a26df.patch.txt new file mode 100644 index 000000000..e3fbf91f3 --- /dev/null +++ b/04_d80620fdbc5bfe4a76c2c89dfa94d7ac1e7a26df.patch.txt @@ -0,0 +1,501 @@ +From d80620fdbc5bfe4a76c2c89dfa94d7ac1e7a26df Mon Sep 17 00:00:00 2001 +From: hellosammu +Date: Thu, 19 Feb 2026 10:17:18 -0500 +Subject: [PATCH] todo fix putting item stack in chests not relaying count to + clients when placing back (have to go to work waaaah) + +--- + .../multiplayer/MultiplayerLocalPlayer.cpp | 2 +- + .../multiplayer/MultiplayerLocalPlayer.hpp | 2 +- + source/server/ServerPlayer.cpp | 10 ++-- + source/server/ServerPlayer.hpp | 2 +- + source/server/ServerSideNetworkHandler.cpp | 1 + + source/world/inventory/CompoundContainer.cpp | 56 ++++++++++++++++++- + source/world/inventory/CompoundContainer.hpp | 11 ++++ + .../ContainerContentChangeListener.hpp | 2 +- + source/world/inventory/ContainerListener.hpp | 3 +- + source/world/inventory/ContainerMenu.cpp | 52 ++++++++++++----- + source/world/inventory/ContainerMenu.hpp | 2 +- + source/world/inventory/SimpleContainer.cpp | 2 +- + source/world/item/Inventory.cpp | 20 +++++++ + source/world/item/Inventory.hpp | 8 ++- + source/world/tile/entity/ChestTileEntity.cpp | 1 + + 15 files changed, 144 insertions(+), 30 deletions(-) + +diff --git a/source/client/multiplayer/MultiplayerLocalPlayer.cpp b/source/client/multiplayer/MultiplayerLocalPlayer.cpp +index 36750209d..772734bf5 100644 +--- a/source/client/multiplayer/MultiplayerLocalPlayer.cpp ++++ b/source/client/multiplayer/MultiplayerLocalPlayer.cpp +@@ -141,7 +141,7 @@ void MultiplayerLocalPlayer::refreshContainer(ContainerMenu* menu, const std::ve + { + } + +-void MultiplayerLocalPlayer::slotChanged(ContainerMenu* menu, int index, ItemStack& item, bool isResultSlot) ++void MultiplayerLocalPlayer::slotChanged(ContainerMenu* menu, int index, Slot* slot, ItemStack& item, bool isResultSlot) + { + #if NETWORK_PROTOCOL_VERSION >= 5 + m_pMinecraft->m_pRakNetInstance->send(new ContainerSetSlotPacket(m_pContainerMenu->m_containerId, index, item)); +diff --git a/source/client/multiplayer/MultiplayerLocalPlayer.hpp b/source/client/multiplayer/MultiplayerLocalPlayer.hpp +index 6ab1a580b..fffd41ec5 100644 +--- a/source/client/multiplayer/MultiplayerLocalPlayer.hpp ++++ b/source/client/multiplayer/MultiplayerLocalPlayer.hpp +@@ -22,7 +22,7 @@ class MultiplayerLocalPlayer : public LocalPlayer, public ContainerListener + void closeContainer() override; + + void refreshContainer(ContainerMenu* menu, const std::vector& items) override; +- void slotChanged(ContainerMenu* menu, int index, ItemStack& item, bool isResultSlot) override; ++ void slotChanged(ContainerMenu* menu, int index, Slot* slot, ItemStack& item, bool isResultSlot) override; + + private: + bool m_flashOnSetHealth; +diff --git a/source/server/ServerPlayer.cpp b/source/server/ServerPlayer.cpp +index a2a5d7cc5..d5bc21ebc 100644 +--- a/source/server/ServerPlayer.cpp ++++ b/source/server/ServerPlayer.cpp +@@ -14,6 +14,7 @@ + #include "world/inventory/ChestMenu.hpp" + #include "world/level/Level.hpp" + #include "world/tile/entity/FurnaceTileEntity.hpp" ++#include "world/inventory/Slot.hpp" + + ServerPlayer::ServerPlayer(Level* pLevel, GameType playerGameType) + : Player(pLevel, playerGameType) +@@ -122,13 +123,13 @@ void ServerPlayer::refreshContainer(ContainerMenu* menu, const std::vector= 5 +- if (!isResultSlot) +- { ++ // @TODO: See my gripes in ContainerMenu::slotChanged ++ // But ultimately this is a bandaid for the fact that the client has authority over the inventory in PE ++ if (!isResultSlot && slot->m_group != Slot::INVENTORY && slot->m_group != Slot::HOTBAR) + m_pLevel->m_pRakNetInstance->send(new ContainerSetSlotPacket(menu->m_containerId, index, item)); +- } + #endif + } + +@@ -162,5 +163,6 @@ void ServerPlayer::setContainerMenu(ContainerMenu* menu) + m_pContainerMenu->m_containerId = m_containerId; + m_pContainerMenu->addSlotListener(this); + refreshContainer(m_pContainerMenu, m_pContainerMenu->cloneItems()); ++ m_pContainerMenu->broadcastChanges(); + } + } +\ No newline at end of file +diff --git a/source/server/ServerPlayer.hpp b/source/server/ServerPlayer.hpp +index 874a0f1cc..2f08d5191 100644 +--- a/source/server/ServerPlayer.hpp ++++ b/source/server/ServerPlayer.hpp +@@ -19,7 +19,7 @@ class ServerPlayer : public Player, public ContainerListener + void take(Entity* pEnt, int count) override; + + void refreshContainer(ContainerMenu* menu, const std::vector& items) override; +- void slotChanged(ContainerMenu* menu, int index, ItemStack& item, bool isResultSlot) override; ++ void slotChanged(ContainerMenu* menu, int index, Slot* slot, ItemStack& item, bool isResultSlot) override; + void setContainerData(ContainerMenu* menu, int id, int value) override; + + void doCloseContainer(); +diff --git a/source/server/ServerSideNetworkHandler.cpp b/source/server/ServerSideNetworkHandler.cpp +index b88fc72d0..0651c8037 100644 +--- a/source/server/ServerSideNetworkHandler.cpp ++++ b/source/server/ServerSideNetworkHandler.cpp +@@ -640,6 +640,7 @@ void ServerSideNetworkHandler::handle(const RakNet::RakNetGUID& guid, ContainerS + switch (pContainerMenu->m_containerType) + { + case Container::FURNACE: ++ case Container::CONTAINER: + pContainerMenu->setItem(packet->m_slot, packet->m_item); + break; + default: +diff --git a/source/world/inventory/CompoundContainer.cpp b/source/world/inventory/CompoundContainer.cpp +index ea4d4dd1c..21adf1e2d 100644 +--- a/source/world/inventory/CompoundContainer.cpp ++++ b/source/world/inventory/CompoundContainer.cpp +@@ -1,8 +1,45 @@ + #include "CompoundContainer.hpp" + ++class CompoundContainer::ChildListener : public ContainerContentChangeListener ++{ ++public: ++ ChildListener(CompoundContainer* owner, int offset) ++ : m_pOwner(owner), m_offset(offset) ++ { ++ } ++ ++ void containerContentChanged(Container* container, SlotID slot) override ++ { ++ if (m_pOwner) ++ m_pOwner->setContainerChanged(slot + m_offset); ++ } ++ ++private: ++ CompoundContainer* m_pOwner; ++ int m_offset; ++}; ++ + CompoundContainer::CompoundContainer(const std::string& name, Container* c1, Container* c2) : +- m_name(name), m_pLeftContainer(c1), m_pRightContainer(c2) ++ m_name(name), m_pLeftContainer(c1), m_pRightContainer(c2), m_pLeftListener(nullptr), m_pRightListener(nullptr) + { ++ m_pLeftListener = new ChildListener(this, 0); ++ m_pRightListener = new ChildListener(this, m_pLeftContainer->getContainerSize()); ++ ++ if (m_pLeftContainer) ++ m_pLeftContainer->addContentChangeListener(m_pLeftListener); ++ if (m_pRightContainer) ++ m_pRightContainer->addContentChangeListener(m_pRightListener); ++} ++ ++CompoundContainer::~CompoundContainer() ++{ ++ if (m_pLeftContainer && m_pLeftListener) ++ m_pLeftContainer->removeContentChangeListener(m_pLeftListener); ++ if (m_pRightContainer && m_pRightListener) ++ m_pRightContainer->removeContentChangeListener(m_pRightListener); ++ ++ delete m_pLeftListener; ++ delete m_pRightListener; + } + + uint16_t CompoundContainer::getContainerSize() const +@@ -46,8 +83,11 @@ int CompoundContainer::getMaxStackSize() + + void CompoundContainer::setContainerChanged(SlotID slot) + { +- m_pLeftContainer->setContainerChanged(slot); +- m_pRightContainer->setContainerChanged(slot); ++ for (ContentChangeListeners::iterator it = m_contentChangeListeners.begin(); it != m_contentChangeListeners.end(); ++it) ++ { ++ ContainerContentChangeListener* listener = *it; ++ listener->containerContentChanged(this, slot); ++ } + } + + bool CompoundContainer::stillValid(Player* player) const +@@ -55,3 +95,13 @@ bool CompoundContainer::stillValid(Player* player) const + return m_pLeftContainer->stillValid(player) && m_pRightContainer->stillValid(player); + } + ++void CompoundContainer::addContentChangeListener(ContainerContentChangeListener* listener) ++{ ++ m_contentChangeListeners.insert(listener); ++} ++ ++void CompoundContainer::removeContentChangeListener(ContainerContentChangeListener* listener) ++{ ++ m_contentChangeListeners.erase(listener); ++} ++ +diff --git a/source/world/inventory/CompoundContainer.hpp b/source/world/inventory/CompoundContainer.hpp +index 1c3fd0fea..a429f1ceb 100644 +--- a/source/world/inventory/CompoundContainer.hpp ++++ b/source/world/inventory/CompoundContainer.hpp +@@ -1,17 +1,25 @@ + #pragma once + ++#include + #include + #include "Container.hpp" ++#include "ContainerContentChangeListener.hpp" + + class CompoundContainer : public Container + { + private: ++ class ChildListener; ++ + std::string m_name; + Container* m_pLeftContainer; + Container* m_pRightContainer; ++ ContentChangeListeners m_contentChangeListeners; ++ ChildListener* m_pLeftListener; ++ ChildListener* m_pRightListener; + + public: + CompoundContainer(const std::string& name, Container* c1, Container* c2); ++ ~CompoundContainer() override; + + uint16_t getContainerSize() const override; + +@@ -28,4 +36,7 @@ class CompoundContainer : public Container + void setContainerChanged(SlotID slot) override; + + bool stillValid(Player* player) const override; ++ ++ void addContentChangeListener(ContainerContentChangeListener* listener) override; ++ void removeContentChangeListener(ContainerContentChangeListener* listener) override; + }; +diff --git a/source/world/inventory/ContainerContentChangeListener.hpp b/source/world/inventory/ContainerContentChangeListener.hpp +index 675f1b221..ed0f7139f 100644 +--- a/source/world/inventory/ContainerContentChangeListener.hpp ++++ b/source/world/inventory/ContainerContentChangeListener.hpp +@@ -8,5 +8,5 @@ class ContainerContentChangeListener + virtual ~ContainerContentChangeListener() {} + + public: +- virtual void containerContentChanged(SlotID slot) = 0; ++ virtual void containerContentChanged(Container* container, SlotID slot) = 0; + }; +diff --git a/source/world/inventory/ContainerListener.hpp b/source/world/inventory/ContainerListener.hpp +index a159ae5db..79a63bb7e 100644 +--- a/source/world/inventory/ContainerListener.hpp ++++ b/source/world/inventory/ContainerListener.hpp +@@ -4,6 +4,7 @@ + + class ContainerMenu; + class ItemStack; ++class Slot; + + class ContainerListener + { +@@ -12,6 +13,6 @@ class ContainerListener + + virtual void refreshContainer(ContainerMenu* menu, const std::vector& items) {} + virtual void refreshContainerItems(ContainerMenu* menu); +- virtual void slotChanged(ContainerMenu* menu, int index, ItemStack& item, bool isResultSlot) {} ++ virtual void slotChanged(ContainerMenu* menu, int index, Slot* slot, ItemStack& item, bool isResultSlot) {} + virtual void setContainerData(ContainerMenu* menu, int id, int value) {} + }; +\ No newline at end of file +diff --git a/source/world/inventory/ContainerMenu.cpp b/source/world/inventory/ContainerMenu.cpp +index 7e52498df..936cb1d3e 100644 +--- a/source/world/inventory/ContainerMenu.cpp ++++ b/source/world/inventory/ContainerMenu.cpp +@@ -54,11 +54,6 @@ void ContainerMenu::addSlot(Slot* slot) + void ContainerMenu::addSlotListener(ContainerListener* listener) + { + m_listeners.insert(listener); +- +- // Not done on PE +- /*std::vector snapshot = cloneItems(); +- listener->refreshContainer(this, snapshot); +- broadcastChanges();*/ + } + + void ContainerMenu::sendData(int id, int value) +@@ -74,7 +69,7 @@ void ContainerMenu::broadcastChanges(SlotID slot) + { + m_lastSlots[slot] = ItemStack(current); + for (ContainerListeners::iterator it = m_listeners.begin(); it != m_listeners.end(); ++it) +- (*it)->slotChanged(this, slot, m_lastSlots[slot], isResultSlot()); ++ (*it)->slotChanged(this, slot, m_slots[slot], m_lastSlots[slot], isResultSlot()); + } + } + +@@ -101,12 +96,36 @@ void ContainerMenu::slotsChanged(Container*) + broadcastChanges(); + } + ++void ContainerMenu::containerContentChanged(Container* container, SlotID containerSlot) ++{ ++ // containerSlot is an index within a specific container, but m_slots contains ++ // slots from multiple containers. We need to find which slot in m_slots corresponds ++ // to this container slot by matching both the container and the slot index. ++ for (size_t i = 0; i < m_slots.size(); ++i) ++ { ++ Slot* slot = m_slots[i]; ++ if (slot->m_pContainer == container && slot->m_slot == containerSlot) ++ { ++ if (m_bBroadcastChanges) ++ broadcastChanges(i); ++ return; ++ } ++ } ++} ++ + std::vector ContainerMenu::cloneItems() + { + std::vector content; + + for (std::vector::iterator it = m_slots.begin(); it != m_slots.end(); ++it) + { ++ Slot* slot = *it; ++ // @TODO: I really do not like this. ++ // Firstly, inventories shouldn't be owned by the client ++ // Secondly, we shouldn't be checking types directly like this ++ // Ultimately this HAS to have two different storages, one for the inventory and one for the container ++ if (slot->m_group == Slot::INVENTORY || slot->m_group == Slot::HOTBAR) ++ continue; + const ItemStack& item = (*it)->getItem(); + content.push_back(item); + } +@@ -247,8 +266,6 @@ ItemStack ContainerMenu::clicked(int slotIndex, MouseButtonType mouseButton, boo + if (!slot) + return result; + +- slot->setChanged(); +- + ItemStack& slotItem = slot->getItem(); + + if (!slotItem.isEmpty()) +@@ -289,6 +306,7 @@ ItemStack ContainerMenu::clicked(int slotIndex, MouseButtonType mouseButton, boo + if (carried.m_count <= slot->getMaxStackSize()) + { + std::swap(carried, slotItem); ++ slot->setChanged(); + } + } + else if (slotItem.getId() == carried.getId()) +@@ -309,6 +327,7 @@ ItemStack ContainerMenu::clicked(int slotIndex, MouseButtonType mouseButton, boo + inv->setCarried(ItemStack::EMPTY); + + slotItem.m_count += count; ++ slot->setChanged(); + break; + } + case MOUSE_BUTTON_RIGHT: +@@ -325,6 +344,7 @@ ItemStack ContainerMenu::clicked(int slotIndex, MouseButtonType mouseButton, boo + inv->setCarried(ItemStack::EMPTY); + + slotItem.m_count += count; ++ slot->setChanged(); + break; + } + default: +@@ -358,6 +378,10 @@ ItemStack ContainerMenu::clicked(int slotIndex, MouseButtonType mouseButton, boo + void ContainerMenu::setItem(int index, ItemStack item) + { + m_slots[index]->set(item); ++ if (!m_bBroadcastChanges && index >= 0 && index < (int)m_lastSlots.size()) ++ { ++ m_lastSlots[index] = ItemStack(m_slots[index]->getItem()); ++ } + } + + void ContainerMenu::setAll(const std::vector& items) +@@ -366,6 +390,10 @@ void ContainerMenu::setAll(const std::vector& items) + for (size_t i = 0; i < n; ++i) + { + m_slots[i]->set(items[i]); ++ if (!m_bBroadcastChanges) ++ { ++ m_lastSlots[i] = ItemStack(m_slots[i]->getItem()); ++ } + } + } + +@@ -397,10 +425,4 @@ void ContainerMenu::setSynched(Player* player, bool isSynched) + m_unsynchedPlayers.erase(player); + else + m_unsynchedPlayers.insert(player); +-} +- +-void ContainerMenu::containerContentChanged(SlotID slot) +-{ +- if (m_bBroadcastChanges) +- broadcastChanges(slot); +-} ++} +\ No newline at end of file +diff --git a/source/world/inventory/ContainerMenu.hpp b/source/world/inventory/ContainerMenu.hpp +index 481d199be..45d1aa360 100644 +--- a/source/world/inventory/ContainerMenu.hpp ++++ b/source/world/inventory/ContainerMenu.hpp +@@ -59,7 +59,7 @@ class ContainerMenu : public ContainerContentChangeListener + virtual bool isPauseScreen() const { return false; } + + public: +- void containerContentChanged(SlotID slot) override; ++ void containerContentChanged(Container* container, SlotID slot) override; + + protected: + std::vector m_lastSlots; +diff --git a/source/world/inventory/SimpleContainer.cpp b/source/world/inventory/SimpleContainer.cpp +index 62d57425c..d6ce69a77 100644 +--- a/source/world/inventory/SimpleContainer.cpp ++++ b/source/world/inventory/SimpleContainer.cpp +@@ -62,7 +62,7 @@ void SimpleContainer::setContainerChanged(SlotID slot) + for (ContentChangeListeners::iterator it = m_contentChangeListeners.begin(); it != m_contentChangeListeners.end(); it++) + { + ContainerContentChangeListener* pListener = *it; +- pListener->containerContentChanged(slot); ++ pListener->containerContentChanged(this, slot); + } + } + +diff --git a/source/world/item/Inventory.cpp b/source/world/item/Inventory.cpp +index e0471397b..a14c88e24 100644 +--- a/source/world/item/Inventory.cpp ++++ b/source/world/item/Inventory.cpp +@@ -4,6 +4,7 @@ + #include "common/Logger.hpp" + #include "nbt/CompoundTag.hpp" + #include "network/Packet.hpp" ++#include "world/inventory/ContainerContentChangeListener.hpp" + + #include "Item.hpp" + +@@ -627,3 +628,22 @@ GameType Inventory::_getGameMode() const + { + return m_pPlayer->getPlayerGameType(); + } ++ ++void Inventory::setContainerChanged(SlotID slot) ++{ ++ for (ContentChangeListeners::iterator it = m_contentChangeListeners.begin(); it != m_contentChangeListeners.end(); ++it) ++ { ++ ContainerContentChangeListener* listener = *it; ++ listener->containerContentChanged(this, slot); ++ } ++} ++ ++void Inventory::addContentChangeListener(ContainerContentChangeListener* listener) ++{ ++ m_contentChangeListeners.insert(listener); ++} ++ ++void Inventory::removeContentChangeListener(ContainerContentChangeListener* listener) ++{ ++ m_contentChangeListeners.erase(listener); ++} +diff --git a/source/world/item/Inventory.hpp b/source/world/item/Inventory.hpp +index 876d06183..09a5c7882 100644 +--- a/source/world/item/Inventory.hpp ++++ b/source/world/item/Inventory.hpp +@@ -1,5 +1,6 @@ + #pragma once + ++#include + #include + #include "GameMods.hpp" + #include "world/inventory/Container.hpp" +@@ -19,6 +20,8 @@ class Player; // in case we're included from Player.hpp + + class Inventory : public Container + { ++private: ++ typedef std::set ContentChangeListeners; + public: + Inventory(Player*); + virtual ~Inventory(); +@@ -80,7 +83,9 @@ class Inventory : public Container + return "Inventory"; + } + +- void setContainerChanged(SlotID slot) override { } ++ void setContainerChanged(SlotID slot) override; ++ void addContentChangeListener(ContainerContentChangeListener* listener) override; ++ void removeContentChangeListener(ContainerContentChangeListener* listener) override; + + bool stillValid(Player* player) const override { return true; } + +@@ -100,4 +105,5 @@ class Inventory : public Container + + std::vector m_items; + std::vector m_armor; ++ ContentChangeListeners m_contentChangeListeners; + }; +diff --git a/source/world/tile/entity/ChestTileEntity.cpp b/source/world/tile/entity/ChestTileEntity.cpp +index 323cfa7eb..e18c18130 100644 +--- a/source/world/tile/entity/ChestTileEntity.cpp ++++ b/source/world/tile/entity/ChestTileEntity.cpp +@@ -27,5 +27,6 @@ bool ChestTileEntity::stillValid(Player* player) const + + void ChestTileEntity::setContainerChanged(SlotID slot) + { ++ SimpleContainer::setContainerChanged(slot); + TileEntity::setChanged(); + } diff --git a/05_a55606e83bc6fa100cd978fda4b14d6cc0cec4e0.patch.txt b/05_a55606e83bc6fa100cd978fda4b14d6cc0cec4e0.patch.txt new file mode 100644 index 000000000..c55e240e3 --- /dev/null +++ b/05_a55606e83bc6fa100cd978fda4b14d6cc0cec4e0.patch.txt @@ -0,0 +1,6842 @@ +From cff514fb41805a8a248b0f8499759246d5c6a247 Mon Sep 17 00:00:00 2001 +From: Brent <43089001+BrentDaMage@users.noreply.github.com> +Date: Thu, 19 Feb 2026 20:23:31 -0800 +Subject: [PATCH 1/9] Fixed Xbox 360 Hanging on Startup (#493) + +Fixed device storage selection UI not rendering, which would result in the game hanging when loading the options. +This would only affect devices with more than one storage device attached. +--- + platforms/xdk360/AppPlatform_xdk360.cpp | 28 +++++++++++++++---- + .../renderer/hal/base/RenderContextBase.cpp | 8 ++++++ + .../renderer/hal/base/RenderContextBase.hpp | 2 ++ + .../renderer/hal/d3d9/RenderContextD3D9.cpp | 21 ++++++++++++-- + .../renderer/hal/d3d9/RenderContextD3D9.hpp | 2 ++ + 5 files changed, 54 insertions(+), 7 deletions(-) + +diff --git a/platforms/xdk360/AppPlatform_xdk360.cpp b/platforms/xdk360/AppPlatform_xdk360.cpp +index 0cf1313db..a0fb29f2f 100644 +--- a/platforms/xdk360/AppPlatform_xdk360.cpp ++++ b/platforms/xdk360/AppPlatform_xdk360.cpp +@@ -51,8 +51,17 @@ const XCONTENTDEVICEID& AppPlatform_xdk360::_getSaveDeviceId(LocalPlayerID playe + if (deviceId != C_SAVEDEVICE_ID_NONE) + return deviceId; + ++ /*mce::RenderContext& renderContext = mce::RenderContextImmediate::get(); ++ // This is required in order for the system UI to render in a blocking context ++ renderContext.suspend();*/ ++ ++ // Create event for asynchronous writing ++ HANDLE hEventComplete = NULL; //CreateEvent(NULL, FALSE, FALSE, NULL); ++ + ULARGE_INTEGER iBytesRequested = { C_SAVEDEVICE_MINIMUM_FREE_BYTES }; + XOVERLAPPED xov = {0}; ++ if (hEventComplete) ++ xov.hEvent = hEventComplete; + + deviceId = C_SAVEDEVICE_ID_PENDING; + DWORD result = XShowDeviceSelectorUI( +@@ -63,7 +72,10 @@ const XCONTENTDEVICEID& AppPlatform_xdk360::_getSaveDeviceId(LocalPlayerID playe + &xov + ); + +- if (result != ERROR_IO_PENDING) ++ if (result != ERROR_IO_PENDING ++ // Only block if we could get an event handle ++ || (hEventComplete && XGetOverlappedResult(&xov, NULL, TRUE) != ERROR_SUCCESS) ++ ) + { + LOG_W("Failed to open save device for player %d", playerId); + deviceId = C_SAVEDEVICE_ID_ERROR; +@@ -72,9 +84,13 @@ const XCONTENTDEVICEID& AppPlatform_xdk360::_getSaveDeviceId(LocalPlayerID playe + // Wait for the save-device handle + while (XHasOverlappedIoCompleted(&xov) == FALSE) + { ++ swapBuffers(); ++ // This only ends up getting run if the above event handle couldn't be created and waited on + sleepMs(20); + } + ++ //renderContext.resume(); ++ + return deviceId; + } + +@@ -154,7 +170,8 @@ void AppPlatform_xdk360::beginProfileDataRead(LocalPlayerID playerId) + XCONTENT_DATA contentData; + _getXContentData(contentData, playerId); + +- XUID xuid; ++ // Fuck this check, it just fails sometimes for no apparent reason ++ /*XUID xuid; + BOOL playerIsCreator; + XContentGetCreator(playerId, &contentData, &playerIsCreator, &xuid, NULL); + +@@ -162,7 +179,7 @@ void AppPlatform_xdk360::beginProfileDataRead(LocalPlayerID playerId) + { + LOG_E("Current player is not the creator of, and therefore cannot write to, the requested profile data!"); + throw std::bad_cast(); +- } ++ }*/ + + // Mount the device to the "savedrive" drive name + DWORD result = XContentCreate(playerId, "savedrive", &contentData, XCONTENTFLAG_OPENEXISTING, NULL, NULL, NULL); +@@ -187,8 +204,9 @@ void AppPlatform_xdk360::endProfileDataRead(LocalPlayerID playerId) + { + if (m_currentSavingPlayerId == -1) + { +- LOG_E("Tried to end profile data read for player %d, but no one is saving any data!", playerId); +- throw std::bad_cast(); ++ LOG_W("Tried to end profile data read for player %d, but no one is saving any data!", playerId); ++ //throw std::bad_cast(); ++ return; + } + + XContentClose("savedrive", NULL); +diff --git a/source/renderer/hal/base/RenderContextBase.cpp b/source/renderer/hal/base/RenderContextBase.cpp +index 165041986..7bcdfcf1a 100644 +--- a/source/renderer/hal/base/RenderContextBase.cpp ++++ b/source/renderer/hal/base/RenderContextBase.cpp +@@ -104,6 +104,14 @@ void RenderContextBase::endRender() + { + } + ++void RenderContextBase::suspend() ++{ ++} ++ ++void RenderContextBase::resume() ++{ ++} ++ + void RenderContextBase::swapBuffers() + { + } +diff --git a/source/renderer/hal/base/RenderContextBase.hpp b/source/renderer/hal/base/RenderContextBase.hpp +index a949fa134..5b869f843 100644 +--- a/source/renderer/hal/base/RenderContextBase.hpp ++++ b/source/renderer/hal/base/RenderContextBase.hpp +@@ -50,6 +50,8 @@ namespace mce + void setRenderTarget(); + void beginRender(); + void endRender(); ++ void suspend(); ++ void resume(); + void swapBuffers(); + void lostContext(); + +diff --git a/source/renderer/hal/d3d9/RenderContextD3D9.cpp b/source/renderer/hal/d3d9/RenderContextD3D9.cpp +index 3c69065f8..329005ee7 100644 +--- a/source/renderer/hal/d3d9/RenderContextD3D9.cpp ++++ b/source/renderer/hal/d3d9/RenderContextD3D9.cpp +@@ -1,5 +1,6 @@ + #include + #include "RenderContextD3D9.hpp" ++#include "compat/PlatformDefinitions.h" + #include "common/Logger.hpp" + #include "renderer/hal/d3d9/helpers/ErrorHandlerD3D9.hpp" + +@@ -117,7 +118,8 @@ void RenderContextD3D9::beginRender() + #if MCE_GFX_D3D9_SHADER_CONSTANT_BUFFERS + m_d3dDevice->GpuOwn(D3DTAG_VERTEXSHADERCONSTANTS); + m_d3dDevice->GpuOwn(D3DTAG_PIXELSHADERCONSTANTS); +-#else ++#endif ++#if !MC_PLATFORM_XBOX360 + m_d3dDevice->BeginScene(); + #endif + } +@@ -126,11 +128,26 @@ void RenderContextD3D9::endRender() + { + #if MCE_GFX_D3D9_SHADER_CONSTANT_BUFFERS + m_d3dDevice->GpuDisownAll(); +-#else ++#endif ++#if !MC_PLATFORM_XBOX360 + m_d3dDevice->EndScene(); + #endif + } + ++void RenderContextD3D9::suspend() ++{ ++#if MC_PLATFORM_XBOX360 ++ m_d3dDevice->Suspend(); ++#endif ++} ++ ++void RenderContextD3D9::resume() ++{ ++#if MC_PLATFORM_XBOX360 ++ m_d3dDevice->Resume(); ++#endif ++} ++ + void RenderContextD3D9::swapBuffers() + { + HRESULT hr = m_d3dDevice->Present(NULL, NULL, NULL, NULL); +diff --git a/source/renderer/hal/d3d9/RenderContextD3D9.hpp b/source/renderer/hal/d3d9/RenderContextD3D9.hpp +index d337803c5..d5c303fdc 100644 +--- a/source/renderer/hal/d3d9/RenderContextD3D9.hpp ++++ b/source/renderer/hal/d3d9/RenderContextD3D9.hpp +@@ -66,6 +66,8 @@ namespace mce + void clearContextState(); + void beginRender(); + void endRender(); ++ void suspend(); ++ void resume(); + void swapBuffers(); + + bool supports8BitIndices() const; + +From ec296519ed77f172ff5754b74f8a4cafc8de6d4f Mon Sep 17 00:00:00 2001 +From: Vimdo +Date: Thu, 19 Feb 2026 23:46:31 -0500 +Subject: [PATCH 2/9] Mipmap Icons for Android (#488) + +--- + game/assets/{ => app/icons}/icon.ico | Bin + game/assets/app/icons/icon.jpg | Bin 0 -> 24462 bytes + game/assets/{ => app/icons}/icon.png | Bin + game/assets/app/icons/mipmap/icon_144x144.png | Bin 0 -> 12364 bytes + game/assets/app/icons/mipmap/icon_192x192.png | Bin 0 -> 20170 bytes + game/assets/app/icons/mipmap/icon_48x48.png | Bin 0 -> 2007 bytes + game/assets/app/icons/mipmap/icon_64x64.png | Bin 0 -> 3071 bytes + game/assets/app/icons/mipmap/icon_72x72.png | Bin 0 -> 3808 bytes + game/assets/app/icons/mipmap/icon_96x96.png | Bin 0 -> 6294 bytes + game/assets/icon.jpg | Bin 24427 -> 0 bytes + game/assets/icon_64x64.png | Bin 7689 -> 0 bytes + platforms/android/project/app/build.gradle | 25 ++++++ + .../project/app/src/main/AndroidManifest.xml | 1 + + platforms/ios/build-ipa.sh | 2 +- + .../Minecraft.xcodeproj/project.pbxproj | 2 +- + platforms/sdl/base/AppPlatform_sdl.cpp | 2 +- + platforms/sdl/sdl2/CMakeLists.txt | 2 +- + platforms/sdl/sdl2/android/app/build.gradle | 22 ++++++ + .../android/app/src/main/AndroidManifest.xml | 3 +- + .../drawable-v24/ic_launcher_foreground.xml | 31 -------- + .../res/drawable/ic_launcher_background.xml | 74 ------------------ + .../res/mipmap-anydpi-v26/ic_launcher.xml | 5 -- + .../mipmap-anydpi-v26/ic_launcher_round.xml | 5 -- + .../src/main/res/mipmap-hdpi/ic_launcher.webp | Bin 2682 -> 0 bytes + .../res/mipmap-hdpi/ic_launcher_round.webp | Bin 4342 -> 0 bytes + .../src/main/res/mipmap-mdpi/ic_launcher.webp | Bin 2172 -> 0 bytes + .../res/mipmap-mdpi/ic_launcher_round.webp | Bin 2924 -> 0 bytes + .../main/res/mipmap-xhdpi/ic_launcher.webp | Bin 4010 -> 0 bytes + .../res/mipmap-xhdpi/ic_launcher_round.webp | Bin 6554 -> 0 bytes + .../main/res/mipmap-xxhdpi/ic_launcher.webp | Bin 5382 -> 0 bytes + .../res/mipmap-xxhdpi/ic_launcher_round.webp | Bin 9466 -> 0 bytes + .../main/res/mipmap-xxxhdpi/ic_launcher.webp | Bin 7624 -> 0 bytes + .../res/mipmap-xxxhdpi/ic_launcher_round.webp | Bin 12522 -> 0 bytes + platforms/windows/MinecraftClient.Win32.rc | 4 +- + .../MinecraftClient.Win32.vcxproj | 2 +- + .../MinecraftClient.Win32.vcxproj.filters | 2 +- + platforms/xdk360/ReMinecraftPE.xlast | Bin 16354 -> 16422 bytes + 37 files changed, 57 insertions(+), 125 deletions(-) + rename game/assets/{ => app/icons}/icon.ico (100%) + create mode 100644 game/assets/app/icons/icon.jpg + rename game/assets/{ => app/icons}/icon.png (100%) + create mode 100644 game/assets/app/icons/mipmap/icon_144x144.png + create mode 100644 game/assets/app/icons/mipmap/icon_192x192.png + create mode 100644 game/assets/app/icons/mipmap/icon_48x48.png + create mode 100644 game/assets/app/icons/mipmap/icon_64x64.png + create mode 100644 game/assets/app/icons/mipmap/icon_72x72.png + create mode 100644 game/assets/app/icons/mipmap/icon_96x96.png + delete mode 100644 game/assets/icon.jpg + delete mode 100644 game/assets/icon_64x64.png + delete mode 100644 platforms/sdl/sdl2/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml + delete mode 100644 platforms/sdl/sdl2/android/app/src/main/res/drawable/ic_launcher_background.xml + delete mode 100644 platforms/sdl/sdl2/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml + delete mode 100644 platforms/sdl/sdl2/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml + delete mode 100644 platforms/sdl/sdl2/android/app/src/main/res/mipmap-hdpi/ic_launcher.webp + delete mode 100644 platforms/sdl/sdl2/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp + delete mode 100644 platforms/sdl/sdl2/android/app/src/main/res/mipmap-mdpi/ic_launcher.webp + delete mode 100644 platforms/sdl/sdl2/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp + delete mode 100644 platforms/sdl/sdl2/android/app/src/main/res/mipmap-xhdpi/ic_launcher.webp + delete mode 100644 platforms/sdl/sdl2/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp + delete mode 100644 platforms/sdl/sdl2/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp + delete mode 100644 platforms/sdl/sdl2/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp + delete mode 100644 platforms/sdl/sdl2/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp + delete mode 100644 platforms/sdl/sdl2/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp + +diff --git a/game/assets/icon.ico b/game/assets/app/icons/icon.ico +similarity index 100% +rename from game/assets/icon.ico +rename to game/assets/app/icons/icon.ico +diff --git a/game/assets/app/icons/icon.jpg b/game/assets/app/icons/icon.jpg +new file mode 100644 +index 0000000000000000000000000000000000000000..16ca8e5db82360d6db6f6a2611232d8908405499 +GIT binary patch +literal 24462 +zcmb4KWmMfvw7s~y7Ax-VuEpKGxVsfEZpGc*-MzrYy|lPrphz$7F7JLn-dgYPJ6S6$ +znPko+nUlSDa%MhOKXw3^3NrFC04OME01adTd~5)u05H)1*?(&oh{3|c{%45r@Nn=* +zh{(uDh)77ts2HfoDCj6iNN8AS=opxoSeVGD*f`jjIFN13|C~VmR}%&n5mFHo1qlUG +z`v0^KAOH&y3IGLwfx-enV?n`SL4Aw>NC5ySI4Fn#{@0)ZP%yA?2=ItVki+7b0B9&^ +zIA~ZHcvv_{`;hw3FtAtvIBW_IcnJg?N_9jtH(V;tkmO=Xjpj={^T9t?NL;)4)b1$+ +zgrQOp4_LaqjKE*3xx@VcN@OQ<%emm1hP#>Ye< +zSBH<;65k+4yJ6-}_fP%HxV)9*!8hXA?|Kw~lAd+}b^HwCT<7myyvm=jv0KV&h8d(tgj8#~?~HK6^gshF?R6XnVM!u;<$aL3g9F=0_>A2^u=V6mM* +z0RmqbU=^qtt5UGVS4N{=b`?w+zRsRxNSaZ{^YZmQegKRp`f-G;WT}-+CHc{pdtYFp +z1QggSTx-eic-ql)g{ny-aZ~aDWqO}ynkn@rmjQ;v7=;518_1f6ST#I9x;Ixt+u +z*j(1KWcF_WIZ@&wpqEzP0w0XV)L)el3z>EkS@E?mUPkZpj_Vs{-2; +zp3!O4@-CZpHjA8Hgv??#zs5Y-Als%;vmQ9Rn7ahVoW1ODwh#fdx%h`>{)8gVqjkR~ +zP%o@W58>Hs|2|uj$qi-epwoTvSfew_TTo!5RHLb-HK!m{OQCg?mAJ(JB{D@8c6 +zZo~weV4iW|u)xMpa!FdHl5pSXul#dcqBHpqJFiV~?gc(FqmuFz#_hxtXrpt}_k;h! +zw%|j-RdT;-!meVmZbg0}#s8KNVnY799F2K8GFIp1%GWuE^`LRDWFgs8y!@_5Q~&4^ +zARxzp_(vu7kkml>{2R*}$`smx%V! +z!~g~rTuuE|hvsBGV@y|gPyw075OUVJ;V)^Lv41kh-e+ea{n6evzs$&rrpn-&?odL` +zUiL-}TI|g#HAZ9Mo5@%4o9{)M327A7kkjsDir38@ufIOtONt_ApX{Yb8SVZeib2-=NxoKvDBg +zT)df8WqBV2z8;a+fRfM&E-6qT#sVcgIxz^S3#XuOAK|D=IynHoorO@VaYptB%L_R* +z4N+?3z4f}sg)Bp*mMiUOr4rR3t1TNNBmN2 +zn^P)HU7h5(^`4c0TU4xfimH%P@RB_G61#J!fb7~3*K`aZZKw2@;_LD+;b;b}zBYe4 +zEkDoJG^$6m!&wwz?hkRf==daDhJb8~m5!15@0Foe-HKUdBcRuu7HKL8C}XZk%x$YE +z>pr8L*8CI{U`l5xnQl&|CD%Mjn=csLjnKb64mt=|I?qB>QqMQAPfrh?w-h$Ie`5(% +zNB;D8K164*t%^|(;VC$<5rj>A@MXlzYKDY>BZRCr3H|8yA|?X5#l}uUlEz$dGym<% +z78*|$j+@MJzfM8bp#c8_zzh!!|4pZ$aH<@ioQo&s;r(okNYw{rn6r2@Ke>Uy6dRWdiZvt}nqiDj__&wfKh +z*vWZsR|aVKyr;a4Yur}1hj0Vq)ZoSg3n1P|`1{?BoaRfGJP(YyQ0#^yB?Vy0x>`Jt +zx9jw$Y50Nc5f{{Glq0`_6%IBbjGF^7RtR?P*GlAp5vN~>U+VjyDC5xB%2pZP&>4hk +zWhQK-^`U>dvldLBT?7E85W~j+G8Lx40gOqqhv5P&lIoobnorqU_IypmfgD~U={$T& +zw!6vtuzna_Be78G7!)WA3aogOUx66M9Qu`ON?PY3)G0W{=Lmsk9{~OkJ7nO_H_c&z +zCf?;w4OFxcAR(8`t2K902+8Bh*pll{32Y{1G-#?D!IsAtARQ#2om|BjLK@NPTO2I2 +zlrP}l;%>>@Hv~@zp|Yf6Yc+oWR^<)2)#=s5g|N|AzJj=}hd5fyy*0Q|?d3CgrFmaQ +z_%slj&P4}=DZx{I7RXFKe%G5QlOWW)lYmu_A!e;mw`ku0;nyU;Wrr3jPmuP$XJJO{%y*)AOd_~_ +z0OAnUd2p4hlklirDibTn^^miCk*Q>bt*`vz?*W%$D|k85dW;F`rGj19R7{3{+#Fsf +zN2SY{40g1uYH@3sn(Ppv^oRi>1NB$XOY8@JpKuJAD~>mayWjX<`?ErjO=}$v$%$ww +z`fR4#vl;;~qs=(T63VXF_*_Z*^hvSsCdC(S9aIBIL)I1k{nf>(@vuo9-F@Q^Aar$P +zqHxP-JJS|e6UEOzJKcgB{a^N!2h{}#_2-CMc~-<-oA_P#rP)L}1RH(5a|4r((ZVv^k9M)T%#}2% +zDi9uRh*v{!Bh2BoUw4cvGBv5iz|yp~c@fwA +zD(oQH%&ISDb=BbLKTLvBQAPZU7&?Y6gfr1a6U@g4yXvxPnvw@QKayf^oxos`XCaQ%m;)&59IkjV=hp<@r@-SM@SP83T +zSB)pR2?}Hur`$|^x;hIPMZ;`d8H4{yN;GJNDA%6o5aP0eY4?=2EvkJK_eG1m3wE;G +z#HE5QkCBQ=hTmY40$e)g;m2VQY=E6bEVJ=9_DaYxa5TajTUz +zV@TO=m6R8!)i*a&YdovB43DL$4?u#H*Q!581uYSDh&%nC_3I}=UC>)IZ`(}G<}POH +zRo7@uJ4~1}6fgoiSQ&!Ngph#v)WXh#0@t}>%7I5-kHj{WxInZi;naXIXqt3(^T?RQSOQ@7P^9@X3Y(V4w +zmK*$)tw8210E}>E9Md5)WrphO8-ry^URuC}VUB(6uaAUfvS&Ebn5MTj>zn-m{DsE7 +zny35g<^Tm=@CWt7GFX-?Vq%|FJP3dP04zXbR+xAr>V~+Zd=tiGr|@&kE&b=+ASsR&eT@h=Z9m^%zWusElBj2_OA0(a3!FH4~;64s|wtQiUExzL_=M(0NJ!_5V|#H82FpxvH_tg~a%fF& +zN8K8=3>xl{_x`C6{3E@l!wT!=Q(R;ZI{;In`OM`aPrmX{{sWN7G{;GhMc&2I!AYE+ +zpafT}tdP7JJxTSLaD)$lu1e9K+K18#!QXtH(+RNfyki;@DJLXauS*dLzFJJ0LP +z-KR30o%!gnG&B%+_uXRC>6a!pog5LrP^&ow%|T;5ZMyWsQt1P?AG$!$xUm{SJCZ!r +z#Clg}zhD{gn_Wz$`?~ot%kiFNg0dMT1&0c +zl!tDM3*YI1#>n9x0Jbc(7m%ax%8#lpFIt@&fSs))X@X^8ASXe``)AFd%VO +zgO(r;_zXh%S@QfXv|eGbu4n-b#++RBB19x3N>|;Q=jq;fK0P~-)k&ci-x%NYsuPq8 +zU2I9E!5O(=>Ps9aTV@hK+NU_h#=Vt^FBh_H*bEz!^;~bY)R+0Sxmve|Pc2H26z=vW +z%lj}48&SxQ)iBHRbnUZ-wSb5+caoyKhNe{_>=<9Y7<_8Ll$sd5uG)z_mmx};IeYDY2R|;`pNC2Yt!J@YgQ}~w(RC*l* +ze{3MkX6~+}Us~~7$4Hvy3GXTiqTCelT+9ejQzMl-H$L(MPyowdjGW&Ij38$EMhA`e +zOvnZ|(alQ-jU)ews23t;s0p$W%s0OQBmP-bKwd7Uwn|+FU=84GB=_n06X}n8RTy8d9!&;qI=QM- +z*+bS~P;MIxRkOk6=ptK=?&3l6T#nqDu!q``QUwx~fra}YyJm9EB&a@M^EmC1Au_X- +zIU!Hd4k!A+gDUl>Tc*b9%b5}ocO;rsgKZS_SB@GtYD^oQpm>`D=D=CG;zEgNEWyQHLl|0@sLw2&SR& +zl{ilF66u@Hp~gg+{>q+k3hY?CO9kuglsU%f}aE9Up(z8-cu))CEYS34a@ +zlQ4ArU6eiadE?-a;hT!77c#S9PEU8%*UoucT}4&m5brZCn=aSav8#Y>gbD)Tpv6@Y +zOhi=t(A4<%l&PL^SV|{nX1y7s6#Eu5$;-J5p6)K&J>_) +zJ-K3mkj1Z`nhKp)v?6789#W1P8`Gj)P$)k>jTs?PawDd@$=#0)mR4?2_C +zw-<>-q-m?WfZ8 +zCG)f%v{2dmaZHmseqv&=Wrh_D_O&%il-+b~rN2-~xK)A7;V^d{9J6A3I%BswET;J! +zabKjOia!91oqUh=IDSk5bx@;aawB#W!@uLc*ACzHD6@L!XsJrvBbC)8wbKv0Z-U-I +z!sr|)qMy-R6gttq6&KkIG%f|a>=}!V*8cn6Om+AH@SfUy^EP_!Nw%&wRzTZ?aejF3 +z+NdYG^`*gzqeq5j-OjH+3qj!$B(guy7OG|6=zqklKDheMw@La90UCd==!LnHa-HbR +z#ViD6Scjr}BM(uBp&E!RC&Y3Oj3`TvkO(}32wL}A4|fR>;8_TApJ%jLGPP@2eCOti +zspJUlu&FDBR4AG{huSC`~-sFOL%H`^Tiy*4Yp#iBisW& +zW`nF2a8Y4HAOtF}Ti|#q-vf}ADczj*wGPged;B@8 +z$^@gaX5~x&@(2?bmP_&t8#~cK1zK)NWF5w+;-;thS_!vZt4SS7F;!fD!t36cA8|ya +zBSQG|m}~DTPto8Xg3c~3B=NZlVa+Suw*PRgEl;lEh#&JWIu1u!d~X{`b_B19XpnC9 +zp9TkMX?3MG8@||67hFwE4nCPhne$#pU+C3_R+j2{w~{^5T8SlUp{@j|i=moU7BHmf +z2wEkqM$TGRW?{xG288mMGauQDEyQCAwiO1dO1uzIU(s{F*gTIkXJS1kd@BWyy)lz0hbiI +zq^*5Gp|dN6-U}yXEB3fkgDZuWzoA(OG6R1HEOnGw#|kh)*$5^Of-RCth%(gON6=VN +zz;JUD2x>~F`9nll84lE@sU%O0#H-Xv2(s<5p-NL(BSdH6ts~K>yj@c0Ro16S(^B(7 +zVyNs>UNl^4f-8lpys^60_j4h1BnS^qW-SgWfC)Z}u449dN?3QJnMg>$-f>Fg4yvo2 +zUl;cH3E|sH_x1w#AitP>6Jr>R5*nU^l{twx`hNm0ioRa${NO|Vx5xw_RUqv=#JItj +zy@l7UYCM^FV4US-Eh?L^{l_`=+VAJ*$4}KGjK&nD8TcgRPvWuYC*3znv^-a3tlnV9 +zN=E@iWGX40KWiY?87r3+*`0Os%|-`c21%sjF?YkuW=W^nbQ|kf8S7D5$7GjgMEnFA +z2w{vMU$|uP=gVwvY4Fr<6OR4z#u&pW!%NlM+=kIvFS0OXH^scd;u=Ia?$*`TSkS&p +zaA85^TKFTcbD^qoq$vFvN;!%g#bU`1eqc8ZPKp%`P=K@_u|vjIu9Iodz|G?F +zUFPUmvR64%rcJ|_`(L<>i`n|dMSRcUO43Y;XgL5~j>-ibf;^@zRjG(LXb#m)zz2-r +zQ`3BMK!GyV3jVGOL6Rf03hYH}ofi`b_S>hxtFes%A&`SSm+r)8xat965y4RudCGL^ +zaBB3wj)-5iQ?8+dvP>nrnl74PUoR=-$=g%_eVMnwh>YzAU<8g4%8q2n_#8AYkt6gg +zBx19Dav5PuE@U4YULL}SBY +zM#oOtf4(<4r!5uz_WK!x)nCNCnbjw&R|m+ULwEg+oc#Yz2WTC4k{a%Ue{%C8aV065 +zBHkh3Og*q6^pt;%w^N7>AI0m5 +zzgwarhw!#0uwkWMd71I6vs6o9AgvSa(=`R^bO9riuqy{vliG3?4KrM_qX+aTMD>hOa?%KwPMun1Vd#-W^C=Tx{2vx +zLv@5W=SF>Q{bNKD^lcBtE_2QfJA1u*`3AjiR=tGB3W)eh7xHfs96jaotc#43ix +zeeF~$o;4?F;S`ibx-~+ljx+%HnY})F(9Euo^$o!kAmES&$*+V +zFixWOnjg@zX}D!I^W_q(8%tXF4F5yHR9DzvaRq_<@y&$3dz{-M{up51uqVg*pp +zGH(q1)v?`r;Cz)y}$z#Z%XR!1ZAD8P1adQ_Z*w3@hZ11MA+fXQ-M1viA7Jn7I +zWkCo{3$)6Y&K)dN%#>B~P30z#!bo_Q|hN$FAe(FOiKtz`jc(WJ4MPol_ +zrA)qypW#`vk&}S!F?av+UK?@vgl?7cnTm6o@!xccIUZ)4)j}(pxKY4W+DiMLft7uY +zMj#aitbarM<=W1QyiWz!aYFr%_L6Dp3b~w|>f&bCgGMX9F5}))rzUEP&80w2?!q-k +zKd*MC;J;d`%Xo?K0aReJ&XesL@ae9@e(|K#Tkcd91MLzKZYyQdNQeWvqMx%P($`vW +z6MngclR}?v`wLp@XGn6vq1&n-@lhgmel}A=pPV(3_~|L7x8jvyq1DJ^oa!?7udJ8( +z##Cs%q)mWD^x55mF-YkO4J6m@lwJ*;E6@Jrg8G*`S7#@MuNN3KSM-V~vy9c=owYD} +z7zMbVEj+6c?#q2HL4D2-;LSgq)od+ySu7NnyD?IE3f>wA^_=Lhj1I_qy~q*Z8UF$? +zBiZI5l0Ixi{Pti&dlfFM8n{EZ(r7wV&#J)6X^-bc0bk#qA~*a^_1LTuG7$aTR_b(F +z&$@6yC$PAr-)ZkGV=1i@T +zNA6M!{ztnB5T{#kbM!P8+|<)Y#+{_d!b!aoZXl>$^7;U%JJf!n<|etv8t^%~Gd3^X +zogN}WkSc_>7#Yw!vi}|lD(P@iU!QZHfp!e^8 +zw|s*Le^UNa3Hztt(@5#JXIfP$lVVUPKqP>q`ASZ_{z4`J6WQLzq~&q@^L-lq`Byxn +zs1--{xhe?c+9~J_3_^X5EwgP6*fyFWki4F~zhJ;0iG4>wmjNcb77;fS*54gRVPc#; +zJlyHyI{i~Zx3_jw%L%-^G#X~VvOn@bb1iFGYq=}cQQ6(NFcU1y<|DwR +zZRDpB_lMQF=`tP-DoMXOQNFVXxQJ%@IWD>aL_TZh~T9YN%`Ai{g5u)#{5i(z3MvQ|sS-;qRd8UFq;%sbLLT>{xyrdFXk9b`0_)&+w%{eEqbSPBls +z%EP(|)zh#M-PzAzSPT-yx~VP;Ii0jTcJ9>}@r +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 +zTk;2#g%WU9P5WDzxG5pqi2^5Jm~@s}*F +z)RV9y4hc%lbpLGlslHPGiuFP6wlZt!ZYt-8 +zXhuLcqczy){db}-MsB4B@e7&HlLrXt_CyJx7DL`Y+$Q<^S!uNyFw$N>Vy(%_Q=b(_24 +zH-Z}2D!-P_;8!E_7@3AP9~DoJNR(9(>7QA+xMZ-xjqF$%Yy +zNz2ZhNu7A}S^0|7lJ3hi&|tLg6@RgC*6iuMD`8qxizuN*Nik^j+vdjV{jg%1r+Zbt +zo}CpRjg#5!kn0fC)RHg24!SIym?s1l<8-e%eyLK3(JUP+dq)B?OUoM+2AMFC+47?| +zT|s{vGnTH3II?5=F?NK3{TKfAH0oDd%dDaWaw3SI6p?5XK?KRs +zicA2XlV8|^(jIwMv~kr|4&5{k+7!R(+f&(O83)fd(>^~e;$HJXkC3=$7Vv|=-JvnL +z+lwv0*yYP&4U(I_;Qi}kyR2<6`Pp*X5`9N;zTrx!Xc4N|WJwsYbUhIDD;F%!IoaAH +zyunk(CEVW5({JgiE}lW5AQ|PWyai=G^~nq87-sH#0P{IY-ATt#AZ-i4&5JTAJe_!b +z`WJwWiWYPFEW6wJ#Y!-~?#7^s&@qI%fVjG~gnW|IOZK|n@ulk7u)KAqzhR)<&W-_G +zHD#%ftTAY)Dh=~l>HN9v=SZZvK4F*xNV&PZqpj~)i;<^2Ht#5?TCID=BTp<1G3ie* +zz!i%FyU(z~Gx$75FtA$C=E!w%$Ve=?9tJfUz%!g0lg{ga7ZmARClZ))yPq$)b|AEJ +zu%tiAsX)c(*!qvDFX32U3%e)+#!=JxIPhBxc!1#&a+FV +z=&M2080`*k{@4fQ*vKRkzGoelV9$Ch#7wt5&9gkMDdUzpYD#RhPbEcdBcUL@Gs?s6 +zw4K$72TuO@%lJ$Fi&l*qFOmzKUjeLeJUlcXnsy*_g`6#VhfFl>XgInH%br>o$AiP# +z(fOxm05`@J+|MP-WXoTnBMq8##!FERKzzo6IxPMO7;0fk8G)TSx&YSYM|@+@x~wXW +zqJB#C0Us-i=n^nBz=j^g$6~OkR?t2%!$o?4w6%|QN(6(AcsdBgIz})|;bi!kms0qF +zljWJE7fe+qOpzR)FU+A~S6oqUz*NSK`1G6Df;5W_HURE?HPvoBJzAx^s{-AdN~1%*p<`f2NpYQ3DhhJxW{eNzY}ck9ir~t%)o*Oc+Yv$AJ9RQ +z8D2$o_+8XByWs0J!w2U1a84!J0L1FV2<-)P`u_k9yhZ6Hj-NNlDq-`trD}FclP>y$ +zD2$gP#8~OJtByefud?wkN$PTKs|;_dTOQ@mKYg>Y1#QC2s*WcW>N|MA?=N*0qqZIka7qz53v|4oevu* +zswR2nhFNGSnIsA~ZA2b93bFyiva|D5fKtdxGLlfMx_%_N=EBC~ +z7BG_OP8|!mUcQG8P(d +zzXWrNwS}k^beRfPq?wB{f(@)Khvo&F@wC$%x~@wS4Z_;z-v^q}vVp#DWhFjaRTF|* +z;X&9R-w`#MqA$QX3d>Q~dYUw4Vr~Vu!RE1EZ|F`6jq=LrZbFlN{fFg)&R|s@p-Eez +z%d;rln|lDOZ+t-3VM98dAbxBGt4{`K@|LBJMwR3WtE7^~`~mv`?|fG3wAv-d3oKvI +zE)8R(oqzA1R(nL8ROZu~nH#77PJXu{&HIFRS?0t?+fXOlb#W4`_aUq_lyU{KMZX +z**r7&SxVeJomI5!SDZ9BkkK6Fpg2sOK%8Jo?G9 +z1L^+&xls;~MXqcSm39OZYh%t^@!d!ow=8(9(GPJDUOP3?cmDv$6Tumk-qX6au(S-Q +z-y)?j1EGPMz~Y!;Bah~6@Ic(%u(8HtDR_q%%y)I=N9Gk1)*Tr=j53xg`oyi}IutxfZASLWu+-i3F{Y=`ycaaao +zKk2CYr#Y&rLmpv~X3bV;X-GO~$~1CA5jXUy7qB}VVQUvRPqVhGHNNyTH^$jCOV9j4D(ZN +z9iyGVB}(ixzM?^}+W5@z6YV}RuDNjxkR((v9l$Ld&T=9-Th6O>VR2rSdzz%uBn@Jr +z4b}Xz#*!p*T;F(7=lyLTRMchN7t)y?dmV0PO;1Ie*H+X+OXt&r{{U$agoz_vOaWta +zv1T^JEc#inZA<&gj4fevnAV3n9o(F_xWJGxzyTKBIP}3%_Vl);Ho4AYSl2i-k@<_H +zxVXTO+zjafYG=8xOn6(>Pn6Knbu>BFT~4uORZBHJZAB!`pb}(^n}K~Q6n+zJ@rh|~ +zch~I2YL2F9Jj=v6jc^CEhdY_)vc|NdbJpw|TC1tLgU`;`sLfV*TH7_kbA5+-${uhrO|2EsghC4ZooChlNJ6 +zeOYKnc|6@$L8{}O{=0sdn@(eeVW~~ckVopVBs05eXo(%bJfD19Aab$YaZGOIRq5(n +z*+C&q?nfAU+7%AFrZ&nnj-dvtFd*y)@Abj0r6G9EQL@i%B}oK=MUKM;uvEp}Bg$2} +z1h)H&U-LX+NjoK|@U2E^(NPjKV7J%oaRVh*WaO)H)QV@< +zNLgAv!y5t*xFd0jpG;M0Ys7-Cv%I{W;u;Lc5zynqrnM+IO?N96$=*Js)A0UP3}(i +z<7`HkgF1Z*Zl^|A36rLGh~%CYx2gUz^&9JVP@!zvdWa|eN04AgC`vbQ)-hU0Fqd^$|01HW9@n^)fX?b*wHVz16kUL9>k@S^gCh8Z+ +z8fkKw9-2uNqAwe@()RVQ`Gv7n=SQJ>0ID|6zoGO}vHVu+_U;DkGO!K~65S35(6=#Im=%JTUp*d?upV}fCj*N +z4ZmgUoj>vKq+2!DF0`oH?#2j!Jpdtuk6}HOmHs&OhH29hbhUnYm^25=N<>DG%m&48rjaTloH~5pX#{It@zF^Zq41Lp$1IJV|C9i($aI-k~m%| +z^8;5)B_(4`DorSunI>j^d+&3x=N%(DT`ErxZs0}$0vA7b_`%j{AN?f?REMB*j7-Kz +zA5aIf^}R3R{{U8WW?sqk`CiBAQC+S+w|YX>TbAe`kkCKf*+vETnW@=rVij3)yJ~f>wg=ctbf94 +zGYpcS?Q~^isHzi5A*o_@IDavm5KsASx3<{TbiV~v>7HvTyyfGZdrKijDd9r$%YJgGb0Hr}4xgJu`ZTIkrs0;Bfu61{dQefNBnE))UN27&8W&v5L)=C(Z6C(*G~Hd?DCHlJR(}k4ydf@ +zEbArDJf{_N<+-4Gc_ah=;7L+eV)wB|(!ArEy70$e41Jp_wxuZ=Lz$2}OG`kG2$H=% +z%ke9ubvji8`jr2}wa +zw1q3Wf$@M~&(xA<-g3Tf@t0BXk5M}OJ!twa)t)Jq{{ZkVacjMZ2PPMunkdG2vpuc&cJW +z(<2qrdjQ0(@7w+G7L&3IN!cM#6^dT>=D-VnX9k%|OywD@U~KYTt+40wzrGfPw01_d +zafrcnBz(L70LBq-WV9K=V^nXY*@^>w*7m`rJshH1r_`*3IQ&BGwZASfw56a%QWdXp +zX#+7kki37s4G7Q_pKWWhmtqd$&3r8iIs#8us03*P?fKvhgc8jD9>_;aOCO1+h +zGE_}Y4Z$`y_x}J)OHz<>j!3o|K7gM1V5tM$3d-7clhQa)a1faZdCrZfIbdyk5C}v9 +z5_yB&$~~{JBLESE0T@gs1X#$6ZSBY9hX{oK0BG1chE*JZaI~XJkk26@bc1oZ-+V19 +zIzkaCS1eIT8-L>t6N$ocE~1`Pnspo4Y(e^9h}{w0F_~_`K`qH7-uNLJQMAQE3!B?u +z+@H@15)G1^&ZIV>e&hjsC`(Fjw6L=WlH42aZTaEpPfrQbQGdEOZfxA&+W{TYE$*C8 +zAtjx(05|Xd02ojZS~yH96^S?80i>SzG^0v!GNHAKn~T`)snY)%L$f{p4fqCF*ai|xq54do!-Qzct75Vv96s32bfY6}2H +zY5+dARt$cJ_rlVap2$AZuGat)*b;0o^ul@|DH^Gs`v5=7eZO2SDQSc^CGJVE-p&62 +zj5Om3NhD|28v;17HYe%rgaPFM9#a{JBrwpdE$x4%93xL;JfLP40Q+294L)$AZ60B> +zl9t#3Y%rMFW`=e;V|}}jM);94!FdCG5yDZzZ#8eI5B4#MJBr$H-H%MhamaPfDXw4*$Jy82=$UJV{Z5~@}7=S`K~u< +z7z4)k{qRCiI7mCOv1Mz2HIMw^LLoXhjEB;s+m|0zD8%qD9&@x?aHSa73afbcB*h;eY`5AoGF% +zL;w4YuFq!2u8u +zw4tn~HY_$8c)<{u5Qz(=ivq3k?||hal$urk7aJRpdB6eyNJ?ZjyAXQ=f*~4VAV~)L +zpWm?<5S>e^`bi;o-A>prmJ%$W2X$+o=Cr8>!7;)yZ%P_A`$`+7LgQd052QZ +z?Slto;2n{MEC5YGM<5-rY@-pnb(jY_mh3p~afKK`7(?a&OoAbPeq#0ycJ=v7 +zZ>^3LC8vo(Sr0NWH`p9YDo|0wtAd)8?{Ij;N{LSIW3vVWkzh#t@Bn}oag|%r%WHp| +z7Q(s_mQiA)h56jy2`_Xm1MGPHFvb%YNnT>W1T2ay0ovFI2!N5*d%cCdumq7Tu=+%Ek!Z=8XQG;`!us;3qFI6v9D(eW=P;K+M#Bic_LXM=} +zI34d}d@`9G(HUfAJOlC@oDdSy6A2~fP(nBt!2u8w^Q6*NLd2c;!2^V89487SBg$Jh +z%AaOOQbwiNlO17)Gub`dFv`0M-;jA~#E0+>0o; +zHUj4a1fU}tS+%o(E!=;$7ouK@Ju$NqTV?Dp@}-n*B|U%)%lTnJNCzfkA!{iFeD}kE +zfB;e5gITuyu+vjD$kYjckvABMi0qO{bF#mumDT+)5P~5sN-nw$+xfeGco2|cwuxsK +zKb^38!Z*w+R~%{I-0_PQsr=~)2)5g}!2udZ>7GdxX*+!m01yBuqn(YfBNBGEVSt1d +zr63?%PUpEeK_Eh57+u_{9qe$)l*#O!s#6waZPBPt7a2lvAmKrxhyIkbLd!tueu9HIv3T4hi}#p>V?cEJgT%6Rn=cN<_R +zK8VYqQ*TdegK%)6Mi3U#p&LmZh-^PqA(XW%jRYU47dSmp5tKlwQ=voKzkDxrz0(77 +z6(l3&Y&0W7sfrXYZ5v45OE)E2hWd8uS>SYJ8_r#9s!@6~i*xi|S{P0Q%34;b}76RS$-wqOpK>O^5 +z+x~cd>O<8V&96#GqyGSrH^KrE0x}Yn(%MX#t-u2a1b`@2MUD3S@XBQy4H1S9ao_2M +zxkzND4yMg}TZI?Hf|(J7;IlQ&&ASX6BV;xzeME&luXBM3-7o50M11fBfrNudpeaGO +zz7!__sR&tC{$RNN8{pY3lSQXsiolLb90%1S>Vf1Xh;7I8!3scjPLii`eQNYSJB>uQXMZltzIAH2{?l6J`a+17`0|^&j +z1xdjGAc3+?Bi_yi-N!f}A`&u0ETFR=d=OCuCjnSlO^?590Rt(e^8#rS5~sESx&$Qx +zxaQ;ia1`9DHSR1(l`GV3bb@5~Kyt0r$X&OG|@fa4++~l#WtXDs;7)+~7?5BUg)2 +z5NT&3#aM5J2b7@4%#mF{i9HNBz7J%!*+g%{E&jM_UdW?;Hzi5>;S`D{C`e?u_ZT4v +zgJ>i10DfCxM5Pj*NsYAH!`xs2Ak-dl1Jup2*o-(*(x6hJ(5nMGP*{(Rg0URbF5R?lE +z6K`>Vj>$)58%cNjrP~Y+>4DuyjH7a(oFWuRDaehK=_OCG;NfKh2yVeF@&5q*4lwQTNDTj4Xx&OKNBu)siSVB*?9xcT6O +z;RWn0Fv4L=8#zKXgYqMcBf5)#l;8k8TYWH+_E8&y?lCA+g(Od7&9M>`ToFrS+yZa} +zn<_ZGv}5KW-?%(snL8;NJ0fI2JuS}|N+6WT{#N8)=YWYp%5f}`HsmP504JmXj!k5O +zT#z^;0EC1j+H?X27bqL3))sG7h#xFSl_KG?oJV!I78s1G$w8rk+~AcdQM9ie!jo?H +T#Gn-*&`^R%+kch>KwtmaZ6gae + +literal 0 +HcmV?d00001 + +diff --git a/game/assets/icon.png b/game/assets/app/icons/icon.png +similarity index 100% +rename from game/assets/icon.png +rename to game/assets/app/icons/icon.png +diff --git a/game/assets/app/icons/mipmap/icon_144x144.png b/game/assets/app/icons/mipmap/icon_144x144.png +new file mode 100644 +index 0000000000000000000000000000000000000000..8fcd3675380646b4e395947a0e1ac786e264bd5f +GIT binary patch +literal 12364 +zcmV-SFtg8zP)u00GX)I=0|^5?EFY(V +zdZHr$oCN?lIyxpLBrzu-t&WMHF#(9O`gs6BL`M8z +z07Z+3b6QpWngAOm5QdG7St$-qRaK83188n;m-zKNetIBENOBnqNt>5SYb|ss7h~De +zqi6tCIT{R_7NT?jyb=Hw +zH8Zza1WvK5h)NA)NEw-2IY$=@&pZQ*=Hba;1IkYXMg{_|wzir>I(y*TTowv)qZuZHM9{16%;MH#FMcouMBSCvKraO +z7RKO)CV6g>O|tHxVYai&`YmP|@?f$Y7zP4E=HHy}T>0gtTGlVi($Tr+o_njaGUa*O +zHhl8dxwdhwcX{LU;MwnwozI4w0^!Dbf=$baYzyI{+TnM~hS*BIu<{-)& +zj*8nTZ(xdI_V_r<1i0lo2N1R(tD(%dqQ%1*+%&;8c?gY6)Ah@r0U +zVN4Xk&nL$vv2iXhOJ^@og|;nHky@v416GPu;mL`{!P +zJP=5g2y3cF_l&$d^aO=WC!l8_Ncl8@eqcsXYRawguU +zB1%#-w|kP*M49JRAT%a5!H +zV=6LqlN2u2Z1cS2@+2WkJU-ks98o30;mvD8Q#shF8emkt_kCCO{lIQDDw~YrdZ0C4 +zsmsrU&&$-7AZm4A0c}SKl@_ttV=4=-fWna>E)^*4BqW!Tcn6OogY=eST%xwL@=YZf +zOVa@A2Kb2`vhD;AniMn%N^a38RXEd%e?cHv4SB<=RN|K==#>E@MyO|%6~ZdO_)!Tt +zt&-lMI_}5YDQ3{6w2CpeWJkDY1sV5ac6A*YHguKnaDuoh2au&S+2%g9LQ@FA%92^y +za(1S;NvKE)Cz>Z@P+62xw&)c|Q#VYB8nJ|G!GSiE-5x`#`bH10q~=a!#lAz?gQ=hU-DhNVU&3p2S +z{OT3HUV0wDLEMHIK-Z75cH$&i2wKUf9HpYWle-Kb+!rGn*5aZ!L{yjw1O{KW +zhGB7q_nc6CQZD#5-O5a8$m@jn?c~6 +z4LHgyxbp6#YFd-?{1Q|Tb-mq=wVAYQRA|TYX%)+8AyD0`G)=hTfp8rs?eXjsOOK?ZU(*Z)0p6a@uPiu(6{yCvSM%_@Cfr=}$ +zXNwWB>bqC)bR1j4fe5y7GgX|3uL%*3qKs`UJy2Gg#?&hXXtpp!3SB00z;idVK0~^8 +zio2_)^Lks)=kuqn*@=uYZQaA*D~!p33?AOsvL+{{$tfikZJNSlXTZt2X0g{cq2(1-5Q_lH9_ +zblqWqA(HF>#OSf?pfb;GVi*XJY%MqQR}&S?I9uUaBH=8XWwXU%gaKhoDElCFLy)pt +zl}Pn{KAjpEgEVK~B{^j-CCP&DoKAn2gZ{LDjL)DhfCK=AtIR-25|shf>nd1j!SL1Qf~tEny`O@~KDObocB)QN0XIg&y{NIc2~9j)I#C3o +zsMZPHVe)h&G>%2Gng&A_GX25IY2Hc)7eNa<5j^g1pfMb96PHNmU+G +zl#$^C(Z$Ltod7MfHb1ZZL~m$g%CtyZX!Lwc#0r=QQ~%~?S9 +z>-GM8-S5wWbv4!%VR+$_+Um8*5CgW=ytElP{3ujG7pgBOna{+HR>6PEG9AJaKd( +z!gPg_>+!tbK@rOyY$H5vb}wYwL2fkkuY)R^IyL!?dICYCT&u;S@2x0tW;ofTrdo~~ +zT7J=xoKb)t3=e>`X4Ec!`fAbBktln(fb4hs<7sy~?T))6J1?JiAjFNVA&OQyj)$lQ +z)65EfXxNzVRyM^Yw@9bi#AxmxOP8(_v3e;kLTT{Q%fy>x;~%w$7^LpR-J>UpGosKbqLhlVzty`c;otEVi{%D +z44P#9+AZ%#L?TERKMB%Tf~I56ZT82VaGb{Fbb5Y1{r25ozyJNeKmK-FpXJYPy*oXh +zpz}Hnjd~)R0FiJ(a|9-jnl2KFCwt%RC?YErD)yB7uXqGW3<*^I1fsDcnvXT){$`5j +zy$E_16s^ki>4 +zefXMZ3;XvBMgC8S#>kD3o-{^G_S30~hZz%)cH#y|9~qy2`SzRdfB50sZ~pVIe_p54 +z^L6+9@%bosfY|v1(=4m8)*sMMYGzY#_qx_=K3I-tBcT7YWqq%0V`_g`oO8Y#Wn+;(Vk#_xq*KR^3na4qW$i#DPCmJ*PE5qpdoi2F +z*%W}1Q5t=;@Pp1_2N@%Sx=vuo7{=`p5#wLzZEuFWBC9jZ0V$7$RV*lJEqHmP0L)gM +zM_5u+Kqm20Wll&{q@-T!0Mg-$I2~nK99_htH2LTwdc8vwbsbFM({$Pp5$=+}c!LCf +zWrg2l4@)70nw3568q3;ys337k_^wrzt-BA$z*I#ppnP8<)o=7J(=1BjXg-@p>BTFb +zC4>l(AOHxS{1zEVOcSRSO#H}MpvwR$v#$dx6H2*M(wOA6R8sj<@PwQcd4;C7Ys%H? +z?z5Vg(R?~fv)Oby&9XkoNNCP3%9e-Q7@7> +znZ?m4nU1E>CApx54?vKiz+!v3h4Z)D?hisp;2I-b5Q(lKLq9(%2#G2di-Lq;xQrY% +zGltxts8K|Q?b3s&E`#7&DC>IFZ}fVUM+SMeH5kz;!%~5FcR?@4w*-Mn(deredW~5h2|q|4RIk2QM2=C +z2IWYuX5gc%{}3yRAHf44UjQ*c4MfNZY#kr;2A(oByyYsWKI4H+R*)i>A=?OZeKoX> +zEyOi{IQD%*l@J#4l}wMP(a@+SiHbLbg) +z-%C)?Wksd0d@b1M_FVIo%U0%bVM2(pySSY`?v{FsB0_12+i?mhQ +zb$$PRG>wx8TA!p@`jNLctBtDA1<1>)N_#OguI_gjC5O&lh$Ubqml5Lai=rJx!dVLc +zGD?P;L}=9L%C)p=CSi3RC-L+GOdX@wvaf+_w#d;iXSVHtyBa=#c^)Z^rd$b+OSv>? +z-ua})Ld;9dR#7j}veGh4*D*17!L>kX_sj4)6W9Q>NeeOm80o5p17 +zl*Rp3hJa3|?u)#l8=HNT#3rVW7J%hfbp#M4iF=sZ?&J<+rU24q=`6K_Au3$QBj7Y3@VA;M%XB18sPr~)X}TZ-(5MDC`kYY$J1tB-O|ln_ +zrcffN$T$7EjJ75baeU%9&aA=LLYX{PqmYnrO=%gS;sVMyt6Wn2EeQ)4c9OTlF5_e2 +zb`7?8aD0`)=1*Zij_1*c11Af~Hvh +z22PQ93{N$j5%5`Lxniu@|8P9C5W(v#KF*G>j$y2m=v)nuH8QzRTfoVKpQgJGM3rQm +z*V0KS>bgvs=&J_weSl~o3#!&U`aD!lAmbTSU#z~%eDIMSSXlH~0;Z0x((_uM!kO=n +zx)eeoM1aAi*rSfkz*C<@nbaOo=qV)y6Xk%ui;pBsxqVhLB18@fQk}ML0Cz~p6=qZ{OU6Q~fb_=R57ybkf=MQ-X%|o>LTKhpg +zj-&W7kbmCmSv$WSszKW6rOr2ovm_HAc085Bew%}m$A=>QwE(<4j +z4>AHnf@~vivm8(*U(3@XzZ2Rn?T>}X)4F1vn#?7DY@D~4ic(YO|e-oi%#k{XpFX%mylgEwXcrvzV +z1d4yzpNz{tO(yrscPIP5pa2Sm@_2F(+6R-#=MNt~Powz5#plnTFFsVQ@!sBKZ!$Ru +ztO?fM-rl&*x48P&fjT*G{(W|Ia&&ZZ`mFWr1ZO8FNA;h*1t8qB^7xM}uC*tJZ{_Td +zhxh&M!S(@bzVhqp>W+PHu)0^=U+o^017xkdGuU+8?bY^byS?!tPTK!#Z{VxxK3MIp +zcDvo(;O-`m-PH%JNmthD_}&9`ve`L$_w-*_PfyQIzdZdf&YzxDj6$9=&|>B7)*9Et +z`-cy4*jn3q`1tnbaA&+yS-DwR?+*7$~wb=XM4Cg9Co`OM(=TK +zciR9o>27wr!{M&e+QnP#;qZaIzs<9D_n`G)*v4<-%N2l&{^csSU-ckj+U +z$G&@Ke|yj#-?h(9U%os#!pqso>6e#h&z?Q|yP<|fHMv$gNY>-~e0j{YNm1_)Ha2jj +zAOBCcwST3NC1JD?--54*2#Vgs2tI;}6GI+-P#}p-(k+IjbDAbC9OM9k67dm5qVcWk +zYm!a&xa*i1lZmr4b7tp1+^Qy%{b}#Pvby_r*Vol`t1h=cI4{T183NBpq$G~_dXtpO +zU}wEKeJ$-x5=*z`a^i&dW-KO)MUQ9MNzO|*s{s&chR1FYC2#bv@iHQFypfRg-9I*u +zP;71Ne%{>1gX%)XFHB*{>@VZ*XWx&%-gvd~eEj`-GgLMbqa{g_{XQuw2$G+Y+31iy +z%1V+DXuC#fBsND(0v)*g$K}=jn^S>3yt=x)(vyrwa9@i_QKKQpN{mw$qy`2~Wo%Vu +zHH=L$Ca|Kxa=!K9EyT}qGk27(yGfy=ps#5c-JHN4aRG +zR`N)GlfXsAK7o&xX;?nm+{Sujs*&~vNE;0SB)vQR=aUnf-(UOj>^DOE9^OOC>IRiM +z=fS}7(6zlXXr6!>vWV-IdK0don +z$Sp+!K>9{7?r-A&ix#w7jWiuqH`2UJU|)<}qxl$dfN>9d^5)^h4$Xgj^W?=l@DR`P +z-W(w+LyTh)9DS!5p?PIBq^Qo?fW+nO#{R8~&4XVzR|s{!qG-F7&0ilG&Irk?=i{%T +zZQScuw2k!#6eW7-oT!b-xmw1<=Ijh?svI6Vrq{}H)E{8^B%}jcmgQ4OjXNxTHu31; +zJ2PuQCMPW$UW2`4CCownMjY-)PEi;;rRt^p>c8~0xC0k#$=0bs0=@HNd +zy662b;28+)S$JN=4ADu)7>qXJ2hEIv^1*P^Lne^C91nm*p3e~N&cut4J3PNX@$~Qe +z(3$tXA{h=MKgn|$lBB&ERSbjQjNgQe4H=nZ9~kxPkcqa4#b`Hgfwn>{4LEyLDIQYk +z)iCI1*HOGeaZiDfLA$^qXo2tX2nZZ>4@TDP +zIwT;>LNL^zaE#caycAFqOw>7#kR&k_0S}vjs#fXG$Se(ZIW~8C_LK19(LO6gp@#f> +zkT5hL6$QZJ6FdRp7d$bNkvut4_;O5OnDKtfqT9Oo{X>7Jy@8xVCi;N&W97U~{T0x< +zl7+@G3*i`p%JF)=S7PN1M7*A|$gJciIT}Qa7W`DD1>BaQSrL372~auy^V6eKK@fPz +zqatAhItc>YDN2Av#Pr~oY1ynSWU8pKK-3eUDAm^P=g;SsX1l(2W#!k;O7++x>OxTK +zUqkdLA+)|xC~42%zk<>Qg#(5lG4YTR^#TYYd3lk-nJ9`sR$|cu>6= +z!tf9;AY||rGEuaXW|+}K +z&wQ(@3Qe!>G}3{S07CC +zs1DHJ1EN8N>z-zu?);yIwq42uNfEAZ`&{2PySpux-=404v~+7Nhv&YN)(3Q{qy2()QM>sD;VH6=~eHLV?`I-Rbjq^7jJwZ65Yy|hKw)iW?~`+AqI +zwCj3LS6A27&i>BH(}{if_ipF%?%mt%r8lnM(A^lg-c?aC&{fgeQc+P~)KSsiUaG5T +z*Xde{%R4$+OG}%q)kWpyCB?;XlS%{gR@LYz0N`5>TUt7F*xI4pXm9W8 +z=^5x5xPALReD2?U`L)HT16`?Ok;zsm-nIF +z?iuLnx&fdn6dQ1>t-6Yqj@FKr`tsuHqT=G>qM{k<+ +zl}{%SKth3l7YAi8j%qr-7f&SOclVd(g2BjKcy)PxIXtxx{IZvr-rJjwC(`kBJ`sm* +z(})}QDahd5mv`@{pygBfY(A7n1x=+=S+E_NoJ2roO=vO{(r7^Rb=GCEZwR_(CnvM{ +z1Yk<16Y&J#OeBVxM8e59-JqnMPKWP@;PUe7YGie0COCh*8a`fFh&*#Lez((Y%MK0> +zyPZth?e@Vr@##c59gnBMYj8aT5z2zmNo4F#!rf={usNw#TMY=vU{VMc3ZuGDA(}R7 +z70yK!zHN6f!zhPsNbRuM98R~-?Q^=_qV04pyto(+hnK?P<;dJ}aDFBdoDY9F^B=n1 +z!^1WkbObnuFp(U#G0tJ9JMD0!)3$tDGx$PNHL38NwAw0wWip}tI;1b7hfJpKDg>o5 +zb$0^@WPB;yH7)YY91ODF;BxL^5d;|V`NUg{@AT)@@ZxH4X7PA+e(BG-h46B4CVV_M +zGj;qT!!WjCTbtAEbowwmVT!^DuG={Xesc_J8x;bW2KkbWja3T4{#q5XdHd=hu3ZL& +zTc%M6i+$aVNUv8|-I}U4WZ{VnvTRqkH3R>fS{ffZ4Z +zkajdggrYpS#26fgl0ZowHv44EmfC~J1SW%(oSEv#7>H2&gxh&Q{~eqF>EtqxpQczWVU$ +zK)6A)UV~f)?C@Zp%XhejXLoOl%VoE=C=5!~I9<=tFo#Kl=#+Y(^le7?gK +z(}yqjUtqhxb91yB^(K=(sF}{H!F&kIx3}PEFd2;ogVAs>9pBEz)oj!sRtr!#{dRxy +z8yA8h@~`mmimSq2^It35c8!XxmxYLSpUbj6F8LxLo|1J&KYsUgk1IZRcjZ3=dt7Dn +zSFYLE-+A$uucxb7zXu>N-yhFWdKL==sk)sG2XlPP#=SX8QLi@~&Sw3=WH7BJUyhEi +zUmhO3fJ?6q_PLl~f9K`?v*+9VC&ivD`{G}LxFn2x>^=R_Y5ag1zV+no9{*W^v8(Zx +ztNWjzDB$bA=>0aAUOmMD2jfYI;WF|= +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 +z?E0M6gDtczBXq~>;Z(}Thq3$ +zAPt*4wZZE=Qhwcwd`ekm+7sf-Bfe<#&T9Ob{d8csZZcq?Wha@^O6h8!dcN)jnaDwb +zl;Te=@{?GXg~0mTg*=}hXH+Q~(khBF_j`vW390;k5*w`2QYzY*Qd*SD*jNY +z*Ky@MO_ap|hZlkqtS38Kce-z|92Ce-Eo7N!r22DEhabFPK!A-e2=@u(&VVDo8o84S +zDmD)`ta4tojRK1tt5kB@S-9*SiA5mTbaf%9ItFOU>p0nH<_3_^OB)2Uvo2LK{D5xZ +zR;MS7GsqBEbNQJ=<2&e8BR0r4!D{=d0?~3O#7*p9V=F-D>}$%js@q#%>gn_grt=ic +z+#S2T65P`Ov*6vVh>NZ4Vy?dkDBa}48s4^kJZwom_KjNkcn_#Vm|2HL1hI=d1Vk5I +zS1__22nWKnG?%C5-nr-G$pv>HHr3MqzxJA{cYYSTrW)wZMAlkl%;3^v$zP;#l`TW9 +ze(VL^{4rFi-0gX4Sl!~58fmfO0`zn3c3&mUwYv&Y&n7Qnqa6^loX(=@4Ll&aCD)WQ +zcB+lxqK@>~6JI!x)+X6k+P&Koa@jmaZeS6lKGKPm#8Fs!NCV&X6b5mX{v{4Tz|tAf +zY&KpPYjTaK72PT^GNERf>@1lL5L)BLl0FB@+AQp`)oz((JkRrH1I!o+@dlmTvcz5` +znAtr~zaobwaLsG=kSZ%nm>lu)MnVU-Ure#c5cL3yYP82W`sar-@dgkw5D|^Kq|){S +zg3wZmB;RMJ%JB>sQyjgJoC88utfhm_k)p6;vQW2X1t65)O`n>P(9i7*nEF5812x!i +zzEB@Byh^pSZ10DF6aqyoA10snzwU=_@x438BoUUw>4awC8@-}&6K(F27#s+A8EgT3 +zhN)OO#Fs$kJ2wk5DZzFk*BV0%8sg(PcCc68KAnisLf4fOS;*$ +zp}Od>d%R611hX!W_Hr(C5Kpe$oJX3|uVm)DX21%y>0V<0HDh?VN8Xgr61qB5h6b%m#4h{|u +z6b=d;92^NA2?z)YBP1jrA0G}a4h07V8WIc-Fb^#s77#KJ0003eCnhl?7$G4cDk>^3 +zFE2PJ8yy`T3kL)gJroi*5)(QT7eW;gG!YyZ5;Zk71Plb5c5pu}AQ%)7m;wL>5Cs)S +z7a|)LqZk38r>CNNa-V;BJ~}xlAQ~+t9-t=zn|pPl1^}ajel;s6F-aVea%`V9157g? +zh6Dkoh=ZR(0bfc*rGI$%2mliq3qe3X`fC9AEC7vaV^csosf~#98~`&s7jITlr?0O! +zR3V^rYI*vo009s#PqD%p33TJ$1QhriI0$f +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 +z3nUqAiH<)XP){Q*v9{=3sY4Pok} +z02Q>H_9eQSIO`BD+)`3Walb69Zhm>l1bE@ChUkAd%)mn4(Dyx0Fd79V5QRUN0tHvA +zh30P*A?k6G@Q!C44{^+B=SjlKO7tPI;PWIsF0Fkz5kR18#=mAapt9KotZi*LX!mDp +zP%u>Rs6S!Hake3i+Gk0_$&UFx!VAelVORmMeS34{FgKQjq3gSz?{~Fr=dA3G?(<4K +zkaTpO87bmTlR5swi#iW{lFWf%&U?_%(#VutIwhw9#(74QuX)zOyho_efXgMipWnuz +z*3(|2M1zN627Nb=d*g4QgpUN?e}n}*5IV$qA2J74(FUUNBgI0Uo${g>nee4Ojt2!#A?g0PiIo9u +zi0&MneH|4P-!%wsqww8ILK8wICt^-D7SLi*g*;y<=mieGo8a+S{^#c-aL&(1o^}LH +z)5#%>Up|5-2U%uK}5V;%*)+xe_Vb&{*tbahiP@mV=%~*#cIJ~ +zJX0Q%G*5Cm(8 +z;Hw;{kQ!>`C<<&Y$Jscsx>3lBmF{({7B8>wUv}UA*!{Wy)Z;eag6t=8!jIG=={6rs +zw3!&`Ld)GmqYE+!y)nJF3msO?Mk1c|D%-$Hs9x7W$PGEk!ccu|EnxAx+DEum8S$^s +z7Esm!L8@sM6T{+%V+9zi)oTCv<*|JE`uO{|Z!c@I7bIVi&V_BB*z5@WZnNH6jqYh} +z*c&nx6AS?acl&aDt-`MWaJOPtyU{03XDF#1-srUI1=fS?9nrxY-*5LPm$7-#qDV#w^cz3*xYR1AbO6*WAY +z1SY5f;InP#2tbF1+|bI%`w@d@DX?Vzsh)zM)~MrDAXpzlv3fc`zPw#FZy(p|>GXKn +zoG$;`W@`Dp0Puf^(@5;13jzHi=TPR(cnWU1pJ>-b{*e0veXBC4B`P)Xpv@3KI8N;} +ziGa@2YKigRQ2aF;R#WoE&`Ib7${>g%(9wSX#A8=pUav1No73*Syk3!wX~u40e(LF+ +z($Tqom;PfQqUvc{w5KzG;(ovS3PJF#hIp1qLsZlN@OP(jW0fVSYbi8h1!+|o5ty1> +zt;rje#)u+`RU$x<)EKU(-DdMxo_3e7Z=37s<8hiMDU0~9A!Q*5w()B49Tb%4HCX|` +z^YhnDd3pKxKs#LC*2oRw-~V&3;Vo6m6u8`tse-07$aJKzxG|)mI;B7P<_a-%mQCLJDp$f?$kN-#xyRWqEyhE?-ZZ*XPS- +zttLltQFkQuC@=u+?V;78dYv9x{i3OBXDlJEqRjk7fxcX4GyMMme#C-f=zBM_kC&dP +z$hTeqG`At6Tys%IN*%K#&>Jb@{OR)kx&eU8^Xq1Jyq +zcWr79Yx|}mZ^@gv}h?8-wCO`)A?SZl!f1e)aGe@wE{H#iF7XOb; +z>w9qaxj7pRtGs1+`7_|_{KD10oqen9g?3A^` +zkcXPBV<_9Dg|elOE#x2UJ?DO(tVyFxG?vFdzWd!@=bS6`09aXgGY8ZF+F(4uBV@v& +z1P!Shp`oywIa-F?Ak84AiP+AcY-AGoRshhS&R~v!*Cen;yWXJ +zwd@xX46)B{KTZRK4ooNO#n50PM?7IkRVyThtN{iZg`t!Kj5l1sYqVpDpiE^5G_Oq8 +zuSSRgQtVf4Q7xOxV&0V1)o!=kNe-~~yM91WBnBko6gn|A0zu{00L3dRJmsqWr8%*X +zHiAwZLJ|)m8Zem*Ze;UN>7_?80Qu~6<=#<(qqK5LT|&H{(TbV!GjYJ%?WUAA1~6B) +zbyc^m7yuNC9ZyrdNbI4sI!wtZ8GQzqTFdD^?O6gfgKX?Jn-NFqfrpWnVq*I8iD# +z@f0`sw&+M%b~<*13Wt)8GDlCdx7r_Bo8<7>qG@Y_xy?!*70cZj=|9q#Bvg7bWnwV^ +z5jE2s5Epo80T@Z-kkQZqT%X&nkg|gkdk@6%M2cwC!*MjA!_h{hfFXv^7>)r-i59G_ +zAk#R~&3$(Jty +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 +za-niiBCGB)UN?+HG~&_Q+n;V1 +zU0v2d+OTw=WHe!tgKVku(#Ic4r81W!r|~MNOeq)}jpoJ-;@FbUlXB$J_;!15c5iR{ +z>z6*I{@&mqB`ft<{Iliw+}kKWQ|M=^QjMt{Su2sxr871@Rx)(jLLAknc5D=i@Ace2 +zoOSp2Tdh+~g4XvN#Bg0G7ytifdQ*!iMCZJD*PgG=r2_+UI)`<9Nhyn>`Dhb72>wB- +zmju?VO=&tuB9F^-x-JtxFfkfVUtqm#Dt*7dw|#j3>1zT<_O`cwJ>0`gD4VgQF9WBZ +zH!=~%?#B|{#ih53;-SP>rCbL3SbnP@+&RtDG-O-Ve +zr#-xbk^m8^c>bvR&1tOBV-#pMCkW<4xGV}t6uMj$#%U-^&GZYEQDrSY@1b+3ajB +z*PuQ_LeCfSMFMc~u#u=KVwfZO1)>@*Q%jZ%L%(V*xo#LOS*1*7-W6`>hW-Ni87Jw& +zo98=7;lAx8xNmp{IS!!4)!dYiwwzhTvuS2J!efq6_1%z94Bn9HEs91}Nw*PY<}xM* +zaM{{_uhYC@_6#Jr?CqA2e2qN*a%K$o-B|g7=k4||3bbpQ408j)vL>ZRBlYf4QlTV% +znydJnCsncv5X1}RG9f={2tZfzWC?Ss$n6Wv`{&PC0p4FSk%2W4Opka0*h!F?lz +zD#R!v3#qhO)vCtKra4l|4&QT|4oF}pxsjZ8wT}>RU9K1?A%-Mqb%`-})30A{4)+Z~ +zqN_FSxsC$xyn0bXOiKq2Tf}j>o2r}^RD2TGm8v)n`A0_g!DWa6SU}>0V7dt7iuHnv +zaHFy}d+_PYzd!0Y&_DcyqH+ghstX2|S0coQB3Xshr=+0JQaq8dy_S?s`gXd)o +zfb&U`RbXoY(HC71Q=vmdGDHR8kS9^MvPxmKR?0bH8=m!NRkc0l9Kot}ic{!j1>5=dI953F7fQeC3wXXlAlxwjBZZESCR8Jt_(*e=Z^Zswpi6I{>1QDbw9OWUQTUmubNKVfnkrF#~M +z9ZtP^^X|ry$KSOyYVViZH!vy?2}%8mwv9ZOu#6?NJ6GC4%a+m8?lG(Nf7}=S +z&0T6-rZv&bAZ2h=OJ_AnYg@>slRi#BkH6kOPC^+^QvDNhP!PKd+gnJJZ{phef_4hn +zl)XO9=+^9<*_+KSXusmS0i)*co~w?**I1GlN)Qdd-WnR649J7LHcZ%E>8wj{*TbP%vk7)f +zyG#bGj2p3%H5<%&gWhB`8Vs&9T9Y+QPETfW^(M(JvD_q2wY5h9c+cH({hb&*ktRUT`oO*%golm6*j^V8F}r>AswyJLQS+CQoD +zpB&VVYPE0o@9!TS)M^I@wS(7Fw{H2P`U(I1bkwe!n!i0|GTHt9$*9o)?L>{nsKf7{ +za^Syza#E)^nC&{R*RGocWz1&1-e}SrHTP+Pntm3B6=FcvZZ?C9T}Ie%0`{Jm07n=m +zK%(~Oq~EV|z+%cjH9a*oO=KQ)zJ2@d>RptTJ1gu1_Qwb8+REA*TVz@G$K(C`FORnN +zx3;$S_TC|aNsE&;51#- +z;78aIrin9an1bfH=S@#2E4RjRw!(#1t}x4XKs3JR}+ikb#& +zkw}9q8lW3l;zD}xzIQI1|9pDd{P3aKJilmOeBjH+XJ^GTltzP9ccT^qrYPVXg`m4Lus>2(N+KtA7-B>6XGy@jGxS?7tpz761 +z0sgu~*1?c2m{6^DC6)*jpfO99MW~PyqegwH2%1UK_8Rk5U6o})Zq-G(!e9Qb?%pUoDwTEaf7 +zHIz!FW>S@uCsxQ*5O3*rcH^6%Fcau@`%E$(NajF;crw7`C@K$sR5D3Xpxs+uxF`#) +zmLe)5$H_cjZ`Zl9EOVSFKceWJJm{UoOa}^AD5jLp!#{k-k-+?9t}pciedwe>mq}GB +zh24}dld1T^7ON)}OBFmm=!#UV5QOkZ=9kI;GmzOffUv=7@7B^7JWpQvRl!eRo%bgr5i#cy#c@hqXPC*+$X!Jym +zM!*STt=w*>$Cm-6Gl*TB^~6#$n2S&7Jx-122d^>wn6mnOvk{V(9Py0cZ_c=L*17Hu +zAr3MGy%Uckup_y|#Ay;b=q#GPW=Ala@%-CURcK$y5H$)f3oV{2H#w!H4tku@Q|baI +z$m*c2$Uhy`HZzv>0AZGpI6~!-e*iCNvP31pCD^DtltLz;dp3pOS>!B$>P{ch?VQet +zCeQ=;sbGz13EUA4x)_Vtka2@pIAnf;*^w9o6h#;jh-!)x=nfJ)us7~4CTm}x^GaJ3 +zAY6F9CAU=>!{(w5v4o2A&=oC?>$M@Y1cm?b5sa9*@hO99;}wt`#bO-aAp8KDWHcd{ +z4k;3E4a&;wdslNlNq>&OdBO3V{Uz3&GfkMT0oSW|2FJw2F+!jd@-Kq=@7! +zn$RxjBpq1n(_7!3%k_(ToiEqb3tlKgtk-#tD~swCbO>A$9q1Mju7@(Gs2An3s`S*; +z*7>93{Nk6Jg!_S=nZy7h(d7;hmgB +zVF*P$1ja%rX6OL~HY2~#iAnz8cPCuObRf>GW1{xrqIC4rX-iS-f}r+#Jzl8ygl3Zy +zFFWTgkrVlL83g)3=If%uD{xi!pa&GS#n+X#A~xSQ;$L9kLk#6e(mjp7$G)Xuw7}61 +z$&@h^Sic$)BWq?m72<_exlM!}+2;y&AWHr8}Os +zd(-X_66zdDsk3Nzdso-L?~a@CN~z& +z->u&JYW4mr{#yUh)4%LL{nxs|B5$MndjH|GonH~S!^Hqo?s#wm1<{%Po3FfFduT79(2c7M@P%$Y;p7Z#ecFm%ig{Jte;c9 +zprfAuh(Lbu$bNGF7|#9SyPtk~hza=-?*Rhu>OQL9`~dls&_JwIDr=Rs^~%axrGg6F +zEBkov?|WW(-A5C^^410J{p){ze)k?#^uf;A?R2km)0;x-z3y$ZH96YDOpDBUGlb58 +z<7m*no=i~34Vi`}%i#j$#L;9t93V=1?b(1E42H9-%j50ka(PnA^DIdc^cC+ufBwhM +zFL>*%=Rfj(2ag}$TgBhkDDPL+R_>{uzW3_Ylc!HmSAUIfN^8|}r5cv2YFEOdf(=}V +z;@m|pi1>)3sGgu`&+`0Y^JYG8cRE+?0h0R7<@WB`Xk+_&Jo_>qboRQP@oa&tw7fjt +z+}OP84ZbW`1@&fM5Yt_7b9;HYSnOen9(LNF=EK3>vbk|`a?(iCyjH7cS;9^OFv8EM +zAousnY@^oURj-0J2!{IpI^W>$6(U-@zfvlrgDQtX5O_479fjG9l01%+dV&uzEKl=1 +zO&g8cWIF70#=Z9Zs(X31+2V<3{zm5TdOSqweAJoG=hv6JBmVMd^X&K8__ui*2#&_> +zMSHwB+}*|MUzRNKAgoDmI7C37{rBJfey!H8;ULfwsK|hVIA{&p0-vU-%Yk#Di*j|{ +z^DFnO>uW3PYbBUYji40_;B;)p&d5bclA`5e3j@>hT7$;p+5dR4J?TwvCfA$I!{gI8 +z8yn{rCns-OXB!iQ0Vdz&<>A>ztJT_k^QP6@KD@jcEM_+o5OZ}2q?=8w-ah^1wucM~ +z279yiUUxnloE`u{qtO5lDH^AGT$i3;245W{0WlDd5U^GALTF~adS3wW5MX*9?#9+? +z71e`fG@k≤uTMa1KjON2Bwtw;vl@Sb@jZ#fObch`iZsZ69BJIN#aX>Tm6wZXb^} +zmTe@@<+6E9T$^Z-N3GWG;pXHTJ;J9M8lNr_(qQrd`-bsoO5rf{WNDtz7IdyIj@8J* +zLP$g?LSG0HKWhrQeU_lZ+G5|HYbx-Yc$%uI6*&zm2VHuFMd6Rl&(6gKyGiF8_o0c +z6S^N1=QVZ7S(+wUMj>aBk>Q8}$s~*daS*Ee_q-BA6iDNQ!pGP;!!@Ddb?gGN1Q>XQ +zz5y3-5|xzJ^a(zz0Rle|1cRhcrID$cUV(4tA%n?V2k0nk^oI>?G_)%S7w?&Rd&wa|B7tk; +zD3Fj4*PtQ@Ew0NYT2F8}h9#1}G)VbpNgbAFags4j!!MZ>l@`az802X!%Z-{Eot9g= +zB4sv`T!}V`o5rVtRx`_OQTr3aDfgFa^MvllL1N_+c^R1|j3QDH-aWihLIsf}#0)+$ +zdjbe@Ssgrr7D54wfMQetPo8ntz?AWnj^Gg~2?GcmUC@9FLda!m&RmuWQeL>BrhtXx +z#cL{#^Sa!CZR@F=$eltK6b9lm=DDMY8J1_MI9nkaN?OaAiYH^JHEK?nsG}rSWME~2 +zAZLuw_?$$M-L#J22W6m+aFD?nMDY1q@=QPsl2YF@4lEiMhlWlbJ{YBoJH0eCNK*_=IjD!y2XJ +zo9S#_mc@Wbl(BL#AZR!NVs)mug^Mti13-q%Cl0s=T7~TihjF`zhjo#~0!`JE0Uaq4 +zN>1|@79=vx2yxvNlY$Fk!$t-fMpyx3rL;tOi#9X3B0E&f#8-felemD&CY`Elov{*l`>a3so4$ +z4sKwTL96)^3pgi0=@aI28cBps_Ar)UrHX1 +zKI!2XLKm5)6gevQoac-J0^u2DY{DtMNY(~TT4mrPj$%8h&6EdIXpo#pBZ*rH#HJz& +zPst!s5*0*BRN2xo3S;LA@3Dd?u{xsN_;YbUM~A9QX;M&xyBLV<%y+;IJuE2=6voPW +z@fI0(7zO_W9eCh6g_osreWj#OIEE!}Y>radAdw^@Qpr>3IU@&-aHM|?lU1eA8UQ@Y +zp)1O2*aHZfD746mRuDqfxIFQ#WUSizu1?`$=mtZ`0xK|2G1#O_!>fbX7h-`)AhsHw +z-@{_@8Ywq8ch`_)`a(J_e4)Sq0M^_D;CWuP;(5NW>N=>RQ1aLy7(&k4fe@&kr4i1y +z#3I)Dp6Mu*!$YxSOOc>ChJZ;_xLLhN!T;?p^y`eJ=#M<;?4QX%fp!C^(aM&?=-O%Ifro&>Wvv+G2odY9u3naDz99H_d{Om@`%QhzU=xH +znq(3~R-DHRPxyBxI!^4>YJl)}lp*y=_UyBQ +zeGN66+;@^!DhN +z1fyji-mXC?81gfnrxObVXwTZ^og% +zt*!x|4q)+D7zb3DC(*%LCEzkozXbnd+RSns$AKU!aDkElK>>3A5QX~HP5S;Oc7zW{&@{%pC&3js9Tjo_nv0_-+9H$7ah~-jkD;arWH5uv=DboXRx4fMBVcu}Nec +zV9$HsDGpyf!wH>deMI-%@&&4;%X);H`~V-TxTEsND1=kL`DyGS;IP2$eM^}&_FAuH +z*P73g$$)-yErV>_u?-ExD#txsBDiky$ZBP;ZKmIupC4#;C#W8?<0q+|5x(vWgcHEq +zSE{<5B_LWobM=!sz7o@UB>OpHZr>@8zt&N;o;Ho0wBn5&tL)b2=UZ1WAGqw^y5N+fQv{$UHpR5=^x3WYSE`SgEF3|G;984yA +zEG`FSp&+`(?Q^vL)+1awcS{{=|CX +zdhHnnt0KIkKl|776@3+t6tNlptbOk&sj$|y>xo`2y-e(0kg$?%)542r6>f;Yc5CeI +zOPnC{$7s5_WS9&vmaf#o@-JPo`~WsGCt}XfKaa5_dj+9eP_h+TcF%=#?0qL7uuFgR +zPhBln000b5NklLUr3^W +zYkO~_TPA4vye_AprwekRcM-MrDN|?Y-^tblMKgL|%7C1@&?h~hqL#n8(h56-4N|2! +zn6tEso=(|7Po)StOTE>ZCLl_eozg_Dil!+b;Ar=_ex+6p@MCr$85_t#F5>}M5miRr +z$gO-ow8oTgazK=IA{3ht>`Qb%)n>$m$SHi-yOz9?T0yRRg$6wa?d*lVK7a`bT`u<4aQS7qLHtTyJ>+t~^Yt>!I>VckI4D~rqYAQ2bufj;cJ@mv +zh%B?q{HYA-Sy?1QKv1&e9cWwT;mUXQhZYL5sq{`wBz8%OE2R@D){^9OTQR0XU=dIm +z(qQBP_0uvdscMi)9=-xWx8r0N;AxT{i%?5T#REb#YwN3b>ni9XX%k>!R$yaaH$dr) +zPex=V7~kCgBHD;L1>&O&;OIh@A5B(EEf +zRUj3L@n=(M<`T^S?z0bt?*VYTR*(T`YHPw(6?937P(OxEX=hP}!4GWnY#GBlJ +zoM$;igyp^u1HCHh7Us{d1}hBt(oP1=>&c?WWgcG$I8A +zv6^B6f@kN0D_0VkjffUvfr&Km?pe71EHN{&Lhl=O+p%-S$!i240ufTBG7O_A74ihvg`OP>W#w)=_rAs)NI`&M +zuvkj~9bgn|BNXt!U?+}l#hxwgfY}7gNs^2P3pXfQEfGP&iVKXuq*QeO0+upjX9suP +zQ&YfO$G0Z(#Ta--q1uKVm|KL4-IUwFC}HlWMC*vBDo8lKN{kRUty_yIZ|AcDyG4=p +zu9ZOsc8N0Y_2JZJ`YcM8mi*vv}8Ub`GtdpM`` +zbWIZ_1R+b}e4PWn`kS~`?&{R?7>2lXLZ%6$O_m09XsI$C)UP@OFUf=4*3xy!@@Qka +z=S3iwX+K?M10|nMbtj?#3&P1NoS+J8MkJ-g`O9Lsk80`(DB?0al`(_u6}8n&W*6b5 +zWISFI&U9WDKPoL9PEIJg!Joz{XI^7FD5##hLeai}r(mDzUJB6;*Me(_PHt-)lEt1f +zkHryj4jqt;!r2iQz{vv7F?>6w?UuQ0JvzF(un+VrCSDNQ^oBcpzn$bl{P@&oYSc5keN;o<0K%k`ECEP?x9&*5t +zUjW|=8dU;#XXkCv411skP_CjX&C{fj{gy-!7Tl!*OhH|Mj%>1(L4UDkw@4*Nr0gFaPfwoV51|6 +zs5+9h&uaKdCYM_Z14J3W_0;DNJ=yp@(2Q^veHElxS1_5x%aL(it@GpN19r|9` +z$Z^)zqFdEMLD7+X&@ziqk2uhXK^Apj$k!;c8@1)0g+qf`?L&lGpZ*RzU9{=VGDhaRP73Ct+Na3+>DJ4q +zDCJT8@OIA}sRRAsK0tq)#`GcxUdPvnwy${^>YsGL`r5woKKSj@D0L9!YkP}Y!c0Yl +zNl77`c76HU#4*rIQ&81nAS@mzKDgkNUB9`v4!Gk{5JvaJB;Ysq{7zcW>Qp#oiMNqlb{TeXzxj2Smlk`g{=)c?A+mfXf+ +z7;01$Bmzb@Ugb7(|5FX$hjM3%E}F!$CGq{Jw$yTA+9Kxtr(;c6Vf<17fJZChLsvi^ +z@$Apf)VYEpfZ{-IdcCUQ^fE8}*{-D;|Eb4)4HNcxCXH%_K&HmA=2)=huGXRha9WTZeKHXP5~*h-*M>K@`pzLoM?kbwFm(r0(>0avNjvIL +zZ#FiN55#eDndC}Q=RwWgM~&a9L(0_Yo$Lif#R +zb%o`lm6|atX=N<+l1%h8EHWcr84I5Cl(|vIYAp~l(&LE0l6-B&WlP!rT85MF+hP`i +z#ZfZ_*1JN9-@NQjxmDyRgm_I5udVr8X3mSU2dQIuIv>&05Q-q0^srht?M3FMI)fXx +zy6_ay^37N5j@gh3A{K*_xrR;UQ;Cgxd!50;PclaIOWYoN*p;?A+R|vTJI@>xy3*SU +z=CP8z<@^Syt+L-lwt-O1N-v}^3nwqV{nbj1+|<4Gy?xWiY%pEwE*5z_HMBO +z&<$2*@I6^-r0_=8nF{Cxu7U7kvs7jsRX~zfF5Cj?Qp(&$_}Yx1t(+XPSrwk0aw$vx +zqgD_qw_4GjiwizrdlZeKC8lPvAlauU`mSYWv@gi5G*8M=7q8xEDVQ=Hxra3%tih=( +zq|MH4E0roTi^GpDhN-;+)9TFi6 +z6d)m2HW5Zb7IAfUHfUvP&dKRp0a>l7W=~9*RX~_R2Fp7ECN&d+h=_?P2LE9IoPBm; +zAqtRkYp^B&^C` +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 +zrON5T!lQ}8&4O*m3&$_KtulSC&dk +zmD1Ab!GyEn@MB8sRj}E>YnHj+({ul~2jTEEsp$32ZJs^bt~~oxs=NW@>(}>3NAHhH +z=cUrp`LjGEgj69vQvVmjF!S^M{r4E};Mmx}!0_nE$jIc>)Z>SzN8dJ{%*hE +zf3ops>CO4K{PMS@B}BCG@bT2-$mqbpz}VQB7uDzuhZ#5zllvrWiVU2`j~`DxD%{ff +zcIM#b_~GN@o6Xscy_<(q52q%97#$ua#^B&!FEfnUQG6imfk-1xG?RBs2kh9S^D~|u +zcA`K86cPT0ode+vJ2v{$MhHK0XLL;fdVFk*@dAUx3nj)d*mlRX&7qPFj1Jq;(&5}7 +zz@T@KQlSA~K^hzzw25F2j&-qK@4zqY*d5otV0s}X05GD^=)nHpY}lZ;%LUdBb%3Z| +zCLF-Z2=|7uKmr&sfY^oM6zB$LpB%Q=+lAUA<0k)b;DI}Ap99^~0~!K0E$onb1KqO? +zCtQF==0Q)7`!_cn+&%8F8-8JTAkfnj4uI_7XmohJ#I$3i6O8)cf!pnV@V^quV97xs +z1foE}9F`kZbI57_|1bA78l$4{=$=Hfy7N4az=nS|*9G(a-l6tX|LMG2BkJ^l`jc6+ +z0a3gWlVg)zJRGX^f>cZ0)asfQC~xdtzxSE8%o=J>ndcz@#g>qUkuxx!WWi->tzRB@ +zuP%u+%ZPN(wi({APRpa>Q|U1W#K_oWz(gtybbixE7sseJ4L4dd91)ahLdFz0nBtOH +z^Iq}9)^9JgEx0vSkxWOY2ptYGxEYAb3J)f(oYBQmA6|zUlF4!b$U`ONYOWXgRIGUMQ*3I_-p2pts;77z;u76%C&2^0It!0- +zY^#WZngl9HRFqJxQvkzG$gJ{#0i +z0^&aaRVfX6?dP6Q1FkdyKP(a`D-*6p2>W;dcHh}cotb1m59n$GR~!d)8V7xXf-+%V +zjY}w|YhEb_0KXamMudP~s;5T~05x=R104l@UK~dp5hY4U{)_-ciifQ)HIx7V49!VI +zK~z}7RF?^J+E^Ba)seCgQIT3G3J69mBLs-@rfL{UF>*10!Qchs-LaYS5+`0_XZ13f +zad*d^boNx1>CVi*opYZYcg+Qa%UGYzJ@4rG!by_kgbl+o48w68OQeG;iug$sRW+eT +z)o22xTE@osgc_DNHXPN7grqP|3!PU)-~citBTSUAV>y;#iNpvfgh)iAgaSy6s)iJ{ +zEY*rca7s8FVUR*0(kDa!3WJ5;T99HnYQPXx5h~O~lyRbXhel|FZdJ4l2_VA=6cs{( +z5Q&6H5vy?6AT^?8ffIld1Og^aDAf}2f@;+QCL}?cA&b*F_#GmUfP2IND3QyV +zIh1JzZ>j|pI75RZDxFpx5g3P6Ec+)Yi;~&GJ66h!eJh(Cm(ono8HqBP9)O#) +zrm8~@Wr@U5lsHWTNRu7I3A1eg=8IJVJ1<&nc07;(p}dD%QMXYH8(f1FunEj +z?jO1096&iwlduUOtH>odDfi56p63*SRrZX=cyjzq7g%cxqvfA=M`8rBW1j9<36}G4 +z1RMfoDT$oIah$@6XbPvvFkf&@{0#|~?>@LVxt$5Jk`JwO{=B!ZPOU5%Lp +zIb2Z5WL9I*bcvLTQIfFyrad=OE|*8kuQRqut6;{(Xt&$K4kX7(b37aL22e7|K%A^m +zqT+EKotZW$K>+4PN~O}m=}0E$>bYVj=cVkTEoWOUE-%vWfbWWirU~;&C^Q +zI3fX5MVLLhRbCh=+Nl&4&h&Bw(sb$zsy&VMMv8cz-FJcxn>E=(0ODSbtv}W9+**0G +z^waJ;h=~=4GiC}4V7oFUH<$8sjkP8OtudRk=V@ZG#Pz5-i18!568tXv9V&$*fh +zjtd1@P5wWR*rI()QxHp&%m(VrZ2HkQ=>l;rE4%cmEC`zH>H!E5NCtLu=d5#8Y*T^R +z<%QERJyy)5bT^kV<*c5{#Q@R;+-t5&AW9O?^T{(<5GEl@{^8bx^(nozwmZF5-kr{6 +z$Z69q#&p>WHVHke;esynC;;)_U`e3a`XQ4kSJq=OPhKl6lzti=v5RxVF*BAKw&O1H +zgk1(<1W4C{Bm+f|GZ;rIvk0Y(t77$R1P~qpwMMrv3q>xD8!;7k*#;6U5w*rZ{5gj{ +zq|3NE+i-E0=B5D}Ew8~Z&!zHmIryvTvY-fO+fShAoP(xVCeD^lr)@=+TO*}%d7(7Y +z^30T+l|9<`TupaV9`-}pE0{~)K>2*09eK7Jz&IqHYY9qH$tr8RyIbY0HR=cWICcb^ +zfL>ftTG>`h)>;D3yam8~b9A&lcXV{HJ-a9K`F3roX)iDHno?`eR^Goam)^gx+KZJ+ +zEw)#wEVi4?`f|I{taQGtuCBiQxVrk}E>9pXzr1s_v%kOdW`DWXSZ=ke&Dln#Qf(_O +zztiz+duzwlS}j$nH~pn*rCu-k&01$@&#$-td$Ric#fy^{FYXoyl+5oRY#%Nj9oYGN +z6CYO_i*3JKX*3gX}0Un)+$T>tX`}5ON;f{wcX?6t&#j*y|cH} +ztStMPX1$2W*Q(wFxRbn#L!m&oTONHK?(ZLN7gKG2HlH7AWSV<}ekMOu_a|!2Ki-YL +ztCZg5Yc;9St?jvfu~x||LbBs8DW9J3p$DIy-2Hl&E!)AH?ZfRiJ3Bkui`8y2-|@Tk +z77r={?} +z9{=(9Brki(iL5YzOerZ|UK$)&``5tWP$8@H9w&G_ea1EmZ~WQS2YO49AE2_d?FVt> +zap+Cx^n%}^&k6te(dkRdGqG{%KXa8!-sf2Tv_@3>EEfI{{HCEqxt#y +zhj@GVXk}%7{%?Qn|Ni@)D?QhH`g^WiyL=HKjQQaWNgN*+$3^woty|A-4c!_VT3?@i +zmRUcYy7TMQ)aKNkJENW1!Svwz`uaa^(PMu8+4%UxMp&KL7$4sVV?0hKF^OWR#Vkwn +zdK!ZsETKVWeeCq*otb<0Z~r&<=A>+B7+MIQDFeX5NSw`NM99b&hAz4{eixa +z&~)io!HCxF{xWv@&&|on$G0~hZ{B`9IWskLXX@qC)3Nm-1{%cm7|5_Nxnkl1s23Cr +zJ7LSf=w#3=tDCP*?{7}d+}oVGKlz`jdy|`ZP2Qfly?JNq>DnMcXzs%3k7=L25IOV} +z;^KFgH*Q?KeD&JK#>Ta4m&V7(fBCKSzaq=$HEtse!`2m_iLc9=Lo*26F?rg2=a)|<_C +zdwy|qcX#vU@Av=SyxiY@d%gVY=4JPB_i%H0`MA5=@9+P)IBYkY)w~!ML)&t-eeZLs +zrfHhTl^~ovoWtSb>G|q}*5O +zIE{Be8PdR04 +zd(WSTw|!0?Qbu4(xzEA~?2q7r7MbU|7*H(f`=PC?Y-3I_vNhV2y|93NpHlCw_e_ag +zi<~5~FZ%nGk7Q{Utge#vL`D;1Ox^}-b4ZhbJ?UV47TqP-Rs||!a>iE9n#guEHJ0#@Toa)2&NQwuIYtZJ +zK?*`Lslu6nXHk}7#zkjx)pSnjoN1P@*ca$17N&`=v$#&ZMW<8-s)KnH8iFL_HV0if +z;tgVngxu&Q>>?L+HH@3i*rr1bm6oytRyfW9FOf>|sKQ%QvDJ|j7Qsyih@{J=Yg{qQ +z8D8+NEOzWfIJhP@od~Qt8A?5-Vv!wPSN0n~tSlSrIw}lYh>{0l^JRnVqQa1$gLXO) +zjtEu-!Y$dwr*XIErKxmrFW8wR>v+(!dA|%1j$uptE7fp&5{-@el_~Cl0EXm +zb#PHxzzxw$M_C;v;84U-L4@oo?dp_Ayr4@+v6rUMH8!sB1zw_uXU>&E@hfrWlKUTA +W;ndt$ps*+a0000Pr3nB{^5DOO=7!NHE2?+`!BO?hO2@Vbp9~KWH7!e*F9VR9w +z5D*UyCk+!02@eks4l51-009jt4H66q3Ly$68xs-|5)d;GDjpRh84@ilEE^je6%Gmz +zFc1a@1}Gd9DJdv0A{Y!N3@soQG%zp{HW3*a85|rO6FCwG4+R+#3^^wqGbI@mKot}` +z6DT4eoHhcWdUG~8ISdR35JeZIfO(lK06#4s_+tR0K>=%2Q2ccO_f-Iwc5a~)0QyD% +zL^w81K06sH5Kl%#jB90?AOWq4g!nK385|6gac6a1Sc79<8Y2&m6$36U6YB~987V01 +zA^>1ZM@u#wtA>BCkBJT#7^Ncsj|BmCAP6Kr7Ixy?6H6IxG7zXS0yjb(5EBVCQX(lx +z8B$hOqpGTMczA`2i@BGOPAL(khJi6O8N@XMEC>c;#=&pc(t{`jr?0OM7zyq_0Haj_ +z>rns`F)s=Z4qL9Oo1&s}MG}rm3QL@rp=Anz?&%vd6Jcd$JzOPt5(zdI5iez8N0N>? +zb8tpzER>s@q+0-ul$6m#0NYjo*>wcJR0JbYPhl4iid09(5&-;`0RM>qpk75q5CW3? +z`9OkxthBVfjw%=c02V$;L_t(&-c(l4YvX7Zv_S`9B7}Y&By9A{NGw9avP5JPBeSxh +zMFLuMG1DxDK1^U>AxR%P+FZ`(B=Ud6oM-_>lbW*F2Y(A3y&3)Oco@%x>fo +zQOcQN7&AWcn9XjUCXpOPO;aMIWXm>@%mxn;w#hxS$tBM>bI;HDNb@SiMKbMo@66P1 +z?C42CCVrV1hHRIzY#SrMOoV^BnUp1f%Se_>CT*6I-!vXd@fxIUdY2hs4dWuzlF2;RK#j`)7=A?3V6 +z+$yc`L +z$j^PGU**-RTH&w_z6s(z!MW_&jBdpF3Jm^C=tbvmz>wfSt`I1E39wB8SK>kyzB&kT$A-@I48fr~WEk +zfgln73NN&s2(tvAvhmZPya(TiybWy%mM(cF17XP7L~uyfvNAcMBcNFc{q!MBCXK*V +zb+x_P;tDT7IyUl$jsXCYZQ21Pm;*(cHWlG7XOo!#fPtYO99wtruwWW`8yfY+Q4UWpPfnOk4QY0$Sv^m2Y0$_GIqxfymD4TVy>vc^GKp;Uc_gB?b +zT5VTH5q?XYl~1$b15#c=IH*K6bBbDGOSA!C&fJX&ikV+(652|4NoG+Xd@YU+Vc5vb +zX9KRTw#V)9cpRz_B++Dfg*lY4TDS%;GfI*;Mtp?jJW3{fKaV{I7pLAAz}VNtzTWR0 +zony)OAkNisiz0}y-5ys^tZzaJz&4wtq&YFEB$yIXjAQo)fH4v870;%-@Jx$f9~1#k +zvEK*5zNjS*q^er6g2VA}00pa5M!|i$XU2TvdZW +z6(06G;_gcySgu +zyaI$eIv%zMRB+fHp(I2zq2R5AN$;~6IY>)*mSY7%u#=?%z${yG6*RK#)K0;WVYuK$ +z{189GH(SwVQgk2yl?n+T(d#of`(u*Xc;O?`Y=~7Qs4exaHD_Bf-sdCtEIA!E^usMN5EWI2MlXAUu?cRxc2Az2nZ=gnIE6lTS*&NFA^@+2o5qt=#Y*kB5KXq93WEb-lY{=j<9x9Ciq6d3Y3`RVmhB|+GIMN +zYU2@}!^tcl#9EB0!F~^?SnRszwtMc{{=;uO06;?FF{nfgsp2>H98mQ%20tO +z0Os@gbjr(osx0~EsjKT5wCiStDq2nF1x~O7Mco;v|BkMNG+_viV7By1+!q;1Ru`x@ +z5ETfkO$UJSj1JM3j)Sd}DORu;?F@z%i`~E5wr^3yQuCpr4nJu!3!%qtlMM80;oqo9nmj{kKzpYQKHjY*feEhr2ZZ3P@n1 +zXR%^Af*(?}962KU6LeZ&P3N(dvAv2_6jYF2Gw<-l%INFo?tJ_3*gbyOZ5Bnb*btyr +z2>|no_^JS?F@A;;27tL(4^z0%qIMK(PxUws8G3Q^2Eu%Md+NIL^TYio00aRb1pxH5 +zaV$lz#h7G#i}7Qz5=hYiK=EY4&l&k)2}Lm~loh}Uhx2oL`u5!SpLT21hQF*;buR#F +zJ#&N{YR6jAR?A`Mj7@e20L6_*KWBrTg$D)*L|GsDuIu{KZU6pyjX?qL0RT*GLUcr_ +znSbqSQ_bb<94Z(A*y9&N$3GziLNIz6ZT?r^wf9|56|w1vOI6a%sN5>3w;GA_7d>-d +zS0K)S;r_r7MX-kbuz?opp*??Vznr@M{avAMHsCr#2LdZ|!O!rS0#4#r9qVN;&tLiA +z1!ElG$eVrsOz6U++jGa)xGYh;hOX~f*%yRhs-H($8ram0zlk<{e<{2 +zH#AB?(<}$NigmTt@BTR5p1X(s;q+dGf_4~0SQ+aDTnYUw#3_)Pn3L8h(-(l(AahC< +zwzh=roB{!?Vxc+!Xxmf!<=lU!f_4ikXuVsYLdNHARSP(Y0<<#C9mEfS`Q_ivT>NVq +zV`L-utj1K(ogiEkcVGLS!>;RZH=N!Wb=(ypEF|OdnbkoXk#pt%u42BVoxqEE-o)J +z3~1OKWIEZUEmN(oaRpkZ$Z*KtMPkpb|Gr)KnBNc=ypXEwxg6QoCRO +z#P6NO&hvaPyR$nxJHOw1nHhFo8h^;br&;6Gi{+0%TCYNJZBTe-0sJ9MPi<|zot%Vn +zDpwl~(@255jPEao(*&Z=>Ms&L=B~x=wlm+lzuD~!bIoT5@wbMd3^#MQC1G^KCGcQ# +zcxh?)@DPgOMPT5EsRGqby?rvw{U^^4H$v +z%C!I8TR%Y{V|p&__0G)AHT!0K^jvVpOXwr%R_~0rRQA0;Y+qPdK*kwk=}c7NX7pUT +z)|d7pLZKjhb5S(-^X0*ZrKLBUFE~LvuXY=QW +zo#BHw<#?_Eflz<-?bNTF5&2i9R&r7lnFIYJGP_$ENzul%uXHAWqH{uTFvu+b-5+o- +zZx5knCY7#4^${kRO8GkM%uJ&G_KoI%y;k^Parnn3dUUWf{NqjOMTjyGpa(0{xp%p1 +zdn?nEMujkJ_tyB0bShdEU|M}??Q|tx1>-5Y}e67}H#d+pS3iy$EqLOjz$v +zH!dvyy&Mhq|FezgU;??BibR^GgPb&MS0Ih1UdpBX=+VxPH%O|0Ac&AqQFlX#V*tpx +zshodnYdV*E03NMDLgY&&ZrTGkIHs8>ZS=YWD8YRC6#n)k>JESVB)iWfP!z068Efp3 +z8&P`=fxm$g42LfM9Ny7O2?)aG&gOvzED#d4So^8HJqW?CsY!n@QmGfBNZLyWBf`KO +zO;n&#CV;r$TrmA<+0D$Q(|2X52*gF>P5Y{uaD60bB=E}YV*_yKAc@x=vaWJvsV4td(cA+RYH7Sk5Gl?qM`s+T7f58ySTkwd|rI{{DsSZ +z2n1I|#Ui;}VvsaYl!4T#NR!QK-QBfj+VcHF%l-Y!L$8KLvKe@0YTMnlwe6a8luE5O +z804bjvIddp{JAS-#pf?zLh%Ja#sqzReOLPWL}fj@yFER-x}Kg4NOmHdotVh(?@vrj +zjO|ZkCz7YGwe{om_!{eaaddRFKQ@-hPmCSy?;q_S!8_SZHe(`tdU^l}3b>&}Wo4_Y +zK+suK)LW%oExFa(*VkLr*K58xkk97_#`19K$mcB%hh>0n*$#PFmW_v8&XChHpENog +zElu|vmb@c5HZX<{V9SH5y}je(MZHD6J%BYnuIvN?rL4-#=ZbKW5hooRoBo20>b({| +zpn%^@C!e2&*m&$qi2dU9#A42v^F@+wYHD#f9LZ$TfnT!RyVufGEh~}1W*;ZNaNdB= +z*|h^fi5VwPZxuCGSL3$oCfISeSPF@Q4tjR<{Tj=HXF~DMPLIdsWU(3Z+oP{(a}P8B$wZ>hn!9i +z=p)awR$FuHng`6SQx&Hp?Xp#rV35F(k|E5_cE4ibr0u-DJQ{ucSj3GeBTcBHJ +zoVkXI47Q~>@kGBKonL329#U{ArMd)Sd8xBq2`b;IuDW!ovr|x9EV>TTAeR(` +zq9~H=(G85gH~~+vC-dE(*)QN9XFPuL`7`@99$Wt!i@Q!vUBKdW9Y6jzV{J3(V1V}Z +zHWsa}uAVW71U)742D#|^ +zl`;XPqA<2jhr!$0+H5d!ts~a1uGx>Xj%+qNGLp>?b@vZFPddi3`RA|Z)Kmtw?i3FsM)M+t3hBvj@Fa_oSimomz{=pqz +z8^Om&W+0#G8sQ}~J2W&qmcea+kGp~k&Nz5efI}|O@#zOB=QY81cbd#h)fP +zZPo&q6`Ojd1L%XBZ8qo)Mu4lpS)__GNMMB$0KN)Lz=mKN9jV3k;M;=WXPA|b4o3J* +zt+o9hxz=VXh!*!Hv7fLR5`l`uw)w~ +z8$>XmL?XFFgh>Ds=nQh)fXA|gAJX_IQA>UccvPjv%mxD$av?r59-gs8;FCaiC_vG4qbH8oxt^4ko02N;Q!j?ylC7JT6Jsqt?WN*m^fY1A$dG{pYJq#SC0+hS*lkH3T?sH!XxaSm2JptYq +z20ke{J6VrAY?9PDV{Q+2Ivc>8DcE^#H3yuXJV)N5OXUnI$iIyCK0nFtod9Icme9yp +zu8gNyIN6~xXF_Z?I1(|>Jfb_(0A&ujP;aXub?vA#3=@jFry#&L>tqFmT(rX3+gYR= +z+q5&$&N60mBh%sF>K1>X!=g=8wp8`Sdc_=T+)d2dCrtXHX{2NJq~QxxPlg&=|PE+ILwJIZpSG= +zoKc248H__XvNLv5Qw-)aNbQCq5KnmFe{`8|Y +zm!^F!l{FyGskHC?S-=j<@nBpW!V(-~tz|m_MIDDKOoTfOXT33FnUHxAV)vQ1x=Qc7LXR|&>RGz(^N*yN>9bi9ti%A4xTILZ4+d +zlri1QtTYrVY#uEet9nhVy|UD*_wwaXv|9Q^6osek0f6EFOjC*xx+N*yBPOMy*eJ@x +zy|bW+#Q|bhe54Nrns_NhO6X(T#IeqGUB6aI6jx)U5rVTe1GeGKoPBUUoB#j- +M07*qoM6N<$g3=yxMgRZ+ + +literal 0 +HcmV?d00001 + +diff --git a/game/assets/icon.jpg b/game/assets/icon.jpg +deleted file mode 100644 +index 10f3fe3296d033d79f7aaf2ff33c0499d230c572..0000000000000000000000000000000000000000 +GIT binary patch +literal 0 +HcmV?d00001 + +literal 24427 +zcmb4qWl&sA6Yc_wOK^90cS&$}cMTBS3GQye9fHf^1QrSIPH11$c)5qyTWR|CRrK;h+VG0Q|2Y +zAt1mbAS0omAR{3oqo932LqWwrMMg%)LdW=kiHU`Yf`*NQjfn%j$NWzS%zr)MfJo4e +zn5f98(Axjc^4*+1fPjdE3~l~| +z34n!xg@;~1AUqrp77hRd3kSpkz++Q!AxI$NP-|Eqx#4n$rqDi{(9zZ#{U?U+R +z61ICY<#lbm7xT!InbYe;!ZqK4!noN!B9#~7zy0mD|0Mi4Oy00B!j*LW{Df0JUD9hwI={6Q +zUC(JTX%NL)5Js@^Q#K5>{`fM0WHsO&&<4i6%6Cn)a==S7Xw)bw7)To%DAY=jWMfcB +zXfBoQDd@I&2SCu$L-uKsI4?)4i3{-C`8PfUyZQfSK#_pK#9pUsbO>_u4+h^2PUx%5 +zk|o2hhKNxn>h6RDHjZF_j6bO-Tz;H8^FoLJHgs6HP$dxJi3t5MzXR@tl +zy~#A2`>SFG0OLnzWKQ{Eo@tR3+ZZVc#T5en^lHLq3wE +z>h^gLkfEe9)5^`7w7@8+J=PVaO&eY2MX+8=gYs;&4T8$x8E?ExCsY?uAQMZl*amUw +z6?Dr8D6xURRR|mzqPXpg3_C9FR&JxjmUU&k4C#tWtnEgTFYxVuuO0-XrP94J0a~MN)=0bg?`tA6pEBH +z!6=@BI*Rp1lE?jZJN%5ZX~NP+c34t@$|Rg*41IWZ^rruBgs^hTKWqXWiO1oXXYxZ} +zXeYI|CT=^iB~ZxHN>{j@B;`8hx!RxhDmhWzA($0=`K7J*Wz`}z8F}(cr9|!VJcmak +z#R1txg;vBxXlqpGG>P$QUAEET_Gv^}tU244eL4R;yPh4x-=l9Hk&F?G%PCPmiSQ!l +z5CKk4yMI_ItCfzg)w$d{$+gcR5WtO8w~jy3q`eNm5h~{%?;0N3|$@Q?Py(9rbD(t2|-Uxk1d)on+v6^#y)HK)?60XZ{*-1Ds +zDCMECR*O4las+1_;Yg9WpLO-U02nKX{?DY?0%~*AKL)%|SMwZM)gZ81$Tu1BRhoV4 +zCkoxs0W_QP2dN +z*NYOZ>@j@<^(H_6@0=6{O`j+hV8jQK=l=T4l{!Rs(vXn7ejA}l!Y%E@Q*qChlFzdE +zYkdgqO9nc5d09&>5?=Y?(heK?3H_Jg8BWzP +zI6A?m56p~9WH%6#dWrM4ru&B>1qLrlY{EUNmZcgNn93k$PbF5)-gnn;I;r|cmo|A-fAlBRHd`8_rpaNy1L?Y`5Cba +zkD{nA=7O74bc%k#%vtl|Hs;7D{_lWF!Wbc~7?d6rMR|=e$o +z0=BvGn>gY;Z!-F*!?R_xP6$I+FWXGnReuMGUp?a8!1m|gxPA~ZQB~RdZTVGHOO=z7 +z(+oGbmp+I?zKR4tOn3YF{ZL2-wgt023D!1vRa(+~Lbh7s(aCMirynVOC4Z_kFxpX2 +z(VB=sog`j=bti&qrFx*@n#3a%y8#wHj+3`1&AjqXuRmMJU}QN +z3mFYXw!%rg_ro~(eLrK8)`gad5_i?2i#h+Z<*^a7{aNzdz;3c!(Q|k#epm^qBw|I1 +zGu~Nu2*rlw54Gm(>P-{{S_Y{NjAzI@Krqyfqy{c@?GxHaUrK|Pv8>8FAUN+gkSVMj +zxs@8hbw=Mrz9%)XiHzB>v0NHo0bhu$pMVzR-S!9go3pGCou)+fva}*o^Cdp81JNSF +zEn>j^(`$N0sSGwK+^UK4)a&#rh%O_OE9`;W8+fq#!ikZjuuiZnk0=qaSA*{{v9ANed5!f-k4fKIo!3n)U{>RR_zo7 +z2_B3R!~P1Y&B5DqhXWO%czO4{1B!d0pD`4rqLYnoIBs=o$mRKdxQ@9JHZGF{$ZtMs +z2}APo+88U2S-ISYpJL*Tph6qWNa-#%IzzpXA%MorWMS>f8{%^A79Xl3nSmCZG7+iC +zsz3q3q~pYWQ0M*lrMb`;;$W6`9fLE_h@m+K($4aB-m2Gq`2a!lquG?M(=?ZKmZ$VL +z`+gKF;6>nABj0heei#@9^*caNYy>la7M^CqQe{&HLYV=_em^vp0kDwM&vP0T3?0ax +z@TFD>+x&L-${DMQb5eFPaDTsz4@#9m;u&qbMzs*%6k`69@X>N)p1I9Z<_>C=EcgFd +zn>!civeG6ZW`p2!f&xbfK%ZHoz9=lB?&@68;lI87<;fdDsnjXpW*dG14RRy +zVEeSg$CPuK_5@<=#X>|s+z#8M>>4sBX~oR5M5YiKZpFu&?SD_u-jQrA!((P1p4VCB +zjTF~J4bk4S{ +zB7InxwXYwy9T~{Az6@mOx0UYOuXHqmUnr8^0Sj0z2b$g!QSvJ@l5Oe`Muq?rLG6J& +z6iOp(`MX{*FhziLHK0N^(vmLji4`e1nC*T@NGrkGTm{EOPku+_n|fPgehwF_(UO?C#piUEKT~;~Zxcv@d{hJ*!9e;ItEPj!J-nY@ +zR^HNLlwrcS+;xibF6MQdIosLO%Bt5i_o%Cp$I$i}&69Q>jLv}Q4 +zlL#CL%v}Owh7?nSXFtyeOe?bzVq)MjaMn^*e-@_$M2V)vAGImz;E`2N7}Hb3IVf#x +z8sRf9MkdBKeHj;+m)L{F>_YR5UJdU654^2nCNh!525X$4cTjA@Vz&~a3?)l6ZTp)` +zI%;NWkVLvViy57sIwGm`=#WMv>lo7eN5X0ni2ZtoFE8CDah^ +zI?#ff^I(TioAz#YEIaa9011_3zx73zy`VCQ!VGlHI9_iAH3mh<_q(C>1@0P;2uZoUc*`Y2>wY*`97Tcd +z`rrk;UN0-Gc+N(n!0C`7?XYy(Ud9h%P44B`f(W}`PumDF^K}=!;ASA^lCC6qt>Eo_ +z3rrOAiE=yTH5d$}{l@VV$kNvVyc5L=MYS34{20~7Y9DueP?j;RR7w+zBoE6)t^nw8 +zGSplAxt(W~^6w*M!-7jdl-37FehHxU(6{MESlXTX>%ks8@V`vh-t0T=Wg|-1jeHy~ +z;;)`Wlou8v**z>sDqQ+QpwJ%0<=Y}eB +z#OjtNnX{!4F$v=_2b3j;{U(NKWTwMY9Tf`{RF56h`sOFJ{jMa+bqGDAgS6Fo9JM96 +zg%t%iDr(HQ8H8kmEc)T+0iK?#>nC_s?LuYd;grugqbhiU^a(Zu&`8Wh@&p@wZmI9I?< +zK4%{`2b}<{nDQj8MhIqn(_W!m03sX{GMzE2EBh3YJi^Kj1hW4QaMZ2SxMoz_xxwr! +z1;F6*i}fD%&@Nolo=>#?j#3Ci4TF$xgE@%wXE0!b^O029#7TD~`9KKEJzJwtg<-SY>W}(QLqS)-Bx%$UNf-BVW8AzIJxHi$6|=VZ7@kswn+ARQzm^-{ +z3e7Q`%XlpQPeb2=TQ8`ba-1z>*JM-GHaz +zdaPV`0GR)^Dngp7){}z@3I1@@`=Ab*2nm>VPHRdfqgj!sSrahHv@tlaN}bMFvf&ex +zqh+JFq(#(rp64>c>HQ-NzQ-SlO7zoBuirt@GC@_SY6Ipu<^G +zs-1_5Wkl<#AK*RSnasE51hP9R*5I4qn_sqFPKT;DQ)qHWEtva~B*>MSzL0e*P4J-$ +zSP!RCe$70Qh|RuZG4gP+Ua7U$IZj-pnvIqS6g?n&EE)>K*ox1zArs@5Tg*vP(^8?u +zp4ZOxPFz2!#n>r?q5!{MU)h&s%XA*#MNN4XC +zpf71dS=tfJ2D({yQ-15DZZ4agMjHXGxn#vuaU-LnOdDbkkXiqz6o(6mXk`(Bu{u1` +zOEjS-m#~%7y{Kw6B$)YhV~=c-u2_|mTQW^FToN6_r8;KkwFx;5#SK;`$i^x)MiBgi +zE(Fe7-Irk-U%*E_Xuiry=0)2=!JMSMhnuK#BxqubaFGOC_8xzDB3>pM26_86gg +zZohH=gz3JZqSi_|dfayAq#`Ure3Tu4t|F3jM^GyQ(`QLUio5_MEqCSC@|r8q>O|3+ +z9O~1K2;Wt)fykmV}faiWB2yMXU?d59VVwsd&6HmO4~pI-F-XxqH!5CwN`Y$#a^$Ky3TCSvAy(zBwsQ@D1#ly> +zlZHbwob1K5=rhPLFy>LE$FQyi-B*X$b+QkTSoWuW5ZTlEX7l`HvbC}}X4oblpU+;` +z)y|#+VDN7=pj?IpqLdKcPRXSke}(B75i{vSrA`{rS)wCTHmvxPOkIrf +z!GY}Z1KrGx)^+Y?bCm;+eOX%?u4_Hyn+8So-rZ0n;imL*L1Rs(;zS!OXg26X9$+aE +z2miXbuH31?Jk8pd(Hiy+=wU@ms1mx4v?WgO{QJmIaLEVctMM}+bOFCMQfc(Od{019 +z79@)n#LP*C+ODQIjUFJQd8#-m)EBM+0O(Mw6HzS!g*~`aogckLcO-p(VRQ6CU@^1P +zom^e13^b|Z)KTHPYcYVVoCrH4X2~3(s8nIsY{_Bh_xNBS%d|RmnewuOtB6@sU&IS$ +z7+#hVnF!|2hZR`dZ-YEcRNigfvx1oE(tz&aI*abMJ7^ctu-s2W`0X?r?e`CTkv(!| +z>GdXJc*;B|6S8>KTEzGZ03}~mM3i}8#~xVpe3;iE7~5)HHucx4*x~gDb$SBu8wGdc +zkz_eHs+U;#Pa;|}sYQNWTi<)#@f^>4eYrx~j>tgKwkQ_LN-dW9)S51JJd +za=GcyLKJU8{&Um6tqrNZRdy(9XgID61>8dAMN9piO) +zG&;@&Diy&qgz(_|C5x3G3x1<&_6RlA;IDT;hEd*&ZfrMR{xabAJgp^f^6p>RPdcTy +zohtOcx!SXmjP$7>$;Fa)z?)Fd<#pd)fZu5JpwWEYph?viqVs>ZW*;B2)GqCxC~D;& +zdpEDzKAEhD)t4E7^fVJU;GFN@9yZvB*SHmF2my>J6%Z#!B%Pf3zcm@SXGBq(_kEG5 +z9Pz#su3m-@m<>9Biqw6q;`(iiP*mAkNrnHg4pa4LQL?wuX(-AtO*5>_se`$sW=-H3 +zM?V}Cf3ti}7^arrRUko^JcPjF5s8?tV&+=3&H(92ze4pSNZkpFf8z;%)^A==DYCaU +zLWc_}?f?etNCq)=wNF$7o8Gc96X|GNhs-6pm>i5xw;hqxkaKFw>jA3)=8|4?t&yui +z?U;p0TEWx31bq=t3W4L~FFTUSsm5^T9REXKoc!2%7|y~DT7T2r301`Ut=onIIO(&1C&0=kVhaP-G$ +zqr8efWwm9Kl$nKwNC9JXo?B{MMg`rhvVCE0MS&Et;7aLK;=^{#Js?~Q)>BAemXU7! +znNyy^P@JXwkI|`= +z*3Y2xe0`Z1cbL&)(4!-P#ikvZxr20X)k}!kx-rs|T3>Il+hRw9k8&7*Bq67zqC(_* +zh+I9A*KkXSnORwFN2A$9BQMY5VQnhbNab*)$jnZyXX(A>-52Sggv?dmuSw9c1!YSG +zdB$7{-0qlptJl||sK!-OEc&Z6qZ&aHs`7Cw@QNU2Nq1f2 +z>wgFfzb~R18Zz3EjA{nBc!VNlKx+xp4q>s7IjIEc9{VC3IA(AAB7RRHx`iSwSIe$P +zLp|QO=DJrTT_E(y`Z;$f3RTTtLdwDleV#AO%w&aHFASwkHqp*!;v~;6ds6{&iu+z)yO1lS +zWCfN&$Zm*C&v)VkaG&^4`VcjvogTwqMgz9Rt(}OXO>H}L?NYqp>7^7TGWC9`sj0~U +z{Ze-9jD_>g$6qmoj>V|AekVHEF0xmxz#6Mcg<$H*$(2Jk`0ElCll%vBe_wNx0x0iR +zt@)JLB~op$tclL_baM1`k=9CVzWgb6;lxiGyoUV=*(X&kb#|^MzB@;=vK5KmL;Mxi +z%tDgaXAjXvbFe{!C8wtMp?uY2K8Cu!p62O4#c_W2Ow5}{M;mJ;-$E(|Qyi?8{1AF~ +z)8VDq(E)|X`;j{zIm&gj3~O21g+>)<9aWUhNLUx#fin$`pS@iq`=16A!07sJaz%-eTtix@%R +zwBI7`Ot?C#4Iz_lp1I`<8g(O_e5!|-YR*{0+4?-?MBJIe; +zGLr_p3>ZXztrX6_suaFGv^j)P~A`Ly6!6)PQn?PH+-SV+| +z@uT4~%*-^B^_2U%y@7dVJ)ey%FXg-k%04EQ|biB%`$oXD29&>LsEvQxR)u?{!4d +zpVys2VvTomKtob)6#?PfB5XkswKqZIAuJBZ^NwWPloHtm6-EivdSzo~Xt(_#fyz!! +ziD+$uLhv#OM!yu`E`8jAdI +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 +zu_X8O`vb60xm`Jk>k&+s%5- +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 +z%~5SwQanR0ag044H+!{?_mEvwfA;v$ +zSOU9f>#eVe6T3@RPaL%Cv4z7486oG}>WJOE1Zr`eyk5nz^J*ADiLlvQ@fQ0{rOM*X +z<_GzkmK5zp;&9%Ssn3$08*GJrWHfC7wpzU&AO0ZT8b_XoGn(>#6Pl*yhO;H#%V<=1 +z2WZWb(AR=M--?B~KH;DY&=~qoj9QLrZa&_Udk^)-^Z@3$&Ol#7>oyL5&bThp*z5%) +zxD@K8^VpwX5|2B)13r*_`fZ)+!Yp#6qHHAJb4SoP0+&>Y{ZqtGHj;jxoqZmV))9&w +zsKyLVhtnYZjE#C$*~7Kj?BraE^6)2zh6J&-ud&SoLbwhJWkyJpNKYP47rfeshaoX* +z66>wJ9TXlwM{M(KxwO_?q~j_5Yvr)MOb$K0$$%1q9q#-(@FS0Yj3#zJ&WeU^Oer3` +z0(rJ9dTP(mFI69^jf$tfGLDtZ=$KbKcn-Sj3z)Z{andxONJ-P!$J2V$B#0qEbrtIu +zIXwfs%2H`f!)gVVt8h2qR6h9(Ezp}=V=;LrYTHvX(r-h~Bm?&&?#}j_NIseP79Etiie{J2k*$JL&?-19MmYZTjg~9Xx!u)YiBA9^6hR^fWWH*XieXRRK6K}q8S9!YV%V+iRyAGGDM_cDX|1K`PC_+!efUnOt(g!_6fc(P{K +z{@b7$THJ8PF-*LntTu(dZtLs)u-awdT;Ni79_kiJbp;=97-dwzFXIo`_%vN~3+N7= +z_;rDDt;MHNty8XoR8ivW6sNR-c){8}9CLvJGKP;1o;|)=I=tT_%ahs{7ekcr79Byy +zfx`0OqRIaDQK?^|R=OwP#64y$0J58N=B3z}$m|!Mga)R|m0i9SoqFL0>2fV}m+XlU +zUP6Eh&vu})w*&$=;RZGJYsoEwB<3RNapKJ7Z9j`1$4VLHj>%hI)XV!3T0{XvZ4s^z8$Se +zuQSxlTL|O3+W$~<9cjbA_0H*G&D{(ZSkcG|TxTBsw|O`kuSF1n{`&6k%`R!AX{iyRCwZC(E0=dX1!V#rr;` +zBcufCHFY3Bg6ladO-rZntK;~L%_G&sFifdG(1|n8I*|DLTOMSkh15Fz93P0}wr+W| +zEPer@q-_L1|E1T0m|;8EUiqhd;&X&NZCd0a_#XWH?fX&WTM@Gv+J%Mf1Gxen$8X)T +zIw(a9PHgnekcjB&Oe=FP*0z515^C`h?mu)X9K~@s#-gQXT8w38jh8Gslh<#_c}M)MXJUzW!%|Def!GU3o|(`j +zrMNEl*$L~$#`%VkgC%X@QH)R|KXFk&3m1*BZ(N^}MbE+h)u(NwHU&*Y^^l6s9i|=y +zIza?1W9!Yn$jGGXz6rzM(3YDlX7-H;+w^qrl&_tc5qRB|gJ(~gYwOtk!ly-qZHh^b +zfTJCR6q=u`d;gfCdoA8*=pCeJ{#jJ=Hn69$qX#q+>(UCW^c0ny3BV4Ky0--h9(muQ +zvsl`HT7Yx9B^RWHg}F10o_`0RJpFl&XK$G!i~ZmY30bHRr2UB?5o2P-^5lP1pPt5= +zC0sWB!(NJK(n!nbKu!u531(gsS7ahk9+|bMz9dlG_jctsg~l;k)ub(VUJ*i6zb`|M +z+nY(gIjqDyKTFFIMbsu%`WB1mG6;c$QYm(~cshA3oOXPPVxrFDt>nl#bLQ%)^DU#i +ztg^C{Ddn@061AyP8iiRjmVgIN? +zAqxucwDt?jJ1Ssz62KpeWnH2bNEFBRz!B1FM!Oe2I!FEy)G>2yntDvN=v#}>%TeHD +zbPz^L<$23!9-(gtfh4iWmq>1u6= +zQ8N0)BlxLLaCM9pr^l8o!+h3{Wk|VAZ2E_29n)T}u)U&ViyEDb6H76T3D3mfgQ2f3 +z?f(fN9^c^|JNx4+;V=3vS~O_BNG@@F2C=~K@X&r~*n!R#XtwGdG}5)8;^;3fd1zxE +z4-acc=AE1X-wJMLlI0R*kI<3o%=%{~sD_|EV?!MleFO|OFs4>uXI^fgHMvpW6i~XZ +z+M}qSlD$Aj%HsMYj16Q6VeE0(tU49+H{%>MSEvh1Soc(*NI2Jnpo~MLV+76>Pl>s8 +zFNhg7S(;IF#ZqL+5y|rW$|5T3iY?d*h|#}3lY0 +zGxD-d31K5?Hh229_z7DfoFn({B4mg)&0~4zdBeWIIvkwk@M(HGqpEt=WVe_}8YIaJK +zF8YHgjF+{DvD3}RAc5D}c$cL0Irw_R8|s$Fp)P^@?VX4#n7r_3U+L7}R;{a?#%|^f +zZ5!@ek+&maJB1n=vlCQOPV%F|rV5DO0<3|Ds5mxdAfM(Q*xLXe(Dsl)$RNl*#9*#; +zR2wF$CVAzGS!gMlBnmfeL>_rOcLwLU#DFo55KJ8Mm|CdQQP87Si4elzumJI6zSrR4 +zmYvFoa2N`@rb>Bg+Bb@!bdUkLO*S_@w>|zbGu$f34#Tpm^HqdW$XOXlC{>+5GTifF +zV{wZbNpz?@4KTI)#?H=)SnA(>t@g#)R6yNAsUVa5J8k)!5(*_sNv@5d3E=G4wZ*m= +zQ3_KjVF+~*4wu<^J2&45%3>2)ff`jzL?2cE0Pfs(!)Qs_P$V{88b-&t;}Z(B>QG8t +z1-|=>Vy$6n1zjdntw}K~$_O^v+~JCWX8dh5hc2s<#NOa?aCxm8C>!SXPE+NzQ8*=m +z3w?q9xJIi=i*MQvMiiE#uJtr%%Ea6b_+Dcb=KhG-rsT^jrMU`C{{TVyVDp$2N2pR# +z=yL4FHzwY|D%;->b!H)*PLMw~0=1`uG$03!Z3ag}&#{2>O0Qbdit4*R@aI(ey +z0^rs~-rXz6JqE68V&fFEP*ZN53{-xzdG +zpI71P*O!mwXfAQ~0Dbbh?w{cvz0_+86zv|@5DYwNFk%R4`Hl$LrwgQXmx6S81w}Vd +zb;WfgkgKeTBjq#6NHm*)b}F+eBEf(vb|-6t#&v%hRlW|k>5U-77<)tW?Io}{+-1A#Ji0$^ZD?KN`rk4w&!Sw4G^)?_(^o^Dw~4M(31 +z1L^+&+^C00BG)7YWg7w*+Sv1k{C80Xt-~HGG(+4(7m3-flm7s)CxSU$?K`V$3qZ<^ +z6e?2yIv5$O4k?BbGZFJPcp!6C)1Dw@z9aQqleR@&o77HZrE1#6ilRD43QD{b+(e-A +zU7pLyUPfK-N2}2+!$m<%2s&FWE*+fx+)tovoq^eA)2V3NZz9&KSQ+yMf>`GQKq1bV +ze8vMqO!2`i%DX%8k1)-;Yc=T%(=v{(RY)VuGdXGKcLvPq11xf-&GhQi_rz#Yq2fAJ +zuISfm3r0&=9RC2Ja4u=}iCemKD0q_EjqR0Tsk`WFOYzve7c|BR0)3Ug;pNG?YKy9R +zgQnx^oZ_mb40(n{n>AUXr6Agjqemn$6MssPdjqk?7O``5`#WmBO&|=mqXTt2{qh{w +zJJif9CR7)fZnw98!?mp-vZXNHZU#+2;O0HR43i*983{9A6``Z(oZWMZS>l2TrG;_~ +z$-IZAmP7gFXi_sPF0-L50tK~&t{uOJ-B$jQpg|-9<}>CF1ArV|q!{^*ONrdAV`*4h +z^$JDR+RBaq`HlI>4dJaWsiCGmVobR5RZoPpy;1)Fr0VJVe=nt)xuBAy$5E1@o@#Bw +zv~#$*Qo9W=sE}+s<2S@lwD`ul<-{^Tkx;;Q0JL*C$d6UK>bF)G73o*Gs!bw5)+!J+ +ze=M=2i5!5+C(8DNY_&USlsMc +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 +z2W_;R8f6+}VK^miCgipEJMV=FLPwG&Wj` +z4U#>iB>V%1zRB;hU16I}`VTg@`r$+(5i>fFFK{^E+W-jwiCmL*(yXIndtpLELK0Bi +z-EVKx4=LqjWk}F!8-L{kkJkb32tDB;c8zZx><-u>cSK3b!6cHnAOL%u5djbaR%0E2 +zB(XLk+~D0CqGkxzI@NoNZG0${qE>$lwf3?74h*9ye91o{aQkil0B#5ffw~r#Q3_Fu +z4ThdDLU9RDykr&yPse-*DHthQRlU)NI*2!(WuP{f-EP +zw8tn5A?8L0us6V*6maU}KxI3VelUtogskS~%oq+ufg=D20auw-y)3r3{n)l4T?or4 +zu~I_(-+V37Lm&<;cs;no7))Vew8el3SwOG{YhWNCfU&g}yV!ld-vIPLO0z@0pg7rw +zY$z!~LaxVe&%W4jnGlT$XDUGqea;a`_gIQSu#DL4?|`7B3+o^@k_PuSz=WwNzn1BB +z?A($1U?Z{!T*je)wuh)z%TcAlv5W5j&`z&?BigUI)#M +z@W@Pph|41(;2&T&I3OjYO2H(&+#wtb;DCq=Jm;}V3m=X&yq)-U)uo)At7ZKT?7{Y05@;f0)DA(R?)2D=k>M?^9gU5 +zP_Ddb-?{CJAzKN2=}8f-w(c-#2+}tx9ohQpsaNa +zp%@?|=lbEyAYjN?SKlYR(nKUFVOg^~RP5%LE3JyEt%0;Nuc5AsIi0$k-LtB|QF9~Quf +zSf!BNhwgU4f}RR3 +zL6~VzSH}keN-`VAc4Y^!#Lnu&x>b$I^)lf~M1+Bro7CEE-%apGWDy7?+*umi;z~o+ +z8%wWBI*q@Wo8bWo2F60kWJ_r>7PkNlAP@mUsw_#^;h>{s(HL<0f#>aoxkzNB4y|Um +z7T^MK;u9c@ECrdaeOC6u*&89T2h>PY+WXc3-p`=h0#x~;P1WIrU +zG8R?8m@Yq-`{8Vs%Olfp5HMlRqk#IM-l!f!sH<*0t%48&#F2-jNOb)6!3ZG~nrsTb +z=idNGEK4hprElK&3TGoJ!to$&AnN{@P*Q?ZVnnsiLa)$&u9mmD6fZUzXd5r73ox2iPQ@0e +z?hjqb_ln|CbCft%wKNvERl%mnGWL0o^+k7q2zUUn_5akHJu)|Jc2_IgoF>8&5 +z@Ce~C93v8iu$4CV7*W|M?3-zB{{X^Wu_*4C7|L-OLAg**+Yy9_N_@5lNhp1f7&4HM +zgqBsa#c}>%cEgF;K5(&Q17XGpL?(x<+Sq$3Y_WkJlOY4xU_eNomb$3@aL=k|JECCB +z+=6@ID2NC&Z~3qG!<9KsK_o$n9sPj9a672D6P1o+Ni`tS4&w_Dkb*LnLIAjsvtS4T +zGq^Xg#FUcXe^BEIB=4DI(omkm3KVqaP@O0MD%UuBscwj{Pg0HkaFX8z3(D>WtNxo| +zfPmDR#k7D=-vlQJZ*73v3?>tiv*;sOKLl}vMpRq`q5uW;ZT7-Q`X-4ZwZpNFBxj2?$u)bOHgwNN$^0S-n=U_{615xjrMh+zSjwRdR%*1G&SM +aDG9cf^_kuwX$0VIj!k?hcE)`{EMZ-4|FSSdhitC0MW!2patH +z-TSIu)$6KLeY&ToyZ=qkOw<<@Ih@yIuK@r6j)J@l=tY104;aV*01^m$f%?Tjas|mr +z0cs~G4qpsZOUciY06;?m)`KzHi;d|duj2{;;P(9oBqA2Yz!xW}o2<5*x}$}ghp~$} +z;ES=fgBz!ctOgA~CpRZIcQnSkiI<=@b8Q6+1qC($#*2XlAVWd}AiXff7m)v7{$D#R +zNGSi~e`yH7jf4z%^P<5ou#Q0eAHUrT|4+^#NPq*B|EK@|ssO;tBJ>pi^(7X_!wW>p +z|KGg-)kMqx-&|=g{69f{$&CCzksx8A7xsU5aRY(>t?Hc^-(en9b^rhrFa;S&4Q)C7 +zOiXCWynDuJH<#myUnZ4>DJkk$pjq&k3DgYzHw)9W2_r2;k(E4~kb#kPkESY#ry^Q0 +zzA9yjsEkk-LMIy?JzUAsU@leJ(2RB~vN2TE%(0J6D<&I}{aJM`50o$nrV@&nZL`VQH2` +zI^4OiYC5i-CW~Xn#RmIPT;Em+c^5gI%=tq+`^}5j-PwCyjOSG)K+1PUyCW_xtFlxQ +zoB?i-N-=nbyC6D*k0S2(v{h()`F5@Wx`X+K5il_Ms>AT9G2p3ra3uN`O;C*TOq%MIS{BPC64~A0^Dy#=fvGDN|hKCcIwz%YU|w%5u%`MX2&^`M*Wqxh+A +zXspx_gntW6QGEki#hT&sc->hQEmd3(1XOM}ep`E(^-&dD)r)2$Fov%jB~UE(7nm4t +z-bna7KYFDx4jDM!-0v@bW4E=v*?ib4K}^Sx`}0s-&)V6$Ug*Ir=8vaaWJhr*TP=KO +zH=MQ+G_mWzbE<0oQMr!po1pbWcrGQF+%{yv;_a@=%pv&d(S3TIKz%pfoE^5c0xPt- +zgQO7qHRy5kk2IKy4>w6#$gUPSn`IcvRp^ZgKJ5i{xrfFYhAe-i2tpKueeOxKe&l25 +zS^V@SqjlN)_lNi~RxF9g7`steO()9-gAczgowso1mYq^YAX;x$(M}bzwq74J%84Gp +zzwhskkCE%tI0WzDV`o7sDMp=-88@9D*J5UoM~bFA3InAronMb8=2Znh7O7!yF2P(5q=^yAdgbI*H8`?Q5z +zVL`<%TcEi*(+DbyW{+MpS9wym +zoGs@Exo?S1ov+sz0NHOVA|tYTb&H`g{xQu^yHDSfmfq^xQ9^zzYP>1?aE=P8FaJ@F +z>qs#96F)Vpewk}j_dX`ESUinF&;^G($_ZWhA0h=Iip_u|$zs7q{VQIbt_6+Y)+}L} +zzfbZVZ+MgPk=Q$#(y9h1s|!NdOyKL;%Mkz&=aIY2Phh*mgbJPF$z{_)MMBglJW~FSCjTJa-sCwLIA5w@b~yEY%qh61Ifj1K?8*f0mEHCc6%zz(r>2D +zG&{YHRu@SYY-Hxhkhb6G4SQi>S3K +zc}AjQTc5`Tsh(yG#U6jJw{Aj?#+K)=t|*mOpSPo=j$nU-zMSu+=mL{ +zv5Ekf@%qF{>aO1t>5boRh>dk0Z*8u=YaZY=7Um@?GZ^;(qU7Laxg%eRkl!8c9BXcN +z)KBBibO&={Dub|+G8r*(#qdUil5Mmd2K6HGq1jeE(#?c%+(Ki!S_n~ZiRt2-ZY?~Z +zHba)=>^#H$xG1Y0YBxh@1+MlF0S(LW0{l{KdDqn@^D={9tRMhQJg(%HeTH>K0sB9w +z>3I@WF@)vd@>HlH#aN0QjIKKBAVcC9(ykeA$bi2+T(!T&+>+)erK_nkogP`9N))e~ +zge5s_OjljUJ%BqF33S05##W)5Xhw-cZN%s<&|Y&&B2~$}4qBrMWilC`Gx5V2rt4sc +z1-Ll8`?afl#1olK$AJZfYt-#3AgoOzOVR}5BA*2U2FSRSOT8pp +zq+bpHe4{mGE`J?SiDO;Sv<;XoE?_&LM?qRlL=l*_l1TbABKCA6T+a(X*t +zwN8t~__kBw5j%@cAbB$^8g06IdC!9Q_Xf03&iQL#U#yuIOHVUC%F@|>`lvj1(1XE` +z8Ys4$^siRBh@`mYg4fS8XeLBp8?265oCeA_a0cGd=?0cPoI4R?5_o*f=oVf$ +zL5dMWXIt$f^qAMkemWk`H8Si)lcIF!M+&iXm+E{ +zVexB-74{E}&YdzDYgTK*rDkYBw#)2a|I~E4C`Xr=O1?jL(R4*1mcl9n}v +zx0`#8|F-9v0j-So+pKJrNorS9%~gFm%EX2i!d=P0-%C($?^4aCx2g7Q9o6-NhqGmp +zJv$ICn=+pjztg*t6p)cI?E +zLRTajTCFUnW$&T|an^%0CSM?D8j=Mdqt{F|L2yD(1_kjh@q=?iki}2}bp$8zS>~^5pj)wY5^<2*l$dlwlGJ +z9T~!|yA6QDX?->vtx}KgyGMp0RX*CyMW-myRHgDvY)o +zC4?G%M>j_CD8*fdzT0ky`uLyrtqoYXLJBuduQkQ*)H#h)?aQJ4}cDpZrxxJGgQ;NQBV|G_oN +z^I^y4YST^DxQZaBg4>CJuSFH(wAbu->(eE(gm72m%<;`r +zZ<%-)rlI)Vgc)o`HEFMDKW#GSc_hzoXZy~gX+5*+csAr3e3}~(-gUhtxWO^xY-*aE +z-!^5HE$(x@G_u@tv*C4wDBhHK8cW#cTz&e<8}aF+g-Ao6J%vwUo%22Jy6J&h;2km) +z-Cn}2$+%r@=^5rOpbH}G#^cgIbbnPdFG&c8Ynh>JzyI8BUB+7+(aMWhE{6Kb++9}N +zggqRZq9P*sgmpA=Lx8x#+Xegj$`>U|k89s2f-++=i#lafI}1e({Vv8wsXmBjl!t14 +zMNUF3wTe&jcbq%KOExA$4>jZ_iQjFEVR`@^0T8$K9Ov&5qZjL~AC3~g-QD~?A!Ilw +z{n{*_-)zRV`sI(n9WxPnWkc!rhq=q9ilNV+R{DntNhVZ#Wg$;eQr8S_PByjvl-~uN^Twb-m^7R$?4_P*uU +z2Wx!%=`AI^SeYE-_J$D+%Nz?W^pDeFY8cquQ5KgX+g*XCHA_5B6sPR3krbyr+_}Uz +ziQLoHK&!b?pMaED!>(92DCqL;K`x>SivxYB@24LjS{hoHq>_=RlD@eRDMe&oNXmeF +zMHqgl=c~%&ahGonL>1_%_7BK*he-Ncy(%FPYrCMq;ZCWQxwd%S>5?6JWjwt2T%(UE +ziS|0!lK7;gvfPg--;0hliYR#7+Xrz>=xVx@ll~P8%ySiT{A*8pdx-s(*Q&Gv3-hy* +zkw37)%bbLy!E=CdxU$ML@!yP#_>>8+59@Fw>Z9!!SwEB_{ITU|ZS8qqqUlc-pUh_F +z;`}eY=FaG7%9VT9NVGJjXAo@1U|!OPI{r@_1?460D4G~bpx5=^V=|j$@$E@7=hZ=w +z9qDREJ^Pk7B)Uw<>ctx-tXW%Ndp{#VnU- +zE_z91Lzxz;5g{39nVtjDzj+>m@~xL?8Tso4(&v7IW{Brv9^~R84GLSBM%l7@V!%+F +zR~a&~G#pY$!bm+oRC?&L%@c3s-QZ=S=0mkuOXS$o!9NBJ#x)b!B^TVw#WB+qcWuK_ +zUp?nrH#7RlOIPpo7kqrz+xwICdrH>kgDUPjj2e7dqb=l0<{P*S3hLJT;F)JD0!&MM +z8M?O=ws$#RiK=8Qa^ +z+MAW4jtETktz6LeWl?a$shA!MQdp#I)O+_$eGJm~jKVm}vm2ucQNERPM6 +zuY6QU! +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 +z2MH;pqYHrt<@o)iO2RfaW@O6M +zMxyEvmb{a8l{el(g5mhSkmRGtkDr-_ysX3E6z=!qy5dMd!(Gy3NJAM@OUQ(-wb(qD +zC0vWKp5|b6s6xMf|EU-$gNG?0beuESd)`?w9{Niz&mkAPJSOZC9Vxc$?!MV7Z0)qH +zaPs3sYJ^GrlaJWH6+9`qW3CaqOr5k?wdMmU!_ItuesM{v986S!;W69kq3T3Mbrkgo +zsDK1PqiakAcFHo(H)wLq=VD6?OxNJ6_N8ysly}?aw|X_0_G;u31tE$b1kC8W!Ja52 +zh~i6R0UN7$Qwr&E{9QSeLJ*K(xYA$n98t+l&I~doXQRt2Md8R`+6#ia(`EpPDCl3~ +zS@7u;1oHjb0A8%1B9m8 +jDEQP1VxM(DN6#p^j{G)gSVZLieP$}is>swz83q3zIq$^K + +diff --git a/platforms/android/project/app/build.gradle b/platforms/android/project/app/build.gradle +index 3929a7673..d5f75a6f6 100644 +--- a/platforms/android/project/app/build.gradle ++++ b/platforms/android/project/app/build.gradle +@@ -11,6 +11,31 @@ android { + from '../../../../game/assets' + into 'src/main/assets' + } ++ sourceSets.main { ++ // Another hack to automatically get our icons ++ assets.srcDir '../../../../game/assets/app' ++ def iconDir = file('../../../../game/assets/app/icons/mipmap') ++ def resDir = file('src/main/res') ++ def densityMap = [ ++ 'icon_48x48.png' : 'mipmap-mdpi', ++ 'icon_72x72.png' : 'mipmap-hdpi', ++ 'icon_96x96.png' : 'mipmap-xhdpi', ++ 'icon_144x144.png': 'mipmap-xxhdpi', ++ 'icon_192x192.png': 'mipmap-xxxhdpi' ++ ] ++ ++ densityMap.each { fileName, folderName -> ++ def sourceFile = new File(iconDir, fileName) ++ def targetDir = new File(resDir, folderName) ++ if (sourceFile.exists()) { ++ targetDir.mkdirs() ++ // Copies and renames it to just "icon.png" so AndroidManifest.xml is happy ++ new File(targetDir, 'icon.png').bytes = sourceFile.bytes ++ } else { ++ println "Warning: Could not find ${fileName} in ${iconDir}" ++ } ++ } ++ } + + applicationVariants.all { variant -> + // Ensure the custom task is executed before building the APK +diff --git a/platforms/android/project/app/src/main/AndroidManifest.xml b/platforms/android/project/app/src/main/AndroidManifest.xml +index 513a5bc65..b14d45171 100644 +--- a/platforms/android/project/app/src/main/AndroidManifest.xml ++++ b/platforms/android/project/app/src/main/AndroidManifest.xml +@@ -9,6 +9,7 @@ + android:hasCode="false" + android:theme="@android:style/Theme.NoTitleBar" + android:label="@string/app_name" ++ android:icon="@mipmap/icon" + tools:targetApi="21"> + + +diff --git a/platforms/ios/build-ipa.sh b/platforms/ios/build-ipa.sh +index 2ca216901..b38987894 100755 +--- a/platforms/ios/build-ipa.sh ++++ b/platforms/ios/build-ipa.sh +@@ -37,7 +37,7 @@ cp -a \ + "$platformdir/precompiled"/* \ + "$assetdir" \ + "$apppath" || true +-mv "$apppath/assets/icon.png" "$apppath/assets/app/launch"/* "$apppath" ++mv "$apppath/assets/app/icons/icon.png" "$apppath/assets/app/launch"/* "$apppath" + rm -rf "$apppath/assets/app" "$apppath/assets/.gitignore" + cd "$ipadir" + rm -f "../$ipaname" +diff --git a/platforms/macos/projects/Minecraft/Minecraft.xcodeproj/project.pbxproj b/platforms/macos/projects/Minecraft/Minecraft.xcodeproj/project.pbxproj +index 7a625e3e8..d8e42801d 100644 +--- a/platforms/macos/projects/Minecraft/Minecraft.xcodeproj/project.pbxproj ++++ b/platforms/macos/projects/Minecraft/Minecraft.xcodeproj/project.pbxproj +@@ -2359,7 +2359,7 @@ + 849371362EE51E6700081C5A /* Default-Landscape~ipad.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "Default-Landscape~ipad.png"; path = "../../game/assets/app/launch/Default-Landscape~ipad.png"; sourceTree = ""; }; + 849371372EE51E6700081C5A /* Default.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = Default.png; path = ../../game/assets/app/launch/Default.png; sourceTree = ""; }; + 849371382EE51E6700081C5A /* Default@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "Default@2x.png"; path = "../../game/assets/app/launch/Default@2x.png"; sourceTree = ""; }; +- 8493713D2EE51F2A00081C5A /* icon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = icon.png; path = ../../game/assets/icon.png; sourceTree = ""; }; ++ 8493713D2EE51F2A00081C5A /* icon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = icon.png; path = ../../game/assets/app/icons/icon.png; sourceTree = ""; }; + 849488342C9284DA006DB706 /* Lighting.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Lighting.cpp; sourceTree = ""; }; + 849488352C9284DA006DB706 /* Lighting.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Lighting.hpp; sourceTree = ""; }; + 8495E4872AF0905B00A06901 /* AppPlatform_iOS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppPlatform_iOS.h; sourceTree = ""; }; +diff --git a/platforms/sdl/base/AppPlatform_sdl.cpp b/platforms/sdl/base/AppPlatform_sdl.cpp +index 30de0ac72..ecacb41fb 100644 +--- a/platforms/sdl/base/AppPlatform_sdl.cpp ++++ b/platforms/sdl/base/AppPlatform_sdl.cpp +@@ -145,7 +145,7 @@ void AppPlatform_sdl::_setIcon(ImageData& image) + void AppPlatform_sdl::_setDefaultIcon() + { + ImageData data; +- loadImage(data, "assets/icon.png"); ++ loadImage(data, "assets/app/icons/icon.png"); + _setIcon(data); + } + +diff --git a/platforms/sdl/sdl2/CMakeLists.txt b/platforms/sdl/sdl2/CMakeLists.txt +index 6c067ae3b..f575bacfa 100644 +--- a/platforms/sdl/sdl2/CMakeLists.txt ++++ b/platforms/sdl/sdl2/CMakeLists.txt +@@ -96,6 +96,6 @@ if(NINTENDO_SWITCH) + + nx_create_nro(reminecraftpe + NACP reminecraftpe.nacp +- ICON "../../../game/assets/icon.jpg" ++ ICON "../../../game/assets/app/icons/icon.jpg" + ) + endif() +diff --git a/platforms/sdl/sdl2/android/app/build.gradle b/platforms/sdl/sdl2/android/app/build.gradle +index b16082fc3..7d8bb604c 100644 +--- a/platforms/sdl/sdl2/android/app/build.gradle ++++ b/platforms/sdl/sdl2/android/app/build.gradle +@@ -30,6 +30,28 @@ android { + sourceSets.main { + java.srcDir '../../../../../thirdparty/SDL2/src/android-project/app/src/main/java' + assets.srcDir '../../../../../game' ++ // Automatically get our icons ++ def iconDir = file('../../../../../game/assets/app/icons/mipmap') ++ def resDir = file('src/main/res') ++ def densityMap = [ ++ 'icon_48x48.png': 'mipmap-mdpi', ++ 'icon_72x72.png': 'mipmap-hdpi', ++ 'icon_96x96.png': 'mipmap-xhdpi', ++ 'icon_144x144.png': 'mipmap-xxhdpi', ++ 'icon_192x192.png': 'mipmap-xxxhdpi' ++ ] ++ ++ densityMap.each { fileName, folderName -> ++ def sourceFile = new File(iconDir, fileName) ++ def targetDir = new File(resDir, folderName) ++ if (sourceFile.exists()) { ++ targetDir.mkdirs() ++ // Copies and renames it to just "icon.png" so AndroidManifest.xml is happy ++ new File(targetDir, 'icon.png').bytes = sourceFile.bytes ++ } else { ++ println "Warning: Could not find ${fileName} in ${iconDir}" ++ } ++ } + } + externalNativeBuild { + cmake { +diff --git a/platforms/sdl/sdl2/android/app/src/main/AndroidManifest.xml b/platforms/sdl/sdl2/android/app/src/main/AndroidManifest.xml +index f580f1b9e..85550509d 100644 +--- a/platforms/sdl/sdl2/android/app/src/main/AndroidManifest.xml ++++ b/platforms/sdl/sdl2/android/app/src/main/AndroidManifest.xml +@@ -22,8 +22,7 @@ + + + +diff --git a/platforms/sdl/sdl2/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/platforms/sdl/sdl2/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml +deleted file mode 100644 +index da1dcd94e..000000000 +--- a/platforms/sdl/sdl2/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml ++++ /dev/null +@@ -1,31 +0,0 @@ +- +- +- +- +- +- +- +- +- +- +- +\ No newline at end of file +diff --git a/platforms/sdl/sdl2/android/app/src/main/res/drawable/ic_launcher_background.xml b/platforms/sdl/sdl2/android/app/src/main/res/drawable/ic_launcher_background.xml +deleted file mode 100644 +index ca3826a46..000000000 +--- a/platforms/sdl/sdl2/android/app/src/main/res/drawable/ic_launcher_background.xml ++++ /dev/null +@@ -1,74 +0,0 @@ +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +diff --git a/platforms/sdl/sdl2/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/platforms/sdl/sdl2/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +deleted file mode 100644 +index bbd3e0212..000000000 +--- a/platforms/sdl/sdl2/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml ++++ /dev/null +@@ -1,5 +0,0 @@ +- +- +- +- +- +\ No newline at end of file +diff --git a/platforms/sdl/sdl2/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/platforms/sdl/sdl2/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +deleted file mode 100644 +index bbd3e0212..000000000 +--- a/platforms/sdl/sdl2/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml ++++ /dev/null +@@ -1,5 +0,0 @@ +- +- +- +- +- +\ No newline at end of file +diff --git a/platforms/sdl/sdl2/android/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/platforms/sdl/sdl2/android/app/src/main/res/mipmap-hdpi/ic_launcher.webp +deleted file mode 100644 +index 3896fbcd7d24a1eaa3f8f706a0ba817fa3b5c429..0000000000000000000000000000000000000000 +GIT binary patch +literal 0 +HcmV?d00001 + +literal 2682 +zcmV-=3WfDjNk&F;3IG6CMM6+kP&iCw3IG5vN5ByfO(<&H22#8IQvQT@V2J4d1eEqR +zS?zp)LwJycv46Bv3pR};IjXyRGvPy6{^&M*2gGqCNmA1ZNsB(`v{cfQU+Onl28qDSY0C)>7F +z$F^;&)INqxwq!OL7V}zO2KS7_axMSzuK@6hzzW9-05iL`iLM +zY}+=rJ#*g|pTF3)ZQHiWDWwlTk_My+X@OaD&9-g7|A!aPpi%gdQ&obrlm;CFi4q|r8mP!27$jJ1 +zfMf5zkajT6urVSDWCoZQnGk3!rV^MaKfzk&Hn@ENH7(&jGRY=ynC{ +zMwSGsuAUtOq=gHxBMFv8PxLiK0d^)4eHa=RX)$Rj;U{ScfCLWAwhRcaVeo(Sg@#e1 +z8Iy+z02YPN$qA4N22`8~!N7$;h*&E;E+Sr<(zP&rt1r4WJfRyVjj0BRf)O|bUxSud0PP2M0 +zT$rO5eiHY8D;$I@8obdL3{ax(xKRx-NaSF1hWnk%*YMA0QuintLN6L5aA**^sRkrh +zE@Zez=06OB1Hd}KgbW7sMc#oYK;RJK+-7M9cr9R4O+I4s$+>nXe|$KRfP?_3P(*@j7iRK3 +z<8LxL4zO)`oWDPod$-Q$#EjnqsAYISZaWZH+EtpxLr1sHas>TMzRct&=ms1fli_~( +z-6MF4hS_?6#cv$Fkn4Bx_eV22mcz5q+)-l%!<9Tfr|&>X*A$Zj|c2P%~EdM$Klxl>eGj!0pRBKIsJ&^H|cg{Q`|Iv=o5EH +z7%C)zquH5jw=q04=b}qP!0acEUP$*(0s#dfG@_CV&C^FlP!OC106Ss7Has*J&O3vh +z2AosFJ-Kml_V-4ODtb{SU?gOxmn*O12^rzQhA87YrEakMxqJ;d2b^1jRj!;iYFVyP +z)UDwPDllfb5u`-vvuoh<_UnLVv;w(f5jgv4~Dh~1b6QCpTC{<+6F5vMExYh{gvw|8J$da^7ut1An8XFZeo?7 +z#m&9==b3LT_dfeQ!rZ1}DZO(`#aP)R(go}v2LWs>!lff8JOA+FK=vAyASWr)iib +zXjeu_9w8_Q5}{oVUGf^L06_)Pu7)w*F$gFigWV5?I$b!$NLp+ZNgj26mN^OvKmdS* +ziXo{WWGp}+DX$#_3Iqd`2`Lc}2T8_Bn*(W*jh67R^UKCj1|Z6SlFGOM1cZtJ+@K%; +zOHIK^Ol6eVNH8SENf=50N=s;%Byn!ja@ySkz$3foH|Ae6(qbHx0KtKZ;0ghN@(wXT +zGE|%pfD_H|G+v{oq}?fMApsb~$r*;wV)}wf4EH7;?=atM{q?hA{6AXrg0ysyY +zLO?JGg+(BtOK3+Bs2~^zDnJ24rBXOEog7c-i#&!YmIx5eyV}fMIB7XbTv4z{oG9Zw +zC_@*5D;NYri#nW82oz3$5{j~kRak(6D|kXENGPZmZcnofz-DT2Cr)~tbdwzcAy5H; +z;6gwQ1&Vqm=NwEa0A#3RlnM%j0LmYV&|$*>O}MLaBqs?%rffTsn1~bAI4QrZ#4Cvb +zmSRAq!KML{+?jLaoHguVB&8svAyCBE4k|+0k#Ry%4GQ7r>z?orXPiOYQiy97(v@$i +zSi)=N$xeVlz*clO{^g7t6eKP{$uh)F9AX6kp~4jbgd&Gt)@BjS(RAbi)d1s)S^|dw +zlmIz*WRB{LI}C@EP&mO@fC@sYl)1&=xJE&te5*XULP0}9pke`dVs@IP|Mi8(c$QA$ +zm6L)ip_2@-0FZDqbJEWQf&(JsoH`UPfPg_YR;9uaNGQq>JBI>DCRGxOCn?UwVdBv->}GDU3#kebvW7DAX;i^PrkS~WSDZ9abcFn{fFdtbNF +zl*yuS&Lz1jcrkG5e%EiE(coQm +z6HQx{0RSMRDY=(GLP#CCB^Xdi$SuNEIm^Q=A%iC;>P}6JjRN%$<`qvg=njTJk#ZJl +z{cSwHwDRT{%8OL%0?OMD`~be+^w@u4^|^aT8XZXr0N8XbZAT!<$SrYSoijOUD*#;a +zG{H>VJ;>0Tl4R^q&_%teI}InyBB2cl6(?umx9RDfkpuX6&9BjOE8`ZepN;gx@XPhC +z_K+LdcdZ>cDd;~m!|B#yNPqz}2fcVLk_w=?&|rNWBmkT`-8@tn2PDV=mUa=(Mq>a5 +zBu7X~FiD=Jp@=kfKfi8X-fYg`^?jN^k0732s_i>#x}gi2w|_4S3jD#_V+((LNYM}T +o^uO<$x?O(j7-x;0C9rKZxjdq3{`v3;pXb8a#jQ0c;QuHA06AE@J^%m! + +diff --git a/platforms/sdl/sdl2/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/platforms/sdl/sdl2/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp +deleted file mode 100644 +index 2ad8e1bbf6cc4118601a0935a9ec93dbfca30f97..0000000000000000000000000000000000000000 +GIT binary patch +literal 0 +HcmV?d00001 + +literal 4342 +zcmVZJ3xp>>UgdF#$3Y6;XtC +zltL>!RNhkj|6io#{s(bE*4=S;XJzJb_m$c5pWpD`XRm`pa(8!^=9R<=-~gK4BHa$4 +z$vPLHT61R;(UYEm8#s-|4ICO-(;sUj&wc}lG?z$QE{|c7NLuvAo#=(kT_bmQ +zcXxMpcXxMphu7VvY435Vu4ZP%nkgm0FN9xK_|k=T;D?oX=9CR=rD&H +zXx!c1-64WZB6oKua$9%DAtalKaNXT~u{oQ%;g$S%uVBydjW*j_-<>;nv1r{j(s)m; +z%eJ-cjHItGt#6`^M&FO5rYo+f7!+qJtg&s|wr$%s4q(}~owm7C&tTdE($N>dR)vnh +zCWMX*42A>&!canJL6}K{#_r@A1wDwcsrtP8^Nb6SO6(%UiCPq)#^gk5`PFNWx|?G# +zc9zo(0D+Q>rYIXuA??vk8oz0fF{Q*92p9ZuY5@{HP^f6VCL2(WiMF+;%;6~_F4Q5KGD#pR)`Cxo& +zaLayt8i3pO)U(e+kRY1tCecF*3709fP@Jn>F~*j`81E{QTwrQhT+6-&KA+JatrB|& +zfDXsEQb?7qT}4x9_2Y@F5KMBRO`%o0VhXA1KzfKadDYzB0Xk8Hw3NScVS+IW( +zR^?q1iWsuT+9Ul +zj1S=%&x0@kQ%KrR6vXwCv0O!=!N355vJWoN0+b|-m9^49+GR?IGDZ+sBw1lZEK}td +zjxJ6hI%S{L-}dXb(dvJ}9B*SOe&pGDJ`c4u3=rzskf??=l8`~oxy%cM`o8sM6*v3@ +z129MV>(z-~LF_s6L%_+l5>ynIZlyR^k2fpw@hnlYv_Eaz!T=@|=?w#GBzj%5AHxzU +z2$B}R?9V$&k(?yVQSPf11Rkb;6BrM8`p9Z(No_4h35d@n1GK?-FJ63#r?~)V*=}vI-6y +z?Jo#oddWs+W9QvOHF`$3#
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 diff --git a/platforms/sdl/sdl2/android/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/platforms/sdl/sdl2/android/app/src/main/res/mipmap-mdpi/ic_launcher.webp deleted file mode 100644 index 9db1cf006e380fa04d7c174d9f2fd224a3c76844..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2172 zcmV-?2!r=hNk&F=2mk`6^*ehR&b=1LzHQq!ZQI(mm}~8t zp3AmvucH;#p*1gIT?a40JoC0M!1g|~?Y+nB7_An+wQZ}mZQE8%rOnBr}n}m(QehxJuZNM`1D=-N%8D^P&aiR7c}twm%d%u{r_dA`z^uh z5$hWOuzH^LzW{*M0jwVYfECsNfYmds{|f-Do?-o88Aq%So*h(F6x5~8Ububo6Q3RRb5CNSPkb?g|8KqZ)9Nz^-rr|+8Wy9C zELRR^{Br<68eIW^bQE0y&=5=jfQ|_O&{3oqYO3wHox}G}+;8ITWtS1O0Px{bz+cxb z$vFmLbRaDzKnDO!Xb1o%KnK7C=m01TbO68vbf;=dCrM*Rj#>PDV|N4+{`arTA~gx0 z&65b^B?8O3Fel4p0t8S1fB*`B79kdZpa1{?6aXNALdT$!d3OeK3QLy5>@Xdm4g%0f z76E`p0ssJu0LThj0H6^70E+-XhdvqL&XUdQ-J1V9=6@e_AZU>V06{|lNR~Gs= zuinqej{^V-3FsI!=97T!oh6$)UCc0{0fJxv04Kl8wKwGt-^jcU)Evc_20#j-1y)D7 z>`wmhR`xCm0Ho1S!$2dL7J$Xo5~yV?5I{j>0l<7R$G_u>%lO+*qLR+uvFH(0=5^M; zTD?QK;z9oO(b&I?=@j|0uDOx4!1lG` zs=8VKbsT;f00Jxkk_0x|(2E;eIztD7h5!e5zUlIZ-XZ}75jLr?B$?&fc60cx!|6`} zNYO1OG-`U_0KnDKmRXGJo;eMHiwUofKTwx z@AA{{j6+MZI*|S!wGGrn%~CsTi9|AA)nXkuDP6WQ~KC!Zs2K z00^vIJTc!bE zL}SOeZSbecanTK;NLC9Z2yI0a9XbS{NMLkU3J5wwUl-GZ^Zf_ACjr|#OE#ymKgJ$L z^aUzf)Pl*lGyHLulL8_g02+ZxhXi941{#%&L9$d@l60Y#kq)eJ=qa#!=xS-}N$%+R z%ap?)OAR5y>Ox4tl8l0tM3K-HNN502D>AYa@QF^zjX2CYG9c(cgcP0CqKK75(Ln_g zx*))cbWp4D$-t_{^;!Q+PJ%!nMOy?|5(NY;30;tcmJ}UQG}_u^c}Sy{OIxM^614~b zbf_>H*Wpl3Dy)QINffbUs3ky4p@j%)MLN-zqGkU7zkwY~Hm9_5-k$&fmP7>rf=*zC zYr=8MDLN~O0vdrtEr1pl1Z|`i7<41a!GWg|PV2O^8p@i?z=*y;g~$Q`(nuDplB?vn zaaK{Vv}Dj(3c^a#1z{-?S|p0plGXiMP_@(l^odVL(7gBDAG?qCeV)?d*cn!gHe{=8 zHB8{+CFFbh@vMp@?*geliU}Q^{=FU-jwW6&8K0eDMR5 zkL>oUvMYA!5Gj>Xsipvcfd&8=OmgMO-|(+WEt6U7Q6Z=vHM*@xx1{Jg=*w7@T!dfi z;jPhI=kWk_p`UwF;?sD(?)Q#nCjP0vofzqCW`0B+TI1gUz>u;8fB|8JA%Dl1C$rci zZ57fEYLziM3TUudDK6Chvi1DxtaA)K`<^8LjDMqDyKk&6cQ?CDpS#c(m`(sN{`((w z>}hHM*e=050P6<)Z~u#&hI*uv(Ju0z8;6dlzWIk7y|0@Wy!UfY5*0STbog&AJ~0Bz yHa7qOrW@FD2Jis5nE@9-`_do!|KQD^8&ud5^_73TQh!)t!zcehE773Bmm>gPn);Lg diff --git a/platforms/sdl/sdl2/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/platforms/sdl/sdl2/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp deleted file mode 100644 index ab7b6d59c2fad6caa5ffb72437bb3db0eaabea41..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2924 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 diff --git a/platforms/sdl/sdl2/android/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/platforms/sdl/sdl2/android/app/src/main/res/mipmap-xhdpi/ic_launcher.webp deleted file mode 100644 index f17e96f9144989d2bfa5009d02c45c96434d6c6e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4010 zcmV;b4^{9|Nk&GZ4*&pHMM6+kP&iDL4*&o!U%(d-O(<&HNK$9!m+&XNCxnRpPe85< zkE!zV0|>;gj7!f_GYjVha5nuXg)vYn{WS`z^IM}>(=Vd*n32G@-wxk}?b zv|7)>7a@>;R0M#K8%dHJB?C(f6LU?pegA(+TZp>@Ca|!G{wFZ-hvCil3-LV$qKGfO ziCD7*t`T8uW8w_Ju?)2%mVxBT4FSM;v@h0A%RT1qImvCcYUKiOw%BstzW}6msSGW? zP66uk($G@3=duSlUY)M$bAJHuz(5Z@QlqF;dguXQ%OGr2-``UkhNWU5z~wCAU%lbC zk=)8H!wAIDeI)MKt2aea*aI-$0RaCXepv|tJlnRdMw)GFtxw5hatdZ<=14|aDPAid z&#<0>*Lr|REyf&EYUP=k89rumc5^dnjuKOIn91eADazWmt!wrVGwymM?P+v{S(llanQ3^&iCMDzgC|Ln9ND&Qi^Vc>VT@4`wQKcctm5k0Y5-oV zv}l#lMq`hWktWBswqtwt{k-oV+pLTca0Y*wi8yt7RqE24t&P^Ut&i{fJQ5^HlG_H2 zwjqMRCa)!RyZ{97)@qDz5_b#W@Wa;k1>^&e?4E}T04?xJ2&nq&?b@$v&;q|Z^vTH8 z&7bC)e{oE-3IG80@t@)FZzldF&QfPkznUDzv+CVXIQ*gsI6)2UzL^}p@|b!zl&8t! z-~=^~*!{W5;XkC~f)ld;8*EfxeFj6|`Qp18G(excybao;dYp!DGinqpmVJugNi#`f z((;-y|K>1L`+{I@O7O0RLtB3~F5?9Ib&B$;Y&xq6PH9 zTcUF$_JLpb6t7mjHh*w)HE8ZT{#+f0w-aE62_rx^QB`a2m<|77{^T?vJE)#^2M;T| z#95-l34%>%(v0w!q90oj7dU~E!>IFy>CT^k3@t5Uk0&d~CUEcE_E9h1e0du*Hzj7o z(d}fjy(^3cW;O*xhOqP{T0Zg|G-NQ3C&=<7R1>M`kG0qS{oI5yUcr0t_D_Vwpw`AXZQft2=Tw z&Tg>%ZN#peqEx>VhV_TQ;eewyn=#?MuMOKuRj^B9Y|NX2KiDL&Jc%$&cNUVRL^2H zLTh+foDg!baRMB%NNC4n;*^j=dgTO!#3`W@C$IzxW`i51Y35IW#bNkAP4%K?h^iPk zL+H}ZB99Z09+YBJJWOq@2rf>5ML9N!$Ebx>P@SB>EJaS7#0dnJWz;HKUwp4%9D{ zf{!y$K?=kP1Xtg9nC)ZdTResu{>_vKOGx7cYgbV8*?1n~!VzFm;V~rGa#22!ziBFf z#UVW?A=C~i{+%Z}Wa9-9ij-?F=i+;5rsRasNYP9zaTp^8!H@$+t6Z~5tO%WyT^z<3 z<_H!qAkr>jdBB~3#hFcSR1qi7tiqBwLug?0;b#kp?ca0tg{hC{;`_-zPb{6B>CvG{(X8YLuis)0M3tutCwIQ{Y^$zPoQj#dVMs*G=l{ka#lB2xht|c2ucZMcdTx(_3;>P8TCVQD7r5o zk9?dV&QLRc-WYGJa&;V3Teh?H{@VF^IDvu92+cTwSt!zsZYXF9dI%ZPBrDEOTH?Ej0SuGzM%Hd1G6s2K#xkd#V!99D zkP@2X#wS)}#F$}x)BQR1B(VfIlwi325nQ*2{P_d^{WZNSQOMt29;;Ha?;mWsYbSdO8b@`H0CPr-nH(e`6*bq6oU}wN)WV9Ho9UQ`lT-y0+-JqZcZ8jSC zQ#(0PU^dPWnz0!M+us5VoHIL|e|ajkgu{o$T_%EmdSqr17~BF$w4d- zl?|vxO$Kz1R=M@5RjvUHoAC#QDWdruffXkhA|k+$>;C9s z|N7Ms1Rww)p+U&fjskQ8q|3{7;&`xF5I|Z006;=U(V;mS0Yrd+2mqk$FDny^hyVZr zIQ1g4VMGMd5&<^JaBDoA4LA~+i3FA?dnvn2|BXNb5CKG_1=MGaW3R9z@fp;oGd_cO*3xMy*6+$TeHN-4^CK=#MEtvOu$Utg zEN|z|QI2cSBhC`Xfk>;nV-SKkOB`V{1nHFN^3YD4CJssG%^nJy634_L0!VfThBR@W zc9^scNDCqeY~C3&D}y`rS!NZ%pD1r^iT_+d8mhp`0k;owdM(zY-AT4Loh^=uJ1b7e z?sxzwJH#>V0Fmytq_ed{+9B=s%Ju>xAtR2`ZX*y$qX>ZD%cE84iu&J_7r;iRtsIbv-u`LpC-qgrt1xuRdHwJ zlxUQ$bHLrGDCv}W5fZKv67vcH0N~p&+`2+;C#OX#vTcbuMF;@EZwp)nfV8?JQq$!F z|Acad+(yobM(Oqn0N3uTIR${cA|h@Z=$-+?F2D-8m7EnVNpzI1*~?iVG3N*r30GiV z1rP)Pyah=V1QrSr6eFYN5Mzod zN>EOmAxL|>f$Fn9((d4Aiy|BKxtV-Twigg6l&iH(SHk(9F~IOyQf^WTgNR7?xSB>f zf*8O=UV&iF*#HsO?l}M)MwZ}ao5CQF0MZu{1_Fc;R{%0+AZbZ!Ud07)C|OFlQ8_nR zCIndl0D@LtDH8c!Ae~dtH76LAUhEJq5tFk32Tm<+giA^!Kq|x(K)NFiAnTm%G^B*v zP6SB=HhQ47_23hC|8K?H^XKzQOlpp8#z+TU$7F@Ip+u3<-QMm93HNdaL;$xf5P`J1 z4|bCsEGDy6(bj`!s~2Xxvpz>c&5U+?+XF8^4a=}D3`77C6aes*7T{+KK*VhWG74Hd z%-8S5H$N?&S3&;50}mCL^wQ)1*)_7(G=qjl>A(vxhSkUwC5kky&6zE_`Or;90%^Gt z6)5jAPQ0~Q}4_z*^Sb499f&cor> z`7i$T`^_x~5L0_?Z$=Bc=h+|o?%uGx1ONc-;soHA!?fT50MM2I_yG(NP(X9DG|SXE zg5O}+=l>KCpax@Z$GB0`h(!?*z#!QJ4}ezy@L$;gOW+&YU9^#T5t0|ZDQ Q4*%~w3_$f((_e}J03oe7G5`Po diff --git a/platforms/sdl/sdl2/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/platforms/sdl/sdl2/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp deleted file mode 100644 index dfe0aa8d77bee0bd8b8a1bb73beec9082a18443f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6554 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 diff --git a/platforms/sdl/sdl2/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/platforms/sdl/sdl2/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp deleted file mode 100644 index 1e79b1b04bc2e2bf3a1c0a09f0daaf5bcecf045e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9466 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 diff --git a/platforms/sdl/sdl2/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/platforms/sdl/sdl2/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp deleted file mode 100644 index 5018e6b2da6d5019968fe84e0576502f61901e93..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7624 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 diff --git a/platforms/sdl/sdl2/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/platforms/sdl/sdl2/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp deleted file mode 100644 index ded8d97c6205d5546bc8b24d7be8560e581545a6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12522 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 Ak^lez diff --git a/platforms/windows/MinecraftClient.Win32.rc b/platforms/windows/MinecraftClient.Win32.rc index 0837b53be..deb22a723 100644 --- a/platforms/windows/MinecraftClient.Win32.rc +++ b/platforms/windows/MinecraftClient.Win32.rc @@ -52,9 +52,9 @@ END // Icon with lowest ID value placed first to ensure application icon // remains consistent on all systems. #ifdef __GNUC__ -IDI_ICON ICON "assets/icon.ico" +IDI_ICON ICON "assets/app/icons/icon.ico" #else -IDI_ICON ICON "..\\..\\game\\assets\\icon.ico" +IDI_ICON ICON "..\\..\\game\\assets\\app\\icons\\icon.ico" #endif #endif // English (United States) resources diff --git a/platforms/windows/projects/MinecraftClient.Win32/MinecraftClient.Win32.vcxproj b/platforms/windows/projects/MinecraftClient.Win32/MinecraftClient.Win32.vcxproj index 7f723cc0e..8f8be420a 100644 --- a/platforms/windows/projects/MinecraftClient.Win32/MinecraftClient.Win32.vcxproj +++ b/platforms/windows/projects/MinecraftClient.Win32/MinecraftClient.Win32.vcxproj @@ -80,7 +80,7 @@ - + diff --git a/platforms/windows/projects/MinecraftClient.Win32/MinecraftClient.Win32.vcxproj.filters b/platforms/windows/projects/MinecraftClient.Win32/MinecraftClient.Win32.vcxproj.filters index e418f0bf7..e55463c4c 100644 --- a/platforms/windows/projects/MinecraftClient.Win32/MinecraftClient.Win32.vcxproj.filters +++ b/platforms/windows/projects/MinecraftClient.Win32/MinecraftClient.Win32.vcxproj.filters @@ -42,7 +42,7 @@ - + Resource Files diff --git a/platforms/xdk360/ReMinecraftPE.xlast b/platforms/xdk360/ReMinecraftPE.xlast index f9d9020a9effb15aaf90fa461889e4aae1c9c9f9..409edcc888385bf2544495fb3ab2f497ed9db5e9 100644 GIT binary patch delta 85 zcmaD^9ZfAT%sZG_Zqo@f`y F2mmBa7hwPZ delta 22 ecmZ41!1$ Date: Fri, 20 Feb 2026 00:59:54 -0500 Subject: [PATCH 28/63] Restored Unused Camera Flash Texture (#494) --- source/client/renderer/ItemInHandRenderer.cpp | 4 +- .../renderer/entity/TripodCameraRenderer.cpp | 40 ++++++++++++++++++- source/world/entity/Animal.cpp | 4 +- source/world/entity/Animal.hpp | 2 +- 4 files changed, 44 insertions(+), 6 deletions(-) diff --git a/source/client/renderer/ItemInHandRenderer.cpp b/source/client/renderer/ItemInHandRenderer.cpp index 81edccd3e..03c1ebc24 100644 --- a/source/client/renderer/ItemInHandRenderer.cpp +++ b/source/client/renderer/ItemInHandRenderer.cpp @@ -234,7 +234,7 @@ void ItemInHandRenderer::renderItem(const Entity& entity, const ItemStack& item, t.vertexUV(1.0f, 0.0f, -C_ONE_PIXEL, texU_1, texV_2); t.vertexUV(0.0f, 0.0f, -C_ONE_PIXEL, texU_2, texV_2); - SHADE_IF_NEEDED(0.8f); + SHADE_IF_NEEDED(1.0f); t.normal(Vec3::NEG_UNIT_X); for (int i = 0; i < 16; i++) { @@ -251,7 +251,7 @@ void ItemInHandRenderer::renderItem(const Entity& entity, const ItemStack& item, t.vertexUV((i + 1) * C_ONE_PIXEL, 0.0f, -C_ONE_PIXEL, Mth::Lerp(texU_2, texU_1, i * C_ONE_PIXEL) - C_RATIO_2, texV_2); } - SHADE_IF_NEEDED(0.6f); + SHADE_IF_NEEDED(1.0f); for (int i = 0; i < 16; i++) { t.vertexUV(0.0f, (i + 1) * C_ONE_PIXEL, 0.0f, texU_2, Mth::Lerp(texV_2, texV_1, i * C_ONE_PIXEL)); diff --git a/source/client/renderer/entity/TripodCameraRenderer.cpp b/source/client/renderer/entity/TripodCameraRenderer.cpp index 15fc4ea67..5855de00f 100644 --- a/source/client/renderer/entity/TripodCameraRenderer.cpp +++ b/source/client/renderer/entity/TripodCameraRenderer.cpp @@ -86,8 +86,46 @@ void TripodCameraRenderer::render(const Entity& entity, const Vec3& pos, float r float time = getFlashTime(camera, a); if (time >= 0.0f) { + // pulse effect currentShaderColor = Color::WHITE; - currentShaderDarkColor = Color(1.0f, 1.0f, 1.0f, sinf(float(M_PI) * 2.0f * time)); + currentShaderDarkColor = Color(1.0f, 1.0f, 1.0f, sinf(float(M_PI) * 1.0f * time)); + + // restore camera flash texture + MatrixStack::Ref flashMatrix = MatrixStack::World.push(); + + const float radToDeg = 1.0f / MTH_DEG_TO_RAD; + + float yaw = m_modelPart.m_rot.y; + float pitch = m_modelPart.m_rot.x; + float forwardX = -sinf(yaw) * cosf(pitch); + float forwardY = sinf(pitch); + float forwardZ = -cosf(yaw) * cosf(pitch); + + flashMatrix->translate(Vec3(forwardX * 0.4f, forwardY * 0.4f + 0.7f, forwardZ * 0.4f)); + flashMatrix->rotate(yaw * radToDeg, Vec3::UNIT_Y); + flashMatrix->rotate(pitch * radToDeg, Vec3::UNIT_X); + flashMatrix->rotate(90.0f, Vec3::UNIT_X); + + t.begin(8); + t.normal(Vec3::UNIT_Y); + + static constexpr float U_RATIO = 1.0f / 64.0f; + static constexpr float V_RATIO = 1.0f / 32.0f; + + // calculate U and V coordinates + float texU_l = 48.0f * U_RATIO; + float texU_r = (48.0f + 15.99f) * U_RATIO; + float texV_u = 0.0f * V_RATIO; + float texV_d = (0.0f + 15.99f) * V_RATIO; + float x1 = -0.5f, x2 = 0.5f; + float z1 = -0.5f, z2 = 0.5f; + float y = 0.0f; + + t.vertexUV(x1, y, z2, texU_l, texV_u); + t.vertexUV(x1, y, z1, texU_r, texV_u); + t.vertexUV(x2, y, z1, texU_r, texV_d); + t.vertexUV(x2, y, z2, texU_l, texV_d); + t.draw(m_shaderMaterials.entity_alphatest); } if (&entity == pHREntity) diff --git a/source/world/entity/Animal.cpp b/source/world/entity/Animal.cpp index 1c30c0614..8719bf4c2 100644 --- a/source/world/entity/Animal.cpp +++ b/source/world/entity/Animal.cpp @@ -76,7 +76,7 @@ float Animal::getWalkTargetValue(const TilePos& pos) const return m_pLevel->getBrightness(pos) - 0.5f; } -bool Animal::hurt(Entity* pCulprit, int damage) +/*bool Animal::hurt(Entity* pCulprit, int damage) { // Run around erratically for three seconds. field_BA4 = 60; @@ -85,7 +85,7 @@ bool Animal::hurt(Entity* pCulprit, int damage) field_BB4 = 0; return Mob::hurt(pCulprit, damage); -} +}*/ bool Animal::removeWhenFarAway() const { diff --git a/source/world/entity/Animal.hpp b/source/world/entity/Animal.hpp index 7a2830e6a..7d1873b89 100644 --- a/source/world/entity/Animal.hpp +++ b/source/world/entity/Animal.hpp @@ -21,7 +21,7 @@ class Animal : public PathfinderMob Entity* findAttackTarget() override; int getAmbientSoundInterval() const override; float getWalkTargetValue(const TilePos& pos) const override; - bool hurt(Entity* pCulprit, int damage) override; + //bool hurt(Entity* pCulprit, int damage) override; bool removeWhenFarAway() const override; int getAge() const; From abd83ce4cee090c43bf9b038400441cbf71744d3 Mon Sep 17 00:00:00 2001 From: Un1q32 Date: Fri, 20 Feb 2026 19:33:27 -0500 Subject: [PATCH 29/63] Fix default UI theme, also fix Minecraft::isTouchscreen always being false (#489) --- source/client/options/Options.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/client/options/Options.cpp b/source/client/options/Options.cpp index f99e36c2f..9f226131f 100644 --- a/source/client/options/Options.cpp +++ b/source/client/options/Options.cpp @@ -59,7 +59,7 @@ static UITheme GetDefaultUiTheme(Minecraft* mc) #if MC_PLATFORM_XBOX360 return UI_CONSOLE; #else - return (mc->useTouchscreen() || mc->isTouchscreen()) ? UI_POCKET : UI_JAVA; + return mc->platform()->isTouchscreen() ? UI_POCKET : UI_JAVA; #endif } From 3482668a43675ea8ef135fa6b1ad3d76e9fe18e6 Mon Sep 17 00:00:00 2001 From: Un1q32 Date: Fri, 20 Feb 2026 19:34:47 -0500 Subject: [PATCH 30/63] Android build update (#487) --- .github/workflows/artifacts.yml | 23 +- .github/workflows/build.yml | 49 ++- .github/workflows/publish.yml | 2 +- build-wasm.sh | 4 +- platforms/sdl/sdl2/android/app/build.gradle | 20 +- platforms/sdl/sdl2/android/build.gradle | 6 +- platforms/sdl/sdl2/android/gradle.properties | 1 - .../android/gradle/wrapper/gradle-wrapper.jar | Bin 54213 -> 43462 bytes .../gradle/wrapper/gradle-wrapper.properties | 7 +- platforms/sdl/sdl2/android/gradlew | 307 +++++++++++------- platforms/sdl/sdl2/android/gradlew.bat | 66 ++-- thirdparty/SDL2/src | 2 +- 12 files changed, 319 insertions(+), 168 deletions(-) diff --git a/.github/workflows/artifacts.yml b/.github/workflows/artifacts.yml index fedf4758e..4e5ae906f 100644 --- a/.github/workflows/artifacts.yml +++ b/.github/workflows/artifacts.yml @@ -50,6 +50,7 @@ jobs: cmake .. -GNinja \ -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_INTERPROCEDURAL_OPTIMIZATION=ON \ + -DCMAKE_C_COMPILER_LAUNCHER=ccache \ -DCMAKE_CXX_COMPILER_LAUNCHER=ccache \ ${{ matrix.flags }} cmake --build . @@ -109,6 +110,7 @@ jobs: # cmake .. -GNinja \ # -DCMAKE_BUILD_TYPE=Release \ # -DCMAKE_INTERPROCEDURAL_OPTIMIZATION=ON \ + # -DCMAKE_C_COMPILER_LAUNCHER=ccache \ # -DCMAKE_CXX_COMPILER_LAUNCHER=ccache \ # -DCMAKE_C_FLAGS='-m32' \ # -DCMAKE_CXX_FLAGS='-m32' \ @@ -215,6 +217,16 @@ jobs: name: Android (${{ matrix.name }}) runs-on: ubuntu-24.04 steps: + - name: Get Time + id: get-time + run: echo "time=$(date -u '+%Y-%m-%d-%H:%M:%S')" >> $GITHUB_OUTPUT + - uses: actions/cache@v5 + with: + path: ~/ccache.tar.xz + key: android-artifact-${{ steps.get-time.outputs.time }} + restore-keys: android-artifact- + - name: Unpack cache + run: cd ~ && [ -f ccache.tar.xz ] && tar xf ccache.tar.xz || true - name: Checkout Repository uses: actions/checkout@v6 with: @@ -225,15 +237,20 @@ jobs: java-version: '17' distribution: 'temurin' cache: gradle + - name: Install Dependencies + run: sudo apt-get install --no-install-recommends -y ccache - name: Build run: | cd ${{ matrix.directory }} ./gradlew assembleRelease + env: + USE_CCACHE: 1 + CCACHE_COMPILERCHECK: "%compiler% -v" - uses: upup-company/apksigner-android@v1 id: sign_apk env: ANDROID_KEYSTORE: ${{ secrets.ANDROID_KEYSTORE }} - BUILD_TOOLS_VERSION: 33.0.1 + BUILD_TOOLS_VERSION: 36.1.0 if: ${{ env.ANDROID_KEYSTORE }} with: releaseDirectory: ${{ matrix.directory }}/app/build/outputs/apk/release @@ -254,6 +271,8 @@ jobs: with: name: Android (${{ matrix.name }}) path: ReMCPE.apk + - name: Pack cache + run: cd ~ && tar cJf ccache.tar.xz .cache/ccache mingw: strategy: @@ -300,6 +319,7 @@ jobs: cmake .. -GNinja \ -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_INTERPROCEDURAL_OPTIMIZATION=ON \ + -DCMAKE_C_COMPILER_LAUNCHER=ccache \ -DCMAKE_CXX_COMPILER_LAUNCHER=ccache \ ${{ matrix.flags }} cmake --build . @@ -341,6 +361,7 @@ jobs: /opt/devkitpro/portlibs/switch/bin/aarch64-none-elf-cmake .. -GNinja \ -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_INTERPROCEDURAL_OPTIMIZATION=ON \ + -DCMAKE_C_COMPILER_LAUNCHER=ccache \ -DCMAKE_CXX_COMPILER_LAUNCHER=ccache /opt/devkitpro/portlibs/switch/bin/aarch64-none-elf-cmake --build . mv platforms/sdl/sdl2/reminecraftpe.nro . diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index cbbd0ab1a..f357b87d4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -46,7 +46,11 @@ jobs: run: | mkdir build cd build - cmake -GNinja -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DWERROR=ON ${{ matrix.flags }} .. + cmake .. -GNinja \ + -DCMAKE_C_COMPILER_LAUNCHER=ccache \ + -DCMAKE_CXX_COMPILER_LAUNCHER=ccache \ + -DWERROR=ON \ + ${{ matrix.flags }} cmake --build . - name: Pack cache run: cd ~ && tar cJf ccache.tar.xz .cache/ccache @@ -61,8 +65,8 @@ jobs: - uses: actions/cache@v5 with: path: ~/ccache.tar.xz - key: linux-${{ matrix.name }}-${{ steps.get-time.outputs.time }} - restore-keys: linux-${{ matrix.name }}- + key: wasm-${{ steps.get-time.outputs.time }} + restore-keys: wasm- - name: Unpack cache run: cd ~ && [ -f ccache.tar.xz ] && tar xf ccache.tar.xz || true - name: Checkout Repository @@ -74,7 +78,11 @@ jobs: sudo apt-get update sudo apt-get install --no-install-recommends -y cmake ninja-build ccache - name: Build - run: ./build-wasm.sh -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DWERROR=ON + run: | + ./build-wasm.sh \ + -DCMAKE_C_COMPILER_LAUNCHER=ccache \ + -DCMAKE_CXX_COMPILER_LAUNCHER=ccache \ + -DWERROR=ON - name: Pack cache run: cd ~ && tar cJf ccache.tar.xz .cache/ccache @@ -165,11 +173,21 @@ jobs: include: - name: SDL 2 directory: platforms/sdl/sdl2/android - - name: Native - directory: platforms/android/project + # - name: Native + # directory: platforms/android/project name: Android (${{ matrix.name }}) runs-on: ubuntu-24.04 steps: + - name: Get Time + id: get-time + run: echo "time=$(date -u '+%Y-%m-%d-%H:%M:%S')" >> $GITHUB_OUTPUT + - uses: actions/cache@v5 + with: + path: ~/ccache.tar.xz + key: android-${{ steps.get-time.outputs.time }} + restore-keys: android- + - name: Unpack cache + run: cd ~ && [ -f ccache.tar.xz ] && tar xf ccache.tar.xz || true - name: Checkout Repository uses: actions/checkout@v6 with: @@ -180,10 +198,17 @@ jobs: java-version: '17' distribution: 'temurin' cache: gradle + - name: Install Dependencies + run: sudo apt-get install --no-install-recommends -y ccache - name: Build run: | cd ${{ matrix.directory }} ./gradlew assembleDebug + env: + USE_CCACHE: 1 + CCACHE_COMPILERCHECK: "%compiler% -v" + - name: Pack cache + run: cd ~ && tar cJf ccache.tar.xz .cache/ccache mingw: strategy: @@ -230,7 +255,12 @@ jobs: run: | mkdir build cd build - cmake -GNinja -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DWERROR=ON -DCMAKE_TOOLCHAIN_FILE=../cmake/mingw-w64-toolchain.cmake ${{ matrix.flags }} .. + cmake .. -GNinja \ + -DCMAKE_C_COMPILER_LAUNCHER=ccache \ + -DCMAKE_CXX_COMPILER_LAUNCHER=ccache \ + -DWERROR=ON \ + -DCMAKE_TOOLCHAIN_FILE=../cmake/mingw-w64-toolchain.cmake \ + ${{ matrix.flags }} cmake --build . - name: Pack cache run: cd ~ && tar cJf ccache.tar.xz .cache/ccache @@ -261,7 +291,10 @@ jobs: run: | mkdir build cd build - /opt/devkitpro/portlibs/switch/bin/aarch64-none-elf-cmake -GNinja -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DWERROR=ON .. + /opt/devkitpro/portlibs/switch/bin/aarch64-none-elf-cmake .. -GNinja \ + -DCMAKE_C_COMPILER_LAUNCHER=ccache \ + -DCMAKE_CXX_COMPILER_LAUNCHER=ccache \ + -DWERROR=ON /opt/devkitpro/portlibs/switch/bin/aarch64-none-elf-cmake --build . - name: Pack cache run: cd ~ && tar cJf ccache.tar.xz .cache/ccache diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index f0e66f129..433e95838 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -190,7 +190,7 @@ jobs: keyStorePassword: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }} alias: remcpe env: - BUILD_TOOLS_VERSION: 33.0.1 + BUILD_TOOLS_VERSION: 36.1.0 - name: Rename APK run: mv ${{ steps.sign_apk.outputs.signedReleaseFile }} ReMCPE.apk - uses: alexellis/upload-assets@0.4.1 diff --git a/build-wasm.sh b/build-wasm.sh index 638906693..355bc8f56 100755 --- a/build-wasm.sh +++ b/build-wasm.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/bin/sh set -e @@ -19,7 +19,7 @@ git pull # Use Emscripten SDK export EMSDK_QUIET=1 -source ./emsdk_env.sh +. ./emsdk_env.sh # Create Output Directory cd ../ diff --git a/platforms/sdl/sdl2/android/app/build.gradle b/platforms/sdl/sdl2/android/app/build.gradle index 7d8bb604c..e41118bbf 100644 --- a/platforms/sdl/sdl2/android/app/build.gradle +++ b/platforms/sdl/sdl2/android/app/build.gradle @@ -1,25 +1,31 @@ apply plugin: 'com.android.application' +def useCcache = System.getenv("USE_CCACHE") == "1" + android { - compileSdk 34 + compileSdk 36 defaultConfig { applicationId 'io.github.reminecraftpe' minSdkVersion 21 //noinspection EditedTargetSdkVersion - targetSdkVersion 34 + targetSdkVersion 36 versionCode 1 versionName '1.0' externalNativeBuild { cmake { - arguments '-DANDROID_PLATFORM=android-21', '-DANDROID_STL=c++_static' + arguments '-DANDROID_PLATFORM=android-21', '-DANDROID_STL=c++_static', '-DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON' abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64' + if (useCcache) { + arguments += '-DCMAKE_C_COMPILER_LAUNCHER=ccache' + arguments += '-DCMAKE_CXX_COMPILER_LAUNCHER=ccache' + } } } } buildTypes { release { minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } applicationVariants.configureEach { variant -> @@ -56,11 +62,11 @@ android { externalNativeBuild { cmake { path '../../../../../CMakeLists.txt' - version '3.22.1' + version = '3.22.1' } } lint { - abortOnError false + abortOnError = false } - namespace 'io.github.reminecraftpe' + namespace = 'io.github.reminecraftpe' } diff --git a/platforms/sdl/sdl2/android/build.gradle b/platforms/sdl/sdl2/android/build.gradle index ab974e859..534feb40a 100644 --- a/platforms/sdl/sdl2/android/build.gradle +++ b/platforms/sdl/sdl2/android/build.gradle @@ -6,7 +6,7 @@ buildscript { google() } dependencies { - classpath 'com.android.tools.build:gradle:8.1.2' + classpath 'com.android.tools.build:gradle:8.13.2' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files @@ -20,6 +20,6 @@ allprojects { } } -task clean(type: Delete) { - delete rootProject.buildDir +tasks.register('clean', Delete) { + delete rootProject.layout.buildDirectory } diff --git a/platforms/sdl/sdl2/android/gradle.properties b/platforms/sdl/sdl2/android/gradle.properties index 8450cacc3..833814d4b 100644 --- a/platforms/sdl/sdl2/android/gradle.properties +++ b/platforms/sdl/sdl2/android/gradle.properties @@ -9,7 +9,6 @@ # Specifies the JVM arguments used for the daemon process. # The setting is particularly useful for tweaking memory settings. -android.defaults.buildfeatures.buildconfig=true android.nonFinalResIds=false android.nonTransitiveRClass=false org.gradle.jvmargs=-Xmx1536m diff --git a/platforms/sdl/sdl2/android/gradle/wrapper/gradle-wrapper.jar b/platforms/sdl/sdl2/android/gradle/wrapper/gradle-wrapper.jar index 2b338a935b508eb6286ed4b84a91176ee5fb93c5..d64cd4917707c1f8861d8cb53dd15194d4248596 100644 GIT binary patch literal 43462 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 -zxB;99VDBQTCSHp}M0GmUUgNtdpg -z64fY!>-l-!jRdY)CYkLcn>Hea>66klc$Pdc))ErlqinK@zJ1Z=90BMo0)$jS_7aP1 -z%vF&w-GBPjlV*PJ`tCs|W62s89uJIll -z;+VG0SyTJ=>2PlEFe4yT;dmap=s9BXBfdl~s5Y -zX8l7dFoo^{%VDrbg#f*0I8JFA6hlYU5=-1B)wKAElSmAcSJ2J7Zg(($z?xXM;Y5I_ -z#BOjCi)=_qAakH~>i~geu8D&r2E9w*7=a?7XUlk|&=ok}ql9XU4X&B$@v@Fm4joI} -zcAHe!$TX>>nx?a~dALkZfpD_E2li=VML&rU1r+CyTxgCrbBXMn97i)}W~ohr6%isZ -zPbQva5?E%QW#MR6))BczsJeW{5zs_PkuM^Oh)FCVkX+BCQEfB?7HFA#JsAA9}flbu&*!{ilTuy8pvB)vid|C31nek -z9{7sDDt&uTSC5}x&Hep$OyioWtWhW -zlXYw?R}~6HSu#!2Nm9R=`ANW`o(jT@EcNv1AhqO`xGa26s!&jKR~2?6iRFnU!JeaP -zN|vfs7uu_Zz(HWznrGVbkV2Lc9=n;86n^7+cw`fPZRhZG^r7~rb;ovmCO0Lsa#Mjv -zOHCya1@r+GOEs!{3-)WboVJGNgVcJZEectpRHVAFiRJy-50!iB+yk6K09(O#?gI$W -zBBqzV4`K{0C5!t#UJ}R*1G~x$B^5bzqyM8gZT~Bmr8$1cJE>91(t4<)y!(s*Epz$s -z5V)HgSQP;xS$_KMn%p$;IARno$cSxhzse0I6BwMHJ!+O}RXG**>lQOGYBzpJp-8;5 -z)(Y>rohQWj^4aqR)y?f4U`eH3xbz}G-Lg&|PLyi8ghIl?|H!dzsM-ygz`?l!2DgUm -z6v>&MYZO@rGrgW%Q_3%H=*!DWi$8xh^&cWwR*7dGg9y;5uFb0}l#(YQ?Hu}c;sBk_ -zp8fhsb2NjfnuejDp`-q-Oj~YadbQ+{WX`lyw|jdn0yHl1%wF(rE4aIe$IoE|Xx8MO -z-OT~3mgFNL?c6eT3poF?OoGU5YgHuBwzjqPb#)}vgad6j&;`Ce@D09woTEC<-Pp+C -znG-o(`{Bb9<V|f{>g%S8{9*>{lJ_WRnDEddAYk4I4i+ -zlS-1ZaZhcVP{O(M={x23;x(G~*17_py+$EG)69SU_wK%5e9)9U_&avxq8to!;rJHp -zS3O0ti-I#fGSlE0)$FNIlog)L^66k&_bH?Q|9#RFc;hU}dJk}S-VdKb2+$A#I<>HH -z?sPIMD7t~a!x-k8t!%I{)sV -zy8&!*d0)8lX7lc&J_58U^v2cLt_Jn1^{!48Wat=ISF+Hl8Z6ZnjPvT@Wk`D^QH;l> -zOq1%IM9#?R0CueZ$u00P)^eeo8J08J2}V>NrWm-o;uzk5ZM -ze1@lxhwHvSWBP2#13^?1iv-* -z{kgn&8Q$2c<-^@~^Y@9}AT8Wi6#Rc2gQDPho+ONLiJ`eo)`}KxF -zjqA^MGA-PLB8Cx@3}feggs8U63B2dwPK0FU|YW-+>M{^=9i`i=(WHF -zPy5C@hqxri`}?kQ!2UWXj~j6QFOO$0Q8~PMuam{k&w3}lGj*CYso#0h#zUVoXdX8< -koa~Y?(B0=0aTxZzFThdau02-~hmYOY&ildX&#`$+0ccHYjsO4v - -diff --git a/platforms/sdl/sdl2/android/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/platforms/sdl/sdl2/android/app/src/main/res/mipmap-mdpi/ic_launcher.webp -deleted file mode 100644 -index 9db1cf006e380fa04d7c174d9f2fd224a3c76844..0000000000000000000000000000000000000000 -GIT binary patch -literal 0 -HcmV?d00001 - -literal 2172 -zcmV-?2!r=hNk&F=2mk`6^*ehR&b=1LzHQq!ZQI(mm}~8t -zp3AmvucH;#p*1gIT?a40JoC0M!1g|~?Y+nB7_An+wQZ}mZQE8%rOnBr}n}m(QehxJuZNM`1D=-N%8D^P&aiR7c}twm%d%u{r_dA`z^uh -z5$hWOuzH^LzW{*M0jwVYfECsNfYmds{|f-Do?-o88Aq%So*h(F6x5~8Ububo6Q3RRb5CNSPkb?g|8KqZ)9Nz^-rr|+8Wy9C -zELRR^{Br<68eIW^bQE0y&=5=jfQ|_O&{3oqYO3wHox}G}+;8ITWtS1O0Px{bz+cxb -z$vFmLbRaDzKnDO!Xb1o%KnK7C=m01TbO68vbf;=dCrM*Rj#>PDV|N4+{`arTA~gx0 -z&65b^B?8O3Fel4p0t8S1fB*`B79kdZpa1{?6aXNALdT$!d3OeK3QLy5>@Xdm4g%0f -z76E`p0ssJu0LThj0H6^70E+-XhdvqL&XUdQ-J1V9=6@e_AZU>V06{|lNR~Gs= -zuinqej{^V-3FsI!=97T!oh6$)UCc0{0fJxv04Kl8wKwGt-^jcU)Evc_20#j-1y)D7 -z>`wmhR`xCm0Ho1S!$2dL7J$Xo5~yV?5I{j>0l<7R$G_u>%lO+*qLR+uvFH(0=5^M; -zTD?QK;z9oO(b&I?=@j|0uDOx4!1lG` -zs=8VKbsT;f00Jxkk_0x|(2E;eIztD7h5!e5zUlIZ-XZ}75jLr?B$?&fc60cx!|6`} -zNYO1OG-`U_0KnDKmRXGJo;eMHiwUofKTwx -z@AA{{j6+MZI*|S!wGGrn%~CsTi9|AA)nXkuDP6WQ~KC!Zs2K -z00^vIJTc!bE -zL}SOeZSbecanTK;NLC9Z2yI0a9XbS{NMLkU3J5wwUl-GZ^Zf_ACjr|#OE#ymKgJ$L -z^aUzf)Pl*lGyHLulL8_g02+ZxhXi941{#%&L9$d@l60Y#kq)eJ=qa#!=xS-}N$%+R -z%ap?)OAR5y>Ox4tl8l0tM3K-HNN502D>AYa@QF^zjX2CYG9c(cgcP0CqKK75(Ln_g -zx*))cbWp4D$-t_{^;!Q+PJ%!nMOy?|5(NY;30;tcmJ}UQG}_u^c}Sy{OIxM^614~b -zbf_>H*Wpl3Dy)QINffbUs3ky4p@j%)MLN-zqGkU7zkwY~Hm9_5-k$&fmP7>rf=*zC -zYr=8MDLN~O0vdrtEr1pl1Z|`i7<41a!GWg|PV2O^8p@i?z=*y;g~$Q`(nuDplB?vn -zaaK{Vv}Dj(3c^a#1z{-?S|p0plGXiMP_@(l^odVL(7gBDAG?qCeV)?d*cn!gHe{=8 -zHB8{+CFFbh@vMp@?*geliU}Q^{=FU-jwW6&8K0eDMR5 -zkL>oUvMYA!5Gj>Xsipvcfd&8=OmgMO-|(+WEt6U7Q6Z=vHM*@xx1{Jg=*w7@T!dfi -z;jPhI=kWk_p`UwF;?sD(?)Q#nCjP0vofzqCW`0B+TI1gUz>u;8fB|8JA%Dl1C$rci -zZ57fEYLziM3TUudDK6Chvi1DxtaA)K`<^8LjDMqDyKk&6cQ?CDpS#c(m`(sN{`((w -z>}hHM*e=050P6<)Z~u#&hI*uv(Ju0z8;6dlzWIk7y|0@Wy!UfY5*0STbog&AJ~0Bz -yHa7qOrW@FD2Jis5nE@9-`_do!|KQD^8&ud5^_73TQh!)t!zcehE773Bmm>gPn);Lg - -diff --git a/platforms/sdl/sdl2/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/platforms/sdl/sdl2/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp -deleted file mode 100644 -index ab7b6d59c2fad6caa5ffb72437bb3db0eaabea41..0000000000000000000000000000000000000000 -GIT binary patch -literal 0 -HcmV?d00001 - -literal 2924 -zcmV-y3zPIxNk&Fw3jhFDMM6+kP&iCi3jhEwFTe{B717rA&yXbLC9E!jH?SArJ>u~m -z?lU*Awr$&JcTXGT%3_<98Ie^zw(W_`tggC)7#yu_dw0}Wdz?MnXKU4-+O}=m+iQ)@ -zT${D_&deT~eM0TlwliY&^iTGiCK`nA8z*n9v1{A5ZQHi(_8jT9tr?Q^&7;q2%wMJ= -zBEK&p!|BYnZH{e&&TU)A_KdM@JE?4RwryLT%^zSpwQaM1-)E%OXXClgH5s^V+sM9- -z^fG*__XnIJYfgZ|niKP4V|l$?DqjPV;KX*aW^}hm8ucZdO!0hP)iUW}qa-eGmHok6 -z90|g6cF!P}wufvnAC!xuv?QJY1#?uDf1@kxmtR%{GUkAYPc}JbTl*%PV;moLRT+WL -zrs?!9&7^qEjM8 -zrHLvfK?^K^#N#VOAh4iC9$z^`@j0krX-6j(oHn#nj43MVO;gDmno2-Spuoe#q1_~KYk?Bt{NI}YXR4_FO{S&~4wguy7&l#eqMmi*)UubJDZFA+N3J0vaeBFYa31@1YYo-T#ASboYasBJ -z%Uyq|!1M()n|mZVjY=FmQ|8AR8#PE~sSMc!tM -z?}GejDou&yT&QnW;LTHUG{O^UUx`+X7)PN{)k$R`8XH&B%#4 -z+cOpSj5p+SC<;(u0eNos-Oa>;h#A#5kM;Kx~tiDpeiB1e1k#dkKbk5z%TO+CrHq*=Fmfatr%H| -zbgIweFW;?&ac)vIG4aJ$D+Z%Wl|TKEX%nk_{O&NmK{KgnMSPoX(RNFWJyX+$;)|d8 -z09B^+nHxw|)qnq#{P111|NgV)dvBR{Use0}4~i>&?g>z1_=uIoFLTXa=vW)ev_d;8 -zC^&xwE6uR~%rcEy^sy^eS~UQ1`_AMJti%K7*aG<$uQ4$hKBiy1+6ChUhI8{Ifq&zH -zt`sUfe23LUU3tc5=AxvF&sahA|Nn!hu1WfXJeQtg3zUBLQ3OI%rNBiL!tWEW+-rkz -zu5G)u5_HP#3Pc`nzWL_omld45tlF42b$It6yOaN4fwyPIVMo{AMR8vAaNH7fO3Mdyk4%S -ze-c)uQB;~72v9+1*@LsxkyBr(Nl`rGps6$^lygfChK)TpPsOv$TkV?2<-c*>bE?W{ -z(#U%}@fuIOrp3@6gzNZM>Q -zl{H)PMoZP}k>iOvi`03I%%W_iqp8zKr4kaMs49y-kplaD-1WPC*d3MtsH#Kwi2EHS -zvYAjzS*~7NzKC2$Bezb=Ee*bPPXdp`^J+A5H)=kiutU;KMtrS9S^^^dpft~2Y^8INw%`>3J(j|K{ -zt^pLZH^p;GVz_iV9@A$Y!;wd`;n`?-HEQ1VTJBEAuiNv@Bfl*2PlLe33vM(*8jX<1 -z2@RdF(2@k!{fc%+?imWtP!dDs8l>;&8V#*k(U~e4b(u6)WuCc98wr3Cf!zm&eg3Xl -ztX-8cm@t^K3oUWLSd|Me1e&BsBcC}-yr<8+`UCIY;3MC5$8R(V7*7IPt-xk0sM!dq -z*TQ-|Nw+7-BS}${<~4a1Dcc?OaHL8CbsUhfr-?jW78$!egKrr^mqmC+P^--i`SL?c -z&PF2;02P-h+D#@pbq9B!Nn$gKB=BstF87Q*@6hoHE#ExyFCstB@b`@V{X=3eGD#2nXy!R92S4EN!`M028Ym_J8hfVk5@j1q -zjRjrKg647!8#0Ch71YpFDQI*lFmQtMD5R(b`&KA1!dy+_YO>gq#evMx$YoPYfL2oDi8o|R%o`D} -z)90%MK-&hBAM8r3HZ}nP&~m=vbTC3-|B9fLB!DRo1d-U36wev0io}%ILP}z=g><$` -z`q1T#0#61>kQ(yendUAXdL+e$P7ZSo%BPRB{b+iSqdy$UV8rk|HKnK|9OApQSklX- -ztV=KcPeqJ&q=Jc5Fi1>!qbXCkZ|vuuh7V)!KAxFgXAPV(os+NGFHZo}A^`U3 -zB8_G7h=_gqp#QjuN+P1lLatIM5rx{;LAp9L5{6bT>EzN+YS-}V^kq{oS=&tjbiVOq -zC$@2`yV>Sk``w#`rjJ*rSiQY<#7j3L34o3{*$d2GFE@DzMc58LClCPT -z2!QH2kF*@()9>!X-FQBD_)E`+9CG*3Eg#7_xUKLUwv$f5B|0cS`I`OmZLC)_DaSr-5R!HRAG - -diff --git a/platforms/sdl/sdl2/android/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/platforms/sdl/sdl2/android/app/src/main/res/mipmap-xhdpi/ic_launcher.webp -deleted file mode 100644 -index f17e96f9144989d2bfa5009d02c45c96434d6c6e..0000000000000000000000000000000000000000 -GIT binary patch -literal 0 -HcmV?d00001 - -literal 4010 -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 -z1rP)Pyah=V1QrSr6eFYN5Mzod -zN>EOmAxL|>f$Fn9((d4Aiy|BKxtV-Twigg6l&iH(SHk(9F~IOyQf^WTgNR7?xSB>f -zf*8O=UV&iF*#HsO?l}M)MwZ}ao5CQF0MZu{1_Fc;R{%0+AZbZ!Ud07)C|OFlQ8_nR -zCIndl0D@LtDH8c!Ae~dtH76LAUhEJq5tFk32Tm<+giA^!Kq|x(K)NFiAnTm%G^B*v -zP6SB=HhQ47_23hC|8K?H^XKzQOlpp8#z+TU$7F@Ip+u3<-QMm93HNdaL;$xf5P`J1 -z4|bCsEGDy6(bj`!s~2Xxvpz>c&5U+?+XF8^4a=}D3`77C6aes*7T{+KK*VhWG74Hd -z%-8S5H$N?&S3&;50}mCL^wQ)1*)_7(G=qjl>A(vxhSkUwC5kky&6zE_`Or;90%^Gt -z6)5jAPQ0~Q}4_z*^Sb499f&cor> -z`7i$T`^_x~5L0_?Z$=Bc=h+|o?%uGx1ONc-;soHA!?fT50MM2I_yG(NP(X9DG|SXE -zg5O}+=l>KCpax@Z$GB0`h(!?*z#!QJ4}ezy@L$;gOW+&YU9^#T5t0|ZDQ -Q4*%~w3_$f((_e}J03oe7G5`Po - -diff --git a/platforms/sdl/sdl2/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/platforms/sdl/sdl2/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp -deleted file mode 100644 -index dfe0aa8d77bee0bd8b8a1bb73beec9082a18443f..0000000000000000000000000000000000000000 -GIT binary patch -literal 0 -HcmV?d00001 - -literal 6554 -zcmV;L8D-{DNk&GJ82|uRMM6+kP&iD582|t;U%(d-2?uQ>Nz$ci{}g}32O^^X6981@ -z0C+129tYI1F^|N}O=nC}B}sG^4mM>0NM@os^zhi`-<8Ahr{y?8bS-d!@*6DNMz-xV -z2T{tmwX+T=pzYQk~bMv|oG9*qKLW}jE*AJvB9Hnwe5S>NE!9o%9k`XNLArMQ%! -zLI0BwB1Ad}f8;L%gb-qHo#;f!MaC$ABo^C=& -zXCME`QKP{rf)MqD{NBQI{|ULCix>qqQIMF0#)OcD(0Q?!q8?FPUrE9OTWG}j#g!WQ -zNM8ddy;%rkV#1Z*Ml^Ee{oEes%oU5y1X;MEAjCM~io{8UQAZ(!5aNTAzFhTOJ#UO5 -z)w_!nW;fr1$&(R7u+Ltha01wsBYNs=T33rwz -zrXfg@8@C~hGHgar`+}V>_}aE@S(|O!)@?sR2o0&bp6;GrZuGp|T?c*0-Cf3f%w@>k -zonCHrcW+#1NeBV9ljV -z$}Qg)Tn~T>YHI44^wcip819n%-$M)ACPR|-M?_Zj*lUkxj&0kvZQDL~wryRlZQHhu -zVRuG^14ojaX8RJk0h{~+f`KPSCRZtl&64Z9Qh`Y?{~mCwopcnHZXulodXJ( -zSIg7zyehIuqI*9lM!seq#|BChvQK!;hF`e#l#{InMApc*OaCm`v;D8GhDAQqc^MJ_ -zIEZj{SeET=m;O^g)<^ -z%mH0N%0L$aDZm5_$iiuX>~j`myVSBFy|Y%T=EV-fl7aExRsmV^mgW{k8Ex-z)r&}B2Fi!wB -zvrOds14j+bCc*MJpjCIL`F6x7l>hsb5+pVxza -z2dGv+NmQ3Ak^0CT>kj~W(V|qHmr|H1c$sYB94cK;Fj4C%hm0%YU)?}o4qT^zx^e;m -z0wAT)tkeajL@laS`CZ9nD^B^V8(XtO1}0)@K!$46VI2i>5-mX)lCX5B!e`1rPy=wH -z4(ccZI#CiyoQueehoeCEJ_jdPud}4oVeV0_PkLBKuhK|-xCDmLuLDNvL{LYn5QJE2 -z&Yo3}I-ojrfAS=Z=D~>l-nj5C0nmMN3Xr55;h1rlqk>Ubh*3jzYq?Y*TR59W=yseR -zonN^-X6yG_-Bmf2`d>zWFcBgV1EMrEgm8e@8l!U-REyz<_PKz^7aVqugh>7a03=V0 -zo-=(j<(xBhD^j-ujxvJL!x{lYSpds1Isro!MI3A}0fV?XwIKoq1TkFSJ$C7W4DL9E -zF>+Re?!odnZ>)7&e8xVb@1Chw?cCfZ_XHqWkkTdv3^p|4p6Xu$hK{u<^9dMO!|D`~ -z1PqAKeEzR-`2SOni~TU~?_9Uun$>p;nsEZEgW6^fLFHDYtX5P45~2_jWD!C*%?QWf -zPy(uml!=*kBVA_#X%_f)s%sBNHi%(ej!J#4Q5ai++|KFzaT%{;&Fl?Fn;t$;w42*(5vQqd;E+{$ZTy5r?4V5er2 -z6t$7&MBA?&-w@OSIz}=%VW88j`g+x~_bz+&xgsXndgFE51DxPt1+)qwV2%~6Q6s?& -z`krr3PmO^7X>~uNSfQU{kp9W1m!h1IV;4lDdWQrQDE9IdKP<7W=#VNLKx7H -zQ?{VkdrUr=sIZ9U9e}v*$?N(}zrJpUy3#r(6qBT=RQZ_1VG!`pfAVtEEBDgIQ5 -zq#ff=z!S-dNsK38nfZM>yf -zuspu%>m3TMJ?@s|a+L~G7n3Ae!1_o8!2~=cPad9O^C>b?{Khk|4m+vh33!Z-h)5)! -zAP+NJCzX|hx9mSpz@uzNENl-*l16NZ@Ddicu9_XG^|)989nB9gpx-@rU!ghd`t(7_ -z8=DZ;A`S>dk?~UeISXA`=$h44kis>v7{QSG2zW?5Sm5EdXD>5KvjS=iz!jrQ9A$F%_}idC -zFjXw{&BdjUqhA>vnPwg}HU!ORcvAWI`O=&yx@wd2Bost|oKRPvQ#!gl6BqdzUFPbt -zx9e-(NPsDVy>si#mn{4k9h)LM%|H;c%rOXr8Lj|0^5K@MP_6`+2(Ulh`cl_`ayq5L -zF2#u%U-~dc?q7PFYlCMXN-u>hG1kEN!`_fW;;AgkJdQH(BxmPm%+jRnKg#$hrwzfV -za05ZuPWtryQy;*;chbbU4r?wwM^^svP~4EcaFvl>7Jx!7^&sG<1z#@B%gxPiHYI=w -zre7&}ku221nqi6Yz@Cs677!2>a;V}Fo36-UeC~foA33e8Ag_>XJH8o{<%tN>)C+|e -zLXR8y?=&HVbI*`mht^XL89>31GZa9@++V1C)%{EHo4ZH7Ns`LMfVOgu1cGBZ -z0)CqJ_H~bM=SSq;*)QiuX3#L~OH&jnzHanZ{9+2QL(q&P7Wa&_>p1L{N5BK3K%4%Gc_E-8yqLeNSy=Jm}pc$98*FKamL8`p=Z8x -zFm;HN2N&6YI}a|MW}g28))1gMO#?uYIwz?hG)DtCxMZkpJh_nG*9t{^4=KvX*kFk8 -z4JMGqUi3>t0!$y|a`5f3v6ae>Zy-)taIBdCRG>PqB($go0CM?$z=?c^Jb;}-AMJzf -z1Qnk{6qY0IA&y6|iCmaS9u7_>4!>n>EoFW@!84W*aS6Ojpr{O>sp?)p0yy#=1`Gi5 -zO$eh$>PhvWuERSZy>-?Cr!=CN0F^bKAWP-^Nb-`w2?cTXU$ML*MNT||(NjeeSW^wh -z4`N!XZk;1f2z{540VOiTr%vt`nxMp;EsI>7o|&o;LExl<6g&6w)3R@*P7{q0n4q|I -zGDY9oVZ%$aphj};#Q#M?3h3sP$yyeBXV~Q6Bx7yUj87bKb3DaoK*^s!wp&7y1SrHt -z3OeJ;bkHzM1Hg$!70L3N*}F#*j35mwsZWwjGAE*l8z-Z4engBBPq6bNZ!bFUEIs3o -z5NH_VZ+yImPxf(W#Wx&$%+^kv=EsC6l2Io)a{?$s$c-`ymU@tv!4t&y1%G~NL~I8L -z&^R8v;Dv3NB%m8ohvE0PY{3o-l^N$0TA$g5hv=7QZp5R}C&)UKTci<{s0$;Tm_eQR -zg0|(b`o$6Ipdtj1dvwCmOABfb9J!#rB{%d4TONtczPQ%jtl69uAdwKer!Z#ykQbnzOW5nkr&E* -z-`UK0IUbg#voWvaau=|-SZlMSof3F4qh68em;4AUt%99j2D?6Mh=OHT1$RsbbOjvj -zd<%}uG@Q|3#Zp*4Js`li+=C+dWh49Ln6k#S)85uzv@*Ik1Vo}42mBvFTZY|Y&o}-iFRIgG#(9s69)hiz*e!CK!9OGem%v>2Wz=Ki!A)uPk!tqy<|m_mz%&W -zfTB)4lA6lS{hegFl}!hX{K$QG^G7yal$TC=ql^Y-0ty^gi4g_M2N5szM)$nCeuM8L!ndIV+O -zd4KjV8#qE?K!_5EZC;-8XDVx;U<$wpn8-@ZpJfJ77Km>#^{^pCh2(Db{BsLv0}OCA7$8;lqEv -z0c)I^-C?#GM_*D!MmwZBF;fn)IQmIy5gF&71h5v8kr-|!s7aW9h3vdetc{A4Sc8Zt -zb|#$+h)SwvOxgVNVwhaRrXOSOdeojd_Oty2JA26_iqN42OSwh)fmuNuDifWPK#PP} -z*FX}0?jI$NpIu6BCI_fulad-rM;wAmDrm=yzYKOaJ#)?V{#RZc^Za8OGu#O-K!GI7U`~-+-3DFwRsr#Zi -zHB_H@yJ(X>dnFD)X -zq7WRdmTAko-hoJB;^$mSRw8;WXV0+UOB;8t*>z_5(oCQ4XesHq%F6eLVdV=Y*H0d;MRv -z&giJ2z -zrDFW_%M+PW*Qtdy$ctsBBKk`86(&L!Iqg^A(kZ9`7YJsDN_uDQxR+ZSzr=^3XZk>< -zPza&urZZBY=$1`?&s;vti-(Oldi&uTCf#g8W-4ayKJG4%&IXOxe+Vi74W&?l1ECNXbdL8BplH+9 -z&k~`iQ1Fchl13*aUr+q=vT8U#9J;%fF@!J+V=0%xA+jM3aiX6wwt?)tZd#uxX&?ce -z95D_Wa7>egZhGfm69c0YpC%DDGlkayptWP2zH8>C%{OBFwbkP0fC92;+V{+s1JQA^ -zMbuV9-ChN10MX03N}A1=B?iX~M>PD?LG^Ca;iPa)zH~*BqhlNT)@85$^}(`FoEfF~5yk=)&s=vy$|3;t -zxUj$v`M+f+i(ek$JlaeJ3)2uTW@9Lqz#%gFk6TaLYUr#HfkaY20Zq!&Am$+H3b2Rz -z66p0P4JE~&zX5Wf+X;|&ll!a&0MMcVi*woj*N?~;f%|!D19b}4NQiEFA0Gm&qSFM| -zF`xeMkxqh(C-MM2H!RY_W71no6|?0CM}bv)aUBCHWChv6Ap{KS&2Tr>z%WWgc)L*O -zvqV*S6EG*{kn7O(@QC!*iNEuFx=8yi1EkNggF@2B2vCJx_>{=gF>t0u(oo&O>84IU -zw;P=2Fg-(sPfjPjo)G6lC(Pj3WRh+GWKfccs~MUbNEjEG3!h(Cz<&%T{o3$uhrlp; -z25%Q>av0GygA$fY)5IVWcbNb!?Vk|ZixmdKbo6xuEtIL^E`)wrM@CHjAcCpMpVVzi -zowu75AY3e_0cfH7)Y}KUym;_O;?renn)hQekn9SOgrbTY_YCKl$Pmj>HWe@-hA2*_ -z3R$M?@)1fR5td;s{#cD?EP12Q@OR=)vWiym02hxGKD$}atgZpQ< -zerR}%021&ZKmYQn&u<^i3E>OcELaHTixhcdP1u8bS&cJtNxh;_j53GGTQoYuE=$Gu -zQ~V$%CZI%#N`y6%7h}+XR%2H`VB%u6>oUCcSuwwWhXjz&R0JTp4?jTyz%RV{gGYYw -z&C{PQ{W`}tx0wb|73YcAL@En$;sQ!!gYy7CJ^JMICz)(K1Bku -z#M7_9{$sq!MRaYEU6`L1^?z&6KhI=>1b}>U1PulGgVkWMKmTh7Hl66cOd@n -zvL#UsmK7&CqGSpAfW?QkC#8T0%!SZpuprWfKF&m>z$gWcQ#_GR^FgM8L{)o$nAPTp -zaTEjG>y25TbaH9H5E93VJ_HriDHW%(~MLow|@cltM9~$r21Q4CnO$cJxbdXx&l@mQy -zWl0$9o)|;5xc3{fw1dp=B(NkHm_Um)3~`>ANBm(l>5_jig5eZsz<~|aQIWdSb_$(j -z!D=m*c%d2}i2o@|gx8n;8;zns+*{QC@8hZMRhGpF17cvRwj$fXfCbI~IOAt>{VXty -zRDr;&X&fmQ%Xd-V94hQB8%6t*^<2qb)K{y329@mm5c|w9Ltp*PtGBPlXM3D& -z$5m_;HUa~!*bp?C5&)epfdQp+hS4*BR}7wriy~0?7(qvoZAV=Hf6ti;G|xzumX7~t -z0j*X`6=b!xuhOPLqJ>K?L{J67gYo9|IB9;&+f+5@#82%k;5t#$K -z2m;;4K|5$Q#0`RVC`QoLv9)8ghW)w9*krLd1gKk_%5)A_+W;-?CjdTyWFd-mw2C_V -zQXtcMe06`>qT!qh4ZitJ6aS4G4 -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 -zd_{j8-ao+Nf4TFsgm%Cdq`J!4UvtY#&MA+tA9 -zy>!~UF^8YZ^-C=OPi!CvolI^ZUm{;+e2i>qg}JC1 -zEH~EtGF1rxp_{``LWb4j&9BEQz?NirDHjg`_HG#We!~3iH9p3{otc~*`O;9%jgpA3N9DR|=$!vsoa7*UrIsR?t7oi4hLqao8d?<&XWkYZF -z1d|ikET?}M=Z^$zLGndzeV(0@Y>ucRV1AC{-wrVN6#H}dsg?DzQRUDv}2t*8|ku;G|hRpzdX0n=F_tQ%OD95?? -zN9LCS!%W`IGoK^t2K1Emkhgx3^%yY3gl3-kEO`&mTV`wH-XEx{fZ>IVCvM8*5TM5# -zKReD|UIYAunC#=$-J}epR5-5J0v2>sTFs*3tfE4aLCu@L&SDKPv@EyAe2tWWlvf{J -zfBFg4G~j*JSZ0jU=%QT6pBp@SV1Y-p45hwPbVI{lH2q -zZk-tKymj1pE&z-oA4+s`ez4^4SPqd8JbO#__LGzlJoj-Hdo!qI=OmSpR)j|8J3Rl} -zJbX3dVtlO1QJzlDTk@I6%YQm7cX{f1-t)nwENROAk=(qFshkGG*@58T?onmKaZLK7 -zJCq1`j40>1k3%rC&Jq@YjYo|GfpRd?4g%^$P&-EZv1(QS1`Hqv@5$bYsGC`r^Y);V -z0Ou4DkWOmQ(aPX;BR4i1c|U67NJjltRbxF20J{fQ)(sd!#!I~Nt2}xEL^xc@fn>cG -zq%8)GSDxOpw7FQY&Lt~1t= -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 - -diff --git a/platforms/sdl/sdl2/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/platforms/sdl/sdl2/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp -deleted file mode 100644 -index 1e79b1b04bc2e2bf3a1c0a09f0daaf5bcecf045e..0000000000000000000000000000000000000000 -GIT binary patch -literal 0 -HcmV?d00001 - -literal 9466 -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 -zVD;`_L_+m?mrzc#%Q6FsfQ3s_Rs${ -zQ7U%IhE$L$LWX4A2FT-~JIF?5PZw7rNUN2kB9mGgClf^9t}Dk+sSxQuQscKb5x7I!Q6@^K -zJ4d*3zT8=SShhtT^IIQLBB-83{c@r-7(|_-C}dDuPU}9dCJ3|m+Lc03$^N{{2owZ3 -zgA8QiR!Fe6cqrj&0Blq$QZA)_^4sJ&k^YbrAsyEBZE}e+X&$0IKAq`wmqQHD?xWos -zY)oE{3{U0oxv${oMsR6K8J!Lq=!+{H9!n#x+x_4hB?===amb@ONHDpVPWX_{xu7dHXMy -z%7C2f+;6=tk0X?zGm@zX&f^+BmE!43_S>&aUYSRI{NXX1K2=YrGa6jC^|3b$s!mrF -zPL-!ZDW(vkuiLw5punjDHj)yCB2Y`ceY$-y8iT*&Mnj5;M)5sBacf_?PN -zQ(9od%4b9GcA9Ec_SS&742D~ZW$QI(>WWAo7;NbrQ*VNIIish!1cF+!`|VoU^4X{T -zOjqVx#4kQ)xo_{^c2QlngGLz$w_3sWTd7rpf!Bl;!K!z@gl{=wccHJ7t#_<0d&R=VB=D1+B2WHe9*Z6}2a_1AY20Ov*C0int -zZ?OYEg4b}|V|#^QJ$Z3U- -zF%)GWlo5W`7|507uoXQjm@t~Xyv?WohJ1^KtuvCL*8g@k8~=s!Or;x~exxOY1li$+ -zoZGIWamJj7RVr2t8kPmI1r4JLpI{^;kH!&BSX&S=)mBj3vZE{@ar`<$V4A($DLr+C -zC~byGgOy1Il2F;ntc>o$Tcnd=6^kfgGTm9)<0Q&(NdJRulmk1wmePY}L9J$cf;oQO -znB!btn8G^>fu`EpeIp~3 -z>u?_~IL@!JA*?Dj=({E^MCHIL*AK%b$UFVQ6oD#Lt0HsleYl`ph0VDj-13BbD+pC^ -zeJU%}+5Hq)^%C-Qr|FaB$3jxE)A(H#hzrh>kkh>irpnrsM5#EtpG5k>>Q`rqzHeDt -zl*~kbtSqpjtfx{8X=q;hGj4l;>WRTHRA-cMnZ8h(1WVN9&1qR|Vj!jLwAb0sICMVk;mP`ADsHni8mauMW5JtPD$! -z^46ZbvH8neJHH&Wc;EoXY&-9L@4V}k5AXk&nGP69xI*(_`WwTOO@<0s(%DDOcQ6du -z@`JWmYCa6(xMp7e`6oev3)H;iiRt^-gFha&Z4TWS?G4~w`p&1FXJW@Rn@|>gNttb0 -zz@X)iKPi%V2U3C^zqN%UL`m$7P9|^u`qPPBfrHa~n|x45O{{ -zrmsGo(g!>+QJi)H^jYmSUjNb$_pb!I&g1#dzA&0@nq&ToQ%J8)LBKHaNg;8*!*M*V -zSkhAF7(nmU9yrYdaG%i8k;l$EzCpS15K^9g_d~m&&(aPDZ~W%S9cV8eQsKEze|f}Z -zaOjivou!etIUG&&EHzBj-cA{Fc|h|WUbz2%CBssH20iok{B^W-c*()La?s|}N>PjE -zNjJ_j9|-!c&mFd9jyW)$G>u}OaQAuaO<^^m$k1aqLr$CYQNg3>{g*Evk%Ud?rQ -zHb2vKc?A*FG@H?T&HjFRkZ7cx;tVNMjbt7D*FzmWpOY$;2t?sENBxALEHHb3B6?#Q -z7U)5q;Me69^yORyu`d6HNLo6^1T^a+B0~y5$Sk*kA*<6HSl5(AFb|L+V?C0zX^lS9 -zO$cwwmxvvOh1sVyG!!V9QO4k4Bkdh>kSbaXP^;m~7_y48>^yn)tVS?|OeYD7qKs6^ -z9vnv#CYwWTr@C(Fp;N>XMW2nq*7SCgfbg28&R -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 -zM_$y@NU5hQQxp2NGOU7 -zYZMJ(nj}!j9B3ofFLFkLG_weaDi$JP-Qg|@E2Oj>%a^+z8@FDfW&6WdMhnyK;n33P -zwh{<&rY}k+$=u^E*R*le%G_u~@rT)1t%uQ|MskMQ>8RCJRuKvb2Y|Rm(8`lSqi7P0 -zlADW=dC(!?F(Mp6M{rH31CExiCCV8eO#(d@Buu&8WlfW1)=W&^PgZM%9JX?jOC^if -z(3&RVN^oU1T4D%5q$uTKhXyu7tYoNUp-ayJjD@C8gIS!q27D#SVx^}Q7n6!=NYYS{ -zAQx6kQ!SLy97PwvYXm^Hhff!Aik^1maI+k=$}w>YwDD+xMSgSzoh#^u*3gTO?qtqu -zvu#Di58H|`gLI9gl_e4cmadnLmL5X*2858R_iH8s+nMgc>P8ob$zU+{^)|1f;!jG2vh~9zdcv$1Bn0w0SpRh!0ZhS4}JTgx075VO;;k# -zhK{rwmeedibYiz^NmaK`P?ime`V~5x?@32FiHML8LXe&28qN~F@XsW;@$q`3HP5Ef -zP$~|jlxOZ(%66Oi?7Yg**l7~B3s-Fwp|^;Tnh+{N#Azg@5<|j_fM_wOpp!UdiMuQV -zJ(BCU^rpJfoiJ_fQ@v?HbK5X;P6tyWAw)QXu%~)jz6a6h=xFx61tp9SF2TCvn -zBNd4^M>L}&2Q3cee7p2Gu~T!Is7pF#N26I7x=1(s(3^`+LYXPS_mSCG&uG*)!c5Rv -zMNg9?8F8gDI!Xu?A5x%{5!rV^f_1Bzkz2#C8mrZOjK<2`57s+TJ%>~@f#MB-1Rwy) -z3d34FL1R!_tNf4B5p))xWb#2JsANXI@1yjQ$Ob&u&5YcPfRr_1u&3F|keQ-uh@W(; -zWS-f+9|VD)8D$UC;$Ke - -diff --git a/platforms/sdl/sdl2/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/platforms/sdl/sdl2/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp -deleted file mode 100644 -index 5018e6b2da6d5019968fe84e0576502f61901e93..0000000000000000000000000000000000000000 -GIT binary patch -literal 0 -HcmV?d00001 - -literal 7624 -zcmV;(9XH}qNk&G%9RL7VMM6+kP&iDq9RL6?zrZgLO+aWQ$&sYYp0`!}3HR(<0ulY6 -z0RG`XX(^PJ!dd1Ayoe}*D1rhlrKM0>3SEjQf+&KkrL+`EN}*q|-%~^l1VwPQl!j|U -zp-Ztd=Q8I*@JW&tIZrJOUo_CAn1n!*;5n{_@<5WntrU`#gpi9OI?gz8oR>>WX-U!~ -zBy=gFIO8~zdGYe`a8|~L1Er-<0)<(x<=Y*8aRIlrZCT9{`X3G+zgz+RSLN8zl_Q3i -zU1l1Pn@EynTPfeq5;VsiBYs;nGWX -z4n=l0#j3Z-f(R?^{>xAV0`nw}h$CS_aA7P+-1E-r5d43_!*(V(g*SFTLo&Qqo* -ziu+uBc2N{@<>FLnfGkL9`?r~q`E251QYHRJNc<*5X_x8gU8+;~I4;dR;hd21 -z(^siNh*G7Bn5RZJzMe*($A=iJhX6cz;t832Cn3TUU3+$mr)}Hbwvn~%yl#*bWy!Ye -z3J#qr^vXG}HLLnFEB=C>tx*o6HA=%AXKS2u&arY%4(%W@;J&sdfg4vrAbX8gpCxR* -zi$XcAGONfkt0?QzwykX>-S78_EV*NMr#!Au)ifjn8D3^)X5Q_ZU(3fp2e3!5|CpJX -zahR#O+v^EAvC#VTz20lvR;|ajtz6oeQba6fX7XUA^;cdkW;9}Xu$)+OWHB=YexGJ< -zbggYuBsb4r)ion#W@U_#B4&n2xiTHyfSK=sF~yA8j_r(@nb|YlRj^>&s@i0GDtTx& -z1f!C){Re_y(FR4}eZNS5u=AlfK7U69B=DR+BkuVaiy5ASLB?)KJm(LHdp@Zw#R8ct -zOSLDCZ^L(0pmCqL@dvMbo}U=~tpNk413>(F9yGaw|Em-yu^nXWD=HViNW)fLB{z<%5Z+9(ZwL+ -zS~Px(gH&H;kP+_G4j(QZM81S=pR -zK$5_#Fmns=I#BV*vtY?QfA%Z43T`1kQVm!^ljKAsB0WG`40_UkSrQ#Sk9QbkBpXSF -z&+&B(GA_cUr0viOgNzH}`lO4Y`C*WefTSPoNDBfIDuu9(AR;n3!0jv*Y(H;g`hSbH -zyI&(PQLQMVz~%^c_;SHxP;}7{dr92^7lX`uh;Ot$w8J1HHj)f9cisOc@s -zusBAQ0gt6KgV+oim4Ib%f#%Ry85N)ung|A=)M~r>H=ldB>m7XInbO9{{@|5m;d|}B -zKlqLPB5vU*(}klBba378fBNx+@V)j2A1#-pcmwDcZR1t~liQp&sF-ZPmh8n6w!q($3-@snRufAodVQW<)87r%4otE)tIHKA|{p^}!6 -zh-F`xJ#JW>yXlv?48K3z*#x96PnHU^lv9a=gw^%CXs4d#K>C-kREFQ_@;w1W&nSXK -zZv>|hUJ4N)llv$@JiskVSa@*(aVMpCR|QTd6jCkSxxBbVDSC?mB~C4d`ZO&{k-My7 -z@iC_so_j2=xhqO=TX2yw0hn4Sw^;DY;m3s6L~x4XxCda@O_bmUG}4RU^a2RZN|BrK -zfA?VJRhYa}YluW>H6tNg&)Zra2j06~Dw2N<-3nL*0 -znRrWr;UWw&=txV%jw6E%Wcey-1vrLs;NXJsYcPEWmH~p{f&(52fQjX7w1JKr78xbt -zK)4{W$e@du1%p8b9@PPh$TG-4K|3@=1cMAT+6~AO8;cD5(hfDbjqYF>VDe5J&hT|4 -zJkosmD&jE+xh#ii2^azj|K7k&i_VA*=k+J0rO-!rcKA>22XJ8#O?t!wW>dKZYS8d&)sOx~^4 -z2k}CO>mexu1|eUeo+QCCZUB>HDV}Wk?B1XBD1|b*7{0~DNN^Yo)eYa^LL@p0RW?rM -zfp-CeFVdd^kHHWfX^j|MF9sPEw0z{N2TIcJ4)X^XgDETnB$p((Q$Jjc*kRF8m)jBz -zVHsq6!@G2Gc3w<-nD&5=KMuW}~>217BpI0}}pJuF>9l3n0OD}I5Pz8y;e -z^_}MeqU}J7W!yk)M6fgl#fCuA7x5q*5+bsM!7AdLT>f9k?mFAI2CyFpdEmvkB;@6X;}>x$ -zoOrH1o$D7vo_Yzb6z&7vRkk-nUcZ_*{*vw}oP5;f!XL*iwg#Mf*@6K+rR%yVr=Euh -zy!GObqFp!zfTlO$$|I25w~g+4_MWC}TW+1n<_Z8_c`Cgfk~EwKKzZv=!&jjTIrTy= -z|6%xsl4V;%c5YiR(APZwogbPOAJ3;VF8zvI+}zIAsTA8BIzbYWc)0)wB$aW~m-u#Q -zM_~&=^1w^ztc37f`W@*JoEBXEefrZ-EIW6Q^hQ7f5|@w+(^<*psnyPq02i(gl8hui -zAR$M?@WXN}w887>P1Adpq#tJqnQn~7xp#%^@8t5I;ncu4x$vW*Z2+B7PHdBPkT6Mq -z96Q72=IV^o9_NP1PF;Gt3=X*ZR$i$9Jp -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 -z4`QX4jBlJX -zBg04#D4eT~Y-i0KfvG^D=BOW@!_4H -z|KzW)d)##SA;W1IiKt|)Bp{^KKo?1YiNR7E)ZyZRAO;z2L46=i`0ws{S(i0KfdW() -zEs+ElkvVULgprwZ>^jmN8?AvloH3j*&#f=}1YkvKH+++oq!HHGBqI}>T9Dq-JPZG= -zJpp8(wDids7=0K^`L`j9MFhHaiY~T;%8%GR`px3XS$5M7oHTSHVK@i~4U3C9T3IB( -zVCjpb?MSlWLJTs#O4^Y0hKpC&V@NK;Cjt=erNwf+xMUDS0YXE@-1%&z)RNzke2G5{ -zA&Df%x2zaRXs8~agK#7%ks>Q|p$Z;)@I@JKl9%xQKlua$VWi8E@w_IfHazV -z+>>u_rJqa!M&DmN9(l|;c*5JbiG{~$Zo3q5Va^X-7fCMA# -zW*etuYbplQPP|o -z6&3_$0OO9o#>dQ-c2HarEo_=L7jz`VqKKN*lYm7A^-3Kk8MMncI0MTeB}9Ai^>RS? -z1UzQ59W4gkx~{iL-`@WR{wEOSm6#C(Mz=lK|MLJ{<3N=F - -diff --git a/platforms/sdl/sdl2/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/platforms/sdl/sdl2/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp -deleted file mode 100644 -index ded8d97c6205d5546bc8b24d7be8560e581545a6..0000000000000000000000000000000000000000 -GIT binary patch -literal 0 -HcmV?d00001 - -literal 12522 -zcmVNt@GW_e|aG39RF}y|Ce+`y! -zLqjn#dcbjQTiLR(my??p-1YaLihMOT|AIk$L-G)Zx|F@ce!h!F{PX*L{Enur -ze_-Y_V7#pqSGaU`Qz?R5C*`7MOS>Yr8SpKD4N!nsYbT|M2yX8^H3BIA1J&eYHWS&K -z?ZMwq!24~$-Z0t)9uL48_fW6IV@tDzUr0gBK{*PYvZgHGP$2?R&dCX`rA|kRk -z+k3swNp-*csUGndjIKw6T#ionV6J0ext`AH=WYT3qhne7EQd8~U$XTK0Hk^e03#x6 -z*4__m?`!8GTQ-1?QYp*10drm6HxsGr+3X-iWc0XHK&4Dthrr|2eZ&rzB3sW}nTN@; -z`UF*s7-{77S4Myl;6A`(>`;1+T{A$X8pd_Ve${;q_^~@FK#5kG1uvjc$bx#EB&RY3KsM$I$82G{~D9kw|`wPnTxpmhjf0$`b$_lE06dQT&n -zFeD~GEax=Rf?MaeN;B2#FlpxV*)n@0>pkzmKTjr~H}DVF;$YCWjUb0V?BCt(8AQYc -zbT;-15nMsz1G>b49vQsF)DA831FcHS*s)`17nZ_OJ5;<;Sqm>TBT_hf9w -z1a1G~NOFGvOr=UyD(vc-*DFYMr%cVcWL0 -zZQk#fWLvh3O`MD4s|}0`V`gUF4w;#^BW7kiXJ%fRnHiiEZHHvB{{QnNNwRF)k|ZJV -zeh>a{m8t_$osUl8l>j>DUv7aM*|ut>_x-rL`;ZLCf&7Q=?hfxrlx!=ABX91C67YZs -z&YSEHC07(MkK11)RB(FhDJkyA@Zvkw&t(FwMu|Y~ -zz4AaM^My8gDAv-3E8o|p@dzS8AV{JRD=)q$HNwA&_GBw4rmSjPPPyd@sbvk}G^C)S -zC9a7vdQFR}j;pGgwH9xd$2aaT-F=SU#Zm5!mef@<%e}|`_J491A;b%w{&BSD|GgMm -z?@p)tRug}lgQ45hXi4Fw30p}tlPM5n30y}6%uz&8BT*(Zsc=sUJsRq&-R(F`{d3$O -z{&1c+9}jlZKVB1*J(?Sb&5s^>i_!95EJO=I&u5``5IXddMQG#1yWh^L#c2`MtkS`H -z16QWlt|p6t)m&6wGcm(qEF4Wx$xI;tw7QPgiEDMK)15)r23&UKRb|iV#ueK4y1VZH -zVE46a9c}#!=?1h#6#kJuzC=3vFBSp>vgV5im+yVqbssrx%O}3C#U{d)-vvOBr`4&F -zpbGN)S3uzaIK6_K0s}xHVVf>U%5bQJc?$qIkH}SJ&S=#UGdQ`!QUAYQmi_P_YyN`1 -z{{@E#ShM(B-pTK6a?OJT2b5T^p%Vb2t%jC&4_lrj@ubn(u%Rbpyhj>OJh4g|N^xa? -z-AaR71fXD8!+C;N4CMu@Tyut?0_?DKvqOX?MjMXtv>WA5F~Z7k&Qb&WY+F^$o3waD -z;Rb>XfJhS~E2onnASx$qO6rj`5Mrh_fth4P;`$l@8QZF*8pv`JL0!W+WuDcko}5~} -z^&~-pi}Mo4SllSb*sQN2`bmY(f>E1~d5^`WcpJexHS8!=LO#3H<|CyhB~3e2Qh+~H -zaE#!e#j*W|;5M+&3lBK@@2)!puqLri -z6#zh~KDjVHNZ7q6QUs?K^MF>X!Ff)uBlJ=u3xU|*2es=>1ZlK2I*mXhqK(re;NIH2 -z>ED6rJxQ}g+Bo5=&n+cInk^E+Ua!>zy8#d(8Xgh^pvBJYTCG7uvqjKY+zK$GuOnCtfwg^5QO$P~TG^kvK{)>h-{4XXiVnKWLnV)^zE?-{#>7oJIM2r0G*nhe+VG>Ww -z?(W7P-RSWAp5U(+`{C1qdP6g)mWd}W{pOu}=ih#yh)~&}gB-b_01_l4qxfhMC6J*i -zj)^I?fs6+eWv2B}gp5Zij6~6B9TCo*l?gG^(w*{?_5Xb3-(CFp&-X@MVLBV)wE5@W -zI{xY_A1bT>62aJ~vJ^rIR3(+Ts3?JnPDBQqhGQJ=6Xucekc>wubS6Op8IK7_9h8wU -zJ2D=(;oX(d{ -z!-9DXQhOoLxkS8{p{HxbMD!m!g%aw?7y{`}GBNYtlnVw%Pf+_*R!ivJfwrj5Dzwl> -zQun06$asR(E3ihX6OW9Hhp2=SNt-A2N5*q=U_DHDl5u1_Dui*sH6ZYM17rVW0`M@wFL@lQRR=1W*osAwT5 -zt4lT@yOLM1lAn6^PxIm@y{yQL9M;f-5G{rGddJ)b_t*WDSCQcr^G))H_%eU_&V7xq -z0t$fE!lJzpxit?zeGOU?57yGpP)Iv}2=$gl#)f-ae(N8ue$pjJN|$L- -ze9zrx)K9(no~!=BV}CjNQb+1L_@}wR6I(gFhm35taNL{rMGwOd)|*tUWdiiX7&kM+P~6?a%5~Serh(Ya`)F -zqZk_{CM+Q1Byr{&v^9Z10JFT~y=is9`U$KZo!uqKI1NqW&OvCRH5?>{2(!kYI&5d2 -z=yVIu_KzjJ&g?~p8W`hVk*B%3zu7vl?Sw|tmh-sGl|*s0ju0{pVtkZ9QP!s>9wDwp -zz@t=*jH09U5rP_p;?aVe+$_u9k$efvbt+*YLQo^8R$4UT0AX2MI-cR`{oOKh_H{({ -zXy9T;eYbrw|3$~Vdm3#*>LR36AqLa!4>zq%oonAGb>imvL!Fw#2MR%h)YK-#sByDI -z#q^FGpuaBt_33Xx2pX7R{F%E*3=yV{@A+f@=+Z&+U&R_G!(aO1#mn`t`0Nh%hdfM> -z)>gHY`DC;>fu7lG8;p!HNYJmQ+ec`HPGzoof$jfs^JpzPg;J#6$T$fSM~Rql4386~ -z-Z*2TRv?+kt#OUPP-L7wFep=r6I5Asg(QB@zL!7ybBCmcL5BXJN;1_4?;Vv^-$>o; -ztbMH?_4rw>r@xiU?)f71!X(w3Jb@K3D%<~`zbg{-F3HZ+V6G3zfgc;Ic3N@6;tzUf -z*0;L!lXGIqTj49S8)rVc-E^h|Zus(C`*D^IU-G2P_R<$Gsj8)9hde!Q$oU{+@Dn3*!{6uC;Y~JY&IC6j*<2FEfC+QMub4Q6 -zqJE?$ki^-$59e=}eXUigr00vM`3wRVk9sHC@R!|9?5qPgwI=ZhOiX(_(%&<{&T4ko -zrC?SCA?c1j^Cp7yL9awrL1VZ3O<3hPmcDPXEU3JFDj`6H+{O -zb~c;?Q0iRsIcZ42u$oB1LDKuy7r*=U?efxJ#8qcBe(3*92J+!yC7TKKC*U)2F;8=huaCWd8pgJiL!z1HC*`}2TVd;fNrts`5*l9QrY3G -z%gzmRbU*^`=)Tzy^H7gtpi{|%-St&egulM>)7e>+<7Z|4l+&A@4=;=~Y0mE2vpIM6 -zHlB0O$vG!vNj$+7UwqNu3zN`YO+*iwwPRw3tO@9Z(ZJ`sU~vS}t84{<>QGQ0&b|0- -zTr%+MK_>rJFsq4BTC@mUH2eNLRl -zIcJ`zHB3hFC^sz3<}%)gNk~bcOCo^_pC^5^M4a!AN3VngyoiS-hL{5v1@+()x`Vmn -zwWrmdpJh@y`~BDa$==Q|3H`yZB$XM-5&`pm%WokLi%oF;3z-1#c^Zw2%n5@;L7EE33KU -zDy!^p@#B<7)!+3fV>--D(7HtBkx?qfgz%aooT!-LNR*aVA=(I2qEArF-j?*YCdfnv -z^r3vXqR4nslqe(!K1@8Q^UleRXMfC!BC=**oIFU$daJy#R{JAS%D5tfOruBYo@Hq9 -zjS`$*Z$guQV~X@I!<`A!5&a2`;UsPj0&@cWCBsbRV%%v3H+qh1R;Kp5#?8}FLb(+q -zlg7$KBK!oV2_dd}zf~$@tk_OI$+&l^6yP69v9}(kRf^cz#5WX&s3fdLZW-rAbe*JB -zP6qp%vkJ2C4HNUfCJa+66EbY1xMyck$xMW(ZzO}E4EKbo)^P8M8x#g;QmKTyuWXSSKco~Ij);R# -z)Vu3A6tJYoR+J$_OH3L=!Ag8K=W_iYT>nRQ)?#D?I7)G9U5Z+K7KuoagO?e}N*Y7s -z{e%@bLg$k&LHY-A9RW(Lcrkmf_DB>&f(w5?1xH|Z_vXdl=b79uo&}#htMS$I4t{az -zt4M%`^`!2fC`crorPX0*aXl+Y2vDFvj`^f})44ZA!tz`%#orjusssOj45%tdhmd -zD4}bQ8q^=#z|6_Zxu1aLviV7)FtX+-&Lv(BDNF{rVLX;0_H+5tNT9A;nypO_ijWo{ -z$EckGa)gydvK|v<8ot=lotoZ}k+2vEc&H$iF%$}uVG>}wy3fFl1nTxnzfBxaQh;1& -zWa6q&*&^9tnD!}gMSal^*&vgR>k)kn3ydhjHi3wzFljNN7_eZqpq)iTb?yAzxHNnvUpWD;n`f(;XS -z@M95C#ZE5%B44XG<0m&4O+9U@l{MCVhvAsTRMVmYKRD-FWFNFQ2J`q2P0Py2zw?X`Qp!xSCo*b@ngthfk2b$b&8W1@9)rxA_T?>dSQGik&|Z~d8FVlRm=k& -zPtiL3EMB{oB1I`4?##cexH0n)Cy;SkvU#zHD9R~<1}7M-f(NhUWxFT{hUA8ZaTVoG -zZxHs4PE<~m4Qzvy&T-6GGub;js!{&JFW^cZgNH!wW0#CyuvJd09rle*^zi#=@V;9V -zzm6H>PA=vFIvC=!Dtd7L0h0GRx@z?#YOm5*gnw@H3yA~&tONiX7sLM1H$K88f9V}I -z-NVOoBcU~p*@rMU!qA*9qPRQEt%BlEecZjD!WB(=C$B5th%U8`){9P5L2_h}2BXwj -z-sFM6Oqhjzq+Sb=Nnr54o-LI`=7fP3BRt94ic_(y=;xmNrdBqY+IG9~OYgh0eyx{I -z%O-{sb;)>51{vtD?>tDnoCVgW5$0hZsbifk{xR4IZ;m3ij0q@fAtoLfmXR^Fx6CbB -z%4Eklz2i2kD8iZFRemmSS3`UEzaH05GWs+bc|N*{W7LV2pFC@-w=<^r>SsuNY73GlqSojlq{yu9=^r7-C)f -zfn+*6*Hqao(hmAomSlK>n-morjs@G3w@IKmxP@BZ_}Y_UUumNkV6|e!CLV^7Ek8NH -zEOZgn-{=D9@#PqWX?YH{ -zFxbQPF+Vv2ibA%%p0bYG2+h1@Y6qL7^tu1PI?NE3qvA6hPKM8yHYDY!T&w43ec}Tc -zCy-^Y$TEEStEW!lJ;|e_v2gnP0BU?=p%~&6xElB>)2wrmMNHte6)V--S0`PweqG`U -zyNaxyzOVbn2PBB6sE%w_fz;4o#u-vxT%9v3@MPNCK&L-Zp8n1xbt(&Gm7%f9HDMv0 -zK1`6kHDmxAD6_Dh-c)pI23tr*lLNlW;|bf+>C49q)?;<93#h6f=abbbBtTM}48hkZ -zDH(~im6HU?-swYGJ$YXjuCF~27P~2g*D2!(hZ<6dntt_r@0Vr1(rQpHN+mOOiO|#_ -zF;IF$269KYp+HEnGe5Ebn8LARMh}ODZVDmJ9iO&&ZHTT?>ux$c3l$h@;s0NTIYYKV -z4OS*1+nrQ_+pAJ$pJ@b{+#H}$lAO_s_xAd8t`XWag$;Q1RoA%`t=+gfsp9|G@c87T -zxVJ!bQuuNaQPZH%NAC8SIk|W$kJOo2)@`U1A<1P0wZNH1lzH(RPZJOTUlU9jtTup+4x*XFNbq9WoNQw81cp_LCLKutiy|`NyYw#Dz3(W4Ffp -zT4GMSn$JYJBbPHlZF9;QfCI=jRyl_aP)ABUSTqEaX@X(tt6W8(%hkw_2VS3=-2K>e -z?2&m#jD%v8U!2G1wp3CE20kBT4>@0Dhhw$xr^9Tb7Fum&`iH9aB_5iRZ&eBtJ-klB -zj0$RdMJ#;{eva}{c&S7J5xY0YOoM)jFK1ekMrazHj9=#E)>bdqD2@r>b+sVhi<=aN -z4Glkh;5hRL)v#R$P?-R!c9)Bd!v}uooanshF_xbA`2Ne@jB`N=WgeNi@rS^~g*W7R -zDUTf<4?ZdE-#ppD@Lus$ke~H^IKuOg?j$A?aKDZ^{zAT@7 -zE^S1$5K!EWYcM%9yzN%|uld;Lf3owG113b0ZXTOTX{n7IC*ZD{3q#IO@@HufEi50era -zb)IIe5tD1@EHaJw9Ypj4DD37pLm!DITVd?Un)wL -zCx5-si3I7G_$1BbAK*3(Z6m7O*;)`+0`Jv0QpAAZH^+wauIS -z`R8^zut~sLa`q+M{+|)5>q&c1};9$Sb5F -zR13lf9v10=lG5&lBbbwIbqS@Hx|F-FWI!HPxfs7CN~oC0rj_ZPIi$b!y$0YM64Q4kv(}3JBD(Xg1VU+Zks`;5m15ov7;w=#56rEqjza?bHQqFBQ+oeDuo9t2jvz6 -z%#|j2d%Y_af^k|Qy_Hpi -z)Rho3|APuZ0t+#LBIXmA9FHS?3~ts<0Sm^Gf?`e#V8+_j@uC@Kh>(={#qfhRXE0P$5-(d6FJ2Bc4IMI&?#q9Ng-CNt2#R}02b`;)2N_L>d^vx{AR -zd?^sdRVg~1Axux9EYB^stY_=ZjKJUz9pDw63Ww#z$kZhS61wtac(mj#`vF588;3JZ -zY2YAaIvrm^q_Y&8knTM7(d+s?yCg2)8g7F>tjdvd<{1yGrPO6vihwFWI7?M|Jj%sh -zL?1Q(u@Am_OUIfF>7|Z{P=KV<>1dGP0J$YDvL+}=!Y5MT9RU_`F%J#WWGP%}D+_1| -zlxkf@dm*!{+#2NA+39oyejrk69E9|SO|i5LHdqKH?T_FSZ{qL@K)Q~2kt;?ALVIuT -zE_4G+0tU#sAJ1%phjjv<_CP6?tcj^$G%>wLS@=`%tHe>2cSBcDAzlV+WFxeplowlC -z%~^b}BsJ&KoaO}X1iw`7Q^vPanKoW_~c936ryzBDR#B#dy3 -z;!C9+X7IU8UQ6J&$N>0DO+gEqVE@`1eXit(h6G!xyr)`2 -z8Bneu`hXRP;xL)+Fo5NO3#fvIkw0lJFmo&omnk$$B9WNhv1yDmxKdWE1*Hh$6P@h7 -zU*#;S_0xyx@m+m$O^{28cLooBHupy~I4nw6kP~0><&nsx4!0vkf65XqyY9Od38*K# -z3(8UOKP)9z?V{{HrD*1A>J=-OLDb-&RH#yIPUDyc2H;AK_Za!;a5irm!^v^x@hSaG -zsZj*A)itFjC&F=5b9k_O5NKAB^HYZkCwxF$49%!Aj}ORLuM^ -zQ(OLW85qppN2?s=U&x~IbEOez`D+uWA-A?sxlM-4bf{|+>5ZcAl-8ffcf-0N*II$p -zsRA(C5bvO*ht#J(rCKP!HT2JP)tl>`N{S9RlT`7PC;Q^4$3Z&lO7R2K`x -zbJgpMh(#yHcchfBPQBxW3d}dp2a&W8?3Mq>3AD0~Dha8V(I}UFlS3ijsx-cb5-7uR -zrM_YHx}#<|z8=bsGx5-(;0OC<+TuKToHj=s9-~4w96X{Cp#v)j+G?<=$+hD+1xE)1 -z>YoFE?W%E#UZ2MP0^+}YQw@;^<#Y*rXeJs})nU^EB}b_yDI -z1J*)U)(m9C(jn~U?Oec1v7z%3h{t;y()i=a>-X`VR&Ps3vsWY0p&&s_TXrU -zJqh8Ja5F8svlMbbBg*_KmLcViDs4oBBPs#z>Et$BZ<&*vrjSwf9Fb1fxhEZ*9jNE( -zqETSxeC(=(ITbsEZE)OP$coCeMG@&!@k8#YXj23%6-eU)j?VlVh0Fk^v62!O4}<>i -zQbO%$(Nk%l0D*e0$I)tr#c0gi^XnRiQ&65jODmL|LgF?229onv85=8K1_`VNR_n)K!dMjbjo6rAyv_0 -zQOE>fJtnL1)DiIi8`y46HYD4&P`+lKydf#~AqY`q$DSw|<3kX>jyQ987hVSB}L_1m% -zMeTAYG$!m#+wEwkw#4e1{Wu@|_VLSaQTgtw_5Rx7D_iZv5|H_}Ok-#G0onP|g2WYmy08?ntflNEt -zfS7>MwfkRv_=u(PKX35I+ow28(tNgDB-6Bp01|{D8kq%vop9Zl6z;v`Ml1H)8~sjS -zRN@B3?Wtu$AVU&80t%x>Uie3-x~YJeG$;V(Oc6N3V_Oy!*w>ISg)Rq}vPcDSow>C} -zdG%V#lQ%G393~sgwo{UVS|D7$Ld -zo!Ky)oHc+L1SA9$>ba=8=^ZB#KR;)F$uYzubiO@p7Mhm(IdJv={^`Td7&dc;W{)|K5F)-0e<4Pw^AY!mmgbKw) -z%PBb=c5)35?@zW_hQRh%?oCboch%@7^x0(j+U8Z5}f~ -z;Nx!I>b8{SCgsBRDa}_-tlT4I?TA=8hzt6H8GWLuTcAgLyf+CGYk0588MZ12GXjo4er-YuKUI7XurC?FoBy^{z!3B -zryjyzw@cL$ktb^+>?SfZ2>j9#f%|T&+kAJ^sXW@GFcBFhO_y_DxU*cm9W0Ht%u#Y? -zyx2i&;l9PF$qJ!lrR9>%EirELL+QIs);-z<`XOWGW`kmBA-dBkD=y1 -zMOW$I-h{b%s`fjpqTiX2rI0z9M(S~AL3?!SA+ckRQM -zTM;kec!`Y;OQFiK3q%M0GkNG;GRHKDRZiECXs|u -zySzs2FyfHWj97_}VDD#o?gX7U2XBE`c@!eF@&Tu!kABnwT1e2Fw#;yUk?&9E>4dhl -z0TM$&fn8mqInzkjp)oO7nXc5mA7mL8Af6|&%L)acf;y}=S=NRI6#@z2d7(xi0R+v< -zN5C5)f$Nl#APCyfzNTgT*?XtR(D%AA{0|~=80{P9@v|6U0aeDj8QnL|mMst~oN|ic -zbXhgjV4FAqLjVyeXUz{Ymm=_PzvgSK32gC;VzCOWEI>p65GtGk;Lwz9R-N+*0T=+p -zTM-DO`C))rf{vB)hqw~PuSHQ?P10RI2| -Ak^lez - -diff --git a/platforms/windows/MinecraftClient.Win32.rc b/platforms/windows/MinecraftClient.Win32.rc -index 0837b53be..deb22a723 100644 ---- a/platforms/windows/MinecraftClient.Win32.rc -+++ b/platforms/windows/MinecraftClient.Win32.rc -@@ -52,9 +52,9 @@ END - // Icon with lowest ID value placed first to ensure application icon - // remains consistent on all systems. - #ifdef __GNUC__ --IDI_ICON ICON "assets/icon.ico" -+IDI_ICON ICON "assets/app/icons/icon.ico" - #else --IDI_ICON ICON "..\\..\\game\\assets\\icon.ico" -+IDI_ICON ICON "..\\..\\game\\assets\\app\\icons\\icon.ico" - #endif - - #endif // English (United States) resources -diff --git a/platforms/windows/projects/MinecraftClient.Win32/MinecraftClient.Win32.vcxproj b/platforms/windows/projects/MinecraftClient.Win32/MinecraftClient.Win32.vcxproj -index 7f723cc0e..8f8be420a 100644 ---- a/platforms/windows/projects/MinecraftClient.Win32/MinecraftClient.Win32.vcxproj -+++ b/platforms/windows/projects/MinecraftClient.Win32/MinecraftClient.Win32.vcxproj -@@ -80,7 +80,7 @@ - - - -- -+ - - - -diff --git a/platforms/windows/projects/MinecraftClient.Win32/MinecraftClient.Win32.vcxproj.filters b/platforms/windows/projects/MinecraftClient.Win32/MinecraftClient.Win32.vcxproj.filters -index e418f0bf7..e55463c4c 100644 ---- a/platforms/windows/projects/MinecraftClient.Win32/MinecraftClient.Win32.vcxproj.filters -+++ b/platforms/windows/projects/MinecraftClient.Win32/MinecraftClient.Win32.vcxproj.filters -@@ -42,7 +42,7 @@ - - - -- -+ - Resource Files - - -diff --git a/platforms/xdk360/ReMinecraftPE.xlast b/platforms/xdk360/ReMinecraftPE.xlast -index f9d9020a9effb15aaf90fa461889e4aae1c9c9f9..409edcc888385bf2544495fb3ab2f497ed9db5e9 100644 -GIT binary patch -delta 85 -zcmaD^9ZfAT%sZG_Zqo@f`y -F2mmBa7hwPZ - -delta 22 -ecmZ41!1$ -Date: Fri, 20 Feb 2026 00:59:54 -0500 -Subject: [PATCH 3/9] Restored Unused Camera Flash Texture (#494) - ---- - source/client/renderer/ItemInHandRenderer.cpp | 4 +- - .../renderer/entity/TripodCameraRenderer.cpp | 40 ++++++++++++++++++- - source/world/entity/Animal.cpp | 4 +- - source/world/entity/Animal.hpp | 2 +- - 4 files changed, 44 insertions(+), 6 deletions(-) - -diff --git a/source/client/renderer/ItemInHandRenderer.cpp b/source/client/renderer/ItemInHandRenderer.cpp -index 81edccd3e..03c1ebc24 100644 ---- a/source/client/renderer/ItemInHandRenderer.cpp -+++ b/source/client/renderer/ItemInHandRenderer.cpp -@@ -234,7 +234,7 @@ void ItemInHandRenderer::renderItem(const Entity& entity, const ItemStack& item, - t.vertexUV(1.0f, 0.0f, -C_ONE_PIXEL, texU_1, texV_2); - t.vertexUV(0.0f, 0.0f, -C_ONE_PIXEL, texU_2, texV_2); - -- SHADE_IF_NEEDED(0.8f); -+ SHADE_IF_NEEDED(1.0f); - t.normal(Vec3::NEG_UNIT_X); - for (int i = 0; i < 16; i++) - { -@@ -251,7 +251,7 @@ void ItemInHandRenderer::renderItem(const Entity& entity, const ItemStack& item, - t.vertexUV((i + 1) * C_ONE_PIXEL, 0.0f, -C_ONE_PIXEL, Mth::Lerp(texU_2, texU_1, i * C_ONE_PIXEL) - C_RATIO_2, texV_2); - } - -- SHADE_IF_NEEDED(0.6f); -+ SHADE_IF_NEEDED(1.0f); - for (int i = 0; i < 16; i++) - { - t.vertexUV(0.0f, (i + 1) * C_ONE_PIXEL, 0.0f, texU_2, Mth::Lerp(texV_2, texV_1, i * C_ONE_PIXEL)); -diff --git a/source/client/renderer/entity/TripodCameraRenderer.cpp b/source/client/renderer/entity/TripodCameraRenderer.cpp -index 15fc4ea67..5855de00f 100644 ---- a/source/client/renderer/entity/TripodCameraRenderer.cpp -+++ b/source/client/renderer/entity/TripodCameraRenderer.cpp -@@ -86,8 +86,46 @@ void TripodCameraRenderer::render(const Entity& entity, const Vec3& pos, float r - float time = getFlashTime(camera, a); - if (time >= 0.0f) - { -+ // pulse effect - currentShaderColor = Color::WHITE; -- currentShaderDarkColor = Color(1.0f, 1.0f, 1.0f, sinf(float(M_PI) * 2.0f * time)); -+ currentShaderDarkColor = Color(1.0f, 1.0f, 1.0f, sinf(float(M_PI) * 1.0f * time)); -+ -+ // restore camera flash texture -+ MatrixStack::Ref flashMatrix = MatrixStack::World.push(); -+ -+ const float radToDeg = 1.0f / MTH_DEG_TO_RAD; -+ -+ float yaw = m_modelPart.m_rot.y; -+ float pitch = m_modelPart.m_rot.x; -+ float forwardX = -sinf(yaw) * cosf(pitch); -+ float forwardY = sinf(pitch); -+ float forwardZ = -cosf(yaw) * cosf(pitch); -+ -+ flashMatrix->translate(Vec3(forwardX * 0.4f, forwardY * 0.4f + 0.7f, forwardZ * 0.4f)); -+ flashMatrix->rotate(yaw * radToDeg, Vec3::UNIT_Y); -+ flashMatrix->rotate(pitch * radToDeg, Vec3::UNIT_X); -+ flashMatrix->rotate(90.0f, Vec3::UNIT_X); -+ -+ t.begin(8); -+ t.normal(Vec3::UNIT_Y); -+ -+ static constexpr float U_RATIO = 1.0f / 64.0f; -+ static constexpr float V_RATIO = 1.0f / 32.0f; -+ -+ // calculate U and V coordinates -+ float texU_l = 48.0f * U_RATIO; -+ float texU_r = (48.0f + 15.99f) * U_RATIO; -+ float texV_u = 0.0f * V_RATIO; -+ float texV_d = (0.0f + 15.99f) * V_RATIO; -+ float x1 = -0.5f, x2 = 0.5f; -+ float z1 = -0.5f, z2 = 0.5f; -+ float y = 0.0f; -+ -+ t.vertexUV(x1, y, z2, texU_l, texV_u); -+ t.vertexUV(x1, y, z1, texU_r, texV_u); -+ t.vertexUV(x2, y, z1, texU_r, texV_d); -+ t.vertexUV(x2, y, z2, texU_l, texV_d); -+ t.draw(m_shaderMaterials.entity_alphatest); - } - - if (&entity == pHREntity) -diff --git a/source/world/entity/Animal.cpp b/source/world/entity/Animal.cpp -index 1c30c0614..8719bf4c2 100644 ---- a/source/world/entity/Animal.cpp -+++ b/source/world/entity/Animal.cpp -@@ -76,7 +76,7 @@ float Animal::getWalkTargetValue(const TilePos& pos) const - return m_pLevel->getBrightness(pos) - 0.5f; - } - --bool Animal::hurt(Entity* pCulprit, int damage) -+/*bool Animal::hurt(Entity* pCulprit, int damage) - { - // Run around erratically for three seconds. - field_BA4 = 60; -@@ -85,7 +85,7 @@ bool Animal::hurt(Entity* pCulprit, int damage) - field_BB4 = 0; - - return Mob::hurt(pCulprit, damage); --} -+}*/ - - bool Animal::removeWhenFarAway() const - { -diff --git a/source/world/entity/Animal.hpp b/source/world/entity/Animal.hpp -index 7a2830e6a..7d1873b89 100644 ---- a/source/world/entity/Animal.hpp -+++ b/source/world/entity/Animal.hpp -@@ -21,7 +21,7 @@ class Animal : public PathfinderMob - Entity* findAttackTarget() override; - int getAmbientSoundInterval() const override; - float getWalkTargetValue(const TilePos& pos) const override; -- bool hurt(Entity* pCulprit, int damage) override; -+ //bool hurt(Entity* pCulprit, int damage) override; - bool removeWhenFarAway() const override; - - int getAge() const; - -From b7aafe0f3e28cdb7a8e58770bbae1677e5a295fd Mon Sep 17 00:00:00 2001 -From: Un1q32 -Date: Fri, 20 Feb 2026 19:33:27 -0500 -Subject: [PATCH 4/9] Fix default UI theme, also fix Minecraft::isTouchscreen - always being false (#489) - ---- - source/client/app/NinecraftApp.cpp | 4 ++-- - source/client/options/Options.cpp | 2 +- - 2 files changed, 3 insertions(+), 3 deletions(-) - -diff --git a/source/client/app/NinecraftApp.cpp b/source/client/app/NinecraftApp.cpp -index a591064f5..1c7bd839c 100644 ---- a/source/client/app/NinecraftApp.cpp -+++ b/source/client/app/NinecraftApp.cpp -@@ -198,6 +198,8 @@ void NinecraftApp::_initAll() - m_pLevelStorageSource = new ExternalFileLevelStorageSource(platform()->m_externalStorageDir); - #endif - -+ _initInput(); -+ - m_pGui = new Gui(this); - m_pFont = new Font(getOptions(), "font/default.png", m_pTextures); - m_pLevelRenderer = new LevelRenderer(this, m_pTextures); -@@ -205,8 +207,6 @@ void NinecraftApp::_initAll() - m_pParticleEngine = new ParticleEngine(m_pLevel, m_pTextures); - m_pUser = new User(getOptions()->m_playerName.get(), ""); - -- _initInput(); -- - platform()->initSoundSystem(); - m_pSoundEngine = new SoundEngine(platform()->getSoundSystem(), 20.0f); // 20.0f on 0.7.0 - m_pSoundEngine->init(getOptions()); -diff --git a/source/client/options/Options.cpp b/source/client/options/Options.cpp -index 6cbc4bead..4e65f5d71 100644 ---- a/source/client/options/Options.cpp -+++ b/source/client/options/Options.cpp -@@ -59,7 +59,7 @@ static UITheme GetDefaultUiTheme(Minecraft* mc) - #if MC_PLATFORM_XBOX360 - return UI_CONSOLE; - #else -- return mc->isTouchscreen() ? UI_POCKET : UI_JAVA; -+ return mc->platform()->isTouchscreen() ? UI_POCKET : UI_JAVA; - #endif - } - - -From 343edc5f675522d7802e77505d17bfed7ca5fdbe Mon Sep 17 00:00:00 2001 -From: Un1q32 -Date: Fri, 20 Feb 2026 19:34:47 -0500 -Subject: [PATCH 5/9] Android build update (#487) - ---- - .github/workflows/artifacts.yml | 23 +- - .github/workflows/build.yml | 49 ++- - .github/workflows/publish.yml | 2 +- - build-wasm.sh | 4 +- - platforms/sdl/sdl2/android/app/build.gradle | 20 +- - platforms/sdl/sdl2/android/build.gradle | 6 +- - platforms/sdl/sdl2/android/gradle.properties | 1 - - .../android/gradle/wrapper/gradle-wrapper.jar | Bin 54213 -> 43462 bytes - .../gradle/wrapper/gradle-wrapper.properties | 7 +- - platforms/sdl/sdl2/android/gradlew | 307 +++++++++++------- - platforms/sdl/sdl2/android/gradlew.bat | 66 ++-- - thirdparty/SDL2/src | 2 +- - 12 files changed, 319 insertions(+), 168 deletions(-) - -diff --git a/.github/workflows/artifacts.yml b/.github/workflows/artifacts.yml -index fedf4758e..4e5ae906f 100644 ---- a/.github/workflows/artifacts.yml -+++ b/.github/workflows/artifacts.yml -@@ -50,6 +50,7 @@ jobs: - cmake .. -GNinja \ - -DCMAKE_BUILD_TYPE=Release \ - -DCMAKE_INTERPROCEDURAL_OPTIMIZATION=ON \ -+ -DCMAKE_C_COMPILER_LAUNCHER=ccache \ - -DCMAKE_CXX_COMPILER_LAUNCHER=ccache \ - ${{ matrix.flags }} - cmake --build . -@@ -109,6 +110,7 @@ jobs: - # cmake .. -GNinja \ - # -DCMAKE_BUILD_TYPE=Release \ - # -DCMAKE_INTERPROCEDURAL_OPTIMIZATION=ON \ -+ # -DCMAKE_C_COMPILER_LAUNCHER=ccache \ - # -DCMAKE_CXX_COMPILER_LAUNCHER=ccache \ - # -DCMAKE_C_FLAGS='-m32' \ - # -DCMAKE_CXX_FLAGS='-m32' \ -@@ -215,6 +217,16 @@ jobs: - name: Android (${{ matrix.name }}) - runs-on: ubuntu-24.04 - steps: -+ - name: Get Time -+ id: get-time -+ run: echo "time=$(date -u '+%Y-%m-%d-%H:%M:%S')" >> $GITHUB_OUTPUT -+ - uses: actions/cache@v5 -+ with: -+ path: ~/ccache.tar.xz -+ key: android-artifact-${{ steps.get-time.outputs.time }} -+ restore-keys: android-artifact- -+ - name: Unpack cache -+ run: cd ~ && [ -f ccache.tar.xz ] && tar xf ccache.tar.xz || true - - name: Checkout Repository - uses: actions/checkout@v6 - with: -@@ -225,15 +237,20 @@ jobs: - java-version: '17' - distribution: 'temurin' - cache: gradle -+ - name: Install Dependencies -+ run: sudo apt-get install --no-install-recommends -y ccache - - name: Build - run: | - cd ${{ matrix.directory }} - ./gradlew assembleRelease -+ env: -+ USE_CCACHE: 1 -+ CCACHE_COMPILERCHECK: "%compiler% -v" - - uses: upup-company/apksigner-android@v1 - id: sign_apk - env: - ANDROID_KEYSTORE: ${{ secrets.ANDROID_KEYSTORE }} -- BUILD_TOOLS_VERSION: 33.0.1 -+ BUILD_TOOLS_VERSION: 36.1.0 - if: ${{ env.ANDROID_KEYSTORE }} - with: - releaseDirectory: ${{ matrix.directory }}/app/build/outputs/apk/release -@@ -254,6 +271,8 @@ jobs: - with: - name: Android (${{ matrix.name }}) - path: ReMCPE.apk -+ - name: Pack cache -+ run: cd ~ && tar cJf ccache.tar.xz .cache/ccache - - mingw: - strategy: -@@ -300,6 +319,7 @@ jobs: - cmake .. -GNinja \ - -DCMAKE_BUILD_TYPE=Release \ - -DCMAKE_INTERPROCEDURAL_OPTIMIZATION=ON \ -+ -DCMAKE_C_COMPILER_LAUNCHER=ccache \ - -DCMAKE_CXX_COMPILER_LAUNCHER=ccache \ - ${{ matrix.flags }} - cmake --build . -@@ -341,6 +361,7 @@ jobs: - /opt/devkitpro/portlibs/switch/bin/aarch64-none-elf-cmake .. -GNinja \ - -DCMAKE_BUILD_TYPE=Release \ - -DCMAKE_INTERPROCEDURAL_OPTIMIZATION=ON \ -+ -DCMAKE_C_COMPILER_LAUNCHER=ccache \ - -DCMAKE_CXX_COMPILER_LAUNCHER=ccache - /opt/devkitpro/portlibs/switch/bin/aarch64-none-elf-cmake --build . - mv platforms/sdl/sdl2/reminecraftpe.nro . -diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml -index cbbd0ab1a..f357b87d4 100644 ---- a/.github/workflows/build.yml -+++ b/.github/workflows/build.yml -@@ -46,7 +46,11 @@ jobs: - run: | - mkdir build - cd build -- cmake -GNinja -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DWERROR=ON ${{ matrix.flags }} .. -+ cmake .. -GNinja \ -+ -DCMAKE_C_COMPILER_LAUNCHER=ccache \ -+ -DCMAKE_CXX_COMPILER_LAUNCHER=ccache \ -+ -DWERROR=ON \ -+ ${{ matrix.flags }} - cmake --build . - - name: Pack cache - run: cd ~ && tar cJf ccache.tar.xz .cache/ccache -@@ -61,8 +65,8 @@ jobs: - - uses: actions/cache@v5 - with: - path: ~/ccache.tar.xz -- key: linux-${{ matrix.name }}-${{ steps.get-time.outputs.time }} -- restore-keys: linux-${{ matrix.name }}- -+ key: wasm-${{ steps.get-time.outputs.time }} -+ restore-keys: wasm- - - name: Unpack cache - run: cd ~ && [ -f ccache.tar.xz ] && tar xf ccache.tar.xz || true - - name: Checkout Repository -@@ -74,7 +78,11 @@ jobs: - sudo apt-get update - sudo apt-get install --no-install-recommends -y cmake ninja-build ccache - - name: Build -- run: ./build-wasm.sh -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DWERROR=ON -+ run: | -+ ./build-wasm.sh \ -+ -DCMAKE_C_COMPILER_LAUNCHER=ccache \ -+ -DCMAKE_CXX_COMPILER_LAUNCHER=ccache \ -+ -DWERROR=ON - - name: Pack cache - run: cd ~ && tar cJf ccache.tar.xz .cache/ccache - -@@ -165,11 +173,21 @@ jobs: - include: - - name: SDL 2 - directory: platforms/sdl/sdl2/android -- - name: Native -- directory: platforms/android/project -+ # - name: Native -+ # directory: platforms/android/project - name: Android (${{ matrix.name }}) - runs-on: ubuntu-24.04 - steps: -+ - name: Get Time -+ id: get-time -+ run: echo "time=$(date -u '+%Y-%m-%d-%H:%M:%S')" >> $GITHUB_OUTPUT -+ - uses: actions/cache@v5 -+ with: -+ path: ~/ccache.tar.xz -+ key: android-${{ steps.get-time.outputs.time }} -+ restore-keys: android- -+ - name: Unpack cache -+ run: cd ~ && [ -f ccache.tar.xz ] && tar xf ccache.tar.xz || true - - name: Checkout Repository - uses: actions/checkout@v6 - with: -@@ -180,10 +198,17 @@ jobs: - java-version: '17' - distribution: 'temurin' - cache: gradle -+ - name: Install Dependencies -+ run: sudo apt-get install --no-install-recommends -y ccache - - name: Build - run: | - cd ${{ matrix.directory }} - ./gradlew assembleDebug -+ env: -+ USE_CCACHE: 1 -+ CCACHE_COMPILERCHECK: "%compiler% -v" -+ - name: Pack cache -+ run: cd ~ && tar cJf ccache.tar.xz .cache/ccache - - mingw: - strategy: -@@ -230,7 +255,12 @@ jobs: - run: | - mkdir build - cd build -- cmake -GNinja -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DWERROR=ON -DCMAKE_TOOLCHAIN_FILE=../cmake/mingw-w64-toolchain.cmake ${{ matrix.flags }} .. -+ cmake .. -GNinja \ -+ -DCMAKE_C_COMPILER_LAUNCHER=ccache \ -+ -DCMAKE_CXX_COMPILER_LAUNCHER=ccache \ -+ -DWERROR=ON \ -+ -DCMAKE_TOOLCHAIN_FILE=../cmake/mingw-w64-toolchain.cmake \ -+ ${{ matrix.flags }} - cmake --build . - - name: Pack cache - run: cd ~ && tar cJf ccache.tar.xz .cache/ccache -@@ -261,7 +291,10 @@ jobs: - run: | - mkdir build - cd build -- /opt/devkitpro/portlibs/switch/bin/aarch64-none-elf-cmake -GNinja -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DWERROR=ON .. -+ /opt/devkitpro/portlibs/switch/bin/aarch64-none-elf-cmake .. -GNinja \ -+ -DCMAKE_C_COMPILER_LAUNCHER=ccache \ -+ -DCMAKE_CXX_COMPILER_LAUNCHER=ccache \ -+ -DWERROR=ON - /opt/devkitpro/portlibs/switch/bin/aarch64-none-elf-cmake --build . - - name: Pack cache - run: cd ~ && tar cJf ccache.tar.xz .cache/ccache -diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml -index f0e66f129..433e95838 100644 ---- a/.github/workflows/publish.yml -+++ b/.github/workflows/publish.yml -@@ -190,7 +190,7 @@ jobs: - keyStorePassword: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }} - alias: remcpe - env: -- BUILD_TOOLS_VERSION: 33.0.1 -+ BUILD_TOOLS_VERSION: 36.1.0 - - name: Rename APK - run: mv ${{ steps.sign_apk.outputs.signedReleaseFile }} ReMCPE.apk - - uses: alexellis/upload-assets@0.4.1 -diff --git a/build-wasm.sh b/build-wasm.sh -index 638906693..355bc8f56 100755 ---- a/build-wasm.sh -+++ b/build-wasm.sh -@@ -1,4 +1,4 @@ --#!/bin/bash -+#!/bin/sh - - set -e - -@@ -19,7 +19,7 @@ git pull - - # Use Emscripten SDK - export EMSDK_QUIET=1 --source ./emsdk_env.sh -+. ./emsdk_env.sh - - # Create Output Directory - cd ../ -diff --git a/platforms/sdl/sdl2/android/app/build.gradle b/platforms/sdl/sdl2/android/app/build.gradle -index 7d8bb604c..e41118bbf 100644 ---- a/platforms/sdl/sdl2/android/app/build.gradle -+++ b/platforms/sdl/sdl2/android/app/build.gradle -@@ -1,25 +1,31 @@ - apply plugin: 'com.android.application' - -+def useCcache = System.getenv("USE_CCACHE") == "1" -+ - android { -- compileSdk 34 -+ compileSdk 36 - defaultConfig { - applicationId 'io.github.reminecraftpe' - minSdkVersion 21 - //noinspection EditedTargetSdkVersion -- targetSdkVersion 34 -+ targetSdkVersion 36 - versionCode 1 - versionName '1.0' - externalNativeBuild { - cmake { -- arguments '-DANDROID_PLATFORM=android-21', '-DANDROID_STL=c++_static' -+ arguments '-DANDROID_PLATFORM=android-21', '-DANDROID_STL=c++_static', '-DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON' - abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64' -+ if (useCcache) { -+ arguments += '-DCMAKE_C_COMPILER_LAUNCHER=ccache' -+ arguments += '-DCMAKE_CXX_COMPILER_LAUNCHER=ccache' -+ } - } - } - } - buildTypes { - release { - minifyEnabled false -- proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' -+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' - } - } - applicationVariants.configureEach { variant -> -@@ -56,11 +62,11 @@ android { - externalNativeBuild { - cmake { - path '../../../../../CMakeLists.txt' -- version '3.22.1' -+ version = '3.22.1' - } - } - lint { -- abortOnError false -+ abortOnError = false - } -- namespace 'io.github.reminecraftpe' -+ namespace = 'io.github.reminecraftpe' - } -diff --git a/platforms/sdl/sdl2/android/build.gradle b/platforms/sdl/sdl2/android/build.gradle -index ab974e859..534feb40a 100644 ---- a/platforms/sdl/sdl2/android/build.gradle -+++ b/platforms/sdl/sdl2/android/build.gradle -@@ -6,7 +6,7 @@ buildscript { - google() - } - dependencies { -- classpath 'com.android.tools.build:gradle:8.1.2' -+ classpath 'com.android.tools.build:gradle:8.13.2' - - // NOTE: Do not place your application dependencies here; they belong - // in the individual module build.gradle files -@@ -20,6 +20,6 @@ allprojects { - } - } - --task clean(type: Delete) { -- delete rootProject.buildDir -+tasks.register('clean', Delete) { -+ delete rootProject.layout.buildDirectory - } -diff --git a/platforms/sdl/sdl2/android/gradle.properties b/platforms/sdl/sdl2/android/gradle.properties -index 8450cacc3..833814d4b 100644 ---- a/platforms/sdl/sdl2/android/gradle.properties -+++ b/platforms/sdl/sdl2/android/gradle.properties -@@ -9,7 +9,6 @@ - - # Specifies the JVM arguments used for the daemon process. - # The setting is particularly useful for tweaking memory settings. --android.defaults.buildfeatures.buildconfig=true - android.nonFinalResIds=false - android.nonTransitiveRClass=false - org.gradle.jvmargs=-Xmx1536m -diff --git a/platforms/sdl/sdl2/android/gradle/wrapper/gradle-wrapper.jar b/platforms/sdl/sdl2/android/gradle/wrapper/gradle-wrapper.jar -index 2b338a935b508eb6286ed4b84a91176ee5fb93c5..d64cd4917707c1f8861d8cb53dd15194d4248596 100644 -GIT binary patch -literal 43462 -zcma&NWl&^owk(X(xVyW%ySuwf;qI=D6|RlDJ2cR^yEKh!@I- -zp9QeisK*rlxC>+~7Dk4IxIRsKBHqdR9b3+fyL=ynHmIDe&|>O*VlvO+%z5;9Z$|DJ -zb4dO}-R=MKr^6EKJiOrJdLnCJn>np?~vU-1sSFgPu;pthGwf}bG -z(1db%xwr#x)r+`4AGu$j7~u2MpVs3VpLp|mx&;>`0p0vH6kF+D2CY0fVdQOZ@h;A` -z{infNyvmFUiu*XG}RNMNwXrbec_*a3N=2zJ|Wh5z* -z5rAX$JJR{#zP>KY**>xHTuw?|-Rg|o24V)74HcfVT;WtQHXlE+_4iPE8QE#DUm%x0 -zEKr75ur~W%w#-My3Tj`hH6EuEW+8K-^5P62$7Sc5OK+22qj&Pd1;)1#4tKihi=~8C -zHiQSst0cpri6%OeaR`PY>HH_;CPaRNty%WTm4{wDK8V6gCZlG@U3$~JQZ;HPvDJcT1V{ -z?>H@13MJcCNe#5z+MecYNi@VT5|&UiN1D4ATT+%M+h4c$t;C#UAs3O_q=GxK0}8%8 -z8J(_M9bayxN}69ex4dzM_P3oh@ZGREjVvn%%r7=xjkqxJP4kj}5tlf;QosR=%4L5y -zWhgejO=vao5oX%mOHbhJ8V+SG&K5dABn6!WiKl{|oPkq(9z8l&Mm%(=qGcFzI=eLu -zWc_oCLyf;hVlB@dnwY98?75B20=n$>u3b|NB28H0u-6Rpl((%KWEBOfElVWJx+5yg -z#SGqwza7f}$z;n~g%4HDU{;V{gXIhft*q2=4zSezGK~nBgu9-Q*rZ#2f=Q}i2|qOp -z!!y4p)4o=LVUNhlkp#JL{tfkhXNbB=Ox>M=n6soptJw-IDI|_$is2w}(XY>a=H52d -z3zE$tjPUhWWS+5h=KVH&uqQS=$v3nRs&p$%11b%5qtF}S2#Pc`IiyBIF4%A!;AVoI -zXU8-Rpv!DQNcF~(qQnyyMy=-AN~U>#&X1j5BLDP{?K!%h!;hfJI>$mdLSvktEr*89 -zdJHvby^$xEX0^l9g$xW-d?J;L0#(`UT~zpL&*cEh$L|HPAu=P8`OQZV!-}l`noSp_ -zQ-1$q$R-gDL)?6YaM!=8H=QGW$NT2SeZlb8PKJdc=F-cT@j7Xags+Pr*jPtlHFnf- -zh?q<6;)27IdPc^Wdy-mX%2s84C1xZq9Xms+==F4);O`VUASmu3(RlgE#0+#giLh-& -zcxm3_e}n4{%|X -zJp{G_j+%`j_q5}k{eW&TlP}J2wtZ2^<^E(O)4OQX8FDp6RJq!F{(6eHWSD3=f~(h} -zJXCf7=r<16X{pHkm%yzYI_=VDP&9bmI1*)YXZeB}F? -z(%QsB5fo*FUZxK$oX~X^69;x~j7ms8xlzpt-T15e9}$4T-pC -z6PFg@;B-j|Ywajpe4~bk#S6(fO^|mm1hKOPfA%8-_iGCfICE|=P_~e;Wz6my&)h_~ -zkv&_xSAw7AZ%ThYF(4jADW4vg=oEdJGVOs>FqamoL3Np8>?!W#!R-0%2Bg4h?kz5I -zKV-rKN2n(vUL%D<4oj@|`eJ>0i#TmYBtYmfla;c!ATW%;xGQ0*TW@PTlGG><@dxUI -zg>+3SiGdZ%?5N=8uoLA|$4isK$aJ%i{hECP$bK{J#0W2gQ3YEa -zZQ50Stn6hqdfxJ*9#NuSLwKFCUGk@c=(igyVL;;2^wi4o30YXSIb2g_ud$ -zgpCr@H0qWtk2hK8Q|&wx)}4+hTYlf;$a4#oUM=V@Cw#!$(nOFFpZ;0lc!qd=c$S}Z -zGGI-0jg~S~cgVT=4Vo)b)|4phjStD49*EqC)IPwyeKBLcN;Wu@Aeph;emROAwJ-0< -z_#>wVm$)ygH|qyxZaet&(Vf%pVdnvKWJn9`%DAxj3ot;v>S$I}jJ$FLBF*~iZ!ZXE -zkvui&p}fI0Y=IDX)mm0@tAd|fEHl~J&K}ZX(Mm3cm1UAuwJ42+AO5@HwYfDH7ipIc -zmI;1J;J@+aCNG1M`Btf>YT>~c&3j~Qi@Py5JT6;zjx$cvOQW@3oQ>|}GH?TW-E -z1R;q^QFjm5W~7f}c3Ww|awg1BAJ^slEV~Pk`Kd`PS$7;SqJZNj->it4DW2l15}xP6 -zoCl$kyEF%yJni0(L!Z&14m!1urXh6Btj_5JYt1{#+H8w?5QI%% -zo-$KYWNMJVH?Hh@1n7OSu~QhSswL8x0=$<8QG_zepi_`y_79=nK=_ZP_`Em2UI*tyQoB+r{1QYZCpb?2OrgUw#oRH$?^Tj!Req>XiE#~B|~ -z+%HB;=ic+R@px4Ld8mwpY;W^A%8%l8$@B@1m5n`TlKI6bz2mp*^^^1mK$COW$HOfp -zUGTz-cN9?BGEp}5A!mDFjaiWa2_J2Iq8qj0mXzk; -z66JBKRP{p%wN7XobR0YjhAuW9T1Gw3FDvR5dWJ8ElNYF94eF3ebu+QwKjtvVu4L -zI9ip#mQ@4uqVdkl-TUQMb^XBJVLW(-$s;Nq;@5gr4`UfLgF$adIhd?rHOa%D);whv -z=;krPp~@I+-Z|r#s3yCH+c1US?dnm+C*)r{m+86sTJusLdNu^sqLrfWed^ndHXH`m -zd3#cOe3>w-ga(Dus_^ppG9AC>Iq{y%%CK+Cro_sqLCs{VLuK=dev>OL1dis4(PQ5R -zcz)>DjEkfV+MO;~>VUlYF00SgfUo~@(&9$Iy2|G0T9BSP?&T22>K46D -zL*~j#yJ?)^*%J3!16f)@Y2Z^kS*BzwfAQ7K96rFRIh>#$*$_Io;z>ux@}G98!fWR@ -zGTFxv4r~v)Gsd|pF91*-eaZ3Qw1MH$K^7JhWIdX%o$2kCbvGDXy)a?@8T&1dY4`;L -z4Kn+f%SSFWE_rpEpL9bnlmYq`D!6F%di<&Hh=+!VI~j)2mfil03T#jJ_s?}VV0_hp -z7T9bWxc>Jm2Z0WMU?`Z$xE74Gu~%s{mW!d4uvKCx@WD+gPUQ -zV0vQS(Ig++z=EHN)BR44*EDSWIyT~R4$FcF*VEY*8@l=218Q05D2$|fXKFhRgBIEE -zdDFB}1dKkoO^7}{5crKX!p?dZWNz$m>1icsXG2N+((x0OIST9Zo^DW_tytvlwXGpn -zs8?pJXjEG;T@qrZi%#h93?FP$!&P4JA(&H61tqQi=opRzNpm -zkrG}$^t9&XduK*Qa1?355wd8G2CI6QEh@Ua>AsD;7oRUNLPb76m4HG3K?)wF~IyS3`fXuNM>${?wmB -zpVz;?6_(Fiadfd{vUCBM*_kt$+F3J+IojI;9L(gc9n3{sEZyzR9o!_mOwFC#tQ{Q~ -zP3-`#uK#tP3Q7~Q;4H|wjZHO8h7e4IuBxl&vz2w~D8)w=Wtg31zpZhz%+kzSzL*dV -zwp@{WU4i;hJ7c2f1O;7Mz6qRKeASoIv0_bV=i@NMG*l<#+;INk-^`5w@}Dj~;k=|}qM1vq_P -z|GpBGe_IKq|LNy9SJhKOQ$c=5L{Dv|Q_lZl=-ky*BFBJLW9&y_C|!vyM~rQx=!vun -z?rZJQB5t}Dctmui5i31C_;_}CEn}_W%>oSXtt>@kE1=JW*4*v4tPp;O6 -zmAk{)m!)}34pTWg8{i>($%NQ(Tl;QC@J@FfBoc%Gr&m560^kgSfodAFrIjF}aIw)X -zoXZ`@IsMkc8_=w%-7`D6Y4e*CG8k%Ud=GXhsTR50jUnm+R*0A(O3UKFg0`K;qp1bl -z7``HN=?39ic_kR|^R^~w-*pa?Vj#7|e9F1iRx{GN2?wK!xR1GW!qa=~pjJb-#u1K8 -zeR?Y2i-pt}yJq;SCiVHODIvQJX|ZJaT8nO+(?HXbLefulKKgM^B(UIO1r+S=7;kLJ -zcH}1J=Px2jsh3Tec&v8Jcbng8;V-`#*UHt?hB(pmOipKwf3Lz8rG$heEB30Sg*2rx -zV<|KN86$soN(I!BwO`1n^^uF2*x&vJ$2d$>+`(romzHP|)K_KkO6Hc>_dwMW-M(#S -zK(~SiXT1@fvc#U+?|?PniDRm01)f^#55;nhM|wi?oG>yBsa?~?^xTU|fX-R(sTA+5 -zaq}-8Tx7zrOy#3*JLIIVsBmHYLdD}!0NP!+ITW+Thn0)8SS!$@)HXwB3tY!fMxc#1 -zMp3H?q3eD?u&Njx4;KQ5G>32+GRp1Ee5qMO0lZjaRRu&{W<&~DoJNGkcYF<5(Ab+J -zgO>VhBl{okDPn78<%&e2mR{jwVCz5Og;*Z;;3%VvoGo_;HaGLWYF7q#jDX=Z#Ml`H -z858YVV$%J|e<1n`%6Vsvq7GmnAV0wW4$5qQ3uR@1i>tW{xrl|ExywIc?fNgYlA?C5 -zh$ezAFb5{rQu6i7BSS5*J-|9DQ{6^BVQ{b*lq`xS@RyrsJN?-t=MTMPY;WYeKBCNg -z^2|pN!Q^WPJuuO4!|P@jzt&tY1Y8d%FNK5xK(!@`jO2aEA*4 -zkO6b|UVBipci?){-Ke=+1;mGlND8)6+P;8sq}UXw2hn;fc7nM>g}GSMWu&v&fqh

iViYT=fZ(|3Ox^$aWPp4a8h24tD<|8-!aK0lHgL$N7Efw}J -zVIB!7=T$U`ao1?upi5V4Et*-lTG0XvExbf!ya{cua==$WJyVG(CmA6Of*8E@DSE%L -z`V^$qz&RU$7G5mg;8;=#`@rRG`-uS18$0WPN@!v2d{H2sOqP|!(cQ@ -zUHo!d>>yFArLPf1q`uBvY32miqShLT1B@gDL4XoVTK&@owOoD)OIHXrYK-a1d$B{v -zF^}8D3Y^g%^cnvScOSJR5QNH+BI%d|;J;wWM3~l>${fb8DNPg)wrf|GBP8p%LNGN# -z3EaIiItgwtGgT&iYCFy9-LG}bMI|4LdmmJt@V@% -zb6B)1kc=T)(|L@0;wr<>=?r04N;E&ef+7C^`wPWtyQe(*pD1pI_&XHy|0gIGHMekd -zF_*M4yi6J&Z4LQj65)S -zXwdM{SwUo%3SbPwFsHgqF@V|6afT|R6?&S;lw=8% -z3}@9B=#JI3@B*#4s!O))~z -zc>2_4Q_#&+5V`GFd?88^;c1i7;Vv_I*qt!_Yx*n=;rj!82rrR2rQ8u5(Ejlo{15P% -zs~!{%XJ>FmJ})H^I9bn^Re&38H{xA!0l3^89k(oU;bZWXM@kn$#aoS&Y4l^-WEn-fH39Jb9lA%s*WsKJQl?n9B7_~P -z-XM&WL7Z!PcoF6_D>V@$CvUIEy=+Z&0kt{szMk=f1|M+r*a43^$$B^MidrT0J;RI` -z(?f!O<8UZkm$_Ny$Hth1J#^4ni+im8M9mr&k|3cIgwvjAgjH -z8`N&h25xV#v*d$qBX5jkI|xOhQn!>IYZK7l5#^P4M&twe9&Ey@@GxYMxBZq2e7?`q -z$~Szs0!g{2fGcp9PZEt|rdQ6bhAgpcLHPz?f-vB?$dc*!9OL?Q8mn7->bFD2Si60* -z!O%y)fCdMSV|lkF9w%x~J*A&srMyYY3{=&$}H -zGQ4VG_?$2X(0|vT0{=;W$~icCI{b6W{B!Q8xdGhF|D{25G_5_+%s(46lhvNLkik~R -z>nr(&C#5wwOzJZQo9m|U<;&Wk!_#q|V>fsmj1g<6%hB{jGoNUPjgJslld>xmODzGjYc?7JSuA?A_QzjDw5AsRgi@Y|Z0{F{!1=!NES-#*f^s4l0Hu -zz468))2IY5dmD9pa*(yT5{EyP^G>@ZWumealS-*WeRcZ}B%gxq{MiJ|RyX-^C1V=0 -z@iKdrGi1jTe8Ya^x7yyH$kBNvM4R~`fbPq$BzHum-3Zo8C6=KW@||>zsA8-Y9uV5V -z#oq-f5L5}V<&wF4@X@<3^C%ptp6+Ce)~hGl`kwj)bsAjmo_GU^r940Z-|`<)oGnh7 -zFF0Tde3>ui?8Yj{sF-Z@)yQd~CGZ*w-6p2U<8}JO-sRsVI5dBji`01W8A&3$?}lxBaC&vn0E$c5tW* -zX>5(zzZ=qn&!J~KdsPl;P@bmA-Pr8T*)eh_+Dv5=Ma|XSle6t(k8qcgNyar{*ReQ8 -zTXwi=8vr>!3Ywr+BhggHDw8ke==NTQVMCK`$69fhzEFB*4+H9LIvdt-#IbhZvpS}} -zO3lz;P?zr0*0$%-Rq_y^k(?I{Mk}h@w}cZpMUp|ucs55bcloL2)($u%mXQw({Wzc~ -z;6nu5MkjP)0C(@%6Q_I_vsWrfhl7Zpoxw#WoE~r&GOSCz;_ro6i(^hM>I$8y>`!wW -z*U^@?B!MMmb89I}2(hcE4zN2G^kwyWCZp5JG>$Ez7zP~D=J^LMjSM)27_0B_X^C(M -z`fFT+%DcKlu?^)FCK>QzSnV%IsXVcUFhFdBP!6~se&xxrIxsvySAWu++IrH;FbcY$ -z2DWTvSBRfLwdhr0nMx+URA$j3i7_*6BWv#DXfym?ZRDcX9C?cY9sD3q)uBDR3uWg= -z(lUIzB)G$Hr!){>E{s4Dew+tb9kvToZp-1&c?y2wn@Z~(VBhqz`cB;{E4(P3N2*nJ -z_>~g@;UF2iG{Kt(<1PyePTKahF8<)pozZ*xH~U-kfoAayCwJViIrnqwqO}7{0pHw$ -zs2Kx?s#vQr7XZ264>5RNKSL8|Ty^=PsIx^}QqOOcfpGUU4tRkUc|kc7-!Ae6!+B{o~7nFpm3|G5^=0#Bnm6`V}oSQlrX(u%OWnC -zoLPy&Q;1Jui&7ST0~#+}I^&?vcE*t47~Xq#YwvA^6^} -z`WkC)$AkNub|t@S!$8CBlwbV~?yp&@9h{D|3z-vJXgzRC5^nYm+PyPcgRzAnEi6Q^gslXYRv4nycsy-SJu?lMps-? -zV`U*#WnFsdPLL)Q$AmD|0`UaC4ND07+&UmOu!eHruzV|OUox<+Jl|Mr@6~C`T@P%s -zW7sgXLF2SSe9Fl^O(I*{9wsFSYb2l%-;&Pi^dpv!{)C3d0AlNY6!4fgmSgj_wQ*7Am7&$z;Jg&wgR-Ih;lUvWS|KTSg!&s_E9_bXBkZvGiC6bFKDWZxsD$*NZ#_8bl -zG1P-#@?OQzED7@jlMJTH@V!6k;W>auvft)}g -zhoV{7$q=*;=l{O>Q4a@ -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 -zFc~4mgSC*G~j0u#qqp9 -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 -zPtI_m%g$`kL_fVUk9J@>EiBH -zOO&jtn~&`hIFMS5S`g8w94R4H40mdNUH4W@@XQk1sr17b{@y|JB*G9z1|CrQjd+GX -z6+KyURG3;!*BQrentw{B2R&@2&`2}n(z-2&X7#r!{yg@Soy}cRD~j -zj9@UBW+N|4HW4AWapy4wfUI- -zZ`gSL6DUlgj*f1hSOGXG0IVH8HxK?o2|3HZ;KW{K+yPAlxtb)NV_2AwJm|E)FRs&& -z=c^e7bvUsztY|+f^k7NXs$o1EUq>cR7C0$UKi6IooHWlK_#?IWDkvywnzg&ThWo^? -z2O_N{5X39#?eV9l)xI(>@!vSB{DLt*oY!K1R8}_?%+0^C{d9a%N4 -zoxHVT1&Lm|uDX%$QrBun5e-F`HJ^T$ -zmzv)p@4ZHd_w9!%Hf9UYNvGCw2TTTbrj9pl+T9%-_-}L(tES>Or-}Z4F*{##n3~L~TuxjirGuIY#H7{%$E${?p{Q01 -zi6T`n;rbK1yIB9jmQNycD~yZq&mbIsFWHo|ZAChSFPQa<(%d8mGw*V3fh|yFoxOOiWJd(qvVb!Z$b88cg->N=qO*4k~6;R==|9ihg&riu#P~s4Oap9O7f%crSr^rljeIfXDEg>wi)&v*a%7zpz<9w -z*r!3q9J|390x`Zk;g$&OeN&ctp)VKRpDSV@kU2Q>jtok($Y-*x8_$2piTxun81@vt -z!Vj?COa0fg2RPXMSIo26T=~0d`{oGP*eV+$!0I<(4azk&Vj3SiG=Q!6mX0p$z7I}; -z9BJUFgT-K9MQQ-0@Z=^7R<{bn2Fm48endsSs`V7_@%8?Bxkqv>BDoVcj?K#dV#uUP -zL1ND~?D-|VGKe3Rw_7-Idpht>H6XRLh*U7epS6byiGvJpr%d}XwfusjH9g;Z98H`x -zyde%%5mhGOiL4wljCaWCk-&uE4_OOccb9c!ZaWt4B(wYl!?vyzl%7n~QepN&eFUrw -zFIOl9c({``6~QD+43*_tzP{f2x41h(?b43^y6=iwyB)2os5hBE!@YUS5?N_tXd=h( -z)WE286Fbd>R4M^P{!G)f;h<3Q>Fipuy+d2q-)!RyTgt;wr$(?9ox3;q+{E*ZQHhOn;lM`cjnu9 -zXa48ks-v(~b*;MAI<>YZH(^NV8vjb34beE<_cwKlJoR;k6lJNSP6v}uiyRD?|0w+X@o1ONrH8a$fCxXpf? -z?$DL0)7|X}Oc%h^zrMKWc-NS9I0Utu@>*j}b@tJ=ixQSJ={4@854wzW@E>VSL+Y{i -z#0b=WpbCZS>kUCO_iQz)LoE>P5LIG-hv9E+oG}DtlIDF>$tJ1aw9^LuhLEHt?BCj& -z(O4I8v1s#HUi5A>nIS-JK{v!7dJx)^Yg%XjNmlkWAq2*cv#tHgz`Y(bETc6CuO1VkN^L-L3j_x<4NqYb5rzrLC-7uOv -z!5e`GZt%B782C5-fGnn*GhDF$%(qP<74Z}3xx+{$4cYKy2ikxI7B2N+2r07DN;|-T->nU&!=Cm#rZt%O_5c&1Z%nlWq3TKAW0w -zQqemZw_ue--2uKQsx+niCUou?HjD`xhEjjQd3%rrBi82crq*~#uA4+>vR<_S{~5ce -z-2EIl?~s -z1=GVL{NxP1N3%=AOaC}j_Fv=ur&THz -zyO!d9kHq|c73kpq`$+t+8Bw7MgeR5~`d7ChYyGCBWSteTB>8WAU(NPYt2Dk`@#+}= -zI4SvLlyk#pBgVigEe`?NG*vl7V6m+<}%FwPV=~PvvA)=#ths==DRTDEYh4V5}Cf$z@#;< -zyWfLY_5sP$gc3LLl2x+Ii)#b2nhNXJ{R~vk`s5U7Nyu^3yFg&D%Txwj6QezMX`V(x -z=C`{76*mNb!qHHs)#GgGZ_7|vkt9izl_&PBrsu@}L`X{95-2jf99K)0=*N)VxBX2q -z((vkpP2RneSIiIUEnGb?VqbMb=Zia+rF~+iqslydE34cSLJ&BJW^3knX@M;t*b=EA -zNvGzv41Ld_T+WT#XjDB840vovUU^FtN_)G}7v)1lPetgpEK9YS^OWFkPoE{ovj^=@ -zO9N$S=G$1ecndT_=5ehth2Lmd1II-PuT~C9`XVePw$y8J#dpZ?Tss<6wtVglm(Ok7 -z3?^oi@pPio6l&!z8JY(pJvG=*pI?GIOu}e^EB6QYk$#FJQ%^AIK$I4epJ+9t?KjqA+bkj&PQ*|vLttme+`9G=L% -ziadyMw_7-M)hS(3E$QGNCu|o23|%O+VN7;Qggp?PB3K-iSeBa2b}V4_wY`G1Jsfz4 -z9|SdB^;|I8E8gWqHKx!vj_@SMY^hLEIbSMCuE?WKq=c2mJK -z8LoG-pnY!uhqFv&L?yEuxo{dpMTsmCn)95xanqBrNPTgXP((H$9N${Ow~Is-FBg%h -z53;|Y5$MUN)9W2HBe2TD`ct^LHI<(xWrw}$qSoei?}s)&w$;&!14w6B6>Yr6Y8b)S -z0r71`WmAvJJ`1h&poLftLUS6Ir -zC$bG9!Im_4Zjse)#K=oJM9mHW1{%l8sz$1o?ltdKlLTxWWPB>Vk22czVt|1%^wnN@*!l)}?EgtvhC>vlHm^t+ogpgHI1_$1ox9e;>0!+b(tBrmXRB`PY1vp-R**8N7 -zGP|QqI$m(Rdu#=(?!(N}G9QhQ%o!aXE=aN{&wtGP8|_qh+7a_j_sU5|J^)vxq;# -zjvzLn%_QPHZZIWu1&mRAj;Sa_97p_lLq_{~j!M9N^1yp3U_SxRqK&JnR%6VI#^E12 -z>CdOVI^_9aPK2eZ4h&^{pQs}xsijXgFYRIxJ~N7&BB9jUR1fm!(xl)mvy|3e6-B3j -zJn#ajL;bFTYJ2+Q)tDjx=3IklO@Q+FFM}6UJr6km7hj7th9n_&JR7fnqC!hTZoM~T -zBeaVFp%)0cbPhejX<8pf5HyRUj2>aXnXBqDJe73~J%P(2C?-RT{c3NjE`)om! -zl$uewSgWkE66$Kb34+QZZvRn`fob~Cl9=cRk@Es}KQm=?E~CE%spXaMO6YmrMl%9Q -zlA3Q$3|L1QJ4?->UjT&CBd!~ru{Ih^in&JXO=|<6J!&qp -zRe*OZ*cj5bHYlz!!~iEKcuE|;U4vN1rk$xq6>bUWD*u(V@8sG^7>kVuo(QL@Ki;yL -zWC!FT(q{E8#on>%1iAS0HMZDJg{Z{^!De(vSIq&;1$+b)oRMwA3nc3mdTSG#3uYO_ -z>+x;7p4I;uHz?ZB>dA-BKl+t-3IB!jBRgdvAbW!aJ(Q{aT>+iz?91`C-xbe)IBoND -z9_Xth{6?(y3rddwY$GD65IT#f3<(0o#`di{sh2gm{dw*#-Vnc3r=4==&PU^hCv$qd -zjw;>i&?L*Wq#TxG$mFIUf>eK+170KG;~+o&1;Tom9}}mKo23KwdEM6UonXgc -z!6N(@k8q@HPw{O8O!lAyi{rZv|DpgfU{py+j(X_cwpKqcalcqKIr0kM^%Br3SdeD> -zHSKV94Yxw;pjzDHo!Q?8^0bb%L|wC;4U^9I#pd5O&eexX+Im{ -z?jKnCcsE|H?{uGMqVie_C~w7GX)kYGWAg%-?8|N_1#W-|4F)3YTDC+QSq1s!DnOML3@d`mG%o2YbYd#jww|jD$gotpa)kntakp#K;+yo-_ZF9qrNZw<%#C -zuPE@#3RocLgPyiBZ+R_-FJ_$xP!RzWm|aN)S+{$LY9vvN+IW~Kf3TsEIvP+B9Mtm! -zpfNNxObWQpLoaO&cJh5>%slZnHl_Q~(-Tfh!DMz(dTWld@LG1VRF`9`DYKhyNv -z2pU|UZ$#_yUx_B_|MxUq^glT}O5Xt(Vm4Mr02><%C)@v;vPb@pT$*yzJ4aPc_FZ3z -z3}PLoMBIM>q_9U2rl^sGhk1VUJ89=*?7|v`{!Z{6bqFMq(mYiA?%KbsI~JwuqVA9$H5vDE+VocjX+G^%bieqx->s;XWlKcuv(s%y%D5Xbc9+ -zc(_2nYS1&^yL*ey664&4`IoOeDIig}y-E~_GS?m;D!xv5-xwz+G`5l6V+}CpeJDi^ -z%4ed$qowm88=iYG+(`ld5Uh&>Dgs4uPHSJ^TngXP_V6fPyl~>2bhi20QB%lSd#yYn -zO05?KT1z@?^-bqO8Cg`;ft>ilejsw@2%RR7;`$Vs;FmO(Yr3Fp`pHGr@P2hC%QcA|X&N2Dn -zYf`MqXdHi%cGR@%y7Rg7?d3?an){s$zA{!H;Ie5exE#c~@NhQUFG8V=SQh%UxUeiV -zd7#UcYqD=lk-}sEwlpu&H^T_V0{#G?lZMxL7ih_&{(g)MWBnCZxtXg -znr#}>U^6!jA%e}@Gj49LWG@*&t0V>Cxc3?oO7LSG%~)Y5}f7vqUUnQ;STjdDU}P9IF9d9<$;=QaXc -zL1^X7>fa^jHBu_}9}J~#-oz3Oq^JmGR#?GO7b9a(=R@fw@}Q{{@`Wy1vIQ#Bw?>@X -z-_RGG@wt|%u`XUc%W{J -z>iSeiz8C3H7@St3mOr_mU+&bL#Uif;+Xw-aZdNYUpdf>Rvu0i0t6k*}vwU`XNO2he -z%miH|1tQ8~ZK!zmL&wa3E;l?!!XzgV#%PMVU!0xrDsNNZUWKlbiOjzH-1Uoxm8E#r`#2Sz;-o&qcqB -zC-O_R{QGuynW14@)7&@yw1U}uP(1cov)twxeLus0s|7ayrtT8c#`&2~Fiu2=R;1_4bCaD=*E@cYI>7YSnt)nQc -zohw5CsK%m?8Ack)qNx`W0_v$5S}nO|(V|RZKBD+btO?JXe|~^Qqur%@eO~<8-L^9d -z=GA3-V14ng9L29~XJ>a5k~xT2152zLhM*@zlp2P5Eu}bywkcqR;ISbas&#T#;HZSf -z2m69qTV(V@EkY(1Dk3`}j)JMo%ZVJ*5eB -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 -zg|^VPf5c6-!FxN{25dvVh#fog=NNpXz -zfB$o+0jbRkHH{!TKhE709f+jI^$3#v1Nmf80w`@7-5$1Iv_`)W^px8P-({xwb;D0y -z7LKDAHgX<84?l!I*Dvi2#D@oAE^J|g$3!)x1Ua;_;<@#l1fD}lqU2_tS^6Ht$1Wl} -zBESo7o^)9-Tjuz$8YQSGhfs{BQV6zW7dA?0b(Dbt=UnQs&4zHfe_sj{RJ4uS-vQpC -zX;Bbsuju4%!o8?&m4UZU@~ZZjeFF6ex2ss5_60_JS_|iNc+R0GIjH1@Z -z=rLT9%B|WWgOrR7IiIwr2=T;Ne?30M!@{%Qf8o`!>=s<2CBpCK_TWc(DX51>e^xh8 -z&@$^b6CgOd7KXQV&Y4%}_#uN*mbanXq(2=Nj`L7H7*k(6F8s6{FOw@(DzU`4-*77{ -zF+dxpv}%mFpYK?>N_2*#Y?oB*qEKB}VoQ@bzm>ptmVS_EC(#}Lxxx730trt0G)#$b -zE=wVvtqOct1%*9}U{q<)2?{+0TzZzP0jgf9*)arV)*e!f`|jgT{7_9iS@e)recI#z -zbzolURQ+TOzE!ymqvBY7+5NnAbWxvMLsLTwEbFqW=CPyCsmJ}P1^V30|D5E|p3BC5 -z)3|qgw@ra7aXb-wsa|l^in~1_fm{7bS9jhVRkYVO#U{qMp -z)Wce+|DJ}4<2gp8r0_xfZpMo#{Hl2MfjLcZdRB9(B(A(f;+4s*FxV{1F|4d`*sRNd -zp4#@sEY|?^FIJ;tmH{@keZ$P(sLh5IdOk@k^0uB^BWr@pk6mHy$qf&~rI>P*a;h0C{%oA*i!VjWn&D~O#MxN&f@1Po# -zKN+ -zrGrkSjcr?^R#nGl<#Q722^wbYcgW@{+6CBS<1@%dPA8HC!~a`jTz<`g_l5N1M@9wn9GOAZ>nqNgq!yOCbZ@1z`U_N`Z>}+1HIZxk*5RDc&rd5{3qjRh8QmT$VyS;jK -z;AF+r6XnnCp=wQYoG|rT2@8&IvKq*IB_WvS%nt%e{MCFm`&W*#LXc|HrD?nVBo=(8*=Aq?u$sDA_sC_RPDUiQ+wnIJET8vx$&fxkW~kP9qXKt -zozR)@xGC!P)CTkjeWvXW5&@2?)qt)jiYWWBU?AUtzAN}{JE1I)dfz~7$;}~BmQF`k -zpn11qmObXwRB8&rnEG*#4Xax3XBkKlw(;tb?Np^i+H8m(Wyz9k{~ogba@laiEk;2! -zV*QV^6g6(QG%vX5Um#^sT&_e`B1pBW5yVth~xUs#0}nv?~C#l?W+9Lsb_5)!71rirGvY -zTIJ$OPOY516Y|_014sNv+Z8cc5t_V=i>lWV=vNu#!58y9Zl&GsMEW#pPYPYGHQ|;vFvd*9eM==$_=vc7xnyz0~ -zY}r??$<`wAO?JQk@?RGvkWVJlq2dk9vB(yV^vm{=NVI8dhsX<)O(#nr9YD?I?(VmQ -z^r7VfUBn<~p3()8yOBjm$#KWx!5hRW)5Jl7wY@ky9lNM^jaT##8QGVsYeaVywmpv>X|Xj7gWE1Ezai&wVLt3p)k4w~yrskT-!PR!kiyQlaxl(( -zXhF%Q9x}1TMt3~u@|#wWm-Vq?ZerK={8@~&@9r5JW}r#45#rWii};t`{5#&3$W)|@ -zbAf2yDNe0q}NEUvq_Quq3cTjcw -z@H_;$hu&xllCI9CFDLuScEMg|x{S7GdV8<&Mq=ezDnRZAyX-8gv97YTm0bg=d)(>N -z+B2FcqvI9>jGtnK%eO%y -zoBPkJTk%y`8TLf4)IXPBn`U|9>O~WL2C~C$z~9|0m*YH<-vg2CD^SX#&)B4ngOSG$ -zV^wmy_iQk>dfN@Pv(ckfy&#ak@MLC7&Q6Ro#!ezM*VEh`+b3Jt%m(^T&p&WJ2Oqvj -zs-4nq0TW6cv~(YI$n0UkfwN}kg3_fp?(ijSV#tR9L0}l2qjc7W?i*q01=St0eZ=4h -zyGQbEw`9OEH>NMuIe)hVwYHsGERWOD;JxEiO7cQv%pFCeR+IyhwQ|y@&^24k+|8fD -zLiOWFNJ2&vu2&`Jv96_z-Cd5RLgmeY3*4rDOQo?Jm`;I_(+ejsPM03!ly!*Cu}Cco -zrQSrEDHNyzT(D5s1rZq!8#?f6@v6dB7a-aWs(Qk>N?UGAo{gytlh$%_IhyL7h?DLXDGx -zgxGEBQoCAWo-$LRvM=F5MTle`M})t3vVv;2j0HZY&G -z22^iGhV@uaJh(XyyY%} -zd4iH_UfdV#T=3n}(Lj^|n;O4|$;xhu*8T3hR1mc_A}fK}jfZ7LX~*n5+`8N2q#rI$ -z@<_2VANlYF$vIH$ -zl<)+*tIWW78IIINA7Rr7i{<;#^yzxoLNkXL)eSs=%|P>$YQIh+ea_3k -z_s7r4%j7%&*NHSl?R4k%1>Z=M9o#zxY!n8sL5>BO-ZP;T3Gut>iLS@U%IBrX6BA3k -z)&@q}V8a{X<5B}K5s(c(LQ=%v1ocr`t$EqqY0EqVjr65usa=0bkf|O#ky{j3)WBR(((L^wmyHRzoWuL2~WTC=`yZ -zn%VX`L=|Ok0v7?s>IHg?yArBcync5rG#^+u)>a%qjES%dRZoIyA8gQ;StH -z1Ao7{<&}6U=5}4v<)1T7t!J_CL%U}CKNs-0xWoTTeqj{5{?Be$L0_tk>M9o8 -zo371}S#30rKZFM{`H_(L`EM9DGp+Mifk&IP|C2Zu_)Ghr4Qtpmkm1osCf@%Z$%t+7 -zYH$Cr)Ro@3-QDeQJ8m+x6%;?YYT;k6Z0E-?kr>x33`H%*ueBD7Zx~3&HtWn0?2Wt} -zTG}*|v?{$ajzt}xPzV%lL1t-URi8*Zn)YljXNGDb>;!905Td|mpa@mHjIH%VIiGx- -zd@MqhpYFu4_?y5N4xiHn3vX&|e6r~Xt> -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 -z(cD%U%k)X>_>9~t8;pGzL6L-fmQO@K; -zo&vQzMlgY95;1BSkngY)e{`n0!NfVgf}2mB3t}D9@*N;FQ{HZ3Pb%BK6;5#-O|WI( -zb6h@qTLU~AbVW#_6?c!?Dj65Now7*pU{h!1+eCV^KCuPAGs28~3k@ueL5+u|Z-7}t -z9|lskE`4B7W8wMs@xJa{#bsCGDFoRSNSnmNYB&U7 -zVGKWe%+kFB6kb)e;TyHfqtU6~fRg)f|>=5(N36)0+C -z`hv65J<$B}WUc!wFAb^QtY31yNleq4dzmG`1wHTj=c*=hay9iD071Hc?oYoUk|M*_ -zU1GihAMBsM@5rUJ(qS?9ZYJ6@{bNqJ`2Mr+5#hKf?doa?F|+^IR!8lq9)wS3tF_9n -zW_?hm)G(M+MYb?V9YoX^_mu5h-LP^TL^!Q9Z7|@sO(rg_4+@=PdI)WL(B7`!K^ND- -z-uIuVDCVEdH_C@c71YGYT^_Scf_dhB8Z2Xy6vGtBSlYud9vggOqv^L~F{BraSE_t} -zIkP+Hp2&nH^-MNEs}^`oMLy11`PQW$T|K(`Bu*(f@)mv1-qY(_YG&J2M2<7k;;RK~ -zL{Fqj9yCz8(S{}@c)S!65aF<=&eLI{hAMErCx&>i7OeDN>okvegO87OaG{Jmi<|}D -zaT@b|0X{d@OIJ7zvT>r+eTzgLq~|Dpu)Z&db-P4z*`M$UL51lf>FLlq6rfG)%doyp -z)3kk_YIM!03eQ8Vu_2fg{+osaEJPtJ-s36R+5_AEG12`NG)IQ#TF9c@$99%0iye+ -zUzZ57=m2)$D(5Nx!n)=5Au&O0BBgwxIBaeI(mro$#&UGCr<;C{UjJVAbVi%|+WP(a -zL$U@TYCxJ=1{Z~}rnW;7UVb7+ZnzgmrogDxhjLGo>c~MiJAWs&&;AGg@%U?Y^0JhL -ze(x6Z74JG6FlOFK(T}SXQfhr}RIFl@QXKnIcXYF)5|V~e-}suHILKT-k|<*~Ij|VF -zC;t@=uj=hot~*!C68G8hTA%8SzOfETOXQ|3FSaIEjvBJp(A)7SWUi5!Eu#yWgY+;n -zlm<$+UDou*V+246_o#V4kMdto8hF%%Lki#zPh}KYXmMf?hrN0;>Mv%`@{0Qn`Ujp) -z=lZe+13>^Q!9zT);H<(#bIeRWz%#*}sgUX9P|9($kexOyKIOc`dLux}c$7It4u|Rl -z6SSkY*V~g_B-hMPo_ak>>z@AVQ(_N)VY2kB3IZ0G(iDUYw+2d7W^~(Jq}KY=JnWS( -z#rzEa&0uNhJ>QE8iiyz;n2H|SV#Og+wEZv=f2%1ELX!SX-(d3tEj$5$1}70Mp<&eI -zCkfbByL7af=qQE@5vDVxx1}FSGt_a1DoE3SDI+G)mBAna)KBG4p8Epxl9QZ4BfdAN -zFnF|Y(umr;gRgG6NLQ$?ZWgllEeeq~z^ZS7L?<(~O&$5|y)Al^iMKy}&W+eMm1W -z7EMU)u^ke(A1#XCV>CZ71}P}0x)4wtHO8#JRG3MA-6g=`ZM!FcICCZ{IEw8Dm2&LQ -z1|r)BUG^0GzI6f946RrBlfB1Vs)~8toZf~7)+G;pv&XiUO(%5bm)pl=p>nV^o*;&T -z;}@oZSibzto$arQgfkp|z4Z($P>dTXE{4O=vY0!)kDO* -zGF8a4wq#VaFpLfK!iELy@?-SeRrdz%F*}hjKcA*y@mj~VD3!it9lhRhX}5YOaR9$} -z3mS%$2Be7{l(+MVx3 -z(4?h;P!jnRmX9J9sYN#7i=iyj_5q7n#X(!cdqI2lnr8T$IfOW<_v`eB!d9xY1P=2q&WtOXY=D9QYteP)De?S4}FK6#6Ma -z=E*V+#s8>L;8aVroK^6iKo=MH{4yEZ_>N-N -z`(|;aOATba1^asjxlILk<4}f~`39dBFlxj>Dw(hMYKPO3EEt1@S`1lxFNM+J@uB7T -zZ8WKjz7HF1-5&2=l=fqF-*@>n5J}jIxdDwpT?oKM3s8Nr`x8JnN-kCE?~aM1H!hAE -z%%w(3kHfGwMnMmNj(SU(w42OrC-euI>Dsjk&jz3ts}WHqmMpzQ3vZrsXrZ|}+MHA7 -z068obeXZTsO*6RS@o3x80E4ok``rV^Y3hr&C1;|ZZ0|*EKO`$lECUYG2gVFtUTw)R -z4Um<0ZzlON`zTdvVdL#KFoMFQX*a5wM0Czp%wTtfK4Sjs)P**RW&?lP$(<}q%r68Z -zS53Y!d@&~ne9O)A^tNrXHhXBkj~$8j%pT1%%mypa9AW5E&s9)rjF4@O3ytH{0z6riz|@< -zB~UPh*wRFg2^7EbQrHf0y?E~dHlkOxof_a?M{LqQ^C!i2dawHTPYUE=X@2(3<=OOxs8qn_(y>pU>u^}3y&df{JarR0@VJn0f+U%UiF=$Wyq -zQvnVHESil@d|8&R<%}uidGh7@u^(%?$#|&J$pvFC-n8&A>utA=n3#)yMkz+qnG3wd -zP7xCnF|$9Dif@N~L)Vde3hW8W!UY0BgT2v(wzp;tlLmyk2%N|0jfG$%<;A&IVrOI< -z!L)o>j>;dFaqA3pL}b-Je(bB@VJ4%!JeX@3x!i{yIeIso^=n?fDX`3bU=eG7sTc%g%ye8$v8P@yKE^XD=NYxTb -zbf!Mk=h|otpqjFaA-vs5YOF-*GwWPc7VbaOW&stlANnCN8iftFMMrUdYNJ_Bnn5Vt -zxfz@Ah|+4&P;reZxp;MmEI7C|FOv8NKUm8njF7Wb6Gi7DeODLl&G~}G4be&*Hi0Qw -z5}77vL0P+7-B%UL@3n1&JPxW^d@vVwp?u#gVcJqY9#@-3X{ok#UfW3<1fb%FT`|)V~ggq -z(3AUoUS-;7)^hCjdT0Kf{i}h)mBg4qhtHHBti=~h^n^OTH5U*XMgDLIR@sre`AaB$ -zg)IGBET_4??m@cx&c~bA80O7B8CHR7(LX7%HThkeC*@vi{-pL%e)yXp!B2InafbDF -zjPXf1mko3h59{lT6EEbxKO1Z5GF71)WwowO6kY|6tjSVSWdQ}NsK2x{>i|MKZK8%Q -zfu&_0D;CO-Jg0#YmyfctyJ!mRJp)e#@O0mYdp|8x;G1%OZQ3Q847YWTyy|%^cpA;m -zze0(5p{tMu^lDkpe?HynyO?a1$_LJl2L&mpeKu%8YvgRNr=%2z${%WThHG=vrWY@4 -zsA`OP#O&)TetZ>s%h!=+CE15lOOls&nvC~$Qz0Ph7tHiP;O$i|eDwpT{cp>+)0-|; -zY$|bB+Gbel>5aRN3>c0x)4U=|X+z+{ -zn*_p*EQoquRL+=+p;=lm`d71&1NqBz&_ph)MXu(Nv6&XE7(RsS)^MGj5Q?Fwude-(sq -zjJ>aOq!7!EN>@(fK7EE#;i_BGvli`5U;r!YA{JRodLBc6-`n8K+Fjgwb%sX;j=qHQ -z7&Tr!)!{HXoO<2BQrV9Sw?JRaLXV8HrsNevvnf>Y-6|{T!pYLl7jp$-nEE -z#X!4G4L#K0qG_4Z;Cj6=;b|Be$hi4JvMH!-voxqx^@8cXp`B??eFBz2lLD8RRaRGh -zn7kUfy!YV~p(R|p7iC1Rdgt$_24i0cd-S8HpG|`@my70g^y`gu%#Tf_L21-k?sRRZHK&at(*ED0P8iw{7?R$9~OF$Ko;Iu5)ur5<->x!m93Eb -zFYpIx60s=Wxxw=`$aS-O&dCO_9?b1yKiPCQmSQb>T)963`*U+Ydj5kI(B(B?HNP8r -z*bfSBpSu)w(Z3j7HQoRjUG(+d=IaE~tv}y14zHHs|0UcN52fT8V_<@2ep_ee{QgZG -zmgp8iv4V{k;~8@I%M3<#B;2R>Ef(Gg_cQM7%}0s*^)SK6!Ym+~P^58*wnwV1BW@eG -z4sZLqsUvBbFsr#8u7S1r4teQ;t)Y@jnn_m5jS$CsW1um!p&PqAcc8!zyiXHVta9QC -zY~wCwCF0U%xiQPD_INKtTb;A|Zf29(mu9NI;E -zc-e>*1%(LSXB`g}kd`#}O;veb<(sk~RWL|f3ljxCnEZDdNSTDV6#Td({6l&y4IjKF -z^}lIUq*ZUqgTPumD)RrCN{M^jhY>E~1pn|KOZ5((%F)G|*ZQ|r4zIbrEiV%42hJV8 -z3xS)=!X1+=olbdGJ=yZil?oXLct8FM{(6ikLL3E%=q#O6(H$p~gQu6T8N!plf!96| -z&Q3=`L~>U0zZh;z(pGR2^S^{#PrPxTRHD1RQOON&f)Siaf`GLj#UOk&(|@0?zm;Sx -ztsGt8=29-MZs5CSf1l1jNFtNt5rFNZxJPvkNu~2}7*9468TWm>nN9TP&^!;J{-h)_ -z7WsHH9|F%I`Pb!>KAS3jQWKfGivTVkMJLO-HUGM_a4UQ_%RgL6WZvrW+Z4ujZn;y@ -zz9$=oO!7qVTaQAA^BhX&ZxS*|5dj803M=k&2%QrXda`-Q#IoZL6E(g+tN!6CA!CP* -zCpWtCujIea)ENl0liwVfj)Nc<9mV%+e@=d`haoZ*`B7+PNjEbXBkv=B+Pi^~L#EO$D$ZqTiD8f<5$eyb54-(=3 -zh)6i8i|jp(@OnRrY5B8t|LFXFQVQ895n*P16cEKTrT*~yLH6Z4e*bZ5otpRDri&+A -zfNbK1D5@O=sm`fN=WzWyse!za5n%^+6dHPGX#8DyIK>?9qyX}2XvBWVqbP%%D)7$= -z=#$WulZlZR<{m#gU7lwqK4WS1Ne$#_P{b17qe$~UOXCl>5b|6WVh;5vVnR<%d+Lnp -z$uEmML38}U4vaW8>shm6CzB(Wei3s#NAWE3)a2)z@i{4jTn;;aQS)O@l{rUM`J@K& -l00vQ5JBs~;vo!vr%%-k{2_Fq1Mn4QF81S)AQ99zk{{c4yR+0b! - -literal 54213 -zcmaI7W3XjgkTrT(b!^+VZQHhOvyN@swr$(CZTqW^?*97Se)qi=aD0)rp{0Dyr3n3s!40Q|jx{^R!d0{?5$!b<$q;xZz%zyNapa2&?V6XpHup!C=N -zhX0SFG{20vh_Ip(jkL&v^yGw;BsI+(v?Mjf^yEx~0^K6x?$P}u^{Dui^c1By6(GcU -zuu<}1p$2&?Dsk~)p}}Z>6UIf_t;3xI;Q!-+n*Zy~K>j|^*1_~2FZI8DApgt9)Is0K -z%J~1+74e_0t`7QkcE%3>uaNcc5Wo>I0D#25{$&3iqWYhq!fwWf&Q7)tG=^6Cj*dyH -zVV;O9@IO^?RPO3fqiD7CVF17a@${~(@kp48o9}Yem=+7e>XMe8VU@@g$h%DD0v?5D -z+Ut$@U9uh{je2vf;M{rAHy=Ddu|8Su9hE8ud5;e#FWa4IFBu0@lbT)kIjFk7YO#M{ -z_UhnpU=OAk&ToalWXHkwGoip`@1`{c+$_;-A@{BrvWGd1n0C?8BkXAcUB}hJ9ifTd -zXmGZt20UMPJ>A`K9d~etf4lL_aN-^=h4i~6pTIuc#?fUTya6@joGghByrRwEp6ns& -zd&Qr~-rb(T@gNSHuKk&*dp$9}97J6mjOctPsOd%;PFee`sqIx2e8rg2HGO8p@5D2N -zJx=u&A7;Ik{?$cw0C8-bXwMvJD{jWVnSq0IeuaU4jg5tdi++wN3k_ZD5gaT^Ec7l@ -zUa~ZunVxelrCFSv!-1zS-V#TvVX@6od@PY3xJ>bsOEg6JdG -z+K!pzxd`b3k$evQeZqTUAhmZe`x3ix`C8_(`>+zEQ}shDw<}~?e3?djT#2A`La?}p -zj0O5dtyyJ+H -zqUrjYQ4e+(l4AV;pwtqZhOgYr#WFAg%Zl0&ZaMV`k(g8ES^@~SDWgW;9h;u?1=8tr -zhoiZ1-y%eL8TN8Sq8K9aFLZim@CgG=D^T~Tb1d}pBShxg|^!mT2n2oXVB%PHm=LAMNN;P01p$$>)?(n+ -zt_o!OLxHg0))2ibNbMM!m!??fAwd5`p_L`rU%9-n#*mCH`H8X7;!s8YR -z7UZrCqE{W1h4i|mwPjfp^5UjtSi?jhvKz5ei8I0GPZvW3W6GIH3k2?0@tX3AV4CQ< -z{qH~842S+LBE*9hht~B2_$?~!>HbXJ(&b;_>F%_5bd|ftS_R))_HLz#Esy9^RdnFu -zBgJN*TpZ%VLaeq_Hqj=~1P{T;OVbKxRz>M$deAaQe4Xw1sB`Oqv5TsUHs%!O)+OCZF->(mdYr{;SrB4(jk^ -zHZpUJJoL;_=KPKz?N{}Y^7pJ`JJ&N>Z -z4bP_VWj74DedTUNKgk1mDPJK;g;5OgKb8A-ZeQTO^LBGyQvwBnMU;rV9wTj}MRDg$ -zt|n+v8Y6kiEZ0i2U!2cO(&cn2?=X_Hr!>#iWxdijtGQ0swst>+l#{rbdxYt6E`)(l -zK-pE2f}6?2XA9U0G`_uiH`uQ_pKQsee6%AMX{ncaa3&0wZcrqdm$>LNm6iVfiptW$ -zyn7m8(>gfzQYFKW{#7zG9^e^<0i0MFp*w9&FtQoL=-)E%@@9Cc -zP9_=$*WH0TVDAK@nl1s3ydV_122<5;>(ebz*twQa8o!2`5X@d4!f}PF8SWX5KQ<#O -zZuQ|zNK9)>s#<)>#vY)+HTMZch!(bdU^n;EbZH+w)yAsxiM?!Iz9LKQaW76UeCfZ& -z&G{g4dIN;I!b>@<2;XBvbcCH!LUaV3T0*)*P6u#2xaYWWJO~LkbIq~$aHvhr*O_SX -zx4chD#{l!&zjREIRfzCw^;)IPsP~fnr3%Vm`Gyh-~&Y^4uB1MIJgVi9;LZdkV -z;tGh}#^1RI=8T+!GDV6U_DZU8wUcFcLli|4uc)bS%8C-A%%PD+f~?Q(%_TW)0`NvR -z)12Xrfts1p=!g*RQ4@C>tJZR5clwKQ*@H@hGwcIRlg6vo5%{FB88gio7O9D-+%d&0 -z88=QAENyEV-ZX`EH9c>0KW}#-rJiyvuVq|ZO+gFPZf$Rv-B=@dX3*w4^SSj5J@fQ! -zyC%Z-xP)EC?5lHyq#n4UCeO7-b*}O80=5`}y2v@X#xL5mk8=;U#Z-IJ>cn`b_W5KE -z0CM~Q;A0JfZpNUVt}7Ba0OqS2fQ$$co!Dhg^Hs9>#H-mGEY9I_uQA^8j`@An8P0IV)J8- -zy=Fx~&V9q*kLhS%Fn}efjT>!tx9S08$5~Sujy`}~Wv58)7+=-SAo(hE0+24Ok0-gn -zDneFf@Xcl`&(3wUx?cyqL?>3gxsW9~sZ05O(GKM5b^N_0gKg|Ym@ZzM=4I}()T{vg -zrx$~)J*tpI0N$#&Ey_zDF~7gE$)>m(Ij*=`;-$>MU(O~yDbLqqCM$i7pAi$c`N7oJ -zdW!qfp6-&3jDMiev3r3X_wb=9q=YLZ;Chc-ij#gUZjQfuvasr__nS{NKdAbBw -zSDX-k4sib4(T^BnR5U0 -zmtS(P^I{9gar_Fr)O1zXpI|rtduN&QQ%za5UiEwLWQlkA@FBy)z5_LB_0?d~u%ATI -z=&Ne#|M)~xcXC8A5=2)8zUGE5S7LS{HSs4~AZ{Ih=Pp=_0Bd!!YT8I+Wj_lwPAzR6 -zpC*s$B>OJ-0{##F`wysfa;fH6{ucyo{567q2SegQwyri-w)#f@34?^A`XKu0pn`uU -z&yJDcJ0WzQ4DLEBAb|Ph9(7t6SR^>lop>^S7xeejx%YRDg-tV;;U^L$S0<$n8I>TczV;H-4&5 -zT?qE8Wh52_lPc7X-{!*wGh_7M8q&5&tUV`2v=T*r7aS{w@Y%`zZVN=wny{91zFK{> -zy6N=={^v>!Ud<^_e*pkszyJV{{QFAf^qtK39UYCW4Xlj+8}zBX>0Xp`1 -zhMan0#!`s*faP1m*3$dQl+6eriIhV!0w|3r7okb@9rbyt9&OS$l-%>}FWw2uahtQU -z51v1z%{yz_lDVNIZ~Qk?p6RR)SvQjzEkEBg7e7FDFh7xdT#JZI0K}&V`w}< -zsKW1!;WMM3YiKeDjtpKpL)OT;q5Bc^M7Ih^x(G+K6Sv6pkIHe~C_^j8-y%pmk^7qT -zUYI-ZC$yq>TV&m&q`E41-pIUic2@0;)u^P>BTZ9H;g-o%pc>2dP@egvoY8w^Y|idJ -zRt_E(&gS|SK2PITHWtqM_B@=9>ik~s!9I#JNX`|p>bZawbmhCZLSqhETMj8t219ao -zMm9drVP#=M?`4Fbnlq?T#3Qvei7dhctcJ-9F&V-EBm^<($!9tWv)Nc$Dsbs!M`bQf -z>y43Vf}fDD##=1L*jB-t&x -zZVPr;U3yaKpab^Ek8cvubvkv@uAB)QAFNLUmK)VdrW;N6pb&;!btC7C%kAPrpa02d1c|Oku&)PqpeJo(Q^Yqz{aX -zwfQ4OM$+(OzPlm>p=%MVdvklYGFuiQ2U -zm(W$DcEay%?jVKdU -zk06WOukkk%?Yl!sQ<+^@&8ktWZZp4pYjDk1B0ok{8I!iEr9&)Mu5JbIVG#cD+jvE*J7r-E?MQ<%4G4G`tU(9*$7eXT%)KXb$%^bJIqk3)f!GHu^U3FG`}z -z5*qT@rr07#9n_Q;%Zbu6S+PO8=!A#unVJ|I80$N>;M!hX8t%flkZ7(vH#uUji6`72 -zAE!j~(RAe?BR`X~F~@HWFwMZQffSth&eD&$r~d_sJD@h9(%Dnha$nohXX#lcZ}yYSa6@i6&aNd=PJiVs`JzkN0P8>}?&4(-C28Wwp_$Kj6H_R5{ch}o{u!yvk -ziY8X1E2udz4&M*NTO<(Z4)&E@l+f*>Cr;!?j7LI;zWPbaU6yHEGOygY0ykb1Yymb? -z7`(tNL=)_iS3RmbhXHd%(5xW%TU7%&wZ9g19U0e&yKy6xfV&deCr8JGX5H&245TR#;AZ$ZrRyRc=NhTka0F -zE(C)7ZH#x0-{o$0q%9GH5}#0vOFzM;Z%~Tg)!v&?_E@674X=&rKhEB$ydk?^=)b`{ -z=tE*|g?_)KjR4hU7IlUa)27F9yu(v@o^Bix!^ZMRT9da_JAGOq1IIjV^Rsm&*xXd@ -zZ$azq>oCbOC@wT+5*{>U{{;FrU*|#MK5>;Us`V+?jJ}=0z1EjeyLgO_2)5yi_w^R> -zaA2*YpF?u14h=xZkfNibNv7JHMHVKk9yD%`O2}?m!e;AZS=qDsDc2Y<=@8t}s_(gq -z@BR>HBZ`nL+!-MU)ZnGJM>J2g(Yp^i1?%b_1v@YT) -zb@oI~8=DSUb|;&&zE|gMCZFzIz4OpGM>1zh7KGPE7AE}ZxTg(w?WW$k1BOB_VQs~{ -zB?zK3pgi1ZwR^Op9q^fM9hP${R+Ba-8%?YjDny#OpWAkYTwRG-$Guen2jy2h^M%;a -z45j9D1Qj{qr?$0;dMq--dyT1Q -zRP=pw0f)|e|K4k{*`lrc&ZGAEvJe=wA%BTPt*GcQ^#}oBlO=PL9noEQGu3Yb!j0sV -znN^-RcL;?V+cxAHj3L~xE3G?cpDpYubLvLQYF44x7?#8#L+?U4SOWr^?7s^3(X~s -z%p~`biV0K9W+<%UiXXq2TLKCW_cS67S^bSVh*b!m_fs<32}rK`F!*53Bj!(siro(! -znHl{eD?PLoXsmvHU_mx2y~^mV-j%!X-hzvq-LT**r9#_X%-3Q+)jMhg^Hp+MwAW@1 -zv|(uAnmuRW9r*#*JC3#6-r=|4itP&tB_Ppzoq@8{YSdI?T@61b%jEHTb#;?e_pt}* -zA)6PevRssjAMI4Z -zDSI3|iA14*Dw%?Ho{H`E7R#DO;Sb%N16^Qr#heT@o2&raa9+KaN6!oP8a6o3n1eXX -zLma;mu>gyBau7dETp8)Hw^L(`XH$cwR`lvp1z$Pe2v#*=T$aZt9*XRd2w`r7M>nbs#qq8*v)eA)1U+rP4NW+42jP+P480(80rJ -zW%u&i%#&A%2cvw59mZ}m1_q0d6=okiJ2eg?d$1%Q7d#IWzB;i0Z6#;eluVel4g%p6 -zXi!lybOy_Upf2!m(%MX`nRRZi#L+DOfhDp*uybli@s6b3X1_GOV@J3o-P6WE54?+! -zdaK)7Dc@_A6U9^t2IFTX6vVcfxdD5$>v>&CVh3GCFMfUQV**; -z!W)R4dkC13NlYnE8pvxGujq$orL9fsq${&dVzzbU^9K?)9Ot-J)Jl<&f`Ei!?>ja< -z&0M)=MU65R;zJx*IIu)LB}ENBLbygQfB1Y!y+Ktyi&ZVB#SW_FR{ax${qq;$E49d6 -z;WEPT=^YWzAo(XI=x4~)M-N-T2U$4UG&ppEkiEoX0iMfV@7fW*2s7aIrT#sH -zF{ZDcPee;m$W!(f*%osjh_3>pJ3v2gbLR42VYe`5Jpq1KtGZuX5F-hDZ$WSk@MDd) -zXXl8E^S*wLR9+Om{42ZKSV(TLcl#e)(TcSZHiV8}Vu7@jGRRqDHwFalcMfLVISgBw -zGy7T>byEB4Nxw5;_oTjX|Jm(X;90~r0s;W200RK9{d)!bN4G~LWoxK!C1mdC# -z>|}0h^IxRDf~F)UKhpQK$<~rng?&@=x@Mz$sO81_zNREU0tkL%5DKmrnN&Q!O#2#i -zf^@`>M4#Mk9&azMG8bd;d?}pQYMSE*jpOP>52`Of=THUvq+S&mtgQ6oB-V^~=c7Ey -zt2Ogzj8YEW&S`iKfr@%(4Z@qxW;vzw?Y$v$=_MQsM%witHuZW~q_6qhjU=`&{M+5O -z9-ilvkj1b&u2T7ZOkmgf|cRsdlxFgQK#UMv&f*VLyvem7U&n9E@eaiR4vUe=EKBt!`1hVgqQ -zmdb~my*pk-J~J*mBn&~mG9#*W7+VC`x6G4EPOMfhT2W0xxkpTyM~^^(M-z~j$O{-0 -zs*p7K$wlKuSSoz}FiK++Ln@TFfT+l1L&5@^MjU~!*Twak08I>c85=^jSPR9q)LQSs2!5vFzQ3~zGU=`whAtT?(orpa$#q_1 -zp!jv7j0T|$>DEFSs#q?$ -zbgvQ43cUA(eB%^&uQTd{CbqrFrJ37byNTctGXJ5#OiHI442Ah7@WuVp+a+ga%Ti{r -zM+1zu{NU)BLTPe4Dj$aL3KQ42oy5HaN9)b#bB;uTK@Ov!R)}#jYM^Fixkc_WIcnP> -zpx%0t3l=kdM{AdE85ag=#h$cTYGc`)2@NV -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 -z=o9pk{LOI~qSp}G -z)ki?n9BRtwaJDHS#K`3!Q@~($VQhoXz@vf@L-~rsdpqlcMEA|>8J%v*TH7A_+3fBe -zq{Ao6q_Xonq9KY{t5UpJgGiBxeO3wN2L2H>HEWw@t#WnMK0C++x)xoh%E$e+1l_Jv=S -z32+|8nKPuuOv?;bL_j^2_uyMX)(tI0-4bZdL -zGuBZx^QLcf-Z^hMus}KPjW|V`{w{tlKSId;h8#|Mk;{JsH)9MNDXIa6;fu0x7)Zpz -zDS1iR!=66}m5{L~WcMaQefcI|iz#na;lz0Tl=y4Ir_t}g4{O!>vTM;4C{{TSU_S)4 -ziMF!tp7Kbw`ER7~u;4-w#$QR!wp|ISzJtC+wQg9Uz}zlDW(qiiWi&!YKLCLo;1V8> -zc*FFydcjoS`>cT`LI(!VVraMTjg(Pk)pJ -zrmV9EEs31VlOa=HIPSLb!o?C7jDouHni5sU4F5b&$kL~#L0wfC_=4^gm666|b572MjQs -zL~P{DD;&?h)bWtTA14!^&c_M8fm -zw-U4h7Z)$@%7BF3%^Up7iE$ls<4k(hyc~ez3HJA*83=eav!+aVml5l?H&xB4AYDjo -zg6cOjwl#M%os(r$P@|Cq204dQl0s0sUkGVSj`;dkL;?sn(22B1p=?Xaig9AB^pp9t -zD>2xDJ@Cdlp~G=|mEZ=>5Qe -zP5-Y{8kF#1J1>Vc(vvbmQA0m$CzXnr1tF{&Y)elPYy=LE3vNR4QI(icEoq*I6!jDC -z8-y`5i2DirSrB>B42_`H5SyLtc*CCaK;irS{SLhgCz~L)YXX#FN9ngwN+KUXC8Qn7 -zDX^JjhsPf`s}~wm^2-%{6?|Zwae!g-1gh>_{3=z)+OrqEUVC7_reuJ}b-T!f!nYNSkQ4?wR&ktwh5%!f -zXDjUkE&x7Ed9hr-VFc -z58{Bz_B@1^28e3NqS$w);rh0iTJcb>R$~tG{@&1nZW#HrXqQtEg!dQtZ0^|_(m|oG -zNaiEdvbf0@hd`hYpTajdNs15NeNrVDi&!${K7|pqgt(99R|auJXporuZ@eZy_ -zdi-ZZ=2r#C(GC>WaL>gnEbvd*&-~pEh7VE8x9G^v`DFQ`)-M7&Gb$7wRfa`2}YwtJ@ -z>BXDMa!xISdxyLLS#7?nmtL;#<+aeK5^qa$3s@k-^kk$odB!hn*J97%reX${7todQ -zBdZoqIqYl1*;bt9W2Os?!&ZQ3g%atB|IjAvMg$7XHe -zV+Z;PR~IQTkoQdTa&6|+>GgrPHt`K^eQA?Ke3|iaDK#67YRL>hUl!@i>Z+30T1Wg0 -z`%3b4PwDY7nG)0c>MkTX=YV0BGW8q^dN3<1@74C)p4n*^mGU7 -znDz`y@v!&AtG6>NaTiBw+PaIL)OyGJ*h%ULjsx`_mj;#K;lr&-gt7o5%W2PMPqSef -z_taY1$7)HvWsFvb_259 -zIG3P1+z!mjia#+mPb=^CA67;xmE78h%AZ5z#hVfiRa{K23hC;JmGhD~@dKu$mXUx#p&`BuShfqjsi1=S+FiushWf%9hqSukXa~7$sDxgF8-drUqqk|k!yMZ%IcEi(k8*?lx~ -zVzsQn!Ph0AazfTio4!2ZnlT}_ss_(8%qs0MT^;XkLO)5g>+E$POk4Q1R`JSnCEO5= -z`*h!yDTrB|znNib9Ey{HrjX~sYN^w+Ou*4{Ji8$_!w5nEX9%+R-!Zt;s0V%!1l;X;xT$pp{^%?saKshn# -z+st|6o`iZ{rDvyXRbk&W89vfVSDCTSJ}f-lq_pW9oG@N5a_!}K4N&EFOQ+BjyrVdZy|DCX1)xe@ -zb7qU_#6RO1T@OP)Y@`S<>Jw_;hvLAk9v1&HY_XDBQS0Ed^x$o1jm$CzqT9{f`fbuR -ztKq<16NNub9Jrd9vE;pAbLQymi1Y3TH=|QLG*=DX7JRuVS@BqEWoGFkS;x5^`*Fe+ -z!(H|%IX}m;UJa@bNCm4eX*UA>K8TlMA7=>&zMLc`S$or`?kt`k7e#UgR>$Se#WmUk<8At4j36`;2Y?Q&(o^D2?wfDs$`As -z!m=wf?s#dX7ieHHw(InLidG(sf^)T8e!ohlFcuJs|7xiDq#X}rE}(h!jB?ctznaTw -zW{7b@bvFBx6~R=O9lM9tTOssqTb?&ye%ApyQ==amDcr8gtvK=2Nh>^lbcT3W -z5JSFMF|(x|_VR(pq5Hf}V!%Ud?)QsXz&!1ul*VkX$$YTL^jn);z1|;7@g^3mc91KG -zbXq~#a8M>~fBCs>DT-Z@(^Z)&&P0)hQPp`eJKSs1rbKob3-O-vhCj%lsiYg69H_Mp -zWvw#d7yDergTfJ1N062Mw7bR_d5trqj{@5wA -zTD|4jv&M}?w1&>{;RBFrj9B2vwauin+wkC2df0W=S91h@x9_1Uy}@F+f5c?%o}QCm -zPPsi6YzPq|PeHAut}QIw5JhP86#-Yc{GDa@)^Cr2nzclj(6`(FTx44^FEeu+U9o6H -zHMROwpFP; -z$Lzi1G<>&_%n%+s%HGfOf0FC$2I=-9KT!$1I}C%`F{-x28ldWLO_{9)Sg%Uvdb-ue -z_`VdN{ze;U(T8g}K!U*^%JQ5QMSVoP2JA!-z16@PDqs`g7JPN=|6&rkrNJ6$Kr0x= -z_saCl+1o~Kdr`jjPQXc_nbZe;>IUqI{Ec44x=^jF-)X++`U`ds+35vD}J5^;pTVg2M14sFx3$ZcU#rSYmg*K_y -zfGcdsR^!%-FEB3j?xuith}9>uaGOnOR+r!xt>NuMAdmhJh}E6#ra!=5D0Z>-s$-#3 -zf#9iy*yI8X!gwWV>x`4(OxP~f6hpAdeH?1PF7SLpdYNK9VSQAKUh|jaukRJBQZM%b -znnoG!>27>A0b5|5gJF?pF|RGXP(mP2aj&6ZN1x&VR>p>z+0u7yWOF5B@pO9Yvh|4I -z!0(x|tuDcKMAv^Sb#nhb`;d^m!xt`k=LwRR(RBC14ryl! -z{LROHg{4n@%`GO{1MWMj?(cplnmGpaXotQXf}H4OOKMy{7%ae7CJhD%CGzY?B{fMoC|1M02`>PF_5%g_qv -z#PFrk+~^7#M{06?W`Otboufh0QZ|W%z -z>_gw(z%)GgUxtaR)~35-6ah+kbr9@_R($dg2~u!xBG!8SnV=d@@26B&XuP2RWHWwQ+ii^s0Q#nz(1-H|vZXr&Qvw+SL# -zsNkw-T#0O14`ExFBPW4GgBBA5KCMo2*@Z1FOr+T*lPfctK -zWa0+c56Xine#z#v2q3rOO@qXUAN5vMsfeK -zWRtdrv`~ceQlmyO{DsCeQ#B8cq?*R$dZlpu?2!eDF46WnJ18Pvkg<}-%Y@4o_Fz8R_zJx56w@fmD|HIfj -zMQPS0``%e;+qP}H(zb2eS(%l#ZQH7}ZQHiZlW)Jhzumk0>~EZlHO6zXZq_qn#*By= -z|6dpsG4bTO5=-};R;xo%1BSqeBxlta85gs6koVjRvWu2cRmmFDX -z2AWx31wXM!8jb=fiUJmu8Ww06R|7_3Xtye(LD?=!=hySCsbKBe>zRF$e+-tffpQM4 -zbXrd%;S|m6HIHMlN|h*XS9H4-6s^=eWH2J$e(;zt2a8N-GsLY)sJD&9@}PJ>YEvkx0= -zrQDHbKKV1o3d@?!RybN8e~3SR$--%XnacdsP@6ujC|taV)Tz7{E~l!KRYk9g4%ZcWEzdryaimVbE-g>co&qS%7+S8BC4;z$|=VEwBT}0b< -zkfg0j0uvz^@sT-=*~Ks7n8>b^7RhUj3BN{_d7yh%>aqKIMG;VjPz@>CZ<75epBFdM -zfWVF2ocZS3#jW(%Qm?XC032Hc;&PS9D -znaP>obCz|f8xAUW-RRr_C~V3lvd~I&|CC|ueCoNki79CjpNwTRgqVVzFdm55ZdQ2F -z_yNi?y^yV9OeP0hTgc^emwGg^)<|i}$ok;cX_pFjj0a=}0_P^{vjV$cN&7S{eZA13 -z2Gr8sirJmepvBij)9LbCjGCIJ{iovo(`B@jyx6vr8LB{?eca-Ajl&ksT!q$Co0NP! -zX@eYiCA+iW%1_y}#XO`rPJJ8kISuNDGMT`|#hn33qZ<&i+GnLZ3z@AveOk%Dv0FtK -zSC@tYaT+dTYh(NJ%t@U7@>2H&M(M(%WB0_(V%>6Pet*i3h+kE+ir<`prCGibCm3&; -z!r&8x#K-GKJueBAB5J_MsksNri=s2+&B|NnclX5VBOXW}0p8A%TNtnWQQcjvS{^*z -z(iW(Ic6FG@y^2S-wr$J2(5@6;Y&u2LJe50TnVWSFz>clY0sF$)hz*kv7MNDjPU7yY -z0tHtB18<>euXAM45ObqHHB>~G}S&CdXV^H9n3B!Z$d97w+ -zKd1f1+dnPl(fi()8g;{L67lvw3~GQVeZrp@tof?oDK;T$LW1+MWdIZLc5TZWqAyim -z@O?xNu6ilgt&`P7f-@b3ZYE>Wp))bDk$EeLRS+$KM6{2D8$ad#G)clWo_SCkna|*^ -z)ChG1Eof^a)KtB=FPh$_@%Tnr1VP*+b0FAMj5Vnf^%;E -zmBh7-Tgh{|=4uI5R1OsBJnFhV--;@C3I! -z&Y)G91H9Wa$S<)2ygMw)uYj$7wQ5DsvUk)krGq~&IP*DGjnNWaN7)qbj)_|+PVatO -zlA0b;kwv$3JBFW$dZvSBrWIK7?a~u6BP&9?!A}D;%YL82cvSDdN4s_`l|N~=g4R9W -zd44q7wu%bkf^79F@{tttShmrPnU*9LiqKeF#2vNAx5M)6z@sOJ3`A6utB|C~3T5-ZPaaJYO(k(JCe -zKGlLFWv#14b0LnsF;tQM)^md6*bnVkRms`Cl4%)jcSAo|e;D9Q`}`j3X+H;MBZ$|U -zMVm#y)$iGFQZ|;JCSO5Xq^9th-szr4lL_6Xp^3+e3ngNT_m)ht9aw_aO+rp&ZP}d& -z-jhgPupat-jmR~2;^1NavHX(ymN>e1cFMY8J22-Efv3JHL_suzeo<@3d+)qzzQWrd -zk=toq;i>gN;Seu%NrE(ZK%Wani)9!Q(dPYXEY=e$uQYqUFLsepka=8fDcesZE}86x -z*&_a!&*+QCvrJew^>s!tajotWC&@NGL!^t)qB{Eqz19~sbYooH$=#BSz`wSHYivlX -zbsH%x)T-wXu7N(u{n7uVttStVdD^JP9i%|JOU_a_e*oG&natl|uF1F-(}#wKG~_fo -zSb%}rr1PfcsBkT=TLT>JX_a{AqNQgXY@h;-VrpC-`xLIY0r!-*P_UCb&k}q8N`jq! -z(UyZ!95BwnCJ{%+gvJ`jvsf&lT*M?ZN<22l?g%KR@Mm4l+G658n8;GEB@-xZuR|<@ -zoH@wdXv+Q|H^*=KZP6K*EF>YYmg7qvS7!P0b`93h`ux1nnqEK6{LMAvN@S8!cWg5{%OA8< -z?c%wT?9mYNWIrbmZE-2p9jf4}B0&z#y$(6p)kh>zqqopI#;kaHS2*t<7ic%mFywAG -z8JfIe?gv81W>n+ZVstQZ9w~!~s@SB3?YHzVqvf!3&qA!hSkp2@c&i+07L>M`4zJYcSS`;)Io8^1X>X5 -zRA!vYg7)vMIhNSh86Bz$s%Lb>5+qG$mQG0JAf^pkF&-cEtT{FW{|MEeOy9w_BGn&G -zL%)JLC_eiHgjKaF=V`w-$l -zy@tJqS8)m!FC2p0ftp1<*^I(bg4}@}-_r>EY(2DDxj6b5(t)D{Qew19T3|-F2!wBo -zB%M~_H^kC%LzpPKV2-8@%5|AeoriS{IN_c(2XdYzng0puJZJ1gky8w0CchStDR+%n -z(FrCpntx{wu_5^stD7*H^1#`OCnDIdw*YjHUy#DzX2yr(s}4mWu+1FkWa~2g~+YXzQcmz!8y_lUt4jWODJhF@GSY?gR -z5CB`Lq6``kOO13bnl0{Yjv&e=bCG?y)d3i1Nb&1IrY`}#JW6p{(Lp^VMdnbk(5<>~ -z{C+yrIKc2oME~C0J65))5}VS$H#++c3MewOfeu;0;}=(HqQDkX -z17UE}`6Ni(RyFqH1aLZY?5 -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 -z@|KOJlYd^Nezid>wCmCE4DG -z+*DTvk^qlhtD1}$nnMlfCkzri+$wo+3+h^TV)O6vLwx=rgwG^=KLmqVwT#?mg7fyWU`0Nq;In-CQ81)g; -zTB!J2^dTp~fk_tC_*v>55UWVzGY2m8348%BN^S2_RFD)qV^n0w#d_qF~ivNiWM5Yx_&zPoSa)Wc3<1!>rkXrQHx(4Pj`7I+`_lvt2$1yNOw58U?87Fg26?Y6BrBHVT8A%5=l>u+-o!+kPXS7^ -z7vn$yT9prkZ8a*ko<|HuvB^WM>55trDZM2*hisppC|9Vnbo$Ji7^|AhIHzp<0IX{! -z8OvK3D6d7C)C2mmhGquo#n58>E&X!d>bSgMv~rVpS=Sa_74LCX+ItAyWK_fAFUizw -zb@f4hme$*}GYFw&AH|pkM1x_~g(gWtZ&!FFe~zxRJk1wcPI?{TSjAY0)cjgOQy$mZ -zvc6zMVhr#R0E58kYXQyZ3`-wcwR6Ff@}R>aUQX?dZ{ -zr_)c~R)6(UzxUF(P67*yJU2FJQ(()*(fz@;7=THo#B^524@|3ysncciQAXzUBT;=LDT;ptPL8uo7+X_;{CbUYj%zvAQj1*q0r<|jWs;+D5fktH5N0j{Sb -zqV*gKFH(cQ)8ZEc&;jouFQQ;3-75(p2_3Kb`uHk9sk=H-Wm|YZD(PvdHOySu-I?A_}C^uh=Yu*C()oQPI5Fjr%Ckt0>-G=(llFVSdMG!G4a -z6Rd_aQN$?=(+g>~&ew=2{}pnyJ@!2Ix$zP_w2aq~w(d4e}dInH_$nQNe2 -zHwM?9D|ZWbov%*1hbniAhJ(Iy#RhY}?h<>wPD$>FlDn+fxxwtyOZ3E8BEYt$C;3mfDWyWocm(?)3 -zXYTV(KBl|;@bd`dk1YZ14De5m;S}n9231d43M3SUCR7P=fsPiR#1CpY>qSH*Cw(ND -zJMF4UrqJ|KR0T(&%LxE0{p6Uxh7Waw_6eqb?6o;35p*b0c6t0a&D%JPj!5jcnZA5$ -z!T%RC{bwpBWNTw$ZtCoy|KA*)$arg6BmwxLueGB^e_lV|ygb4Sf{dJPCI~oX24!dz -zF)yJiyCkB6sC8|Y8%1+MhMPdVZaCwN4$Yj3wSG3HdZxSVj|;80x2Y*zfWvF@V9Asb -zJ=SpS2H6aC2^>=|^k6>vI*h8tq{H8hf)}j4(rx5tS1U#n6G9 -zuVE*e(1j(%hMd;<;w;59PaRDDKtZ{iN_X8Ex@uM~(de_f=X+*|(RrKmOl!6NBtdSC -zO%pL{&QGOTw#!iuO`h|0?N27_Eoiy@;5F$NKFaWz1AQXY1`Z7Do1Rm-W(KbJ!*sNlM3Z$<|vEzXsb%k%hQ6( -zz8=d&MBCy_g;x+t|CsI&{n}E9Qo@tr6P>)`5l%E~E(sBuyYTQ_gi62qrPR2s{)q{L -z0zIRnYeQSja@wXj@p^b!9?9km<3G#AIc7<2w`e>v=m*TtP4;jAtiuxI!sH1f8I3jUYAP`{=2^4w>c?=P9D&e5=CK23yC|W4V==<(8r9PsEXDc-c -z@EPIprn~l9NV_DbQ8zZ;ufVB}30f(c_!AR$y>{~?q}O6lTcdV3Y{>Zb7)A;Zi~@To -z-@gh(FgxJDzv$n0H6>ySpc(UlTPi`tNAVpCQm=p%;PK-nVk)5Pa)4X%K}SaMqs8wE -z;Kby8r6>dx7>6B6#FSy;;slb!>u13Vi1{rfVj7?oRQ-;>VNlR@GHFZR{G)(Iob&4+ -zQ2(>ouopP3i~C(Ld9RNi_Vyw$9?a# -zjBowWzu_1EdR@rY+WH$HBV}%5ET`}AeI^jg+WocD6u;S3Hl}|c3yF#sGDzRVqB*#x -zghuVrWb!mWRW -zYrxNrN1I%Zmpn(4|2P?bl7wQwhz!;mC%~BWHsb*=< -z+UfQI1+hP+L$@^Ye8y_Rx~4Ch9Ix3prs{WF1~(nW)f=?AG>_72p7SiFQ&=+)Tj&VU -z8!cI>R$TpY3HVC7Vi$C|JzZbf?WEZwPX%|q@D)kc2fom-%-U*5 -zwSoUy<0kI(4N}_HkSFE1 -zWJr`gI;R7A>|tyaHMD_FshL}aAqEvR(newS)tZdZGiR2b@(_#^LrqxJS<38nLaqbF -zDfHmiD;Ae$9xmf}1|O5h*iR0d{B)cXSi#HS9xkqRWArn}mcpm|QTH~Qb&{ZK$GOf@{1Y -z5<&h6rVZExA1Lu(tU;4jUR*oO_?ET$1438xk#6)a$az_)l@4^~xB^$8(b<4xTzW!b -z6QbKNaiYDssRu2F{jjauX@2RML}X0U^f(jzeGzHD?`?8@n`3*e*H83Cc3V@;O;e=H -zXBoyRHTw%%eI+lwGn -zA}{o>V}<)qd4652AA_{V6vxy07RS-1<63rC=Ldk?U>GRM9A;h037NPmLpedDI}9nR -zQi3uyyw`zmZK`n+4_`4?Cz=t- -zIB`X@@e?b!FdHuci)1Ab|9f2FFqOsWq0{Mv{n~`nO6TaceCQck$96!lGj-6yEp`l}!%rU154&bJ{K`}xoWu_34r0BZ -z=EhBOa1CNTh|*9%gg0vPwA3$#nV^HHAkU3@FlWe4lzc0)2fmENei0c?Qbc^v6Va%A -z|2RoKX`1DiXh-=WWt7c+5woe9;821NvvRS4CF0{^7fz`S%mVdc5w<2X*#Ae5r#dZ{Pd{1rfZLwSkQ)|`m{t-l4{^cg+=zm{Q`&(7H&!`JVqmA8aalnj8YORv!_NoQg>y$#dt{*?PC_Bg}Zs<$;YoB -zT5?s2Lvc9OvARj|SMa(E6FL$#d-XL-Hq -zF>gHu+onno=Cn_P3n4>;mzLyx(HH!33^}c+z;To%NhS(<^ybXXd2Hl`n8N*fDtb}^ -zE>32?>Y_MQlt~CtA+ZTy9b+oaQt_41o7^E;*C -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 -z`Ek?kGwm~J>=9fhDAs{ -z+%TJs%z1k?4Kg`F(ueOOMoK!D89dsjHXPhS42MC!C_(yD?r -z=G}*9eWW}$87$@)Xfk*b1RHKW3h?3q(o?09kLX@lJr_9?^?3( -zDwR(yyTZSqeIGpxYwKym7s9;F>R -zEECECm?1+*lc{CCwFRVSsdc2LyeD&!eSmobCI=sSfsNSjD2){3zy9 -zGn29JsjEL6d7U5|$1m$HM$Q2Uj*2eT!E2FepfLS0Wv8a-)P>qmblLlJ+M{)nPaYOnj^mbxQEFF@|C`%b$vUb -zObNXZJ`58WvzT+B#&FGUm3B~!pqDzor^AT%RH?6WI(-97kS2Op#RQwikYPD`%!g|l -zLX_SF7U`s5Ol^?tmr^?z!2WPr>!?B@x$AJ;FEX+5kTR9Ea>d6U?85{X#3iV6Sgq>2 -zJKpkx0{%vfMJ|Vd+uv)56NtzF)KQI4A?VNy#u}HgS~p>abZr$2E46G}fLUHP4{F7qHw@g9f9Y7a31^ITPs2=7j;(42 -z&dCoN=FbqqIb|OFV5af^rU-5f!LI2VuEzMeB=sGj0F?TM8#%VQZj$yCa -z<8pc^evk91NemvLsf5_tTbmfUE-i5=Q*iZ11vfis|Hz40lf@Y;J;J(Td~E_%opCn` -z1C{cy+aH(4rZCw-7{mstiI8R$3OvTCryoJ#qeh03nXbg~ee3hZlgCPwrzVc|DB;rS -zT#lr3_i?!bImjUVlb5MLR7Yc@jzRSfax-y8wft)PB)`TL%BazK`ve98Z4r(y1O;s) -zoPMUu_d!HOWAx`KprbpXZCFnWw9e(wOKb3; -zZc;(>5oMRBmIZS^VxMWKdq)nwL3buq&$)H=WFMTtd}40_WQ;}8_}lISh4_a-`twb( -zeK&7WFn64ys0MjD|Ma&#;sP)uku)rX2pdQ=2HhF~ -z3p}Rvzd95Gux6o~W0VZ;H+K)M4Vy?b78_$`msUw1%byXO-|ox1U+z)If^HNOWO{Ul -z^BLLpeBt%KLKEWdc13^3=QFzQ^BUf!o8tlf9KUoc9hj0Z?BpZh!ClZ -zD2~52&*8i#OE16Gs7?(UH8?nWA*L%*rdeC#e^RF@9;dbbHp0)Riz4lylKC?kO*!~k~M&An$pHJzFh9Fa23uW>0 -zRwLM?XZsSV9WtkKm}*7g!owEq3``za-iYKZI|=7hzL6oo8Eb|w-t@{CFY1oTG|sMW -zs0_*#1a-J0Ss(6J?XHEj*U-%|Ob_K@BA -zfur7B$t7SH`ANr7y@tlFgHq7sGl(9lhrOazt)+cX8j* -zKD6vc^pr*L!9!nKw^(JCj&c+#sPMRq(RlhcZ5d{vedeyfMBa~qG1^#ds5WV}=py$EXcGs-?WX7DqQ0B~xsa!`)`zS$Frz)7 -ze9z{D(y(aJi&eU0eUa(gG>)stBb@Mo19jyF*WgOAu~lSaU_nndSU{zjZ<)-&E-6bH -zjc-DOK$*VZd3HvbC_7YKku84x74gnwU1f(h?9O&J1yh1GI$Ie|C{)g%Miteff==U_%EhpNTo*8HY( -z5DgsXjC|EtA09lhr6ma*1^h1tJ+c>Yg%t~^a{Wk5X91;FYAqW60M}u3XjZ{aF~}{d -z&tF;MLp{n_iiSoULbwasr9oj8CK|2MUnS0GN0*OW@I|X7zI<`|mC%}1cV|dO&8XZ9=g@{&CUgFr>qlZ+Ebf0S~-_qn^5EG}iQ -z@M;OFMID2g9&JTEI*r|`dd62)%J7u?VW`9jogN?A5)ylOWU!r1znL&Ah59oYQJ26^ -zA!91FzR5xT5*v9=!n7h5N8RT_)1PpktfQ44e2Zy5xW18nWNV~azak%7pN}|7I5wqk -z9Pctx;c-rmh^bGg)jZ`c!&nThh=@0$2+y_MeRB?%l6qJG8H7>-iCoGOD>5!k{Vh+u -zax%q*;s~R0j5+!9QY+Is#tF~{zbL6{z!6AgNe;AKIuc#4K@0*xYx53a(3}YTWCFdR -zXTrQLeUa0QDHh1cBtkvfl1x77y~t503$7>#~K-w4h}L9rcq -zHd!y^_~gFSeBI08&)4M0Nf4gBl9QEF@p3hm6mZ55@0sp9aJ(94%NKBa=i-rXId+XD -ze8v$R!dTms5|y7HZ-!oGPR^b}`fx{i -zTLcqkfHtz1^@X3SIdCM}!6bb2=vx+hVBy%2gavx>7%*n~!fMe7&1ckD1s=h(DysV! -z_u;~EMwLD2c$bjP#ej!Kx35!!B1RN;17b?7^qzkC@OcRcyPx&Xup->JYO({jAc@n} -zDbpj~0j-2HMX-#^cRSH&NTZLVsY)nC1$!JJ~f6S2}~gwG>0ds0%Hv67E{4cZj#DZ_e(zpMC2wbYA0dB7g?h;+v-T4|xzrL=2$ -zy(|{HYwZHkDiUSJkk7~1mG1wVB@Mz@Zs*%M@h* -zWTtQHOkmmr@Qe%6C(|2t7YJ_nxHHbmZhEl`97hKDaaXY0wVC!$LKe6p@zR^`W%0Qo -zQ?AJ9g=M)jqRUviTfH8kUFf|zZ0U(GE8-dn^UZ*u%vPJQ#c{+zU(Cvu5;`zg{bn$e;>@8ntrS!EYe=AV -z&3jEd7<{{(V3Y-^F(sqao@CJKDt2S~l}5d$x-_ybADxtmTQ+gZi4-q?5oqLo>jId4 -zUp03dr1~Bz{Ubzs-n$9Kb8k{2TjE@_u?k?krm@y6QmC2X}bdUqLSVkEZPcV$D2#X98~FfvQDz>!G8} -z1NHT0$v%GrC5vw;UJ^PCh958Ijdpbi7a=SmlMTJTEBaVeNfQl}-n%iiEWszw7V~u> -z=4_#6JMx_<_2js~S{HP(D9f-1E6`o9hw376o*}LM3?#~R?#5Mah&*L;oaP}f67VuC -z91iU?6UYkBxX~>m!Gl1ujHkExo9w|IOLC$|X;(T&nlKw!MnK>l{hZL~Mxhx0ozVvj -zk!kuAUpNG#Q9jQK9|!b(w>%#Q)|Y;Wl;uloFADN99|U3MWr#B$(5{tih{#p~C2x+p -zca{AWxn7|;@T4mkDix%K;@#qF2lYHjd~#IY|NMF)4gIM@)@J0G_zC}ET$>(VfMNfl -zV5+Q8br>O>$JgQfEsFBt8-9yfMtMaHPU#av^7GnaLWmGQldt_;=Z~et?m?w!VK_n> -z4HdyU=-Er*oNBD|0OOi~E9yMA(Uni1;h1LGeS`01qkR+Ffjvfsp99bQXm%^!nUE1r -z;3Y&YYm@B`(EOCtG-#6J42hd<>^4Zo*VXxcIQ#R33u<} -zd+(zS$d>!JosrkqZ1_9`8yI*Wj&(8ZD?6{DJ;|J%$-^>Eue32ER}Cfa&S36At|YEW -zN}T08D*!p3-J;kU(ELNq$4*^iz_{j{nMA*(Ta%)IUQ+36v#1V5{lGN@} -zJSm|XghV*JeZI71-`-x$mS&_BW)K&!%jLtQ6r-)(HP@`(6nfK-038X-ySnlOzpIkk -zv?Q?>^UA!X_H3eK5KWWA`O9Z_EodEbEl|bJ5xcAysaxMpja1o;U@kM2I$ktHXOV%; -z3lFd=}rY` -zzc`X)DF>6qp5JyV*E}5Cn!9{i`-_Pg;xrACG!>ig3S|R0E0q(P0L+MvXy=Fay6o30 -zrfe_u<1*OoHZwitQM+q~Cy76<+!6#(CU&qx*T9VEN_W^g2?Gw{G}}XAJo-9z=2OHQ -zAf`=(n{uRg>e}>fXxFZg9y3=X_kDTR_#=)UPGyf7njc1;ggS&;C@Vb(d&)mV2Yq9? -zm;=peDqdy2_fMDwXUUz$jKG?Wt;2hj(LHTILk~pMtyI7uh6*yHl-6+yo$7p -z*l-DYY2qD0QWt@fKMig*Jh;aPA~k4CaGsA&>+apO-pXLaG&OHbQ(%TMSvYA)dn}B* -zucmB&ZiedQQVi2-NzIj&^6Id#pd>*vVG%Q6OqHw6i?b|1GYyJccq!PAIodgLrz#%K -zkQMz#AGzyOL3;-Y+pDJ&&0{ROtg2{D6`3evg^KftahQ-8kOv4sZiHOgdk!Ev_AJVPT( -zRvbzflx=NV#b$q;s!FPw_@z#5RZ$~{yao%XwEiO$u5IR(JI1p -z{c)!9TWy$iK7WcaUl=*7KqYOsaIL9tkO2j2bZ_=ZRM_j*Fv|VIA}6nq2_LcdG4$P` -zEedEUyOd>CD!iYJIRpCmPoJLc5MqHilfL_X-2;w;*?-+3JkP}b)6wuRZeN4qUA!m( -zqjM$Z0J&|GX`w}Ceq_OJfAlCc^mnvxcRiBvCZmawNhkT2#g%+u@!JED$czz+__|e6 -z8P7(ahEp3`JX_j;+kwpz2;jqs%uupa{DwiyP&0)HJU?@hjza6r-$wD)4fcs<&Ku$_ -zOD-XXxN_Khcz0wQ${|3h%p~1inp)}AE}H?nPz#RBX{7p+5mK9AycKy-gb))#kyVZ;1Q~=GTVDsp(tV&oBy)V`F49g|D$u2lJ<7+quDZ@c*_E8a)=E-~RsVG* -zn1mwri)(phr-AH{i%%a61i|GqfrLPEMKW=^-}Wh?cy9oUo0l7TRG0PQGqLj>m^hVv -z9It#)Hfh^s1eg(qQ%Ttt2yFL#%tZ?tnXiStQKn)(*;YcbU#!Xwf&`CbuUBIwQ6k6L -z-qG>`wwci4lv}!0FZEHWY<)jd(YL`&gB=oE<+q*Jda-=UN}{!+0UxH$<}5M-{P{Xt -z6{h%&>Ava{*k9eggzKQLxB+f_pP_&2mh@8>{&~9ppI9+rV;fsLIeVQy|M`zY%O1sV -z?Rk)rk%JOPon!hD)Mz+o}jn -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 -z^A`Q%W%|>*Gfr;Ba^fw@qcjBytZnAv4NM2> -zDH{&ngR%?Db&W}xTH?HDKS_JpVSzP8asRnji{(g&9 -zZ?j5VqJ!p=o(S?vING2D65!RAc*9epkpx!kA=d`^`6v&y5sDilmC_;o6~?KYbg -z?d-^X1&^uJJW3ChSi9zrNY%{cVmD!xhE}0%ff0jdo}e~dhobUM<*%ufASbCT~?pdIIIluw1DmLGsOu&E%-9t~<^KlfLq -z%YZ1CgOD=v(L+#HN%GF8>KpACP+Z_{&8pF*&<_bC#k69SmDC!tOk+`ypIr> -z-A}F>J~B7plp7-VrZ(yy7F(By@69bs`g)3zan78ZWX(dg5^gp&pgC>1#^Q_Yxt&cvc -zS#R|y2t>t$a)Y6(5Ru`6m-7$u;Zz(34&$#Y+6Ph~T~9!QNqcKNKb#CZl<}LIu-PrA -zT(vnLEm$s}_l?r70HK@=wIgi3z%UuF%G4eA3LyEI$PurQY$n?m5}^>_O|#ptl|mSw -z!J{?y*2# -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 -z`wN2^?r#~MJH(0tGE4kzH>8zd -zTu%YIfd!Dn;;%zR67bjGbOS)=)=t~ZjQCF#(dVr_a#2k<01lf-FFc>tMA!90Ux$MR2rW{8~NxN2`$z10Vyx3>aL5Kac=dhyXk3Z*Kg*t^56ygD}TW -z#sw@2`+%oL@E15E!0PxLxIg>l_h`>Eul@k+diPs2k>BTGecmO{^IZMFEz|f1+}~xr -zdJg?OH_;Dhd9B|<|G6^$CR5RK=;x_nen8Xd{_n;5=Ysp&m+Q}f-yJ8v1AHGx-wR+K -z{Zt|WfIoHCcahrXm7k}E`BA&=*UCTbH2?BV_J<+#*Utamy61^Geh_3C{L;E#34Tq} -z@%#>+XF>Qu5NG^P1V8(aKF55n!Tkf%)bgJ&|9mR{N|E~)&Sy%>KR7XM{srf6R+;aQ -z|G8%I4^(j5e}VePL;B;kezN;LZ~Jp`+#f6;_P?Yr5xpOZb8Vf#Twtzf&Wj3iqCK5 -z`6l1OI@RYxyVmKj+`?jl$1Ko?Fs> -zkl26vh2*~r{7<9$|5!twTStDt19kpyOZRuSlK-~#zm}1ots3908$XIH0POGVByT0a -W0pC&I?SueJAtDe^1>pYz0sTMJrJ5N4 - -diff --git a/platforms/sdl/sdl2/android/gradle/wrapper/gradle-wrapper.properties b/platforms/sdl/sdl2/android/gradle/wrapper/gradle-wrapper.properties -index 099f1c8c0..37f853b1c 100644 ---- a/platforms/sdl/sdl2/android/gradle/wrapper/gradle-wrapper.properties -+++ b/platforms/sdl/sdl2/android/gradle/wrapper/gradle-wrapper.properties -@@ -1,6 +1,7 @@ --#Thu Nov 11 18:20:34 PST 2021 - distributionBase=GRADLE_USER_HOME --distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip - distributionPath=wrapper/dists --zipStorePath=wrapper/dists -+distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip -+networkTimeout=10000 -+validateDistributionUrl=true - zipStoreBase=GRADLE_USER_HOME -+zipStorePath=wrapper/dists -diff --git a/platforms/sdl/sdl2/android/gradlew b/platforms/sdl/sdl2/android/gradlew -index 9d82f7891..1aa94a426 100755 ---- a/platforms/sdl/sdl2/android/gradlew -+++ b/platforms/sdl/sdl2/android/gradlew -@@ -1,74 +1,127 @@ --#!/usr/bin/env bash -+#!/bin/sh -+ -+# -+# Copyright © 2015-2021 the original authors. -+# -+# Licensed under the Apache License, Version 2.0 (the "License"); -+# you may not use this file except in compliance with the License. -+# You may obtain a copy of the License at -+# -+# https://www.apache.org/licenses/LICENSE-2.0 -+# -+# Unless required by applicable law or agreed to in writing, software -+# distributed under the License is distributed on an "AS IS" BASIS, -+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -+# See the License for the specific language governing permissions and -+# limitations under the License. -+# - - ############################################################################## --## --## Gradle start up script for UN*X --## -+# -+# Gradle start up script for POSIX generated by Gradle. -+# -+# Important for running: -+# -+# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is -+# noncompliant, but you have some other compliant shell such as ksh or -+# bash, then to run this script, type that shell name before the whole -+# command line, like: -+# -+# ksh Gradle -+# -+# Busybox and similar reduced shells will NOT work, because this script -+# requires all of these POSIX shell features: -+# * functions; -+# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», -+# «${var#prefix}», «${var%suffix}», and «$( cmd )»; -+# * compound commands having a testable exit status, especially «case»; -+# * various built-in commands including «command», «set», and «ulimit». -+# -+# Important for patching: -+# -+# (2) This script targets any POSIX shell, so it avoids extensions provided -+# by Bash, Ksh, etc; in particular arrays are avoided. -+# -+# The "traditional" practice of packing multiple parameters into a -+# space-separated string is a well documented source of bugs and security -+# problems, so this is (mostly) avoided, by progressively accumulating -+# options in "$@", and eventually passing that to Java. -+# -+# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, -+# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; -+# see the in-line comments for details. -+# -+# There are tweaks for specific operating systems such as AIX, CygWin, -+# Darwin, MinGW, and NonStop. -+# -+# (3) This script is generated from the Groovy template -+# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt -+# within the Gradle project. -+# -+# You can find Gradle at https://github.com/gradle/gradle/. -+# - ############################################################################## - --# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. --DEFAULT_JVM_OPTS="" -+# Attempt to set APP_HOME -+ -+# Resolve links: $0 may be a link -+app_path=$0 -+ -+# Need this for daisy-chained symlinks. -+while -+ APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path -+ [ -h "$app_path" ] -+do -+ ls=$( ls -ld "$app_path" ) -+ link=${ls#*' -> '} -+ case $link in #( -+ /*) app_path=$link ;; #( -+ *) app_path=$APP_HOME$link ;; -+ esac -+done - --APP_NAME="Gradle" --APP_BASE_NAME=`basename "$0"` -+# This is normally unused -+# shellcheck disable=SC2034 -+APP_BASE_NAME=${0##*/} -+# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) -+APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit - - # Use the maximum available, or set MAX_FD != -1 to use that value. --MAX_FD="maximum" -+MAX_FD=maximum - --warn ( ) { -+warn () { - echo "$*" --} -+} >&2 - --die ( ) { -+die () { - echo - echo "$*" - echo - exit 1 --} -+} >&2 - - # OS specific support (must be 'true' or 'false'). - cygwin=false - msys=false - darwin=false --case "`uname`" in -- CYGWIN* ) -- cygwin=true -- ;; -- Darwin* ) -- darwin=true -- ;; -- MINGW* ) -- msys=true -- ;; -+nonstop=false -+case "$( uname )" in #( -+ CYGWIN* ) cygwin=true ;; #( -+ Darwin* ) darwin=true ;; #( -+ MSYS* | MINGW* ) msys=true ;; #( -+ NONSTOP* ) nonstop=true ;; - esac - --# Attempt to set APP_HOME --# Resolve links: $0 may be a link --PRG="$0" --# Need this for relative symlinks. --while [ -h "$PRG" ] ; do -- ls=`ls -ld "$PRG"` -- link=`expr "$ls" : '.*-> \(.*\)$'` -- if expr "$link" : '/.*' > /dev/null; then -- PRG="$link" -- else -- PRG=`dirname "$PRG"`"/$link" -- fi --done --SAVED="`pwd`" --cd "`dirname \"$PRG\"`/" >/dev/null --APP_HOME="`pwd -P`" --cd "$SAVED" >/dev/null -- - CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar - -+ - # Determine the Java command to use to start the JVM. - if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables -- JAVACMD="$JAVA_HOME/jre/sh/java" -+ JAVACMD=$JAVA_HOME/jre/sh/java - else -- JAVACMD="$JAVA_HOME/bin/java" -+ JAVACMD=$JAVA_HOME/bin/java - fi - if [ ! -x "$JAVACMD" ] ; then - die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME -@@ -77,84 +130,120 @@ Please set the JAVA_HOME variable in your environment to match the - location of your Java installation." - fi - else -- JAVACMD="java" -- which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -+ JAVACMD=java -+ if ! command -v java >/dev/null 2>&1 -+ then -+ die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. - - Please set the JAVA_HOME variable in your environment to match the - location of your Java installation." -+ fi - fi - - # Increase the maximum file descriptors if we can. --if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then -- MAX_FD_LIMIT=`ulimit -H -n` -- if [ $? -eq 0 ] ; then -- if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then -- MAX_FD="$MAX_FD_LIMIT" -- fi -- ulimit -n $MAX_FD -- if [ $? -ne 0 ] ; then -- warn "Could not set maximum file descriptor limit: $MAX_FD" -- fi -- else -- warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" -- fi -+if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then -+ case $MAX_FD in #( -+ max*) -+ # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. -+ # shellcheck disable=SC2039,SC3045 -+ MAX_FD=$( ulimit -H -n ) || -+ warn "Could not query maximum file descriptor limit" -+ esac -+ case $MAX_FD in #( -+ '' | soft) :;; #( -+ *) -+ # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. -+ # shellcheck disable=SC2039,SC3045 -+ ulimit -n "$MAX_FD" || -+ warn "Could not set maximum file descriptor limit to $MAX_FD" -+ esac - fi - --# For Darwin, add options to specify how the application appears in the dock --if $darwin; then -- GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" --fi -+# Collect all arguments for the java command, stacking in reverse order: -+# * args from the command line -+# * the main class name -+# * -classpath -+# * -D...appname settings -+# * --module-path (only if needed) -+# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. -+ -+# For Cygwin or MSYS, switch paths to Windows format before running java -+if "$cygwin" || "$msys" ; then -+ APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) -+ CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) -+ -+ JAVACMD=$( cygpath --unix "$JAVACMD" ) - --# For Cygwin, switch paths to Windows format before running java --if $cygwin ; then -- APP_HOME=`cygpath --path --mixed "$APP_HOME"` -- CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` -- JAVACMD=`cygpath --unix "$JAVACMD"` -- -- # We build the pattern for arguments to be converted via cygpath -- ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` -- SEP="" -- for dir in $ROOTDIRSRAW ; do -- ROOTDIRS="$ROOTDIRS$SEP$dir" -- SEP="|" -- done -- OURCYGPATTERN="(^($ROOTDIRS))" -- # Add a user-defined pattern to the cygpath arguments -- if [ "$GRADLE_CYGPATTERN" != "" ] ; then -- OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" -- fi - # Now convert the arguments - kludge to limit ourselves to /bin/sh -- i=0 -- for arg in "$@" ; do -- CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` -- CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option -- -- if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition -- eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` -- else -- eval `echo args$i`="\"$arg\"" -+ for arg do -+ if -+ case $arg in #( -+ -*) false ;; # don't mess with options #( -+ /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath -+ [ -e "$t" ] ;; #( -+ *) false ;; -+ esac -+ then -+ arg=$( cygpath --path --ignore --mixed "$arg" ) - fi -- i=$((i+1)) -+ # Roll the args list around exactly as many times as the number of -+ # args, so each arg winds up back in the position where it started, but -+ # possibly modified. -+ # -+ # NB: a `for` loop captures its iteration list before it begins, so -+ # changing the positional parameters here affects neither the number of -+ # iterations, nor the values presented in `arg`. -+ shift # remove old arg -+ set -- "$@" "$arg" # push replacement arg - done -- case $i in -- (0) set -- ;; -- (1) set -- "$args0" ;; -- (2) set -- "$args0" "$args1" ;; -- (3) set -- "$args0" "$args1" "$args2" ;; -- (4) set -- "$args0" "$args1" "$args2" "$args3" ;; -- (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; -- (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; -- (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; -- (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; -- (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; -- esac - fi - --# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules --function splitJvmOpts() { -- JVM_OPTS=("$@") --} --eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS --JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" - --exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" -+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' -+ -+# Collect all arguments for the java command: -+# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, -+# and any embedded shellness will be escaped. -+# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be -+# treated as '${Hostname}' itself on the command line. -+ -+set -- \ -+ "-Dorg.gradle.appname=$APP_BASE_NAME" \ -+ -classpath "$CLASSPATH" \ -+ org.gradle.wrapper.GradleWrapperMain \ -+ "$@" -+ -+# Stop when "xargs" is not available. -+if ! command -v xargs >/dev/null 2>&1 -+then -+ die "xargs is not available" -+fi -+ -+# Use "xargs" to parse quoted args. -+# -+# With -n1 it outputs one arg per line, with the quotes and backslashes removed. -+# -+# In Bash we could simply go: -+# -+# readarray ARGS < <( xargs -n1 <<<"$var" ) && -+# set -- "${ARGS[@]}" "$@" -+# -+# but POSIX shell has neither arrays nor command substitution, so instead we -+# post-process each arg (as a line of input to sed) to backslash-escape any -+# character that might be a shell metacharacter, then use eval to reverse -+# that process (while maintaining the separation between arguments), and wrap -+# the whole thing up as a single "set" statement. -+# -+# This will of course break if any of these variables contains a newline or -+# an unmatched quote. -+# -+ -+eval "set -- $( -+ printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | -+ xargs -n1 | -+ sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | -+ tr '\n' ' ' -+ )" '"$@"' -+ -+exec "$JAVACMD" "$@" -diff --git a/platforms/sdl/sdl2/android/gradlew.bat b/platforms/sdl/sdl2/android/gradlew.bat -index 8a0b282aa..93e3f59f1 100644 ---- a/platforms/sdl/sdl2/android/gradlew.bat -+++ b/platforms/sdl/sdl2/android/gradlew.bat -@@ -1,4 +1,20 @@ --@if "%DEBUG%" == "" @echo off -+@rem -+@rem Copyright 2015 the original author or authors. -+@rem -+@rem Licensed under the Apache License, Version 2.0 (the "License"); -+@rem you may not use this file except in compliance with the License. -+@rem You may obtain a copy of the License at -+@rem -+@rem https://www.apache.org/licenses/LICENSE-2.0 -+@rem -+@rem Unless required by applicable law or agreed to in writing, software -+@rem distributed under the License is distributed on an "AS IS" BASIS, -+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -+@rem See the License for the specific language governing permissions and -+@rem limitations under the License. -+@rem -+ -+@if "%DEBUG%"=="" @echo off - @rem ########################################################################## - @rem - @rem Gradle startup script for Windows -@@ -8,20 +24,24 @@ - @rem Set local scope for the variables with windows NT shell - if "%OS%"=="Windows_NT" setlocal - --@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. --set DEFAULT_JVM_OPTS= -- - set DIRNAME=%~dp0 --if "%DIRNAME%" == "" set DIRNAME=. -+if "%DIRNAME%"=="" set DIRNAME=. -+@rem This is normally unused - set APP_BASE_NAME=%~n0 - set APP_HOME=%DIRNAME% - -+@rem Resolve any "." and ".." in APP_HOME to make it shorter. -+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi -+ -+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" -+ - @rem Find java.exe - if defined JAVA_HOME goto findJavaFromJavaHome - - set JAVA_EXE=java.exe - %JAVA_EXE% -version >NUL 2>&1 --if "%ERRORLEVEL%" == "0" goto init -+if %ERRORLEVEL% equ 0 goto execute - - echo. - echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -@@ -35,7 +55,7 @@ goto fail - set JAVA_HOME=%JAVA_HOME:"=% - set JAVA_EXE=%JAVA_HOME%/bin/java.exe - --if exist "%JAVA_EXE%" goto init -+if exist "%JAVA_EXE%" goto execute - - echo. - echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -@@ -45,44 +65,26 @@ echo location of your Java installation. - - goto fail - --:init --@rem Get command-line arguments, handling Windowz variants -- --if not "%OS%" == "Windows_NT" goto win9xME_args --if "%@eval[2+2]" == "4" goto 4NT_args -- --:win9xME_args --@rem Slurp the command line arguments. --set CMD_LINE_ARGS= --set _SKIP=2 -- --:win9xME_args_slurp --if "x%~1" == "x" goto execute -- --set CMD_LINE_ARGS=%* --goto execute -- --:4NT_args --@rem Get arguments from the 4NT Shell from JP Software --set CMD_LINE_ARGS=%$ -- - :execute - @rem Setup the command line - - set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - -+ - @rem Execute Gradle --"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% -+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* - - :end - @rem End local scope for the variables with windows NT shell --if "%ERRORLEVEL%"=="0" goto mainEnd -+if %ERRORLEVEL% equ 0 goto mainEnd - - :fail - rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of - rem the _cmd.exe /c_ return code! --if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 --exit /b 1 -+set EXIT_CODE=%ERRORLEVEL% -+if %EXIT_CODE% equ 0 set EXIT_CODE=1 -+if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% -+exit /b %EXIT_CODE% - - :mainEnd - if "%OS%"=="Windows_NT" endlocal -diff --git a/thirdparty/SDL2/src b/thirdparty/SDL2/src -index 9959e840b..cd6fbfbb0 160000 ---- a/thirdparty/SDL2/src -+++ b/thirdparty/SDL2/src -@@ -1 +1 @@ --Subproject commit 9959e840b6b74a19a0ca716cc4249b6ca8512b0e -+Subproject commit cd6fbfbb035452a991b9762ecbb888b48ec84c1f - -From 6a2067b28f28ad4a6fa36434ea3746472bce0efd Mon Sep 17 00:00:00 2001 -From: Brent <43089001+BrentDaMage@users.noreply.github.com> -Date: Fri, 20 Feb 2026 17:27:58 -0800 -Subject: [PATCH 6/9] Fixed crash (#497) - ---- - source/client/renderer/Textures.cpp | 5 ++++- - 1 file changed, 4 insertions(+), 1 deletion(-) - -diff --git a/source/client/renderer/Textures.cpp b/source/client/renderer/Textures.cpp -index d1e1a8e86..5fdc17b52 100644 ---- a/source/client/renderer/Textures.cpp -+++ b/source/client/renderer/Textures.cpp -@@ -61,8 +61,11 @@ size_t _mipTagStart(const std::string& path) - constexpr size_t mipSuffixLength = MIP_TAG_SIZE + 2; - - std::string extension = Util::getExtension(path); -+ size_t len = path.length() - extension.length(); -+ if (len <= mipSuffixLength) -+ return 0; - -- return path.length() - mipSuffixLength - extension.length(); -+ return len - mipSuffixLength; - } - - bool _isMipmap(const std::string& path) - -From b3e07ef8737fd13c4662deb7a9e4544df8d4cf1c Mon Sep 17 00:00:00 2001 -From: Un1q32 -Date: Sat, 21 Feb 2026 17:29:20 -0500 -Subject: [PATCH 7/9] XDG base directory spec compliance (#498) - ---- - platforms/ios/build.sh | 6 ++---- - platforms/macos/build.sh | 4 ++-- - platforms/sdl/sdl1/main.cpp | 17 +++++++++++++++-- - platforms/sdl/sdl2/main.cpp | 15 ++++++++++++++- - 4 files changed, 33 insertions(+), 9 deletions(-) - -diff --git a/platforms/ios/build.sh b/platforms/ios/build.sh -index 43bd17764..8e4a951e5 100755 ---- a/platforms/ios/build.sh -+++ b/platforms/ios/build.sh -@@ -85,10 +85,8 @@ else - fi - # ensure we use ccache for the toolchain build - ccache="$(command -v ccache || true)" --printf '#!/bin/sh\n -- exec %sclang "$@"\n' "$ccache " > bin/remcpe-clang --printf '#!/bin/sh\n -- exec %sclang++ "$@"\n' "$ccache " > bin/remcpe-clang++ -+printf '#!/bin/sh\nexec %s clang "$@"\n' "$ccache" > bin/remcpe-clang -+printf '#!/bin/sh\nexec %s clang++ "$@"\n' "$ccache" > bin/remcpe-clang++ - chmod +x bin/remcpe-clang bin/remcpe-clang++ - - if [ -n "$outdated_toolchain" ]; then -diff --git a/platforms/macos/build.sh b/platforms/macos/build.sh -index 441fcd2a8..3e31399a6 100755 ---- a/platforms/macos/build.sh -+++ b/platforms/macos/build.sh -@@ -109,8 +109,8 @@ else - fi - # ensure we use ccache for the toolchain build - ccache="$(command -v ccache || true)" --printf '#!/bin/sh\nexec %sclang "$@"\n' "$ccache " > bin/remcpe-clang --printf '#!/bin/sh\nexec %sclang++ "$@"\n' "$ccache " > bin/remcpe-clang++ -+printf '#!/bin/sh\nexec %s clang "$@"\n' "$ccache" > bin/remcpe-clang -+printf '#!/bin/sh\nexec %s clang++ "$@"\n' "$ccache" > bin/remcpe-clang++ - chmod +x bin/remcpe-clang bin/remcpe-clang++ - - if [ -n "$outdated_toolchain" ]; then -diff --git a/platforms/sdl/sdl1/main.cpp b/platforms/sdl/sdl1/main.cpp -index 2bc5c4b62..4e08fc1b9 100644 ---- a/platforms/sdl/sdl1/main.cpp -+++ b/platforms/sdl/sdl1/main.cpp -@@ -92,10 +92,23 @@ static std::string getStoragePath() - #ifdef _WIN32 - pathBase = getenv("APPDATA"); - #else -- pathBase = getenv("HOME"); -+ const char *xdg_data = getenv("XDG_DATA_HOME"); -+ if (xdg_data) -+ pathBase = xdg_data; -+ else -+ { -+ xdg_data = getenv("HOME"); -+ if (!xdg_data) -+ { -+ LOG_E("HOME not set"); -+ pathBase = ""; // current working directory -+ } -+ else -+ pathBase = ((std::string)xdg_data + "/.local/share").c_str(); -+ } - #endif - -- if (pathBase == nullptr || pathBase[0] == '\0') -+ if (!pathBase) - pathBase = ""; // just use the current working directory - - std::string path(pathBase); -diff --git a/platforms/sdl/sdl2/main.cpp b/platforms/sdl/sdl2/main.cpp -index fa228b636..3cbaabbe5 100644 ---- a/platforms/sdl/sdl2/main.cpp -+++ b/platforms/sdl/sdl2/main.cpp -@@ -420,7 +420,20 @@ int main(int argc, char *argv[]) - #elif defined(ANDROID) - storagePath = SDL_AndroidGetExternalStoragePath(); - #else -- storagePath = getenv("HOME"); -+ const char *xdg_data = getenv("XDG_DATA_HOME"); -+ if (xdg_data) -+ storagePath = xdg_data; -+ else -+ { -+ xdg_data = getenv("HOME"); -+ if (!xdg_data) -+ { -+ LOG_E("HOME not set"); -+ storagePath = "."; // current working directory -+ } -+ else -+ storagePath = (std::string)xdg_data + "/.local/share"; -+ } - #endif - storagePath += "/.reminecraftpe"; - - -From a31e7ce4d1b5814b075c1d23e27c62755a025cef Mon Sep 17 00:00:00 2001 -From: Brent <43089001+BrentDaMage@users.noreply.github.com> -Date: Sat, 21 Feb 2026 15:34:20 -0800 -Subject: [PATCH 8/9] Fixed ClassicCraftingScreen_Console positioning (#499) - ---- - .../gui/screens/inventory/ClassicCraftingScreen_Console.cpp | 5 ----- - .../gui/screens/inventory/ClassicCraftingScreen_Console.hpp | 1 - - source/network/MinecraftPackets.cpp | 1 + - source/network/Packet.hpp | 6 ++++-- - 4 files changed, 5 insertions(+), 8 deletions(-) - -diff --git a/source/client/gui/screens/inventory/ClassicCraftingScreen_Console.cpp b/source/client/gui/screens/inventory/ClassicCraftingScreen_Console.cpp -index 56619d6bb..f81d568a5 100644 ---- a/source/client/gui/screens/inventory/ClassicCraftingScreen_Console.cpp -+++ b/source/client/gui/screens/inventory/ClassicCraftingScreen_Console.cpp -@@ -6,11 +6,6 @@ ClassicCraftingScreen_Console::ClassicCraftingScreen_Console(Inventory* inventor - ContainerScreen(new CraftingMenu(inventory, tilePos, level)) - { - m_uiTheme = UI_CONSOLE; --} -- --void ClassicCraftingScreen_Console::init() --{ -- ContainerScreen::init(); - m_imageWidth = 428; - m_imageHeight = 450; - } -diff --git a/source/client/gui/screens/inventory/ClassicCraftingScreen_Console.hpp b/source/client/gui/screens/inventory/ClassicCraftingScreen_Console.hpp -index d5e5b5091..e74ce590e 100644 ---- a/source/client/gui/screens/inventory/ClassicCraftingScreen_Console.hpp -+++ b/source/client/gui/screens/inventory/ClassicCraftingScreen_Console.hpp -@@ -7,7 +7,6 @@ class ClassicCraftingScreen_Console : public ContainerScreen - public: - ClassicCraftingScreen_Console(Inventory* inventory, const TilePos& tilePos, Level* level); - -- void init() override; - void renderBackground() override; - - protected: -diff --git a/source/network/MinecraftPackets.cpp b/source/network/MinecraftPackets.cpp -index 297124cfb..0fe7ddaa1 100644 ---- a/source/network/MinecraftPackets.cpp -+++ b/source/network/MinecraftPackets.cpp -@@ -58,6 +58,7 @@ Packet* MinecraftPackets::createPacket(MinecraftPacketIds id) - case PACKET_CONTAINER_SET_DATA: return new ContainerSetDataPacket; - case PACKET_CONTAINER_SET_CONTENT: return new ContainerSetContentPacket; - case PACKET_CONTAINER_ACK: return new ContainerAckPacket; -+ case PACKET_CHAT: return nullptr; - - case PACKET_LEVEL_DATA: return new LevelDataPacket; - } -diff --git a/source/network/Packet.hpp b/source/network/Packet.hpp -index 4825bee6f..12fe4688d 100644 ---- a/source/network/Packet.hpp -+++ b/source/network/Packet.hpp -@@ -13,12 +13,13 @@ - #include "BitStream.h" - #include "MessageIdentifiers.h" - --#define NETWORK_PROTOCOL_VERSION_MIN 6 // the packet IDs changed completely between 2, 3, 4, 5, and 6 -+#define NETWORK_PROTOCOL_VERSION_MIN 6 // the packet IDs changed completely between 2 thru 6 - //#define NETWORK_PROTOCOL_VERSION 2 // 0.1.0 (actual client crashes with unrecognized tiles) - //#define NETWORK_PROTOCOL_VERSION 3 // 0.2.0 (actual client crashes with unrecognized entities) - //#define NETWORK_PROTOCOL_VERSION 4 // 0.3.0 - //#define NETWORK_PROTOCOL_VERSION 5 // 0.3.2 --#define NETWORK_PROTOCOL_VERSION 6 // 0.3.3 -+#define NETWORK_PROTOCOL_VERSION 6 // 0.3.3 -+//#define NETWORK_PROTOCOL_VERSION 7 // 0.4.0 - - class NetEventCallback; - class Level; -@@ -262,6 +263,7 @@ enum MinecraftPacketIds - PACKET_CONTAINER_SET_DATA, - PACKET_CONTAINER_SET_CONTENT, - PACKET_CONTAINER_ACK, // Unused in PE -+ PACKET_CHAT, - - PACKET_LEVEL_DATA = 200 - - -From 7bdeeec7510261bc50d5c60a4ce07e087efede44 Mon Sep 17 00:00:00 2001 -From: Un1q32 -Date: Sat, 21 Feb 2026 18:50:11 -0500 -Subject: [PATCH 9/9] Enabled Auto-Jump by Default for Touchscreens (#500) - ---- - source/client/options/Options.cpp | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/source/client/options/Options.cpp b/source/client/options/Options.cpp -index 4e65f5d71..4aea60bbc 100644 ---- a/source/client/options/Options.cpp -+++ b/source/client/options/Options.cpp -@@ -80,7 +80,7 @@ Options::Options(Minecraft* mc, const std::string& folderPath) : - , m_flightHax("misc_flycheat", "options.flightHax", false) - , m_playerName("mp_username", "options.username", "Steve") - , m_serverVisibleDefault("mp_server_visible_default", "options.serverVisibleDefault", true) -- , m_autoJump("ctrl_autojump", "options.autoJump", false) -+ , m_autoJump("ctrl_autojump", "options.autoJump", mc->platform()->isTouchscreen()) - , m_debugText("info_debugtext", "options.debugText", false) - , m_blockOutlines("gfx_blockoutlines", "options.blockOutlines", false) - , m_fancyGrass("gfx_fancygrass", "options.fancyGrass", true) diff --git a/478.patch.txt b/478.patch.txt deleted file mode 100644 index 2b014e0d9..000000000 --- a/478.patch.txt +++ /dev/null @@ -1,10726 +0,0 @@ -From 8c687e0cc188c5abba28105c742e43919e29b472 Mon Sep 17 00:00:00 2001 -From: hellosammu -Date: Sun, 15 Feb 2026 09:33:45 -0500 -Subject: [PATCH 01/22] clean branch - ---- - source/CMakeLists.txt | 16 +- - source/client/app/NinecraftApp.cpp | 4 +- - .../gui/screens/inventory/FurnaceScreen.cpp | 57 +++++ - .../gui/screens/inventory/FurnaceScreen.hpp | 20 ++ - source/client/player/LocalPlayer.cpp | 5 +- - source/client/player/LocalPlayer.hpp | 2 +- - source/client/renderer/Chunk.cpp | 49 +++- - source/client/renderer/Chunk.hpp | 5 +- - source/client/renderer/LevelRenderer.cpp | 19 +- - source/client/renderer/LevelRenderer.hpp | 1 + - source/client/sound/SoundRepository.cpp | 2 +- - source/client/sound/sound_list.h | 12 + - source/common/Mth.cpp | 4 +- - source/world/Facing.cpp | 19 ++ - source/world/Facing.hpp | 4 +- - source/world/entity/Player.cpp | 5 + - source/world/entity/Player.hpp | 3 +- - source/world/inventory/FurnaceMenu.cpp | 100 ++++++++ - source/world/inventory/FurnaceMenu.hpp | 24 ++ - source/world/inventory/FurnaceResultSlot.cpp | 19 ++ - source/world/inventory/FurnaceResultSlot.hpp | 16 ++ - source/world/inventory/SimpleContainer.cpp | 6 +- - source/world/inventory/SimpleContainer.hpp | 31 +-- - source/world/item/CoalItem.cpp | 12 + - source/world/item/CoalItem.hpp | 12 + - source/world/item/Item.cpp | 17 +- - source/world/item/crafting/FurnaceRecipes.cpp | 2 + - source/world/item/crafting/Recipes.cpp | 26 +-- - source/world/level/Level.cpp | 74 ++++++ - source/world/level/Level.hpp | 8 + - source/world/level/Region.cpp | 5 + - source/world/level/Region.hpp | 1 + - .../level/levelgen/chunk/ChunkTilePos.hpp | 9 +- - .../world/level/levelgen/chunk/LevelChunk.cpp | 69 ++++++ - .../world/level/levelgen/chunk/LevelChunk.hpp | 7 + - .../storage/ExternalFileLevelStorage.cpp | 30 +++ - source/world/level/storage/LevelSource.hpp | 3 + - source/world/particle/BubbleParticle.cpp | 10 +- - source/world/particle/ExplodeParticle.cpp | 10 +- - source/world/particle/FlameParticle.cpp | 14 +- - source/world/particle/LavaParticle.cpp | 16 +- - source/world/particle/NoteParticle.cpp | 47 ++++ - source/world/particle/Particle.cpp | 30 +-- - source/world/particle/Particle.hpp | 22 +- - source/world/particle/RedDustParticle.cpp | 14 +- - source/world/particle/SmokeParticle.cpp | 14 +- - source/world/particle/TerrainParticle.cpp | 18 +- - source/world/phys/Vec3.hpp | 7 + - source/world/tile/ChestTile.cpp | 217 ++++++++++++++++++ - source/world/tile/ChestTile.hpp | 20 ++ - source/world/tile/CropsTile.cpp | 2 +- - source/world/tile/FurnaceTile.cpp | 194 ++++++++++++++++ - source/world/tile/FurnaceTile.hpp | 33 +++ - source/world/tile/MusicTile.cpp | 98 ++++++++ - source/world/tile/MusicTile.hpp | 17 ++ - source/world/tile/Tile.cpp | 55 ++++- - source/world/tile/Tile.hpp | 9 +- - source/world/tile/entity/ChestTileEntity.cpp | 31 +++ - source/world/tile/entity/ChestTileEntity.hpp | 17 ++ - .../world/tile/entity/FurnaceTileEntity.cpp | 163 +++++++++++++ - .../world/tile/entity/FurnaceTileEntity.hpp | 33 +++ - source/world/tile/entity/MusicTileEntity.cpp | 46 ++++ - source/world/tile/entity/MusicTileEntity.hpp | 21 ++ - source/world/tile/entity/TileEntity.cpp | 106 +++++++++ - source/world/tile/entity/TileEntity.hpp | 50 ++++ - source/world/tile/entity/TileEntityType.cpp | 51 ++++ - source/world/tile/entity/TileEntityType.hpp | 50 ++++ - 67 files changed, 1978 insertions(+), 135 deletions(-) - create mode 100644 source/client/gui/screens/inventory/FurnaceScreen.cpp - create mode 100644 source/client/gui/screens/inventory/FurnaceScreen.hpp - create mode 100644 source/world/Facing.cpp - create mode 100644 source/world/inventory/FurnaceMenu.cpp - create mode 100644 source/world/inventory/FurnaceMenu.hpp - create mode 100644 source/world/inventory/FurnaceResultSlot.cpp - create mode 100644 source/world/inventory/FurnaceResultSlot.hpp - create mode 100644 source/world/item/CoalItem.cpp - create mode 100644 source/world/item/CoalItem.hpp - create mode 100644 source/world/particle/NoteParticle.cpp - create mode 100644 source/world/tile/ChestTile.cpp - create mode 100644 source/world/tile/ChestTile.hpp - create mode 100644 source/world/tile/FurnaceTile.cpp - create mode 100644 source/world/tile/FurnaceTile.hpp - create mode 100644 source/world/tile/MusicTile.cpp - create mode 100644 source/world/tile/MusicTile.hpp - create mode 100644 source/world/tile/entity/ChestTileEntity.cpp - create mode 100644 source/world/tile/entity/ChestTileEntity.hpp - create mode 100644 source/world/tile/entity/FurnaceTileEntity.cpp - create mode 100644 source/world/tile/entity/FurnaceTileEntity.hpp - create mode 100644 source/world/tile/entity/MusicTileEntity.cpp - create mode 100644 source/world/tile/entity/MusicTileEntity.hpp - create mode 100644 source/world/tile/entity/TileEntity.cpp - create mode 100644 source/world/tile/entity/TileEntity.hpp - create mode 100644 source/world/tile/entity/TileEntityType.cpp - create mode 100644 source/world/tile/entity/TileEntityType.hpp - -diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt -index 36493fd02..afe085a83 100644 ---- a/source/CMakeLists.txt -+++ b/source/CMakeLists.txt -@@ -124,8 +124,9 @@ add_library(reminecraftpe-core STATIC - client/gui/screens/inventory/ContainerScreen.cpp - client/gui/screens/inventory/InventoryScreen.cpp - client/gui/screens/inventory/CraftingScreen.cpp -- client/gui/screens/inventory/ClassicCraftingScreen_Console.cpp -+ client/gui/screens/inventory/FurnaceScreen.cpp - client/gui/screens/inventory/ChestScreen.cpp -+ client/gui/screens/inventory/ClassicCraftingScreen_Console.cpp - client/gui/components/ScrolledSelectionList.cpp - client/gui/components/AvailableGamesList.cpp - client/gui/components/RolledSelectionList.cpp -@@ -350,6 +351,7 @@ add_library(reminecraftpe-core STATIC - world/item/TilePlanterItem.cpp - world/item/CameraItem.cpp - world/item/TileItem.cpp -+ world/item/CoalItem.cpp - world/item/Inventory.cpp - world/item/crafting/Recipes.cpp - world/item/crafting/FurnaceRecipes.cpp -@@ -360,10 +362,12 @@ add_library(reminecraftpe-core STATIC - world/inventory/ContainerMenu.cpp - world/inventory/InventoryMenu.cpp - world/inventory/CraftingMenu.cpp -+ world/inventory/FurnaceMenu.cpp - world/inventory/ChestMenu.cpp - world/inventory/Slot.cpp - world/inventory/ResultSlot.cpp - world/inventory/ArmorSlot.cpp -+ world/inventory/FurnaceResultSlot.cpp - world/inventory/CraftingContainer.cpp - world/inventory/ResultContainer.cpp - world/inventory/SimpleContainer.cpp -@@ -386,6 +390,7 @@ add_library(reminecraftpe-core STATIC - world/item/SlabItem.cpp - world/particle/RedDustParticle.cpp - world/particle/TerrainParticle.cpp -+ world/particle/NoteParticle.cpp - world/particle/BubbleParticle.cpp - world/particle/ExplodeParticle.cpp - world/particle/ParticleEngine.cpp -@@ -421,6 +426,9 @@ add_library(reminecraftpe-core STATIC - world/tile/OreTile.cpp - world/tile/StairTile.cpp - world/tile/SandStoneTile.cpp -+ world/tile/ChestTile.cpp -+ world/tile/FurnaceTile.cpp -+ world/tile/MusicTile.cpp - world/tile/FireTile.cpp - world/tile/StoneSlabTile.cpp - world/tile/LiquidTile.cpp -@@ -442,6 +450,12 @@ add_library(reminecraftpe-core STATIC - world/tile/Web.cpp - world/tile/FenceTile.cpp - world/tile/CraftingTableTile.cpp -+ world/tile/entity/TileEntity.cpp -+ world/tile/entity/TileEntityType.cpp -+ world/tile/entity/ChestTileEntity.cpp -+ world/tile/entity/FurnaceTileEntity.cpp -+ world/tile/entity/MusicTileEntity.cpp -+ world/Facing.cpp - renderer/GL/GL.cpp - renderer/Attribute.cpp - renderer/ConstantBufferMetaData.cpp -diff --git a/source/client/app/NinecraftApp.cpp b/source/client/app/NinecraftApp.cpp -index a591064f5..b805ddcd1 100644 ---- a/source/client/app/NinecraftApp.cpp -+++ b/source/client/app/NinecraftApp.cpp -@@ -10,6 +10,7 @@ - #include "world/item/Item.hpp" - #include "world/entity/MobCategory.hpp" - #include "world/entity/MobFactory.hpp" -+#include "world/tile/entity/TileEntityType.hpp" - #include "client/player/input/GameControllerHandler.hpp" - #include "client/player/input/Multitouch.hpp" - #include "client/gui/screens/StartMenuScreen.hpp" -@@ -182,7 +183,7 @@ void NinecraftApp::_initAll() - Tile::initTiles(); - Item::initItems(); - Biome::initBiomes(); -- //TileEntity::initTileEntities(); -+ TileEntityType::InitTileEntities(); - } - - _initOptions(); -@@ -340,6 +341,7 @@ void NinecraftApp::onGraphicsReset() - - void NinecraftApp::teardown() - { -+ TileEntityType::TeardownTileEntities(); - teardownRenderer(); - Resource::teardownLoaders(); - // Stop our SoundSystem before we nuke our sound buffers and cause it to implode -diff --git a/source/client/gui/screens/inventory/FurnaceScreen.cpp b/source/client/gui/screens/inventory/FurnaceScreen.cpp -new file mode 100644 -index 000000000..b28b6d538 ---- /dev/null -+++ b/source/client/gui/screens/inventory/FurnaceScreen.cpp -@@ -0,0 +1,57 @@ -+#include "FurnaceScreen.hpp" -+#include "world/inventory/FurnaceMenu.hpp" -+#include "renderer/ShaderConstants.hpp" -+ -+FurnaceScreen::FurnaceScreen(Inventory* inventory, FurnaceTileEntity* container) -+ : ContainerScreen(new FurnaceMenu(inventory, container)), m_inventory(inventory), m_furnace(container) -+{ -+ m_uiTheme = UI_JAVA; -+} -+ -+void FurnaceScreen::tick() -+{ -+ ContainerScreen::tick(); -+} -+ -+void FurnaceScreen::_renderLabels() -+{ -+ m_pFont->draw(m_furnace->getName(), 66, 6, 0x404040); -+ m_pFont->draw(m_inventory->getName(), 8, m_imageHeight - 96 + 2, 0x404040); -+} -+ -+void FurnaceScreen::_renderBg(float a) -+{ -+ currentShaderColor = Color::WHITE; -+ -+ m_pMinecraft->m_pTextures->loadAndBindTexture("gui/furnace.png"); -+ -+ blit(m_leftPos, m_topPos, 0, 0, m_imageWidth, m_imageHeight, 0, 0); -+ -+ int p; -+ if (m_furnace->isLit()) -+ { -+ p = m_furnace->getLitProgress(12); -+ blit(m_leftPos + 56, m_topPos + 36 + 12 - p, 176, 12 - p, 14, p + 2, 0, 0); -+ } -+ -+ p = m_furnace->getBurnProgress(24); -+ blit(m_leftPos + 79, m_topPos + 34, 176, 14, p + 1, 16, 0, 0); -+} -+ -+SlotDisplay FurnaceScreen::_createSlotDisplay(const Slot& slot) -+{ -+ constexpr int slotSize = 18; -+ switch (slot.m_group) -+ { -+ case Slot::INPUT: -+ return SlotDisplay(56, 17 + (slot.m_slot % 2) * (slotSize * 2)); -+ case Slot::OUTPUT: -+ return SlotDisplay(116, 35); -+ case Slot::INVENTORY: -+ return SlotDisplay(8 + (slot.m_slot % 9) * slotSize, 84 + ((slot.m_slot / 9) - 1) * slotSize, slotSize); -+ case Slot::HOTBAR: -+ return SlotDisplay(8 + (slot.m_slot % 9) * slotSize, 142, slotSize); -+ default: -+ return SlotDisplay(); -+ } -+} -\ No newline at end of file -diff --git a/source/client/gui/screens/inventory/FurnaceScreen.hpp b/source/client/gui/screens/inventory/FurnaceScreen.hpp -new file mode 100644 -index 000000000..5b84e36d3 ---- /dev/null -+++ b/source/client/gui/screens/inventory/FurnaceScreen.hpp -@@ -0,0 +1,20 @@ -+#pragma once -+ -+#include "ContainerScreen.hpp" -+#include "world/tile/entity/FurnaceTileEntity.hpp" -+ -+class FurnaceScreen : public ContainerScreen -+{ -+public: -+ FurnaceScreen(Inventory* inventory, FurnaceTileEntity* container); -+ void tick() override; -+ -+protected: -+ void _renderLabels() override; -+ void _renderBg(float a) override; -+ SlotDisplay _createSlotDisplay(const Slot&) override; -+ -+private: -+ Inventory* m_inventory; -+ FurnaceTileEntity* m_furnace; -+}; -diff --git a/source/client/player/LocalPlayer.cpp b/source/client/player/LocalPlayer.cpp -index ec62ed15b..8db2a1dcb 100644 ---- a/source/client/player/LocalPlayer.cpp -+++ b/source/client/player/LocalPlayer.cpp -@@ -13,6 +13,7 @@ - #include "network/packets/PlayerEquipmentPacket.hpp" - #include "client/gui/screens/inventory/CraftingScreen.hpp" - #include "client/gui/screens/inventory/ChestScreen.hpp" -+#include "client/gui/screens/inventory/FurnaceScreen.hpp" - - int dword_250ADC, dword_250AE0; - -@@ -134,11 +135,11 @@ void LocalPlayer::startCrafting(const TilePos& pos) - m_pMinecraft->getScreenChooser()->pushCraftingScreen(this, pos); - } - --/*void LocalPlayer::openFurnace(FurnaceTileEntity* furnace) -+void LocalPlayer::openFurnace(FurnaceTileEntity* furnace) - { - // PE 0.3.2 doesn't let you cook in creative mode - m_pMinecraft->setScreen(new FurnaceScreen(m_pInventory, furnace)); --}*/ -+} - - void LocalPlayer::openContainer(Container* container) - { -diff --git a/source/client/player/LocalPlayer.hpp b/source/client/player/LocalPlayer.hpp -index 1eecb1288..076c5abe1 100644 ---- a/source/client/player/LocalPlayer.hpp -+++ b/source/client/player/LocalPlayer.hpp -@@ -39,7 +39,7 @@ class LocalPlayer : public Player - void setPlayerGameType(GameType gameType) override; - void swing() override; - void startCrafting(const TilePos&) override; -- //void openFurnace(FurnaceTileEntity* furnace) override; -+ void openFurnace(FurnaceTileEntity* furnace) override; - void openContainer(Container* container) override; - void closeContainer() override; - //void openTrap(DispenserTileEntity* tileEntity) override; -diff --git a/source/client/renderer/Chunk.cpp b/source/client/renderer/Chunk.cpp -index 86ece3261..ec7d6db5e 100644 ---- a/source/client/renderer/Chunk.cpp -+++ b/source/client/renderer/Chunk.cpp -@@ -133,6 +133,9 @@ void Chunk::rebuild() - - LevelChunk::touchedSky = false; - -+ std::set tmpSet(m_renderableTileEntities.begin(), m_renderableTileEntities.end()); -+ m_renderableTileEntities.clear(); -+ - for (int i = Tile::RENDER_LAYERS_MIN; i <= Tile::RENDER_LAYERS_MAX; i++) - { - m_empty[i] = true; -@@ -169,6 +172,16 @@ void Chunk::rebuild() - t.setOffset(-m_pos); - } - -+ if (!layer && Tile::isEntityTile[tile]) -+ { -+ /* -+ // @TODO: ADD TILE ENTITY RENDER DISPATCHER -+ TileEntity* et = region.getTileEntity(tp); -+ if (TileEntityRenderDispatcher::getInstance()->hasRenderer(et)) -+ m_renderableTileEntities.push_back(et); -+ */ -+ } -+ - Tile* pTile = Tile::tiles[tile]; - - if (layer == pTile->getRenderLayer()) -@@ -201,11 +214,45 @@ void Chunk::rebuild() - break; - } - -+ std::set newSet(m_renderableTileEntities.begin(), m_renderableTileEntities.end()); -+ std::vector toAdd, toRemove; -+ -+ std::set_difference( -+ newSet.begin(), newSet.end(), -+ tmpSet.begin(), tmpSet.end(), -+ std::back_inserter(toAdd) -+ ); -+ -+ std::set_difference( -+ tmpSet.begin(), tmpSet.end(), -+ newSet.begin(), newSet.end(), -+ std::back_inserter(toRemove) -+ ); -+ -+ // Add -+ for (std::vector::iterator it = toAdd.begin(); it != toAdd.end(); ++it) -+ { -+ m_globalRenderableTileEntities.push_back(*it); -+ } -+ -+ // Remove -+ for (std::vector::iterator it = toRemove.begin(); it != toRemove.end(); ++it) -+ { -+ std::vector::iterator f = -+ std::find(m_globalRenderableTileEntities.begin(), -+ m_globalRenderableTileEntities.end(), -+ *it); -+ -+ if (f != m_globalRenderableTileEntities.end()) -+ m_globalRenderableTileEntities.erase(f); -+ } -+ - field_54 = LevelChunk::touchedSky; - m_bCompiled = true; - } - --Chunk::Chunk(Level* level, const TilePos& pos, int size, int lists) -+Chunk::Chunk(Level* level, std::vector& renderableTileEntities, const TilePos& pos, int size, int lists) -+ : m_globalRenderableTileEntities(renderableTileEntities) - { - m_bOcclusionVisible = true; - m_bOcclusionQuerying = false; -diff --git a/source/client/renderer/Chunk.hpp b/source/client/renderer/Chunk.hpp -index 5f575c0d8..957ef0446 100644 ---- a/source/client/renderer/Chunk.hpp -+++ b/source/client/renderer/Chunk.hpp -@@ -15,11 +15,12 @@ - - class Level; - class Entity; -+class TileEntity; - - class Chunk - { - public: -- Chunk(Level*, const TilePos& pos, int, int); -+ Chunk(Level*, std::vector& renderableTileEntities, const TilePos& pos, int, int); - - public: - float distanceToSqr(const Entity& entity) const; -@@ -42,6 +43,8 @@ class Chunk - - public: - Level* m_pLevel; -+ std::vector& m_globalRenderableTileEntities; -+ std::vector m_renderableTileEntities; - TilePos m_pos; - TilePos m_posS; - bool m_empty[Tile::RENDER_LAYERS_COUNT]; -diff --git a/source/client/renderer/LevelRenderer.cpp b/source/client/renderer/LevelRenderer.cpp -index c7202fe54..98a311c52 100644 ---- a/source/client/renderer/LevelRenderer.cpp -+++ b/source/client/renderer/LevelRenderer.cpp -@@ -617,7 +617,7 @@ void LevelRenderer::allChanged() - m_zMinChunk = 0; - - m_dirtyChunks.clear(); -- //m_renderableTileEntities.clear(); -+ m_renderableTileEntities.clear(); - - m_xMaxChunk = m_xChunks; - m_yMaxChunk = m_yChunks; -@@ -638,7 +638,7 @@ void LevelRenderer::allChanged() - { - int index = (cp.z * m_yChunks + cp.y) * m_xChunks + cp.x; - -- Chunk* pChunk = new Chunk(m_pLevel, cp * 16, 16, id + m_chunkLists); -+ Chunk* pChunk = new Chunk(m_pLevel, m_renderableTileEntities, cp * 16, 16, id + m_chunkLists); - - if (m_bOcclusionCheck) - pChunk->m_occlusionId = 0; // m_occlusionCheckIds.get(count) -@@ -1421,6 +1421,11 @@ void LevelRenderer::addParticle(const std::string& name, const Vec3& pos, const - pe->add(new SmokeParticle(m_pLevel, pos, dir, 1.0f)); - return; - } -+ if (name == "note") -+ { -+ pe->add(new NoteParticle(m_pLevel, pos, dir)); -+ return; -+ } - if (name == "explode") - { - pe->add(new ExplodeParticle(m_pLevel, pos, dir)); -@@ -1599,6 +1604,16 @@ void LevelRenderer::renderEntities(Vec3 pos, Culler* culler, float f) - EntityRenderDispatcher::getInstance()->render(*entity, f); - } - } -+ -+ /* -+ // @TODO: TileEntityRenderDispatcher -+ for (std::vector::const_iterator it = m_renderableTileEntities.begin(); -+ it != m_renderableTileEntities.end(); ++it) -+ { -+ TileEntity* tileEntity = *it; -+ TileEntityRenderDispatcher::getInstance()->render(tileEntity, f); -+ } -+ */ - } - - void LevelRenderer::renderShadow(const Entity& entity, const Vec3& pos, float r, float pow, float a) -diff --git a/source/client/renderer/LevelRenderer.hpp b/source/client/renderer/LevelRenderer.hpp -index 1e2fbbc60..18947ff8c 100644 ---- a/source/client/renderer/LevelRenderer.hpp -+++ b/source/client/renderer/LevelRenderer.hpp -@@ -223,4 +223,5 @@ class LevelRenderer : public LevelListener, public AppPlatformListener - mce::Mesh m_darkMesh; - //... - Textures* m_pTextures; -+ std::vector m_renderableTileEntities; - }; -diff --git a/source/client/sound/SoundRepository.cpp b/source/client/sound/SoundRepository.cpp -index 32a24a2dd..17342dc52 100644 ---- a/source/client/sound/SoundRepository.cpp -+++ b/source/client/sound/SoundRepository.cpp -@@ -33,7 +33,7 @@ bool SoundRepository::get(const std::string& name, SoundDesc& sd) - std::map >::iterator iter = m_repo.find(name); - if (iter == m_repo.end()) - { -- LOG_E("Couldn't find a sound with id: %s", name.c_str()); -+ LOG_W("Couldn't find a sound with id: %s", name.c_str()); - return false; - } - -diff --git a/source/client/sound/sound_list.h b/source/client/sound/sound_list.h -index e20026c34..ee6906caf 100644 ---- a/source/client/sound/sound_list.h -+++ b/source/client/sound/sound_list.h -@@ -48,6 +48,12 @@ SOUND(ui, press) - SOUND(ui, scroll) - - SOUND(fire, fire) -+SOUND(fire, ignite) -+SOUND_NUM(fire, fire_crackle, 1) -+SOUND_NUM(fire, fire_crackle, 2) -+SOUND_NUM(fire, fire_crackle, 3) -+SOUND_NUM(fire, fire_crackle, 4) -+SOUND_NUM(fire, fire_crackle, 5) - - SOUND_NUM(damage, fallbig, 1) - SOUND_NUM(damage, fallbig, 2) -@@ -103,3 +109,9 @@ SOUND_NUM(mob, zombie, 3) - SOUND_NUM(mob, zombiehurt, 1) - SOUND_NUM(mob, zombiehurt, 2) - SOUND(mob, zombiedeath) -+ -+SOUND(note, harp) -+SOUND(note, bd) -+SOUND(note, hat) -+SOUND(note, snare) -+SOUND(note, bassattack) -\ No newline at end of file -diff --git a/source/common/Mth.cpp b/source/common/Mth.cpp -index d3a20ddb0..5126313ab 100644 ---- a/source/common/Mth.cpp -+++ b/source/common/Mth.cpp -@@ -48,13 +48,13 @@ float Mth::invSqrt(float number) - // they just stole it from Quake. - - float x2, y; -- const float threehalfs = 1.5F; -+ const float threehalfs = 1.5f; - union { - float f; - int32_t i; - } un; - -- x2 = number * 0.5F; -+ x2 = number * 0.5f; - un.f = number; // evil floating point bit level hacking - un.i = 0x5f3759df - ( un.i >> 1 ); // what the fuck? - y = un.f; -diff --git a/source/world/Facing.cpp b/source/world/Facing.cpp -new file mode 100644 -index 000000000..0893ffc57 ---- /dev/null -+++ b/source/world/Facing.cpp -@@ -0,0 +1,19 @@ -+#include "Facing.hpp" -+ -+const Facing::Name Facing::OPPOSITE[6] = -+{ -+ Facing::UP, // DOWN -> UP -+ Facing::DOWN, // UP -> DOWN -+ Facing::SOUTH, // NORTH -> SOUTH -+ Facing::NORTH, // SOUTH -> NORTH -+ Facing::EAST, // WEST -> EAST -+ Facing::WEST // EAST -> WEST -+}; -+ -+const Facing::Name Facing::HORIZONTAL[4] = -+{ -+ Facing::NORTH, -+ Facing::SOUTH, -+ Facing::EAST, -+ Facing::WEST -+}; -\ No newline at end of file -diff --git a/source/world/Facing.hpp b/source/world/Facing.hpp -index 740f1ce37..2875da6d5 100644 ---- a/source/world/Facing.hpp -+++ b/source/world/Facing.hpp -@@ -12,4 +12,6 @@ class Facing - WEST, // -X - EAST // +X - }; --}; -+ static const Name OPPOSITE[6]; -+ static const Name HORIZONTAL[4]; -+}; -\ No newline at end of file -diff --git a/source/world/entity/Player.cpp b/source/world/entity/Player.cpp -index 444cb5254..d7e0cff66 100644 ---- a/source/world/entity/Player.cpp -+++ b/source/world/entity/Player.cpp -@@ -540,6 +540,11 @@ void Player::startCrafting(const TilePos& pos) - - } - -+void Player::openFurnace(FurnaceTileEntity* tileEntity) -+{ -+ -+} -+ - void Player::startStonecutting(const TilePos& pos) - { - -diff --git a/source/world/entity/Player.hpp b/source/world/entity/Player.hpp -index 2d88c21ed..1a25f7228 100644 ---- a/source/world/entity/Player.hpp -+++ b/source/world/entity/Player.hpp -@@ -16,6 +16,7 @@ - #include "world/inventory/InventoryMenu.hpp" - - class Inventory; // in case we're included from Inventory.hpp -+class FurnaceTileEntity; - - class Player : public Mob - { -@@ -72,7 +73,7 @@ class Player : public Mob - virtual void startStonecutting(const TilePos& pos); - virtual void startDestroying(); - virtual void stopDestroying(); -- //virtual void openFurnace(FurnaceTileEntity* tileEntity); -+ virtual void openFurnace(FurnaceTileEntity* tileEntity); - virtual void openContainer(Container* container) {} - virtual void closeContainer() {} - //virtual void openTrap(DispenserTileEntity* tileEntity); -diff --git a/source/world/inventory/FurnaceMenu.cpp b/source/world/inventory/FurnaceMenu.cpp -new file mode 100644 -index 000000000..e8f6678ac ---- /dev/null -+++ b/source/world/inventory/FurnaceMenu.cpp -@@ -0,0 +1,100 @@ -+#include "FurnaceMenu.hpp" -+#include "Slot.hpp" -+#include "FurnaceResultSlot.hpp" -+#include "world/ContainerListener.hpp" -+ -+FurnaceMenu::FurnaceMenu(Inventory* inventory, FurnaceTileEntity* furnace) -+ : ContainerMenu(Container::FURNACE), m_furnace(furnace), m_lastCookTime(0), m_lastBurnTime(0), m_lastLitDuration(0) -+{ -+ addSlot(new Slot(m_furnace, 0, Slot::INPUT)); -+ addSlot(new Slot(m_furnace, 1, Slot::INPUT)); -+ addSlot(new FurnaceResultSlot(inventory->m_pPlayer, m_furnace, 2)); -+ -+ for (int y = 0; y < 3; ++y) -+ { -+ for (int x = 0; x < 9; ++x) -+ addSlot(new Slot(inventory, x + (y + 1) * 9, Slot::INVENTORY)); -+ } -+ -+ for (int i = 0; i < 9; ++i) -+ { -+ addSlot(new Slot(inventory, i, Slot::HOTBAR)); -+ } -+} -+ -+bool FurnaceMenu::stillValid(Player* player) const -+{ -+ return m_furnace->stillValid(player); -+} -+ -+void FurnaceMenu::addSlotListener(ContainerListener* listener) -+{ -+ ContainerMenu::addSlotListener(listener); -+ listener->setContainerData(this, 0, m_furnace->m_tickCount); -+ listener->setContainerData(this, 1, m_furnace->m_litTime); -+ listener->setContainerData(this, 2, m_furnace->m_litDuration); -+} -+ -+void FurnaceMenu::broadcastChanges() -+{ -+ ContainerMenu::broadcastChanges(); -+ -+ for (std::vector::iterator it = m_listeners.begin(); it != m_listeners.end(); ++it) -+ { -+ ContainerListener* listener = *it; -+ -+ if (m_lastCookTime != m_furnace->m_tickCount) -+ listener->setContainerData(this, 0, m_furnace->m_tickCount); -+ -+ if (m_lastBurnTime != m_furnace->m_litTime) -+ listener->setContainerData(this, 1, m_furnace->m_litTime); -+ -+ if (m_lastLitDuration != m_furnace->m_litDuration) -+ listener->setContainerData(this, 2, m_furnace->m_litDuration); -+ } -+ -+ m_lastCookTime = m_furnace->m_tickCount; -+ m_lastBurnTime = m_furnace->m_litTime; -+ m_lastLitDuration = m_furnace->m_litDuration; -+} -+ -+void FurnaceMenu::setData(int index, int value) -+{ -+ if (index == 0) -+ m_furnace->m_tickCount = value; -+ else if (index == 1) -+ m_furnace->m_litTime = value; -+ else if (index == 2) -+ m_furnace->m_litDuration = value; -+} -+ -+ItemStack FurnaceMenu::quickMoveStack(int index) -+{ -+ ItemStack item; -+ Slot* slot = getSlot(index); -+ if (slot && slot->hasItem()) -+ { -+ ItemStack& slotItem = slot->getItem(); -+ item = slotItem.copy(); -+ if (index == 2) -+ moveItemStackTo(slotItem, 3, 39, true); -+ else if (index >= 3 && index < 30) -+ moveItemStackTo(slotItem, 30, 39, false); -+ else if (index >= 30 && index < 39) -+ moveItemStackTo(slotItem, 3, 30, false); -+ else -+ moveItemStackTo(slotItem, 3, 39, false); -+ -+ if (slotItem.m_count == 0) -+ slot->set(ItemStack::EMPTY); -+ else -+ slot->setChanged(); -+ -+ if (slotItem.m_count == item.m_count) -+ return ItemStack::EMPTY; -+ -+ slot->onTake(slotItem); -+ } -+ -+ return item; -+} -diff --git a/source/world/inventory/FurnaceMenu.hpp b/source/world/inventory/FurnaceMenu.hpp -new file mode 100644 -index 000000000..42025cf41 ---- /dev/null -+++ b/source/world/inventory/FurnaceMenu.hpp -@@ -0,0 +1,24 @@ -+#pragma once -+ -+#include "ContainerMenu.hpp" -+#include "world/entity/Player.hpp" -+#include -+ -+class FurnaceMenu : public ContainerMenu -+{ -+public: -+ FurnaceMenu(Inventory* inventory, FurnaceTileEntity* container); -+ -+public: -+ bool stillValid(Player* player) const override; -+ void addSlotListener(ContainerListener* listener) override; -+ void broadcastChanges() override; -+ void setData(int, int) override; -+ ItemStack quickMoveStack(int index) override; -+ -+private: -+ FurnaceTileEntity* m_furnace; -+ int m_lastCookTime; -+ int m_lastBurnTime; -+ int m_lastLitDuration; -+}; -diff --git a/source/world/inventory/FurnaceResultSlot.cpp b/source/world/inventory/FurnaceResultSlot.cpp -new file mode 100644 -index 000000000..84d628de0 ---- /dev/null -+++ b/source/world/inventory/FurnaceResultSlot.cpp -@@ -0,0 +1,19 @@ -+#include "FurnaceResultSlot.hpp" -+#include "../item/Item.hpp" -+#include "world/entity/Player.hpp" -+ -+FurnaceResultSlot::FurnaceResultSlot(Player* player, Container* container, int slotIndex) -+ : Slot(container, slotIndex, OUTPUT), m_pPlayer(player) -+{ -+} -+ -+bool FurnaceResultSlot::mayPlace(const ItemStack&) const -+{ -+ return false; -+} -+ -+void FurnaceResultSlot::onTake(ItemStack& inst) -+{ -+ inst.onCraftedBy(m_pPlayer, m_pPlayer->m_pLevel); -+ Slot::onTake(inst); -+} -diff --git a/source/world/inventory/FurnaceResultSlot.hpp b/source/world/inventory/FurnaceResultSlot.hpp -new file mode 100644 -index 000000000..4c1e277a1 ---- /dev/null -+++ b/source/world/inventory/FurnaceResultSlot.hpp -@@ -0,0 +1,16 @@ -+#pragma once -+ -+#include "Slot.hpp" -+#include "source/world/Container.hpp" -+ -+class FurnaceResultSlot : public Slot -+{ -+public: -+ FurnaceResultSlot(Player* player, Container* container, int slotIdx); -+ -+ bool mayPlace(const ItemStack&) const override; -+ void onTake(ItemStack&) override; -+ -+private: -+ Player* m_pPlayer; -+}; -\ No newline at end of file -diff --git a/source/world/inventory/SimpleContainer.cpp b/source/world/inventory/SimpleContainer.cpp -index 6799cd433..74b8c4c5d 100644 ---- a/source/world/inventory/SimpleContainer.cpp -+++ b/source/world/inventory/SimpleContainer.cpp -@@ -64,7 +64,7 @@ bool SimpleContainer::stillValid(Player* player) const - return true; - } - --void SimpleContainer::load(CompoundTag& tag) -+void SimpleContainer::load(const CompoundTag& tag) - { - clear(); - const ListTag* list = tag.getList("Items"); -@@ -78,14 +78,14 @@ void SimpleContainer::load(CompoundTag& tag) - { - uint8_t slot = itemTag->getInt8("Slot") & 255; - ItemStack item = ItemStack::fromTag(*itemTag); -- if (itemTag->isEmpty() && slot >= 0 && slot < m_items.size()) -+ if (!itemTag->isEmpty() && slot >= 0 && slot < m_items.size()) - m_items[slot] = item; - } - } - - } - --void SimpleContainer::save(CompoundTag& tag) -+void SimpleContainer::save(CompoundTag& tag) const - { - ListTag* list = new ListTag; - -diff --git a/source/world/inventory/SimpleContainer.hpp b/source/world/inventory/SimpleContainer.hpp -index 2e676e564..a371d2532 100644 ---- a/source/world/inventory/SimpleContainer.hpp -+++ b/source/world/inventory/SimpleContainer.hpp -@@ -9,28 +9,21 @@ class SimpleContainer : public Container - public: - SimpleContainer(int size, const std::string& name); - -- virtual uint16_t getContainerSize() const override; -- -- virtual ItemStack& getItem(int index) override; -- -- virtual ItemStack removeItem(int index, int count) override; -- -- virtual void setItem(int index, const ItemStack& item) override; -- -- virtual std::string getName() const override; -- -- virtual void setChanged() override; -- -- virtual bool stillValid(Player* player) const override; -+public: -+ uint16_t getContainerSize() const override; -+ ItemStack& getItem(int index) override; -+ ItemStack removeItem(int index, int count) override; -+ void setItem(int index, const ItemStack& item) override; -+ std::string getName() const override; -+ void setChanged() override; -+ bool stillValid(Player* player) const override; - -+public: - virtual void clear(); -- -- virtual void load(CompoundTag& tag); -- virtual void save(CompoundTag& tag); -+ virtual void load(const CompoundTag& tag); -+ virtual void save(CompoundTag& tag) const; - - private: - std::vector m_items; - std::string m_name; --}; -- -- -+}; -\ No newline at end of file -diff --git a/source/world/item/CoalItem.cpp b/source/world/item/CoalItem.cpp -new file mode 100644 -index 000000000..4d3f6669e ---- /dev/null -+++ b/source/world/item/CoalItem.cpp -@@ -0,0 +1,12 @@ -+#include "CoalItem.hpp" -+ -+CoalItem::CoalItem(int id) : Item(id) -+{ -+ m_maxDamage = 0; -+ m_bStackedByData = true; -+} -+ -+std::string CoalItem::getDescriptionId(ItemStack* inst) const -+{ -+ return (inst->getAuxValue() == 1) ? "item.charcoal" : "item.coal"; -+} -\ No newline at end of file -diff --git a/source/world/item/CoalItem.hpp b/source/world/item/CoalItem.hpp -new file mode 100644 -index 000000000..fc1474401 ---- /dev/null -+++ b/source/world/item/CoalItem.hpp -@@ -0,0 +1,12 @@ -+#pragma once -+ -+#include "Item.hpp" -+ -+class CoalItem : public Item -+{ -+public: -+ CoalItem(int id); -+ -+ std::string getDescriptionId(ItemStack* inst) const override; -+ -+}; -\ No newline at end of file -diff --git a/source/world/item/Item.cpp b/source/world/item/Item.cpp -index 88fe95939..7377fca9f 100644 ---- a/source/world/item/Item.cpp -+++ b/source/world/item/Item.cpp -@@ -11,19 +11,20 @@ - #include "common/Logger.hpp" - #include "common/Util.hpp" - -+#include "ArmorItem.hpp" -+#include "BowItem.hpp" - #include "CameraItem.hpp" -+#include "CoalItem.hpp" - #include "DoorItem.hpp" --#include "TileItem.hpp" --#include "TilePlanterItem.hpp" --#include "RocketItem.hpp" --#include "ToolItem.hpp" --#include "BowItem.hpp" - #include "DyePowderItem.hpp" --#include "WeaponItem.hpp" - #include "FoodItem.hpp" --#include "ArmorItem.hpp" - #include "HoeItem.hpp" -+#include "RocketItem.hpp" - #include "SeedItem.hpp" -+#include "TileItem.hpp" -+#include "ToolItem.hpp" -+#include "TilePlanterItem.hpp" -+#include "WeaponItem.hpp" - - #define ITEM(x) ((x) - 256) - -@@ -339,7 +340,7 @@ void Item::initItems() - ->setIcon(5, 2) - ->setDescriptionId("arrow"); - -- Item::coal = NEW_ITEM(ITEM_COAL) -+ Item::coal = NEW_X_ITEMN(CoalItem, ITEM_COAL) - ->setIcon(7, 0) - ->setDescriptionId("coal"); - -diff --git a/source/world/item/crafting/FurnaceRecipes.cpp b/source/world/item/crafting/FurnaceRecipes.cpp -index 0c170f30d..36f99a069 100644 ---- a/source/world/item/crafting/FurnaceRecipes.cpp -+++ b/source/world/item/crafting/FurnaceRecipes.cpp -@@ -1,6 +1,8 @@ - #include "FurnaceRecipes.hpp" - #include "common/Logger.hpp" - -+FurnaceRecipes* FurnaceRecipes::instance; -+ - FurnaceRecipes::FurnaceRecipes() - { - addFurnaceRecipe(Tile::ironOre, ItemStack(Item::ironIngot)); -diff --git a/source/world/item/crafting/Recipes.cpp b/source/world/item/crafting/Recipes.cpp -index 05f0e32c0..fe375b83b 100644 ---- a/source/world/item/crafting/Recipes.cpp -+++ b/source/world/item/crafting/Recipes.cpp -@@ -120,14 +120,14 @@ Recipes::Recipes() - addOre(ItemStack(Item::dye_powder, 1, 4), Tile::lapisBlock); - - // StructureRecipes -- //add(ShapedRecipeBuilder("###", -- // "# #", -- // "###", ItemStack(Tile::chest)) -- // .add('#', Tile::wood)); -- //add(ShapedRecipeBuilder("###", -- // "# #", -- // "###", ItemStack(Tile::furnace)) -- // .add('#', Tile::stoneBrick)); -+ add(ShapedRecipeBuilder("###", -+ "# #", -+ "###", ItemStack(Tile::chest)) -+ .add('#', Tile::wood)); -+ add(ShapedRecipeBuilder("###", -+ "# #", -+ "###", ItemStack(Tile::furnace)) -+ .add('#', Tile::stoneBrick)); - - add(ShapedRecipeBuilder("##", - "##", ItemStack(Tile::craftingTable)) -@@ -169,11 +169,11 @@ Recipes::Recipes() - // .add('#', Tile::wood) - // .add('X', Item::emerald)); - -- //add(ShapedRecipeBuilder("###", -- // "#X#", -- // "###", ItemStack(Tile::musicBlock, 1)) -- // .add('#', Tile::wood) -- // .add('X', Item::redStone)); -+ add(ShapedRecipeBuilder("###", -+ "#X#", -+ "###", ItemStack(Tile::musicBlock, 1)) -+ .add('#', Tile::wood) -+ .add('X', Item::redStone)); - - add(ShapedRecipeBuilder("###", - "XXX", -diff --git a/source/world/level/Level.cpp b/source/world/level/Level.cpp -index e91a3c8f2..efec2255d 100644 ---- a/source/world/level/Level.cpp -+++ b/source/world/level/Level.cpp -@@ -18,6 +18,7 @@ - #include "network/packets/ExplodePacket.hpp" - #include "world/level/levelgen/chunk/ChunkCache.hpp" - #include "world/entity/MobSpawner.hpp" -+#include "world/tile/entity/TileEntity.hpp" - - #include "Explosion.hpp" - #include "Region.hpp" -@@ -38,6 +39,8 @@ Level::Level(LevelStorage* pStor, const std::string& name, const LevelSettings& - m_bCalculatingInitialSpawn = false; - m_pChunkSource = nullptr; - m_pLevelStorage = pStor; -+ m_tileEntityList = TileEntityVector(); -+ m_bUpdatingTileEntities = false; - m_randValue = 42184323; - m_addend = 1013904223; - m_bUpdateLights = true; -@@ -258,6 +261,54 @@ int Level::getRawBrightness(const TilePos& pos, bool b) const - return pChunk->getRawBrightness(pos, m_skyDarken); - } - -+TileEntity* Level::getTileEntity(const TilePos& pos) const -+{ -+ LevelChunk* pChunk = getChunk(pos); -+ return pChunk ? pChunk->getTileEntity(pos) : nullptr; -+} -+ -+const TileEntityVector* Level::getAllTileEntities() const -+{ -+ return &m_tileEntityList; -+} -+ -+void Level::setTileEntity(const TilePos& pos, TileEntity* tileEntity) -+{ -+ -+ if (tileEntity->isRemoved()) -+ return; -+ -+ if (m_bUpdatingTileEntities) -+ { -+ tileEntity->m_pos = pos; -+ m_pendingTileEntities.push_back(tileEntity); -+ return; -+ } -+ -+ m_tileEntityList.push_back(tileEntity); -+ LevelChunk* pChunk = getChunk(pos); -+ if (pChunk) -+ pChunk->setTileEntity(pos, tileEntity); -+} -+ -+void Level::removeTileEntity(const TilePos& pos) -+{ -+ TileEntity* old = getTileEntity(pos); -+ -+ if (old != nullptr && m_bUpdatingTileEntities) -+ { -+ old->setRemoved(); -+ return; -+ } -+ -+ if (old) -+ Util::remove(m_tileEntityList, old); -+ -+ LevelChunk* pChunk = getChunk(pos); -+ if (pChunk) -+ pChunk->removeTileEntity(pos); -+} -+ - void Level::swap(const TilePos& pos1, const TilePos& pos2) - { - TileID tile1 = getTile(pos1); -@@ -1706,6 +1757,29 @@ void Level::tickEntities() - delete pEnt; - } - } -+ -+ m_bUpdatingTileEntities = true; -+ for (size_t i = 0; i < m_tileEntityList.size(); i++) -+ { -+ TileEntity* tileEnt = m_tileEntityList[i]; -+ -+ if (!tileEnt->isRemoved()) -+ { -+ tileEnt->tick(); -+ } -+ else -+ { -+ LevelChunk* ch = getChunk(tileEnt->m_pos); -+ if (ch) -+ ch->removeTileEntity(tileEnt->m_pos); -+ -+ m_entities.erase(m_entities.begin() + i); -+ i--; -+ -+ delete tileEnt; -+ } -+ } -+ m_bUpdatingTileEntities = false; - } - - HitResult Level::clip(Vec3 v1, Vec3 v2, bool flag) const -diff --git a/source/world/level/Level.hpp b/source/world/level/Level.hpp -index 993830cf6..95ae707ea 100644 ---- a/source/world/level/Level.hpp -+++ b/source/world/level/Level.hpp -@@ -38,6 +38,7 @@ class Packet; - class MobSpawner; - - typedef std::vector EntityVector; -+typedef std::vector TileEntityVector; - typedef std::vector AABBVector; - - struct Brightness -@@ -72,6 +73,10 @@ class Level : public LevelSource - LevelChunk* getChunkAt(const TilePos& pos) const; - int getRawBrightness(const TilePos& pos) const; - int getRawBrightness(const TilePos& pos, bool b) const; -+ TileEntity* getTileEntity(const TilePos& pos) const override; -+ const TileEntityVector* getAllTileEntities() const; -+ void setTileEntity(const TilePos& pos, TileEntity* tileEntity); -+ void removeTileEntity(const TilePos& pos); - int getBrightness(const LightLayer&, const TilePos& pos) const; - void setBrightness(const LightLayer&, const TilePos& pos, int brightness); - int getSeaLevel() const { return 63; } -@@ -203,6 +208,7 @@ class Level : public LevelSource - - private: - LevelData* m_pLevelData; -+ bool m_bUpdatingTileEntities; - - protected: - int m_randValue; -@@ -237,5 +243,7 @@ class Level : public LevelSource - MobSpawner* m_pMobSpawner; - - std::map m_entityCountsByCategory; -+ TileEntityVector m_tileEntityList; -+ TileEntityVector m_pendingTileEntities; - }; - -diff --git a/source/world/level/Region.cpp b/source/world/level/Region.cpp -index 9a7109ce6..8d55fcd48 100644 ---- a/source/world/level/Region.cpp -+++ b/source/world/level/Region.cpp -@@ -111,6 +111,11 @@ BiomeSource* Region::getBiomeSource() const - return m_pLevel->getBiomeSource(); - } - -+TileEntity* Region::getTileEntity(const TilePos& pos) const -+{ -+ return m_pLevel->getTileEntity(pos); -+} -+ - Region::~Region() - { - delete[] field_C; -diff --git a/source/world/level/Region.hpp b/source/world/level/Region.hpp -index ca0101edb..e0eeea90a 100644 ---- a/source/world/level/Region.hpp -+++ b/source/world/level/Region.hpp -@@ -21,6 +21,7 @@ class Region : public LevelSource - Material* getMaterial(const TilePos& pos) const override; - bool isSolidTile(const TilePos& pos) const override; - BiomeSource* getBiomeSource() const override; -+ TileEntity* getTileEntity(const TilePos& pos) const override; - - virtual ~Region(); - Region(const Level* level, const TilePos& min, const TilePos& max); -diff --git a/source/world/level/levelgen/chunk/ChunkTilePos.hpp b/source/world/level/levelgen/chunk/ChunkTilePos.hpp -index 923a430f3..258f9010d 100644 ---- a/source/world/level/levelgen/chunk/ChunkTilePos.hpp -+++ b/source/world/level/levelgen/chunk/ChunkTilePos.hpp -@@ -19,4 +19,11 @@ struct ChunkTilePos - { - return ChunkTilePos(x + other.x, y + other.y, z + other.z); - } --}; -+ -+ bool operator<(const ChunkTilePos& other) const -+ { -+ if (x != other.x) return x < other.x; -+ if (y != other.y) return y < other.y; -+ return z < other.z; -+ } -+}; -\ No newline at end of file -diff --git a/source/world/level/levelgen/chunk/LevelChunk.cpp b/source/world/level/levelgen/chunk/LevelChunk.cpp -index e0de339b4..598a4ef8a 100644 ---- a/source/world/level/levelgen/chunk/LevelChunk.cpp -+++ b/source/world/level/levelgen/chunk/LevelChunk.cpp -@@ -8,6 +8,7 @@ - - #include "common/Logger.hpp" - #include "world/level/Level.hpp" -+#include "world/tile/entity/TileEntity.hpp" - #include "world/phys/AABB.hpp" - - bool LevelChunk::touchedSky = false; -@@ -683,6 +684,74 @@ bool LevelChunk::setTileAndData(const ChunkTilePos& pos, TileID tile, TileData d - return true; - } - -+TileEntity* LevelChunk::getTileEntity(const ChunkTilePos& pos) -+{ -+ std::map::iterator it = m_tileEntities.find(pos); -+ if (it == m_tileEntities.end()) -+ { -+ int tileId = getTile(pos); -+ if (tileId <= TILE_AIR || !Tile::isEntityTile[tileId]) -+ return nullptr; -+ -+ TilePos tilePos(m_chunkPos, pos.y); -+ tilePos += TilePos(pos.x, 0, pos.z); -+ -+ Tile* pTile = Tile::tiles[tileId]; -+ pTile->onPlace(m_pLevel, tilePos); -+ -+ // do a recheck to see if a tile entity was actually added. -+ it = m_tileEntities.find(pos); -+ return (it == m_tileEntities.end()) ? nullptr : it->second; -+ } -+ -+ if (!it->second || it->second->isRemoved()) -+ { -+ m_tileEntities.erase(it); -+ return nullptr; -+ } -+ -+ return it->second; -+} -+ -+void LevelChunk::addTileEntity(TileEntity* tileEntity) -+{ -+ setTileEntity(tileEntity->m_pos, tileEntity); -+ if (m_bLoaded) -+ m_pLevel->m_tileEntityList.push_back(tileEntity); -+} -+ -+void LevelChunk::setTileEntity(const ChunkTilePos& pos, TileEntity* tileEntity) -+{ -+ tileEntity->m_pLevel = m_pLevel; -+ TileID tile = getTile(pos); -+ TilePos tilePos(m_chunkPos, pos.y); -+ tilePos.x += pos.x; -+ tilePos.z += pos.z; -+ tileEntity->m_pos = tilePos; -+ if (tile > 0 && Tile::isEntityTile[tile]) -+ { -+ tileEntity->clearRemoved(); -+ m_tileEntities[pos] = tileEntity; -+ return; -+ } -+ -+ LOG_I("Attempted to place a tile entity where there was no entity tile! %d", tile); -+} -+ -+void LevelChunk::removeTileEntity(const ChunkTilePos& pos) -+{ -+ if (!m_bLoaded) -+ return; -+ -+ std::map::iterator it = m_tileEntities.find(pos); -+ if (it != m_tileEntities.end()) -+ { -+ if (it->second) -+ it->second->setRemoved(); -+ m_tileEntities.erase(it); -+ } -+} -+ - TileData LevelChunk::getData(const ChunkTilePos& pos) - { - CheckPosition(pos); -diff --git a/source/world/level/levelgen/chunk/LevelChunk.hpp b/source/world/level/levelgen/chunk/LevelChunk.hpp -index f25faf097..d103eb18a 100644 ---- a/source/world/level/levelgen/chunk/LevelChunk.hpp -+++ b/source/world/level/levelgen/chunk/LevelChunk.hpp -@@ -11,6 +11,7 @@ - #include - #include - #include -+#include - #include "common/Random.hpp" - #include "common/Utils.hpp" - #include "client/renderer/LightLayer.hpp" -@@ -21,6 +22,7 @@ - class Level; - class AABB; - class Entity; -+class TileEntity; - - class LevelChunk - { -@@ -69,6 +71,10 @@ class LevelChunk - virtual void setBlocks(uint8_t* pData, int y); - virtual int getBlocksAndData(uint8_t* pData, int, int, int, int, int, int, int); - virtual int setBlocksAndData(uint8_t* pData, int, int, int, int, int, int, int); -+ virtual TileEntity* getTileEntity(const ChunkTilePos& pos); -+ virtual void addTileEntity(TileEntity* tileEntity); -+ virtual void setTileEntity(const ChunkTilePos& pos, TileEntity* tileEntity); -+ virtual void removeTileEntity(const ChunkTilePos& pos); - virtual Random getRandom(int32_t l); - virtual void recalcHeight(const ChunkTilePos& pos); - virtual bool isEmpty(); -@@ -96,4 +102,5 @@ class LevelChunk - int field_23C; - TileID* m_pBlockData; - std::vector m_entities[128 / 16]; -+ std::map m_tileEntities; - }; -diff --git a/source/world/level/storage/ExternalFileLevelStorage.cpp b/source/world/level/storage/ExternalFileLevelStorage.cpp -index 18b1e297c..681d04db0 100644 ---- a/source/world/level/storage/ExternalFileLevelStorage.cpp -+++ b/source/world/level/storage/ExternalFileLevelStorage.cpp -@@ -16,6 +16,7 @@ - #include "network/RakIO.hpp" - #include "world/entity/EntityFactory.hpp" - #include "world/level/Level.hpp" -+#include "world/tile/entity/TileEntity.hpp" - #include "thirdparty/raknet/GetTime.h" - - #ifndef DEMO -@@ -335,6 +336,22 @@ void ExternalFileLevelStorage::loadEntities(Level* level, LevelChunk* chunk) - level->addEntity(entity); - } - } -+ -+ const ListTag* tileEntitiesTag = tag->getList("TileEntities"); -+ if (tileEntitiesTag) -+ { -+ const std::vector& tileEntities = tileEntitiesTag->rawView(); -+ for (std::vector::const_iterator it = tileEntities.begin(); it != tileEntities.end(); it++) -+ { -+ const Tag* betterTag = *it; -+ if (!betterTag || betterTag->getId() != Tag::TAG_TYPE_COMPOUND) -+ continue; -+ -+ TileEntity* tileEntity = TileEntity::LoadTileEntity(*(CompoundTag*)betterTag); -+ if (tileEntity) -+ level->setTileEntity(tileEntity->m_pos, tileEntity); -+ } -+ } - } - - tag->deleteChildren(); -@@ -395,8 +412,21 @@ void ExternalFileLevelStorage::saveEntities(Level* level, LevelChunk* chunk) - entitiesTag->add(tag); - } - -+ ListTag* tileEntitiesTag = new ListTag(); -+ -+ const TileEntityVector* tileEntities = level->getAllTileEntities(); -+ for (TileEntityVector::const_iterator it = tileEntities->begin(); it != tileEntities->end(); it++) -+ { -+ const TileEntity* tileEntity = *it; -+ CompoundTag* tag = new CompoundTag(); -+ -+ tileEntity->save(*tag); -+ tileEntitiesTag->add(tag); -+ } -+ - CompoundTag tag = CompoundTag(); - tag.put("Entities", entitiesTag); -+ tag.put("TileEntities", tileEntitiesTag); - RakNet::BitStream bs; - RakDataOutput dos = RakDataOutput(bs); - NbtIo::write(tag, dos); -diff --git a/source/world/level/storage/LevelSource.hpp b/source/world/level/storage/LevelSource.hpp -index 97cc026b2..7cdbc8d2b 100644 ---- a/source/world/level/storage/LevelSource.hpp -+++ b/source/world/level/storage/LevelSource.hpp -@@ -12,6 +12,8 @@ - #include "world/level/Material.hpp" - #include "world/level/levelgen/biome/BiomeSource.hpp" - -+class TileEntity; -+ - class LevelSource - { - public: -@@ -22,5 +24,6 @@ class LevelSource - virtual Material* getMaterial(const TilePos& pos) const = 0; - virtual bool isSolidTile(const TilePos& pos) const = 0; - virtual BiomeSource* getBiomeSource() const = 0; -+ virtual TileEntity* getTileEntity(const TilePos& pos) const = 0; - }; - -diff --git a/source/world/particle/BubbleParticle.cpp b/source/world/particle/BubbleParticle.cpp -index 234917b45..96642b64a 100644 ---- a/source/world/particle/BubbleParticle.cpp -+++ b/source/world/particle/BubbleParticle.cpp -@@ -13,14 +13,14 @@ BubbleParticle::BubbleParticle(Level* level, const Vec3& pos, const Vec3& dir) : - Particle(level, pos, dir) - { - m_rCol = m_gCol = m_bCol = 1.0f; -- field_DC = PTI_BUBBLE; -+ m_tex = PTI_BUBBLE; - setSize(0.02f, 0.02f); - -- field_F0 *= 0.2f + 0.6f * sharedRandom.nextFloat(); -+ m_size *= 0.2f + 0.6f * sharedRandom.nextFloat(); - m_vel.x = dir.x * 0.2f + 0.02f * (2.0f * Mth::random() - 1.0f); - m_vel.y = dir.y * 0.2f + 0.02f * (2.0f * Mth::random() - 1.0f); - m_vel.z = dir.z * 0.2f + 0.02f * (2.0f * Mth::random() - 1.0f); -- field_EC = int(8.0f / (Mth::random() * 0.8f + 0.2f)); -+ m_lifetime = int(8.0f / (Mth::random() * 0.8f + 0.2f)); - } - - void BubbleParticle::tick() -@@ -35,7 +35,7 @@ void BubbleParticle::tick() - if (m_pLevel->getMaterial(m_pos) != Material::water) - remove(); - -- field_EC--; -- if (field_EC <= 0) -+ m_lifetime--; -+ if (m_lifetime <= 0) - remove(); - } -diff --git a/source/world/particle/ExplodeParticle.cpp b/source/world/particle/ExplodeParticle.cpp -index e5149e29a..c13d15f45 100644 ---- a/source/world/particle/ExplodeParticle.cpp -+++ b/source/world/particle/ExplodeParticle.cpp -@@ -18,20 +18,20 @@ ExplodeParticle::ExplodeParticle(Level* level, const Vec3& pos, const Vec3& dir) - m_vel.z = dir.z + 0.05f * (2.0f * Mth::random() - 1.0f); - - m_rCol = m_gCol = m_bCol = 0.7f + 0.3f * sharedRandom.nextFloat(); -- field_F0 = 1.0f + 6.0f * sharedRandom.nextFloat() * sharedRandom.nextFloat(); -- field_EC = int(16.0f / (0.2f + 0.8f * sharedRandom.nextFloat())) + 2; -+ m_size = 1.0f + 6.0f * sharedRandom.nextFloat() * sharedRandom.nextFloat(); -+ m_lifetime = int(16.0f / (0.2f + 0.8f * sharedRandom.nextFloat())) + 2; - } - - void ExplodeParticle::tick() - { - m_oPos = m_pos; - -- field_E8++; -- if (field_E8 > field_EC) -+ m_timer++; -+ if (m_timer > m_lifetime) - remove(); - - m_vel.y += 0.004f; -- field_DC = -8 * field_E8 / field_EC + 7; -+ m_tex = -8 * m_timer / m_lifetime + 7; - - move(m_vel); - -diff --git a/source/world/particle/FlameParticle.cpp b/source/world/particle/FlameParticle.cpp -index 39a63d158..5e17d316e 100644 ---- a/source/world/particle/FlameParticle.cpp -+++ b/source/world/particle/FlameParticle.cpp -@@ -23,10 +23,10 @@ FlameParticle::FlameParticle(Level* level, const Vec3& pos, const Vec3& dir) : - sharedRandom.genrand_int32(); - sharedRandom.genrand_int32(); - -- field_104 = field_F0; -+ field_104 = m_size; - m_rCol = m_gCol = m_bCol = 1.0f; -- field_EC = int(8.0f / (0.2f + 0.8f * Mth::random())) + 4; -- field_DC = PTI_FLAME; -+ m_lifetime = int(8.0f / (0.2f + 0.8f * Mth::random())) + 4; -+ m_tex = PTI_FLAME; - } - - float FlameParticle::getBrightness(float unused) const -@@ -38,8 +38,8 @@ void FlameParticle::tick() - { - m_oPos = m_pos; - -- field_E8++; -- if (field_E8 > field_EC) -+ m_timer++; -+ if (m_timer > m_lifetime) - remove(); - - move(m_vel); -@@ -55,7 +55,7 @@ void FlameParticle::tick() - - void FlameParticle::render(Tesselator& t, float f, float a, float b, float c, float d, float e) - { -- float mult = float(field_E8 + f) / float(field_EC); -- field_F0 = field_104 * (1.0f - 0.5f * mult * mult); -+ float mult = float(m_timer + f) / float(m_lifetime); -+ m_size = field_104 * (1.0f - 0.5f * mult * mult); - Particle::render(t, f, a, b, c, d, e); - } -diff --git a/source/world/particle/LavaParticle.cpp b/source/world/particle/LavaParticle.cpp -index d76ad60bd..413eedd87 100644 ---- a/source/world/particle/LavaParticle.cpp -+++ b/source/world/particle/LavaParticle.cpp -@@ -17,9 +17,9 @@ LavaParticle::LavaParticle(Level* level, const Vec3& pos) : - m_vel *= 0.8f; - m_vel.y = sharedRandom.nextFloat() * 0.4f + 0.05f; - m_rCol = m_gCol = m_bCol = 1.0f; -- field_104 = field_F0 = field_F0 * (0.2f + 2 * sharedRandom.nextFloat()); -- field_DC = PTI_LAVA; -- field_EC = int(16.0f / (0.2f + 0.8f * Mth::random())); -+ field_104 = m_size = m_size * (0.2f + 2 * sharedRandom.nextFloat()); -+ m_tex = PTI_LAVA; -+ m_lifetime = int(16.0f / (0.2f + 0.8f * Mth::random())); - } - - float LavaParticle::getBrightness(float unused) const -@@ -31,11 +31,11 @@ void LavaParticle::tick() - { - m_oPos = m_pos; - -- field_E8++; -- if (field_E8 > field_EC) -+ m_timer++; -+ if (m_timer > m_lifetime) - remove(); - -- float a = float(field_E8) / float(field_EC); -+ float a = float(m_timer) / float(m_lifetime); - float b = sharedRandom.nextFloat(); - if (a < b) - { -@@ -55,7 +55,7 @@ void LavaParticle::tick() - - void LavaParticle::render(Tesselator& t, float f, float a, float b, float c, float d, float e) - { -- float mult = float(field_E8 + f) / float(field_EC); -- field_F0 = field_104 * (1.0f - mult * mult); -+ float mult = float(m_timer + f) / float(m_lifetime); -+ m_size = field_104 * (1.0f - mult * mult); - Particle::render(t, f, a, b, c, d, e); - } -diff --git a/source/world/particle/NoteParticle.cpp b/source/world/particle/NoteParticle.cpp -new file mode 100644 -index 000000000..149dee931 ---- /dev/null -+++ b/source/world/particle/NoteParticle.cpp -@@ -0,0 +1,47 @@ -+#include "Particle.hpp" -+ -+NoteParticle::NoteParticle(Level* level, const Vec3& pos, const Vec3& dir, float scale) : -+ Particle(level, pos, Vec3::ZERO) -+{ -+ m_vel.x *= 0.01f; -+ m_vel.y *= 0.01f; -+ m_vel.z *= 0.01f; -+ m_vel.y += 0.2f; -+ m_rCol = Mth::sin((dir.x + 0.0f) * M_PI * 2.0f) * 0.65f + 0.35f; -+ m_gCol = Mth::sin((dir.x + (1.0f / 3.0f)) * M_PI * 2.0f) * 0.65f + 0.35f; -+ m_bCol = Mth::sin((dir.x + (2.0f / 3.0f)) * M_PI * 2.0f) * 0.65f + 0.35f; -+ m_size *= 0.75f * scale; -+ m_oSize = m_size; -+ m_lifetime = 6; -+ m_bNoPhysics = false; -+ m_tex = PTI_NOTE; -+} -+ -+void NoteParticle::tick() -+{ -+ m_oPos = m_pos; -+ -+ m_timer++; -+ if (m_timer > m_lifetime) -+ remove(); -+ -+ move(m_vel); -+ if (m_pos.y == m_oPos.y) -+ { -+ m_vel *= Vec3(1.1f, 1.0f, 1.1f); -+ } -+ -+ m_vel *= 0.66f; -+ -+ if (m_bOnGround) -+ { -+ m_vel *= Vec3(0.7f, 1.0f, 0.7f); -+ } -+} -+ -+void NoteParticle::render(Tesselator& t, float f, float a, float b, float c, float d, float e) -+{ -+ float mult = float(m_timer + f) / float(m_lifetime) * 32.0f; -+ m_size = m_oSize * Mth::clamp(mult, 0.0f, 1.0f); -+ Particle::render(t, f, a, b, c, d, e); -+} -diff --git a/source/world/particle/Particle.cpp b/source/world/particle/Particle.cpp -index 8b276c1db..e29863500 100644 ---- a/source/world/particle/Particle.cpp -+++ b/source/world/particle/Particle.cpp -@@ -12,12 +12,12 @@ float Particle::xOff, Particle::yOff, Particle::zOff; - - void Particle::_init() - { -- field_DC = 0; -+ m_tex = 0; - field_E0 = 0.0f; - field_E4 = 0.0f; -- field_E8 = 0; -- field_EC = 0; -- field_F0 = 0.0f; -+ m_timer = 0; -+ m_lifetime = 0; -+ m_size = 0.0f; - field_F4 = 0.0f; - m_rCol = 1.0f; - m_gCol = 1.0f; -@@ -45,8 +45,8 @@ Particle::Particle(Level* level, const Vec3& pos, const Vec3& dir) : Entity(leve - - field_E0 = 3.0f * sharedRandom.nextFloat(); - field_E4 = 3.0f * sharedRandom.nextFloat(); -- field_F0 = 2.0f * (0.5f + 0.5f * sharedRandom.nextFloat()); -- field_EC = int(4.0f / (0.1f + 0.9f * sharedRandom.nextFloat())); -+ m_size = 2.0f * (0.5f + 0.5f * sharedRandom.nextFloat()); -+ m_lifetime = int(4.0f / (0.1f + 0.9f * sharedRandom.nextFloat())); - } - - int Particle::getParticleTexture() -@@ -57,7 +57,7 @@ int Particle::getParticleTexture() - Particle* Particle::scale(float f) - { - setSize(0.2f * f, 0.2f * f); -- field_F0 *= f; -+ m_size *= f; - return this; - } - -@@ -73,7 +73,7 @@ void Particle::render(Tesselator& t, float f, float a4, float a5, float a6, floa - { - constexpr float C_MAGIC_1 = 0.062438f; // @BUG: Slightly bigger than 1/16.0f - -- int texture = field_DC; -+ int texture = m_tex; - int texX = texture % 16; - if (texture < 0) - texture += 15; -@@ -86,11 +86,11 @@ void Particle::render(Tesselator& t, float f, float a4, float a5, float a6, floa - float posZ = Mth::Lerp(m_oPos.z, m_pos.z, f) - zOff; - float fBright = m_bIsUnlit ? 1.0f : getBrightness(f); - -- float sizeX = a4 * field_F0 * 0.1f; -- float sizeY = a5 * field_F0 * 0.1f; -- float sizeZ = a6 * field_F0 * 0.1f; -- float siz2X = a7 * field_F0 * 0.1f; -- float siz2Z = a8 * field_F0 * 0.1f; -+ float sizeX = a4 * m_size * 0.1f; -+ float sizeY = a5 * m_size * 0.1f; -+ float sizeZ = a6 * m_size * 0.1f; -+ float siz2X = a7 * m_size * 0.1f; -+ float siz2Z = a8 * m_size * 0.1f; - - t.color(m_rCol * fBright, m_gCol * fBright, m_bCol * fBright); - t.vertexUV(posX - sizeX - siz2X, posY - sizeY, posZ - sizeZ - siz2Z, texU_1 + C_MAGIC_1, texV_1 + C_MAGIC_1); -@@ -102,8 +102,8 @@ void Particle::render(Tesselator& t, float f, float a4, float a5, float a6, floa - void Particle::tick() - { - m_oPos = m_pos; -- field_E8++; -- if (field_E8 >= field_EC) -+ m_timer++; -+ if (m_timer >= m_lifetime) - remove(); - - m_vel.y -= field_F4 * 0.04f; -diff --git a/source/world/particle/Particle.hpp b/source/world/particle/Particle.hpp -index 85d4814bc..9d58ab074 100644 ---- a/source/world/particle/Particle.hpp -+++ b/source/world/particle/Particle.hpp -@@ -23,7 +23,8 @@ enum eParticleTextureIndex - { - PTI_BUBBLE = 32, - PTI_FLAME = 48, -- PTI_LAVA -+ PTI_LAVA, -+ PTI_NOTE = 64 - }; - - class Particle : public Entity -@@ -45,12 +46,12 @@ class Particle : public Entity - Particle* setPower(float); - - public: -- int field_DC; -+ int m_tex; - float field_E0; - float field_E4; -- int field_E8; -- int field_EC; -- float field_F0; -+ int m_timer; -+ int m_lifetime; -+ float m_size; - float field_F4; - float m_rCol; - float m_gCol; -@@ -138,3 +139,14 @@ class LavaParticle : public Particle - public: - float field_104; - }; -+ -+class NoteParticle : public Particle -+{ -+public: -+ NoteParticle(Level*, const Vec3& pos, const Vec3& dir, float scale = 2.0f); -+ void tick() override; -+ void render(Tesselator&, float, float, float, float, float, float) override; -+ -+public: -+ float m_oSize; -+}; -\ No newline at end of file -diff --git a/source/world/particle/RedDustParticle.cpp b/source/world/particle/RedDustParticle.cpp -index bae587260..b7edf53a4 100644 ---- a/source/world/particle/RedDustParticle.cpp -+++ b/source/world/particle/RedDustParticle.cpp -@@ -20,21 +20,21 @@ RedDustParticle::RedDustParticle(Level* level, const Vec3& pos, const Vec3& dir) - m_gCol = f * dir.y * (Mth::random() * 0.2f + 0.8f); - m_bCol = f * dir.z * (Mth::random() * 0.2f + 0.8f); - -- field_104 = field_F0 = field_F0 * 0.75f; -+ field_104 = m_size = m_size * 0.75f; - - m_bNoPhysics = false; -- field_EC = int(8.0f / (0.2f + 0.8f * Mth::random())); -+ m_lifetime = int(8.0f / (0.2f + 0.8f * Mth::random())); - } - - void RedDustParticle::render(Tesselator& t, float f, float a, float b, float c, float d, float e) - { -- float mult = 32.0f * (float(field_E8 + f) / float(field_EC)); -+ float mult = 32.0f * (float(m_timer + f) / float(m_lifetime)); - if (mult < 0.0f) - mult = 0.0f; - if (mult > 1.0f) - mult = 1.0f; - -- field_F0 = field_104 * mult; -+ m_size = field_104 * mult; - Particle::render(t, f, a, b, c, d, e); - } - -@@ -42,12 +42,12 @@ void RedDustParticle::tick() - { - m_oPos = m_pos; - -- field_E8++; -- if (field_E8 > field_EC) -+ m_timer++; -+ if (m_timer > m_lifetime) - remove(); - - m_vel.y += 0.004f; -- field_DC = -8 * field_E8 / field_EC + 7; -+ m_tex = -8 * m_timer / m_lifetime + 7; - - move(m_vel); - -diff --git a/source/world/particle/SmokeParticle.cpp b/source/world/particle/SmokeParticle.cpp -index 2c781a660..812e291ee 100644 ---- a/source/world/particle/SmokeParticle.cpp -+++ b/source/world/particle/SmokeParticle.cpp -@@ -17,21 +17,21 @@ SmokeParticle::SmokeParticle(Level* level, const Vec3& pos, const Vec3& dir, flo - - m_bCol = m_gCol = m_rCol = Mth::random() * 0.5f; - -- field_104 = field_F0 = field_F0 * 0.75f * a9; -+ field_104 = m_size = m_size * 0.75f * a9; - - m_bNoPhysics = false; -- field_EC = int((a9 * 8.0f) / (0.2f + 0.8f * Mth::random())); -+ m_lifetime = int((a9 * 8.0f) / (0.2f + 0.8f * Mth::random())); - } - - void SmokeParticle::render(Tesselator& t, float f, float a, float b, float c, float d, float e) - { -- float mult = 32.0f * (float(field_E8 + f) / float(field_EC)); -+ float mult = 32.0f * (float(m_timer + f) / float(m_lifetime)); - if (mult < 0.0f) - mult = 0.0f; - if (mult > 1.0f) - mult = 1.0f; - -- field_F0 = field_104 * mult; -+ m_size = field_104 * mult; - Particle::render(t, f, a, b, c, d, e); - } - -@@ -39,12 +39,12 @@ void SmokeParticle::tick() - { - m_oPos = m_pos; - -- field_E8++; -- if (field_E8 > field_EC) -+ m_timer++; -+ if (m_timer > m_lifetime) - remove(); - - m_vel.y += 0.004f; -- field_DC = -8 * field_E8 / field_EC + 7; -+ m_tex = -8 * m_timer / m_lifetime + 7; - - move(m_vel); - -diff --git a/source/world/particle/TerrainParticle.cpp b/source/world/particle/TerrainParticle.cpp -index 3235e2b95..cf6b02f60 100644 ---- a/source/world/particle/TerrainParticle.cpp -+++ b/source/world/particle/TerrainParticle.cpp -@@ -12,10 +12,10 @@ - void TerrainParticle::_init(Tile* tile) - { - m_pTile = tile; -- field_DC = tile->m_TextureFrame; -+ m_tex = tile->m_TextureFrame; - field_F4 = tile->field_28; - m_rCol = m_gCol = m_bCol = 0.6f; -- field_F0 *= 0.5f; -+ m_size *= 0.5f; - } - - TerrainParticle::TerrainParticle(Level* level, const Vec3& pos, Tile* tile) : -@@ -36,7 +36,7 @@ TerrainParticle* TerrainParticle::init(const TilePos& tilePos, Facing::Name face - face = Facing::DOWN; - #endif - -- field_DC = m_pTile->getTexture(m_pLevel, tilePos, face); -+ m_tex = m_pTile->getTexture(m_pLevel, tilePos, face); - - if (m_pTile == Tile::grass && face != Facing::UP) - return this; -@@ -58,7 +58,7 @@ void TerrainParticle::render(Tesselator& t, float f, float a4, float a5, float a - { - constexpr float C_MAGIC_1 = 0.015609f; // @BUG: Slightly bigger than 1/64.0f - -- int texture = field_DC; -+ int texture = m_tex; - int texX = texture % 16; - if (texture < 0) - texture += 15; -@@ -71,11 +71,11 @@ void TerrainParticle::render(Tesselator& t, float f, float a4, float a5, float a - float posZ = Mth::Lerp(m_oPos.z, m_pos.z, f) - zOff; - float fBright = getBrightness(f); - -- float sizeX = a4 * field_F0 * 0.1f; -- float sizeY = a5 * field_F0 * 0.1f; -- float sizeZ = a6 * field_F0 * 0.1f; -- float siz2X = a7 * field_F0 * 0.1f; -- float siz2Z = a8 * field_F0 * 0.1f; -+ float sizeX = a4 * m_size * 0.1f; -+ float sizeY = a5 * m_size * 0.1f; -+ float sizeZ = a6 * m_size * 0.1f; -+ float siz2X = a7 * m_size * 0.1f; -+ float siz2Z = a8 * m_size * 0.1f; - - t.color(m_rCol * fBright, m_gCol * fBright, m_bCol * fBright); - t.vertexUV(posX - sizeX - siz2X, posY - sizeY, posZ - sizeZ - siz2Z, texU_1 + C_MAGIC_1, texV_1 + C_MAGIC_1); -diff --git a/source/world/phys/Vec3.hpp b/source/world/phys/Vec3.hpp -index a3725336e..efb4333ae 100644 ---- a/source/world/phys/Vec3.hpp -+++ b/source/world/phys/Vec3.hpp -@@ -123,6 +123,13 @@ class Vec3 - (*this) += -b; - } - -+ void operator*=(const Vec3& b) -+ { -+ x *= b.x; -+ y *= b.y; -+ z *= b.z; -+ } -+ - void operator+=(float f) - { - x += f; -diff --git a/source/world/tile/ChestTile.cpp b/source/world/tile/ChestTile.cpp -new file mode 100644 -index 000000000..327bd0bf6 ---- /dev/null -+++ b/source/world/tile/ChestTile.cpp -@@ -0,0 +1,217 @@ -+#include "ChestTile.hpp" -+#include "world/level/Level.hpp" -+#include "world/CompoundContainer.hpp" -+#include "world/tile/entity/ChestTileEntity.hpp" -+ -+ChestTile::ChestTile(int id, int texture) : Tile(id, texture, Material::wood) -+{ -+ setTicking(true); -+} -+ -+int ChestTile::getTexture(const LevelSource* level, const TilePos& pos, Facing::Name face) const -+{ -+ if (face == Facing::UP || face == Facing::DOWN) { -+ return m_TextureFrame - 1; -+ } -+ -+ int id_north = level->getTile(pos.north()); -+ int id_south = level->getTile(pos.south()); -+ int id_west = level->getTile(pos.west()); -+ int id_east = level->getTile(pos.east()); -+ -+ bool isDoubleNS = (id_north == m_ID || id_south == m_ID); -+ bool isDoubleWE = (id_west == m_ID || id_east == m_ID); -+ -+ if (!isDoubleNS && !isDoubleWE) { -+ Facing::Name front = Facing::SOUTH; -+ -+ if (Tile::solid[id_north] && !Tile::solid[id_south]) -+ front = Facing::SOUTH; -+ else if (Tile::solid[id_south] && !Tile::solid[id_north]) -+ front = Facing::NORTH; -+ else if (Tile::solid[id_west] && !Tile::solid[id_east]) -+ front = Facing::EAST; -+ else if (Tile::solid[id_east] && !Tile::solid[id_west]) -+ front = Facing::WEST; -+ -+ return (face == front) ? m_TextureFrame + 1 : m_TextureFrame; -+ } -+ -+ bool left = false; -+ Facing::Name front = Facing::SOUTH; -+ -+ if (isDoubleWE) { -+ left = id_west == m_ID; -+ TilePos side = left ? pos.west() : pos.east(); -+ -+ int id_behind = level->getTile(side.north()); -+ int id_infront = level->getTile(side.south()); -+ -+ if ((Tile::solid[id_north] || Tile::solid[id_behind]) && !Tile::solid[id_south] && !Tile::solid[id_infront]) -+ front = Facing::SOUTH; -+ else if ((Tile::solid[id_south] || Tile::solid[id_infront]) && !Tile::solid[id_north] && !Tile::solid[id_behind]) -+ front = Facing::NORTH; -+ -+ if (front == Facing::SOUTH) left = !left; -+ if (face == front) return m_TextureFrame + (left ? 15 : 16); -+ if (face == Facing::OPPOSITE[front]) return m_TextureFrame + (left ? 32 : 31); -+ return m_TextureFrame; -+ } -+ -+ if (isDoubleNS) -+ { -+ front = Facing::EAST; -+ left = id_north == m_ID; -+ TilePos side = left ? pos.north() : pos.south(); -+ -+ int id_behind = level->getTile(side.west()); -+ int id_infront = level->getTile(side.east()); -+ -+ if ((Tile::solid[id_west] || Tile::solid[id_behind]) && !Tile::solid[id_east] && !Tile::solid[id_infront]) { -+ front = Facing::EAST; -+ } -+ else if ((Tile::solid[id_east] || Tile::solid[id_infront]) && !Tile::solid[id_west] && !Tile::solid[id_behind]) { -+ front = Facing::WEST; -+ left = !left; -+ } -+ -+ if (face == front) -+ return m_TextureFrame + (left ? 15 : 16); -+ if (face == Facing::OPPOSITE[front]) -+ return m_TextureFrame + (left ? 32 : 31); -+ return m_TextureFrame; -+ } -+ -+ return m_TextureFrame; -+} -+ -+int ChestTile::getTexture(Facing::Name face) const -+{ -+ switch (face) { -+ case Facing::UP: -+ case Facing::DOWN: -+ return m_TextureFrame - 1; -+ case Facing::SOUTH: -+ return m_TextureFrame + 1; -+ default: -+ return m_TextureFrame; -+ } -+} -+ -+bool ChestTile::mayPlace(const Level* level, const TilePos& pos) const -+{ -+ return !hasNeighbors(level, pos, 1); -+} -+ -+bool ChestTile::hasNeighbors(const Level* level, const TilePos& pos, int count) const -+{ -+ int neighbors = 0; -+ for (int i = 0; i < 4; ++i) -+ { -+ Facing::Name f = static_cast(Facing::HORIZONTAL[i]); -+ TilePos relative = pos.relative(f); -+ if (level->getTile(relative) == m_ID) -+ { -+ neighbors++; -+ if (neighbors > count) return true; -+ -+ if (hasNeighbors(level, relative, 0)) return true; -+ } -+ } -+ -+ return false; -+} -+ -+void ChestTile::onRemove(Level* level, const TilePos& pos){ -+ ChestTileEntity* ent = static_cast(level->getTileEntity(pos)); -+ -+ if (!ent) -+ { -+ Tile::onRemove(level, pos); -+ } -+ -+ for (int slot = 0; slot < ent->getContainerSize(); ++slot) -+ { -+ ItemStack& item = ent->getItem(slot); -+ if (!item.isEmpty()) -+ { -+ TilePos offset( -+ (m_chestRandom.nextFloat() * 0.8f) + 0.1f, -+ (m_chestRandom.nextFloat() * 0.8f) + 0.1f, -+ (m_chestRandom.nextFloat() * 0.8f) + 0.1f -+ ); -+ -+ while (item.m_count > 0) -+ { -+ int toRemove = std::min(m_chestRandom.nextInt(21) + 10, static_cast(item.m_count)); -+ item.m_count -= toRemove; -+ ItemEntity* itemEnt = new ItemEntity(level, pos + offset, ItemStack(item.getItem()->m_itemID, toRemove, item.getAuxValue())); -+ float spread = 0.05f; -+ itemEnt->m_vel.x = (double)((float)m_chestRandom.nextGaussian() * spread); -+ itemEnt->m_vel.y = (double)((float)m_chestRandom.nextGaussian() * spread + 0.2f); -+ itemEnt->m_vel.z = (double)((float)m_chestRandom.nextGaussian() * spread); -+ level->addEntity(itemEnt); -+ } -+ } -+ } -+ -+ Tile::onRemove(level, pos); -+} -+ -+bool ChestTile::use(Level* level, const TilePos& pos, Player* player) -+{ -+ if (level->m_bIsClientSide) -+ return true; -+ -+ if (level->isSolidTile(pos.above())) -+ return true; -+ -+ if (level->getTile(pos.west()) == m_ID && level->isSolidTile(pos + TilePos(-1, 1, 0))) -+ return true; -+ -+ if (level->getTile(pos.east()) == m_ID && level->isSolidTile(pos + TilePos(1, 1, 0))) -+ return true; -+ -+ if (level->getTile(pos.north()) == m_ID && level->isSolidTile(pos + TilePos(0, 1, -1))) -+ return true; -+ -+ if (level->getTile(pos.south()) == m_ID && level->isSolidTile(pos + TilePos(0, 1, 1))) -+ return true; -+ -+ TileEntity* tileEnt = level->getTileEntity(pos); -+ if (!tileEnt) -+ return false; -+ -+ ChestTileEntity* chest = static_cast(tileEnt); -+ Container* container = static_cast(chest); -+ for (int rel = Facing::NORTH; rel <= Facing::EAST; rel++) -+ { -+ TilePos relPos = pos.relative(static_cast(rel)); -+ if (level->getTile(pos.relative(static_cast(rel))) == m_ID) -+ { -+ TileEntity* nearbyTileEntity = level->getTileEntity(relPos); -+ if (!nearbyTileEntity) -+ continue; -+ -+ Container* nearbyContainer = static_cast(static_cast(nearbyTileEntity)); -+ if (rel % 2 == 0) -+ container = new CompoundContainer("Large chest", nearbyContainer, container); -+ else -+ container = new CompoundContainer("Large chest", container, nearbyContainer); -+ break; -+ } -+ } -+ -+ player->openContainer(container); -+ return true; -+} -+ -+bool ChestTile::hasTileEntity() const -+{ -+ return true; -+} -+ -+TileEntity* ChestTile::newTileEntity() -+{ -+ return new ChestTileEntity(); -+} -diff --git a/source/world/tile/ChestTile.hpp b/source/world/tile/ChestTile.hpp -new file mode 100644 -index 000000000..051081631 ---- /dev/null -+++ b/source/world/tile/ChestTile.hpp -@@ -0,0 +1,20 @@ -+#pragma once -+ -+#include "Tile.hpp" -+ -+class ChestTile : public Tile -+{ -+public: -+ ChestTile(int id, int texture); -+public: -+ int getTexture(const LevelSource*, const TilePos& pos, Facing::Name face) const override; -+ int getTexture(Facing::Name face) const override; -+ bool mayPlace(const Level* level, const TilePos& pos) const override; -+ bool hasNeighbors(const Level* level, const TilePos& pos, int count) const; -+ void onRemove(Level* level, const TilePos& pos) override; -+ bool use(Level* level, const TilePos& pos, Player* var5) override; -+ bool hasTileEntity() const override; -+ TileEntity* newTileEntity() override; -+public: -+ Random m_chestRandom; -+}; -\ No newline at end of file -diff --git a/source/world/tile/CropsTile.cpp b/source/world/tile/CropsTile.cpp -index cb361a031..9cd432ede 100644 ---- a/source/world/tile/CropsTile.cpp -+++ b/source/world/tile/CropsTile.cpp -@@ -85,7 +85,7 @@ float CropsTile::getGrowthRate(Level* level, const TilePos& pos) - - if (diag || (hor && vert)) - { -- rate /= 2.0F; -+ rate /= 2.0f; - } - - return rate; -diff --git a/source/world/tile/FurnaceTile.cpp b/source/world/tile/FurnaceTile.cpp -new file mode 100644 -index 000000000..e5ea21f65 ---- /dev/null -+++ b/source/world/tile/FurnaceTile.cpp -@@ -0,0 +1,194 @@ -+#include "FurnaceTile.hpp" -+#include "world/level/Level.hpp" -+#include "entity/FurnaceTileEntity.hpp" -+ -+bool FurnaceTile::keepInventory = false; -+ -+FurnaceTile::FurnaceTile(int id, bool active) : Tile(id, TEXTURE_FURNACE_SIDE, Material::stone) -+{ -+ setTicking(true); -+ m_active = active; -+} -+ -+int FurnaceTile::getTexture(const LevelSource* level, const TilePos& pos, Facing::Name face) const -+{ -+ switch (face) -+ { -+ case Facing::UP: -+ case Facing::DOWN: -+ return m_TextureFrame + 17; -+ default: -+ { -+ int meta = level->getData(pos); -+ return face != meta ? m_TextureFrame : (m_active ? m_TextureFrame + 16 : m_TextureFrame - 1); -+ } -+ } -+} -+ -+void FurnaceTile::animateTick(Level* level, const TilePos& pos, Random* random) { -+ if (!m_active) -+ return; -+ -+ int data = level->getData(pos); -+ Vec3 particlePos(pos.x + 0.5f, pos.y + 0.0f + random->nextFloat() * 6.0f / 16.0f, pos.z + 0.5f); -+ const float outward = 0.52f; -+ float randomOffset = random->nextFloat() * 0.6f - 0.3f; -+ -+ if (random->nextFloat() < 0.1f) -+ level->playSound(Vec3(particlePos.x + 0.5f, particlePos.y + 0.5f, particlePos.z + 0.5f), "fire.fire_crackle", 1.0f, 1.0f); -+ -+ if (data == 4 || data == 5) -+ { -+ particlePos.x += data == 4 ? -outward : outward; -+ particlePos.z += randomOffset; -+ } -+ else if (data == 2 || data == 3) -+ { -+ particlePos.x += randomOffset; -+ particlePos.z += data == 2 ? -outward : outward; -+ } -+ -+ level->addParticle("smoke", particlePos); -+ level->addParticle("flame", particlePos); -+} -+ -+int FurnaceTile::getTexture(Facing::Name face) const -+{ -+ switch (face) { -+ case Facing::UP: -+ case Facing::DOWN: -+ return m_TextureFrame + 17; -+ case Facing::SOUTH: -+ return (m_active) ? m_TextureFrame + 16 : m_TextureFrame - 1; -+ default: -+ return m_TextureFrame; -+ } -+} -+ -+void FurnaceTile::onPlace(Level* level, const TilePos& pos) -+{ -+ Tile::onPlace(level, pos); -+ RecalculateLookDirection(level, pos); -+} -+ -+bool FurnaceTile::use(Level* level, const TilePos& pos, Player* player) -+{ -+ if (player->isSneaking() && player->getSelectedItem()) -+ return false; -+ -+ if (level->m_bIsClientSide) -+ return true; -+ -+ player->openFurnace(static_cast(level->getTileEntity(pos))); -+ return true; -+} -+ -+void FurnaceTile::setPlacedBy(Level* level, const TilePos& pos, Mob* mob) -+{ -+ int rot = Mth::floor(0.5f + (mob->m_rot.y * 4.0f / 360.0f)) & 3; -+ int data = 4; -+ -+ switch (rot) -+ { -+ case 0: -+ data = 2; -+ break; -+ case 1: -+ data = 5; -+ break; -+ case 2: -+ data = 3; -+ break; -+ } -+ -+ level->setData(pos, data); -+} -+ -+void FurnaceTile::onRemove(Level* level, const TilePos& pos) -+{ -+ if (keepInventory) -+ return; -+ -+ TileEntity* tileEnt = level->getTileEntity(pos); -+ -+ if (!tileEnt) -+ { -+ return; // this has to be wrapped in a guard or the compiler screams, thanks -Wmisleading-indentation -+ } -+ -+ FurnaceTileEntity* furnace = static_cast(tileEnt); -+ for (int i = 0; i < furnace->getContainerSize(); ++i) -+ { -+ ItemStack& item = furnace->getItem(i); -+ if (item.isEmpty()) -+ continue; -+ -+ TilePos splitSource( -+ m_random.nextFloat() * 0.8f + 0.1f, -+ m_random.nextFloat() * 0.8f + 0.1f, -+ m_random.nextFloat() * 0.8f + 0.1f -+ ); -+ -+ while (item.m_count > 0) -+ { -+ int splitCount = std::min(m_random.nextInt(21) + 10, static_cast(item.m_count)); -+ item.m_count -= splitCount; -+ ItemEntity* itemEnt = new ItemEntity(level, splitSource + pos, ItemStack(item.getId(), splitCount, item.getAuxValue())); -+ -+ float deviation = 0.05f; -+ itemEnt->m_vel.x = (double)((float)m_random.nextGaussian() * deviation); -+ itemEnt->m_vel.y = (double)((float)m_random.nextGaussian() * deviation + 0.2f); -+ itemEnt->m_vel.z = (double)((float)m_random.nextGaussian() * deviation); -+ level->addEntity(itemEnt); -+ } -+ } -+} -+ -+void FurnaceTile::SetLit(bool lit, Level* level, const TilePos& pos) -+{ -+ TileEntity* tileEntity = level->getTileEntity(pos); -+ int data = level->getData(pos); -+ -+ keepInventory = true; -+ level->setTile(pos, (lit) ? Tile::furnaceLit->m_ID : Tile::furnace->m_ID); -+ keepInventory = false; -+ -+ level->setData(pos, data); -+ tileEntity->clearRemoved(); -+ level->setTileEntity(pos, tileEntity); -+} -+ -+bool FurnaceTile::hasTileEntity() const -+{ -+ return true; -+} -+ -+TileEntity* FurnaceTile::newTileEntity() -+{ -+ return new FurnaceTileEntity(); -+} -+ -+int FurnaceTile::getResource(TileData, Random*) const -+{ -+ return Tile::furnace->m_ID; -+} -+ -+void FurnaceTile::RecalculateLookDirection(Level* level, const TilePos& pos) -+{ -+ TileID n = level->getTile(pos.north()); -+ TileID s = level->getTile(pos.south()); -+ TileID w = level->getTile(pos.west()); -+ TileID e = level->getTile(pos.east()); -+ uint8_t lookData = 3; -+ -+ if (Tile::solid[e] && !Tile::solid[w]) -+ lookData = 4; -+ else if (Tile::solid[w] && !Tile::solid[e]) -+ lookData = 5; -+ else if (Tile::solid[s] && !Tile::solid[n]) -+ lookData = 2; -+ else if (Tile::solid[n] && !Tile::solid[s]) -+ lookData = 3; -+ -+ level->setData(pos, lookData); -+} -diff --git a/source/world/tile/FurnaceTile.hpp b/source/world/tile/FurnaceTile.hpp -new file mode 100644 -index 000000000..bb0ac9359 ---- /dev/null -+++ b/source/world/tile/FurnaceTile.hpp -@@ -0,0 +1,33 @@ -+#pragma once -+ -+#include "Tile.hpp" -+ -+class FurnaceTile : public Tile -+{ -+public: -+ FurnaceTile(int id, bool active); -+ -+public: -+ int getTexture(const LevelSource*, const TilePos& pos, Facing::Name face) const override; -+ void animateTick(Level* level, const TilePos& pos, Random* random) override; -+ int getTexture(Facing::Name face) const override; -+ void onPlace(Level* level, const TilePos& pos) override; -+ bool use(Level* level, const TilePos& pos, Player* player) override; -+ void setPlacedBy(Level*, const TilePos& pos, Mob*) override; -+ void onRemove(Level* level, const TilePos& pos) override; -+ bool hasTileEntity() const override; -+ TileEntity* newTileEntity() override; -+ int getResource(TileData, Random*) const override; -+ -+public: -+ static void SetLit(bool lit, Level* level, const TilePos& pos); -+ static void RecalculateLookDirection(Level* level, const TilePos& pos); -+ -+private: -+ Random m_random; -+ static bool keepInventory; -+ -+public: -+ bool m_active; -+}; -+ -diff --git a/source/world/tile/MusicTile.cpp b/source/world/tile/MusicTile.cpp -new file mode 100644 -index 000000000..7842f458e ---- /dev/null -+++ b/source/world/tile/MusicTile.cpp -@@ -0,0 +1,98 @@ -+#include "MusicTile.hpp" -+#include "world/level/Level.hpp" -+#include "entity/MusicTileEntity.hpp" -+ -+MusicTile::MusicTile(TileID id, int texture) : Tile(id, texture, Material::wood) -+{ -+} -+ -+void MusicTile::neighborChanged(Level* level, const TilePos& pos, TileID tile) -+{ -+ if (tile <= TILE_AIR || !Tile::tiles[tile]->isSignalSource()) -+ return; -+ -+ TileEntity* tileEnt = level->getTileEntity(pos); -+ if (!tileEnt) -+ return; -+ -+ bool signalProvided = level->hasDirectSignal(pos); -+ MusicTileEntity* musEnt = static_cast(tileEnt); -+ if (musEnt->m_bOn == signalProvided) -+ return; -+ -+ if (signalProvided) -+ musEnt->play(level, pos); -+ -+ musEnt->m_bOn = signalProvided; -+} -+ -+bool MusicTile::use(Level* level, const TilePos& pos, Player* player) -+{ -+ if (player->isSneaking() && player->getSelectedItem()) -+ return false; -+ -+ if (level->m_bIsClientSide) -+ return true; -+ -+ TileEntity* tileEnt = level->getTileEntity(pos); -+ if (!tileEnt) -+ return false; -+ -+ MusicTileEntity* musEnt = static_cast(tileEnt); -+ -+ musEnt->tune(); -+ musEnt->play(level, pos); -+ return true; -+} -+ -+void MusicTile::attack(Level* level, const TilePos& pos, Player* player) -+{ -+ if (level->m_bIsClientSide) -+ return; -+ -+ TileEntity* tileEnt = level->getTileEntity(pos); -+ if (!tileEnt) -+ return; -+ -+ (static_cast(tileEnt))->play(level, pos); -+} -+ -+bool MusicTile::hasTileEntity() const -+{ -+ return true; -+} -+ -+TileEntity* MusicTile::newTileEntity() -+{ -+ return new MusicTileEntity(); -+} -+ -+void MusicTile::triggerEvent(Level* level, const TileEvent& event) -+{ -+ int instrument = event.b0; -+ int note = event.b1; -+ -+ -+ std::string soundType; -+ switch (instrument) -+ { -+ default: -+ soundType = "harp"; -+ break; -+ case 1: -+ soundType = "bd"; -+ break; -+ case 2: -+ soundType = "snare"; -+ break; -+ case 3: -+ soundType = "hat"; -+ break; -+ case 4: -+ soundType = "bassattack"; -+ break; -+ } -+ -+ level->playSound(event.pos + 0.5f, "note." + soundType, 3.0f, std::pow(2.0f, (note - 12) / 12.0f)); -+ level->addParticle("note", Vec3(event.pos.x + 0.5f, event.pos.y + 1.2f, event.pos.z + 0.5f), Vec3(note / 24.0f, 0, 0)); -+} -diff --git a/source/world/tile/MusicTile.hpp b/source/world/tile/MusicTile.hpp -new file mode 100644 -index 000000000..fcddfba82 ---- /dev/null -+++ b/source/world/tile/MusicTile.hpp -@@ -0,0 +1,17 @@ -+#pragma once -+ -+#include "Tile.hpp" -+ -+class MusicTile : public Tile -+{ -+public: -+ MusicTile(TileID id, int textureID); -+ -+public: -+ void neighborChanged(Level*, const TilePos& pos, TileID tile) override; -+ bool use(Level*, const TilePos&, Player*) override; -+ void attack(Level*, const TilePos&, Player*) override; -+ bool hasTileEntity() const override; -+ void triggerEvent(Level*, const TileEvent&) override; -+ TileEntity* newTileEntity() override; -+}; -\ No newline at end of file -diff --git a/source/world/tile/Tile.cpp b/source/world/tile/Tile.cpp -index 7c5646745..4aea4c4b7 100644 ---- a/source/world/tile/Tile.cpp -+++ b/source/world/tile/Tile.cpp -@@ -55,12 +55,12 @@ - #include "RocketLauncherTile.hpp" - //#include "RedStoneDustTile.hpp" - #include "CraftingTableTile.hpp" --//#include "FurnaceTile.hpp" -+#include "FurnaceTile.hpp" - #include "TallGrass.hpp" - #include "DeadBush.hpp" - //#include "Fern.hpp" - #include "CactusTile.hpp" --//#include "ChestTile.hpp" -+#include "ChestTile.hpp" - #include "PumpkinTile.hpp" - #include "SoulSandTile.hpp" - #include "GlowstoneTile.hpp" -@@ -79,7 +79,7 @@ - //#include "RedstoneTorchTile.hpp" - //#include "CakeTile.hpp" - //#include "DispenserTile.hpp" --//#include "MusicTile.hpp" -+#include "MusicTile.hpp" - //#include "RecordPlayerTile.hpp" - //#include "TrapDoorTile.hpp" - //#include "PortalTile.hpp" -@@ -205,7 +205,7 @@ Tile* Tile::init() - solid[m_ID] = isSolidRender(); - lightBlock[m_ID] = isSolidRender() ? 255 : 0; - translucent[m_ID] = m_pMaterial->blocksLight(); -- isEntityTile[m_ID] = 0; -+ isEntityTile[m_ID] = hasTileEntity(); - - m_toolMask = Tool::NONE; - m_requiredToolLevel = 0; -@@ -263,6 +263,11 @@ bool Tile::mayPick(TileData data, bool y) const - return mayPick(); - } - -+bool Tile::hasTileEntity() const -+{ -+ return false; -+} -+ - int Tile::getResource(TileData data, Random* pRandom) const - { - return m_ID; -@@ -278,6 +283,11 @@ int Tile::getSpawnResourcesAuxValue(int x) const - return 0; - } - -+TileEntity* Tile::newTileEntity() -+{ -+ return nullptr; -+} -+ - Tile* Tile::setToolTypes(unsigned int toolMask) - { - m_toolMask |= toolMask; -@@ -786,6 +796,31 @@ void Tile::initTiles() - ->setSoundType(Tile::SOUND_GRASS) - ->setDescriptionId("crops"); - -+ Tile::musicBlock = (new MusicTile(TILE_NOTE_BLOCK, TEXTURE_JUKEBOX_SIDE)) -+ ->init() -+ ->setSoundType(Tile::SOUND_WOOD) -+ ->setDestroyTime(0.8f) -+ ->setDescriptionId("musicBlock"); -+ -+ Tile::furnace = (new FurnaceTile(TILE_FURNACE, false)) -+ ->init() -+ ->setDestroyTime(3.5f) -+ ->setSoundType(Tile::SOUND_STONE) -+ ->setDescriptionId("furnace"); -+ -+ Tile::furnaceLit = (new FurnaceTile(TILE_FURNACE_LIT, true)) -+ ->init() -+ ->setLightEmission(14.0f / 16.0f) -+ ->setDestroyTime(3.5f) -+ ->setSoundType(Tile::SOUND_STONE) -+ ->setDescriptionId("furnace"); -+ -+ Tile::chest = (new ChestTile(TILE_CHEST, TEXTURE_CHEST_ONE_SIDE)) -+ ->init() -+ ->setDestroyTime(2.5f) -+ ->setSoundType(Tile::SOUND_WOOD) -+ ->setDescriptionId("chest"); -+ - // Great - Item::items[Tile::cloth->m_ID] = (new ClothItem(Tile::cloth->m_ID - C_MAX_TILES)) - ->setDescriptionId("cloth"); -@@ -963,12 +998,14 @@ void Tile::neighborChanged(Level* pLevel, const TilePos& pos, TileID tile) - - void Tile::onPlace(Level* pLevel, const TilePos& pos) - { -- -+ if (hasTileEntity()) -+ pLevel->setTileEntity(pos, newTileEntity()); - } - - void Tile::onRemove(Level* pLevel, const TilePos& pos) - { -- -+ if (hasTileEntity()) -+ pLevel->removeTileEntity(pos); - } - - bool Tile::containsX(const Vec3& v) -@@ -1279,4 +1316,8 @@ Tile - *Tile::web, - *Tile::fence, - *Tile::craftingTable, -- *Tile::crops; -+ *Tile::crops, -+ *Tile::musicBlock, -+ *Tile::furnace, -+ *Tile::furnaceLit, -+ *Tile::chest; -\ No newline at end of file -diff --git a/source/world/tile/Tile.hpp b/source/world/tile/Tile.hpp -index aad6af4c2..2a9bc0ad3 100644 ---- a/source/world/tile/Tile.hpp -+++ b/source/world/tile/Tile.hpp -@@ -29,6 +29,7 @@ class Entity; - class Mob; - class Player; - class LiquidTile; -+class TileEntity; - - class Tile - { -@@ -76,6 +77,7 @@ class Tile - virtual bool isSolidRender() const; - virtual bool mayPick() const; - virtual bool mayPick(TileData, bool) const; -+ virtual bool hasTileEntity() const; - virtual bool mayPlace(const Level*, const TilePos& pos) const; - virtual int getTickDelay() const; - virtual void tick(Level*, const TilePos& pos, Random*); -@@ -121,6 +123,7 @@ class Tile - virtual Tile* setDestroyTime(float); - virtual Tile* setTicking(bool); - virtual int getSpawnResourcesAuxValue(int) const; -+ virtual TileEntity* newTileEntity(); - Tile* setToolTypes(unsigned int toolMask); - Tile* setToolLevel(int toolLevel); - Tile* setToolTypesAndLevel(unsigned int toolMask, int toolLevel = 0); -@@ -241,7 +244,11 @@ class Tile - * web, - * fence, - * craftingTable, -- * crops; -+ * crops, -+ * furnace, -+ * furnaceLit, -+ * musicBlock, -+ * chest; - - public: - int m_TextureFrame; -diff --git a/source/world/tile/entity/ChestTileEntity.cpp b/source/world/tile/entity/ChestTileEntity.cpp -new file mode 100644 -index 000000000..f72f78a4c ---- /dev/null -+++ b/source/world/tile/entity/ChestTileEntity.cpp -@@ -0,0 +1,31 @@ -+#include "ChestTileEntity.hpp" -+ -+ChestTileEntity::ChestTileEntity() : SimpleContainer(27, "Chest") -+{ -+ m_pType = TileEntityType::chest; -+} -+ -+void ChestTileEntity::load(const CompoundTag& tag) -+{ -+ TileEntity::load(tag); -+ SimpleContainer::load(tag); -+} -+ -+void ChestTileEntity::save(CompoundTag& tag) const -+{ -+ TileEntity::save(tag); -+ SimpleContainer::save(tag); -+} -+ -+bool ChestTileEntity::stillValid(Player* player) const -+{ -+ if (m_pLevel->getTileEntity(m_pos) != this) -+ return false; -+ -+ return player->distanceToSqr(m_pos + 0.5f) <= 64.0f; -+} -+ -+void ChestTileEntity::setChanged() -+{ -+ TileEntity::setChanged(); -+} -diff --git a/source/world/tile/entity/ChestTileEntity.hpp b/source/world/tile/entity/ChestTileEntity.hpp -new file mode 100644 -index 000000000..38137f631 ---- /dev/null -+++ b/source/world/tile/entity/ChestTileEntity.hpp -@@ -0,0 +1,17 @@ -+#pragma once -+ -+#include "TileEntity.hpp" -+#include "world/level/Level.hpp" -+#include "world/inventory/SimpleContainer.hpp" -+ -+class ChestTileEntity : public TileEntity, public SimpleContainer { -+public: -+ ChestTileEntity(); -+ -+ void load(const CompoundTag& tag) override; -+ void save(CompoundTag& tag) const override; -+ -+ bool stillValid(Player* player) const override; -+ -+ virtual void setChanged() override; -+}; -diff --git a/source/world/tile/entity/FurnaceTileEntity.cpp b/source/world/tile/entity/FurnaceTileEntity.cpp -new file mode 100644 -index 000000000..1134d2736 ---- /dev/null -+++ b/source/world/tile/entity/FurnaceTileEntity.cpp -@@ -0,0 +1,163 @@ -+#include "FurnaceTileEntity.hpp" -+#include "world/item/crafting/FurnaceRecipes.hpp" -+#include "world/tile/FurnaceTile.hpp" -+ -+#define C_BURN_TIME (200) -+ -+FurnaceTileEntity::FurnaceTileEntity() -+ : SimpleContainer(3, "gui.furnace"), m_litTime(0), m_litDuration(0), m_tickCount(0) -+{ -+ m_pType = TileEntityType::furnace; -+} -+ -+bool FurnaceTileEntity::_canBurn() -+{ -+ if (getItem(0).isEmpty()) -+ return false; -+ -+ const ItemStack& result = FurnaceRecipes::singleton().getItemFor(this); -+ -+ // Not a furnace recipe -+ if (result.isEmpty()) -+ return false; -+ -+ // Nothing has started burning yet -+ if (getItem(2).isEmpty()) -+ return true; -+ -+ // Potential result/current result mismatch -+ if (getItem(2).getItem()->m_itemID != result.getItem()->m_itemID) -+ return false; -+ -+ if (getItem(2).m_count < getMaxStackSize() && getItem(2).m_count < getItem(2).getMaxStackSize()) -+ return true; -+ -+ // Return if result slot is full -+ return getItem(2).m_count < result.getMaxStackSize(); -+} -+ -+void FurnaceTileEntity::_burn() -+{ -+ if (!_canBurn()) -+ return; -+ -+ const ItemStack& result = FurnaceRecipes::singleton().getItemFor(this); -+ -+ // Either set the item in the result slot to the item, or add what's already there -+ if (getItem(2).isEmpty()) -+ setItem(2, result); -+ else if (getItem(2).getItem()->m_itemID == result.getItem()->m_itemID) -+ ++getItem(2).m_count; -+ -+ // Decrement burning item -+ if (getItem(0).m_count > 0) -+ --getItem(0).m_count; -+ -+ // No more burning item -+ if (getItem(0).m_count <= 0) -+ setItem(0, ItemStack::EMPTY); -+} -+ -+void FurnaceTileEntity::tick() -+{ -+ if (m_pLevel->m_bIsClientSide) -+ return; -+ -+ bool wasBurning = m_litTime > 0; -+ bool changed = false; -+ -+ if (m_litTime > 0) -+ --m_litTime; -+ -+ if (m_litTime == 0 && _canBurn()) -+ { -+ m_litDuration = m_litTime = FurnaceRecipes::singleton().getBurnDuration(getItem(1)); -+ if (m_litTime > 0) -+ { -+ changed = true; -+ if (!getItem(1).isEmpty()) -+ { -+ --getItem(1).m_count; -+ if (!getItem(1).m_count) -+ setItem(1, ItemStack::EMPTY); -+ } -+ } -+ } -+ -+ if (isLit() && _canBurn()) -+ { -+ ++m_tickCount; -+ if (m_tickCount == C_BURN_TIME) -+ { -+ m_tickCount = 0; -+ _burn(); -+ changed = true; -+ } -+ } -+ else -+ m_tickCount = 0; -+ -+ bool isBurning = m_litTime > 0; -+ if (isBurning != wasBurning) -+ { -+ changed = true; -+ FurnaceTile::SetLit(isBurning, m_pLevel, m_pos); -+ } -+ -+ if (changed) -+ setChanged(); -+} -+ -+bool FurnaceTileEntity::stillValid(Player* player) const -+{ -+ if (m_pLevel->getTileEntity(m_pos) != this) -+ return false; -+ -+ return player->distanceToSqr(m_pos + 0.5f) <= 64.0; -+} -+ -+void FurnaceTileEntity::setChanged() -+{ -+ TileEntity::setChanged(); -+} -+ -+void FurnaceTileEntity::load(const CompoundTag& tag) -+{ -+ TileEntity::load(tag); -+ SimpleContainer::load(tag); -+ -+ m_litTime = tag.getInt16("BurnTime"); -+ m_tickCount = tag.getInt16("CookTime"); -+ m_litDuration = FurnaceRecipes::singleton().getBurnDuration(getItem(1)); -+} -+ -+void FurnaceTileEntity::save(CompoundTag& tag) const -+{ -+ TileEntity::save(tag); -+ tag.putInt16("BurnTime", m_litTime); -+ tag.putInt16("CookTime", m_tickCount); -+ SimpleContainer::save(tag); -+} -+ -+std::string FurnaceTileEntity::getName() const -+{ -+ return "Furnace"; -+} -+ -+int FurnaceTileEntity::getBurnProgress(int height) -+{ -+ return m_tickCount * height / C_BURN_TIME; -+} -+ -+int FurnaceTileEntity::getLitProgress(int height) -+{ -+ if (m_litDuration == 0) -+ m_litDuration = C_BURN_TIME; -+ -+ return m_litTime * height / m_litDuration; -+} -+ -+bool FurnaceTileEntity::isLit() -+{ -+ return m_litTime > 0; -+} -\ No newline at end of file -diff --git a/source/world/tile/entity/FurnaceTileEntity.hpp b/source/world/tile/entity/FurnaceTileEntity.hpp -new file mode 100644 -index 000000000..55108282f ---- /dev/null -+++ b/source/world/tile/entity/FurnaceTileEntity.hpp -@@ -0,0 +1,33 @@ -+#pragma once -+ -+#include "TileEntity.hpp" -+#include "world/inventory/SimpleContainer.hpp" -+#include "world/level/Level.hpp" -+ -+class FurnaceTileEntity : public SimpleContainer, public TileEntity -+{ -+public: -+ FurnaceTileEntity(); -+ -+private: -+ bool _canBurn(); -+ void _burn(); -+ -+public: -+ void tick() override; -+ bool stillValid(Player* player) const override; -+ void setChanged() override; -+ void load(const CompoundTag& tag) override; -+ void save(CompoundTag& tag) const override; -+ std::string getName() const override; -+ -+public: -+ int getBurnProgress(int height); -+ int getLitProgress(int height); -+ bool isLit(); -+ -+public: -+ int m_litTime; -+ int m_litDuration; -+ int m_tickCount; -+}; -diff --git a/source/world/tile/entity/MusicTileEntity.cpp b/source/world/tile/entity/MusicTileEntity.cpp -new file mode 100644 -index 000000000..d38630bd7 ---- /dev/null -+++ b/source/world/tile/entity/MusicTileEntity.cpp -@@ -0,0 +1,46 @@ -+#include "MusicTileEntity.hpp" -+#include "world/level/Level.hpp" -+ -+MusicTileEntity::MusicTileEntity() : TileEntity(), m_note(0), m_bOn(false) -+{ -+ m_pType = TileEntityType::noteblock; -+} -+ -+void MusicTileEntity::load(const CompoundTag& tag) -+{ -+ TileEntity::load(tag); -+ m_note = static_cast(Mth::clamp(tag.getInt8("note"), 0, 24)); -+} -+ -+void MusicTileEntity::save(CompoundTag& tag) const -+{ -+ TileEntity::save(tag); -+ tag.putInt8("note", m_note); -+} -+ -+void MusicTileEntity::tune() -+{ -+ m_note = (m_note + 1) % 25; -+ setChanged(); -+} -+ -+void MusicTileEntity::play(Level* pLevel, const TilePos& pos) -+{ -+ // noteblocks only play if the block above them is air -+ if (pLevel->getMaterial(TilePos(pos.x, pos.y + 1, pos.z)) != Material::air) -+ return; -+ -+ int instrument = 0; -+ Material* below = pLevel->getMaterial(TilePos(pos.x, pos.y - 1, pos.z)); -+ -+ if (below == Material::stone) -+ instrument = 1; -+ else if (below == Material::sand) -+ instrument = 2; -+ else if (below == Material::glass) -+ instrument = 3; -+ else if (below == Material::wood) -+ instrument = 4; -+ -+ pLevel->tileEvent(TileEvent(pos, instrument, m_note)); -+} -\ No newline at end of file -diff --git a/source/world/tile/entity/MusicTileEntity.hpp b/source/world/tile/entity/MusicTileEntity.hpp -new file mode 100644 -index 000000000..9a8a1a60a ---- /dev/null -+++ b/source/world/tile/entity/MusicTileEntity.hpp -@@ -0,0 +1,21 @@ -+#pragma once -+ -+#include "TileEntity.hpp" -+ -+class MusicTileEntity : public TileEntity -+{ -+public: -+ MusicTileEntity(); -+ -+public: -+ void load(const CompoundTag& tag) override; -+ void save(CompoundTag& tag) const override; -+ -+public: -+ void tune(); -+ void play(Level* pLevel, const TilePos& pos); -+ -+public: -+ uint8_t m_note; -+ bool m_bOn; -+}; -\ No newline at end of file -diff --git a/source/world/tile/entity/TileEntity.cpp b/source/world/tile/entity/TileEntity.cpp -new file mode 100644 -index 000000000..9cd7b79ad ---- /dev/null -+++ b/source/world/tile/entity/TileEntity.cpp -@@ -0,0 +1,106 @@ -+#include "TileEntity.hpp" -+#include "common/Logger.hpp" -+#include "world/level/Level.hpp" -+ -+TileEntity::TileEntity() : m_bRemove(false), m_pLevel(nullptr) -+{ -+} -+ -+TileEntity::~TileEntity() -+{ -+} -+ -+TileEntity* TileEntity::LoadTileEntity(const CompoundTag& tag) -+{ -+ if (!tag.contains("id")) -+ { -+ LOG_W("Skipping TileEntity with no id!"); -+ return nullptr; -+ } -+ -+ std::string id = tag.getString("id"); -+ const TileEntityType* type = TileEntityType::GetType(id); -+ -+ if (!type) -+ { -+ LOG_I("Skipping TileEntity with id %s", id.c_str()); -+ return nullptr; -+ } -+ -+ TileEntity* newEnt = type->newTileEntity(); -+ newEnt->load(tag); -+ return newEnt; -+} -+ -+void TileEntity::load(const CompoundTag& tag) -+{ -+ m_pos.x = tag.getInt32("x"); -+ m_pos.y = tag.getInt32("y"); -+ m_pos.z = tag.getInt32("z"); -+} -+ -+void TileEntity::save(CompoundTag& tag) const -+{ -+ tag.putString("id", getId()); -+ tag.putInt32("x", m_pos.x); -+ tag.putInt32("y", m_pos.y); -+ tag.putInt32("z", m_pos.z); -+} -+ -+void TileEntity::tick() -+{ -+} -+ -+Packet* TileEntity::getUpdatePacket() -+{ -+ return nullptr; -+} -+ -+bool TileEntity::isRemoved() const -+{ -+ return m_bRemove; -+} -+ -+void TileEntity::setRemoved() -+{ -+ m_bRemove = true; -+} -+ -+void TileEntity::clearRemoved() -+{ -+ m_bRemove = false; -+} -+ -+int TileEntity::getData() const -+{ -+ return m_pLevel->getData(m_pos); -+} -+ -+void TileEntity::setData(int data) -+{ -+ m_pLevel->setData(m_pos, data); -+} -+ -+void TileEntity::setChanged() -+{ -+ // @TODO: tileEntityChanged -+ /* -+ if (m_pLevel) -+ m_pLevel->tileEntityChanged(m_pos, this); -+ */ -+} -+ -+float TileEntity::distanceToSqr(const Vec3& vec) const -+{ -+ return (m_pos + 0.5f).distanceToSqr(vec); -+} -+ -+Tile* TileEntity::getTile() const -+{ -+ return Tile::tiles[m_pLevel->getTile(m_pos)]; -+} -+ -+std::string TileEntity::getId() const -+{ -+ return (m_pType) ? m_pType->getName() : "Unknown"; -+} -\ No newline at end of file -diff --git a/source/world/tile/entity/TileEntity.hpp b/source/world/tile/entity/TileEntity.hpp -new file mode 100644 -index 000000000..2a3666fc7 ---- /dev/null -+++ b/source/world/tile/entity/TileEntity.hpp -@@ -0,0 +1,50 @@ -+#pragma once -+ -+#include "TileEntityType.hpp" -+#include "world/level/TilePos.hpp" -+#include "world/phys/Vec3.hpp" -+#include "nbt/CompoundTag.hpp" -+ -+class Tile; -+class Level; -+class Packet; -+ -+class TileEntity -+{ -+public: -+ TileEntity(); -+ virtual ~TileEntity(); -+ -+protected: -+ virtual std::string getId() const; -+ -+public: -+ virtual void load(const CompoundTag& tag); -+ virtual void save(CompoundTag& tag) const; -+ virtual void tick(); -+ virtual Packet* getUpdatePacket(); -+ const TileEntityType* getType() const; -+ virtual int getData() const; -+ virtual Tile* getTile() const; -+ -+public: -+ static TileEntity* LoadTileEntity(const CompoundTag& tag); -+ -+public: -+ float distanceToSqr(const Vec3& vec) const; -+ bool isRemoved() const; -+ void setRemoved(); -+ void clearRemoved(); -+ -+ void setData(int data); -+ void setChanged(); -+ -+protected: -+ const TileEntityType* m_pType; -+ bool m_bRemove; -+ -+public: -+ Level* m_pLevel; -+ TilePos m_pos; -+}; -+ -diff --git a/source/world/tile/entity/TileEntityType.cpp b/source/world/tile/entity/TileEntityType.cpp -new file mode 100644 -index 000000000..8ee14b727 ---- /dev/null -+++ b/source/world/tile/entity/TileEntityType.cpp -@@ -0,0 +1,51 @@ -+#include "TileEntityType.hpp" -+#include "FurnaceTileEntity.hpp" -+#include "ChestTileEntity.hpp" -+#include "MusicTileEntity.hpp" -+// #include "MobSpawnerTileEntity.hpp" -+// #include "DispenserTileEntity.hpp" -+// #include "SignTileEntity.hpp" -+// #include "RecordPlayerTileEntity.hpp" -+// #include "PistonMovingTileEntity.hpp" -+ -+std::map TileEntityType::_types; -+ -+TileEntityType* TileEntityType::furnace; -+TileEntityType* TileEntityType::chest; -+TileEntityType* TileEntityType::noteblock; -+ -+TileEntityType::TileEntityType(const std::string& name, CreateFunction func) : _name(name), _function(func) -+{ -+} -+ -+const std::string& TileEntityType::getName() const -+{ -+ return _name; -+} -+ -+TileEntity* TileEntityType::newTileEntity() const -+{ -+ return _function(); -+} -+ -+void TileEntityType::InitTileEntities() -+{ -+ furnace = RegisterTileEntity("Furnace"); -+ chest = RegisterTileEntity("Chest"); -+ noteblock = RegisterTileEntity("Music"); -+} -+ -+const TileEntityType* TileEntityType::GetType(const std::string& name) -+{ -+ return _types[name]; -+} -+ -+void TileEntityType::TeardownTileEntities() -+{ -+ // delete all heap allocated tile entity types (furnace, chest, etc.) -+ for (std::map::const_iterator it = _types.begin(); it != _types.end(); ++it) -+ { -+ SAFE_DELETE(it->second); -+ } -+ _types.clear(); -+} -\ No newline at end of file -diff --git a/source/world/tile/entity/TileEntityType.hpp b/source/world/tile/entity/TileEntityType.hpp -new file mode 100644 -index 000000000..3a981cf6c ---- /dev/null -+++ b/source/world/tile/entity/TileEntityType.hpp -@@ -0,0 +1,50 @@ -+#pragma once -+ -+#include -+#include -+#include -+ -+class Level; -+class TileEntity; -+ -+class TileEntityType -+{ -+public: -+ typedef TileEntity* (*CreateFunction)(); -+ -+public: -+ TileEntityType(const std::string& name, CreateFunction func); -+ -+public: -+ const std::string& getName() const; -+ TileEntity* newTileEntity() const; -+ -+public: -+ static void InitTileEntities(); -+ static const TileEntityType* GetType(const std::string& name); -+ static void TeardownTileEntities(); -+ -+private: -+ static std::map _types; -+ -+private: -+ std::string _name; -+ CreateFunction _function; -+ -+public: -+ static TileEntityType* furnace; -+ static TileEntityType* chest; -+ static TileEntityType* noteblock; -+ -+ template -+ static TileEntity* CreateType() { return new T(); } -+ -+public: -+ template -+ static TileEntityType* RegisterTileEntity(const std::string& name) -+ { -+ TileEntityType* type = new TileEntityType(name, &TileEntityType::CreateType); -+ _types[type->_name] = type; -+ return type; -+ } -+}; -\ No newline at end of file - -From 3054cbd410c68aa6f1ea5ce8c885caf3657b998b Mon Sep 17 00:00:00 2001 -From: hellosammu -Date: Sun, 15 Feb 2026 17:59:03 -0500 -Subject: [PATCH 02/22] furnace rot fix, useTouchscreen/isTouchscreen - ---- - source/client/app/Minecraft.cpp | 21 ++++++----- - source/client/app/Minecraft.hpp | 6 ++-- - source/client/app/NinecraftApp.cpp | 2 +- - source/client/gui/Gui.cpp | 17 +++++---- - source/client/gui/Gui.hpp | 1 + - source/client/gui/Screen.cpp | 2 +- - source/client/gui/components/OptionList.cpp | 3 +- - source/client/gui/components/TextBox.cpp | 19 ++++++---- - .../gui/screens/inventory/ContainerScreen.cpp | 6 ++-- - source/client/model/models/SpiderModel.cpp | 6 ++-- - source/client/options/Options.cpp | 16 +++++++-- - source/client/options/Options.hpp | 14 ++++++-- - source/client/player/input/Keyboard.cpp | 3 +- - source/client/renderer/Chunk.hpp | 2 ++ - source/client/renderer/Font.cpp | 6 ++-- - source/client/renderer/GameRenderer.cpp | 2 +- - source/client/renderer/LevelRenderer.cpp | 3 +- - source/client/renderer/LogoRenderer.cpp | 6 ++-- - source/client/renderer/TileRenderer.cpp | 6 ++-- - .../renderer/entity/HumanoidMobRenderer.cpp | 6 ++++ - .../renderer/entity/HumanoidMobRenderer.hpp | 1 + - .../client/renderer/entity/ItemRenderer.cpp | 6 ++-- - .../client/renderer/entity/SheepRenderer.cpp | 2 +- - source/client/sound/SoundData.cpp | 6 ++-- - source/client/sound/SoundEngine.hpp | 1 + - source/common/Logger.cpp | 5 ++- - source/common/Random.cpp | 9 +++-- - source/network/packets/AddMobPacket.hpp | 2 ++ - .../network/packets/SetEntityDataPacket.hpp | 2 ++ - source/renderer/GL/GL.cpp | 24 ++++++++----- - source/world/entity/Creeper.cpp | 3 +- - source/world/entity/MobSpawner.hpp | 2 +- - source/world/entity/Player.cpp | 12 ++++--- - source/world/entity/Player.hpp | 2 +- - source/world/inventory/ContainerMenu.cpp | 12 ++++--- - source/world/item/BowItem.cpp | 3 +- - source/world/item/ItemStack.hpp | 1 + - .../levelgen/chunk/RandomLevelSource.cpp | 6 ++-- - .../level/levelgen/chunk/TestChunkSource.cpp | 6 ++-- - source/world/level/path/BinaryHeap.cpp | 30 ++++++++-------- - source/world/level/path/BinaryHeap.hpp | 12 ++++--- - source/world/tile/ChestTile.cpp | 35 +++++++++++-------- - source/world/tile/ChestTile.hpp | 2 ++ - source/world/tile/CraftingTableTile.cpp | 3 +- - source/world/tile/FurnaceTile.cpp | 23 ++++++------ - source/world/tile/MusicTile.cpp | 2 +- - source/world/tile/PumpkinTile.cpp | 6 ++-- - 47 files changed, 231 insertions(+), 134 deletions(-) - -diff --git a/source/client/app/Minecraft.cpp b/source/client/app/Minecraft.cpp -index cd3f8986a..749a6a493 100644 ---- a/source/client/app/Minecraft.cpp -+++ b/source/client/app/Minecraft.cpp -@@ -173,12 +173,12 @@ GameMode* Minecraft::_createGameMode(GameType gameType, Level& level) - } - } - --void Minecraft::_reloadInput() -+void Minecraft::reloadInput() - { - if (m_pInputHolder) - delete m_pInputHolder; - -- if (isTouchscreen()) -+ if (useTouchscreen()) - { - m_pInputHolder = new TouchInputHolder(this, getOptions()); - } -@@ -206,7 +206,7 @@ void Minecraft::_reloadInput() - m_pLocalPlayer->m_pMoveInput = m_pInputHolder->getMoveInput(); - } - -- getOptions()->field_19 = !isTouchscreen(); -+ getOptions()->m_bUseMouseToBreak = !useTouchscreen(); - } - - int Minecraft::getLicenseId() -@@ -246,7 +246,7 @@ void Minecraft::grabMouse() - // This will call grabMouse again, so why are we calling it here? - //setScreen(nullptr); - -- if (useController() || isTouchscreen()) -+ if (useController() || useTouchscreen()) - return; // don't actually try to grab the mouse - - platform()->setMouseGrabbed(true); -@@ -254,7 +254,7 @@ void Minecraft::grabMouse() - - void Minecraft::recenterMouse() - { -- if (useController() || isTouchscreen()) -+ if (useController() || useTouchscreen()) - return; - - platform()->recenterMouse(); -@@ -364,9 +364,14 @@ bool Minecraft::isTouchscreen() const - return m_bIsTouchscreen; - } - -+bool Minecraft::useTouchscreen() const -+{ -+ return isTouchscreen() && !getOptions()->m_bUseController.get(); -+} -+ - bool Minecraft::useSplitControls() const - { -- return !m_bIsTouchscreen || getOptions()->m_splitControls.get(); -+ return !useTouchscreen() || getOptions()->m_splitControls.get(); - } - - bool Minecraft::useController() const -@@ -625,7 +630,7 @@ void Minecraft::tickInput() - #endif - } - -- if (getOptions()->field_19) -+ if (!getOptions()->m_bUseMouseToBreak) - continue; - - // @TODO: Replace with KeyboardBuildInput -@@ -672,7 +677,7 @@ void Minecraft::tickMouse() - * This is exactly what Minecraft Java does too - **/ - -- if (useController() || isTouchscreen()) -+ if (useController() || useTouchscreen()) - return; // don't actually try to recenter the mouse - - if (platform()->getRecenterMouseEveryTick()) // just for SDL1 -diff --git a/source/client/app/Minecraft.hpp b/source/client/app/Minecraft.hpp -index edd594852..6a112aaad 100644 ---- a/source/client/app/Minecraft.hpp -+++ b/source/client/app/Minecraft.hpp -@@ -44,9 +44,6 @@ class Minecraft : public App - void _resetPlayer(Player* player); - GameMode* _createGameMode(GameType gameType, Level& level); - --protected: -- void _reloadInput(); -- - public: - int getLicenseId(); - void setScreen(Screen * pScreen); -@@ -79,11 +76,13 @@ class Minecraft : public App - void handlePointerPressedButtonRelease(); - void handleKeyboardClosed(); - void resetInput(); -+ void reloadInput(); - void sendMessage(const std::string& message); - void respawnPlayer(); - void freeResources(bool bCopyMap); - std::string getVersionString(const std::string& str = Util::EMPTY_STRING) const; - bool isTouchscreen() const; -+ bool useTouchscreen() const; - bool useSplitControls() const; - bool useController() const; - -@@ -118,6 +117,7 @@ class Minecraft : public App - private: - // Value provided by the OS - static float _renderScaleMultiplier; -+ - public: - static float getRenderScaleMultiplier() { return _renderScaleMultiplier; } - static void setRenderScaleMultiplier(float value) { _renderScaleMultiplier = value; } -diff --git a/source/client/app/NinecraftApp.cpp b/source/client/app/NinecraftApp.cpp -index b805ddcd1..0a16d0d3d 100644 ---- a/source/client/app/NinecraftApp.cpp -+++ b/source/client/app/NinecraftApp.cpp -@@ -105,7 +105,7 @@ void NinecraftApp::_initInput() - m_bIsTouchscreen = platform()->isTouchscreen(); - getOptions()->m_bUseController.set(platform()->hasGamepad()); - getOptions()->loadControls(); -- _reloadInput(); -+ reloadInput(); - } - - void NinecraftApp::_updateStats() -diff --git a/source/client/gui/Gui.cpp b/source/client/gui/Gui.cpp -index d8e94fcdf..5e4784d4f 100644 ---- a/source/client/gui/Gui.cpp -+++ b/source/client/gui/Gui.cpp -@@ -259,8 +259,6 @@ void Gui::renderSlot(int slot, int x, int y, float f) - - ItemRenderer::singleton().renderGuiItem(m_pMinecraft->m_pFont, m_pMinecraft->m_pTextures, item, x, y, true); - } -- -- //ItemRenderer::renderGuiItemDecorations(m_pMinecraft->m_pFont, m_pMinecraft->m_pTextures, item, x, y); - } - - void Gui::renderSlotOverlay(int slot, int x, int y, float f) -@@ -311,7 +309,8 @@ void Gui::handleClick(int clickID, int mouseX, int mouseY) - if (slot == -1) - return; - -- if (m_pMinecraft->isTouchscreen() && slot == getNumSlots() - 1) -+ // Final slot on touch opens inventory -+ if (m_pMinecraft->useTouchscreen() && slot == getNumSlots() - 1) - { - if (m_pMinecraft->m_pGameMode->isSurvivalType()) - m_pMinecraft->setScreen(new InventoryScreen(m_pMinecraft->m_pLocalPlayer)); -@@ -360,7 +359,7 @@ void Gui::handleKeyPressed(int keyCode) - if (slotL || slotR) - { - int maxItems = getNumSlots() - 1; -- if (m_pMinecraft->isTouchscreen()) -+ if (m_pMinecraft->useTouchscreen()) - maxItems--; - SlotID* slot = &m_pMinecraft->m_pLocalPlayer->m_pInventory->m_selectedSlot; - -@@ -672,7 +671,7 @@ void Gui::renderToolBar(float f, float alpha) - - textures->loadAndBindTexture(C_BLOCKS_NAME); - -- int diff = mc->isTouchscreen(); -+ int diff = mc->useTouchscreen(); - - int slotX = -hotbarWidth / 2 + 3; - for (int i = 0; i < nSlots - diff; i++) -@@ -694,8 +693,8 @@ void Gui::renderToolBar(float f, float alpha) - - field_A3C = false; - -- // blit the "more items" button -- if (mc->isTouchscreen()) -+ // blit the "more items" button if using touch -+ if (mc->useTouchscreen()) - { - textures->loadAndBindTexture(C_TERRAIN_NAME); - blit(hotbarWidth / 2 - 19, -19, 208, 208, 16, 16, 0, 0); -@@ -704,7 +703,7 @@ void Gui::renderToolBar(float f, float alpha) - - int Gui::getNumSlots() - { -- if (m_pMinecraft->isTouchscreen()) -+ if (m_pMinecraft->useTouchscreen()) - return 6; - - return 9; -@@ -712,7 +711,7 @@ int Gui::getNumSlots() - - int Gui::getNumUsableSlots() - { -- return getNumSlots() - m_pMinecraft->isTouchscreen(); -+ return getNumSlots() - m_pMinecraft->useTouchscreen(); - } - - RectangleArea Gui::getRectangleArea(bool b) -diff --git a/source/client/gui/Gui.hpp b/source/client/gui/Gui.hpp -index 9957d2f65..156f13788 100644 ---- a/source/client/gui/Gui.hpp -+++ b/source/client/gui/Gui.hpp -@@ -42,6 +42,7 @@ class Gui : public GuiComponent - - private: - static bool _isVignetteAvailable; -+ - public: - static bool isVignetteAvailable() { return _isVignetteAvailable; } - static void setIsVignetteAvailable(bool value) { _isVignetteAvailable = value; } -diff --git a/source/client/gui/Screen.cpp b/source/client/gui/Screen.cpp -index 230a85921..7aec2e65e 100644 ---- a/source/client/gui/Screen.cpp -+++ b/source/client/gui/Screen.cpp -@@ -393,7 +393,7 @@ void Screen::pointerPressed(const MenuPointer& pointer, MouseButtonType btn) // - { - m_pClickedElement = element; - -- if (!m_pMinecraft->isTouchscreen()) -+ if (!m_pMinecraft->useTouchscreen()) - { - if (_useController()) - m_pMinecraft->m_pSoundEngine->playUI(C_SOUND_UI_PRESS); -diff --git a/source/client/gui/components/OptionList.cpp b/source/client/gui/components/OptionList.cpp -index 062f32f5e..26e7afa1f 100644 ---- a/source/client/gui/components/OptionList.cpp -+++ b/source/client/gui/components/OptionList.cpp -@@ -173,7 +173,8 @@ void OptionList::initControlsMenu() - - if (!m_pMinecraft->isTouchscreen()) - m_items[idxSplit]->setEnabled(false); -- m_items[idxController]->setEnabled(false); -+ if (!m_pMinecraft->m_pPlatform->hasGamepad()) -+ m_items[idxController]->setEnabled(false); - } - - void OptionList::initVideoMenu() -diff --git a/source/client/gui/components/TextBox.cpp b/source/client/gui/components/TextBox.cpp -index f18371498..36d64344d 100644 ---- a/source/client/gui/components/TextBox.cpp -+++ b/source/client/gui/components/TextBox.cpp -@@ -149,7 +149,8 @@ bool TextBox::pointerPressed(Minecraft* pMinecraft, const MenuPointer& pointer) - - #ifndef HANDLE_CHARS_SEPARATELY - --char TextBox::guessCharFromKey(int key) { -+char TextBox::guessCharFromKey(int key) -+{ - bool bShiftPressed = m_pParent->m_pMinecraft->platform()->shiftPressed(); - char chr = '\0'; - if (key >= AKEYCODE_A && key <= AKEYCODE_Z) -@@ -218,13 +219,15 @@ void TextBox::handleButtonPress(Minecraft* pMinecraft, int key) - - #ifndef HANDLE_CHARS_SEPARATELY - char guess = guessCharFromKey(key); -- if (guess != '\0') { -+ if (guess != '\0') -+ { - handleTextChar(guess); - return; - } - #endif - -- switch (key) { -+ switch (key) -+ { - case AKEYCODE_DEL: - { - // handled elsewhere, do not dupe -@@ -298,7 +301,8 @@ void TextBox::handleTextChar(Minecraft* pMinecraft, int k) - if (!hasFocus()) - return; - -- switch (k) { -+ switch (k) -+ { - case '\b': // BACKSPACE - case '\x7f': // DELETE - { -@@ -546,10 +550,13 @@ void TextBox::recalculateScroll() - } - else - { -- if (m_scrollPos == int(m_text.length())) { -+ if (m_scrollPos == int(m_text.length())) -+ { - LOG_W("Text Box Is Too Small"); - break; -- } else { -+ } -+ else -+ { - m_scrollPos++; - } - } -diff --git a/source/client/gui/screens/inventory/ContainerScreen.cpp b/source/client/gui/screens/inventory/ContainerScreen.cpp -index 991e74357..98d0b447d 100644 ---- a/source/client/gui/screens/inventory/ContainerScreen.cpp -+++ b/source/client/gui/screens/inventory/ContainerScreen.cpp -@@ -234,14 +234,14 @@ void ContainerScreen::render(float partialTicks) - void ContainerScreen::pointerPressed(const MenuPointer& pointer, MouseButtonType button) - { - Screen::pointerPressed(pointer, button); -- if (m_pMinecraft->isTouchscreen()) return; -+ if (m_pMinecraft->useTouchscreen()) return; - slotClicked(pointer, button); - } - - void ContainerScreen::pointerReleased(const MenuPointer& pointer, MouseButtonType button) - { - Screen::pointerReleased(pointer, button); -- if (m_pMinecraft->isTouchscreen() && m_timeSlotDragged < 5) -+ if (m_pMinecraft->useTouchscreen() && m_timeSlotDragged < 5) - slotClicked(pointer, button); - m_timeSlotDragged = 0; - } -@@ -253,7 +253,7 @@ void ContainerScreen::handlePointerPressed(bool isPressed) - m_timeSlotDragged++; - else m_timeSlotDragged = 0; - -- if (m_pMinecraft->isTouchscreen() && m_timeSlotDragged % 5 == 0) -+ if (m_pMinecraft->useTouchscreen() && m_timeSlotDragged % 5 == 0) - { - slotClicked(m_menuPointer, MOUSE_BUTTON_RIGHT); - } -diff --git a/source/client/model/models/SpiderModel.cpp b/source/client/model/models/SpiderModel.cpp -index a3293ecb4..677798049 100644 ---- a/source/client/model/models/SpiderModel.cpp -+++ b/source/client/model/models/SpiderModel.cpp -@@ -57,7 +57,8 @@ SpiderModel::~SpiderModel() - { - } - --void SpiderModel::render(float time, float r, float bob, float yRot, float xRot, float scale) { -+void SpiderModel::render(float time, float r, float bob, float yRot, float xRot, float scale) -+{ - setupAnim(time, r, bob, yRot, xRot, scale); - m_head.render(scale); - -@@ -74,7 +75,8 @@ void SpiderModel::render(float time, float r, float bob, float yRot, float xRot, - m_leg7.render(scale); - } - --void SpiderModel::setupAnim(float time, float r, float bob, float yRot, float xRot, float scale) { -+void SpiderModel::setupAnim(float time, float r, float bob, float yRot, float xRot, float scale) -+{ - m_head.m_rot.y = yRot / 57.295776f; - m_head.m_rot.x = xRot / 57.295776f; - -diff --git a/source/client/options/Options.cpp b/source/client/options/Options.cpp -index 6cbc4bead..546f53a58 100644 ---- a/source/client/options/Options.cpp -+++ b/source/client/options/Options.cpp -@@ -41,11 +41,11 @@ void Options::_initDefaultValues() - field_16 = 0; - field_240 = 1; - field_1C = "Default"; -- field_19 = 1; -+ m_bUseMouseToBreak = true; - #ifdef ORIGINAL_CODE - m_viewDistance.set(2); - m_thirdPerson.set(0); -- field_19 = 0; -+ m_bUseMouseToBreak = false; - #endif - - // Force this on until we get a proper UI -@@ -59,7 +59,7 @@ static UITheme GetDefaultUiTheme(Minecraft* mc) - #if MC_PLATFORM_XBOX360 - return UI_CONSOLE; - #else -- return mc->isTouchscreen() ? UI_POCKET : UI_JAVA; -+ return (mc->useTouchscreen() || mc->isTouchscreen()) ? UI_POCKET : UI_JAVA; - #endif - } - -@@ -126,6 +126,7 @@ Options::Options(Minecraft* mc, const std::string& folderPath) : - add(m_playerName); - add(m_debugText); - add(m_lang); -+ add(m_bUseController); - add(m_uiTheme); - add(m_logoType); - add(m_hudSize); -@@ -803,3 +804,12 @@ void UIThemeOption::apply() - m_pMinecraft->getOptions()->m_logoType.apply(); - } - } -+ -+void ControllerOption::apply() -+{ -+ // @TODO: This works but ultimately needs to be a multi-select (KBM, controller, touch) instead of a single option. -+ // Either that or figure out an automatic way to switch between them based on input like how Minecraft Bedrock does -+ // For now, I just wanted to be able to switch to controller input on mobile devices. -+ if (m_pMinecraft && m_pMinecraft->m_pInputHolder) -+ m_pMinecraft->reloadInput(); -+} -\ No newline at end of file -diff --git a/source/client/options/Options.hpp b/source/client/options/Options.hpp -index 99bacdf1a..1fe458dae 100644 ---- a/source/client/options/Options.hpp -+++ b/source/client/options/Options.hpp -@@ -214,6 +214,14 @@ class GraphicsOption : public BoolOption - void apply() override; - }; - -+class ControllerOption : public BoolOption -+{ -+public: -+ ControllerOption(const std::string& key, const std::string& name, bool initial = true) : BoolOption(key, name, initial) {} -+ -+ void apply() override; -+}; -+ - class FancyGraphicsOption : public GraphicsOption - { - public: -@@ -329,6 +337,7 @@ class Options - { - public: - struct KeyBind; -+ - private: - static bool _hasResourcePack(const ResourcePack& pack, ResourcePackStack& packs); - static void _tryAddResourcePack(const std::string& name, ResourcePackStack& packs); -@@ -358,6 +367,7 @@ class Options - void _initDefaultValues(); - void _load(); - AsyncTask _saveAsync(); -+ - public: - Options(Minecraft*, const std::string& folderPath = ""); - -@@ -399,7 +409,7 @@ class Options - uint8_t field_16; - FancyGraphicsOption m_fancyGraphics; - AOOption m_ambientOcclusion; -- uint8_t field_19; // use Mouse as input for breaking -+ bool m_bUseMouseToBreak; - std::string field_1C; - ValuesOption m_difficulty; - BoolOption m_hideGui; -@@ -419,7 +429,7 @@ class Options - GraphicsOption m_fancyGrass; - GraphicsOption m_biomeColors; - BoolOption m_splitControls; -- BoolOption m_bUseController; -+ ControllerOption m_bUseController; - BoolOption m_dynamicHand; - BoolOption m_menuPanorama; - GuiScaleOption m_guiScale; -diff --git a/source/client/player/input/Keyboard.cpp b/source/client/player/input/Keyboard.cpp -index 562003650..0f82cc3e8 100644 ---- a/source/client/player/input/Keyboard.cpp -+++ b/source/client/player/input/Keyboard.cpp -@@ -18,9 +18,8 @@ Keyboard::KeyState Keyboard::_states[KEYBOARD_STATES_SIZE]; - void Keyboard::feed(KeyState state, int key) - { - // Prevent Crashes -- if (key >= KEYBOARD_STATES_SIZE || key < 0) { -+ if (key >= KEYBOARD_STATES_SIZE || key < 0) - return; -- } - - _inputs.push_back(KeyboardAction(key, state)); - -diff --git a/source/client/renderer/Chunk.hpp b/source/client/renderer/Chunk.hpp -index 957ef0446..aea9643c1 100644 ---- a/source/client/renderer/Chunk.hpp -+++ b/source/client/renderer/Chunk.hpp -@@ -59,8 +59,10 @@ class Chunk - bool field_54; - RenderChunk m_renderChunks[Tile::RENDER_LAYERS_COUNT]; - Tesselator* m_pTesselator; -+ - private: - int m_lists; -+ - public: - bool m_bCompiled; - bool m_bDirty; -diff --git a/source/client/renderer/Font.cpp b/source/client/renderer/Font.cpp -index 6ec81196f..e66f98b6e 100644 ---- a/source/client/renderer/Font.cpp -+++ b/source/client/renderer/Font.cpp -@@ -138,7 +138,8 @@ void Font::drawOutlinedString(const std::string& str, int x, int y, const Color& - for (int yi = 0; yi < 3; ++yi) - { - int t1 = translations[yi]; -- if (t != 0 || t1 != 0) { -+ if (t != 0 || t1 != 0) -+ { - MatrixStack::Ref matrix = MatrixStack::World.push(); - matrix->translate(Vec3(t, t1, 0)); - drawScalable(str, x, y, outlineColor, scale, false); -@@ -290,7 +291,8 @@ std::vector Font::split(const std::string& text, int maxWidth) - std::istringstream iss(paragraph); - std::string word; - -- while (iss >> word) { -+ while (iss >> word) -+ { - std::string testLine = currentLine.empty() ? word : currentLine + " " + word; - - if (width(testLine) <= maxWidth) -diff --git a/source/client/renderer/GameRenderer.cpp b/source/client/renderer/GameRenderer.cpp -index d71fd3d4e..c265d6b5e 100644 ---- a/source/client/renderer/GameRenderer.cpp -+++ b/source/client/renderer/GameRenderer.cpp -@@ -643,7 +643,7 @@ void GameRenderer::render(const Timer& timer) - int mouseY = -9999; - bool bMouseData = false; - -- if (m_pMinecraft->isTouchscreen()) -+ if (m_pMinecraft->useTouchscreen()) - { - int pointerId = Multitouch::getFirstActivePointerIdExThisUpdate(); - if (pointerId >= 0) -diff --git a/source/client/renderer/LevelRenderer.cpp b/source/client/renderer/LevelRenderer.cpp -index 98a311c52..f9c47bc95 100644 ---- a/source/client/renderer/LevelRenderer.cpp -+++ b/source/client/renderer/LevelRenderer.cpp -@@ -1163,7 +1163,8 @@ bool LevelRenderer::updateDirtyChunks(const Entity& camera, bool b) - if (--j <= 0) - continue; - -- for (int k = j; --k != 0;) { -+ for (int k = j; --k != 0;) -+ { - pChunks[k - 1] = pChunks[k]; - } - -diff --git a/source/client/renderer/LogoRenderer.cpp b/source/client/renderer/LogoRenderer.cpp -index c5dac8dc3..e9cdbbfeb 100644 ---- a/source/client/renderer/LogoRenderer.cpp -+++ b/source/client/renderer/LogoRenderer.cpp -@@ -414,8 +414,10 @@ Tile* TitleTile::getRandomTile(Tile* except1, Tile* except2) - for (;;) - { - id = _random.nextInt(256); -- for (int i = 0; i < _tileBlockListSize; i++) { -- if (_tileBlockList[i] == id) { -+ for (int i = 0; i < _tileBlockListSize; i++) -+ { -+ if (_tileBlockList[i] == id) -+ { - // N.B. Air does not have a tile - id = TILE_AIR; - break; -diff --git a/source/client/renderer/TileRenderer.cpp b/source/client/renderer/TileRenderer.cpp -index 575742689..ed07fc37b 100644 ---- a/source/client/renderer/TileRenderer.cpp -+++ b/source/client/renderer/TileRenderer.cpp -@@ -1168,7 +1168,8 @@ bool TileRenderer::tesselateFenceInWorld(Tile* tile, const TilePos& pos) - bool connectsHorizontally = tileWest || tileEast; - bool connectsVertically = tileNorth || tileSouth; - -- if (!connectsHorizontally && !connectsVertically) { -+ if (!connectsHorizontally && !connectsVertically) -+ { - connectsHorizontally = true; - } - -@@ -2793,7 +2794,8 @@ void TileRenderer::renderTile(const FullTile& tile, const mce::MaterialPtr& mate - float v6 = v5 * 2.0f; - for (int i = 0; i < 4; i++) - { -- switch (i) { -+ switch (i) -+ { - case 0: tileType->setShape(0.5f - v6, 0.0f, 0.0f, 0.5f + v6, 1.0f, v6 * 2.0f); break; - case 1: tileType->setShape(0.5f - v6, 0.0f, 1.0f - (v6 * 2.0f), 0.5f + v6, 1.0f, 1.0f); break; - case 2: tileType->setShape(0.5f - v5, 1.0f - v5 * 3.0f, -v5 * 2.0f, 0.5f + v5, 1.0f - v5, 1.0f + v5 * 2.0f); break; -diff --git a/source/client/renderer/entity/HumanoidMobRenderer.cpp b/source/client/renderer/entity/HumanoidMobRenderer.cpp -index c448e50c7..31f1d6c26 100644 ---- a/source/client/renderer/entity/HumanoidMobRenderer.cpp -+++ b/source/client/renderer/entity/HumanoidMobRenderer.cpp -@@ -203,3 +203,9 @@ void HumanoidMobRenderer::renderHand(const Entity& entity, float a) - } - #endif - } -+ -+void HumanoidMobRenderer::scale(const Mob &mob, Matrix &matrix, float a) -+{ -+ // players are actually 15/16ths the size than any other bipedal mob -+ matrix.scale((mob.isPlayer()) ? (15.0f / 16.0f) : 1.0f); -+} -diff --git a/source/client/renderer/entity/HumanoidMobRenderer.hpp b/source/client/renderer/entity/HumanoidMobRenderer.hpp -index bcb072c89..62b900098 100644 ---- a/source/client/renderer/entity/HumanoidMobRenderer.hpp -+++ b/source/client/renderer/entity/HumanoidMobRenderer.hpp -@@ -21,6 +21,7 @@ class HumanoidMobRenderer : public MobRenderer - virtual void onGraphicsReset() override; - - void renderHand(const Entity& entity, float a); -+ void scale(const Mob& mob, Matrix& matrix, float a); - - public: - HumanoidModel* m_pHumanoidModel; -diff --git a/source/client/renderer/entity/ItemRenderer.cpp b/source/client/renderer/entity/ItemRenderer.cpp -index aa3d9973f..e55794ba5 100644 ---- a/source/client/renderer/entity/ItemRenderer.cpp -+++ b/source/client/renderer/entity/ItemRenderer.cpp -@@ -192,7 +192,8 @@ void ItemRenderer::renderGuiItemOverlay(Font* font, Textures* textures, ItemStac - return; - - // Draw damage amount -- if (item.isDamaged()) { -+ if (item.isDamaged()) -+ { - int duraWidth = ceilf(13.0f - static_cast(item.getDamageValue()) * 13.0f / static_cast(item.getMaxDamage())); - int duraPercent = ceilf(255.0f - static_cast(item.getDamageValue()) * 255.0f / static_cast(item.getMaxDamage())); - -@@ -207,7 +208,8 @@ void ItemRenderer::renderGuiItemOverlay(Font* font, Textures* textures, ItemStac - blitRect(t, x + 2, y + 13, duraWidth, 1, duraColor); - } - -- if (item.m_count <= 1) { -+ if (item.m_count <= 1) -+ { - return; - } - -diff --git a/source/client/renderer/entity/SheepRenderer.cpp b/source/client/renderer/entity/SheepRenderer.cpp -index 3e864a296..ed2ef91b8 100644 ---- a/source/client/renderer/entity/SheepRenderer.cpp -+++ b/source/client/renderer/entity/SheepRenderer.cpp -@@ -15,7 +15,7 @@ int SheepRenderer::prepareArmor(const Mob& mob, int layer, float a) - const Sheep& sheep = (const Sheep&)mob; - if (layer == 0 && !sheep.isSheared()) - { -- bindTexture("/mob/sheep_fur.png"); -+ bindTexture("mob/sheep_fur.png"); - float brightness = sheep.getBrightness(a); - int color = sheep.getColor(); - currentShaderColor = Sheep::COLOR[color]; -diff --git a/source/client/sound/SoundData.cpp b/source/client/sound/SoundData.cpp -index 44c6bb1ed..23ab30a8c 100644 ---- a/source/client/sound/SoundData.cpp -+++ b/source/client/sound/SoundData.cpp -@@ -81,11 +81,13 @@ bool SoundDesc::_load(const char* category, const char *name) - } - location.path = "sound/" + std::string(name) + ".pcm"; - ret = _loadPcm(location); -- if (!ret) { -+ if (!ret) -+ { - m_codecType = AudioCodec::NONE; - LOG_W("Failed to load sound \"%s\"!", name); - return false; -- } else -+ } -+ else - return true; - } - -diff --git a/source/client/sound/SoundEngine.hpp b/source/client/sound/SoundEngine.hpp -index 620a108e0..e01b33f4e 100644 ---- a/source/client/sound/SoundEngine.hpp -+++ b/source/client/sound/SoundEngine.hpp -@@ -43,6 +43,7 @@ class SoundEngine - - public: - SoundSystem* m_pSoundSystem; -+ - private: - SoundRepository m_sounds; - SoundPathRepository m_songs; -diff --git a/source/common/Logger.cpp b/source/common/Logger.cpp -index a98c4559f..d0115bc6e 100644 ---- a/source/common/Logger.cpp -+++ b/source/common/Logger.cpp -@@ -15,11 +15,10 @@ Logger* Logger::singleton() - void Logger::setSingleton(Logger* logger) - { - // Stick with the first output handle we get -- if (!m_singleton) { -+ if (!m_singleton) - m_singleton = logger; -- } else { -+ else - m_singleton->print(LOG_ERR, "Logging already setup!"); -- } - } - - Logger::~Logger() -diff --git a/source/common/Random.cpp b/source/common/Random.cpp -index 66c9b30f8..8ea1c3763 100644 ---- a/source/common/Random.cpp -+++ b/source/common/Random.cpp -@@ -35,7 +35,8 @@ void Random::setSeed(int32_t seed) - void Random::init_genrand(uint32_t s) - { - mt[0] = s & 0xffffffffUL; -- for (mti = 1; mti < N; mti++) { -+ for (mti = 1; mti < N; mti++) -+ { - mt[mti] = - (1812433253UL * (mt[mti - 1] ^ (mt[mti - 1] >> 30)) + mti); - /* See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier. */ -@@ -65,11 +66,13 @@ uint32_t Random::genrand_int32() - if (mti == N+1) /* if init_genrand() has not been called, */ - init_genrand(5489UL); /* a default initial seed is used */ - -- for (kk=0;kk> 1) ^ mag01[y & 0x1UL]; - } -- for (;kk> 1) ^ mag01[y & 0x1UL]; - } -diff --git a/source/network/packets/AddMobPacket.hpp b/source/network/packets/AddMobPacket.hpp -index 0fc9d397e..8d311e986 100644 ---- a/source/network/packets/AddMobPacket.hpp -+++ b/source/network/packets/AddMobPacket.hpp -@@ -17,11 +17,13 @@ class AddMobPacket : public Packet - void write(RakNet::BitStream&) override; - void read(RakNet::BitStream&) override; - const SynchedEntityData::ItemsArray& getUnpackedData() const { return m_unpack; } -+ - public: - int32_t m_entityId; - int32_t m_entityTypeId; - Vec3 m_pos; - Vec2 m_rot; -+ - private: - SynchedEntityData m_entityData; - SynchedEntityData::ItemsArray m_unpack; -diff --git a/source/network/packets/SetEntityDataPacket.hpp b/source/network/packets/SetEntityDataPacket.hpp -index 917e42e2b..cd2b18e4a 100644 ---- a/source/network/packets/SetEntityDataPacket.hpp -+++ b/source/network/packets/SetEntityDataPacket.hpp -@@ -17,9 +17,11 @@ class SetEntityDataPacket : public Packet - void write(RakNet::BitStream&) override; - void read(RakNet::BitStream&) override; - const SynchedEntityData::ItemsArray& getUnpackedData() const { return m_packedItems; } -+ - public: - int32_t m_entityId; - bool m_bIsIncoming; -+ - private: - SynchedEntityData::ItemsArray m_packedItems; - }; -diff --git a/source/renderer/GL/GL.cpp b/source/renderer/GL/GL.cpp -index 230895f46..12ffd16ad 100644 ---- a/source/renderer/GL/GL.cpp -+++ b/source/renderer/GL/GL.cpp -@@ -157,25 +157,29 @@ int glhInvertMatrixf2(float* m, float* out) - r2[3] -= m2 * s; - r3[3] -= m3 * s; - s = r0[4]; -- if (s != 0.0f) { -+ if (s != 0.0f) -+ { - r1[4] -= m1 * s; - r2[4] -= m2 * s; - r3[4] -= m3 * s; - } - s = r0[5]; -- if (s != 0.0f) { -+ if (s != 0.0f) -+ { - r1[5] -= m1 * s; - r2[5] -= m2 * s; - r3[5] -= m3 * s; - } - s = r0[6]; -- if (s != 0.0f) { -+ if (s != 0.0f) -+ { - r1[6] -= m1 * s; - r2[6] -= m2 * s; - r3[6] -= m3 * s; - } - s = r0[7]; -- if (s != 0.0f) { -+ if (s != 0.0f) -+ { - r1[7] -= m1 * s; - r2[7] -= m2 * s; - r3[7] -= m3 * s; -@@ -195,22 +199,26 @@ int glhInvertMatrixf2(float* m, float* out) - r2[3] -= m2 * r1[3]; - r3[3] -= m3 * r1[3]; - s = r1[4]; -- if (0.0f != s) { -+ if (0.0f != s) -+ { - r2[4] -= m2 * s; - r3[4] -= m3 * s; - } - s = r1[5]; -- if (0.0f != s) { -+ if (0.0f != s) -+ { - r2[5] -= m2 * s; - r3[5] -= m3 * s; - } - s = r1[6]; -- if (0.0f != s) { -+ if (0.0f != s) -+ { - r2[6] -= m2 * s; - r3[6] -= m3 * s; - } - s = r1[7]; -- if (0.0f != s) { -+ if (0.0f != s) -+ { - r2[7] -= m2 * s; - r3[7] -= m3 * s; - } -diff --git a/source/world/entity/Creeper.cpp b/source/world/entity/Creeper.cpp -index 5046ff694..e55485e5b 100644 ---- a/source/world/entity/Creeper.cpp -+++ b/source/world/entity/Creeper.cpp -@@ -76,7 +76,8 @@ void Creeper::checkHurtTarget(Entity* pEnt, float f) - { - setSwellDir(-1); - m_swell--; -- if (m_swell < 0) { -+ if (m_swell < 0) -+ { - m_swell = 0; - } - } -diff --git a/source/world/entity/MobSpawner.hpp b/source/world/entity/MobSpawner.hpp -index fd29acaf3..3d7316255 100644 ---- a/source/world/entity/MobSpawner.hpp -+++ b/source/world/entity/MobSpawner.hpp -@@ -24,7 +24,7 @@ class MobSpawner { - public: - TilePos getRandomPosWithin(Level& level, int chunkX, int chunkZ); - void tick(Level& level, bool allowHostile, bool allowFriendly); --private: - -+private: - std::set chunksToPoll; - }; -\ No newline at end of file -diff --git a/source/world/entity/Player.cpp b/source/world/entity/Player.cpp -index d7e0cff66..34c37381b 100644 ---- a/source/world/entity/Player.cpp -+++ b/source/world/entity/Player.cpp -@@ -318,7 +318,8 @@ void Player::readAdditionalSaveData(const CompoundTag& tag) - m_dimension = tag.getInt32("Dimension"); - //m_sleepTimer = tag.getInt32("SleepTimer"); - -- if (tag.contains("SpawnX") && tag.contains("SpawnY") && tag.contains("SpawnZ")) { -+ if (tag.contains("SpawnX") && tag.contains("SpawnY") && tag.contains("SpawnZ")) -+ { - setRespawnPos(TilePos( static_cast(tag.getInt32("SpawnX")), - static_cast(tag.getInt32("SpawnY")), - static_cast(tag.getInt32("SpawnZ")))); -@@ -383,7 +384,8 @@ void Player::attack(Entity* pEnt) - if (!item.isEmpty() && isMob) - { - item.hurtEnemy((Mob*)pEnt, this); -- if (item.m_count <= 0) { -+ if (item.m_count <= 0) -+ { - item.snap(this); - removeSelectedItem(); - } -@@ -575,9 +577,11 @@ void Player::interact(Entity* pEnt) - return; - - ItemStack& item = getSelectedItem(); -- if (!item.isEmpty()) { -+ if (!item.isEmpty()) -+ { - item.interactEnemy(static_cast(pEnt)); -- if (item.m_count <= 0) { -+ if (item.m_count <= 0) -+ { - item.snap(this); - removeSelectedItem(); - } -diff --git a/source/world/entity/Player.hpp b/source/world/entity/Player.hpp -index 1a25f7228..ed63daee9 100644 ---- a/source/world/entity/Player.hpp -+++ b/source/world/entity/Player.hpp -@@ -45,7 +45,7 @@ class Player : public Mob - public: - void reset() override; - void remove() override; -- float getHeadHeight() const override { return 0.12f; /*@HUH: what ?*/ } -+ float getHeadHeight() const override { return 0.12f; } - int getMaxHealth() const override { return 20; } - bool isShootable() const override { return true; } - bool isPlayer() const override { return true; } -diff --git a/source/world/inventory/ContainerMenu.cpp b/source/world/inventory/ContainerMenu.cpp -index 8c083c908..7a549e7b0 100644 ---- a/source/world/inventory/ContainerMenu.cpp -+++ b/source/world/inventory/ContainerMenu.cpp -@@ -338,17 +338,21 @@ void ContainerMenu::setAll(const std::vector& items) - } - } - --void ContainerMenu::setData(int id, int value) { -+void ContainerMenu::setData(int id, int value) -+{ - } - --uint16_t ContainerMenu::backup(Inventory*) { -+uint16_t ContainerMenu::backup(Inventory*) -+{ - return ++m_changeUid; - } - --void ContainerMenu::deleteBackup(uint16_t) { -+void ContainerMenu::deleteBackup(uint16_t) -+{ - } - --void ContainerMenu::rollbackToBackup(uint16_t) { -+void ContainerMenu::rollbackToBackup(uint16_t) -+{ - } - - bool ContainerMenu::isSynched(Player* player) const -diff --git a/source/world/item/BowItem.cpp b/source/world/item/BowItem.cpp -index 53ebdce5d..5b7ef84f1 100644 ---- a/source/world/item/BowItem.cpp -+++ b/source/world/item/BowItem.cpp -@@ -13,9 +13,8 @@ ItemStack* BowItem::use(ItemStack* inst, Level* level, Mob* user) const - if (!user->isPlayer() || static_cast(user)->isCreative() || static_cast(user)->m_pInventory->removeResource(Item::arrow->m_itemID)) - { - level->playSound(user, "random.bow", 1.0f, 1.0f / (level->m_random.nextFloat() * 0.4f + 0.8f)); -- if (!level->m_bIsClientSide) { -+ if (!level->m_bIsClientSide) - level->addEntity(new Arrow(level, user)); -- } - } - - return inst; -diff --git a/source/world/item/ItemStack.hpp b/source/world/item/ItemStack.hpp -index 53db80153..39586fe5a 100644 ---- a/source/world/item/ItemStack.hpp -+++ b/source/world/item/ItemStack.hpp -@@ -125,6 +125,7 @@ class ItemStack - public: - int16_t m_count; - int m_popTime; -+ - private: - int16_t m_auxValue; - CompoundTag* m_userData; -diff --git a/source/world/level/levelgen/chunk/RandomLevelSource.cpp b/source/world/level/levelgen/chunk/RandomLevelSource.cpp -index 1908668f1..222a48533 100644 ---- a/source/world/level/levelgen/chunk/RandomLevelSource.cpp -+++ b/source/world/level/levelgen/chunk/RandomLevelSource.cpp -@@ -320,11 +320,13 @@ void RandomLevelSource::buildSurfaces(const ChunkPos& pos, TileID* tiles, Biome* - { - byte1 = pBiome->field_20; - byte2 = pBiome->field_21; -- if (flag1) { -+ if (flag1) -+ { - byte1 = 0; - byte2 = Tile::gravel->m_ID; - } -- if (flag) { -+ if (flag) -+ { - byte1 = Tile::sand->m_ID; - byte2 = Tile::sand->m_ID; - } -diff --git a/source/world/level/levelgen/chunk/TestChunkSource.cpp b/source/world/level/levelgen/chunk/TestChunkSource.cpp -index d0f5bde69..48b72e22f 100644 ---- a/source/world/level/levelgen/chunk/TestChunkSource.cpp -+++ b/source/world/level/levelgen/chunk/TestChunkSource.cpp -@@ -59,7 +59,8 @@ LevelChunk* TestChunkSource::generateChunk(const ChunkPos& pos) - *p = TILE_BEDROCK; - //else if (j == 0 || k == 0) - // *p = TILE_AIR; -- else if (i == 65) { -+ else if (i == 65) -+ { - /*if (rand() % 10 == 0) - *p = TILE_ROSE; - else if (rand() % 10 == 0) -@@ -69,7 +70,8 @@ LevelChunk* TestChunkSource::generateChunk(const ChunkPos& pos) - } - else if (i > 64) - *p = TILE_AIR; -- else if (i == 64) { -+ else if (i == 64) -+ { - //if ((j + k) % 2 == 0) - *p = TILE_GRASS; - } -diff --git a/source/world/level/path/BinaryHeap.cpp b/source/world/level/path/BinaryHeap.cpp -index 5eed80453..07faf7fd7 100644 ---- a/source/world/level/path/BinaryHeap.cpp -+++ b/source/world/level/path/BinaryHeap.cpp -@@ -39,12 +39,12 @@ void BinaryHeap::inlined0(int num) - Node* var2 = m_items[num]; - - int var4; -- for (float var3 = var2->field_C; num > 0; num = var4) { -+ for (float var3 = var2->field_C; num > 0; num = var4) -+ { - var4 = (num - 1) >> 1; - Node* var5 = m_items[var4]; -- if (var3 >= var5->field_C) { -+ if (var3 >= var5->field_C) - break; -- } - - m_items[num] = var5; - var5->field_0 = num; -@@ -59,39 +59,41 @@ void BinaryHeap::downHeap(int num) - Node* var2 = m_items[num]; - float var3 = var2->field_C; - -- while (true) { -+ while (true) -+ { - int var4 = 1 + (num << 1); - int var5 = var4 + 1; -- if (var4 >= m_count) { -+ if (var4 >= m_count) - break; -- } - - Node* var6 = m_items[var4]; - float var7 = var6->field_C; - Node* var8; - float var9; -- if (var5 >= m_count) { -+ if (var5 >= m_count) -+ { - var8 = nullptr; - var9 = std::numeric_limits::infinity(); - } -- else { -+ else -+ { - var8 = m_items[var5]; - var9 = var8->field_C; - } - -- if (var7 < var9) { -- if (var7 >= var3) { -+ if (var7 < var9) -+ { -+ if (var7 >= var3) - break; -- } - - m_items[num] = var6; - var6->field_0 = num; - num = var4; - } -- else { -- if (var9 >= var3) { -+ else -+ { -+ if (var9 >= var3) - break; -- } - - m_items[num] = var8; - var8->field_0 = num; -diff --git a/source/world/level/path/BinaryHeap.hpp b/source/world/level/path/BinaryHeap.hpp -index e0142b685..57fb67525 100644 ---- a/source/world/level/path/BinaryHeap.hpp -+++ b/source/world/level/path/BinaryHeap.hpp -@@ -29,7 +29,8 @@ class BinaryHeap - void inlined0(int i); - void downHeap(int i); - -- Node* removeTop() { -+ Node* removeTop() -+ { - Node* pNode = m_items[0]; - m_items[0] = m_items[--m_count]; - m_items[m_count] = 0; -@@ -41,15 +42,18 @@ class BinaryHeap - return pNode; - } - -- void clear() { -+ void clear() -+ { - m_count = 0; - } - -- int size() const { -+ int size() const -+ { - return m_count; - } - -- void setDistance(Node* pNode, float distance) { -+ void setDistance(Node* pNode, float distance) -+ { - float oldDistance = pNode->field_C; - pNode->field_C = distance; - -diff --git a/source/world/tile/ChestTile.cpp b/source/world/tile/ChestTile.cpp -index 327bd0bf6..ca8f42ad9 100644 ---- a/source/world/tile/ChestTile.cpp -+++ b/source/world/tile/ChestTile.cpp -@@ -10,9 +10,8 @@ ChestTile::ChestTile(int id, int texture) : Tile(id, texture, Material::wood) - - int ChestTile::getTexture(const LevelSource* level, const TilePos& pos, Facing::Name face) const - { -- if (face == Facing::UP || face == Facing::DOWN) { -+ if (face == Facing::UP || face == Facing::DOWN) - return m_TextureFrame - 1; -- } - - int id_north = level->getTile(pos.north()); - int id_south = level->getTile(pos.south()); -@@ -22,7 +21,8 @@ int ChestTile::getTexture(const LevelSource* level, const TilePos& pos, Facing:: - bool isDoubleNS = (id_north == m_ID || id_south == m_ID); - bool isDoubleWE = (id_west == m_ID || id_east == m_ID); - -- if (!isDoubleNS && !isDoubleWE) { -+ if (!isDoubleNS && !isDoubleWE) -+ { - Facing::Name front = Facing::SOUTH; - - if (Tile::solid[id_north] && !Tile::solid[id_south]) -@@ -40,7 +40,8 @@ int ChestTile::getTexture(const LevelSource* level, const TilePos& pos, Facing:: - bool left = false; - Facing::Name front = Facing::SOUTH; - -- if (isDoubleWE) { -+ if (isDoubleWE) -+ { - left = id_west == m_ID; - TilePos side = left ? pos.west() : pos.east(); - -@@ -52,9 +53,12 @@ int ChestTile::getTexture(const LevelSource* level, const TilePos& pos, Facing:: - else if ((Tile::solid[id_south] || Tile::solid[id_infront]) && !Tile::solid[id_north] && !Tile::solid[id_behind]) - front = Facing::NORTH; - -- if (front == Facing::SOUTH) left = !left; -- if (face == front) return m_TextureFrame + (left ? 15 : 16); -- if (face == Facing::OPPOSITE[front]) return m_TextureFrame + (left ? 32 : 31); -+ if (front == Facing::SOUTH) -+ left = !left; -+ if (face == front) -+ return m_TextureFrame + (left ? 15 : 16); -+ if (face == Facing::OPPOSITE[front]) -+ return m_TextureFrame + (left ? 32 : 31); - return m_TextureFrame; - } - -@@ -67,10 +71,10 @@ int ChestTile::getTexture(const LevelSource* level, const TilePos& pos, Facing:: - int id_behind = level->getTile(side.west()); - int id_infront = level->getTile(side.east()); - -- if ((Tile::solid[id_west] || Tile::solid[id_behind]) && !Tile::solid[id_east] && !Tile::solid[id_infront]) { -+ if ((Tile::solid[id_west] || Tile::solid[id_behind]) && !Tile::solid[id_east] && !Tile::solid[id_infront]) - front = Facing::EAST; -- } -- else if ((Tile::solid[id_east] || Tile::solid[id_infront]) && !Tile::solid[id_west] && !Tile::solid[id_behind]) { -+ else if ((Tile::solid[id_east] || Tile::solid[id_infront]) && !Tile::solid[id_west] && !Tile::solid[id_behind]) -+ { - front = Facing::WEST; - left = !left; - } -@@ -87,7 +91,8 @@ int ChestTile::getTexture(const LevelSource* level, const TilePos& pos, Facing:: - - int ChestTile::getTexture(Facing::Name face) const - { -- switch (face) { -+ switch (face) -+ { - case Facing::UP: - case Facing::DOWN: - return m_TextureFrame - 1; -@@ -122,13 +127,12 @@ bool ChestTile::hasNeighbors(const Level* level, const TilePos& pos, int count) - return false; - } - --void ChestTile::onRemove(Level* level, const TilePos& pos){ -+void ChestTile::onRemove(Level* level, const TilePos& pos) -+{ - ChestTileEntity* ent = static_cast(level->getTileEntity(pos)); - - if (!ent) -- { - Tile::onRemove(level, pos); -- } - - for (int slot = 0; slot < ent->getContainerSize(); ++slot) - { -@@ -160,6 +164,9 @@ void ChestTile::onRemove(Level* level, const TilePos& pos){ - - bool ChestTile::use(Level* level, const TilePos& pos, Player* player) - { -+ if (player->isSneaking() && !player->getSelectedItem().isEmpty()) -+ return false; -+ - if (level->m_bIsClientSide) - return true; - -diff --git a/source/world/tile/ChestTile.hpp b/source/world/tile/ChestTile.hpp -index 051081631..8ade330a0 100644 ---- a/source/world/tile/ChestTile.hpp -+++ b/source/world/tile/ChestTile.hpp -@@ -6,6 +6,7 @@ class ChestTile : public Tile - { - public: - ChestTile(int id, int texture); -+ - public: - int getTexture(const LevelSource*, const TilePos& pos, Facing::Name face) const override; - int getTexture(Facing::Name face) const override; -@@ -15,6 +16,7 @@ class ChestTile : public Tile - bool use(Level* level, const TilePos& pos, Player* var5) override; - bool hasTileEntity() const override; - TileEntity* newTileEntity() override; -+ - public: - Random m_chestRandom; - }; -\ No newline at end of file -diff --git a/source/world/tile/CraftingTableTile.cpp b/source/world/tile/CraftingTableTile.cpp -index f6cd821ab..dc5c26e95 100644 ---- a/source/world/tile/CraftingTableTile.cpp -+++ b/source/world/tile/CraftingTableTile.cpp -@@ -24,7 +24,8 @@ bool CraftingTableTile::use(Level* level, const TilePos& pos, Player* player) - - int CraftingTableTile::getTexture(Facing::Name face) const - { -- switch (face) { -+ switch (face) -+ { - case Facing::UP: return m_TextureFrame - 16; - case Facing::DOWN: return Tile::wood->getTexture(face); - case Facing::NORTH: case Facing::SOUTH: return m_TextureFrame + 1; -diff --git a/source/world/tile/FurnaceTile.cpp b/source/world/tile/FurnaceTile.cpp -index e5ea21f65..85ff6a4cb 100644 ---- a/source/world/tile/FurnaceTile.cpp -+++ b/source/world/tile/FurnaceTile.cpp -@@ -25,7 +25,8 @@ int FurnaceTile::getTexture(const LevelSource* level, const TilePos& pos, Facing - } - } - --void FurnaceTile::animateTick(Level* level, const TilePos& pos, Random* random) { -+void FurnaceTile::animateTick(Level* level, const TilePos& pos, Random* random) -+{ - if (!m_active) - return; - -@@ -54,7 +55,8 @@ void FurnaceTile::animateTick(Level* level, const TilePos& pos, Random* random) - - int FurnaceTile::getTexture(Facing::Name face) const - { -- switch (face) { -+ switch (face) -+ { - case Facing::UP: - case Facing::DOWN: - return m_TextureFrame + 17; -@@ -73,7 +75,7 @@ void FurnaceTile::onPlace(Level* level, const TilePos& pos) - - bool FurnaceTile::use(Level* level, const TilePos& pos, Player* player) - { -- if (player->isSneaking() && player->getSelectedItem()) -+ if (player->isSneaking() && !player->getSelectedItem().isEmpty()) - return false; - - if (level->m_bIsClientSide) -@@ -85,20 +87,15 @@ bool FurnaceTile::use(Level* level, const TilePos& pos, Player* player) - - void FurnaceTile::setPlacedBy(Level* level, const TilePos& pos, Mob* mob) - { -- int rot = Mth::floor(0.5f + (mob->m_rot.y * 4.0f / 360.0f)) & 3; -+ int rot = Mth::floor(0.5f + (mob->m_rot.x * 4.0f / 360.0f)) & 3; - int data = 4; - - switch (rot) - { -- case 0: -- data = 2; -- break; -- case 1: -- data = 5; -- break; -- case 2: -- data = 3; -- break; -+ case 0: data = 2; break; -+ case 1: data = 5; break; -+ case 2: data = 3; break; -+ case 3: data = 4; break; - } - - level->setData(pos, data); -diff --git a/source/world/tile/MusicTile.cpp b/source/world/tile/MusicTile.cpp -index 7842f458e..5d50c6a1d 100644 ---- a/source/world/tile/MusicTile.cpp -+++ b/source/world/tile/MusicTile.cpp -@@ -28,7 +28,7 @@ void MusicTile::neighborChanged(Level* level, const TilePos& pos, TileID tile) - - bool MusicTile::use(Level* level, const TilePos& pos, Player* player) - { -- if (player->isSneaking() && player->getSelectedItem()) -+ if (player->isSneaking() && !player->getSelectedItem().isEmpty()) - return false; - - if (level->m_bIsClientSide) -diff --git a/source/world/tile/PumpkinTile.cpp b/source/world/tile/PumpkinTile.cpp -index c9481e442..cab8de26f 100644 ---- a/source/world/tile/PumpkinTile.cpp -+++ b/source/world/tile/PumpkinTile.cpp -@@ -7,7 +7,8 @@ PumpkinTile::PumpkinTile(TileID id, bool lantern) : Tile(id, TEXTURE_PUMPKIN_TOP - - int PumpkinTile::getTexture(Facing::Name face, TileData data) const - { -- switch (face) { -+ switch (face) -+ { - case Facing::UP: case Facing::DOWN: return m_TextureFrame; - default: - return (face == 2 && data == 2) || (face == 5 && data == 3) || (face == 3 && data == 0) || (face == 4 && data == 1) ? m_TextureFrame + (m_bLantern ? 18 : 17) : m_TextureFrame + 16; -@@ -16,7 +17,8 @@ int PumpkinTile::getTexture(Facing::Name face, TileData data) const - - int PumpkinTile::getTexture(Facing::Name face) const - { -- switch (face) { -+ switch (face) -+ { - case Facing::UP: case Facing::DOWN: return m_TextureFrame; - case Facing::SOUTH: return m_TextureFrame + 17; - default: return m_TextureFrame + 16; - -From 26e7f2e3746fc5b0114e446c3601b708dbcfde0f Mon Sep 17 00:00:00 2001 -From: hellosammu -Date: Mon, 16 Feb 2026 11:05:15 -0500 -Subject: [PATCH 03/22] Virtual EntityTile class; fix save/load of entity tiles - (far safer) - ---- - source/CMakeLists.txt | 1 + - source/world/level/Level.cpp | 21 +++++++++----- - .../world/level/levelgen/chunk/LevelChunk.cpp | 28 +++++++++++-------- - source/world/tile/ChestTile.cpp | 6 ++-- - source/world/tile/ChestTile.hpp | 4 +-- - source/world/tile/EntityTile.cpp | 23 +++++++++++++++ - source/world/tile/EntityTile.hpp | 17 +++++++++++ - source/world/tile/FurnaceTile.cpp | 10 +++++-- - source/world/tile/FurnaceTile.hpp | 4 +-- - source/world/tile/MusicTile.cpp | 2 +- - source/world/tile/MusicTile.hpp | 4 +-- - source/world/tile/Tile.cpp | 9 ------ - source/world/tile/Tile.hpp | 1 - - 13 files changed, 89 insertions(+), 41 deletions(-) - create mode 100644 source/world/tile/EntityTile.cpp - create mode 100644 source/world/tile/EntityTile.hpp - -diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt -index afe085a83..a91b654c5 100644 ---- a/source/CMakeLists.txt -+++ b/source/CMakeLists.txt -@@ -426,6 +426,7 @@ add_library(reminecraftpe-core STATIC - world/tile/OreTile.cpp - world/tile/StairTile.cpp - world/tile/SandStoneTile.cpp -+ world/tile/EntityTile.cpp - world/tile/ChestTile.cpp - world/tile/FurnaceTile.cpp - world/tile/MusicTile.cpp -diff --git a/source/world/level/Level.cpp b/source/world/level/Level.cpp -index efec2255d..7460bbb08 100644 ---- a/source/world/level/Level.cpp -+++ b/source/world/level/Level.cpp -@@ -293,20 +293,27 @@ void Level::setTileEntity(const TilePos& pos, TileEntity* tileEntity) - - void Level::removeTileEntity(const TilePos& pos) - { -- TileEntity* old = getTileEntity(pos); -+ TileEntity* tileEntity = getTileEntity(pos); - -- if (old != nullptr && m_bUpdatingTileEntities) -+ if (tileEntity == nullptr) - { -- old->setRemoved(); -+ LOG_W("Tried to remove a tile entity at %d, %d, %d, but there was no tile entity there!", pos.x, pos.y, pos.z); - return; - } - -- if (old) -- Util::remove(m_tileEntityList, old); -+ // During a tile entity update, just mark it for potential removal -+ if (m_bUpdatingTileEntities) -+ { -+ tileEntity->setRemoved(); -+ return; -+ } - -- LevelChunk* pChunk = getChunk(pos); -- if (pChunk) -+ Util::remove(m_tileEntityList, tileEntity); -+ -+ if (LevelChunk* pChunk = getChunk(pos)) -+ { - pChunk->removeTileEntity(pos); -+ } - } - - void Level::swap(const TilePos& pos1, const TilePos& pos2) -diff --git a/source/world/level/levelgen/chunk/LevelChunk.cpp b/source/world/level/levelgen/chunk/LevelChunk.cpp -index 598a4ef8a..357a805c1 100644 ---- a/source/world/level/levelgen/chunk/LevelChunk.cpp -+++ b/source/world/level/levelgen/chunk/LevelChunk.cpp -@@ -589,7 +589,7 @@ bool LevelChunk::setTile(const ChunkTilePos& pos, TileID tile) - tilePos.x += pos.x; - tilePos.z += pos.z; - m_pBlockData[index] = tile; -- if (oldTile) -+ if (oldTile && Tile::tiles[oldTile]) - { - Tile::tiles[oldTile]->onRemove(m_pLevel, tilePos); - } -@@ -722,20 +722,24 @@ void LevelChunk::addTileEntity(TileEntity* tileEntity) - - void LevelChunk::setTileEntity(const ChunkTilePos& pos, TileEntity* tileEntity) - { -- tileEntity->m_pLevel = m_pLevel; -- TileID tile = getTile(pos); - TilePos tilePos(m_chunkPos, pos.y); -- tilePos.x += pos.x; -- tilePos.z += pos.z; -- tileEntity->m_pos = tilePos; -- if (tile > 0 && Tile::isEntityTile[tile]) -- { -- tileEntity->clearRemoved(); -- m_tileEntities[pos] = tileEntity; -- return; -+ -+ if (tileEntity) -+ { -+ tileEntity->m_pLevel = m_pLevel; -+ TileID tile = getTile(pos); -+ tilePos.x += pos.x; -+ tilePos.z += pos.z; -+ tileEntity->m_pos = tilePos; -+ if (tile > 0 && Tile::isEntityTile[tile]) -+ { -+ tileEntity->clearRemoved(); -+ m_tileEntities[pos] = tileEntity; -+ return; -+ } - } - -- LOG_I("Attempted to place a tile entity where there was no entity tile! %d", tile); -+ LOG_W("Attempted to place a tile entity at %d, %d, %d where there was no entity tile!", tilePos.x, tilePos.y, tilePos.z); - } - - void LevelChunk::removeTileEntity(const ChunkTilePos& pos) -diff --git a/source/world/tile/ChestTile.cpp b/source/world/tile/ChestTile.cpp -index ca8f42ad9..e7aad4afa 100644 ---- a/source/world/tile/ChestTile.cpp -+++ b/source/world/tile/ChestTile.cpp -@@ -3,7 +3,7 @@ - #include "world/CompoundContainer.hpp" - #include "world/tile/entity/ChestTileEntity.hpp" - --ChestTile::ChestTile(int id, int texture) : Tile(id, texture, Material::wood) -+ChestTile::ChestTile(int id, int texture) : EntityTile(id, texture, Material::wood) - { - setTicking(true); - } -@@ -132,7 +132,7 @@ void ChestTile::onRemove(Level* level, const TilePos& pos) - ChestTileEntity* ent = static_cast(level->getTileEntity(pos)); - - if (!ent) -- Tile::onRemove(level, pos); -+ EntityTile::onRemove(level, pos); - - for (int slot = 0; slot < ent->getContainerSize(); ++slot) - { -@@ -159,7 +159,7 @@ void ChestTile::onRemove(Level* level, const TilePos& pos) - } - } - -- Tile::onRemove(level, pos); -+ EntityTile::onRemove(level, pos); - } - - bool ChestTile::use(Level* level, const TilePos& pos, Player* player) -diff --git a/source/world/tile/ChestTile.hpp b/source/world/tile/ChestTile.hpp -index 8ade330a0..e154d454e 100644 ---- a/source/world/tile/ChestTile.hpp -+++ b/source/world/tile/ChestTile.hpp -@@ -1,8 +1,8 @@ - #pragma once - --#include "Tile.hpp" -+#include "EntityTile.hpp" - --class ChestTile : public Tile -+class ChestTile : public EntityTile - { - public: - ChestTile(int id, int texture); -diff --git a/source/world/tile/EntityTile.cpp b/source/world/tile/EntityTile.cpp -new file mode 100644 -index 000000000..ed5e75f84 ---- /dev/null -+++ b/source/world/tile/EntityTile.cpp -@@ -0,0 +1,23 @@ -+#include "EntityTile.hpp" -+#include "world/level/Level.hpp" -+ -+EntityTile::EntityTile(TileID id, int textureID, Material *material) : Tile(id, textureID, material) -+{ -+} -+ -+bool EntityTile::hasTileEntity() const -+{ -+ return true; -+} -+ -+void EntityTile::onPlace(Level *level, const TilePos &pos) -+{ -+ Tile::onPlace(level, pos); -+ level->setTileEntity(pos, newTileEntity()); -+} -+ -+void EntityTile::onRemove(Level *level, const TilePos &pos) -+{ -+ Tile::onRemove(level, pos); -+ level->removeTileEntity(pos); -+} -\ No newline at end of file -diff --git a/source/world/tile/EntityTile.hpp b/source/world/tile/EntityTile.hpp -new file mode 100644 -index 000000000..d79c2cc1a ---- /dev/null -+++ b/source/world/tile/EntityTile.hpp -@@ -0,0 +1,17 @@ -+#pragma once -+ -+#include "Tile.hpp" -+ -+class EntityTile : public Tile -+{ -+public: -+ EntityTile(TileID id, int textureID, Material* material); -+ -+public: -+ virtual TileEntity* newTileEntity() = 0; -+ -+public: -+ bool hasTileEntity() const override; -+ void onPlace(Level* level, const TilePos& pos) override; -+ void onRemove(Level* level, const TilePos& pos) override; -+}; -\ No newline at end of file -diff --git a/source/world/tile/FurnaceTile.cpp b/source/world/tile/FurnaceTile.cpp -index 85ff6a4cb..ccf3e1c28 100644 ---- a/source/world/tile/FurnaceTile.cpp -+++ b/source/world/tile/FurnaceTile.cpp -@@ -4,7 +4,7 @@ - - bool FurnaceTile::keepInventory = false; - --FurnaceTile::FurnaceTile(int id, bool active) : Tile(id, TEXTURE_FURNACE_SIDE, Material::stone) -+FurnaceTile::FurnaceTile(int id, bool active) : EntityTile(id, TEXTURE_FURNACE_SIDE, Material::stone) - { - setTicking(true); - m_active = active; -@@ -69,7 +69,7 @@ int FurnaceTile::getTexture(Facing::Name face) const - - void FurnaceTile::onPlace(Level* level, const TilePos& pos) - { -- Tile::onPlace(level, pos); -+ EntityTile::onPlace(level, pos); - RecalculateLookDirection(level, pos); - } - -@@ -110,6 +110,7 @@ void FurnaceTile::onRemove(Level* level, const TilePos& pos) - - if (!tileEnt) - { -+ EntityTile::onRemove(level, pos); - return; // this has to be wrapped in a guard or the compiler screams, thanks -Wmisleading-indentation - } - -@@ -139,11 +140,16 @@ void FurnaceTile::onRemove(Level* level, const TilePos& pos) - level->addEntity(itemEnt); - } - } -+ -+ EntityTile::onRemove(level, pos); - } - - void FurnaceTile::SetLit(bool lit, Level* level, const TilePos& pos) - { - TileEntity* tileEntity = level->getTileEntity(pos); -+ if (!tileEntity) -+ return; -+ - int data = level->getData(pos); - - keepInventory = true; -diff --git a/source/world/tile/FurnaceTile.hpp b/source/world/tile/FurnaceTile.hpp -index bb0ac9359..0a79e01aa 100644 ---- a/source/world/tile/FurnaceTile.hpp -+++ b/source/world/tile/FurnaceTile.hpp -@@ -1,8 +1,8 @@ - #pragma once - --#include "Tile.hpp" -+#include "EntityTile.hpp" - --class FurnaceTile : public Tile -+class FurnaceTile : public EntityTile - { - public: - FurnaceTile(int id, bool active); -diff --git a/source/world/tile/MusicTile.cpp b/source/world/tile/MusicTile.cpp -index 5d50c6a1d..372077028 100644 ---- a/source/world/tile/MusicTile.cpp -+++ b/source/world/tile/MusicTile.cpp -@@ -2,7 +2,7 @@ - #include "world/level/Level.hpp" - #include "entity/MusicTileEntity.hpp" - --MusicTile::MusicTile(TileID id, int texture) : Tile(id, texture, Material::wood) -+MusicTile::MusicTile(TileID id, int texture) : EntityTile(id, texture, Material::wood) - { - } - -diff --git a/source/world/tile/MusicTile.hpp b/source/world/tile/MusicTile.hpp -index fcddfba82..7383a0694 100644 ---- a/source/world/tile/MusicTile.hpp -+++ b/source/world/tile/MusicTile.hpp -@@ -1,8 +1,8 @@ - #pragma once - --#include "Tile.hpp" -+#include "EntityTile.hpp" - --class MusicTile : public Tile -+class MusicTile : public EntityTile - { - public: - MusicTile(TileID id, int textureID); -diff --git a/source/world/tile/Tile.cpp b/source/world/tile/Tile.cpp -index 4aea4c4b7..14878f2d2 100644 ---- a/source/world/tile/Tile.cpp -+++ b/source/world/tile/Tile.cpp -@@ -283,11 +283,6 @@ int Tile::getSpawnResourcesAuxValue(int x) const - return 0; - } - --TileEntity* Tile::newTileEntity() --{ -- return nullptr; --} -- - Tile* Tile::setToolTypes(unsigned int toolMask) - { - m_toolMask |= toolMask; -@@ -998,14 +993,10 @@ void Tile::neighborChanged(Level* pLevel, const TilePos& pos, TileID tile) - - void Tile::onPlace(Level* pLevel, const TilePos& pos) - { -- if (hasTileEntity()) -- pLevel->setTileEntity(pos, newTileEntity()); - } - - void Tile::onRemove(Level* pLevel, const TilePos& pos) - { -- if (hasTileEntity()) -- pLevel->removeTileEntity(pos); - } - - bool Tile::containsX(const Vec3& v) -diff --git a/source/world/tile/Tile.hpp b/source/world/tile/Tile.hpp -index 2a9bc0ad3..61388b225 100644 ---- a/source/world/tile/Tile.hpp -+++ b/source/world/tile/Tile.hpp -@@ -123,7 +123,6 @@ class Tile - virtual Tile* setDestroyTime(float); - virtual Tile* setTicking(bool); - virtual int getSpawnResourcesAuxValue(int) const; -- virtual TileEntity* newTileEntity(); - Tile* setToolTypes(unsigned int toolMask); - Tile* setToolLevel(int toolLevel); - Tile* setToolTypesAndLevel(unsigned int toolMask, int toolLevel = 0); - -From f64f96a392f9124127306d3274585aa59c11ad3a Mon Sep 17 00:00:00 2001 -From: hellosammu -Date: Mon, 16 Feb 2026 12:18:45 -0500 -Subject: [PATCH 04/22] base client riding logic - ---- - source/client/renderer/entity/MobRenderer.cpp | 2 +- - source/world/entity/Entity.cpp | 139 ++++++++++++++++++ - source/world/entity/Entity.hpp | 19 ++- - source/world/entity/Mob.cpp | 20 ++- - source/world/entity/Mob.hpp | 6 +- - source/world/entity/Pig.cpp | 31 ++++ - source/world/entity/Pig.hpp | 2 +- - source/world/entity/Player.cpp | 4 +- - source/world/entity/Player.hpp | 3 +- - source/world/entity/Spider.hpp | 2 +- - source/world/level/Level.cpp | 56 ++++++- - source/world/tile/FurnaceTile.cpp | 2 + - 12 files changed, 267 insertions(+), 19 deletions(-) - -diff --git a/source/client/renderer/entity/MobRenderer.cpp b/source/client/renderer/entity/MobRenderer.cpp -index 13d7ba76e..c3af5f015 100644 ---- a/source/client/renderer/entity/MobRenderer.cpp -+++ b/source/client/renderer/entity/MobRenderer.cpp -@@ -90,7 +90,7 @@ void MobRenderer::render(const Entity& entity, const Vec3& pos, float rot, float - MatrixStack::Ref matrix = MatrixStack::World.push(); - - m_pModel->m_attackTime = getAttackAnim(mob, a); -- m_pModel->m_bRiding = false; -+ m_pModel->m_bRiding = mob.isRiding(); - m_pModel->m_bIsBaby = mob.isBaby(); - - if (m_pArmorModel != nullptr) -diff --git a/source/world/entity/Entity.cpp b/source/world/entity/Entity.cpp -index 49d7112a0..f7edc64a2 100644 ---- a/source/world/entity/Entity.cpp -+++ b/source/world/entity/Entity.cpp -@@ -28,6 +28,9 @@ void Entity::_init() - field_28 = 0; - field_30 = 1.0f; - m_dimensionId = DIMENSION_OVERWORLD; -+ m_riderId = 0; -+ m_ridingId = 0; -+ m_bRiding = false; - m_bBlocksBuilding = false; - m_pLevel = nullptr; - m_tintColor = Color::WHITE; -@@ -469,6 +472,14 @@ void Entity::tick() - void Entity::baseTick() - { - //@TODO: untangle the gotos -+ if (const Entity* riding = getRiding()) -+ { -+ if ((!riding && m_riderId > 0) || riding->m_bRemoved) -+ { -+ m_riderId = 0; -+ setSharedFlag(C_ENTITY_FLAG_RIDING, false); -+ } -+ } - - field_90 = m_walkDist; - m_oPos = m_pos; -@@ -937,6 +948,46 @@ AABB* Entity::getCollideAgainstBox(Entity* ent) const - return nullptr; - } - -+void Entity::rideTick() -+{ -+ Entity* riding = getRiding(); -+ if (!riding || riding->m_bRemoved) -+ { -+ m_riderId = 0; -+ setSharedFlag(C_ENTITY_FLAG_RIDING, false); -+ } -+ -+ // we don't move -+ m_vel = Vec3::ZERO; -+ -+ tick(); -+ -+ riding->positionRider(); -+ m_rideRot.x += riding->m_rot.x - riding->m_oRot.x; -+ m_rideRot.y += riding->m_rot.y - riding->m_oRot.y; -+ while (m_rideRot.y >= 180.0f) -+ m_rideRot.y -= 360.0f; -+ while (m_rideRot.y < -180.0f) -+ m_rideRot.y += 360.0f; -+ while (m_rideRot.x >= 180.0f) -+ m_rideRot.x -= 360.0f; -+ while (m_rideRot.x < -180.0f) -+ m_rideRot.x += 360.0f; -+ -+ float rotX = m_rideRot.x * 0.5f; -+ float rotY = m_rideRot.y * 0.5f; -+ -+ float lookLimiter = 10.0f; -+ rotX = Mth::clamp(rotX, -lookLimiter, lookLimiter); -+ rotY = Mth::clamp(rotY, -lookLimiter, lookLimiter); -+ -+ m_rideRot.x -= rotX; -+ m_rideRot.y -= rotY; -+ -+ m_rot.x += rotX; -+ m_rot.y += rotY; -+} -+ - void Entity::handleInsidePortal() - { - } -@@ -946,6 +997,94 @@ void Entity::handleEntityEvent(EventType::ID eventId) - LOG_W("Unknown EntityEvent ID: %d, EntityType: %s", eventId, getDescriptor().getEntityType().getName().c_str()); - } - -+void Entity::positionRider() -+{ -+ Entity* rider = getRider(); -+ if (!rider) -+ return; -+ -+ rider->setPos(Vec3(m_pos.x, m_pos.y + getRideHeight() + rider->getRidingHeight(), m_pos.z)); -+} -+ -+void Entity::ride(Entity* newRiding) -+{ -+ m_rideRot = Vec2::ZERO; -+ Entity* oldRiding = getRiding(); -+ -+ // Dismount current ride if nullptr is fed in -+ if (newRiding == nullptr) -+ { -+ if (oldRiding) -+ { -+ moveTo(oldRiding->m_pos); -+ setRot(oldRiding->m_rot); -+ oldRiding->m_riderId = 0; // Let them know you dismounted them -+ } -+ -+ // Let yourself know you aren't riding anything -+ m_ridingId = 0; -+ setSharedFlag(C_ENTITY_FLAG_RIDING, false); -+ -+ return; -+ } -+ -+ // Dismount if the same entity is fed in -+ if (oldRiding && oldRiding == newRiding) -+ { -+ oldRiding->m_riderId = 0; -+ -+ m_ridingId = 0; -+ setSharedFlag(C_ENTITY_FLAG_RIDING, false); -+ -+ moveTo(oldRiding->m_pos); -+ setRot(oldRiding->m_rot); -+ return; -+ } -+ -+ // if (this.riding != null) this.riding.rider = null; -+ if (oldRiding) -+ { -+ oldRiding->m_riderId = 0; -+ } -+ -+ // if (newRiding.rider != null) newRiding.rider.riding = null; -+ // i hate this name but it's literally what it is -+ if (Entity* newRidesOldRider = newRiding->getRider()) -+ { -+ newRidesOldRider->m_ridingId = 0; -+ newRidesOldRider->setSharedFlag(C_ENTITY_FLAG_RIDING, false); -+ } -+ -+ // Tell yourself that you're riding the new ride -+ m_ridingId = newRiding->m_EntityID; -+ setSharedFlag(C_ENTITY_FLAG_RIDING, true); -+ -+ // Tell the new ride that it's being ridden by you -+ newRiding->m_riderId = m_EntityID; -+} -+ -+Entity* Entity::getRiding() const -+{ -+ if (m_ridingId <= 0) -+ return nullptr; -+ -+ if (Entity* riding = m_pLevel->getEntity(m_ridingId)) -+ return riding; -+ -+ return nullptr; -+} -+ -+Entity* Entity::getRider() const -+{ -+ if (m_riderId <= 0) -+ return nullptr; -+ -+ if (Entity* rider = m_pLevel->getEntity(m_riderId)) -+ return rider; -+ -+ return nullptr; -+} -+ - /*void Entity::thunderHit(LightningBolt* bolt) - { - burn(5); -diff --git a/source/world/entity/Entity.hpp b/source/world/entity/Entity.hpp -index 07a0baf01..e104b01de 100644 ---- a/source/world/entity/Entity.hpp -+++ b/source/world/entity/Entity.hpp -@@ -112,11 +112,9 @@ class Entity - Entity(Level*); - virtual ~Entity(); - --protected: -+public: - virtual bool getSharedFlag(SharedFlag flag) const; - virtual void setSharedFlag(SharedFlag flag, bool value); -- --public: - virtual void reset(); - virtual void setLevel(Level*); - virtual void removed(); -@@ -165,7 +163,7 @@ class Entity - virtual bool isPushable() const { return false; } - virtual bool isShootable() const { return false; } - virtual bool isOnFire() const { return m_fireTicks > 0 || getSharedFlag(C_ENTITY_FLAG_ONFIRE); } -- virtual bool isRiding() const { return /*m_pRiding != nullptr ||*/ getSharedFlag(C_ENTITY_FLAG_RIDING); } -+ virtual bool isRiding() const { return getRiding() || getSharedFlag(C_ENTITY_FLAG_RIDING); } - virtual bool isSneaking() const { return getSharedFlag(C_ENTITY_FLAG_SNEAKING); } - virtual void setSneaking(bool value) { setSharedFlag(C_ENTITY_FLAG_SNEAKING, value); } - virtual bool isAlive() const { return m_bRemoved; } -@@ -196,9 +194,18 @@ class Entity - virtual RenderType queryEntityRenderer() const; - virtual const AABB* getCollideBox() const; - virtual AABB* getCollideAgainstBox(Entity* ent) const; -+ virtual void rideTick(); - virtual void handleInsidePortal(); - virtual void handleEntityEvent(EventType::ID eventId); - //virtual void thunderHit(LightningBolt*); -+ virtual void positionRider(); -+ virtual void ride(Entity*); -+ virtual float getRideHeight() const { return m_bbHeight * 0.75f; } -+ virtual float getRidingHeight() const { return m_heightOffset; } -+ Entity* getRiding() const; -+ Entity* getRider() const; -+ // void setRiding(Entity* newRiding); -+ // void setRider(Entity* newRider); - void load(const CompoundTag& tag); - bool save(CompoundTag& tag) const; - void saveWithoutId(CompoundTag& tag) const; -@@ -240,12 +247,16 @@ class Entity - float field_30; - //TileSource* m_pTileSource; - DimensionId m_dimensionId; -+ Entity::ID m_ridingId; -+ Entity::ID m_riderId; -+ bool m_bRiding; - bool m_bBlocksBuilding; - Level* m_pLevel; - Vec3 m_oPos; // "o" in Java or "xo" "yo" "zo" - Vec3 m_vel; - Vec2 m_rot; - Vec2 m_oRot; // "RotO" in Java or "xRotO" "yRotO" -+ Vec2 m_rideRot; - Color m_tintColor; - AABB m_hitbox; - bool m_bOnGround; -diff --git a/source/world/entity/Mob.cpp b/source/world/entity/Mob.cpp -index a336c5654..a3967ce12 100644 ---- a/source/world/entity/Mob.cpp -+++ b/source/world/entity/Mob.cpp -@@ -56,7 +56,7 @@ void Mob::_init() - m_lPos = Vec3::ZERO; - m_lRot = Vec2::ZERO; - m_lastHurt = 0; -- m_pEntLookedAt = nullptr; -+ m_entLookedAtId = 0; - m_bSwinging = false; - m_swingTime = 0; - m_ambientSoundTime = 0; -@@ -786,6 +786,13 @@ void Mob::lookAt(Entity* pEnt, float a3, float a4) - -rotlerp(m_rot.y, x2 * 180.0f / float(M_PI), a3))); - } - -+Entity *Mob::getLookingAt() const -+{ -+ if (m_entLookedAtId == 0) -+ return nullptr; -+ return m_pLevel->getEntity(m_entLookedAtId); -+} -+ - bool Mob::canSpawn() - { - return m_pLevel->getCubes(this, m_hitbox)->empty(); -@@ -832,7 +839,7 @@ void Mob::updateAi() - Entity* nearestPlayer = m_pLevel->getNearestPlayer(*this, 8.0f); - if (nearestPlayer) - { -- m_pEntLookedAt = nearestPlayer; -+ m_entLookedAtId = nearestPlayer->m_EntityID; - - field_120 = m_random.nextInt(20) + 10; - } -@@ -843,17 +850,18 @@ void Mob::updateAi() - } - - // @TODO: we get a crash here when a Player leaves -- if (m_pEntLookedAt) -+ if (m_entLookedAtId > 0) - { -- lookAt(m_pEntLookedAt, 10.0f, getMaxHeadXRot()); -+ Entity* pEnt = m_pLevel->getEntity(m_entLookedAtId); -+ lookAt(pEnt, 10.0f, getMaxHeadXRot()); - - // gaze timer - field_120--; - - // if the entity was removed, or we're too far away, or our gaze timer is up -- if (field_120 < 0 || m_pEntLookedAt->m_bRemoved || m_pEntLookedAt->distanceToSqr(this) > 64.0f) -+ if (field_120 < 0 || pEnt->m_bRemoved || pEnt->distanceToSqr(this) > 64.0f) - // stop staring -- m_pEntLookedAt = nullptr; -+ m_entLookedAtId = 0; - } - else - { -diff --git a/source/world/entity/Mob.hpp b/source/world/entity/Mob.hpp -index 2f3909617..b98687817 100644 ---- a/source/world/entity/Mob.hpp -+++ b/source/world/entity/Mob.hpp -@@ -63,9 +63,9 @@ class Mob : public Entity - virtual void updateWalkAnim(); - virtual void aiStep(); - virtual void lookAt(Entity* pEnt, float, float); -- virtual bool isLookingAtAnEntity() { return m_pEntLookedAt != nullptr; } -+ virtual bool isLookingAtAnEntity() { return m_entLookedAtId > 0; } - virtual bool isSlowedByLiquids() const { return true; } -- virtual Entity* getLookingAt() const { return m_pEntLookedAt; } -+ virtual Entity* getLookingAt() const; - virtual void beforeRemove() {} - virtual bool canSpawn(); - virtual float getAttackAnim(float f) const; -@@ -145,7 +145,7 @@ class Mob : public Entity - Vec3 m_lPos; - Vec2 m_lRot; - int m_lastHurt; -- Entity* m_pEntLookedAt; -+ Entity::ID m_entLookedAtId; - - float v020_field_104; - -diff --git a/source/world/entity/Pig.cpp b/source/world/entity/Pig.cpp -index 153163f49..0f160d6d0 100644 ---- a/source/world/entity/Pig.cpp -+++ b/source/world/entity/Pig.cpp -@@ -6,6 +6,8 @@ - SPDX-License-Identifier: BSD-1-Clause - ********************************************************************/ - #include "Pig.hpp" -+#include "Player.hpp" -+#include "world/level/Level.hpp" - - Pig::Pig(Level* pLevel) : Animal(pLevel) - { -@@ -14,6 +16,7 @@ Pig::Pig(Level* pLevel) : Animal(pLevel) - m_texture = "mob/pig.png"; - setSize(0.9f, 0.9f); - // some dataitem stuff -+ setSaddle(true); - } - int Pig::getDeathLoot() const - { -@@ -23,6 +26,34 @@ int Pig::getDeathLoot() const - return Item::porkChop_raw->m_itemID; - } - -+bool Pig::interact(Player* pPlayer) -+{ -+ return false; -+ // @TODO: add saddles -+ /* -+ if (m_pLevel->m_bIsClientSide) -+ { -+ return false; -+ } -+ -+ if (!m_bSaddled) -+ { -+ return false; -+ } -+ -+ Entity* rider = getRider(); -+ -+ // already being ridden by someone else -+ if (rider && rider != pPlayer) -+ { -+ return false; -+ } -+ -+ pPlayer->ride(this); -+ return true; -+ */ -+} -+ - void Pig::setSaddle(bool b) - { - // @TODO: this -diff --git a/source/world/entity/Pig.hpp b/source/world/entity/Pig.hpp -index fb1d9ae77..0e93310d9 100644 ---- a/source/world/entity/Pig.hpp -+++ b/source/world/entity/Pig.hpp -@@ -19,7 +19,7 @@ class Pig : public Animal - std::string getHurtSound() const override { return "mob.pig"; } - int getDeathLoot() const override; - int getMaxHealth() const override { return 10; } -- bool interact(Player*) override { return false; } -+ bool interact(Player*) override; - - bool hasSaddle() const { return false; } - void setSaddle(bool b); -diff --git a/source/world/entity/Player.cpp b/source/world/entity/Player.cpp -index 34c37381b..a22fb7e90 100644 ---- a/source/world/entity/Player.cpp -+++ b/source/world/entity/Player.cpp -@@ -472,7 +472,9 @@ void Player::respawn() - - void Player::rideTick() - { -- -+ Mob::rideTick(); -+ m_oBob = m_bob; -+ m_bob = 0.0f; - } - - void Player::setDefaultHeadHeight() -diff --git a/source/world/entity/Player.hpp b/source/world/entity/Player.hpp -index ed63daee9..9e982f7dd 100644 ---- a/source/world/entity/Player.hpp -+++ b/source/world/entity/Player.hpp -@@ -94,7 +94,8 @@ class Player : public Mob - Dimension* getDimension() const; - void prepareCustomTextures(); - void respawn(); -- void rideTick(); -+ void rideTick() override; -+ float getRidingHeight() const override { return m_heightOffset - 0.5f; } - void setDefaultHeadHeight(); - void setRespawnPos(const TilePos& pos); - inline const Abilities& getAbilities() const { return m_abilities; } -diff --git a/source/world/entity/Spider.hpp b/source/world/entity/Spider.hpp -index e176275ca..1b2662fb5 100644 ---- a/source/world/entity/Spider.hpp -+++ b/source/world/entity/Spider.hpp -@@ -11,7 +11,7 @@ class Spider : public Monster - std::string getDeathSound() const override { return "mob.spiderdeath"; } - std::string getHurtSound() const override { return "mob.spider"; } - int getDeathLoot() const override { return ITEM_STRING; } -- float getRideHeight() const { return m_bbHeight * 0.75f - 0.5f; } -+ float getRideHeight() const override { return m_bbHeight * 0.75f - 0.5f; } - - Entity* findAttackTarget() override; - void checkHurtTarget(Entity* ent, float var2) override; -diff --git a/source/world/level/Level.cpp b/source/world/level/Level.cpp -index 7460bbb08..c5b170c09 100644 ---- a/source/world/level/Level.cpp -+++ b/source/world/level/Level.cpp -@@ -1253,6 +1253,17 @@ void Level::removeAllPendingEntityRemovals() - for (EntityVector::iterator it = m_pendingEntityRemovals.begin(); it != m_pendingEntityRemovals.end(); it++) - { - Entity* ent = *it; -+ if (Entity* riding = ent->getRiding()) -+ { -+ if (riding->m_bRemoved || riding->getRider() != ent) -+ { -+ riding->m_riderId = 0; -+ ent->m_ridingId = 0; -+ ent->setSharedFlag(C_ENTITY_FLAG_RIDING, false); -+ } -+ else -+ continue; -+ } - ent->removed(); - - LevelChunk* chunk = getChunk(ent->m_chunkPos); -@@ -1273,6 +1284,14 @@ void Level::removeEntities(const EntityVector& vec) - - bool Level::removeEntity(Entity* pEnt) - { -+ // kick off rider before disappearing -+ if (Entity* rider = pEnt->getRider()) -+ rider->ride(nullptr); -+ -+ // kick self off mount before disappearing -+ if (Entity* mount = pEnt->getRiding()) -+ mount->ride(nullptr); -+ - pEnt->remove(); - - if (pEnt->isPlayer()) -@@ -1678,7 +1697,12 @@ void Level::tick(Entity* pEnt, bool shouldTick) - pEnt->m_oRot = pEnt->m_rot; - - if (pEnt->m_bInAChunk) -- pEnt->tick(); -+ { -+ if (pEnt->getRiding()) -+ pEnt->rideTick(); -+ else -+ pEnt->tick(); -+ } - } - else - { -@@ -1712,6 +1736,24 @@ void Level::tick(Entity* pEnt, bool shouldTick) - getChunk(cp)->updateEntity(pEnt); - } - } -+ if (shouldTick && pEnt->m_bInAChunk) -+ { -+ Entity* rider = pEnt->getRider(); -+ if (rider) -+ { -+ if (rider->m_bRemoved || rider->getRiding() != pEnt) -+ { -+ rider->m_riderId = 0; -+ -+ pEnt->m_ridingId = 0; -+ pEnt->setSharedFlag(C_ENTITY_FLAG_RIDING, false); -+ } -+ else -+ { -+ tick(rider); -+ } -+ } -+ } - } - - void Level::tick(Entity* pEnt) -@@ -1748,6 +1790,18 @@ void Level::tickEntities() - { - Entity* pEnt = m_entities[i]; - -+ if (Entity* riding = pEnt->getRiding()) -+ { -+ if (riding->m_bRemoved || riding->getRider() != pEnt) -+ { -+ riding->m_riderId = 0; -+ pEnt->m_ridingId = 0; -+ pEnt->setSharedFlag(C_ENTITY_FLAG_RIDING, false); -+ } -+ else -+ continue; -+ } -+ - if (!pEnt->m_bRemoved) - { - tick(pEnt); -diff --git a/source/world/tile/FurnaceTile.cpp b/source/world/tile/FurnaceTile.cpp -index ccf3e1c28..e17ea782d 100644 ---- a/source/world/tile/FurnaceTile.cpp -+++ b/source/world/tile/FurnaceTile.cpp -@@ -148,7 +148,9 @@ void FurnaceTile::SetLit(bool lit, Level* level, const TilePos& pos) - { - TileEntity* tileEntity = level->getTileEntity(pos); - if (!tileEntity) -+ { - return; -+ } - - int data = level->getData(pos); - - -From bd18e03f60f3f899b7fb18ea0a96b1ee697fb97c Mon Sep 17 00:00:00 2001 -From: hellosammu -Date: Mon, 16 Feb 2026 19:30:15 -0500 -Subject: [PATCH 05/22] naming convention adjustment to match other classes - better - ---- - source/client/app/NinecraftApp.cpp | 4 +- - source/world/tile/entity/TileEntity.cpp | 2 +- - source/world/tile/entity/TileEntityType.cpp | 40 ++++++++-------- - source/world/tile/entity/TileEntityType.hpp | 52 ++++++++++++--------- - 4 files changed, 53 insertions(+), 45 deletions(-) - -diff --git a/source/client/app/NinecraftApp.cpp b/source/client/app/NinecraftApp.cpp -index 0a16d0d3d..1b336d72c 100644 ---- a/source/client/app/NinecraftApp.cpp -+++ b/source/client/app/NinecraftApp.cpp -@@ -180,10 +180,10 @@ void NinecraftApp::_initAll() - EntityTypeDescriptor::initDescriptors(); // custom - MobCategory::initMobCategories(); - MobFactory::initMobLists(); -+ TileEntityFactory::InitTileEntities(); - Tile::initTiles(); - Item::initItems(); - Biome::initBiomes(); -- TileEntityType::InitTileEntities(); - } - - _initOptions(); -@@ -341,7 +341,7 @@ void NinecraftApp::onGraphicsReset() - - void NinecraftApp::teardown() - { -- TileEntityType::TeardownTileEntities(); -+ TileEntityFactory::TeardownTileEntities(); - teardownRenderer(); - Resource::teardownLoaders(); - // Stop our SoundSystem before we nuke our sound buffers and cause it to implode -diff --git a/source/world/tile/entity/TileEntity.cpp b/source/world/tile/entity/TileEntity.cpp -index 9cd7b79ad..a5b11be86 100644 ---- a/source/world/tile/entity/TileEntity.cpp -+++ b/source/world/tile/entity/TileEntity.cpp -@@ -19,7 +19,7 @@ TileEntity* TileEntity::LoadTileEntity(const CompoundTag& tag) - } - - std::string id = tag.getString("id"); -- const TileEntityType* type = TileEntityType::GetType(id); -+ const TileEntityType* type = TileEntityFactory::GetType(id); - - if (!type) - { -diff --git a/source/world/tile/entity/TileEntityType.cpp b/source/world/tile/entity/TileEntityType.cpp -index 8ee14b727..1391e41ac 100644 ---- a/source/world/tile/entity/TileEntityType.cpp -+++ b/source/world/tile/entity/TileEntityType.cpp -@@ -8,44 +8,44 @@ - // #include "RecordPlayerTileEntity.hpp" - // #include "PistonMovingTileEntity.hpp" - --std::map TileEntityType::_types; -- - TileEntityType* TileEntityType::furnace; - TileEntityType* TileEntityType::chest; - TileEntityType* TileEntityType::noteblock; - --TileEntityType::TileEntityType(const std::string& name, CreateFunction func) : _name(name), _function(func) -+std::map TileEntityFactory::_types; -+ -+void TileEntityFactory::InitTileEntities() - { -+ TileEntityType::furnace = RegisterTileEntity("Furnace"); -+ TileEntityType::chest = RegisterTileEntity("Chest"); -+ TileEntityType::noteblock = RegisterTileEntity("Music"); - } - --const std::string& TileEntityType::getName() const -+void TileEntityFactory::TeardownTileEntities() - { -- return _name; -+ // delete all heap allocated tile entity types (furnace, chest, etc.) -+ for (std::map::const_iterator it = _types.begin(); it != _types.end(); ++it) -+ { -+ SAFE_DELETE(it->second); -+ } -+ _types.clear(); - } - --TileEntity* TileEntityType::newTileEntity() const -+const TileEntityType* TileEntityFactory::GetType(const std::string& name) - { -- return _function(); -+ return _types[name]; - } - --void TileEntityType::InitTileEntities() -+TileEntityType::TileEntityType(const std::string& name, CreateFunction func) : _name(name), _function(func) - { -- furnace = RegisterTileEntity("Furnace"); -- chest = RegisterTileEntity("Chest"); -- noteblock = RegisterTileEntity("Music"); - } - --const TileEntityType* TileEntityType::GetType(const std::string& name) -+const std::string& TileEntityType::getName() const - { -- return _types[name]; -+ return _name; - } - --void TileEntityType::TeardownTileEntities() -+TileEntity* TileEntityType::newTileEntity() const - { -- // delete all heap allocated tile entity types (furnace, chest, etc.) -- for (std::map::const_iterator it = _types.begin(); it != _types.end(); ++it) -- { -- SAFE_DELETE(it->second); -- } -- _types.clear(); -+ return _function(); - } -\ No newline at end of file -diff --git a/source/world/tile/entity/TileEntityType.hpp b/source/world/tile/entity/TileEntityType.hpp -index 3a981cf6c..0baa6b857 100644 ---- a/source/world/tile/entity/TileEntityType.hpp -+++ b/source/world/tile/entity/TileEntityType.hpp -@@ -6,10 +6,32 @@ - - class Level; - class TileEntity; -+class TileEntityType; -+ -+class TileEntityFactory -+{ -+private: -+ static std::map _types; -+ -+public: -+ static void InitTileEntities(); -+ static void TeardownTileEntities(); -+ static const TileEntityType* GetType(const std::string& name); -+ -+public: -+ template -+ static TileEntityType* RegisterTileEntity(const std::string& name); -+}; - - class TileEntityType - { - public: -+ static TileEntityType* furnace; -+ static TileEntityType* chest; -+ static TileEntityType* noteblock; -+ -+public: -+ friend class TileEntityFactory; - typedef TileEntity* (*CreateFunction)(); - - public: -@@ -19,32 +41,18 @@ class TileEntityType - const std::string& getName() const; - TileEntity* newTileEntity() const; - --public: -- static void InitTileEntities(); -- static const TileEntityType* GetType(const std::string& name); -- static void TeardownTileEntities(); -- --private: -- static std::map _types; -- - private: - std::string _name; - CreateFunction _function; - --public: -- static TileEntityType* furnace; -- static TileEntityType* chest; -- static TileEntityType* noteblock; -- - template - static TileEntity* CreateType() { return new T(); } -+}; - --public: -- template -- static TileEntityType* RegisterTileEntity(const std::string& name) -- { -- TileEntityType* type = new TileEntityType(name, &TileEntityType::CreateType); -- _types[type->_name] = type; -- return type; -- } --}; -\ No newline at end of file -+template -+TileEntityType* TileEntityFactory::RegisterTileEntity(const std::string& name) -+{ -+ TileEntityType* type = new TileEntityType(name, &TileEntityType::CreateType); -+ _types[type->_name] = type; -+ return type; -+} -\ No newline at end of file - -From ecdaaea97942f004e67b06d95b8a88594d702ae5 Mon Sep 17 00:00:00 2001 -From: hellosammu -Date: Mon, 16 Feb 2026 20:52:43 -0500 -Subject: [PATCH 06/22] Entities should be hashmaps. Can open furnace now - ---- - source/client/gui/ScreenChooser.cpp | 6 ++ - source/client/gui/ScreenChooser.hpp | 2 + - .../client/multiplayer/MultiPlayerLevel.cpp | 2 +- - .../network/ClientSideNetworkHandler.cpp | 3 +- - source/client/player/LocalPlayer.cpp | 2 +- - source/client/renderer/LevelRenderer.cpp | 8 +- - source/server/ServerPlayer.cpp | 20 ++++- - source/server/ServerPlayer.hpp | 1 + - source/server/ServerSideNetworkHandler.cpp | 4 +- - source/world/entity/Entity.hpp | 2 - - source/world/entity/Pig.cpp | 7 +- - source/world/entity/Player.cpp | 3 - - source/world/level/Level.cpp | 84 ++++++++++++------- - source/world/level/Level.hpp | 6 +- - .../storage/ExternalFileLevelStorage.cpp | 6 +- - 15 files changed, 104 insertions(+), 52 deletions(-) - -diff --git a/source/client/gui/ScreenChooser.cpp b/source/client/gui/ScreenChooser.cpp -index d2620083f..2ce71d3a7 100644 ---- a/source/client/gui/ScreenChooser.cpp -+++ b/source/client/gui/ScreenChooser.cpp -@@ -5,6 +5,7 @@ - #include "screens/PauseScreen.hpp" - #include "screens/inventory/CraftingScreen.hpp" - #include "screens/inventory/ChestScreen.hpp" -+#include "screens/inventory/FurnaceScreen.hpp" - #include "screens/OptionsScreen.hpp" - #include "screens/OptionsScreen_Console.hpp" - #include "screens/CreateWorldScreen.hpp" -@@ -53,6 +54,11 @@ void ScreenChooser::pushCraftingScreen(Player* player, const TilePos& pos) - m_pMinecraft->setScreen(new CraftingScreen(player->m_pInventory, pos, player->m_pLevel)); - } - -+void ScreenChooser::pushFurnaceScreen(Player* player, FurnaceTileEntity* furnace) -+{ -+ m_pMinecraft->setScreen(new FurnaceScreen(player->m_pInventory, furnace)); -+} -+ - void ScreenChooser::pushChestScreen(Player* player, Container* container) - { - m_pMinecraft->setScreen(new ChestScreen(player->m_pInventory, container)); -diff --git a/source/client/gui/ScreenChooser.hpp b/source/client/gui/ScreenChooser.hpp -index c052f6233..e78800ade 100644 ---- a/source/client/gui/ScreenChooser.hpp -+++ b/source/client/gui/ScreenChooser.hpp -@@ -7,6 +7,7 @@ class Player; - class Minecraft; - class Container; - class Screen; -+class FurnaceTileEntity; - - //@NOTE: This is just based on MCPE, not really a decompilation, make it accurate if necessary - class ScreenChooser -@@ -22,6 +23,7 @@ class ScreenChooser - virtual void pushOptionsScreen(Screen*); - virtual void pushProgressScreen(); - virtual void pushCraftingScreen(Player*, const TilePos&); // originally pushWorkbenchScreen -+ virtual void pushFurnaceScreen(Player*, FurnaceTileEntity*); - virtual void pushChestScreen(Player*, Container*); - virtual void pushCreditsScreen(Screen*); - -diff --git a/source/client/multiplayer/MultiPlayerLevel.cpp b/source/client/multiplayer/MultiPlayerLevel.cpp -index 1890674d5..66c6bcfa4 100644 ---- a/source/client/multiplayer/MultiPlayerLevel.cpp -+++ b/source/client/multiplayer/MultiPlayerLevel.cpp -@@ -13,7 +13,7 @@ void MultiPlayerLevel::tick() - for (size_t i = 0; i < 10 && i < m_reEntries.size(); i++) - { - Entity* pEntity = m_reEntries[i]; -- if (std::find(m_entities.begin(), m_entities.end(), pEntity) != m_entities.end()) -+ if (m_entitiesById.find(pEntity->hashCode()) == m_entitiesById.end()) - { - addEntity(pEntity); - } -diff --git a/source/client/network/ClientSideNetworkHandler.cpp b/source/client/network/ClientSideNetworkHandler.cpp -index 7b5fd9559..56f55f0df 100644 ---- a/source/client/network/ClientSideNetworkHandler.cpp -+++ b/source/client/network/ClientSideNetworkHandler.cpp -@@ -17,6 +17,7 @@ - #include "world/entity/MobFactory.hpp" - #include "world/level/Explosion.hpp" - #include "world/inventory/SimpleContainer.hpp" -+#include "world/tile/entity/FurnaceTileEntity.hpp" - - // This lets you make the client shut up and not log events in the debug console. - //#define VERBOSE_CLIENT -@@ -683,7 +684,7 @@ void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& guid, ContainerO - pLocalPlayer->openContainer(new SimpleContainer(packet->m_size, packet->m_title.C_String())); - break; - case Container::FURNACE: -- //pLocalPlayer->openFurnace(new FurnaceTileEntity()); -+ pLocalPlayer->openFurnace(new FurnaceTileEntity); - break; - case Container::DISPENSER: - //pLocalPlayer->openTrap(new DispenserTileEntity()); -diff --git a/source/client/player/LocalPlayer.cpp b/source/client/player/LocalPlayer.cpp -index 8db2a1dcb..6ca19d378 100644 ---- a/source/client/player/LocalPlayer.cpp -+++ b/source/client/player/LocalPlayer.cpp -@@ -138,7 +138,7 @@ void LocalPlayer::startCrafting(const TilePos& pos) - void LocalPlayer::openFurnace(FurnaceTileEntity* furnace) - { - // PE 0.3.2 doesn't let you cook in creative mode -- m_pMinecraft->setScreen(new FurnaceScreen(m_pInventory, furnace)); -+ m_pMinecraft->getScreenChooser()->pushFurnaceScreen(this, furnace); - } - - void LocalPlayer::openContainer(Container* container) -diff --git a/source/client/renderer/LevelRenderer.cpp b/source/client/renderer/LevelRenderer.cpp -index f9c47bc95..e30b75164 100644 ---- a/source/client/renderer/LevelRenderer.cpp -+++ b/source/client/renderer/LevelRenderer.cpp -@@ -1584,12 +1584,12 @@ void LevelRenderer::renderEntities(Vec3 pos, Culler* culler, float f) - - EntityRenderDispatcher::off = camera->m_posPrev + (camera->m_pos - camera->m_posPrev) * f; - -- const EntityVector* pVec = m_pLevel->getAllEntities(); -+ const EntityMap* pVec = m_pLevel->getAllEntities(); - m_totalEntities = int(pVec->size()); - -- for (int i = 0; i < m_totalEntities; i++) -- { -- const Entity* entity = (*pVec)[i]; -+ for (EntityMap::const_iterator it = pVec->begin(); it != pVec->end(); ++it) -+ { -+ const Entity* entity = it->second; - if (!entity->shouldRender(pos)) - continue; - -diff --git a/source/server/ServerPlayer.cpp b/source/server/ServerPlayer.cpp -index c48af8dd7..21ed28339 100644 ---- a/source/server/ServerPlayer.cpp -+++ b/source/server/ServerPlayer.cpp -@@ -9,8 +9,10 @@ - #include "network/packets/ContainerSetContentPacket.hpp" - #include "network/RakNetInstance.hpp" - #include "world/inventory/CraftingMenu.hpp" -+#include "world/inventory/FurnaceMenu.hpp" - #include "world/inventory/ChestMenu.hpp" - #include "world/level/Level.hpp" -+#include "world/tile/entity/FurnaceTileEntity.hpp" - - ServerPlayer::ServerPlayer(Level* pLevel, GameType playerGameType) - : Player(pLevel, playerGameType) -@@ -58,7 +60,7 @@ void ServerPlayer::openContainer(Container* container) - #if NETWORK_PROTOCOL_VERSION >= 5 - m_pLevel->m_pRakNetInstance->send( - new ContainerOpenPacket( -- m_pContainerMenu->m_containerId, Container::CONTAINER, -+ m_containerId, Container::CONTAINER, - container->getName(), container->getContainerSize() - ) - ); -@@ -76,6 +78,22 @@ void ServerPlayer::closeContainer() - doCloseContainer(); - } - -+void ServerPlayer::openFurnace(FurnaceTileEntity* furnace) -+{ -+ _nextContainerCounter(); -+ -+#if NETWORK_PROTOCOL_VERSION >= 5 -+ m_pLevel->m_pRakNetInstance->send( -+ new ContainerOpenPacket( -+ m_containerId, Container::FURNACE, -+ furnace->getName(), furnace->getContainerSize() -+ ) -+ ); -+#endif -+ -+ setContainerMenu(new FurnaceMenu(m_pInventory, furnace)); -+} -+ - void ServerPlayer::take(Entity* pEnt, int count) - { - m_pLevel->m_pRakNetInstance->send(new TakeItemEntityPacket(pEnt->m_EntityID, m_EntityID)); -diff --git a/source/server/ServerPlayer.hpp b/source/server/ServerPlayer.hpp -index ddc2b7d53..11152a123 100644 ---- a/source/server/ServerPlayer.hpp -+++ b/source/server/ServerPlayer.hpp -@@ -14,6 +14,7 @@ class ServerPlayer : public Player, public ContainerListener - void startCrafting(const TilePos& pos) override; - void openContainer(Container* container) override; - void closeContainer() override; -+ void openFurnace(FurnaceTileEntity* tileEntity); - void take(Entity* pEnt, int count) override; - - void refreshContainer(ContainerMenu* menu, const std::vector& items) override; -diff --git a/source/server/ServerSideNetworkHandler.cpp b/source/server/ServerSideNetworkHandler.cpp -index 840207710..3cb71fd4e 100644 ---- a/source/server/ServerSideNetworkHandler.cpp -+++ b/source/server/ServerSideNetworkHandler.cpp -@@ -218,9 +218,9 @@ void ServerSideNetworkHandler::handle(const RakNet::RakNetGUID& guid, ReadyPacke - - #if NETWORK_PROTOCOL_VERSION >= 3 - // send the connecting player info about all entities in the world -- for (size_t i = 0; i < m_pLevel->m_entities.size(); i++) -+ for (EntityMap::iterator it = m_pLevel->m_entities.begin(); it != m_pLevel->m_entities.end(); ++it) - { -- Entity* entity = m_pLevel->m_entities[i]; -+ Entity* entity = it->second; - if (canReplicateEntity(entity)) - { - AddMobPacket packet(*((Mob*)entity)); -diff --git a/source/world/entity/Entity.hpp b/source/world/entity/Entity.hpp -index e104b01de..0869cc71b 100644 ---- a/source/world/entity/Entity.hpp -+++ b/source/world/entity/Entity.hpp -@@ -204,8 +204,6 @@ class Entity - virtual float getRidingHeight() const { return m_heightOffset; } - Entity* getRiding() const; - Entity* getRider() const; -- // void setRiding(Entity* newRiding); -- // void setRider(Entity* newRider); - void load(const CompoundTag& tag); - bool save(CompoundTag& tag) const; - void saveWithoutId(CompoundTag& tag) const; -diff --git a/source/world/entity/Pig.cpp b/source/world/entity/Pig.cpp -index 0f160d6d0..7ba77895e 100644 ---- a/source/world/entity/Pig.cpp -+++ b/source/world/entity/Pig.cpp -@@ -20,10 +20,9 @@ Pig::Pig(Level* pLevel) : Animal(pLevel) - } - int Pig::getDeathLoot() const - { -- if (isOnFire()) -- return Item::porkChop_cooked->m_itemID; -- else -- return Item::porkChop_raw->m_itemID; -+ return (isOnFire()) ? -+ Item::porkChop_cooked->m_itemID : -+ Item::porkChop_raw->m_itemID; - } - - bool Pig::interact(Player* pPlayer) -diff --git a/source/world/entity/Player.cpp b/source/world/entity/Player.cpp -index a22fb7e90..9d0b19685 100644 ---- a/source/world/entity/Player.cpp -+++ b/source/world/entity/Player.cpp -@@ -541,17 +541,14 @@ void Player::drop(const ItemStack& item, bool randomly) - - void Player::startCrafting(const TilePos& pos) - { -- - } - - void Player::openFurnace(FurnaceTileEntity* tileEntity) - { -- - } - - void Player::startStonecutting(const TilePos& pos) - { -- - } - - void Player::startDestroying() -diff --git a/source/world/level/Level.cpp b/source/world/level/Level.cpp -index c5b170c09..0c1efd729 100644 ---- a/source/world/level/Level.cpp -+++ b/source/world/level/Level.cpp -@@ -80,10 +80,9 @@ Level::~Level() - SAFE_DELETE(m_pPathFinder); - SAFE_DELETE(m_pMobSpawner); - -- const size_t size = m_entities.size(); -- for (size_t i = 0; i < size; i++) -+ for (EntityMap::iterator it = m_entities.begin(); it != m_entities.end(); it++) - { -- Entity* pEnt = m_entities.at(i); -+ Entity* pEnt = it->second; - - //you better HOPE this is freed by Minecraft! (or a NetworkHandler) - //Really should have used shared pointers and stuff. -@@ -358,22 +357,18 @@ Material* Level::getMaterial(const TilePos& pos) const - - Entity* Level::getEntity(Entity::ID id) const - { -- // @TODO: wtf? no map?? -- - // prioritize players first. - for (std::vector::const_iterator it = m_players.begin(); it != m_players.end(); it++) - { - Player* pEnt = *it; -- if (pEnt->m_EntityID == id) -- return pEnt; -- } -- for (std::vector::const_iterator it = m_entities.begin(); it != m_entities.end(); it++) -- { -- Entity* pEnt = *it; -- if (pEnt->m_EntityID == id) -+ if (pEnt->hashCode() == id) - return pEnt; - } - -+ EntityMap::const_iterator it = m_entities.find(id); -+ if (it != m_entities.end()) -+ return it->second; -+ - return nullptr; - } - -@@ -386,7 +381,7 @@ unsigned int Level::getEntityCount(const EntityCategories& category) const - return it->second; - } - --const EntityVector* Level::getAllEntities() const -+const EntityMap* Level::getAllEntities() const - { - return &m_entities; - } -@@ -1248,7 +1243,15 @@ void Level::validateSpawn() - - void Level::removeAllPendingEntityRemovals() - { -- Util::removeAll(m_entities, m_pendingEntityRemovals); -+ for (EntityVector::iterator it = m_pendingEntityRemovals.begin(); it != m_pendingEntityRemovals.end(); it++) -+ { -+ Entity* ent = *it; -+ if (m_entities.find(ent->hashCode()) != m_entities.end()) -+ { -+ m_entities.erase(ent->hashCode()); -+ } -+ -+ } - - for (EntityVector::iterator it = m_pendingEntityRemovals.begin(); it != m_pendingEntityRemovals.end(); it++) - { -@@ -1326,7 +1329,7 @@ bool Level::addEntity(Entity* pEnt) - m_players.push_back((Player*)pEnt); - } - -- m_entities.push_back(pEnt); -+ m_entities.insert(std::make_pair(pEnt->hashCode(), pEnt)); - - entityAdded(pEnt); - -@@ -1392,9 +1395,9 @@ void Level::sendEntityData() - return; - - // Inlined on 0.2.1, god bless PerfTimer -- for (EntityVector::iterator it = m_entities.begin(); it != m_entities.end(); it++) -+ for (EntityMap::iterator it = m_entities.begin(); it != m_entities.end(); it++) - { -- Entity* ent = *it; -+ Entity* ent = it->second; - SynchedEntityData& data = ent->getEntityData(); - if (data.isDirty()) - m_pRakNetInstance->send(new SetEntityDataPacket(ent->m_EntityID, data)); -@@ -1786,9 +1789,9 @@ void Level::tickEntities() - // inlined in the original - removeAllPendingEntityRemovals(); - -- for (size_t i = 0; i < m_entities.size(); i++) -+ for (EntityMap::iterator it = m_entities.begin(); it != m_entities.end();) - { -- Entity* pEnt = m_entities[i]; -+ Entity* pEnt = it->second; - - if (Entity* riding = pEnt->getRiding()) - { -@@ -1799,24 +1802,36 @@ void Level::tickEntities() - pEnt->setSharedFlag(C_ENTITY_FLAG_RIDING, false); - } - else -+ { -+ ++it; - continue; -+ } - } - - if (!pEnt->m_bRemoved) - { - tick(pEnt); -+ ++it; -+ -+ continue; - } -- else if (!pEnt->isPlayer() || pEnt->m_bForceRemove) -+ -+ if (!pEnt->isPlayer() || pEnt->m_bForceRemove) - { - if (pEnt->m_bInAChunk && hasChunk(pEnt->m_chunkPos)) - getChunk(pEnt->m_chunkPos)->removeEntity(pEnt); - -- m_entities.erase(m_entities.begin() + i); -- i--; -+ EntityMap::iterator itErase = it; -+ it++; -+ m_entities.erase(itErase); - - entityRemoved(pEnt); - delete pEnt; -+ -+ continue; - } -+ -+ ++it; - } - - m_bUpdatingTileEntities = true; -@@ -1834,7 +1849,7 @@ void Level::tickEntities() - if (ch) - ch->removeTileEntity(tileEnt->m_pos); - -- m_entities.erase(m_entities.begin() + i); -+ m_tileEntityList.erase(m_tileEntityList.begin() + i); - i--; - - delete tileEnt; -@@ -2076,11 +2091,24 @@ void Level::explode(Entity* entity, const Vec3& pos, float power, bool bIsFiery) - - void Level::addEntities(const EntityVector& entities) - { -- m_entities.insert(m_entities.end(), entities.begin(), entities.end()); -- -- for (EntityVector::iterator it = m_entities.begin(); it != m_entities.end(); it++) -+ for (EntityVector::const_iterator it = entities.begin(); it != entities.end(); it++) - { - Entity* pEnt = *it; -+ EntityMap::iterator result = m_entities.find(pEnt->hashCode()); -+ -+ if (result != m_entities.end()) -+ { -+ if (result->second == pEnt) -+ { -+ LOG_W("Entity %d already exists. Skipping...", pEnt->hashCode()); -+ continue; -+ } -+ -+ removeEntity(result->second); -+ continue; -+ } -+ -+ m_entities.insert(std::make_pair(pEnt->hashCode(), pEnt)); - entityAdded(pEnt); - } - } -@@ -2097,9 +2125,9 @@ void Level::ensureAdded(Entity* entity) - for (cp.z = chunkPos.z - 2; cp.z <= chunkPos.z + 2; cp.z++) - getChunk(cp); - -- EntityVector::iterator result = std::find(m_entities.begin(), m_entities.end(), entity); -+ EntityMap::iterator result = m_entities.find(entity->hashCode()); - if (result == m_entities.end()) -- m_entities.push_back(entity); -+ m_entities.insert(std::make_pair(entity->hashCode(), entity)); - } - - bool Level::extinguishFire(Player* player, const TilePos& pos, Facing::Name face) -diff --git a/source/world/level/Level.hpp b/source/world/level/Level.hpp -index 95ae707ea..9caf0153e 100644 ---- a/source/world/level/Level.hpp -+++ b/source/world/level/Level.hpp -@@ -14,6 +14,7 @@ - #define _USE_MATH_DEFINES - #endif - #include -+#include - - #include "client/renderer/LightUpdate.hpp" - #include "world/tile/Tile.hpp" -@@ -38,6 +39,7 @@ class Packet; - class MobSpawner; - - typedef std::vector EntityVector; -+typedef std::map EntityMap; - typedef std::vector TileEntityVector; - typedef std::vector AABBVector; - -@@ -183,7 +185,7 @@ class Level : public LevelSource - HitResult clip(Vec3 a, Vec3 b, bool c) const; - Entity* getEntity(Entity::ID id) const; - unsigned int getEntityCount(const EntityCategories&) const; -- const EntityVector* getAllEntities() const; -+ const EntityMap* getAllEntities() const; - EntityVector getEntities(Entity* pAvoid, const AABB&) const; - BiomeSource* getBiomeSource() const override; - LevelStorage* getLevelStorage() const { return m_pLevelStorage; } -@@ -219,7 +221,7 @@ class Level : public LevelSource - bool m_bInstantTicking; - bool m_bIsClientSide; // if the level is controlled externally by a server. - bool m_bPostProcessing; -- EntityVector m_entities; -+ EntityMap m_entities; - std::vector m_players; - int m_skyDarken; - uint8_t field_30; -diff --git a/source/world/level/storage/ExternalFileLevelStorage.cpp b/source/world/level/storage/ExternalFileLevelStorage.cpp -index 681d04db0..31e8fcb35 100644 ---- a/source/world/level/storage/ExternalFileLevelStorage.cpp -+++ b/source/world/level/storage/ExternalFileLevelStorage.cpp -@@ -400,10 +400,10 @@ void ExternalFileLevelStorage::saveEntities(Level* level, LevelChunk* chunk) - //getTimeS(); - ListTag* entitiesTag = new ListTag(); - -- const EntityVector* entities = level->getAllEntities(); -- for (EntityVector::const_iterator it = entities->begin(); it != entities->end(); it++) -+ const EntityMap* entities = level->getAllEntities(); -+ for (EntityMap::const_iterator it = entities->begin(); it != entities->end(); it++) - { -- const Entity* entity = *it; -+ const Entity* entity = it->second; - CompoundTag* tag = new CompoundTag(); - - if (!entity->save(*tag)) - -From d0699c4839197b656c468e8191fcb0589f975ef4 Mon Sep 17 00:00:00 2001 -From: hellosammu -Date: Tue, 17 Feb 2026 08:22:55 -0500 -Subject: [PATCH 07/22] bounds check fix - ---- - source/world/inventory/ContainerMenu.cpp | 3 ++- - 1 file changed, 2 insertions(+), 1 deletion(-) - -diff --git a/source/world/inventory/ContainerMenu.cpp b/source/world/inventory/ContainerMenu.cpp -index 7a549e7b0..f618561e3 100644 ---- a/source/world/inventory/ContainerMenu.cpp -+++ b/source/world/inventory/ContainerMenu.cpp -@@ -332,7 +332,8 @@ void ContainerMenu::setItem(int index, ItemStack item) - - void ContainerMenu::setAll(const std::vector& items) - { -- for (size_t i = 0; i < items.size(); ++i) -+ size_t n = std::min(items.size(), m_slots.size()); -+ for (size_t i = 0; i < n; ++i) - { - m_slots[i]->set(items[i]); - } - -From 22170d2907952956801216a66f102eacb0c294b8 Mon Sep 17 00:00:00 2001 -From: hellosammu -Date: Tue, 17 Feb 2026 13:44:35 -0500 -Subject: [PATCH 08/22] vsync, top snow fix - ---- - platforms/android/AppPlatform_android.cpp | 9 ++++++ - platforms/android/AppPlatform_android.hpp | 1 + - platforms/sdl/base/AppPlatform_sdl.cpp | 7 +++++ - platforms/sdl/base/AppPlatform_sdl.hpp | 1 + - platforms/sdl/sdl2/main.cpp | 14 +++------ - platforms/windows/AppPlatform_win32.cpp | 7 +++++ - platforms/windows/AppPlatform_win32.hpp | 1 + - platforms/windows/main.cpp | 2 ++ - platforms/xdk360/AppPlatform_xdk360.cpp | 6 ++++ - platforms/xdk360/AppPlatform_xdk360.hpp | 1 + - platforms/xdk360/main.cpp | 2 ++ - source/client/app/AppPlatform.cpp | 4 +++ - source/client/app/AppPlatform.hpp | 2 ++ - source/client/options/Options.cpp | 12 +++++-- - source/client/options/Options.hpp | 12 ++++++- - source/client/renderer/LevelRenderer.cpp | 4 +-- - source/client/renderer/TileRenderer.cpp | 38 +++++++++++------------ - source/world/tile/TopSnowTile.cpp | 18 ++++++++++- - source/world/tile/TopSnowTile.hpp | 1 + - 19 files changed, 107 insertions(+), 35 deletions(-) - -diff --git a/platforms/android/AppPlatform_android.cpp b/platforms/android/AppPlatform_android.cpp -index ceb1f9872..a6cedc81b 100644 ---- a/platforms/android/AppPlatform_android.cpp -+++ b/platforms/android/AppPlatform_android.cpp -@@ -141,6 +141,15 @@ void AppPlatform_android::setScreenSize(int width, int height) - m_ScreenHeight = height; - } - -+void AppPlatform_android::setVSyncEnabled(bool enabled) -+{ -+ EGLDisplay display = eglGetCurrentDisplay(); -+ if (display == EGL_NO_DISPLAY) -+ return; -+ -+ glSwapInterval(display, enabled ? 1 : 0); -+} -+ - void AppPlatform_android::initAndroidApp(android_app* ptr) - { - m_app = ptr; -diff --git a/platforms/android/AppPlatform_android.hpp b/platforms/android/AppPlatform_android.hpp -index ce7f219a4..fecbbc46c 100644 ---- a/platforms/android/AppPlatform_android.hpp -+++ b/platforms/android/AppPlatform_android.hpp -@@ -55,6 +55,7 @@ class AppPlatform_android : public AppPlatform - void setExternalStoragePath(const std::string& path); - - AssetFile readAssetFile(const std::string&, bool) const override; -+ void setVSyncEnabled(bool enabled) override; - - private: - void changeKeyboardVisibility(bool bShown); -diff --git a/platforms/sdl/base/AppPlatform_sdl.cpp b/platforms/sdl/base/AppPlatform_sdl.cpp -index 30de0ac72..aae613798 100644 ---- a/platforms/sdl/base/AppPlatform_sdl.cpp -+++ b/platforms/sdl/base/AppPlatform_sdl.cpp -@@ -149,6 +149,13 @@ void AppPlatform_sdl::_setDefaultIcon() - _setIcon(data); - } - -+void AppPlatform_sdl::setVSyncEnabled(bool enabled) -+{ -+#if MCE_GFX_API_OGL -+ SDL_GL_SetSwapInterval(enabled ? 1 : 0); -+#endif -+} -+ - void AppPlatform_sdl::initSoundSystem() - { - if (m_pSoundSystem) -diff --git a/platforms/sdl/base/AppPlatform_sdl.hpp b/platforms/sdl/base/AppPlatform_sdl.hpp -index c93d4f4b2..d72a97b5b 100644 ---- a/platforms/sdl/base/AppPlatform_sdl.hpp -+++ b/platforms/sdl/base/AppPlatform_sdl.hpp -@@ -38,6 +38,7 @@ class AppPlatform_sdl : public AppPlatform - int getUserInputStatus() override; - void saveScreenshot(const std::string& fileName, int width, int height) override; - SoundSystem* getSoundSystem() const override { return m_pSoundSystem; } -+ void setVSyncEnabled(bool enabled) override; - - // Also add these to allow proper turning within the game. - void setMouseGrabbed(bool b) override; -diff --git a/platforms/sdl/sdl2/main.cpp b/platforms/sdl/sdl2/main.cpp -index fa228b636..0fc0713a4 100644 ---- a/platforms/sdl/sdl2/main.cpp -+++ b/platforms/sdl/sdl2/main.cpp -@@ -63,16 +63,11 @@ static void initGraphics() - exit(EXIT_FAILURE); - } - -- // Enable V-Sync -- // Not setting this explicitly results in undefined behavior -- if (SDL_GL_SetSwapInterval(-1) == -1) // Try adaptive -+ // Vsync is controlled through the AppPlatform, -+ // default to no vsync here, let platform set it when needed -+ if (SDL_GL_SetSwapInterval(0) == -1) - { -- LOG_W("Adaptive V-Sync is not supported on this platform. Falling back to standard V-Sync..."); -- // fallback to standard -- if (SDL_GL_SetSwapInterval(1) == -1) -- { -- LOG_W("Setting the swap interval for V-Sync is not supported on this platform!"); -- } -+ LOG_W("Setting the swap interval is not supported on this platform!"); - } - - if (!mce::Platform::OGL::InitBindings()) -@@ -430,6 +425,7 @@ int main(int argc, char *argv[]) - // Start MCPE - g_pAppPlatform = new UsedAppPlatform(storagePath, window); - g_pAppPlatform->m_externalStorageDir = storagePath; -+ g_pAppPlatform->setVSyncEnabled(true); - g_pApp = new NinecraftApp; - g_pApp->m_pPlatform = g_pAppPlatform; - g_pApp->init(); -diff --git a/platforms/windows/AppPlatform_win32.cpp b/platforms/windows/AppPlatform_win32.cpp -index f54c64812..674b5bf3b 100644 ---- a/platforms/windows/AppPlatform_win32.cpp -+++ b/platforms/windows/AppPlatform_win32.cpp -@@ -484,6 +484,13 @@ bool AppPlatform_win32::initGraphics(int width, int height) - return true; - } - -+void AppPlatform_win32::setVSyncEnabled(bool enabled) -+{ -+#if MCE_GFX_API_OGL -+ xglSwapIntervalEXT(enabled ? 1 : 0); -+#endif -+} -+ - void AppPlatform_win32::createWindowSizeDependentResources(const Vec2& logicalSize, const Vec2& compositionScale) - { - #if MCE_GFX_API_D3D9 -diff --git a/platforms/windows/AppPlatform_win32.hpp b/platforms/windows/AppPlatform_win32.hpp -index 1dc3bb7e0..d047243e9 100644 ---- a/platforms/windows/AppPlatform_win32.hpp -+++ b/platforms/windows/AppPlatform_win32.hpp -@@ -82,6 +82,7 @@ class AppPlatform_win32 : public AppPlatform - bool initGraphics(int width, int height); - void createWindowSizeDependentResources(const Vec2& logicalSize, const Vec2& compositionScale); - void swapBuffers(); -+ void setVSyncEnabled(bool enabled) override; - - static MouseButtonType GetMouseButtonType(UINT iMsg); - static bool GetMouseButtonState(UINT iMsg, WPARAM wParam); -diff --git a/platforms/windows/main.cpp b/platforms/windows/main.cpp -index 00f703ea1..b614ca405 100644 ---- a/platforms/windows/main.cpp -+++ b/platforms/windows/main.cpp -@@ -152,6 +152,8 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine - if (!g_AppPlatform.initGraphics(Minecraft::width, Minecraft::height)) - goto _cleanup; - -+ g_AppPlatform.setVSyncEnabled(true); -+ - g_pApp = new NinecraftApp; - g_pApp->m_pPlatform = &g_AppPlatform; - -diff --git a/platforms/xdk360/AppPlatform_xdk360.cpp b/platforms/xdk360/AppPlatform_xdk360.cpp -index 0cf1313db..33fea93f4 100644 ---- a/platforms/xdk360/AppPlatform_xdk360.cpp -+++ b/platforms/xdk360/AppPlatform_xdk360.cpp -@@ -319,6 +319,12 @@ const std::string& AppPlatform_xdk360::getKeyboardText() const - return m_keyboardText; - } - -+void AppPlatform_xdk360::setVSyncEnabled(bool enabled) -+{ -+ // XDK360 V-Sync control would go here -+ // For Xbox 360, this is typically handled by the D3D device settings -+} -+ - bool AppPlatform_xdk360::initGraphics(unsigned int width, unsigned int height) - { - m_bHasGraphics = true; -diff --git a/platforms/xdk360/AppPlatform_xdk360.hpp b/platforms/xdk360/AppPlatform_xdk360.hpp -index d1409aaff..d2934aa71 100644 ---- a/platforms/xdk360/AppPlatform_xdk360.hpp -+++ b/platforms/xdk360/AppPlatform_xdk360.hpp -@@ -52,6 +52,7 @@ class AppPlatform_xdk360 : public AppPlatform - bool initGraphics(unsigned int width, unsigned int height); - void createWindowSizeDependentResources(unsigned int width, unsigned int height); - void swapBuffers(); -+ void setVSyncEnabled(bool enabled) override; - - private: - int m_ScreenWidth; -diff --git a/platforms/xdk360/main.cpp b/platforms/xdk360/main.cpp -index 2af6b639a..06d09f0b7 100644 ---- a/platforms/xdk360/main.cpp -+++ b/platforms/xdk360/main.cpp -@@ -45,6 +45,8 @@ void __cdecl main() - if (!g_AppPlatform.initGraphics(Minecraft::width, Minecraft::height)) - goto _cleanup; - -+ g_AppPlatform.setVSyncEnabled(true); -+ - g_pApp = new NinecraftApp; - g_pApp->m_pPlatform = &g_AppPlatform; - g_AppPlatform.m_externalStorageDir = "savedrive:"; -diff --git a/source/client/app/AppPlatform.cpp b/source/client/app/AppPlatform.cpp -index ef28f0f4b..74713c1ca 100644 ---- a/source/client/app/AppPlatform.cpp -+++ b/source/client/app/AppPlatform.cpp -@@ -320,6 +320,10 @@ std::string AppPlatform::getExternalStoragePath(const std::string& path) const - return m_externalStorageDir + C_HOME_PATH + path; - } - -+void AppPlatform::setVSyncEnabled(bool enabled) -+{ -+} -+ - bool AppPlatform::hasAssetFile(const std::string& path) const - { - return isRegularFile(path.c_str()); -diff --git a/source/client/app/AppPlatform.hpp b/source/client/app/AppPlatform.hpp -index 9cf1b6cc6..26d74bbd6 100644 ---- a/source/client/app/AppPlatform.hpp -+++ b/source/client/app/AppPlatform.hpp -@@ -107,6 +107,8 @@ class AppPlatform - virtual void vibrate(int milliSeconds); - virtual bool getRecenterMouseEveryTick(); - virtual std::string getClipboardText(); -+ // Graphics settings -+ virtual void setVSyncEnabled(bool enabled); - - void _fireLowMemory(); - void _fireAppSuspended(); -diff --git a/source/client/options/Options.cpp b/source/client/options/Options.cpp -index 546f53a58..c6d24cb34 100644 ---- a/source/client/options/Options.cpp -+++ b/source/client/options/Options.cpp -@@ -98,7 +98,7 @@ Options::Options(Minecraft* mc, const std::string& folderPath) : - //, m_limitFramerate("gfx_fpslimit", "options.framerateLimit", 0, ValuesBuilder().add(performance.max").add("performance.balanced").add("performance.powersaver")) - //, m_bMipmaps("gfx_mipmaps", "options.mipmaps") - //, m_moreWorldOptions("misc_moreworldoptions", "options.moreWorldOptions", true) -- //, m_vSync("enableVsync", "options.enableVsync") -+ , m_vSync("enableVsync", "options.enableVsync") - { - add(m_musicVolume); - add(m_masterVolume); -@@ -127,10 +127,11 @@ Options::Options(Minecraft* mc, const std::string& folderPath) : - add(m_debugText); - add(m_lang); - add(m_bUseController); -+ add(m_hudSize); - add(m_uiTheme); - add(m_logoType); -- add(m_hudSize); - add(m_classicCrafting); -+ add(m_vSync); - _initDefaultValues(); - if (folderPath.empty()) return; - m_filePath = folderPath + "/options.txt"; -@@ -812,4 +813,9 @@ void ControllerOption::apply() - // For now, I just wanted to be able to switch to controller input on mobile devices. - if (m_pMinecraft && m_pMinecraft->m_pInputHolder) - m_pMinecraft->reloadInput(); --} -\ No newline at end of file -+} -+ -+void VsyncOption::apply() -+{ -+ m_pMinecraft->platform()->setVSyncEnabled(get()); -+} -diff --git a/source/client/options/Options.hpp b/source/client/options/Options.hpp -index 1fe458dae..72cce0703 100644 ---- a/source/client/options/Options.hpp -+++ b/source/client/options/Options.hpp -@@ -222,6 +222,14 @@ class ControllerOption : public BoolOption - void apply() override; - }; - -+class VsyncOption : public BoolOption -+{ -+public: -+ VsyncOption(const std::string& key, const std::string& name, bool initial = true) : BoolOption(key, name, initial) {} -+ -+ void apply() override; -+}; -+ - class FancyGraphicsOption : public GraphicsOption - { - public: -@@ -326,7 +334,7 @@ class UIThemeOption : public ValuesOption - class HUDSizeOption : public MinMaxOption - { - public: -- HUDSizeOption(const std::string& key, const std::string& name, int initial) : MinMaxOption(key, name, initial, HUD_SIZE_1, HUD_SIZE_3 + 1) -+ HUDSizeOption(const std::string& key, const std::string& name, int initial) : MinMaxOption(key, name, initial, HUD_SIZE_1, HUD_SIZE_MAX + 1) - { - } - -@@ -438,6 +446,7 @@ class Options - LogoTypeOption m_logoType; - HUDSizeOption m_hudSize; - BoolOption m_classicCrafting; -+ VsyncOption m_vSync; - ResourcePackStack m_resourcePacks; - }; - -@@ -483,6 +492,7 @@ class Options - OPTION(m_viewBobbing); \ - OPTION(m_anaglyphs); \ - OPTION(m_blockOutlines); \ -+ OPTION(m_vSync); \ - OPTION(m_fancyGrass); \ - OPTION(m_biomeColors); \ - OPTION(m_dynamicHand); \ -diff --git a/source/client/renderer/LevelRenderer.cpp b/source/client/renderer/LevelRenderer.cpp -index e30b75164..ef388e89f 100644 ---- a/source/client/renderer/LevelRenderer.cpp -+++ b/source/client/renderer/LevelRenderer.cpp -@@ -1137,7 +1137,7 @@ void LevelRenderer::tick() - typedef std::vector ChunkVector; - typedef ChunkVector::iterator ChunkVectorIterator; - --bool LevelRenderer::updateDirtyChunks(const Entity& camera, bool b) -+bool LevelRenderer::updateDirtyChunks(const Entity& camera, bool force) - { - constexpr int C_MAX = 3; - DirtyChunkSorter dcs(camera); -@@ -1148,7 +1148,7 @@ bool LevelRenderer::updateDirtyChunks(const Entity& camera, bool b) - for (size_t i = 0; i < pendingChunkSize; i++) - { - Chunk* pChunk = m_dirtyChunks[i]; -- if (!b) -+ if (!force) - { - if (pChunk->distanceToSqr(camera) > 1024.0f) - { -diff --git a/source/client/renderer/TileRenderer.cpp b/source/client/renderer/TileRenderer.cpp -index ed07fc37b..026c42d68 100644 ---- a/source/client/renderer/TileRenderer.cpp -+++ b/source/client/renderer/TileRenderer.cpp -@@ -204,7 +204,7 @@ void TileRenderer::renderEast(Tile* tile, const Vec3& pos, int texture) - texV_d = C_RATIO * (texY + aabb.max.y * 16.0f - 0.01f); - } - -- Tesselator& t = Tesselator::instance; -+ Tesselator& t = m_tessellator; - - if (m_ambientOcclusion) - { -@@ -281,7 +281,7 @@ void TileRenderer::renderWest(Tile* tile, const Vec3& pos, int texture) - texV_d = C_RATIO * (texY + aabb.max.y * 16.0f - 0.01f); - } - -- Tesselator& t = Tesselator::instance; -+ Tesselator& t = m_tessellator; - - if (m_ambientOcclusion) - { -@@ -358,7 +358,7 @@ void TileRenderer::renderSouth(Tile* tile, const Vec3& pos, int texture) - texV_d = C_RATIO * (texY + aabb.max.y * 16.0f - 0.01f); - } - -- Tesselator& t = Tesselator::instance; -+ Tesselator& t = m_tessellator; - - if (m_ambientOcclusion) - { -@@ -435,7 +435,7 @@ void TileRenderer::renderNorth(Tile* tile, const Vec3& pos, int texture) - texV_d = C_RATIO * (texY + aabb.max.y * 16.0f - 0.01f); - } - -- Tesselator& t = Tesselator::instance; -+ Tesselator& t = m_tessellator; - - if (m_ambientOcclusion) - { -@@ -506,7 +506,7 @@ void TileRenderer::renderFaceDown(Tile* tile, const Vec3& pos, int texture) - texV_2 = C_RATIO * (texY + 15.99f); - } - -- Tesselator& t = Tesselator::instance; -+ Tesselator& t = m_tessellator; - - if (m_ambientOcclusion) - { -@@ -577,7 +577,7 @@ void TileRenderer::renderFaceUp(Tile* tile, const Vec3& pos, int texture) - texV_2 = C_RATIO * (texY + 15.99f); - } - -- Tesselator& t = Tesselator::instance; -+ Tesselator& t = m_tessellator; - - if (m_ambientOcclusion) - { -@@ -641,7 +641,7 @@ void TileRenderer::tesselateCrossTexture(const FullTile& tile, const Vec3& pos, - float x1 = cenX - 0.45f, x2 = cenX + 0.45f; - float z1 = cenZ - 0.45f, z2 = cenZ + 0.45f; - -- Tesselator& t = Tesselator::instance; -+ Tesselator& t = m_tessellator; - // face 1 - t.vertexUV(x1, newY + 1, z1, texU_l, texV_u); - t.vertexUV(x1, newY + 0, z1, texU_l, texV_d); -@@ -693,7 +693,7 @@ void TileRenderer::tesselateRowTexture(Tile* tile, int data, const Vec3& pos) - float x0 = pos.x + 0.25f, x1 = pos.x + 0.75f; - float z0 = pos.z, z1 = pos.z + 1.0f; - -- Tesselator& t = Tesselator::instance; -+ Tesselator& t = m_tessellator; - t.vertexUV(x0, pos.y + 1.0, z0, u0, v0); - t.vertexUV(x0, pos.y + 0.0, z0, u0, v1); - t.vertexUV(x0, pos.y + 0.0, z1, u1, v1); -@@ -739,7 +739,7 @@ bool TileRenderer::tesselateBlockInWorld(Tile* tile, const TilePos& pos, float r - if (tile == Tile::grass) - r = g = b = 1.0f; - -- Tesselator& t = Tesselator::instance; -+ Tesselator& t = m_tessellator; - - float fLightHere = tile->getBrightness(m_pTileSource, pos); - bool bDrewAnything = false; -@@ -871,7 +871,7 @@ bool TileRenderer::tesselateBlockInWorld(Tile* tile, const TilePos& pos) - - bool TileRenderer::tesselateCrossInWorld(Tile* tile, const TilePos& pos) - { -- Tesselator& t = Tesselator::instance; -+ Tesselator& t = m_tessellator; - - float bright = tile->getBrightness(m_pTileSource, pos); - int color = getTileColor(tile, pos); -@@ -888,7 +888,7 @@ bool TileRenderer::tesselateCrossInWorld(Tile* tile, const TilePos& pos) - - bool TileRenderer::tesselateRowInWorld(Tile* tile, const TilePos& pos) - { -- Tesselator& t = Tesselator::instance; -+ Tesselator& t = m_tessellator; - - Color color = getTileColor(tile, pos); - color.a = 1.0f; -@@ -906,7 +906,7 @@ bool TileRenderer::tesselateWaterInWorld(Tile* tile1, const TilePos& pos) - LiquidTile* tile = (LiquidTile*)tile1; - bool bRenderFaceDown, bRenderFaceUp, bRenderSides[4]; - -- Tesselator& t = Tesselator::instance; -+ Tesselator& t = m_tessellator; - - bRenderFaceDown = tile->shouldRenderFace(m_pTileSource, pos.above(), Facing::UP); - bRenderFaceUp = tile->shouldRenderFace(m_pTileSource, pos.below(), Facing::DOWN); -@@ -1217,7 +1217,7 @@ bool TileRenderer::tesselateFenceInWorld(Tile* tile, const TilePos& pos) - - bool TileRenderer::tesselateDoorInWorld(Tile* tile, const TilePos& pos) - { -- Tesselator& t = Tesselator::instance; -+ Tesselator& t = m_tessellator; - float fBrightHere = tile->getBrightness(m_pTileSource, pos), fBright; - int texture; - -@@ -1298,7 +1298,7 @@ void TileRenderer::tesselateTorch(Tile* tile, const Vec3& pos, float a, float b) - float x2 = x1 + (float)(a * C_TOP_SKEW_RATIO); - float z2 = z1 + (float)(b * C_TOP_SKEW_RATIO); - -- Tesselator& t = Tesselator::instance; -+ Tesselator& t = m_tessellator; - - // Top side (flame) - float x_1 = x2 - C_ONE_PIXEL; -@@ -1371,7 +1371,7 @@ bool TileRenderer::tesselateTorchInWorld(Tile* tile, const TilePos& pos) - if (Tile::lightEmission[tile->m_ID] > 0) - bright = 1.0f; - -- Tesselator& t = Tesselator::instance; -+ Tesselator& t = m_tessellator; - t.color(bright, bright, bright); - - switch (data) -@@ -1400,7 +1400,7 @@ bool TileRenderer::tesselateLadderInWorld(Tile* tile, const TilePos& pos) - { - constexpr float C_RATIO = 1.0f / 256.0f; - -- Tesselator& t = Tesselator::instance; -+ Tesselator& t = m_tessellator; - - int texture = tile->getTexture(Facing::DOWN); - -@@ -1453,7 +1453,7 @@ bool TileRenderer::tesselateFireInWorld(Tile* tile, const TilePos& pos) - { - constexpr float C_RATIO = 1.0f / 256.0f; - -- Tesselator& t = Tesselator::instance; -+ Tesselator& t = m_tessellator; - - int texture = tile->getTexture(Facing::DOWN); - float bright = tile->getBrightness(m_pTileSource, pos); -@@ -2691,7 +2691,7 @@ bool TileRenderer::tesselateBlockInWorldWithAmbienceOcclusion(Tile* a2, const Ti - - void TileRenderer::renderTile(const FullTile& tile, const mce::MaterialPtr& material, float bright, bool preshade) - { -- Tesselator& t = Tesselator::instance; -+ Tesselator& t = m_tessellator; - Tile* tileType = tile.getType(); - - #ifndef ENH_SHADE_HELD_TILES -@@ -2926,7 +2926,7 @@ bool TileRenderer::tesselateBlockInWorldWithAmbienceOcclusionV2(Tile* tile, cons - if (tile == Tile::grass) - r = g = b = 1.0f; - -- //Tesselator& t = Tesselator::instance; -+ //Tesselator& t = m_tessellator; - - //float fLightHere = tile->getBrightness(m_pTileSource, pos); - -diff --git a/source/world/tile/TopSnowTile.cpp b/source/world/tile/TopSnowTile.cpp -index b974b308b..9bec5bc70 100644 ---- a/source/world/tile/TopSnowTile.cpp -+++ b/source/world/tile/TopSnowTile.cpp -@@ -32,7 +32,7 @@ bool TopSnowTile::isSolidRender() const - - int TopSnowTile::getResource(TileData data, Random* random) const - { -- return 0; -+ return Item::snowBall->m_itemID; - } - - int TopSnowTile::getResourceCount(Random* random) const -@@ -84,3 +84,19 @@ void TopSnowTile::tick(Level* level, const TilePos& pos, Random* random) - level->setTile(pos, TILE_AIR); - } - } -+ -+void TopSnowTile::playerDestroy(Level* level, Player* player, const TilePos& pos, TileData data) -+{ -+ float dispersion = 0.7f; -+ -+ Vec3 offset ( -+ (level->m_random.nextFloat() * dispersion) + (1.0f - dispersion) * 0.5f, -+ (level->m_random.nextFloat() * dispersion) + (1.0f - dispersion) * 0.5f, -+ (level->m_random.nextFloat() * dispersion) + (1.0f - dispersion) * 0.5f -+ ); -+ -+ ItemEntity* pItemEntity = new ItemEntity(level, Vec3(pos) + offset, ItemStack(getResource(data, &level->m_random), 1, 0)); -+ pItemEntity->m_throwTime = 10; -+ level->addEntity(pItemEntity); -+ level->setTile(pos, TILE_AIR); -+} -diff --git a/source/world/tile/TopSnowTile.hpp b/source/world/tile/TopSnowTile.hpp -index 82af8e659..cafd89ea2 100644 ---- a/source/world/tile/TopSnowTile.hpp -+++ b/source/world/tile/TopSnowTile.hpp -@@ -24,6 +24,7 @@ class TopSnowTile : public Tile - void neighborChanged(Level*, const TilePos& pos, TileID tile) override; - bool shouldRenderFace(const LevelSource*, const TilePos& pos, Facing::Name face) const override; - void tick(Level*, const TilePos& pos, Random*) override; -+ virtual void playerDestroy(Level*, Player*, const TilePos& pos, TileData data); - - bool checkCanSurvive(Level*, const TilePos& pos); - }; - -From 837b7412c8900d3c6cb3c1151bfa81efb0a360d9 Mon Sep 17 00:00:00 2001 -From: hellosammu -Date: Tue, 17 Feb 2026 13:58:15 -0500 -Subject: [PATCH 09/22] drop sand - ---- - platforms/xdk360/AppPlatform_xdk360.cpp | 4 ++-- - source/client/options/Options.hpp | 2 +- - source/world/entity/FallingTile.cpp | 5 ++++- - 3 files changed, 7 insertions(+), 4 deletions(-) - -diff --git a/platforms/xdk360/AppPlatform_xdk360.cpp b/platforms/xdk360/AppPlatform_xdk360.cpp -index 33fea93f4..f9fe54f86 100644 ---- a/platforms/xdk360/AppPlatform_xdk360.cpp -+++ b/platforms/xdk360/AppPlatform_xdk360.cpp -@@ -321,8 +321,8 @@ const std::string& AppPlatform_xdk360::getKeyboardText() const - - void AppPlatform_xdk360::setVSyncEnabled(bool enabled) - { -- // XDK360 V-Sync control would go here -- // For Xbox 360, this is typically handled by the D3D device settings -+ // You have to reset/recreate the D3D device to change the vsync setting -+ // @TODO: Someone with a windows machine or xbox please do and test this. - } - - bool AppPlatform_xdk360::initGraphics(unsigned int width, unsigned int height) -diff --git a/source/client/options/Options.hpp b/source/client/options/Options.hpp -index 72cce0703..b15eb695b 100644 ---- a/source/client/options/Options.hpp -+++ b/source/client/options/Options.hpp -@@ -334,7 +334,7 @@ class UIThemeOption : public ValuesOption - class HUDSizeOption : public MinMaxOption - { - public: -- HUDSizeOption(const std::string& key, const std::string& name, int initial) : MinMaxOption(key, name, initial, HUD_SIZE_1, HUD_SIZE_MAX + 1) -+ HUDSizeOption(const std::string& key, const std::string& name, int initial) : MinMaxOption(key, name, initial, HUD_SIZE_1, HUD_SIZE_3 + 1) - { - } - -diff --git a/source/world/entity/FallingTile.cpp b/source/world/entity/FallingTile.cpp -index 8fb89b6a9..23c028573 100644 ---- a/source/world/entity/FallingTile.cpp -+++ b/source/world/entity/FallingTile.cpp -@@ -66,7 +66,10 @@ void FallingTile::tick() - if (!m_bOnGround) - { - if (field_E0 > 100 && !m_pLevel->m_bIsClientSide) -+ { -+ spawnAtLocation(m_id, 1); - remove(); -+ } - - return; - } -@@ -81,7 +84,7 @@ void FallingTile::tick() - } - else - { -- // @TODO: spawn resources? -+ spawnAtLocation(m_id, 1); - } - } - - -From da33efab2a2597efa36c6dd445b51f8c488d4e21 Mon Sep 17 00:00:00 2001 -From: hellosammu -Date: Tue, 17 Feb 2026 19:43:33 -0500 -Subject: [PATCH 10/22] containers shouldnt look stupid - ---- - platforms/android/AppPlatform_android.cpp | 5 +++++ - platforms/android/AppPlatform_android.hpp | 1 + - platforms/sdl/base/AppPlatform_sdl.cpp | 9 +++++++++ - platforms/sdl/base/AppPlatform_sdl.hpp | 1 + - platforms/windows/AppPlatform_win32.cpp | 9 +++++++++ - platforms/windows/AppPlatform_win32.hpp | 1 + - platforms/xdk360/AppPlatform_xdk360.cpp | 5 +++++ - platforms/xdk360/AppPlatform_xdk360.hpp | 1 + - source/client/app/AppPlatform.cpp | 5 +++++ - source/client/app/AppPlatform.hpp | 1 + - source/client/gui/screens/inventory/ChestScreen.cpp | 7 +++---- - source/client/options/Options.cpp | 5 ++++- - source/world/inventory/ChestMenu.cpp | 6 ++++-- - 13 files changed, 49 insertions(+), 7 deletions(-) - -diff --git a/platforms/android/AppPlatform_android.cpp b/platforms/android/AppPlatform_android.cpp -index a6cedc81b..a2aacf87c 100644 ---- a/platforms/android/AppPlatform_android.cpp -+++ b/platforms/android/AppPlatform_android.cpp -@@ -150,6 +150,11 @@ void AppPlatform_android::setVSyncEnabled(bool enabled) - glSwapInterval(display, enabled ? 1 : 0); - } - -+bool AppPlatform_android::isVsyncSwitchable() const -+{ -+ return eglGetCurrentDisplay() != EGL_NO_DISPLAY; -+} -+ - void AppPlatform_android::initAndroidApp(android_app* ptr) - { - m_app = ptr; -diff --git a/platforms/android/AppPlatform_android.hpp b/platforms/android/AppPlatform_android.hpp -index fecbbc46c..b18433f5c 100644 ---- a/platforms/android/AppPlatform_android.hpp -+++ b/platforms/android/AppPlatform_android.hpp -@@ -56,6 +56,7 @@ class AppPlatform_android : public AppPlatform - - AssetFile readAssetFile(const std::string&, bool) const override; - void setVSyncEnabled(bool enabled) override; -+ bool isVsyncSwitchable() const override; - - private: - void changeKeyboardVisibility(bool bShown); -diff --git a/platforms/sdl/base/AppPlatform_sdl.cpp b/platforms/sdl/base/AppPlatform_sdl.cpp -index aae613798..1d21c36c8 100644 ---- a/platforms/sdl/base/AppPlatform_sdl.cpp -+++ b/platforms/sdl/base/AppPlatform_sdl.cpp -@@ -156,6 +156,15 @@ void AppPlatform_sdl::setVSyncEnabled(bool enabled) - #endif - } - -+bool AppPlatform_sdl::isVsyncSwitchable() const -+{ -+#if MCE_GFX_API_OGL -+ return true; -+#else -+ return false; -+#endif -+} -+ - void AppPlatform_sdl::initSoundSystem() - { - if (m_pSoundSystem) -diff --git a/platforms/sdl/base/AppPlatform_sdl.hpp b/platforms/sdl/base/AppPlatform_sdl.hpp -index d72a97b5b..976668744 100644 ---- a/platforms/sdl/base/AppPlatform_sdl.hpp -+++ b/platforms/sdl/base/AppPlatform_sdl.hpp -@@ -39,6 +39,7 @@ class AppPlatform_sdl : public AppPlatform - void saveScreenshot(const std::string& fileName, int width, int height) override; - SoundSystem* getSoundSystem() const override { return m_pSoundSystem; } - void setVSyncEnabled(bool enabled) override; -+ bool isVsyncSwitchable() const override; - - // Also add these to allow proper turning within the game. - void setMouseGrabbed(bool b) override; -diff --git a/platforms/windows/AppPlatform_win32.cpp b/platforms/windows/AppPlatform_win32.cpp -index 674b5bf3b..b177fef9a 100644 ---- a/platforms/windows/AppPlatform_win32.cpp -+++ b/platforms/windows/AppPlatform_win32.cpp -@@ -491,6 +491,15 @@ void AppPlatform_win32::setVSyncEnabled(bool enabled) - #endif - } - -+bool AppPlatform_win32::isVsyncSwitchable() const -+{ -+#if MCE_GFX_API_OGL -+ return true; -+#else -+ return false; -+#endif -+} -+ - void AppPlatform_win32::createWindowSizeDependentResources(const Vec2& logicalSize, const Vec2& compositionScale) - { - #if MCE_GFX_API_D3D9 -diff --git a/platforms/windows/AppPlatform_win32.hpp b/platforms/windows/AppPlatform_win32.hpp -index d047243e9..bac0f7eda 100644 ---- a/platforms/windows/AppPlatform_win32.hpp -+++ b/platforms/windows/AppPlatform_win32.hpp -@@ -83,6 +83,7 @@ class AppPlatform_win32 : public AppPlatform - void createWindowSizeDependentResources(const Vec2& logicalSize, const Vec2& compositionScale); - void swapBuffers(); - void setVSyncEnabled(bool enabled) override; -+ bool isVsyncSwitchable() const override; - - static MouseButtonType GetMouseButtonType(UINT iMsg); - static bool GetMouseButtonState(UINT iMsg, WPARAM wParam); -diff --git a/platforms/xdk360/AppPlatform_xdk360.cpp b/platforms/xdk360/AppPlatform_xdk360.cpp -index f9fe54f86..8b4ec16a6 100644 ---- a/platforms/xdk360/AppPlatform_xdk360.cpp -+++ b/platforms/xdk360/AppPlatform_xdk360.cpp -@@ -325,6 +325,11 @@ void AppPlatform_xdk360::setVSyncEnabled(bool enabled) - // @TODO: Someone with a windows machine or xbox please do and test this. - } - -+bool AppPlatform_xdk360::isVsyncSwitchable() const -+{ -+ return false; -+} -+ - bool AppPlatform_xdk360::initGraphics(unsigned int width, unsigned int height) - { - m_bHasGraphics = true; -diff --git a/platforms/xdk360/AppPlatform_xdk360.hpp b/platforms/xdk360/AppPlatform_xdk360.hpp -index d2934aa71..d1fb3b859 100644 ---- a/platforms/xdk360/AppPlatform_xdk360.hpp -+++ b/platforms/xdk360/AppPlatform_xdk360.hpp -@@ -53,6 +53,7 @@ class AppPlatform_xdk360 : public AppPlatform - void createWindowSizeDependentResources(unsigned int width, unsigned int height); - void swapBuffers(); - void setVSyncEnabled(bool enabled) override; -+ bool isVsyncSwitchable() const override; - - private: - int m_ScreenWidth; -diff --git a/source/client/app/AppPlatform.cpp b/source/client/app/AppPlatform.cpp -index 74713c1ca..7eba59212 100644 ---- a/source/client/app/AppPlatform.cpp -+++ b/source/client/app/AppPlatform.cpp -@@ -324,6 +324,11 @@ void AppPlatform::setVSyncEnabled(bool enabled) - { - } - -+bool AppPlatform::isVsyncSwitchable() const -+{ -+ return false; -+} -+ - bool AppPlatform::hasAssetFile(const std::string& path) const - { - return isRegularFile(path.c_str()); -diff --git a/source/client/app/AppPlatform.hpp b/source/client/app/AppPlatform.hpp -index 26d74bbd6..b7852bd21 100644 ---- a/source/client/app/AppPlatform.hpp -+++ b/source/client/app/AppPlatform.hpp -@@ -109,6 +109,7 @@ class AppPlatform - virtual std::string getClipboardText(); - // Graphics settings - virtual void setVSyncEnabled(bool enabled); -+ virtual bool isVsyncSwitchable() const; - - void _fireLowMemory(); - void _fireAppSuspended(); -diff --git a/source/client/gui/screens/inventory/ChestScreen.cpp b/source/client/gui/screens/inventory/ChestScreen.cpp -index 312618638..2df77fff9 100644 ---- a/source/client/gui/screens/inventory/ChestScreen.cpp -+++ b/source/client/gui/screens/inventory/ChestScreen.cpp -@@ -31,15 +31,14 @@ SlotDisplay ChestScreen::_createSlotDisplay(const Slot& slot) - { - constexpr int slotSize = 18; - int rows = m_pContainer->getContainerSize() / 9; -- int verticalOffset = (rows - 4) * slotSize; - switch (slot.m_group) - { - case Slot::CONTAINER: -- return SlotDisplay(8 + (slot.m_slot % 9) * slotSize, 18 + verticalOffset + ((slot.m_slot / 9) - 1) * slotSize, slotSize); -+ return SlotDisplay(8 + (slot.m_slot % 9) * slotSize, 18 + (slot.m_slot / 9) * slotSize, slotSize); - case Slot::INVENTORY: -- return SlotDisplay(8 + (slot.m_slot % 9) * slotSize, 103 + verticalOffset + ((slot.m_slot / 9) - 1) * slotSize, slotSize); -+ return SlotDisplay(8 + (slot.m_slot % 9) * slotSize, (rows * 18) + 13 + (slot.m_slot / 9) * slotSize, slotSize); - case Slot::HOTBAR: -- return SlotDisplay(8 + (slot.m_slot % 9) * slotSize, 142, slotSize); -+ return SlotDisplay(8 + (slot.m_slot % 9) * slotSize, 89 + rows * slotSize, slotSize); - default: - return SlotDisplay(); - } -diff --git a/source/client/options/Options.cpp b/source/client/options/Options.cpp -index c6d24cb34..f99e36c2f 100644 ---- a/source/client/options/Options.cpp -+++ b/source/client/options/Options.cpp -@@ -98,7 +98,7 @@ Options::Options(Minecraft* mc, const std::string& folderPath) : - //, m_limitFramerate("gfx_fpslimit", "options.framerateLimit", 0, ValuesBuilder().add(performance.max").add("performance.balanced").add("performance.powersaver")) - //, m_bMipmaps("gfx_mipmaps", "options.mipmaps") - //, m_moreWorldOptions("misc_moreworldoptions", "options.moreWorldOptions", true) -- , m_vSync("enableVsync", "options.enableVsync") -+ , m_vSync("enableVsync", "options.enableVsync", true) - { - add(m_musicVolume); - add(m_masterVolume); -@@ -676,6 +676,9 @@ void Options::initResourceDependentOptions() - - if (!Screen::isMenuPanoramaAvailable()) - m_menuPanorama.set(false); -+ -+ if (!m_pMinecraft->platform()->isVsyncSwitchable()) -+ m_vSync.set(false); - } - - const std::string& OptionEntry::getDisplayName() const -diff --git a/source/world/inventory/ChestMenu.cpp b/source/world/inventory/ChestMenu.cpp -index 95a78894f..7766dd63c 100644 ---- a/source/world/inventory/ChestMenu.cpp -+++ b/source/world/inventory/ChestMenu.cpp -@@ -7,16 +7,18 @@ ChestMenu::ChestMenu(Container* inventory, Container* container) - { - int rows = m_pContainer->getContainerSize() / 9; - -+ // Chest slots - for (int row = 0; row < rows; ++row) - { - for (int col = 0; col < 9; ++col) -- addSlot(new Slot(m_pContainer, col + row * 9)); -+ addSlot(new Slot(m_pContainer, col + row * 9, Slot::CONTAINER)); - } - -+ // Inventory slots - for (int row = 0; row < 3; ++row) - { - for (int col = 0; col < 9; ++col) -- addSlot(new Slot(inventory, col + row * 9 + 9)); -+ addSlot(new Slot(inventory, col + row * 9 + 9, Slot::INVENTORY)); - } - - for (int col = 0; col < 9; ++col) - -From fa895e26b5439b840badc5477853909e003ee321 Mon Sep 17 00:00:00 2001 -From: hellosammu -Date: Tue, 17 Feb 2026 20:15:46 -0500 -Subject: [PATCH 11/22] naming conventions, not done yet - ---- - source/client/renderer/Chunk.cpp | 30 ++++++++++----------- - source/client/renderer/Chunk.hpp | 6 ++--- - source/client/renderer/LevelRenderer.cpp | 2 +- - source/client/renderer/LevelRenderer.hpp | 2 +- - source/world/entity/FallingTile.cpp | 4 +-- - source/world/tile/entity/TileEntityType.cpp | 12 ++++----- - source/world/tile/entity/TileEntityType.hpp | 10 +++---- - 7 files changed, 33 insertions(+), 33 deletions(-) - -diff --git a/source/client/renderer/Chunk.cpp b/source/client/renderer/Chunk.cpp -index ec7d6db5e..aed0ab5c7 100644 ---- a/source/client/renderer/Chunk.cpp -+++ b/source/client/renderer/Chunk.cpp -@@ -133,8 +133,8 @@ void Chunk::rebuild() - - LevelChunk::touchedSky = false; - -- std::set tmpSet(m_renderableTileEntities.begin(), m_renderableTileEntities.end()); -- m_renderableTileEntities.clear(); -+ std::set tmpSet(m_tileEntities.begin(), m_tileEntities.end()); -+ m_tileEntities.clear(); - - for (int i = Tile::RENDER_LAYERS_MIN; i <= Tile::RENDER_LAYERS_MAX; i++) - { -@@ -178,7 +178,7 @@ void Chunk::rebuild() - // @TODO: ADD TILE ENTITY RENDER DISPATCHER - TileEntity* et = region.getTileEntity(tp); - if (TileEntityRenderDispatcher::getInstance()->hasRenderer(et)) -- m_renderableTileEntities.push_back(et); -+ m_tileEntities.push_back(et); - */ - } - -@@ -214,8 +214,8 @@ void Chunk::rebuild() - break; - } - -- std::set newSet(m_renderableTileEntities.begin(), m_renderableTileEntities.end()); -- std::vector toAdd, toRemove; -+ std::set newSet(m_tileEntities.begin(), m_tileEntities.end()); -+ TileEntityVector toAdd, toRemove; - - std::set_difference( - newSet.begin(), newSet.end(), -@@ -230,29 +230,29 @@ void Chunk::rebuild() - ); - - // Add -- for (std::vector::iterator it = toAdd.begin(); it != toAdd.end(); ++it) -+ for (TileEntityVector::iterator it = toAdd.begin(); it != toAdd.end(); ++it) - { -- m_globalRenderableTileEntities.push_back(*it); -+ m_globalTileEntities.push_back(*it); - } - - // Remove -- for (std::vector::iterator it = toRemove.begin(); it != toRemove.end(); ++it) -+ for (TileEntityVector::iterator it = toRemove.begin(); it != toRemove.end(); ++it) - { -- std::vector::iterator f = -- std::find(m_globalRenderableTileEntities.begin(), -- m_globalRenderableTileEntities.end(), -+ TileEntityVector::iterator f = -+ std::find(m_globalTileEntities.begin(), -+ m_globalTileEntities.end(), - *it); - -- if (f != m_globalRenderableTileEntities.end()) -- m_globalRenderableTileEntities.erase(f); -+ if (f != m_globalTileEntities.end()) -+ m_globalTileEntities.erase(f); - } - - field_54 = LevelChunk::touchedSky; - m_bCompiled = true; - } - --Chunk::Chunk(Level* level, std::vector& renderableTileEntities, const TilePos& pos, int size, int lists) -- : m_globalRenderableTileEntities(renderableTileEntities) -+Chunk::Chunk(Level* level, TileEntityVector& tileEntities, const TilePos& pos, int size, int lists) -+ : m_globalTileEntities(tileEntities) - { - m_bOcclusionVisible = true; - m_bOcclusionQuerying = false; -diff --git a/source/client/renderer/Chunk.hpp b/source/client/renderer/Chunk.hpp -index aea9643c1..58d6d4793 100644 ---- a/source/client/renderer/Chunk.hpp -+++ b/source/client/renderer/Chunk.hpp -@@ -20,7 +20,7 @@ class TileEntity; - class Chunk - { - public: -- Chunk(Level*, std::vector& renderableTileEntities, const TilePos& pos, int, int); -+ Chunk(Level*, std::vector& tileEntities, const TilePos& pos, int, int); - - public: - float distanceToSqr(const Entity& entity) const; -@@ -43,8 +43,8 @@ class Chunk - - public: - Level* m_pLevel; -- std::vector& m_globalRenderableTileEntities; -- std::vector m_renderableTileEntities; -+ std::vector& m_globalTileEntities; -+ std::vector m_tileEntities; - TilePos m_pos; - TilePos m_posS; - bool m_empty[Tile::RENDER_LAYERS_COUNT]; -diff --git a/source/client/renderer/LevelRenderer.cpp b/source/client/renderer/LevelRenderer.cpp -index ef388e89f..a8dc66049 100644 ---- a/source/client/renderer/LevelRenderer.cpp -+++ b/source/client/renderer/LevelRenderer.cpp -@@ -1608,7 +1608,7 @@ void LevelRenderer::renderEntities(Vec3 pos, Culler* culler, float f) - - /* - // @TODO: TileEntityRenderDispatcher -- for (std::vector::const_iterator it = m_renderableTileEntities.begin(); -+ for (TileEntityVector::const_iterator it = m_renderableTileEntities.begin(); - it != m_renderableTileEntities.end(); ++it) - { - TileEntity* tileEntity = *it; -diff --git a/source/client/renderer/LevelRenderer.hpp b/source/client/renderer/LevelRenderer.hpp -index 18947ff8c..63520dc24 100644 ---- a/source/client/renderer/LevelRenderer.hpp -+++ b/source/client/renderer/LevelRenderer.hpp -@@ -223,5 +223,5 @@ class LevelRenderer : public LevelListener, public AppPlatformListener - mce::Mesh m_darkMesh; - //... - Textures* m_pTextures; -- std::vector m_renderableTileEntities; -+ TileEntityVector m_renderableTileEntities; - }; -diff --git a/source/world/entity/FallingTile.cpp b/source/world/entity/FallingTile.cpp -index 23c028573..aa249e52d 100644 ---- a/source/world/entity/FallingTile.cpp -+++ b/source/world/entity/FallingTile.cpp -@@ -80,11 +80,11 @@ void FallingTile::tick() - remove(); - if (m_pLevel->mayPlace(m_id, tilePos, true)) - { -- m_pLevel->setTile(tilePos, m_id); -+ m_pLevel->setTile(tilePos, static_cast(m_id)); - } - else - { -- spawnAtLocation(m_id, 1); -+ spawnAtLocation(static_cast(m_id), 1); - } - } - -diff --git a/source/world/tile/entity/TileEntityType.cpp b/source/world/tile/entity/TileEntityType.cpp -index 1391e41ac..18589f526 100644 ---- a/source/world/tile/entity/TileEntityType.cpp -+++ b/source/world/tile/entity/TileEntityType.cpp -@@ -14,14 +14,14 @@ TileEntityType* TileEntityType::noteblock; - - std::map TileEntityFactory::_types; - --void TileEntityFactory::InitTileEntities() -+void TileEntityFactory::initTileEntities() - { -- TileEntityType::furnace = RegisterTileEntity("Furnace"); -- TileEntityType::chest = RegisterTileEntity("Chest"); -- TileEntityType::noteblock = RegisterTileEntity("Music"); -+ TileEntityType::furnace = registerTileEntity("Furnace"); -+ TileEntityType::chest = registerTileEntity("Chest"); -+ TileEntityType::noteblock = registerTileEntity("Music"); - } - --void TileEntityFactory::TeardownTileEntities() -+void TileEntityFactory::teardownTileEntities() - { - // delete all heap allocated tile entity types (furnace, chest, etc.) - for (std::map::const_iterator it = _types.begin(); it != _types.end(); ++it) -@@ -31,7 +31,7 @@ void TileEntityFactory::TeardownTileEntities() - _types.clear(); - } - --const TileEntityType* TileEntityFactory::GetType(const std::string& name) -+const TileEntityType* TileEntityFactory::getType(const std::string& name) - { - return _types[name]; - } -diff --git a/source/world/tile/entity/TileEntityType.hpp b/source/world/tile/entity/TileEntityType.hpp -index 0baa6b857..d1ad2b6ff 100644 ---- a/source/world/tile/entity/TileEntityType.hpp -+++ b/source/world/tile/entity/TileEntityType.hpp -@@ -14,13 +14,13 @@ class TileEntityFactory - static std::map _types; - - public: -- static void InitTileEntities(); -- static void TeardownTileEntities(); -- static const TileEntityType* GetType(const std::string& name); -+ static void initTileEntities(); -+ static void teardownTileEntities(); -+ static const TileEntityType* getType(const std::string& name); - - public: - template -- static TileEntityType* RegisterTileEntity(const std::string& name); -+ static TileEntityType* registerTileEntity(const std::string& name); - }; - - class TileEntityType -@@ -50,7 +50,7 @@ class TileEntityType - }; - - template --TileEntityType* TileEntityFactory::RegisterTileEntity(const std::string& name) -+TileEntityType* TileEntityFactory::registerTileEntity(const std::string& name) - { - TileEntityType* type = new TileEntityType(name, &TileEntityType::CreateType); - _types[type->_name] = type; - -From 732374d48ab2f66399efb8839019ec531052fff8 Mon Sep 17 00:00:00 2001 -From: hellosammu -Date: Tue, 17 Feb 2026 20:17:21 -0500 -Subject: [PATCH 12/22] wrong way of doing this but i have to merge - ---- - source/world/entity/FallingTile.cpp | 4 ++-- - 1 file changed, 2 insertions(+), 2 deletions(-) - -diff --git a/source/world/entity/FallingTile.cpp b/source/world/entity/FallingTile.cpp -index aa249e52d..dd9edfdc8 100644 ---- a/source/world/entity/FallingTile.cpp -+++ b/source/world/entity/FallingTile.cpp -@@ -80,11 +80,11 @@ void FallingTile::tick() - remove(); - if (m_pLevel->mayPlace(m_id, tilePos, true)) - { -- m_pLevel->setTile(tilePos, static_cast(m_id)); -+ m_pLevel->setTile(tilePos, getTile()); - } - else - { -- spawnAtLocation(static_cast(m_id), 1); -+ spawnAtLocation(getTile()); - } - } - - -From 67a9b26d4ca7a09d6e14013cd7ad61aaa093ebc8 Mon Sep 17 00:00:00 2001 -From: hellosammu -Date: Tue, 17 Feb 2026 20:19:38 -0500 -Subject: [PATCH 13/22] syntax bitch - ---- - source/client/app/NinecraftApp.cpp | 4 ++-- - source/world/entity/FallingTile.cpp | 4 ++-- - source/world/tile/entity/TileEntity.cpp | 2 +- - 3 files changed, 5 insertions(+), 5 deletions(-) - -diff --git a/source/client/app/NinecraftApp.cpp b/source/client/app/NinecraftApp.cpp -index 1b336d72c..4994bbfe2 100644 ---- a/source/client/app/NinecraftApp.cpp -+++ b/source/client/app/NinecraftApp.cpp -@@ -180,7 +180,7 @@ void NinecraftApp::_initAll() - EntityTypeDescriptor::initDescriptors(); // custom - MobCategory::initMobCategories(); - MobFactory::initMobLists(); -- TileEntityFactory::InitTileEntities(); -+ TileEntityFactory::initTileEntities(); - Tile::initTiles(); - Item::initItems(); - Biome::initBiomes(); -@@ -341,7 +341,7 @@ void NinecraftApp::onGraphicsReset() - - void NinecraftApp::teardown() - { -- TileEntityFactory::TeardownTileEntities(); -+ TileEntityFactory::teardownTileEntities(); - teardownRenderer(); - Resource::teardownLoaders(); - // Stop our SoundSystem before we nuke our sound buffers and cause it to implode -diff --git a/source/world/entity/FallingTile.cpp b/source/world/entity/FallingTile.cpp -index 5b23d84ac..838b63a0a 100644 ---- a/source/world/entity/FallingTile.cpp -+++ b/source/world/entity/FallingTile.cpp -@@ -79,7 +79,7 @@ void FallingTile::tick() - { - if (field_E0 > 100 && !m_pLevel->m_bIsClientSide) - { -- spawnAtLocation(m_id, 1); -+ spawnAtLocation(getTile(), 1); - remove(); - } - -@@ -96,7 +96,7 @@ void FallingTile::tick() - } - else - { -- spawnAtLocation(getTile()); -+ spawnAtLocation(getTile(), 1); - } - } - -diff --git a/source/world/tile/entity/TileEntity.cpp b/source/world/tile/entity/TileEntity.cpp -index a5b11be86..8cf5078c1 100644 ---- a/source/world/tile/entity/TileEntity.cpp -+++ b/source/world/tile/entity/TileEntity.cpp -@@ -19,7 +19,7 @@ TileEntity* TileEntity::LoadTileEntity(const CompoundTag& tag) - } - - std::string id = tag.getString("id"); -- const TileEntityType* type = TileEntityFactory::GetType(id); -+ const TileEntityType* type = TileEntityFactory::getType(id); - - if (!type) - { - -From 7506a6cf273ac381f307509974696229ce39b3cc Mon Sep 17 00:00:00 2001 -From: hellosammu -Date: Tue, 17 Feb 2026 20:24:46 -0500 -Subject: [PATCH 14/22] sorry SDL1 no vsync yet - ---- - platforms/sdl/base/AppPlatform_sdl.cpp | 16 ---------------- - platforms/sdl/base/AppPlatform_sdl.hpp | 2 -- - 2 files changed, 18 deletions(-) - -diff --git a/platforms/sdl/base/AppPlatform_sdl.cpp b/platforms/sdl/base/AppPlatform_sdl.cpp -index 1d21c36c8..30de0ac72 100644 ---- a/platforms/sdl/base/AppPlatform_sdl.cpp -+++ b/platforms/sdl/base/AppPlatform_sdl.cpp -@@ -149,22 +149,6 @@ void AppPlatform_sdl::_setDefaultIcon() - _setIcon(data); - } - --void AppPlatform_sdl::setVSyncEnabled(bool enabled) --{ --#if MCE_GFX_API_OGL -- SDL_GL_SetSwapInterval(enabled ? 1 : 0); --#endif --} -- --bool AppPlatform_sdl::isVsyncSwitchable() const --{ --#if MCE_GFX_API_OGL -- return true; --#else -- return false; --#endif --} -- - void AppPlatform_sdl::initSoundSystem() - { - if (m_pSoundSystem) -diff --git a/platforms/sdl/base/AppPlatform_sdl.hpp b/platforms/sdl/base/AppPlatform_sdl.hpp -index 976668744..c93d4f4b2 100644 ---- a/platforms/sdl/base/AppPlatform_sdl.hpp -+++ b/platforms/sdl/base/AppPlatform_sdl.hpp -@@ -38,8 +38,6 @@ class AppPlatform_sdl : public AppPlatform - int getUserInputStatus() override; - void saveScreenshot(const std::string& fileName, int width, int height) override; - SoundSystem* getSoundSystem() const override { return m_pSoundSystem; } -- void setVSyncEnabled(bool enabled) override; -- bool isVsyncSwitchable() const override; - - // Also add these to allow proper turning within the game. - void setMouseGrabbed(bool b) override; - -From b39ae301d75c329e3949a1d521860206d3d67ea3 Mon Sep 17 00:00:00 2001 -From: hellosammu -Date: Tue, 17 Feb 2026 20:26:19 -0500 -Subject: [PATCH 15/22] android - ---- - platforms/android/AppPlatform_android.cpp | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/platforms/android/AppPlatform_android.cpp b/platforms/android/AppPlatform_android.cpp -index a2aacf87c..f3547ef58 100644 ---- a/platforms/android/AppPlatform_android.cpp -+++ b/platforms/android/AppPlatform_android.cpp -@@ -147,7 +147,7 @@ void AppPlatform_android::setVSyncEnabled(bool enabled) - if (display == EGL_NO_DISPLAY) - return; - -- glSwapInterval(display, enabled ? 1 : 0); -+ eglSwapInterval(display, enabled ? 1 : 0); - } - - bool AppPlatform_android::isVsyncSwitchable() const - -From 4d117c3db6398f09f1835674dac8e64cfdfa03f6 Mon Sep 17 00:00:00 2001 -From: Brent Da Mage -Date: Tue, 17 Feb 2026 19:30:35 -0600 -Subject: [PATCH 16/22] Updated Visual Studio Projects - ---- - .../windows/projects/Client/Client.vcxproj | 2 + - .../projects/Client/Client.vcxproj.filters | 6 ++ - .../windows/projects/World/World.vcxproj | 26 ++++++ - .../projects/World/World.vcxproj.filters | 93 +++++++++++++++++-- - source/server/ServerPlayer.cpp | 10 ++ - 5 files changed, 128 insertions(+), 9 deletions(-) - -diff --git a/platforms/windows/projects/Client/Client.vcxproj b/platforms/windows/projects/Client/Client.vcxproj -index c046d534d..8da4fa551 100644 ---- a/platforms/windows/projects/Client/Client.vcxproj -+++ b/platforms/windows/projects/Client/Client.vcxproj -@@ -258,6 +258,7 @@ - - - -+ - - - -@@ -434,6 +435,7 @@ - - - -+ - - - -diff --git a/platforms/windows/projects/Client/Client.vcxproj.filters b/platforms/windows/projects/Client/Client.vcxproj.filters -index 55cd0f467..35b451268 100644 ---- a/platforms/windows/projects/Client/Client.vcxproj.filters -+++ b/platforms/windows/projects/Client/Client.vcxproj.filters -@@ -677,6 +677,9 @@ - - Header Files\GUI\Screens\Inventory - -+ -+ Header Files\GUI\Screens\Inventory -+ - - - -@@ -1204,5 +1207,8 @@ - - Source Files\GUI\Screens\Inventory - -+ -+ Source Files\GUI\Screens\Inventory -+ - - -\ No newline at end of file -diff --git a/platforms/windows/projects/World/World.vcxproj b/platforms/windows/projects/World/World.vcxproj -index 8a8312eca..7247155f5 100644 ---- a/platforms/windows/projects/World/World.vcxproj -+++ b/platforms/windows/projects/World/World.vcxproj -@@ -228,6 +228,20 @@ - - - -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ - - - -@@ -397,6 +411,18 @@ - - - -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ - - - -diff --git a/platforms/windows/projects/World/World.vcxproj.filters b/platforms/windows/projects/World/World.vcxproj.filters -index 18a19e701..6c9bbd804 100644 ---- a/platforms/windows/projects/World/World.vcxproj.filters -+++ b/platforms/windows/projects/World/World.vcxproj.filters -@@ -105,6 +105,12 @@ - - {3a97a5e5-77e6-43eb-9890-3fa79b287cd1} - -+ -+ {53363441-c079-4adf-a246-481be4b68199} -+ -+ -+ {9dca9e2a-e126-4598-a852-105be172d198} -+ - - - -@@ -659,6 +665,48 @@ - - Source Files\Item - -+ -+ Source Files\Inventory -+ -+ -+ Source Files\Inventory -+ -+ -+ Source Files\Item -+ -+ -+ Source Files\Particle -+ -+ -+ Source Files -+ -+ -+ Source Files\Tile -+ -+ -+ Source Files\Tile -+ -+ -+ Source Files\Tile -+ -+ -+ Source Files\Tile -+ -+ -+ Source Files\Tile\Entity -+ -+ -+ Source Files\Tile\Entity -+ -+ -+ Source Files\Tile\Entity -+ -+ -+ Source Files\Tile\Entity -+ -+ -+ Source Files\Tile\Entity -+ - - - -@@ -1126,15 +1174,6 @@ - - Header Files\Item - -- -- Header Files\Item -- -- -- Header Files\Item -- -- -- Header Files\Item -- - - Header Files\Item - -@@ -1171,5 +1210,41 @@ - - Header Files\Item - -+ -+ Header Files\Inventory -+ -+ -+ Header Files\Inventory -+ -+ -+ Header Files\Item -+ -+ -+ Header Files\Tile\Entity -+ -+ -+ Header Files\Tile\Entity -+ -+ -+ Header Files\Tile\Entity -+ -+ -+ Header Files\Tile\Entity -+ -+ -+ Header Files\Tile\Entity -+ -+ -+ Header Files\Tile -+ -+ -+ Header Files\Tile -+ -+ -+ Header Files\Tile -+ -+ -+ Header Files\Tile -+ - - -\ No newline at end of file -diff --git a/source/server/ServerPlayer.cpp b/source/server/ServerPlayer.cpp -index 21ed28339..8a0aad136 100644 ---- a/source/server/ServerPlayer.cpp -+++ b/source/server/ServerPlayer.cpp -@@ -1,4 +1,5 @@ - #include "ServerPlayer.hpp" -+#include "common/Logger.hpp" - #include "network/packets/SetHealthPacket.hpp" - #include "network/packets/TakeItemEntityPacket.hpp" - #include "network/packets/SendInventoryPacket.hpp" -@@ -55,6 +56,8 @@ void ServerPlayer::startCrafting(const TilePos& pos) - - void ServerPlayer::openContainer(Container* container) - { -+ LOG_I("Client is opening a container"); -+ - _nextContainerCounter(); - - #if NETWORK_PROTOCOL_VERSION >= 5 -@@ -71,6 +74,8 @@ void ServerPlayer::openContainer(Container* container) - - void ServerPlayer::closeContainer() - { -+ LOG_I("Client is closing a container"); -+ - #if NETWORK_PROTOCOL_VERSION >= 5 - m_pLevel->m_pRakNetInstance->send(new ContainerClosePacket(m_pContainerMenu->m_containerId)); - #endif -@@ -80,6 +85,8 @@ void ServerPlayer::closeContainer() - - void ServerPlayer::openFurnace(FurnaceTileEntity* furnace) - { -+ LOG_I("Client is opening a furnace"); -+ - _nextContainerCounter(); - - #if NETWORK_PROTOCOL_VERSION >= 5 -@@ -131,6 +138,9 @@ void ServerPlayer::doCloseContainer() - { - if (m_pContainerMenu) - m_pContainerMenu->removed(this); -+ else -+ LOG_W("Container is missing @ doCloseContainer!"); -+ - setContainerMenu(nullptr); // m_pInventoryMenu on Java, nullptr on Pocket - } - - -From d2bdb19429320f21a2ac526564b4a7d244d01a87 Mon Sep 17 00:00:00 2001 -From: Sam -Date: Tue, 17 Feb 2026 20:47:38 -0500 -Subject: [PATCH 17/22] tim apple - ---- - .../Minecraft.xcodeproj/project.pbxproj | 220 ++++++++++++++---- - 1 file changed, 170 insertions(+), 50 deletions(-) - -diff --git a/platforms/macos/projects/Minecraft/Minecraft.xcodeproj/project.pbxproj b/platforms/macos/projects/Minecraft/Minecraft.xcodeproj/project.pbxproj -index 7a625e3e8..dcc3c8727 100644 ---- a/platforms/macos/projects/Minecraft/Minecraft.xcodeproj/project.pbxproj -+++ b/platforms/macos/projects/Minecraft/Minecraft.xcodeproj/project.pbxproj -@@ -194,6 +194,34 @@ - 8426107D2AE989730065905F /* UpdateBlockPacket.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 840DD64C2AC810620006A435 /* UpdateBlockPacket.cpp */; }; - 8426107E2AE989730065905F /* RakNetInstance.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 840DD64E2AC810620006A435 /* RakNetInstance.cpp */; }; - 842610882AE98A4C0065905F /* libRakNet.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 84FFBD7E2ACA2876005A8CCF /* libRakNet.a */; }; -+ 84358AFE2F4550B30077EA44 /* TileEntityType.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84358AFC2F4550B30077EA44 /* TileEntityType.cpp */; }; -+ 84358AFF2F4550B30077EA44 /* ChestTileEntity.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84358AF42F4550B30077EA44 /* ChestTileEntity.cpp */; }; -+ 84358B002F4550B30077EA44 /* FurnaceTileEntity.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84358AF62F4550B30077EA44 /* FurnaceTileEntity.cpp */; }; -+ 84358B012F4550B30077EA44 /* TileEntity.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84358AFA2F4550B30077EA44 /* TileEntity.cpp */; }; -+ 84358B022F4550B30077EA44 /* MusicTileEntity.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84358AF82F4550B30077EA44 /* MusicTileEntity.cpp */; }; -+ 84358B032F4550B30077EA44 /* TileEntity.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84358AF92F4550B30077EA44 /* TileEntity.hpp */; }; -+ 84358B042F4550B30077EA44 /* FurnaceTileEntity.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84358AF52F4550B30077EA44 /* FurnaceTileEntity.hpp */; }; -+ 84358B052F4550B30077EA44 /* ChestTileEntity.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84358AF32F4550B30077EA44 /* ChestTileEntity.hpp */; }; -+ 84358B062F4550B30077EA44 /* MusicTileEntity.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84358AF72F4550B30077EA44 /* MusicTileEntity.hpp */; }; -+ 84358B072F4550B30077EA44 /* TileEntityType.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84358AFB2F4550B30077EA44 /* TileEntityType.hpp */; }; -+ 84358B102F4550CC0077EA44 /* FurnaceTile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84358B0D2F4550CC0077EA44 /* FurnaceTile.cpp */; }; -+ 84358B112F4550CC0077EA44 /* EntityTile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84358B0B2F4550CC0077EA44 /* EntityTile.cpp */; }; -+ 84358B122F4550CC0077EA44 /* ChestTile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84358B092F4550CC0077EA44 /* ChestTile.cpp */; }; -+ 84358B132F4550CC0077EA44 /* MusicTile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84358B0F2F4550CC0077EA44 /* MusicTile.cpp */; }; -+ 84358B142F4550CC0077EA44 /* ChestTile.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84358B082F4550CC0077EA44 /* ChestTile.hpp */; }; -+ 84358B152F4550CC0077EA44 /* FurnaceTile.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84358B0C2F4550CC0077EA44 /* FurnaceTile.hpp */; }; -+ 84358B162F4550CC0077EA44 /* EntityTile.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84358B0A2F4550CC0077EA44 /* EntityTile.hpp */; }; -+ 84358B172F4550CC0077EA44 /* MusicTile.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84358B0E2F4550CC0077EA44 /* MusicTile.hpp */; }; -+ 84358B192F45511C0077EA44 /* NoteParticle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84358B182F45511C0077EA44 /* NoteParticle.cpp */; }; -+ 84358B1B2F4551290077EA44 /* Facing.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84358B1A2F4551290077EA44 /* Facing.cpp */; }; -+ 84358B1E2F4551500077EA44 /* CoalItem.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84358B1D2F4551500077EA44 /* CoalItem.cpp */; }; -+ 84358B1F2F4551500077EA44 /* CoalItem.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84358B1C2F4551500077EA44 /* CoalItem.hpp */; }; -+ 84358B242F4551730077EA44 /* FurnaceResultSlot.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84358B232F4551730077EA44 /* FurnaceResultSlot.cpp */; }; -+ 84358B252F4551730077EA44 /* FurnaceMenu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84358B212F4551730077EA44 /* FurnaceMenu.cpp */; }; -+ 84358B262F4551730077EA44 /* FurnaceMenu.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84358B202F4551730077EA44 /* FurnaceMenu.hpp */; }; -+ 84358B272F4551730077EA44 /* FurnaceResultSlot.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84358B222F4551730077EA44 /* FurnaceResultSlot.hpp */; }; -+ 84358B2A2F45519B0077EA44 /* FurnaceScreen.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84358B282F45519B0077EA44 /* FurnaceScreen.hpp */; }; -+ 84358B2B2F45519B0077EA44 /* FurnaceScreen.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84358B292F45519B0077EA44 /* FurnaceScreen.cpp */; }; - 8435BB192DCD47F400D38282 /* SoundStreamOAL.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8435BB182DCD47F400D38282 /* SoundStreamOAL.cpp */; }; - 8435BB1A2DCD47F400D38282 /* SoundStreamOAL.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8435BB182DCD47F400D38282 /* SoundStreamOAL.cpp */; }; - 8441F98F2DB4E911005977BD /* SoundSystemOAL.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8441F9862DB4E911005977BD /* SoundSystemOAL.cpp */; }; -@@ -2234,6 +2262,34 @@ - 84336BA52B1EB57E00097DB0 /* Settings_iOS_Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Settings_iOS_Debug.xcconfig; path = ../Configuration/Settings_iOS_Debug.xcconfig; sourceTree = ""; }; - 84336BA82B1EB88500097DB0 /* Settings_macOS.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Settings_macOS.xcconfig; path = ../Configuration/Settings_macOS.xcconfig; sourceTree = ""; }; - 84336BA92B1EB9C200097DB0 /* Settings_macOS_Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Settings_macOS_Debug.xcconfig; path = ../Configuration/Settings_macOS_Debug.xcconfig; sourceTree = ""; }; -+ 84358AF32F4550B30077EA44 /* ChestTileEntity.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ChestTileEntity.hpp; sourceTree = ""; }; -+ 84358AF42F4550B30077EA44 /* ChestTileEntity.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ChestTileEntity.cpp; sourceTree = ""; }; -+ 84358AF52F4550B30077EA44 /* FurnaceTileEntity.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = FurnaceTileEntity.hpp; sourceTree = ""; }; -+ 84358AF62F4550B30077EA44 /* FurnaceTileEntity.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FurnaceTileEntity.cpp; sourceTree = ""; }; -+ 84358AF72F4550B30077EA44 /* MusicTileEntity.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = MusicTileEntity.hpp; sourceTree = ""; }; -+ 84358AF82F4550B30077EA44 /* MusicTileEntity.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = MusicTileEntity.cpp; sourceTree = ""; }; -+ 84358AF92F4550B30077EA44 /* TileEntity.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = TileEntity.hpp; sourceTree = ""; }; -+ 84358AFA2F4550B30077EA44 /* TileEntity.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = TileEntity.cpp; sourceTree = ""; }; -+ 84358AFB2F4550B30077EA44 /* TileEntityType.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = TileEntityType.hpp; sourceTree = ""; }; -+ 84358AFC2F4550B30077EA44 /* TileEntityType.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = TileEntityType.cpp; sourceTree = ""; }; -+ 84358B082F4550CC0077EA44 /* ChestTile.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ChestTile.hpp; sourceTree = ""; }; -+ 84358B092F4550CC0077EA44 /* ChestTile.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ChestTile.cpp; sourceTree = ""; }; -+ 84358B0A2F4550CC0077EA44 /* EntityTile.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = EntityTile.hpp; sourceTree = ""; }; -+ 84358B0B2F4550CC0077EA44 /* EntityTile.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = EntityTile.cpp; sourceTree = ""; }; -+ 84358B0C2F4550CC0077EA44 /* FurnaceTile.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = FurnaceTile.hpp; sourceTree = ""; }; -+ 84358B0D2F4550CC0077EA44 /* FurnaceTile.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FurnaceTile.cpp; sourceTree = ""; }; -+ 84358B0E2F4550CC0077EA44 /* MusicTile.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = MusicTile.hpp; sourceTree = ""; }; -+ 84358B0F2F4550CC0077EA44 /* MusicTile.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = MusicTile.cpp; sourceTree = ""; }; -+ 84358B182F45511C0077EA44 /* NoteParticle.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = NoteParticle.cpp; sourceTree = ""; }; -+ 84358B1A2F4551290077EA44 /* Facing.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Facing.cpp; sourceTree = ""; }; -+ 84358B1C2F4551500077EA44 /* CoalItem.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = CoalItem.hpp; sourceTree = ""; }; -+ 84358B1D2F4551500077EA44 /* CoalItem.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = CoalItem.cpp; sourceTree = ""; }; -+ 84358B202F4551730077EA44 /* FurnaceMenu.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = FurnaceMenu.hpp; sourceTree = ""; }; -+ 84358B212F4551730077EA44 /* FurnaceMenu.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FurnaceMenu.cpp; sourceTree = ""; }; -+ 84358B222F4551730077EA44 /* FurnaceResultSlot.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = FurnaceResultSlot.hpp; sourceTree = ""; }; -+ 84358B232F4551730077EA44 /* FurnaceResultSlot.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FurnaceResultSlot.cpp; sourceTree = ""; }; -+ 84358B282F45519B0077EA44 /* FurnaceScreen.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = FurnaceScreen.hpp; sourceTree = ""; }; -+ 84358B292F45519B0077EA44 /* FurnaceScreen.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FurnaceScreen.cpp; sourceTree = ""; }; - 8435BB172DCD47F400D38282 /* SoundStreamOAL.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = SoundStreamOAL.hpp; sourceTree = ""; }; - 8435BB182DCD47F400D38282 /* SoundStreamOAL.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = SoundStreamOAL.cpp; sourceTree = ""; }; - 8441F9852DB4E911005977BD /* CustomSoundSystem.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = CustomSoundSystem.hpp; sourceTree = ""; }; -@@ -3756,6 +3812,7 @@ - 84E7BF2D2F286A20002D3936 /* ContainerListener.cpp */, - 84E7BF2E2F286A20002D3936 /* ContainerListener.hpp */, - 840DD6572AC810620006A435 /* entity */, -+ 84358B1A2F4551290077EA44 /* Facing.cpp */, - 8477B3A82C4DC3B3004E1AC5 /* Facing.hpp */, - 840DD6682AC810620006A435 /* gamemode */, - 84E7BF092F286A08002D3936 /* inventory */, -@@ -3852,6 +3909,8 @@ - children = ( - 84B9D8962F3BE0A900FD67C4 /* ArmorItem.hpp */, - 84B9D8972F3BE0A900FD67C4 /* ArmorItem.cpp */, -+ 84358B1C2F4551500077EA44 /* CoalItem.hpp */, -+ 84358B1D2F4551500077EA44 /* CoalItem.cpp */, - 84B9D8982F3BE0A900FD67C4 /* HoeItem.hpp */, - 84B9D8992F3BE0A900FD67C4 /* HoeItem.cpp */, - 84B9D89A2F3BE0A900FD67C4 /* SeedItem.hpp */, -@@ -4042,6 +4101,7 @@ - 840DD6CE2AC810620006A435 /* particle */ = { - isa = PBXGroup; - children = ( -+ 84358B182F45511C0077EA44 /* NoteParticle.cpp */, - 840DD6CF2AC810620006A435 /* BubbleParticle.cpp */, - 840DD6D02AC810620006A435 /* ExplodeParticle.cpp */, - 8470AF3B2BE9B6FA00BCA54E /* FireworkParticle.hpp */, -@@ -4077,104 +4137,113 @@ - 840DD6E12AC810620006A435 /* tile */ = { - isa = PBXGroup; - children = ( -- 84B9D8A22F3BE0B200FD67C4 /* CropsTile.hpp */, -- 84B9D8A32F3BE0B200FD67C4 /* CropsTile.cpp */, -- 840DD6E22AC810620006A435 /* BookshelfTile.cpp */, -+ 84358AFD2F4550B30077EA44 /* entity */, - 840DD6E32AC810620006A435 /* BookshelfTile.hpp */, -- 840DD6E42AC810620006A435 /* Bush.cpp */, -+ 840DD6E22AC810620006A435 /* BookshelfTile.cpp */, - 840DD6E52AC810620006A435 /* Bush.hpp */, -- 84E1C9BF2E7FDC26007D2F5D /* CactusTile.cpp */, -+ 840DD6E42AC810620006A435 /* Bush.cpp */, - 84E1C9C02E7FDC26007D2F5D /* CactusTile.hpp */, -- 840DD6E62AC810620006A435 /* ClayTile.cpp */, -+ 84E1C9BF2E7FDC26007D2F5D /* CactusTile.cpp */, -+ 84358B082F4550CC0077EA44 /* ChestTile.hpp */, -+ 84358B092F4550CC0077EA44 /* ChestTile.cpp */, - 840DD6E72AC810620006A435 /* ClayTile.hpp */, -- 840DD6E82AC810620006A435 /* ClothTile.cpp */, -+ 840DD6E62AC810620006A435 /* ClayTile.cpp */, - 840DD6E92AC810620006A435 /* ClothTile.hpp */, -- 84DED1B52F30970A004001C5 /* CraftingTableTile.cpp */, -+ 840DD6E82AC810620006A435 /* ClothTile.cpp */, - 84DED1B62F30970A004001C5 /* CraftingTableTile.hpp */, -- 84E1C9C12E7FDC26007D2F5D /* DeadBush.cpp */, -+ 84DED1B52F30970A004001C5 /* CraftingTableTile.cpp */, -+ 84B9D8A22F3BE0B200FD67C4 /* CropsTile.hpp */, -+ 84B9D8A32F3BE0B200FD67C4 /* CropsTile.cpp */, - 84E1C9C22E7FDC26007D2F5D /* DeadBush.hpp */, -- 840DD6EA2AC810620006A435 /* DirtTile.cpp */, -+ 84E1C9C12E7FDC26007D2F5D /* DeadBush.cpp */, - 840DD6EB2AC810620006A435 /* DirtTile.hpp */, -- 840DD6EC2AC810620006A435 /* DoorTile.cpp */, -+ 840DD6EA2AC810620006A435 /* DirtTile.cpp */, - 840DD6ED2AC810620006A435 /* DoorTile.hpp */, -- 840DD6EE2AC810620006A435 /* FarmTile.cpp */, -+ 840DD6EC2AC810620006A435 /* DoorTile.cpp */, -+ 84358B0A2F4550CC0077EA44 /* EntityTile.hpp */, -+ 84358B0B2F4550CC0077EA44 /* EntityTile.cpp */, - 840DD6EF2AC810620006A435 /* FarmTile.hpp */, -- 84E1C9C32E7FDC26007D2F5D /* FenceTile.cpp */, -+ 840DD6EE2AC810620006A435 /* FarmTile.cpp */, - 84E1C9C42E7FDC26007D2F5D /* FenceTile.hpp */, -- 840DD6F02AC810620006A435 /* FireTile.cpp */, -+ 84E1C9C32E7FDC26007D2F5D /* FenceTile.cpp */, - 840DD6F12AC810620006A435 /* FireTile.hpp */, -- 840DD6F22AC810620006A435 /* GlassTile.cpp */, -+ 840DD6F02AC810620006A435 /* FireTile.cpp */, -+ 84358B0C2F4550CC0077EA44 /* FurnaceTile.hpp */, -+ 84358B0D2F4550CC0077EA44 /* FurnaceTile.cpp */, - 840DD6F32AC810620006A435 /* GlassTile.hpp */, -- 84E1C9C52E7FDC26007D2F5D /* GlowstoneTile.cpp */, -+ 840DD6F22AC810620006A435 /* GlassTile.cpp */, - 84E1C9C62E7FDC26007D2F5D /* GlowstoneTile.hpp */, -- 840DD6F42AC810620006A435 /* GrassTile.cpp */, -+ 84E1C9C52E7FDC26007D2F5D /* GlowstoneTile.cpp */, - 840DD6F52AC810620006A435 /* GrassTile.hpp */, -- 840DD6F62AC810620006A435 /* GravelTile.cpp */, -+ 840DD6F42AC810620006A435 /* GrassTile.cpp */, - 840DD6F72AC810620006A435 /* GravelTile.hpp */, -- 840DD6F82AC810620006A435 /* HalfTransparentTile.cpp */, -+ 840DD6F62AC810620006A435 /* GravelTile.cpp */, - 840DD6F92AC810620006A435 /* HalfTransparentTile.hpp */, -- 840DD6FA2AC810620006A435 /* IceTile.cpp */, -+ 840DD6F82AC810620006A435 /* HalfTransparentTile.cpp */, - 840DD6FB2AC810620006A435 /* IceTile.hpp */, -- 840DD6FC2AC810620006A435 /* InvisibleTile.cpp */, -+ 840DD6FA2AC810620006A435 /* IceTile.cpp */, - 840DD6FD2AC810620006A435 /* InvisibleTile.hpp */, -- 840DD6FE2AC810620006A435 /* LadderTile.cpp */, -+ 840DD6FC2AC810620006A435 /* InvisibleTile.cpp */, - 840DD6FF2AC810620006A435 /* LadderTile.hpp */, -- 840DD7002AC810620006A435 /* LeafTile.cpp */, -+ 840DD6FE2AC810620006A435 /* LadderTile.cpp */, - 840DD7012AC810620006A435 /* LeafTile.hpp */, -- 840DD7022AC810620006A435 /* LiquidTile.cpp */, -+ 840DD7002AC810620006A435 /* LeafTile.cpp */, - 840DD7032AC810620006A435 /* LiquidTile.hpp */, -- 840DD7042AC810620006A435 /* LiquidTileDynamic.cpp */, -+ 840DD7022AC810620006A435 /* LiquidTile.cpp */, - 840DD7052AC810620006A435 /* LiquidTileDynamic.hpp */, -- 840DD7062AC810620006A435 /* LiquidTileStatic.cpp */, -+ 840DD7042AC810620006A435 /* LiquidTileDynamic.cpp */, - 840DD7072AC810620006A435 /* LiquidTileStatic.hpp */, -- 840DD7082AC810620006A435 /* MetalTile.cpp */, -+ 840DD7062AC810620006A435 /* LiquidTileStatic.cpp */, - 840DD7092AC810620006A435 /* MetalTile.hpp */, -- 840DD70A2AC810620006A435 /* ObsidianTile.cpp */, -+ 840DD7082AC810620006A435 /* MetalTile.cpp */, -+ 84358B0E2F4550CC0077EA44 /* MusicTile.hpp */, -+ 84358B0F2F4550CC0077EA44 /* MusicTile.cpp */, - 840DD70B2AC810620006A435 /* ObsidianTile.hpp */, -- 840DD70C2AC810620006A435 /* OreTile.cpp */, -+ 840DD70A2AC810620006A435 /* ObsidianTile.cpp */, - 840DD70D2AC810620006A435 /* OreTile.hpp */, -- 84E1C9C72E7FDC26007D2F5D /* PumpkinTile.cpp */, -+ 840DD70C2AC810620006A435 /* OreTile.cpp */, - 84E1C9C82E7FDC26007D2F5D /* PumpkinTile.hpp */, -- 840DD70E2AC810620006A435 /* RedStoneOreTile.cpp */, -+ 84E1C9C72E7FDC26007D2F5D /* PumpkinTile.cpp */, - 840DD70F2AC810620006A435 /* RedStoneOreTile.hpp */, -- 840DD7102AC810620006A435 /* ReedTile.cpp */, -+ 840DD70E2AC810620006A435 /* RedStoneOreTile.cpp */, - 840DD7112AC810620006A435 /* ReedTile.hpp */, -- 84E78C852B58B66B00D515EF /* RocketLauncherTile.cpp */, -+ 840DD7102AC810620006A435 /* ReedTile.cpp */, - 84E78C862B58B66B00D515EF /* RocketLauncherTile.hpp */, -- 840DD7122AC810620006A435 /* SandStoneTile.cpp */, -+ 84E78C852B58B66B00D515EF /* RocketLauncherTile.cpp */, - 840DD7132AC810620006A435 /* SandStoneTile.hpp */, -- 840DD7142AC810620006A435 /* SandTile.cpp */, -+ 840DD7122AC810620006A435 /* SandStoneTile.cpp */, - 840DD7152AC810620006A435 /* SandTile.hpp */, -- 840DD7162AC810620006A435 /* Sapling.cpp */, -+ 840DD7142AC810620006A435 /* SandTile.cpp */, - 840DD7172AC810620006A435 /* Sapling.hpp */, -- 84E1C9C92E7FDC26007D2F5D /* SoulSandTile.cpp */, -+ 840DD7162AC810620006A435 /* Sapling.cpp */, - 84E1C9CA2E7FDC26007D2F5D /* SoulSandTile.hpp */, -- 840DD7182AC810620006A435 /* SpongeTile.cpp */, -+ 84E1C9C92E7FDC26007D2F5D /* SoulSandTile.cpp */, - 840DD7192AC810620006A435 /* SpongeTile.hpp */, -- 840DD71A2AC810620006A435 /* StairTile.cpp */, -+ 840DD7182AC810620006A435 /* SpongeTile.cpp */, - 840DD71B2AC810620006A435 /* StairTile.hpp */, -- 840DD71C2AC810620006A435 /* StoneSlabTile.cpp */, -+ 840DD71A2AC810620006A435 /* StairTile.cpp */, - 840DD71D2AC810620006A435 /* StoneSlabTile.hpp */, -- 840DD71E2AC810620006A435 /* StoneTile.cpp */, -+ 840DD71C2AC810620006A435 /* StoneSlabTile.cpp */, - 840DD71F2AC810620006A435 /* StoneTile.hpp */, -- 84E1C9CB2E7FDC26007D2F5D /* TallGrass.cpp */, -+ 840DD71E2AC810620006A435 /* StoneTile.cpp */, - 84E1C9CC2E7FDC26007D2F5D /* TallGrass.hpp */, -- 840DD7202AC810620006A435 /* Tile.cpp */, -+ 84E1C9CB2E7FDC26007D2F5D /* TallGrass.cpp */, - 840DD7212AC810620006A435 /* Tile.hpp */, -- 840DD7222AC810620006A435 /* TntTile.cpp */, -+ 840DD7202AC810620006A435 /* Tile.cpp */, - 840DD7232AC810620006A435 /* TntTile.hpp */, -- 840DD7242AC810620006A435 /* TopSnowTile.cpp */, -+ 840DD7222AC810620006A435 /* TntTile.cpp */, - 840DD7252AC810620006A435 /* TopSnowTile.hpp */, -- 840DD7262AC810620006A435 /* TorchTile.cpp */, -+ 840DD7242AC810620006A435 /* TopSnowTile.cpp */, - 840DD7272AC810620006A435 /* TorchTile.hpp */, -- 840DD7282AC810620006A435 /* TransparentTile.cpp */, -+ 840DD7262AC810620006A435 /* TorchTile.cpp */, - 840DD7292AC810620006A435 /* TransparentTile.hpp */, -- 840DD72A2AC810620006A435 /* TreeTile.cpp */, -+ 840DD7282AC810620006A435 /* TransparentTile.cpp */, - 840DD72B2AC810620006A435 /* TreeTile.hpp */, -- 84E1C9CD2E7FDC26007D2F5D /* Web.cpp */, -+ 840DD72A2AC810620006A435 /* TreeTile.cpp */, - 84E1C9CE2E7FDC26007D2F5D /* Web.hpp */, -- 840DD72C2AC810620006A435 /* WireTile.cpp */, -+ 84E1C9CD2E7FDC26007D2F5D /* Web.cpp */, - 840DD72D2AC810620006A435 /* WireTile.hpp */, -+ 840DD72C2AC810620006A435 /* WireTile.cpp */, - ); - path = tile; - sourceTree = ""; -@@ -4566,6 +4635,23 @@ - name = macOS; - sourceTree = ""; - }; -+ 84358AFD2F4550B30077EA44 /* entity */ = { -+ isa = PBXGroup; -+ children = ( -+ 84358AF32F4550B30077EA44 /* ChestTileEntity.hpp */, -+ 84358AF42F4550B30077EA44 /* ChestTileEntity.cpp */, -+ 84358AF52F4550B30077EA44 /* FurnaceTileEntity.hpp */, -+ 84358AF62F4550B30077EA44 /* FurnaceTileEntity.cpp */, -+ 84358AF72F4550B30077EA44 /* MusicTileEntity.hpp */, -+ 84358AF82F4550B30077EA44 /* MusicTileEntity.cpp */, -+ 84358AF92F4550B30077EA44 /* TileEntity.hpp */, -+ 84358AFA2F4550B30077EA44 /* TileEntity.cpp */, -+ 84358AFB2F4550B30077EA44 /* TileEntityType.hpp */, -+ 84358AFC2F4550B30077EA44 /* TileEntityType.cpp */, -+ ); -+ path = entity; -+ sourceTree = ""; -+ }; - 8441F9872DB4E911005977BD /* openal */ = { - isa = PBXGroup; - children = ( -@@ -4949,6 +5035,8 @@ - 84E022E82F2A0211003C8FFE /* inventory */ = { - isa = PBXGroup; - children = ( -+ 84358B282F45519B0077EA44 /* FurnaceScreen.hpp */, -+ 84358B292F45519B0077EA44 /* FurnaceScreen.cpp */, - 84E022E92F2A0211003C8FFE /* ChestScreen.cpp */, - 84E022EA2F2A0211003C8FFE /* ChestScreen.hpp */, - 844337192F405E1C00EB3115 /* ClassicCraftingScreen_Console.cpp */, -@@ -5006,6 +5094,10 @@ - 84E7BF0F2F286A08002D3936 /* CraftingContainer.hpp */, - 84DED19A2F3095FD004001C5 /* CraftingMenu.cpp */, - 84DED19B2F3095FD004001C5 /* CraftingMenu.hpp */, -+ 84358B202F4551730077EA44 /* FurnaceMenu.hpp */, -+ 84358B212F4551730077EA44 /* FurnaceMenu.cpp */, -+ 84358B222F4551730077EA44 /* FurnaceResultSlot.hpp */, -+ 84358B232F4551730077EA44 /* FurnaceResultSlot.cpp */, - 84E7BF102F286A08002D3936 /* InventoryMenu.cpp */, - 84E7BF112F286A08002D3936 /* InventoryMenu.hpp */, - 84E7BF122F286A08002D3936 /* ResultContainer.cpp */, -@@ -5806,6 +5898,7 @@ - 84A2FF182DB61D440090CE3E /* SoundPathRepository.hpp in Headers */, - 84EB09FB2EE2CCA8008FB007 /* TextureData.hpp in Headers */, - 84B1E0302E04FD4500ED000A /* ArrowRenderer.hpp in Headers */, -+ 84358B2A2F45519B0077EA44 /* FurnaceScreen.hpp in Headers */, - 84AA8C2B2B32F3F3003F5B82 /* LightUpdate.hpp in Headers */, - 84AA8C2D2B32F3F3003F5B82 /* PatchManager.hpp in Headers */, - 84AA8C2F2B32F3F3003F5B82 /* RenderChunk.hpp in Headers */, -@@ -5845,6 +5938,10 @@ - 84E1C9D42E7FDC26007D2F5D /* FenceTile.hpp in Headers */, - 84BF637B2AF186C8008A9995 /* ItemEntity.hpp in Headers */, - 84BF637D2AF186C8008A9995 /* Mob.hpp in Headers */, -+ 84358B142F4550CC0077EA44 /* ChestTile.hpp in Headers */, -+ 84358B152F4550CC0077EA44 /* FurnaceTile.hpp in Headers */, -+ 84358B162F4550CC0077EA44 /* EntityTile.hpp in Headers */, -+ 84358B172F4550CC0077EA44 /* MusicTile.hpp in Headers */, - 84BF637E2AF186C8008A9995 /* Player.hpp in Headers */, - 8477B3A92C4DC3B3004E1AC5 /* Facing.hpp in Headers */, - 84BF637F2AF186C8008A9995 /* PrimedTnt.hpp in Headers */, -@@ -5915,6 +6012,8 @@ - 84BF63A92AF186C8008A9995 /* RegionFile.hpp in Headers */, - 84BF63AA2AF186C8008A9995 /* TickNextTickData.hpp in Headers */, - 84BF63AB2AF186C8008A9995 /* Particle.hpp in Headers */, -+ 84358B262F4551730077EA44 /* FurnaceMenu.hpp in Headers */, -+ 84358B272F4551730077EA44 /* FurnaceResultSlot.hpp in Headers */, - 84E7BF232F286A08002D3936 /* ResultContainer.hpp in Headers */, - 84E1C9E62E7FDC72007D2F5D /* AuxTileItem.hpp in Headers */, - 84E7BF272F286A08002D3936 /* SimpleContainer.hpp in Headers */, -@@ -6001,6 +6100,7 @@ - 84E78C842B58B5FB00D515EF /* RocketItem.hpp in Headers */, - 84E78C882B58B66B00D515EF /* RocketLauncherTile.hpp in Headers */, - 8477B3B72C4DC414004E1AC5 /* EmptyLevelChunk.hpp in Headers */, -+ 84358B1F2F4551500077EA44 /* CoalItem.hpp in Headers */, - 84E1C9E82E7FDC72007D2F5D /* ClothItem.hpp in Headers */, - 8470AF2B2BE9B60A00BCA54E /* EntityType.hpp in Headers */, - 8445E7A42D769329008DC834 /* EntityTypeDescriptor.hpp in Headers */, -@@ -6009,6 +6109,11 @@ - 84B9D8A12F3BE0A900FD67C4 /* SeedItem.hpp in Headers */, - 8470AF2D2BE9B60A00BCA54E /* MobFactory.hpp in Headers */, - 8470AF392BE9B6D100BCA54E /* GameType.hpp in Headers */, -+ 84358B032F4550B30077EA44 /* TileEntity.hpp in Headers */, -+ 84358B042F4550B30077EA44 /* FurnaceTileEntity.hpp in Headers */, -+ 84358B052F4550B30077EA44 /* ChestTileEntity.hpp in Headers */, -+ 84358B062F4550B30077EA44 /* MusicTileEntity.hpp in Headers */, -+ 84358B072F4550B30077EA44 /* TileEntityType.hpp in Headers */, - 8470AF3D2BE9B6FA00BCA54E /* FireworkParticle.hpp in Headers */, - 84E7BF302F286A20002D3936 /* CompoundContainer.hpp in Headers */, - ); -@@ -6785,6 +6890,7 @@ - 84B8AEFD2AF1896F008DE93D /* InvalidLicenseScreen.cpp in Sources */, - 84B8AEFE2AF1896F008DE93D /* JoinGameScreen.cpp in Sources */, - 844336FB2F405DCA00EB3115 /* VerticalLayout.cpp in Sources */, -+ 84358B2B2F45519B0077EA44 /* FurnaceScreen.cpp in Sources */, - 84B8AEFF2AF1896F008DE93D /* OptionsScreen.cpp in Sources */, - 84B8AF002AF1896F008DE93D /* PauseScreen.cpp in Sources */, - 84B8AF012AF1896F008DE93D /* ProgressScreen.cpp in Sources */, -@@ -6976,6 +7082,11 @@ - 84BF63162AF18631008A9995 /* SurvivalMode.cpp in Sources */, - 84BF63172AF18631008A9995 /* CameraItem.cpp in Sources */, - 84DED1AF2F309611004001C5 /* ShapedRecipe.cpp in Sources */, -+ 84358AFE2F4550B30077EA44 /* TileEntityType.cpp in Sources */, -+ 84358AFF2F4550B30077EA44 /* ChestTileEntity.cpp in Sources */, -+ 84358B002F4550B30077EA44 /* FurnaceTileEntity.cpp in Sources */, -+ 84358B012F4550B30077EA44 /* TileEntity.cpp in Sources */, -+ 84358B022F4550B30077EA44 /* MusicTileEntity.cpp in Sources */, - 84E1C9E52E7FDC72007D2F5D /* AuxTileItem.cpp in Sources */, - 84E1C9D52E7FDC26007D2F5D /* GlowstoneTile.cpp in Sources */, - 84BF63182AF18631008A9995 /* DoorItem.cpp in Sources */, -@@ -6983,6 +7094,7 @@ - 84B9D8A42F3BE0B200FD67C4 /* CropsTile.cpp in Sources */, - 8445E7A22D769329008DC834 /* EntityType.cpp in Sources */, - 84BF631A2AF18631008A9995 /* Item.cpp in Sources */, -+ 84358B1B2F4551290077EA44 /* Facing.cpp in Sources */, - 84BF631C2AF18631008A9995 /* TileItem.cpp in Sources */, - 84E1C9F02E7FDC89007D2F5D /* VegetationFeature.cpp in Sources */, - 84BF631D2AF18631008A9995 /* TilePlanterItem.cpp in Sources */, -@@ -6995,8 +7107,11 @@ - 84BF63222AF18631008A9995 /* BiomeSource.cpp in Sources */, - 84BF63232AF18631008A9995 /* ChunkCache.cpp in Sources */, - 84E1C9CF2E7FDC26007D2F5D /* CactusTile.cpp in Sources */, -+ 84358B242F4551730077EA44 /* FurnaceResultSlot.cpp in Sources */, -+ 84358B252F4551730077EA44 /* FurnaceMenu.cpp in Sources */, - 84E7BF1A2F286A08002D3936 /* ArmorSlot.cpp in Sources */, - 84BF63242AF18631008A9995 /* ChunkSource.cpp in Sources */, -+ 84358B192F45511C0077EA44 /* NoteParticle.cpp in Sources */, - 84BF63252AF18631008A9995 /* LevelChunk.cpp in Sources */, - 84BF63262AF18631008A9995 /* PerformanceTestChunkSource.cpp in Sources */, - 84EE51312E8DCC9000D3DCA2 /* DataLayer.cpp in Sources */, -@@ -7026,6 +7141,10 @@ - 84B9D89C2F3BE0A900FD67C4 /* ArmorItem.cpp in Sources */, - 84B9D89D2F3BE0A900FD67C4 /* HoeItem.cpp in Sources */, - 84B9D89E2F3BE0A900FD67C4 /* SeedItem.cpp in Sources */, -+ 84358B102F4550CC0077EA44 /* FurnaceTile.cpp in Sources */, -+ 84358B112F4550CC0077EA44 /* EntityTile.cpp in Sources */, -+ 84358B122F4550CC0077EA44 /* ChestTile.cpp in Sources */, -+ 84358B132F4550CC0077EA44 /* MusicTile.cpp in Sources */, - 84BF63382AF18631008A9995 /* LevelListener.cpp in Sources */, - 84E7BF2F2F286A20002D3936 /* CompoundContainer.cpp in Sources */, - 84BF63392AF18631008A9995 /* Material.cpp in Sources */, -@@ -7097,6 +7216,7 @@ - 84BF63662AF18631008A9995 /* MetalTile.cpp in Sources */, - 84BF63672AF18631008A9995 /* ObsidianTile.cpp in Sources */, - 84CCBC932E61880600E251AF /* EntityFactory.cpp in Sources */, -+ 84358B1E2F4551500077EA44 /* CoalItem.cpp in Sources */, - 84BF63682AF18631008A9995 /* OreTile.cpp in Sources */, - 84B1E0392E04FD7900ED000A /* Arrow.cpp in Sources */, - 84BF63692AF18631008A9995 /* RedStoneOreTile.cpp in Sources */, - -From da4f8640e5a7cc22e72d8186ddaf0df8add4428a Mon Sep 17 00:00:00 2001 -From: Sam <31021951+samdotjpg@users.noreply.github.com> -Date: Tue, 17 Feb 2026 20:53:59 -0500 -Subject: [PATCH 18/22] add to lang file - ---- - game/assets/lang/en_US.lang | 1 + - 1 file changed, 1 insertion(+) - -diff --git a/game/assets/lang/en_US.lang b/game/assets/lang/en_US.lang -index d85d3c0b3..1002d7955 100644 ---- a/game/assets/lang/en_US.lang -+++ b/game/assets/lang/en_US.lang -@@ -104,6 +104,7 @@ options.guiScale.small=Small - options.guiScale.normal=Normal - options.guiScale.large=Large - options.advancedOpengl=Advanced OpenGL -+options.enableVsync=Enable Vsync - - performance.max=Max FPS - performance.balanced=Balanced - -From d46806d9cf49edd66fd97cd83ad806874be0d36a Mon Sep 17 00:00:00 2001 -From: Sam -Date: Tue, 17 Feb 2026 22:18:30 -0500 -Subject: [PATCH 19/22] Make riding / rider code not suck really badly - ---- - source/world/entity/Entity.cpp | 60 +++++++++++++++++++--------------- - source/world/entity/Entity.hpp | 8 +++-- - source/world/entity/Mob.cpp | 2 +- - source/world/level/Level.cpp | 17 ++++------ - 4 files changed, 48 insertions(+), 39 deletions(-) - -diff --git a/source/world/entity/Entity.cpp b/source/world/entity/Entity.cpp -index b70616ba1..8e8ba64a9 100644 ---- a/source/world/entity/Entity.cpp -+++ b/source/world/entity/Entity.cpp -@@ -28,8 +28,8 @@ void Entity::_init() - field_28 = 0; - field_30 = 1.0f; - m_dimensionId = DIMENSION_OVERWORLD; -- m_riderId = 0; -- m_ridingId = 0; -+ _riderId = 0; -+ _ridingId = 0; - m_bRiding = false; - m_bBlocksBuilding = false; - m_pLevel = nullptr; -@@ -474,10 +474,10 @@ void Entity::baseTick() - //@TODO: untangle the gotos - if (const Entity* riding = getRiding()) - { -- if ((!riding && m_riderId > 0) || riding->m_bRemoved) -+ // if you were riding an entity and they no longer exist, stop -+ if ((!riding && _ridingId > 0) || riding->m_bRemoved) - { -- m_riderId = 0; -- setSharedFlag(C_ENTITY_FLAG_RIDING, false); -+ setRiding(nullptr); - } - } - -@@ -756,6 +756,9 @@ void Entity::playerTouch(Player* player) - - void Entity::push(Entity* bud) - { -+ if (bud == getRider() || bud == getRiding()) -+ return; -+ - float diffX = bud->m_pos.x - m_pos.x; - float diffZ = bud->m_pos.z - m_pos.z; - float maxDiff = Mth::absMax(diffX, diffZ); -@@ -963,8 +966,8 @@ void Entity::rideTick() - Entity* riding = getRiding(); - if (!riding || riding->m_bRemoved) - { -- m_riderId = 0; -- setSharedFlag(C_ENTITY_FLAG_RIDING, false); -+ setRiding(nullptr); -+ return; - } - - // we don't move -@@ -1028,12 +1031,11 @@ void Entity::ride(Entity* newRiding) - { - moveTo(oldRiding->m_pos); - setRot(oldRiding->m_rot); -- oldRiding->m_riderId = 0; // Let them know you dismounted them -+ oldRiding->setRider(nullptr); - } - - // Let yourself know you aren't riding anything -- m_ridingId = 0; -- setSharedFlag(C_ENTITY_FLAG_RIDING, false); -+ setRiding(nullptr); - - return; - } -@@ -1041,10 +1043,9 @@ void Entity::ride(Entity* newRiding) - // Dismount if the same entity is fed in - if (oldRiding && oldRiding == newRiding) - { -- oldRiding->m_riderId = 0; -+ oldRiding->setRider(nullptr); - -- m_ridingId = 0; -- setSharedFlag(C_ENTITY_FLAG_RIDING, false); -+ setRiding(nullptr); - - moveTo(oldRiding->m_pos); - setRot(oldRiding->m_rot); -@@ -1054,31 +1055,27 @@ void Entity::ride(Entity* newRiding) - // if (this.riding != null) this.riding.rider = null; - if (oldRiding) - { -- oldRiding->m_riderId = 0; -+ oldRiding->setRider(nullptr); - } - - // if (newRiding.rider != null) newRiding.rider.riding = null; - // i hate this name but it's literally what it is - if (Entity* newRidesOldRider = newRiding->getRider()) - { -- newRidesOldRider->m_ridingId = 0; -- newRidesOldRider->setSharedFlag(C_ENTITY_FLAG_RIDING, false); -+ setRiding(nullptr); -+ newRidesOldRider->setRider(nullptr); - } - -- // Tell yourself that you're riding the new ride -- m_ridingId = newRiding->m_EntityID; -- setSharedFlag(C_ENTITY_FLAG_RIDING, true); -- -- // Tell the new ride that it's being ridden by you -- newRiding->m_riderId = m_EntityID; -+ setRiding(newRiding); -+ newRiding->setRider(this); - } - - Entity* Entity::getRiding() const - { -- if (m_ridingId <= 0) -+ if (_ridingId <= 0) - return nullptr; - -- if (Entity* riding = m_pLevel->getEntity(m_ridingId)) -+ if (Entity* riding = m_pLevel->getEntity(_ridingId)) - return riding; - - return nullptr; -@@ -1086,15 +1083,26 @@ Entity* Entity::getRiding() const - - Entity* Entity::getRider() const - { -- if (m_riderId <= 0) -+ if (_riderId <= 0) - return nullptr; - -- if (Entity* rider = m_pLevel->getEntity(m_riderId)) -+ if (Entity* rider = m_pLevel->getEntity(_riderId)) - return rider; - - return nullptr; - } - -+void Entity::setRider(Entity* rider) -+{ -+ _riderId = (rider) ? rider->m_EntityID : 0; -+} -+ -+void Entity::setRiding(Entity* riding) -+{ -+ _ridingId = (riding) ? riding->m_EntityID : 0; -+ setSharedFlag(C_ENTITY_FLAG_RIDING, riding); -+} -+ - /*void Entity::thunderHit(LightningBolt* bolt) - { - burn(5); -diff --git a/source/world/entity/Entity.hpp b/source/world/entity/Entity.hpp -index e3b53d6a1..9fec6801f 100644 ---- a/source/world/entity/Entity.hpp -+++ b/source/world/entity/Entity.hpp -@@ -207,6 +207,8 @@ class Entity - virtual float getRidingHeight() const { return m_heightOffset; } - Entity* getRiding() const; - Entity* getRider() const; -+ void setRiding(Entity* ent); -+ void setRider(Entity* ent); - void load(const CompoundTag& tag); - bool save(CompoundTag& tag) const; - void saveWithoutId(CompoundTag& tag) const; -@@ -231,6 +233,10 @@ class Entity - (m_pos.z - pos.z) * (m_pos.z - pos.z); - } - -+private: -+ Entity::ID _ridingId; -+ Entity::ID _riderId; -+ - protected: - SynchedEntityData m_entityData; - bool m_bMakeStepSound; -@@ -248,8 +254,6 @@ class Entity - float field_30; - //TileSource* m_pTileSource; - DimensionId m_dimensionId; -- Entity::ID m_ridingId; -- Entity::ID m_riderId; - bool m_bRiding; - bool m_bBlocksBuilding; - Level* m_pLevel; -diff --git a/source/world/entity/Mob.cpp b/source/world/entity/Mob.cpp -index 6dede3f9b..459ef5571 100644 ---- a/source/world/entity/Mob.cpp -+++ b/source/world/entity/Mob.cpp -@@ -758,7 +758,7 @@ void Mob::aiStep() - { - Entity* pEnt = *it; - if (pEnt->isPushable()) -- pEnt->push(this); -+ pEnt->push(this); - } - } - -diff --git a/source/world/level/Level.cpp b/source/world/level/Level.cpp -index 0c1efd729..08d495c18 100644 ---- a/source/world/level/Level.cpp -+++ b/source/world/level/Level.cpp -@@ -1260,9 +1260,8 @@ void Level::removeAllPendingEntityRemovals() - { - if (riding->m_bRemoved || riding->getRider() != ent) - { -- riding->m_riderId = 0; -- ent->m_ridingId = 0; -- ent->setSharedFlag(C_ENTITY_FLAG_RIDING, false); -+ riding->setRider(nullptr); -+ ent->setRiding(nullptr); - } - else - continue; -@@ -1742,14 +1741,13 @@ void Level::tick(Entity* pEnt, bool shouldTick) - if (shouldTick && pEnt->m_bInAChunk) - { - Entity* rider = pEnt->getRider(); -+ // someone is riding this entity - if (rider) - { - if (rider->m_bRemoved || rider->getRiding() != pEnt) - { -- rider->m_riderId = 0; -- -- pEnt->m_ridingId = 0; -- pEnt->setSharedFlag(C_ENTITY_FLAG_RIDING, false); -+ rider->setRiding(nullptr); -+ pEnt->setRider(nullptr); - } - else - { -@@ -1797,9 +1795,8 @@ void Level::tickEntities() - { - if (riding->m_bRemoved || riding->getRider() != pEnt) - { -- riding->m_riderId = 0; -- pEnt->m_ridingId = 0; -- pEnt->setSharedFlag(C_ENTITY_FLAG_RIDING, false); -+ riding->setRider(nullptr); -+ pEnt->setRiding(nullptr); - } - else - { - -From 6bea080d8b33da01a06726233cb517584222a2f7 Mon Sep 17 00:00:00 2001 -From: Brent Da Mage -Date: Wed, 18 Feb 2026 02:23:38 -0600 -Subject: [PATCH 20/22] Fixed container listening and networking - ---- - .../windows/projects/World/World.vcxproj | 14 ++-- - .../projects/World/World.vcxproj.filters | 42 +++++++---- - .../multiplayer/MultiplayerLocalPlayer.cpp | 12 ++- - .../multiplayer/MultiplayerLocalPlayer.hpp | 3 +- - .../network/ClientSideNetworkHandler.cpp | 6 ++ - source/client/player/LocalPlayer.cpp | 10 +++ - source/network/Packet.hpp | 1 + - source/network/PacketUtil.cpp | 5 ++ - .../network/packets/ContainerOpenPacket.hpp | 2 +- - .../packets/ContainerSetContentPacket.cpp | 2 +- - source/server/ServerPlayer.cpp | 6 ++ - source/server/ServerPlayer.hpp | 3 +- - source/world/Container.hpp | 37 ---------- - source/world/entity/Player.cpp | 26 +++++-- - source/world/entity/Player.hpp | 5 +- - source/world/inventory/ArmorSlot.hpp | 2 +- - source/world/inventory/ChestMenu.hpp | 2 +- - .../{ => inventory}/CompoundContainer.cpp | 6 +- - .../{ => inventory}/CompoundContainer.hpp | 2 +- - source/world/inventory/Container.hpp | 55 ++++++++++++++ - .../ContainerContentChangeListener.cpp | 2 + - .../ContainerContentChangeListener.hpp | 12 +++ - .../{ => inventory}/ContainerListener.cpp | 2 +- - .../{ => inventory}/ContainerListener.hpp | 0 - source/world/inventory/ContainerMenu.cpp | 74 ++++++++++++++----- - source/world/inventory/ContainerMenu.hpp | 22 ++++-- - .../inventory/ContainerSizeChangeListener.cpp | 1 + - .../inventory/ContainerSizeChangeListener.hpp | 12 +++ - source/world/inventory/CraftingContainer.cpp | 2 +- - source/world/inventory/CraftingContainer.hpp | 4 +- - source/world/inventory/CraftingMenu.cpp | 3 + - source/world/inventory/FurnaceMenu.cpp | 4 +- - source/world/inventory/FurnaceResultSlot.hpp | 2 +- - source/world/inventory/InventoryMenu.cpp | 3 + - source/world/inventory/ResultContainer.cpp | 2 +- - source/world/inventory/ResultContainer.hpp | 4 +- - source/world/inventory/ResultSlot.hpp | 2 +- - source/world/inventory/SimpleContainer.cpp | 51 ++++++++++--- - source/world/inventory/SimpleContainer.hpp | 15 +++- - source/world/inventory/Slot.hpp | 8 +- - source/world/item/Inventory.hpp | 6 +- - source/world/item/crafting/Recipe.hpp | 2 +- - source/world/tile/ChestTile.cpp | 2 +- - source/world/tile/entity/ChestTileEntity.cpp | 2 +- - source/world/tile/entity/ChestTileEntity.hpp | 2 +- - .../world/tile/entity/FurnaceTileEntity.cpp | 3 +- - .../world/tile/entity/FurnaceTileEntity.hpp | 2 +- - 47 files changed, 346 insertions(+), 139 deletions(-) - delete mode 100644 source/world/Container.hpp - rename source/world/{ => inventory}/CompoundContainer.cpp (90%) - rename source/world/{ => inventory}/CompoundContainer.hpp (92%) - create mode 100644 source/world/inventory/Container.hpp - create mode 100644 source/world/inventory/ContainerContentChangeListener.cpp - create mode 100644 source/world/inventory/ContainerContentChangeListener.hpp - rename source/world/{ => inventory}/ContainerListener.cpp (81%) - rename source/world/{ => inventory}/ContainerListener.hpp (100%) - create mode 100644 source/world/inventory/ContainerSizeChangeListener.cpp - create mode 100644 source/world/inventory/ContainerSizeChangeListener.hpp - -diff --git a/platforms/windows/projects/World/World.vcxproj b/platforms/windows/projects/World/World.vcxproj -index 7247155f5..7b610602c 100644 ---- a/platforms/windows/projects/World/World.vcxproj -+++ b/platforms/windows/projects/World/World.vcxproj -@@ -202,7 +202,7 @@ - - - -- -+ - - - -@@ -210,7 +210,7 @@ - - - -- -+ - - - -@@ -242,6 +242,8 @@ - - - -+ -+ - - - -@@ -385,8 +387,8 @@ - - - -- -- -+ -+ - - - -@@ -394,7 +396,7 @@ - - - -- -+ - - - -@@ -423,6 +425,8 @@ - - - -+ -+ - - - -diff --git a/platforms/windows/projects/World/World.vcxproj.filters b/platforms/windows/projects/World/World.vcxproj.filters -index 6c9bbd804..85317d225 100644 ---- a/platforms/windows/projects/World/World.vcxproj.filters -+++ b/platforms/windows/projects/World/World.vcxproj.filters -@@ -587,9 +587,6 @@ - - Source Files\Level\LevelGen\Chunk - -- -- Source Files -- - - Source Files\Inventory - -@@ -614,9 +611,6 @@ - - Source Files\Inventory - -- -- Source Files -- - - Source Files\Inventory - -@@ -707,6 +701,18 @@ - - Source Files\Tile\Entity - -+ -+ Source Files\Inventory -+ -+ -+ Source Files\Inventory -+ -+ -+ Source Files\Inventory -+ -+ -+ Source Files\Inventory -+ - - - -@@ -1132,12 +1138,6 @@ - - Header Files\Level - -- -- Header Files -- -- -- Header Files -- - - Header Files\Inventory - -@@ -1159,9 +1159,6 @@ - - Header Files\Inventory - -- -- Header Files -- - - Header Files\Inventory - -@@ -1246,5 +1243,20 @@ - - Header Files\Tile - -+ -+ Header Files\Inventory -+ -+ -+ Header Files\Inventory -+ -+ -+ Header Files\Inventory -+ -+ -+ Header Files\Inventory -+ -+ -+ Header Files\Inventory -+ - - -\ No newline at end of file -diff --git a/source/client/multiplayer/MultiplayerLocalPlayer.cpp b/source/client/multiplayer/MultiplayerLocalPlayer.cpp -index 85742cbbf..36750209d 100644 ---- a/source/client/multiplayer/MultiplayerLocalPlayer.cpp -+++ b/source/client/multiplayer/MultiplayerLocalPlayer.cpp -@@ -15,6 +15,14 @@ void MultiplayerLocalPlayer::reallyDrop(ItemEntity* itemEntity) - { - } - -+void MultiplayerLocalPlayer::_handleOpenedContainerMenu() -+{ -+ if (m_pContainerMenu) -+ m_pContainerMenu->addSlotListener(this); -+ else -+ LOG_W("Tried to add MultiplayerLocalPlayer as ContainerListener for NULL container!"); -+} -+ - bool MultiplayerLocalPlayer::hurt(Entity* pAttacker, int damage) - { - // Java returns false -@@ -135,5 +143,7 @@ void MultiplayerLocalPlayer::refreshContainer(ContainerMenu* menu, const std::ve - - void MultiplayerLocalPlayer::slotChanged(ContainerMenu* menu, int index, ItemStack& item, bool isResultSlot) - { -- // @TODO: Replicate ContainerSetSlotPacket -+#if NETWORK_PROTOCOL_VERSION >= 5 -+ m_pMinecraft->m_pRakNetInstance->send(new ContainerSetSlotPacket(m_pContainerMenu->m_containerId, index, item)); -+#endif - } -diff --git a/source/client/multiplayer/MultiplayerLocalPlayer.hpp b/source/client/multiplayer/MultiplayerLocalPlayer.hpp -index d1a7e2bec..6ab1a580b 100644 ---- a/source/client/multiplayer/MultiplayerLocalPlayer.hpp -+++ b/source/client/multiplayer/MultiplayerLocalPlayer.hpp -@@ -1,7 +1,7 @@ - #pragma once - - #include "client/player/LocalPlayer.hpp" --#include "world/ContainerListener.hpp" -+#include "world/inventory/ContainerListener.hpp" - - class MultiplayerLocalPlayer : public LocalPlayer, public ContainerListener - { -@@ -10,6 +10,7 @@ class MultiplayerLocalPlayer : public LocalPlayer, public ContainerListener - - protected: - void reallyDrop(ItemEntity* itemEntity) override; -+ void _handleOpenedContainerMenu() override; - - public: - bool hurt(Entity*, int) override; -diff --git a/source/client/network/ClientSideNetworkHandler.cpp b/source/client/network/ClientSideNetworkHandler.cpp -index a1392ef6e..5079976d5 100644 ---- a/source/client/network/ClientSideNetworkHandler.cpp -+++ b/source/client/network/ClientSideNetworkHandler.cpp -@@ -777,7 +777,9 @@ void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& guid, ContainerS - if (pContainerMenu->m_containerId != packet->m_containerId) - return; - -+ pContainerMenu->m_bBroadcastChanges = false; - pContainerMenu->setItem(packet->m_slot, packet->m_item); -+ pContainerMenu->m_bBroadcastChanges = true; - } - - void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& guid, ContainerSetDataPacket* packet) -@@ -798,7 +800,9 @@ void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& guid, ContainerS - if (pContainerMenu->m_containerId != packet->m_containerId) - return; - -+ pContainerMenu->m_bBroadcastChanges = false; - pContainerMenu->setData(packet->m_slot, packet->m_value); -+ pContainerMenu->m_bBroadcastChanges = true; - } - - void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& guid, ContainerSetContentPacket* packet) -@@ -819,7 +823,9 @@ void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& guid, ContainerS - if (pContainerMenu->m_containerId != packet->m_containerId) - return; - -+ pContainerMenu->m_bBroadcastChanges = false; - pContainerMenu->setAll(packet->m_items); -+ pContainerMenu->m_bBroadcastChanges = true; - } - - void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& guid, LevelDataPacket* packet) -diff --git a/source/client/player/LocalPlayer.cpp b/source/client/player/LocalPlayer.cpp -index 6ca19d378..787d65806 100644 ---- a/source/client/player/LocalPlayer.cpp -+++ b/source/client/player/LocalPlayer.cpp -@@ -133,18 +133,24 @@ void LocalPlayer::swing() - void LocalPlayer::startCrafting(const TilePos& pos) - { - m_pMinecraft->getScreenChooser()->pushCraftingScreen(this, pos); -+ -+ Player::startCrafting(pos); - } - - void LocalPlayer::openFurnace(FurnaceTileEntity* furnace) - { - // PE 0.3.2 doesn't let you cook in creative mode - m_pMinecraft->getScreenChooser()->pushFurnaceScreen(this, furnace); -+ -+ Player::openFurnace(furnace); - } - - void LocalPlayer::openContainer(Container* container) - { - // PE 0.3.2 doesn't let you open chests in creative mode - m_pMinecraft->getScreenChooser()->pushChestScreen(this, container); -+ -+ Player::openContainer(container); - } - - void LocalPlayer::closeContainer() -@@ -157,11 +163,15 @@ void LocalPlayer::closeContainer() - /*void LocalPlayer::openTrap(DispenserTileEntity* tileEntity) - { - m_pMinecraft->setScreen(new TrapScreen(m_pInventory, tileEntity)); -+ -+ Player::openTrap(tileEntity); - }*/ - - /*void LocalPlayer::openTextEdit(SignTileEntity* tileEntity) - { - m_pMinecraft->setScreen(new TextEditScreen(tileEntity)); -+ -+ Player::openTextEdit(tileEntity); - }*/ - - void LocalPlayer::reset() -diff --git a/source/network/Packet.hpp b/source/network/Packet.hpp -index 4825bee6f..4d7a538d5 100644 ---- a/source/network/Packet.hpp -+++ b/source/network/Packet.hpp -@@ -19,6 +19,7 @@ - //#define NETWORK_PROTOCOL_VERSION 4 // 0.3.0 - //#define NETWORK_PROTOCOL_VERSION 5 // 0.3.2 - #define NETWORK_PROTOCOL_VERSION 6 // 0.3.3 -+//#define NETWORK_PROTOCOL_VERSION 29 // 0.12.1 - - class NetEventCallback; - class Level; -diff --git a/source/network/PacketUtil.cpp b/source/network/PacketUtil.cpp -index f6000cb85..8be6f9bbe 100644 ---- a/source/network/PacketUtil.cpp -+++ b/source/network/PacketUtil.cpp -@@ -1,4 +1,5 @@ - #include "PacketUtil.hpp" -+#include "Packet.hpp" - #include "nbt/NbtIo.hpp" - - char PacketUtil::Rot_degreesToChar(float degrees) -@@ -146,12 +147,14 @@ void PacketUtil::WriteItemStack(const ItemStack& item, RakNet::BitStream& bs, bo - int16_t itemId = item.getId(); - int8_t count = item.m_count; - int16_t auxValue = item.getAuxValue(); -+#if NETWORK_PROTOCOL_VERSION >= 29 - if (itemId <= 0) - { - itemId = -1; - bs.Write(itemId); - return; - } -+#endif - - bs.Write(itemId); - bs.Write(count); -@@ -166,8 +169,10 @@ ItemStack PacketUtil::ReadItemStack(RakNet::BitStream& bs, bool doUserData) - if (!bs.Read(itemId)) - return ItemStack(); - -+#if NETWORK_PROTOCOL_VERSION >= 29 - if (itemId == ItemStack::EMPTY.getId()) - return ItemStack(); -+#endif - - uint8_t count; - int16_t auxValue; -diff --git a/source/network/packets/ContainerOpenPacket.hpp b/source/network/packets/ContainerOpenPacket.hpp -index 11fc81f6c..648d6569b 100644 ---- a/source/network/packets/ContainerOpenPacket.hpp -+++ b/source/network/packets/ContainerOpenPacket.hpp -@@ -2,7 +2,7 @@ - - #include - #include "../Packet.hpp" --#include "world/Container.hpp" -+#include "world/inventory/Container.hpp" - - class ContainerOpenPacket : public Packet - { -diff --git a/source/network/packets/ContainerSetContentPacket.cpp b/source/network/packets/ContainerSetContentPacket.cpp -index 02b5b07b5..cc1552065 100644 ---- a/source/network/packets/ContainerSetContentPacket.cpp -+++ b/source/network/packets/ContainerSetContentPacket.cpp -@@ -30,7 +30,7 @@ void ContainerSetContentPacket::read(RakNet::BitStream& bs) - bs.Read(m_containerId); - int16_t size = 0; - bs.Read(size); -- m_items.resize(size); -+ m_items.reserve(size); - - for (uint16_t i = 0; i < size; i++) - { -diff --git a/source/server/ServerPlayer.cpp b/source/server/ServerPlayer.cpp -index 8a0aad136..a2a5d7cc5 100644 ---- a/source/server/ServerPlayer.cpp -+++ b/source/server/ServerPlayer.cpp -@@ -24,6 +24,11 @@ ServerPlayer::ServerPlayer(Level* pLevel, GameType playerGameType) - m_pInventoryMenu->addSlotListener(this); - } - -+ServerPlayer::~ServerPlayer() -+{ -+ doCloseContainer(); -+} -+ - void ServerPlayer::_nextContainerCounter() - { - m_containerId++; -@@ -156,5 +161,6 @@ void ServerPlayer::setContainerMenu(ContainerMenu* menu) - { - m_pContainerMenu->m_containerId = m_containerId; - m_pContainerMenu->addSlotListener(this); -+ refreshContainer(m_pContainerMenu, m_pContainerMenu->cloneItems()); - } - } -\ No newline at end of file -diff --git a/source/server/ServerPlayer.hpp b/source/server/ServerPlayer.hpp -index 11152a123..874a0f1cc 100644 ---- a/source/server/ServerPlayer.hpp -+++ b/source/server/ServerPlayer.hpp -@@ -1,10 +1,11 @@ - #include "world/entity/Player.hpp" --#include "world/ContainerListener.hpp" -+#include "world/inventory/ContainerListener.hpp" - - class ServerPlayer : public Player, public ContainerListener - { - public: - ServerPlayer(Level* pLevel, GameType playerGameType); -+ ~ServerPlayer(); - - protected: - void _nextContainerCounter(); -diff --git a/source/world/Container.hpp b/source/world/Container.hpp -deleted file mode 100644 -index 8c18f3a6c..000000000 ---- a/source/world/Container.hpp -+++ /dev/null -@@ -1,37 +0,0 @@ --#pragma once -- --#include "world/item/ItemStack.hpp" -- --#define C_MAX_CONTAINER_STACK_SIZE (64) -- --class Container --{ --public: -- enum Type -- { -- CONTAINER, -- CRAFTING, -- FURNACE, -- DISPENSER -- }; -- --public: -- virtual uint16_t getContainerSize() const = 0; -- virtual ItemStack& getItem(int index) = 0; -- virtual ItemStack* tryGetItem(int index) -- { -- if (index >= 0 && index < getContainerSize()) -- return &getItem(index); -- else -- return nullptr; -- } -- virtual ItemStack removeItem(int index, int count) = 0; -- virtual void setItem(int index, const ItemStack& item) = 0; -- virtual std::string getName() const = 0; -- virtual int getMaxStackSize() -- { -- return C_MAX_CONTAINER_STACK_SIZE; -- } -- virtual void setChanged() = 0; -- virtual bool stillValid(Player* player) const = 0; --}; -\ No newline at end of file -diff --git a/source/world/entity/Player.cpp b/source/world/entity/Player.cpp -index 95193e007..2ce2c9012 100644 ---- a/source/world/entity/Player.cpp -+++ b/source/world/entity/Player.cpp -@@ -63,8 +63,8 @@ Player::Player(Level* pLevel, GameType playerGameType) : Mob(pLevel) - - Player::~Player() - { -- delete m_pInventory; - delete m_pInventoryMenu; -+ delete m_pInventory; - } - - void Player::reallyDrop(ItemEntity* pEnt) -@@ -72,6 +72,10 @@ void Player::reallyDrop(ItemEntity* pEnt) - m_pLevel->addEntity(pEnt); - } - -+void Player::_handleOpenedContainerMenu() -+{ -+} -+ - void Player::reset() - { - Mob::reset(); -@@ -563,14 +567,12 @@ void Player::drop(const ItemStack& item, bool randomly) - - void Player::startCrafting(const TilePos& pos) - { --} -- --void Player::openFurnace(FurnaceTileEntity* tileEntity) --{ -+ _handleOpenedContainerMenu(); - } - - void Player::startStonecutting(const TilePos& pos) - { -+ _handleOpenedContainerMenu(); - } - - void Player::startDestroying() -@@ -583,6 +585,20 @@ void Player::stopDestroying() - m_destroyingBlock = false; - } - -+void Player::openFurnace(FurnaceTileEntity* tileEntity) -+{ -+ _handleOpenedContainerMenu(); -+} -+ -+void Player::openContainer(Container* container) -+{ -+ _handleOpenedContainerMenu(); -+} -+ -+void Player::closeContainer() -+{ -+} -+ - void Player::touch(Entity* pEnt) - { - pEnt->playerTouch(this); -diff --git a/source/world/entity/Player.hpp b/source/world/entity/Player.hpp -index 3ba5345a2..c2c5dcdd2 100644 ---- a/source/world/entity/Player.hpp -+++ b/source/world/entity/Player.hpp -@@ -41,6 +41,7 @@ class Player : public Mob - - protected: - virtual void reallyDrop(ItemEntity* pEnt); -+ virtual void _handleOpenedContainerMenu(); - - public: - void reset() override; -@@ -74,8 +75,8 @@ class Player : public Mob - virtual void startDestroying(); - virtual void stopDestroying(); - virtual void openFurnace(FurnaceTileEntity* tileEntity); -- virtual void openContainer(Container* container) {} -- virtual void closeContainer() {} -+ virtual void openContainer(Container* container); -+ virtual void closeContainer(); - //virtual void openTrap(DispenserTileEntity* tileEntity); - //virtual void openTextEdit(SignTileEntity* tileEntity); - virtual bool isLocalPlayer() const { return false; } -diff --git a/source/world/inventory/ArmorSlot.hpp b/source/world/inventory/ArmorSlot.hpp -index 2b679e8d9..0e54f8ab5 100644 ---- a/source/world/inventory/ArmorSlot.hpp -+++ b/source/world/inventory/ArmorSlot.hpp -@@ -1,7 +1,7 @@ - #pragma once - - #include "Slot.hpp" --#include "world/Container.hpp" -+#include "Container.hpp" - - class ArmorSlot : public Slot - { -diff --git a/source/world/inventory/ChestMenu.hpp b/source/world/inventory/ChestMenu.hpp -index ea787bea3..04eb68da5 100644 ---- a/source/world/inventory/ChestMenu.hpp -+++ b/source/world/inventory/ChestMenu.hpp -@@ -1,7 +1,7 @@ - #pragma once - - #include "ContainerMenu.hpp" --#include "world/Container.hpp" -+#include "Container.hpp" - #include "world/entity/Player.hpp" - - class ChestMenu : public ContainerMenu -diff --git a/source/world/CompoundContainer.cpp b/source/world/inventory/CompoundContainer.cpp -similarity index 90% -rename from source/world/CompoundContainer.cpp -rename to source/world/inventory/CompoundContainer.cpp -index 80e339b90..ea4d4dd1c 100644 ---- a/source/world/CompoundContainer.cpp -+++ b/source/world/inventory/CompoundContainer.cpp -@@ -44,10 +44,10 @@ int CompoundContainer::getMaxStackSize() - return m_pLeftContainer->getMaxStackSize(); - } - --void CompoundContainer::setChanged() -+void CompoundContainer::setContainerChanged(SlotID slot) - { -- m_pLeftContainer->setChanged(); -- m_pRightContainer->setChanged(); -+ m_pLeftContainer->setContainerChanged(slot); -+ m_pRightContainer->setContainerChanged(slot); - } - - bool CompoundContainer::stillValid(Player* player) const -diff --git a/source/world/CompoundContainer.hpp b/source/world/inventory/CompoundContainer.hpp -similarity index 92% -rename from source/world/CompoundContainer.hpp -rename to source/world/inventory/CompoundContainer.hpp -index 5a4884591..1c3fd0fea 100644 ---- a/source/world/CompoundContainer.hpp -+++ b/source/world/inventory/CompoundContainer.hpp -@@ -25,7 +25,7 @@ class CompoundContainer : public Container - - int getMaxStackSize() override; - -- void setChanged() override; -+ void setContainerChanged(SlotID slot) override; - - bool stillValid(Player* player) const override; - }; -diff --git a/source/world/inventory/Container.hpp b/source/world/inventory/Container.hpp -new file mode 100644 -index 000000000..6e6956689 ---- /dev/null -+++ b/source/world/inventory/Container.hpp -@@ -0,0 +1,55 @@ -+#pragma once -+ -+#include -+ -+#include "world/item/ItemStack.hpp" -+ -+#define C_MAX_CONTAINER_STACK_SIZE (64) -+ -+class ContainerContentChangeListener; -+class ContainerSizeChangeListener; -+ -+class Container -+{ -+protected: -+ typedef std::set ContentChangeListeners; -+ typedef std::set SizeChangeListeners; -+ -+public: -+ typedef uint16_t Size; -+ -+public: -+ enum Type -+ { -+ CONTAINER, -+ CRAFTING, -+ FURNACE, -+ DISPENSER -+ }; -+ -+public: -+ virtual Size getContainerSize() const = 0; -+ virtual ItemStack& getItem(int index) = 0; -+ virtual ItemStack* tryGetItem(int index) -+ { -+ if (index >= 0 && index < getContainerSize()) -+ return &getItem(index); -+ else -+ return nullptr; -+ } -+ virtual ItemStack removeItem(int index, int count) = 0; -+ virtual void setItem(int index, const ItemStack& item) = 0; -+ virtual std::string getName() const = 0; -+ virtual int getMaxStackSize() -+ { -+ return C_MAX_CONTAINER_STACK_SIZE; -+ } -+ // Was called setChanged in Java -+ virtual void setContainerChanged(SlotID slot) = 0; -+ virtual bool stillValid(Player* player) const = 0; -+ -+ virtual void addContentChangeListener(ContainerContentChangeListener* listener) {} -+ virtual void addSizeChangeListener(ContainerSizeChangeListener* listener) {} -+ virtual void removeContentChangeListener(ContainerContentChangeListener* listener) {} -+ virtual void removeSizeChangeListener(ContainerSizeChangeListener* listener) {} -+}; -\ No newline at end of file -diff --git a/source/world/inventory/ContainerContentChangeListener.cpp b/source/world/inventory/ContainerContentChangeListener.cpp -new file mode 100644 -index 000000000..bb72498aa ---- /dev/null -+++ b/source/world/inventory/ContainerContentChangeListener.cpp -@@ -0,0 +1,2 @@ -+#include "ContainerContentChangeListener.hpp" -+ -diff --git a/source/world/inventory/ContainerContentChangeListener.hpp b/source/world/inventory/ContainerContentChangeListener.hpp -new file mode 100644 -index 000000000..675f1b221 ---- /dev/null -+++ b/source/world/inventory/ContainerContentChangeListener.hpp -@@ -0,0 +1,12 @@ -+#pragma once -+#include -+#include "Container.hpp" -+ -+class ContainerContentChangeListener -+{ -+public: -+ virtual ~ContainerContentChangeListener() {} -+ -+public: -+ virtual void containerContentChanged(SlotID slot) = 0; -+}; -diff --git a/source/world/ContainerListener.cpp b/source/world/inventory/ContainerListener.cpp -similarity index 81% -rename from source/world/ContainerListener.cpp -rename to source/world/inventory/ContainerListener.cpp -index ddfc041fa..d0377bd7c 100644 ---- a/source/world/ContainerListener.cpp -+++ b/source/world/inventory/ContainerListener.cpp -@@ -7,5 +7,5 @@ ContainerListener::~ContainerListener() - - void ContainerListener::refreshContainerItems(ContainerMenu* menu) - { -- refreshContainer(menu, menu->copyItems()); -+ refreshContainer(menu, menu->cloneItems()); - } -diff --git a/source/world/ContainerListener.hpp b/source/world/inventory/ContainerListener.hpp -similarity index 100% -rename from source/world/ContainerListener.hpp -rename to source/world/inventory/ContainerListener.hpp -diff --git a/source/world/inventory/ContainerMenu.cpp b/source/world/inventory/ContainerMenu.cpp -index f618561e3..7e52498df 100644 ---- a/source/world/inventory/ContainerMenu.cpp -+++ b/source/world/inventory/ContainerMenu.cpp -@@ -1,14 +1,15 @@ - #include "ContainerMenu.hpp" --#include "Slot.hpp" - #include "world/item/ItemStack.hpp" - #include "world/item/Inventory.hpp" --#include "world/Container.hpp" --#include "world/ContainerListener.hpp" -+#include "Slot.hpp" -+#include "Container.hpp" -+#include "ContainerListener.hpp" - - ContainerMenu::ContainerMenu(Container::Type containerType) - : m_changeUid(0) - , m_containerId(0) - , m_containerType(containerType) -+ , m_bBroadcastChanges(true) - { - } - -@@ -18,8 +19,24 @@ ContainerMenu::~ContainerMenu() - /*for (std::vector::iterator it = m_listeners.begin(); it != m_listeners.end(); ++it) - delete (*it);*/ - -+ _clearSlots(); -+} -+ -+void ContainerMenu::_clearSlots() -+{ - for (std::vector::iterator it = m_slots.begin(); it != m_slots.end(); ++it) -- delete (*it); -+ { -+ Slot* slot = *it; -+ -+ // @HACK: I don't like this -+ Container* pContainer = slot->m_pContainer; -+ if (pContainer) -+ pContainer->removeContentChangeListener(this); -+ -+ delete slot; -+ } -+ -+ m_slots.clear(); - } - - void ContainerMenu::addSlot(Slot* slot) -@@ -27,35 +44,45 @@ void ContainerMenu::addSlot(Slot* slot) - slot->m_index = m_slots.size(); - m_slots.push_back(slot); - m_lastSlots.push_back(ItemStack::EMPTY); -+ -+ // @HACK: holy hack -+ Container* pContainer = slot->m_pContainer; -+ if (pContainer) -+ pContainer->addContentChangeListener(this); - } - - void ContainerMenu::addSlotListener(ContainerListener* listener) - { -- m_listeners.push_back(listener); -+ m_listeners.insert(listener); - - // Not done on PE -- /*std::vector snapshot = copyItems(); -+ /*std::vector snapshot = cloneItems(); - listener->refreshContainer(this, snapshot); - broadcastChanges();*/ - } - - void ContainerMenu::sendData(int id, int value) - { -- for (std::vector::iterator it = m_listeners.begin(); it != m_listeners.end(); ++it) -+ for (ContainerListeners::iterator it = m_listeners.begin(); it != m_listeners.end(); ++it) - (*it)->setContainerData(this, id, value); - } - -+void ContainerMenu::broadcastChanges(SlotID slot) -+{ -+ ItemStack& current = m_slots[slot]->getItem(); -+ if (m_lastSlots[slot] != current) -+ { -+ m_lastSlots[slot] = ItemStack(current); -+ for (ContainerListeners::iterator it = m_listeners.begin(); it != m_listeners.end(); ++it) -+ (*it)->slotChanged(this, slot, m_lastSlots[slot], isResultSlot()); -+ } -+} -+ - void ContainerMenu::broadcastChanges() - { - for (size_t i = 0; i < m_slots.size(); ++i) - { -- ItemStack& current = m_slots[i]->getItem(); -- if (m_lastSlots[i] != current) -- { -- m_lastSlots[i] = ItemStack(current); -- for (std::vector::iterator it = m_listeners.begin(); it != m_listeners.end(); ++it) -- (*it)->slotChanged(this, i, m_lastSlots[i], isResultSlot()); -- } -+ broadcastChanges(i); - } - } - -@@ -74,12 +101,15 @@ void ContainerMenu::slotsChanged(Container*) - broadcastChanges(); - } - --std::vector ContainerMenu::copyItems() -+std::vector ContainerMenu::cloneItems() - { - std::vector content; - - for (std::vector::iterator it = m_slots.begin(); it != m_slots.end(); ++it) -- content.push_back((*it)->getItem()); -+ { -+ const ItemStack& item = (*it)->getItem(); -+ content.push_back(item); -+ } - - return content; - } -@@ -358,13 +388,19 @@ void ContainerMenu::rollbackToBackup(uint16_t) - - bool ContainerMenu::isSynched(Player* player) const - { -- return unsynchedPlayers.find(player) == unsynchedPlayers.end(); -+ return m_unsynchedPlayers.find(player) == m_unsynchedPlayers.end(); - } - - void ContainerMenu::setSynched(Player* player, bool isSynched) - { - if (isSynched) -- unsynchedPlayers.erase(player); -+ m_unsynchedPlayers.erase(player); - else -- unsynchedPlayers.insert(player); -+ m_unsynchedPlayers.insert(player); -+} -+ -+void ContainerMenu::containerContentChanged(SlotID slot) -+{ -+ if (m_bBroadcastChanges) -+ broadcastChanges(slot); - } -diff --git a/source/world/inventory/ContainerMenu.hpp b/source/world/inventory/ContainerMenu.hpp -index f7b299806..481d199be 100644 ---- a/source/world/inventory/ContainerMenu.hpp -+++ b/source/world/inventory/ContainerMenu.hpp -@@ -3,30 +3,38 @@ - #include - #include - #include "world/item/ItemStack.hpp" --#include "world/Container.hpp" - #include "client/player/input/MouseDevice.hpp" -+#include "Container.hpp" -+#include "ContainerContentChangeListener.hpp" - - class Player; - class Inventory; - class Slot; - class ContainerListener; - --class ContainerMenu -+class ContainerMenu : public ContainerContentChangeListener - { -+protected: -+ typedef std::set ContainerListeners; -+ - public: - ContainerMenu(Container::Type containerType); - virtual ~ContainerMenu(); - -+protected: -+ void _clearSlots(); -+ - public: - void addSlot(Slot* slot); - virtual void addSlotListener(ContainerListener* listener); - void sendData(int id, int value); -+ virtual void broadcastChanges(SlotID slot); - virtual void broadcastChanges(); - virtual void removed(Player* player); - virtual void slotsChanged(Container* container); - - // Called getItems in PE and Java -- std::vector copyItems(); -+ std::vector cloneItems(); - Slot* getSlotFor(Container* container, int index); - Slot* getSlot(int index); - virtual ItemStack clicked(int slotIndex, MouseButtonType mouseButton, bool quickMove, Player* player); -@@ -50,14 +58,18 @@ class ContainerMenu - //Unused - virtual bool isPauseScreen() const { return false; } - -+public: -+ void containerContentChanged(SlotID slot) override; -+ - protected: - std::vector m_lastSlots; - uint16_t m_changeUid; -- std::vector m_listeners; -- std::set unsynchedPlayers; -+ ContainerListeners m_listeners; -+ std::set m_unsynchedPlayers; - - public: - int m_containerId; - Container::Type m_containerType; - std::vector m_slots; -+ bool m_bBroadcastChanges; - }; -diff --git a/source/world/inventory/ContainerSizeChangeListener.cpp b/source/world/inventory/ContainerSizeChangeListener.cpp -new file mode 100644 -index 000000000..671ca58bd ---- /dev/null -+++ b/source/world/inventory/ContainerSizeChangeListener.cpp -@@ -0,0 +1 @@ -+#include "ContainerSizeChangeListener.hpp" -diff --git a/source/world/inventory/ContainerSizeChangeListener.hpp b/source/world/inventory/ContainerSizeChangeListener.hpp -new file mode 100644 -index 000000000..f39a00780 ---- /dev/null -+++ b/source/world/inventory/ContainerSizeChangeListener.hpp -@@ -0,0 +1,12 @@ -+#pragma once -+#include -+#include "Container.hpp" -+ -+class ContainerSizeChangeListener -+{ -+public: -+ virtual ~ContainerSizeChangeListener() {} -+ -+public: -+ virtual void containerSizeChanged(Container::Size size) = 0; -+}; -diff --git a/source/world/inventory/CraftingContainer.cpp b/source/world/inventory/CraftingContainer.cpp -index 548c6db7b..edfc5a04c 100644 ---- a/source/world/inventory/CraftingContainer.cpp -+++ b/source/world/inventory/CraftingContainer.cpp -@@ -73,7 +73,7 @@ void CraftingContainer::setItem(int index, const ItemStack& item) - } - } - --void CraftingContainer::setChanged() -+void CraftingContainer::setContainerChanged(SlotID slot) - { - } - -diff --git a/source/world/inventory/CraftingContainer.hpp b/source/world/inventory/CraftingContainer.hpp -index 5fecc7d91..f450e2c9f 100644 ---- a/source/world/inventory/CraftingContainer.hpp -+++ b/source/world/inventory/CraftingContainer.hpp -@@ -1,8 +1,8 @@ - #pragma once - - #include --#include "world/Container.hpp" - #include "world/item/ItemStack.hpp" -+#include "Container.hpp" - #include "ContainerMenu.hpp" - - class ContainerMenu; -@@ -25,7 +25,7 @@ class CraftingContainer : public Container - ItemStack removeItem(int index, int amount) override; - void setItem(int index, const ItemStack& item) override; - -- void setChanged() override; -+ void setContainerChanged(SlotID slot) override; - bool stillValid(Player* player) const override; - - private: -diff --git a/source/world/inventory/CraftingMenu.cpp b/source/world/inventory/CraftingMenu.cpp -index aeb9d016b..728aad397 100644 ---- a/source/world/inventory/CraftingMenu.cpp -+++ b/source/world/inventory/CraftingMenu.cpp -@@ -36,6 +36,9 @@ CraftingMenu::CraftingMenu(Inventory* inventory, const TilePos& tilePos, Level* - - CraftingMenu::~CraftingMenu() - { -+ _clearSlots(); -+ -+ // clearSlots must be called before these are deleted - delete m_pCraftSlots; - delete m_pResultSlots; - } -diff --git a/source/world/inventory/FurnaceMenu.cpp b/source/world/inventory/FurnaceMenu.cpp -index e8f6678ac..b3c3cb2f3 100644 ---- a/source/world/inventory/FurnaceMenu.cpp -+++ b/source/world/inventory/FurnaceMenu.cpp -@@ -1,7 +1,7 @@ - #include "FurnaceMenu.hpp" - #include "Slot.hpp" - #include "FurnaceResultSlot.hpp" --#include "world/ContainerListener.hpp" -+#include "ContainerListener.hpp" - - FurnaceMenu::FurnaceMenu(Inventory* inventory, FurnaceTileEntity* furnace) - : ContainerMenu(Container::FURNACE), m_furnace(furnace), m_lastCookTime(0), m_lastBurnTime(0), m_lastLitDuration(0) -@@ -39,7 +39,7 @@ void FurnaceMenu::broadcastChanges() - { - ContainerMenu::broadcastChanges(); - -- for (std::vector::iterator it = m_listeners.begin(); it != m_listeners.end(); ++it) -+ for (ContainerListeners::iterator it = m_listeners.begin(); it != m_listeners.end(); ++it) - { - ContainerListener* listener = *it; - -diff --git a/source/world/inventory/FurnaceResultSlot.hpp b/source/world/inventory/FurnaceResultSlot.hpp -index 4c1e277a1..912b17000 100644 ---- a/source/world/inventory/FurnaceResultSlot.hpp -+++ b/source/world/inventory/FurnaceResultSlot.hpp -@@ -1,7 +1,7 @@ - #pragma once - - #include "Slot.hpp" --#include "source/world/Container.hpp" -+#include "Container.hpp" - - class FurnaceResultSlot : public Slot - { -diff --git a/source/world/inventory/InventoryMenu.cpp b/source/world/inventory/InventoryMenu.cpp -index 6c5d2a76f..1e5b2c37e 100644 ---- a/source/world/inventory/InventoryMenu.cpp -+++ b/source/world/inventory/InventoryMenu.cpp -@@ -42,6 +42,9 @@ InventoryMenu::InventoryMenu(Inventory* inventory, bool active) - - InventoryMenu::~InventoryMenu() - { -+ _clearSlots(); -+ -+ // clearSlots must be called before these are deleted - delete m_pCraftSlots; - delete m_pResultSlots; - } -diff --git a/source/world/inventory/ResultContainer.cpp b/source/world/inventory/ResultContainer.cpp -index 3f6763d53..1cf3be2d6 100644 ---- a/source/world/inventory/ResultContainer.cpp -+++ b/source/world/inventory/ResultContainer.cpp -@@ -40,7 +40,7 @@ void ResultContainer::setItem(int index, const ItemStack& item) - m_item = item; - } - --void ResultContainer::setChanged() -+void ResultContainer::setContainerChanged(SlotID slot) - { - } - -diff --git a/source/world/inventory/ResultContainer.hpp b/source/world/inventory/ResultContainer.hpp -index a3d95dc65..14228c1fb 100644 ---- a/source/world/inventory/ResultContainer.hpp -+++ b/source/world/inventory/ResultContainer.hpp -@@ -1,7 +1,7 @@ - #pragma once - --#include "world/Container.hpp" - #include "world/item/ItemStack.hpp" -+#include "Container.hpp" - - class Player; - -@@ -19,7 +19,7 @@ class ResultContainer : public Container - ItemStack removeItem(int index, int amount) override; - void setItem(int index, const ItemStack& item) override; - -- void setChanged() override; -+ void setContainerChanged(SlotID slot) override; - bool stillValid(Player* player) const override; - - private: -diff --git a/source/world/inventory/ResultSlot.hpp b/source/world/inventory/ResultSlot.hpp -index 92dd109e6..7da681e83 100644 ---- a/source/world/inventory/ResultSlot.hpp -+++ b/source/world/inventory/ResultSlot.hpp -@@ -1,7 +1,7 @@ - #pragma once - - #include "Slot.hpp" --#include "world/Container.hpp" -+#include "Container.hpp" - - class ResultSlot : public Slot - { -diff --git a/source/world/inventory/SimpleContainer.cpp b/source/world/inventory/SimpleContainer.cpp -index 74b8c4c5d..62d57425c 100644 ---- a/source/world/inventory/SimpleContainer.cpp -+++ b/source/world/inventory/SimpleContainer.cpp -@@ -1,8 +1,10 @@ - #include "SimpleContainer.hpp" -+#include "ContainerContentChangeListener.hpp" -+#include "ContainerSizeChangeListener.hpp" - --SimpleContainer::SimpleContainer(int size, const std::string& name) : -- m_items(size), -- m_name(name) -+SimpleContainer::SimpleContainer(int size, const std::string& name) -+ : m_items(size) -+ , m_name(name) - { - } - -@@ -25,7 +27,7 @@ ItemStack SimpleContainer::removeItem(int index, int count) - { - result = m_items[index]; - m_items[index] = ItemStack::EMPTY; -- setChanged(); -+ setContainerChanged(index); - return result; - } - else -@@ -34,7 +36,7 @@ ItemStack SimpleContainer::removeItem(int index, int count) - if (!m_items[index].m_count) - m_items[index] = ItemStack::EMPTY; - -- setChanged(); -+ setContainerChanged(index); - return result; - } - } -@@ -47,7 +49,7 @@ void SimpleContainer::setItem(int index, const ItemStack& item) - if (!item.isEmpty() && item.m_count > getMaxStackSize()) - m_items[index].m_count = getMaxStackSize(); - -- setChanged(); -+ setContainerChanged(index); - } - - std::string SimpleContainer::getName() const -@@ -55,8 +57,13 @@ std::string SimpleContainer::getName() const - return m_name; - } - --void SimpleContainer::setChanged() -+void SimpleContainer::setContainerChanged(SlotID slot) - { -+ for (ContentChangeListeners::iterator it = m_contentChangeListeners.begin(); it != m_contentChangeListeners.end(); it++) -+ { -+ ContainerContentChangeListener* pListener = *it; -+ pListener->containerContentChanged(slot); -+ } - } - - bool SimpleContainer::stillValid(Player* player) const -@@ -64,6 +71,31 @@ bool SimpleContainer::stillValid(Player* player) const - return true; - } - -+void SimpleContainer::addContentChangeListener(ContainerContentChangeListener* listener) -+{ -+ m_contentChangeListeners.insert(listener); -+} -+ -+void SimpleContainer::addSizeChangeListener(ContainerSizeChangeListener* listener) -+{ -+ m_sizeChangeListeners.insert(listener); -+} -+ -+void SimpleContainer::removeContentChangeListener(ContainerContentChangeListener* listener) -+{ -+ m_contentChangeListeners.erase(listener); -+} -+ -+void SimpleContainer::removeSizeChangeListener(ContainerSizeChangeListener* listener) -+{ -+ m_sizeChangeListeners.erase(listener); -+} -+ -+void SimpleContainer::clear() -+{ -+ std::fill(m_items.begin(), m_items.end(), ItemStack::EMPTY); -+} -+ - void SimpleContainer::load(const CompoundTag& tag) - { - clear(); -@@ -102,8 +134,3 @@ void SimpleContainer::save(CompoundTag& tag) const - - tag.put("Items", list); - } -- --void SimpleContainer::clear() --{ -- std::fill(m_items.begin(), m_items.end(), ItemStack::EMPTY); --} -\ No newline at end of file -diff --git a/source/world/inventory/SimpleContainer.hpp b/source/world/inventory/SimpleContainer.hpp -index a371d2532..2df4756a4 100644 ---- a/source/world/inventory/SimpleContainer.hpp -+++ b/source/world/inventory/SimpleContainer.hpp -@@ -1,9 +1,10 @@ - #pragma once - --#include "world/Container.hpp" --#include "nbt/CompoundTag.hpp" - #include - -+#include "Container.hpp" -+#include "nbt/CompoundTag.hpp" -+ - class SimpleContainer : public Container - { - public: -@@ -15,15 +16,21 @@ class SimpleContainer : public Container - ItemStack removeItem(int index, int count) override; - void setItem(int index, const ItemStack& item) override; - std::string getName() const override; -- void setChanged() override; -+ void setContainerChanged(SlotID slot) override; - bool stillValid(Player* player) const override; -+ void addContentChangeListener(ContainerContentChangeListener* listener) override; -+ void addSizeChangeListener(ContainerSizeChangeListener* listener) override; -+ void removeContentChangeListener(ContainerContentChangeListener* listener) override; -+ void removeSizeChangeListener(ContainerSizeChangeListener* listener) override; - - public: - virtual void clear(); - virtual void load(const CompoundTag& tag); - virtual void save(CompoundTag& tag) const; - --private: -+protected: -+ ContentChangeListeners m_contentChangeListeners; -+ SizeChangeListeners m_sizeChangeListeners; - std::vector m_items; - std::string m_name; - }; -\ No newline at end of file -diff --git a/source/world/inventory/Slot.hpp b/source/world/inventory/Slot.hpp -index 12a033823..f4286639d 100644 ---- a/source/world/inventory/Slot.hpp -+++ b/source/world/inventory/Slot.hpp -@@ -1,6 +1,6 @@ - #pragma once - --#include "world/Container.hpp" -+#include "Container.hpp" - - class ItemStack; - -@@ -32,7 +32,7 @@ class Slot - - virtual void set(const ItemStack& item); - -- virtual void setChanged() { m_pContainer->setChanged(); } -+ virtual void setChanged() { m_pContainer->setContainerChanged(m_slot); } - - virtual int getMaxStackSize() const { return m_pContainer->getMaxStackSize(); } - -@@ -42,7 +42,7 @@ class Slot - - public: - Container* m_pContainer; -- int m_slot; -- int m_index; -+ int m_slot; // the position in the attached container -+ int m_index; // the position in the ContainerMenu::m_slots vector - Group m_group; - }; -\ No newline at end of file -diff --git a/source/world/item/Inventory.hpp b/source/world/item/Inventory.hpp -index 568a1e54a..876d06183 100644 ---- a/source/world/item/Inventory.hpp -+++ b/source/world/item/Inventory.hpp -@@ -1,8 +1,8 @@ - #pragma once - - #include --#include "world/Container.hpp" - #include "GameMods.hpp" -+#include "world/inventory/Container.hpp" - #include "world/item/ItemStack.hpp" - #include "world/entity/Player.hpp" - #include "world/gamemode/GameType.hpp" -@@ -80,9 +80,9 @@ class Inventory : public Container - return "Inventory"; - } - -- void setChanged() override { } -+ void setContainerChanged(SlotID slot) override { } - -- bool stillValid(Player* player) const override { return true; } -+ bool stillValid(Player* player) const override { return true; } - - private: - GameType _getGameMode() const; -diff --git a/source/world/item/crafting/Recipe.hpp b/source/world/item/crafting/Recipe.hpp -index 6bb7a40dd..169d83a17 100644 ---- a/source/world/item/crafting/Recipe.hpp -+++ b/source/world/item/crafting/Recipe.hpp -@@ -1,6 +1,6 @@ - #pragma once - --#include "world/Container.hpp" -+#include "world/inventory/Container.hpp" - - class Recipe - { -diff --git a/source/world/tile/ChestTile.cpp b/source/world/tile/ChestTile.cpp -index e7aad4afa..6db79d413 100644 ---- a/source/world/tile/ChestTile.cpp -+++ b/source/world/tile/ChestTile.cpp -@@ -1,6 +1,6 @@ - #include "ChestTile.hpp" - #include "world/level/Level.hpp" --#include "world/CompoundContainer.hpp" -+#include "world/inventory/CompoundContainer.hpp" - #include "world/tile/entity/ChestTileEntity.hpp" - - ChestTile::ChestTile(int id, int texture) : EntityTile(id, texture, Material::wood) -diff --git a/source/world/tile/entity/ChestTileEntity.cpp b/source/world/tile/entity/ChestTileEntity.cpp -index f72f78a4c..323cfa7eb 100644 ---- a/source/world/tile/entity/ChestTileEntity.cpp -+++ b/source/world/tile/entity/ChestTileEntity.cpp -@@ -25,7 +25,7 @@ bool ChestTileEntity::stillValid(Player* player) const - return player->distanceToSqr(m_pos + 0.5f) <= 64.0f; - } - --void ChestTileEntity::setChanged() -+void ChestTileEntity::setContainerChanged(SlotID slot) - { - TileEntity::setChanged(); - } -diff --git a/source/world/tile/entity/ChestTileEntity.hpp b/source/world/tile/entity/ChestTileEntity.hpp -index 38137f631..0fb177245 100644 ---- a/source/world/tile/entity/ChestTileEntity.hpp -+++ b/source/world/tile/entity/ChestTileEntity.hpp -@@ -13,5 +13,5 @@ class ChestTileEntity : public TileEntity, public SimpleContainer { - - bool stillValid(Player* player) const override; - -- virtual void setChanged() override; -+ void setContainerChanged(SlotID slot) override; - }; -diff --git a/source/world/tile/entity/FurnaceTileEntity.cpp b/source/world/tile/entity/FurnaceTileEntity.cpp -index 1134d2736..28e7ead28 100644 ---- a/source/world/tile/entity/FurnaceTileEntity.cpp -+++ b/source/world/tile/entity/FurnaceTileEntity.cpp -@@ -116,8 +116,9 @@ bool FurnaceTileEntity::stillValid(Player* player) const - return player->distanceToSqr(m_pos + 0.5f) <= 64.0; - } - --void FurnaceTileEntity::setChanged() -+void FurnaceTileEntity::setContainerChanged(SlotID slot) - { -+ SimpleContainer::setContainerChanged(slot); - TileEntity::setChanged(); - } - -diff --git a/source/world/tile/entity/FurnaceTileEntity.hpp b/source/world/tile/entity/FurnaceTileEntity.hpp -index 55108282f..0a50b691e 100644 ---- a/source/world/tile/entity/FurnaceTileEntity.hpp -+++ b/source/world/tile/entity/FurnaceTileEntity.hpp -@@ -16,7 +16,7 @@ class FurnaceTileEntity : public SimpleContainer, public TileEntity - public: - void tick() override; - bool stillValid(Player* player) const override; -- void setChanged() override; -+ void setContainerChanged(SlotID slot) override; - void load(const CompoundTag& tag) override; - void save(CompoundTag& tag) const override; - std::string getName() const override; - -From 02783ae6b725a90e35b4da109644cc57d0c013d7 Mon Sep 17 00:00:00 2001 -From: Sam -Date: Wed, 18 Feb 2026 08:49:39 -0500 -Subject: [PATCH 21/22] CMakeLists & Xcode - ---- - .../Minecraft.xcodeproj/project.pbxproj | 56 ++++++++++++------- - source/CMakeLists.txt | 6 +- - 2 files changed, 40 insertions(+), 22 deletions(-) - -diff --git a/platforms/macos/projects/Minecraft/Minecraft.xcodeproj/project.pbxproj b/platforms/macos/projects/Minecraft/Minecraft.xcodeproj/project.pbxproj -index dcc3c8727..7600da5f0 100644 ---- a/platforms/macos/projects/Minecraft/Minecraft.xcodeproj/project.pbxproj -+++ b/platforms/macos/projects/Minecraft/Minecraft.xcodeproj/project.pbxproj -@@ -989,11 +989,6 @@ - 84E7BF272F286A08002D3936 /* SimpleContainer.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84E7BF172F286A08002D3936 /* SimpleContainer.hpp */; }; - 84E7BF282F286A08002D3936 /* Slot.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84E7BF182F286A08002D3936 /* Slot.cpp */; }; - 84E7BF292F286A08002D3936 /* Slot.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84E7BF192F286A08002D3936 /* Slot.hpp */; }; -- 84E7BF2F2F286A20002D3936 /* CompoundContainer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84E7BF2A2F286A20002D3936 /* CompoundContainer.cpp */; }; -- 84E7BF302F286A20002D3936 /* CompoundContainer.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84E7BF2B2F286A20002D3936 /* CompoundContainer.hpp */; }; -- 84E7BF312F286A20002D3936 /* Container.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84E7BF2C2F286A20002D3936 /* Container.hpp */; }; -- 84E7BF322F286A20002D3936 /* ContainerListener.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84E7BF2D2F286A20002D3936 /* ContainerListener.cpp */; }; -- 84E7BF332F286A20002D3936 /* ContainerListener.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84E7BF2E2F286A20002D3936 /* ContainerListener.hpp */; }; - 84E7BF362F286A6A002D3936 /* ItemStack.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84E7BF342F286A6A002D3936 /* ItemStack.cpp */; }; - 84E7BF372F286A6A002D3936 /* ItemStack.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84E7BF352F286A6A002D3936 /* ItemStack.hpp */; }; - 84E8DCE52E84ACBC00B30789 /* libRenderer.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 8406FD292AF1814500B09C1D /* libRenderer.a */; }; -@@ -1287,6 +1282,15 @@ - 84EEED642F0A01B700F741B6 /* ViewportOrigin.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84EEED622F0A01B700F741B6 /* ViewportOrigin.hpp */; }; - 84EEED682F0A02F300F741B6 /* ErrorHandlerOGL.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84EEED662F0A02F300F741B6 /* ErrorHandlerOGL.cpp */; }; - 84EEED692F0A02F300F741B6 /* ErrorHandlerOGL.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84EEED672F0A02F300F741B6 /* ErrorHandlerOGL.hpp */; }; -+ 84F0DA762F45FAE100906960 /* ContainerContentChangeListener.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84F0DA702F45FAE100906960 /* ContainerContentChangeListener.hpp */; }; -+ 84F0DA772F45FAE100906960 /* ContainerListener.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84F0DA722F45FAE100906960 /* ContainerListener.hpp */; }; -+ 84F0DA782F45FAE100906960 /* Container.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84F0DA6F2F45FAE100906960 /* Container.hpp */; }; -+ 84F0DA792F45FAE100906960 /* CompoundContainer.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84F0DA6D2F45FAE100906960 /* CompoundContainer.hpp */; }; -+ 84F0DA7A2F45FAE100906960 /* ContainerSizeChangeListener.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84F0DA742F45FAE100906960 /* ContainerSizeChangeListener.hpp */; }; -+ 84F0DA7B2F45FAE100906960 /* ContainerContentChangeListener.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84F0DA712F45FAE100906960 /* ContainerContentChangeListener.cpp */; }; -+ 84F0DA7C2F45FAE100906960 /* CompoundContainer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84F0DA6E2F45FAE100906960 /* CompoundContainer.cpp */; }; -+ 84F0DA7D2F45FAE100906960 /* ContainerListener.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84F0DA732F45FAE100906960 /* ContainerListener.cpp */; }; -+ 84F0DA7E2F45FAE100906960 /* ContainerSizeChangeListener.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84F0DA752F45FAE100906960 /* ContainerSizeChangeListener.cpp */; }; - 84F4880E2EEA4F5C00309B1E /* FixedPipelineStateNull.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84F487F02EEA4F5C00309B1E /* FixedPipelineStateNull.cpp */; }; - 84F4880F2EEA4F5C00309B1E /* FixedPipelineStateNull.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84F487F12EEA4F5C00309B1E /* FixedPipelineStateNull.hpp */; }; - 84F488102EEA4F5C00309B1E /* FogStateNull.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84F487F22EEA4F5C00309B1E /* FogStateNull.cpp */; }; -@@ -2766,11 +2770,6 @@ - 84E7BF172F286A08002D3936 /* SimpleContainer.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = SimpleContainer.hpp; sourceTree = ""; }; - 84E7BF182F286A08002D3936 /* Slot.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Slot.cpp; sourceTree = ""; }; - 84E7BF192F286A08002D3936 /* Slot.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Slot.hpp; sourceTree = ""; }; -- 84E7BF2A2F286A20002D3936 /* CompoundContainer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CompoundContainer.cpp; sourceTree = ""; }; -- 84E7BF2B2F286A20002D3936 /* CompoundContainer.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = CompoundContainer.hpp; sourceTree = ""; }; -- 84E7BF2C2F286A20002D3936 /* Container.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Container.hpp; sourceTree = ""; }; -- 84E7BF2D2F286A20002D3936 /* ContainerListener.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ContainerListener.cpp; sourceTree = ""; }; -- 84E7BF2E2F286A20002D3936 /* ContainerListener.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ContainerListener.hpp; sourceTree = ""; }; - 84E7BF342F286A6A002D3936 /* ItemStack.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ItemStack.cpp; sourceTree = ""; }; - 84E7BF352F286A6A002D3936 /* ItemStack.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ItemStack.hpp; sourceTree = ""; }; - 84EAE8DD2AF1EAA1000894E8 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; -@@ -3069,6 +3068,15 @@ - 84EEED622F0A01B700F741B6 /* ViewportOrigin.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ViewportOrigin.hpp; sourceTree = ""; }; - 84EEED662F0A02F300F741B6 /* ErrorHandlerOGL.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ErrorHandlerOGL.cpp; sourceTree = ""; }; - 84EEED672F0A02F300F741B6 /* ErrorHandlerOGL.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ErrorHandlerOGL.hpp; sourceTree = ""; }; -+ 84F0DA6D2F45FAE100906960 /* CompoundContainer.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = CompoundContainer.hpp; sourceTree = ""; }; -+ 84F0DA6E2F45FAE100906960 /* CompoundContainer.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = CompoundContainer.cpp; sourceTree = ""; }; -+ 84F0DA6F2F45FAE100906960 /* Container.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Container.hpp; sourceTree = ""; }; -+ 84F0DA702F45FAE100906960 /* ContainerContentChangeListener.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ContainerContentChangeListener.hpp; sourceTree = ""; }; -+ 84F0DA712F45FAE100906960 /* ContainerContentChangeListener.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ContainerContentChangeListener.cpp; sourceTree = ""; }; -+ 84F0DA722F45FAE100906960 /* ContainerListener.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ContainerListener.hpp; sourceTree = ""; }; -+ 84F0DA732F45FAE100906960 /* ContainerListener.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ContainerListener.cpp; sourceTree = ""; }; -+ 84F0DA742F45FAE100906960 /* ContainerSizeChangeListener.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ContainerSizeChangeListener.hpp; sourceTree = ""; }; -+ 84F0DA752F45FAE100906960 /* ContainerSizeChangeListener.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ContainerSizeChangeListener.cpp; sourceTree = ""; }; - 84F487F02EEA4F5C00309B1E /* FixedPipelineStateNull.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FixedPipelineStateNull.cpp; sourceTree = ""; }; - 84F487F12EEA4F5C00309B1E /* FixedPipelineStateNull.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = FixedPipelineStateNull.hpp; sourceTree = ""; }; - 84F487F22EEA4F5C00309B1E /* FogStateNull.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FogStateNull.cpp; sourceTree = ""; }; -@@ -3806,11 +3814,6 @@ - 840DD6562AC810620006A435 /* world */ = { - isa = PBXGroup; - children = ( -- 84E7BF2A2F286A20002D3936 /* CompoundContainer.cpp */, -- 84E7BF2B2F286A20002D3936 /* CompoundContainer.hpp */, -- 84E7BF2C2F286A20002D3936 /* Container.hpp */, -- 84E7BF2D2F286A20002D3936 /* ContainerListener.cpp */, -- 84E7BF2E2F286A20002D3936 /* ContainerListener.hpp */, - 840DD6572AC810620006A435 /* entity */, - 84358B1A2F4551290077EA44 /* Facing.cpp */, - 8477B3A82C4DC3B3004E1AC5 /* Facing.hpp */, -@@ -5083,6 +5086,15 @@ - 84E7BF092F286A08002D3936 /* inventory */ = { - isa = PBXGroup; - children = ( -+ 84F0DA6D2F45FAE100906960 /* CompoundContainer.hpp */, -+ 84F0DA6E2F45FAE100906960 /* CompoundContainer.cpp */, -+ 84F0DA6F2F45FAE100906960 /* Container.hpp */, -+ 84F0DA702F45FAE100906960 /* ContainerContentChangeListener.hpp */, -+ 84F0DA712F45FAE100906960 /* ContainerContentChangeListener.cpp */, -+ 84F0DA722F45FAE100906960 /* ContainerListener.hpp */, -+ 84F0DA732F45FAE100906960 /* ContainerListener.cpp */, -+ 84F0DA742F45FAE100906960 /* ContainerSizeChangeListener.hpp */, -+ 84F0DA752F45FAE100906960 /* ContainerSizeChangeListener.cpp */, - 84E7BF0A2F286A08002D3936 /* ArmorSlot.cpp */, - 84E7BF0B2F286A08002D3936 /* ArmorSlot.hpp */, - 84E022FE2F2A0245003C8FFE /* ChestMenu.cpp */, -@@ -5955,7 +5967,6 @@ - 84BF63862AF186C8008A9995 /* Inventory.hpp in Headers */, - 8477B3B52C4DC414004E1AC5 /* ChunkTilePos.hpp in Headers */, - 84BF63872AF186C8008A9995 /* Item.hpp in Headers */, -- 84E7BF332F286A20002D3936 /* ContainerListener.hpp in Headers */, - 84BF63892AF186C8008A9995 /* TileItem.hpp in Headers */, - 84BF638A2AF186C8008A9995 /* TilePlanterItem.hpp in Headers */, - 84E7BF292F286A08002D3936 /* Slot.hpp in Headers */, -@@ -5969,6 +5980,11 @@ - 84BF638F2AF186C8008A9995 /* BiomeSource.hpp in Headers */, - 84BF63902AF186C8008A9995 /* ChunkCache.hpp in Headers */, - 84BF63912AF186C8008A9995 /* ChunkSource.hpp in Headers */, -+ 84F0DA762F45FAE100906960 /* ContainerContentChangeListener.hpp in Headers */, -+ 84F0DA772F45FAE100906960 /* ContainerListener.hpp in Headers */, -+ 84F0DA782F45FAE100906960 /* Container.hpp in Headers */, -+ 84F0DA792F45FAE100906960 /* CompoundContainer.hpp in Headers */, -+ 84F0DA7A2F45FAE100906960 /* ContainerSizeChangeListener.hpp in Headers */, - 84BF63922AF186C8008A9995 /* LevelChunk.hpp in Headers */, - 84BF63932AF186C8008A9995 /* PerformanceTestChunkSource.hpp in Headers */, - 84E023012F2A0245003C8FFE /* ChestMenu.hpp in Headers */, -@@ -6074,7 +6090,6 @@ - 84BF63D22AF186C9008A9995 /* TorchTile.hpp in Headers */, - 84BF63D32AF186C9008A9995 /* TransparentTile.hpp in Headers */, - 84BF63D42AF186C9008A9995 /* TreeTile.hpp in Headers */, -- 84E7BF312F286A20002D3936 /* Container.hpp in Headers */, - 84BF63D52AF186C9008A9995 /* WireTile.hpp in Headers */, - 84AA8B512B32F39A003F5B82 /* BinaryHeap.hpp in Headers */, - 84AA8B532B32F39A003F5B82 /* Node.hpp in Headers */, -@@ -6115,7 +6130,6 @@ - 84358B062F4550B30077EA44 /* MusicTileEntity.hpp in Headers */, - 84358B072F4550B30077EA44 /* TileEntityType.hpp in Headers */, - 8470AF3D2BE9B6FA00BCA54E /* FireworkParticle.hpp in Headers */, -- 84E7BF302F286A20002D3936 /* CompoundContainer.hpp in Headers */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -@@ -7067,7 +7081,6 @@ - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( -- 84E7BF322F286A20002D3936 /* ContainerListener.cpp in Sources */, - 84BF630C2AF18631008A9995 /* Entity.cpp in Sources */, - 84BF630D2AF18631008A9995 /* FallingTile.cpp in Sources */, - 84BF630E2AF18631008A9995 /* ItemEntity.cpp in Sources */, -@@ -7146,7 +7159,6 @@ - 84358B122F4550CC0077EA44 /* ChestTile.cpp in Sources */, - 84358B132F4550CC0077EA44 /* MusicTile.cpp in Sources */, - 84BF63382AF18631008A9995 /* LevelListener.cpp in Sources */, -- 84E7BF2F2F286A20002D3936 /* CompoundContainer.cpp in Sources */, - 84BF63392AF18631008A9995 /* Material.cpp in Sources */, - 84BF633A2AF18631008A9995 /* Region.cpp in Sources */, - 84BF633B2AF18631008A9995 /* ChunkStorage.cpp in Sources */, -@@ -7260,6 +7272,10 @@ - 84E78C7B2B58B3E000D515EF /* Rocket.cpp in Sources */, - 84E78C832B58B5FB00D515EF /* RocketItem.cpp in Sources */, - 845BBCB62F2EA81700C7232C /* ToolItem.cpp in Sources */, -+ 84F0DA7B2F45FAE100906960 /* ContainerContentChangeListener.cpp in Sources */, -+ 84F0DA7C2F45FAE100906960 /* CompoundContainer.cpp in Sources */, -+ 84F0DA7D2F45FAE100906960 /* ContainerListener.cpp in Sources */, -+ 84F0DA7E2F45FAE100906960 /* ContainerSizeChangeListener.cpp in Sources */, - 84E78C872B58B66B00D515EF /* RocketLauncherTile.cpp in Sources */, - 8470AF2C2BE9B60A00BCA54E /* MobFactory.cpp in Sources */, - 84E1C9E92E7FDC72007D2F5D /* SlabItem.cpp in Sources */, -diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt -index 853b0897c..057f3ac44 100644 ---- a/source/CMakeLists.txt -+++ b/source/CMakeLists.txt -@@ -360,7 +360,10 @@ add_library(reminecraftpe-core STATIC - world/item/crafting/SingleInputRecipe.cpp - world/item/crafting/ShapedRecipe.cpp - world/item/crafting/ShapelessRecipe.cpp -- world/ContainerListener.cpp -+ world/inventory/ContainerListener.cpp -+ world/inventory/CompoundContainer.cpp -+ world/inventory/ContainerContentChangeListener.cpp -+ world/inventory/ContainerSizeChangeListener.cpp - world/inventory/ContainerMenu.cpp - world/inventory/InventoryMenu.cpp - world/inventory/CraftingMenu.cpp -@@ -373,7 +376,6 @@ add_library(reminecraftpe-core STATIC - world/inventory/CraftingContainer.cpp - world/inventory/ResultContainer.cpp - world/inventory/SimpleContainer.cpp -- world/CompoundContainer.cpp - world/item/DoorItem.cpp - world/item/ItemStack.cpp - world/item/RocketItem.cpp - -From d80620fdbc5bfe4a76c2c89dfa94d7ac1e7a26df Mon Sep 17 00:00:00 2001 -From: hellosammu -Date: Thu, 19 Feb 2026 10:17:18 -0500 -Subject: [PATCH 22/22] todo fix putting item stack in chests not relaying - count to clients when placing back (have to go to work waaaah) - ---- - .../multiplayer/MultiplayerLocalPlayer.cpp | 2 +- - .../multiplayer/MultiplayerLocalPlayer.hpp | 2 +- - source/server/ServerPlayer.cpp | 10 ++-- - source/server/ServerPlayer.hpp | 2 +- - source/server/ServerSideNetworkHandler.cpp | 1 + - source/world/inventory/CompoundContainer.cpp | 56 ++++++++++++++++++- - source/world/inventory/CompoundContainer.hpp | 11 ++++ - .../ContainerContentChangeListener.hpp | 2 +- - source/world/inventory/ContainerListener.hpp | 3 +- - source/world/inventory/ContainerMenu.cpp | 52 ++++++++++++----- - source/world/inventory/ContainerMenu.hpp | 2 +- - source/world/inventory/SimpleContainer.cpp | 2 +- - source/world/item/Inventory.cpp | 20 +++++++ - source/world/item/Inventory.hpp | 8 ++- - source/world/tile/entity/ChestTileEntity.cpp | 1 + - 15 files changed, 144 insertions(+), 30 deletions(-) - -diff --git a/source/client/multiplayer/MultiplayerLocalPlayer.cpp b/source/client/multiplayer/MultiplayerLocalPlayer.cpp -index 36750209d..772734bf5 100644 ---- a/source/client/multiplayer/MultiplayerLocalPlayer.cpp -+++ b/source/client/multiplayer/MultiplayerLocalPlayer.cpp -@@ -141,7 +141,7 @@ void MultiplayerLocalPlayer::refreshContainer(ContainerMenu* menu, const std::ve - { - } - --void MultiplayerLocalPlayer::slotChanged(ContainerMenu* menu, int index, ItemStack& item, bool isResultSlot) -+void MultiplayerLocalPlayer::slotChanged(ContainerMenu* menu, int index, Slot* slot, ItemStack& item, bool isResultSlot) - { - #if NETWORK_PROTOCOL_VERSION >= 5 - m_pMinecraft->m_pRakNetInstance->send(new ContainerSetSlotPacket(m_pContainerMenu->m_containerId, index, item)); -diff --git a/source/client/multiplayer/MultiplayerLocalPlayer.hpp b/source/client/multiplayer/MultiplayerLocalPlayer.hpp -index 6ab1a580b..fffd41ec5 100644 ---- a/source/client/multiplayer/MultiplayerLocalPlayer.hpp -+++ b/source/client/multiplayer/MultiplayerLocalPlayer.hpp -@@ -22,7 +22,7 @@ class MultiplayerLocalPlayer : public LocalPlayer, public ContainerListener - void closeContainer() override; - - void refreshContainer(ContainerMenu* menu, const std::vector& items) override; -- void slotChanged(ContainerMenu* menu, int index, ItemStack& item, bool isResultSlot) override; -+ void slotChanged(ContainerMenu* menu, int index, Slot* slot, ItemStack& item, bool isResultSlot) override; - - private: - bool m_flashOnSetHealth; -diff --git a/source/server/ServerPlayer.cpp b/source/server/ServerPlayer.cpp -index a2a5d7cc5..d5bc21ebc 100644 ---- a/source/server/ServerPlayer.cpp -+++ b/source/server/ServerPlayer.cpp -@@ -14,6 +14,7 @@ - #include "world/inventory/ChestMenu.hpp" - #include "world/level/Level.hpp" - #include "world/tile/entity/FurnaceTileEntity.hpp" -+#include "world/inventory/Slot.hpp" - - ServerPlayer::ServerPlayer(Level* pLevel, GameType playerGameType) - : Player(pLevel, playerGameType) -@@ -122,13 +123,13 @@ void ServerPlayer::refreshContainer(ContainerMenu* menu, const std::vector= 5 -- if (!isResultSlot) -- { -+ // @TODO: See my gripes in ContainerMenu::slotChanged -+ // But ultimately this is a bandaid for the fact that the client has authority over the inventory in PE -+ if (!isResultSlot && slot->m_group != Slot::INVENTORY && slot->m_group != Slot::HOTBAR) - m_pLevel->m_pRakNetInstance->send(new ContainerSetSlotPacket(menu->m_containerId, index, item)); -- } - #endif - } - -@@ -162,5 +163,6 @@ void ServerPlayer::setContainerMenu(ContainerMenu* menu) - m_pContainerMenu->m_containerId = m_containerId; - m_pContainerMenu->addSlotListener(this); - refreshContainer(m_pContainerMenu, m_pContainerMenu->cloneItems()); -+ m_pContainerMenu->broadcastChanges(); - } - } -\ No newline at end of file -diff --git a/source/server/ServerPlayer.hpp b/source/server/ServerPlayer.hpp -index 874a0f1cc..2f08d5191 100644 ---- a/source/server/ServerPlayer.hpp -+++ b/source/server/ServerPlayer.hpp -@@ -19,7 +19,7 @@ class ServerPlayer : public Player, public ContainerListener - void take(Entity* pEnt, int count) override; - - void refreshContainer(ContainerMenu* menu, const std::vector& items) override; -- void slotChanged(ContainerMenu* menu, int index, ItemStack& item, bool isResultSlot) override; -+ void slotChanged(ContainerMenu* menu, int index, Slot* slot, ItemStack& item, bool isResultSlot) override; - void setContainerData(ContainerMenu* menu, int id, int value) override; - - void doCloseContainer(); -diff --git a/source/server/ServerSideNetworkHandler.cpp b/source/server/ServerSideNetworkHandler.cpp -index b88fc72d0..0651c8037 100644 ---- a/source/server/ServerSideNetworkHandler.cpp -+++ b/source/server/ServerSideNetworkHandler.cpp -@@ -640,6 +640,7 @@ void ServerSideNetworkHandler::handle(const RakNet::RakNetGUID& guid, ContainerS - switch (pContainerMenu->m_containerType) - { - case Container::FURNACE: -+ case Container::CONTAINER: - pContainerMenu->setItem(packet->m_slot, packet->m_item); - break; - default: -diff --git a/source/world/inventory/CompoundContainer.cpp b/source/world/inventory/CompoundContainer.cpp -index ea4d4dd1c..21adf1e2d 100644 ---- a/source/world/inventory/CompoundContainer.cpp -+++ b/source/world/inventory/CompoundContainer.cpp -@@ -1,8 +1,45 @@ - #include "CompoundContainer.hpp" - -+class CompoundContainer::ChildListener : public ContainerContentChangeListener -+{ -+public: -+ ChildListener(CompoundContainer* owner, int offset) -+ : m_pOwner(owner), m_offset(offset) -+ { -+ } -+ -+ void containerContentChanged(Container* container, SlotID slot) override -+ { -+ if (m_pOwner) -+ m_pOwner->setContainerChanged(slot + m_offset); -+ } -+ -+private: -+ CompoundContainer* m_pOwner; -+ int m_offset; -+}; -+ - CompoundContainer::CompoundContainer(const std::string& name, Container* c1, Container* c2) : -- m_name(name), m_pLeftContainer(c1), m_pRightContainer(c2) -+ m_name(name), m_pLeftContainer(c1), m_pRightContainer(c2), m_pLeftListener(nullptr), m_pRightListener(nullptr) - { -+ m_pLeftListener = new ChildListener(this, 0); -+ m_pRightListener = new ChildListener(this, m_pLeftContainer->getContainerSize()); -+ -+ if (m_pLeftContainer) -+ m_pLeftContainer->addContentChangeListener(m_pLeftListener); -+ if (m_pRightContainer) -+ m_pRightContainer->addContentChangeListener(m_pRightListener); -+} -+ -+CompoundContainer::~CompoundContainer() -+{ -+ if (m_pLeftContainer && m_pLeftListener) -+ m_pLeftContainer->removeContentChangeListener(m_pLeftListener); -+ if (m_pRightContainer && m_pRightListener) -+ m_pRightContainer->removeContentChangeListener(m_pRightListener); -+ -+ delete m_pLeftListener; -+ delete m_pRightListener; - } - - uint16_t CompoundContainer::getContainerSize() const -@@ -46,8 +83,11 @@ int CompoundContainer::getMaxStackSize() - - void CompoundContainer::setContainerChanged(SlotID slot) - { -- m_pLeftContainer->setContainerChanged(slot); -- m_pRightContainer->setContainerChanged(slot); -+ for (ContentChangeListeners::iterator it = m_contentChangeListeners.begin(); it != m_contentChangeListeners.end(); ++it) -+ { -+ ContainerContentChangeListener* listener = *it; -+ listener->containerContentChanged(this, slot); -+ } - } - - bool CompoundContainer::stillValid(Player* player) const -@@ -55,3 +95,13 @@ bool CompoundContainer::stillValid(Player* player) const - return m_pLeftContainer->stillValid(player) && m_pRightContainer->stillValid(player); - } - -+void CompoundContainer::addContentChangeListener(ContainerContentChangeListener* listener) -+{ -+ m_contentChangeListeners.insert(listener); -+} -+ -+void CompoundContainer::removeContentChangeListener(ContainerContentChangeListener* listener) -+{ -+ m_contentChangeListeners.erase(listener); -+} -+ -diff --git a/source/world/inventory/CompoundContainer.hpp b/source/world/inventory/CompoundContainer.hpp -index 1c3fd0fea..a429f1ceb 100644 ---- a/source/world/inventory/CompoundContainer.hpp -+++ b/source/world/inventory/CompoundContainer.hpp -@@ -1,17 +1,25 @@ - #pragma once - -+#include - #include - #include "Container.hpp" -+#include "ContainerContentChangeListener.hpp" - - class CompoundContainer : public Container - { - private: -+ class ChildListener; -+ - std::string m_name; - Container* m_pLeftContainer; - Container* m_pRightContainer; -+ ContentChangeListeners m_contentChangeListeners; -+ ChildListener* m_pLeftListener; -+ ChildListener* m_pRightListener; - - public: - CompoundContainer(const std::string& name, Container* c1, Container* c2); -+ ~CompoundContainer() override; - - uint16_t getContainerSize() const override; - -@@ -28,4 +36,7 @@ class CompoundContainer : public Container - void setContainerChanged(SlotID slot) override; - - bool stillValid(Player* player) const override; -+ -+ void addContentChangeListener(ContainerContentChangeListener* listener) override; -+ void removeContentChangeListener(ContainerContentChangeListener* listener) override; - }; -diff --git a/source/world/inventory/ContainerContentChangeListener.hpp b/source/world/inventory/ContainerContentChangeListener.hpp -index 675f1b221..ed0f7139f 100644 ---- a/source/world/inventory/ContainerContentChangeListener.hpp -+++ b/source/world/inventory/ContainerContentChangeListener.hpp -@@ -8,5 +8,5 @@ class ContainerContentChangeListener - virtual ~ContainerContentChangeListener() {} - - public: -- virtual void containerContentChanged(SlotID slot) = 0; -+ virtual void containerContentChanged(Container* container, SlotID slot) = 0; - }; -diff --git a/source/world/inventory/ContainerListener.hpp b/source/world/inventory/ContainerListener.hpp -index a159ae5db..79a63bb7e 100644 ---- a/source/world/inventory/ContainerListener.hpp -+++ b/source/world/inventory/ContainerListener.hpp -@@ -4,6 +4,7 @@ - - class ContainerMenu; - class ItemStack; -+class Slot; - - class ContainerListener - { -@@ -12,6 +13,6 @@ class ContainerListener - - virtual void refreshContainer(ContainerMenu* menu, const std::vector& items) {} - virtual void refreshContainerItems(ContainerMenu* menu); -- virtual void slotChanged(ContainerMenu* menu, int index, ItemStack& item, bool isResultSlot) {} -+ virtual void slotChanged(ContainerMenu* menu, int index, Slot* slot, ItemStack& item, bool isResultSlot) {} - virtual void setContainerData(ContainerMenu* menu, int id, int value) {} - }; -\ No newline at end of file -diff --git a/source/world/inventory/ContainerMenu.cpp b/source/world/inventory/ContainerMenu.cpp -index 7e52498df..936cb1d3e 100644 ---- a/source/world/inventory/ContainerMenu.cpp -+++ b/source/world/inventory/ContainerMenu.cpp -@@ -54,11 +54,6 @@ void ContainerMenu::addSlot(Slot* slot) - void ContainerMenu::addSlotListener(ContainerListener* listener) - { - m_listeners.insert(listener); -- -- // Not done on PE -- /*std::vector snapshot = cloneItems(); -- listener->refreshContainer(this, snapshot); -- broadcastChanges();*/ - } - - void ContainerMenu::sendData(int id, int value) -@@ -74,7 +69,7 @@ void ContainerMenu::broadcastChanges(SlotID slot) - { - m_lastSlots[slot] = ItemStack(current); - for (ContainerListeners::iterator it = m_listeners.begin(); it != m_listeners.end(); ++it) -- (*it)->slotChanged(this, slot, m_lastSlots[slot], isResultSlot()); -+ (*it)->slotChanged(this, slot, m_slots[slot], m_lastSlots[slot], isResultSlot()); - } - } - -@@ -101,12 +96,36 @@ void ContainerMenu::slotsChanged(Container*) - broadcastChanges(); - } - -+void ContainerMenu::containerContentChanged(Container* container, SlotID containerSlot) -+{ -+ // containerSlot is an index within a specific container, but m_slots contains -+ // slots from multiple containers. We need to find which slot in m_slots corresponds -+ // to this container slot by matching both the container and the slot index. -+ for (size_t i = 0; i < m_slots.size(); ++i) -+ { -+ Slot* slot = m_slots[i]; -+ if (slot->m_pContainer == container && slot->m_slot == containerSlot) -+ { -+ if (m_bBroadcastChanges) -+ broadcastChanges(i); -+ return; -+ } -+ } -+} -+ - std::vector ContainerMenu::cloneItems() - { - std::vector content; - - for (std::vector::iterator it = m_slots.begin(); it != m_slots.end(); ++it) - { -+ Slot* slot = *it; -+ // @TODO: I really do not like this. -+ // Firstly, inventories shouldn't be owned by the client -+ // Secondly, we shouldn't be checking types directly like this -+ // Ultimately this HAS to have two different storages, one for the inventory and one for the container -+ if (slot->m_group == Slot::INVENTORY || slot->m_group == Slot::HOTBAR) -+ continue; - const ItemStack& item = (*it)->getItem(); - content.push_back(item); - } -@@ -247,8 +266,6 @@ ItemStack ContainerMenu::clicked(int slotIndex, MouseButtonType mouseButton, boo - if (!slot) - return result; - -- slot->setChanged(); -- - ItemStack& slotItem = slot->getItem(); - - if (!slotItem.isEmpty()) -@@ -289,6 +306,7 @@ ItemStack ContainerMenu::clicked(int slotIndex, MouseButtonType mouseButton, boo - if (carried.m_count <= slot->getMaxStackSize()) - { - std::swap(carried, slotItem); -+ slot->setChanged(); - } - } - else if (slotItem.getId() == carried.getId()) -@@ -309,6 +327,7 @@ ItemStack ContainerMenu::clicked(int slotIndex, MouseButtonType mouseButton, boo - inv->setCarried(ItemStack::EMPTY); - - slotItem.m_count += count; -+ slot->setChanged(); - break; - } - case MOUSE_BUTTON_RIGHT: -@@ -325,6 +344,7 @@ ItemStack ContainerMenu::clicked(int slotIndex, MouseButtonType mouseButton, boo - inv->setCarried(ItemStack::EMPTY); - - slotItem.m_count += count; -+ slot->setChanged(); - break; - } - default: -@@ -358,6 +378,10 @@ ItemStack ContainerMenu::clicked(int slotIndex, MouseButtonType mouseButton, boo - void ContainerMenu::setItem(int index, ItemStack item) - { - m_slots[index]->set(item); -+ if (!m_bBroadcastChanges && index >= 0 && index < (int)m_lastSlots.size()) -+ { -+ m_lastSlots[index] = ItemStack(m_slots[index]->getItem()); -+ } - } - - void ContainerMenu::setAll(const std::vector& items) -@@ -366,6 +390,10 @@ void ContainerMenu::setAll(const std::vector& items) - for (size_t i = 0; i < n; ++i) - { - m_slots[i]->set(items[i]); -+ if (!m_bBroadcastChanges) -+ { -+ m_lastSlots[i] = ItemStack(m_slots[i]->getItem()); -+ } - } - } - -@@ -397,10 +425,4 @@ void ContainerMenu::setSynched(Player* player, bool isSynched) - m_unsynchedPlayers.erase(player); - else - m_unsynchedPlayers.insert(player); --} -- --void ContainerMenu::containerContentChanged(SlotID slot) --{ -- if (m_bBroadcastChanges) -- broadcastChanges(slot); --} -+} -\ No newline at end of file -diff --git a/source/world/inventory/ContainerMenu.hpp b/source/world/inventory/ContainerMenu.hpp -index 481d199be..45d1aa360 100644 ---- a/source/world/inventory/ContainerMenu.hpp -+++ b/source/world/inventory/ContainerMenu.hpp -@@ -59,7 +59,7 @@ class ContainerMenu : public ContainerContentChangeListener - virtual bool isPauseScreen() const { return false; } - - public: -- void containerContentChanged(SlotID slot) override; -+ void containerContentChanged(Container* container, SlotID slot) override; - - protected: - std::vector m_lastSlots; -diff --git a/source/world/inventory/SimpleContainer.cpp b/source/world/inventory/SimpleContainer.cpp -index 62d57425c..d6ce69a77 100644 ---- a/source/world/inventory/SimpleContainer.cpp -+++ b/source/world/inventory/SimpleContainer.cpp -@@ -62,7 +62,7 @@ void SimpleContainer::setContainerChanged(SlotID slot) - for (ContentChangeListeners::iterator it = m_contentChangeListeners.begin(); it != m_contentChangeListeners.end(); it++) - { - ContainerContentChangeListener* pListener = *it; -- pListener->containerContentChanged(slot); -+ pListener->containerContentChanged(this, slot); - } - } - -diff --git a/source/world/item/Inventory.cpp b/source/world/item/Inventory.cpp -index e0471397b..a14c88e24 100644 ---- a/source/world/item/Inventory.cpp -+++ b/source/world/item/Inventory.cpp -@@ -4,6 +4,7 @@ - #include "common/Logger.hpp" - #include "nbt/CompoundTag.hpp" - #include "network/Packet.hpp" -+#include "world/inventory/ContainerContentChangeListener.hpp" - - #include "Item.hpp" - -@@ -627,3 +628,22 @@ GameType Inventory::_getGameMode() const - { - return m_pPlayer->getPlayerGameType(); - } -+ -+void Inventory::setContainerChanged(SlotID slot) -+{ -+ for (ContentChangeListeners::iterator it = m_contentChangeListeners.begin(); it != m_contentChangeListeners.end(); ++it) -+ { -+ ContainerContentChangeListener* listener = *it; -+ listener->containerContentChanged(this, slot); -+ } -+} -+ -+void Inventory::addContentChangeListener(ContainerContentChangeListener* listener) -+{ -+ m_contentChangeListeners.insert(listener); -+} -+ -+void Inventory::removeContentChangeListener(ContainerContentChangeListener* listener) -+{ -+ m_contentChangeListeners.erase(listener); -+} -diff --git a/source/world/item/Inventory.hpp b/source/world/item/Inventory.hpp -index 876d06183..09a5c7882 100644 ---- a/source/world/item/Inventory.hpp -+++ b/source/world/item/Inventory.hpp -@@ -1,5 +1,6 @@ - #pragma once - -+#include - #include - #include "GameMods.hpp" - #include "world/inventory/Container.hpp" -@@ -19,6 +20,8 @@ class Player; // in case we're included from Player.hpp - - class Inventory : public Container - { -+private: -+ typedef std::set ContentChangeListeners; - public: - Inventory(Player*); - virtual ~Inventory(); -@@ -80,7 +83,9 @@ class Inventory : public Container - return "Inventory"; - } - -- void setContainerChanged(SlotID slot) override { } -+ void setContainerChanged(SlotID slot) override; -+ void addContentChangeListener(ContainerContentChangeListener* listener) override; -+ void removeContentChangeListener(ContainerContentChangeListener* listener) override; - - bool stillValid(Player* player) const override { return true; } - -@@ -100,4 +105,5 @@ class Inventory : public Container - - std::vector m_items; - std::vector m_armor; -+ ContentChangeListeners m_contentChangeListeners; - }; -diff --git a/source/world/tile/entity/ChestTileEntity.cpp b/source/world/tile/entity/ChestTileEntity.cpp -index 323cfa7eb..e18c18130 100644 ---- a/source/world/tile/entity/ChestTileEntity.cpp -+++ b/source/world/tile/entity/ChestTileEntity.cpp -@@ -27,5 +27,6 @@ bool ChestTileEntity::stillValid(Player* player) const - - void ChestTileEntity::setContainerChanged(SlotID slot) - { -+ SimpleContainer::setContainerChanged(slot); - TileEntity::setChanged(); - } From 5fdec57b1e7f9bf188414a1c4c9eb08682c2bbe9 Mon Sep 17 00:00:00 2001 From: Brent Da Mage Date: Tue, 12 May 2026 04:48:09 -0500 Subject: [PATCH 36/63] Fixed bad merge --- .github/workflows/build.yml | 4 +-- projects/visual-studio/World/World.vcxproj | 2 -- .../visual-studio/World/World.vcxproj.filters | 6 ---- source/CMakeLists.txt | 2 +- source/client/app/Minecraft.cpp | 2 +- source/client/gui/Gui.cpp | 2 ++ source/client/options/Options.cpp | 28 +++++++++---------- source/client/options/Options.hpp | 16 +++++++++++ source/world/entity/Entity.cpp | 2 +- source/world/entity/Mob.cpp | 2 +- source/world/inventory/Container.hpp | 3 ++ source/world/inventory/ContainerMenu.cpp | 2 +- source/world/level/Level.cpp | 5 ++-- source/world/level/path/BinaryHeap.cpp | 25 ++++++++--------- source/world/level/path/BinaryHeap.hpp | 13 ++++----- source/world/particle/NoteParticle.cpp | 6 ++-- 16 files changed, 64 insertions(+), 56 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 760583e30..45dc3946e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -176,8 +176,8 @@ jobs: include: - name: SDL 2 directory: platforms/sdl/sdl2/android - # - name: Native - # directory: platforms/android/project + - name: Native + directory: platforms/android/project name: Android (${{ matrix.name }}) runs-on: ubuntu-24.04 steps: diff --git a/projects/visual-studio/World/World.vcxproj b/projects/visual-studio/World/World.vcxproj index 9d1402f71..f2e980d1e 100644 --- a/projects/visual-studio/World/World.vcxproj +++ b/projects/visual-studio/World/World.vcxproj @@ -233,7 +233,6 @@ - @@ -418,7 +417,6 @@ - diff --git a/projects/visual-studio/World/World.vcxproj.filters b/projects/visual-studio/World/World.vcxproj.filters index e9d64177d..6d71fb082 100644 --- a/projects/visual-studio/World/World.vcxproj.filters +++ b/projects/visual-studio/World/World.vcxproj.filters @@ -671,9 +671,6 @@ Source Files\Item - - Source Files\Item - Source Files\Particle @@ -1222,9 +1219,6 @@ Header Files\Inventory - - Header Files\Item - Header Files\Tile\Entity diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index e720f1132..4299d1c30 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -125,9 +125,9 @@ add_library(nbcraft-core STATIC client/gui/screens/inventory/ContainerScreen.cpp client/gui/screens/inventory/InventoryScreen.cpp client/gui/screens/inventory/CraftingScreen.cpp + client/gui/screens/inventory/ClassicCraftingScreen_Console.cpp client/gui/screens/inventory/FurnaceScreen.cpp client/gui/screens/inventory/ChestScreen.cpp - client/gui/screens/inventory/ClassicCraftingScreen_Console.cpp client/gui/components/ScrolledSelectionList.cpp client/gui/components/AvailableGamesList.cpp client/gui/components/RolledSelectionList.cpp diff --git a/source/client/app/Minecraft.cpp b/source/client/app/Minecraft.cpp index 2cdda9fc6..5d88cb7cb 100644 --- a/source/client/app/Minecraft.cpp +++ b/source/client/app/Minecraft.cpp @@ -185,7 +185,7 @@ void Minecraft::_initGameModes(Level& level) } } -void Minecraft::_reloadInput() +void Minecraft::reloadInput() { if (m_pInputHolder) delete m_pInputHolder; diff --git a/source/client/gui/Gui.cpp b/source/client/gui/Gui.cpp index 025b3b4e8..07755c51f 100644 --- a/source/client/gui/Gui.cpp +++ b/source/client/gui/Gui.cpp @@ -260,6 +260,8 @@ void Gui::renderSlot(int slot, int x, int y, float f) ItemRenderer::singleton().renderGuiItem(m_pMinecraft->m_pFont, m_pMinecraft->m_pTextures, item, x, y, true); } + + //ItemRenderer::renderGuiItemDecorations(m_pMinecraft->m_pFont, m_pMinecraft->m_pTextures, item, x, y); } void Gui::renderSlotOverlay(int slot, int x, int y, float f) diff --git a/source/client/options/Options.cpp b/source/client/options/Options.cpp index 6e26a811e..c19f74190 100644 --- a/source/client/options/Options.cpp +++ b/source/client/options/Options.cpp @@ -762,6 +762,15 @@ std::string SensitivityOption::getDisplayValue() const return get() == 0.0f ? Language::get("options.sensitivity.min") : get() == 1.0f ? Language::get("options.sensitivity.max") : Util::toString(int(get() * 200)) + "%"; } +void ControllerOption::apply() +{ + // @TODO: This works but ultimately needs to be a multi-select (KBM, controller, touch) instead of a single option. + // Either that or figure out an automatic way to switch between them based on input like how Minecraft Bedrock does + // For now, I just wanted to be able to switch to controller input on mobile devices. + if (m_pMinecraft && m_pMinecraft->m_pInputHolder) + m_pMinecraft->reloadInput(); +} + void AOOption::apply() { Minecraft::useAmbientOcclusion = get(); @@ -800,6 +809,11 @@ std::string FancyGraphicsOption::getMessage() const return Util::format(Language::get("options.value").c_str(), Language::get("options.graphics").c_str(), Language::get(get() ? "options.graphics.fancy" : "options.graphics.fast").c_str()); } +void VsyncOption::apply() +{ + m_pMinecraft->platform()->setVSyncEnabled(get()); +} + void LogoTypeOption::apply() { if (m_pMinecraft->getOptions()) @@ -821,17 +835,3 @@ void UIThemeOption::apply() m_pMinecraft->getOptions()->m_logoType.apply(); } } - -void ControllerOption::apply() -{ - // @TODO: This works but ultimately needs to be a multi-select (KBM, controller, touch) instead of a single option. - // Either that or figure out an automatic way to switch between them based on input like how Minecraft Bedrock does - // For now, I just wanted to be able to switch to controller input on mobile devices. - if (m_pMinecraft && m_pMinecraft->m_pInputHolder) - m_pMinecraft->reloadInput(); -} - -void VsyncOption::apply() -{ - m_pMinecraft->platform()->setVSyncEnabled(get()); -} diff --git a/source/client/options/Options.hpp b/source/client/options/Options.hpp index e472e8b85..4ddd869a5 100644 --- a/source/client/options/Options.hpp +++ b/source/client/options/Options.hpp @@ -266,6 +266,14 @@ class SensitivityOption : public FloatOption std::string getDisplayValue() const override; }; +class ControllerOption : public BoolOption +{ +public: + ControllerOption(const std::string& key, const std::string& name, bool initial = true) : BoolOption(key, name, initial) {} + + void apply() override; +}; + class AOOption : public BoolOption { public: @@ -290,6 +298,14 @@ class FancyGraphicsOption : public GraphicsOption std::string getMessage() const override; }; +class VsyncOption : public BoolOption +{ +public: + VsyncOption(const std::string& key, const std::string& name, bool initial = true) : BoolOption(key, name, initial) {} + + void apply() override; +}; + class GuiScaleOption : public ValuesOption { public: diff --git a/source/world/entity/Entity.cpp b/source/world/entity/Entity.cpp index 168838cf0..9502ddaca 100644 --- a/source/world/entity/Entity.cpp +++ b/source/world/entity/Entity.cpp @@ -1112,7 +1112,7 @@ void Entity::setRider(Entity* rider) void Entity::setRiding(Entity* riding) { _ridingId = (riding) ? riding->m_EntityID : 0; - setSharedFlag(C_ENTITY_FLAG_RIDING, riding); + setSharedFlag(FLAG_RIDING, riding); } /*void Entity::thunderHit(LightningBolt* bolt) diff --git a/source/world/entity/Mob.cpp b/source/world/entity/Mob.cpp index e2fdf6a63..8a4dca3d3 100644 --- a/source/world/entity/Mob.cpp +++ b/source/world/entity/Mob.cpp @@ -861,7 +861,7 @@ void Mob::updateAi() m_lookTime--; // if the entity was removed, or we're too far away, or our gaze timer is up - if (m_lookTime < 0 || m_pEntLookedAt->m_bRemoved || m_pEntLookedAt->distanceToSqr(this) > 64.0f) + if (m_lookTime < 0 || pEnt->m_bRemoved || pEnt->distanceToSqr(this) > 64.0f) // stop staring m_entLookedAtId = 0; } diff --git a/source/world/inventory/Container.hpp b/source/world/inventory/Container.hpp index 6e6956689..1d5f84de1 100644 --- a/source/world/inventory/Container.hpp +++ b/source/world/inventory/Container.hpp @@ -27,6 +27,9 @@ class Container DISPENSER }; +public: + virtual ~Container() {} + public: virtual Size getContainerSize() const = 0; virtual ItemStack& getItem(int index) = 0; diff --git a/source/world/inventory/ContainerMenu.cpp b/source/world/inventory/ContainerMenu.cpp index ba45da7f8..44b14430e 100644 --- a/source/world/inventory/ContainerMenu.cpp +++ b/source/world/inventory/ContainerMenu.cpp @@ -53,7 +53,7 @@ void ContainerMenu::addSlot(Slot* slot) void ContainerMenu::addSlotListener(ContainerListener* listener) { - m_listeners.push_back(listener); + m_listeners.insert(listener); // @PARITY: Not done on PE /*std::vector snapshot = copyItems(); diff --git a/source/world/level/Level.cpp b/source/world/level/Level.cpp index 5c8d9f0bc..62a6de457 100644 --- a/source/world/level/Level.cpp +++ b/source/world/level/Level.cpp @@ -81,10 +81,9 @@ Level::~Level() SAFE_DELETE(m_pPathFinder); SAFE_DELETE(m_pMobSpawner); - const size_t size = m_entities.size(); - for (size_t i = 0; i < size; ++i) + for (EntityMap::iterator it = m_entities.begin(); it != m_entities.end(); it++) { - Entity* pEnt = m_entities[i]; + Entity* pEnt = it->second; //you better HOPE this is freed by Minecraft! (or a NetworkHandler) //Really should have used shared pointers and stuff. diff --git a/source/world/level/path/BinaryHeap.cpp b/source/world/level/path/BinaryHeap.cpp index 66313747d..2ca12f82b 100644 --- a/source/world/level/path/BinaryHeap.cpp +++ b/source/world/level/path/BinaryHeap.cpp @@ -44,6 +44,7 @@ void BinaryHeap::inlined0(int num) Node* var5 = m_items[var4]; if (var3 >= var5->f) { break; + } m_items[num] = var5; var5->heapIdx = num; @@ -58,41 +59,39 @@ void BinaryHeap::downHeap(int num) Node* var2 = m_items[num]; float var3 = var2->f; - while (true) - { + while (true) { int var4 = 1 + (num << 1); int var5 = var4 + 1; - if (var4 >= m_count) + if (var4 >= m_count) { break; + } Node* var6 = m_items[var4]; float var7 = var6->f; Node* var8; float var9; - if (var5 >= m_count) - { + if (var5 >= m_count) { var8 = nullptr; var9 = std::numeric_limits::infinity(); } - else - { + else { var8 = m_items[var5]; var9 = var8->f; } - if (var7 < var9) - { - if (var7 >= var3) + if (var7 < var9) { + if (var7 >= var3) { break; + } m_items[num] = var6; var6->heapIdx = num; num = var4; } - else - { - if (var9 >= var3) + else { + if (var9 >= var3) { break; + } m_items[num] = var8; var8->heapIdx = num; diff --git a/source/world/level/path/BinaryHeap.hpp b/source/world/level/path/BinaryHeap.hpp index d252bf994..1c1da492c 100644 --- a/source/world/level/path/BinaryHeap.hpp +++ b/source/world/level/path/BinaryHeap.hpp @@ -16,7 +16,7 @@ class BinaryHeap { m_count = 0; m_capacity = 1024; - m_items = new Node*[m_capacity]; + m_items = new Node * [m_capacity]; } ~BinaryHeap() @@ -29,8 +29,7 @@ class BinaryHeap void inlined0(int i); void downHeap(int i); - Node* removeTop() - { + Node* removeTop() { Node* pNode = m_items[0]; m_items[0] = m_items[--m_count]; m_items[m_count] = 0; @@ -42,13 +41,11 @@ class BinaryHeap return pNode; } - void clear() - { + void clear() { m_count = 0; } - - int size() const - { + + int size() const { return m_count; } diff --git a/source/world/particle/NoteParticle.cpp b/source/world/particle/NoteParticle.cpp index 149dee931..aca991fef 100644 --- a/source/world/particle/NoteParticle.cpp +++ b/source/world/particle/NoteParticle.cpp @@ -21,8 +21,8 @@ void NoteParticle::tick() { m_oPos = m_pos; - m_timer++; - if (m_timer > m_lifetime) + m_age++; + if (m_age > m_lifetime) remove(); move(m_vel); @@ -41,7 +41,7 @@ void NoteParticle::tick() void NoteParticle::render(Tesselator& t, float f, float a, float b, float c, float d, float e) { - float mult = float(m_timer + f) / float(m_lifetime) * 32.0f; + float mult = float(m_age + f) / float(m_lifetime) * 32.0f; m_size = m_oSize * Mth::clamp(mult, 0.0f, 1.0f); Particle::render(t, f, a, b, c, d, e); } From 097dd30b2922bd28f81ff280a32caa81e0e808e4 Mon Sep 17 00:00:00 2001 From: Brent Da Mage Date: Tue, 12 May 2026 05:11:01 -0500 Subject: [PATCH 37/63] Fixed crash from breaking chests --- source/world/tile/ChestTile.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/source/world/tile/ChestTile.cpp b/source/world/tile/ChestTile.cpp index 6db79d413..9cfbb1b87 100644 --- a/source/world/tile/ChestTile.cpp +++ b/source/world/tile/ChestTile.cpp @@ -132,7 +132,10 @@ void ChestTile::onRemove(Level* level, const TilePos& pos) ChestTileEntity* ent = static_cast(level->getTileEntity(pos)); if (!ent) + { EntityTile::onRemove(level, pos); + return; + } for (int slot = 0; slot < ent->getContainerSize(); ++slot) { From f500a72de729788a9dde09c67b8da09e2aee90c8 Mon Sep 17 00:00:00 2001 From: Brent Da Mage Date: Tue, 12 May 2026 05:19:52 -0500 Subject: [PATCH 38/63] Minor cleanup --- source/world/tile/ChestTile.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/source/world/tile/ChestTile.cpp b/source/world/tile/ChestTile.cpp index 9cfbb1b87..a71916d53 100644 --- a/source/world/tile/ChestTile.cpp +++ b/source/world/tile/ChestTile.cpp @@ -176,16 +176,16 @@ bool ChestTile::use(Level* level, const TilePos& pos, Player* player) if (level->isSolidTile(pos.above())) return true; - if (level->getTile(pos.west()) == m_ID && level->isSolidTile(pos + TilePos(-1, 1, 0))) + if (level->getTile(pos.west()) == m_ID && level->isSolidTile(pos.west().above())) return true; - if (level->getTile(pos.east()) == m_ID && level->isSolidTile(pos + TilePos(1, 1, 0))) + if (level->getTile(pos.east()) == m_ID && level->isSolidTile(pos.east().above())) return true; - if (level->getTile(pos.north()) == m_ID && level->isSolidTile(pos + TilePos(0, 1, -1))) + if (level->getTile(pos.north()) == m_ID && level->isSolidTile(pos.north().above())) return true; - if (level->getTile(pos.south()) == m_ID && level->isSolidTile(pos + TilePos(0, 1, 1))) + if (level->getTile(pos.south()) == m_ID && level->isSolidTile(pos.south().above())) return true; TileEntity* tileEnt = level->getTileEntity(pos); From ec4dbcd678e0c2fd89d36d92f562230ef55662a2 Mon Sep 17 00:00:00 2001 From: Brent Da Mage Date: Tue, 12 May 2026 05:26:48 -0500 Subject: [PATCH 39/63] Added v-sync lang --- game/assets/lang/en_US.lang | 1 + 1 file changed, 1 insertion(+) diff --git a/game/assets/lang/en_US.lang b/game/assets/lang/en_US.lang index c86cb0cfe..a197bb6e6 100644 --- a/game/assets/lang/en_US.lang +++ b/game/assets/lang/en_US.lang @@ -104,6 +104,7 @@ options.guiScale.small=Small options.guiScale.normal=Normal options.guiScale.large=Large options.gamma=Gamma +options.enableVsync=V-Sync performance.max=Max FPS performance.balanced=Balanced From 9e80ba5d65126766e2e57198200972d787ea8008 Mon Sep 17 00:00:00 2001 From: Brent Da Mage Date: Wed, 13 May 2026 01:55:59 -0500 Subject: [PATCH 40/63] Fixed weird multiplayer dupe bug --- source/client/multiplayer/MultiplayerLocalPlayer.cpp | 2 +- source/client/renderer/LevelRenderer.cpp | 6 +++++- source/world/inventory/ContainerMenu.cpp | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/source/client/multiplayer/MultiplayerLocalPlayer.cpp b/source/client/multiplayer/MultiplayerLocalPlayer.cpp index d6a8d775b..c025bccd6 100644 --- a/source/client/multiplayer/MultiplayerLocalPlayer.cpp +++ b/source/client/multiplayer/MultiplayerLocalPlayer.cpp @@ -144,6 +144,6 @@ void MultiplayerLocalPlayer::refreshContainer(ContainerMenu* menu, const std::ve void MultiplayerLocalPlayer::slotChanged(ContainerMenu* menu, int index, Slot* slot, ItemStack& item, bool isResultSlot) { #if NETWORK_PROTOCOL_VERSION >= 5 - m_pMinecraft->m_pRakNetInstance->send(new ContainerSetSlotPacket(m_pContainerMenu->m_containerId, index, item)); + m_pMinecraft->m_pRakNetInstance->send(new ContainerSetSlotPacket(menu->m_containerId, index, item)); #endif } diff --git a/source/client/renderer/LevelRenderer.cpp b/source/client/renderer/LevelRenderer.cpp index 6a855b977..047c061a9 100644 --- a/source/client/renderer/LevelRenderer.cpp +++ b/source/client/renderer/LevelRenderer.cpp @@ -1118,7 +1118,11 @@ void LevelRenderer::setTilesDirty(const TilePos& min, const TilePos& max) void LevelRenderer::tick() { - const Entity& camera = *m_pMinecraft->m_pCameraEntity; + const Entity* pCamera = m_pMinecraft->m_pCameraEntity; + if (!pCamera) + return; + + const Entity& camera = *pCamera; const Level& level = *m_pMinecraft->m_pLevel; const Options& options = *m_pMinecraft->getOptions(); diff --git a/source/world/inventory/ContainerMenu.cpp b/source/world/inventory/ContainerMenu.cpp index 44b14430e..5a43b8521 100644 --- a/source/world/inventory/ContainerMenu.cpp +++ b/source/world/inventory/ContainerMenu.cpp @@ -109,7 +109,7 @@ void ContainerMenu::containerContentChanged(Container* container, SlotID contain for (size_t i = 0; i < m_slots.size(); ++i) { Slot* slot = m_slots[i]; - if (slot->m_pContainer == container && slot->m_slot == containerSlot) + if (slot->m_pContainer == container && slot->m_index == containerSlot) { if (m_bBroadcastChanges) broadcastChanges(i); From 2b9d1814ae6df47f65f32efc1446a9896f6b50fc Mon Sep 17 00:00:00 2001 From: Brent Da Mage Date: Thu, 14 May 2026 02:09:18 -0500 Subject: [PATCH 41/63] Properly typed Container::StackID vs Container::SlotID * Fixes container replication jankiness --- source/client/gui/Gui.cpp | 26 +++---- .../screens/IngameBlockSelectionScreen.hpp | 2 +- .../gui/screens/inventory/ChestScreen.cpp | 6 +- .../ClassicCraftingScreen_Console.cpp | 6 +- .../gui/screens/inventory/ContainerScreen.cpp | 20 ++--- .../gui/screens/inventory/ContainerScreen.hpp | 2 +- .../gui/screens/inventory/CraftingScreen.cpp | 6 +- .../gui/screens/inventory/FurnaceScreen.cpp | 6 +- .../gui/screens/inventory/InventoryScreen.cpp | 12 +-- .../multiplayer/MultiplayerLocalPlayer.cpp | 4 +- .../multiplayer/MultiplayerLocalPlayer.hpp | 2 +- .../network/ClientSideNetworkHandler.cpp | 4 +- source/client/player/LocalPlayer.cpp | 3 + source/common/Utils.hpp | 2 - .../packets/ContainerSetDataPacket.cpp | 8 +- .../packets/ContainerSetDataPacket.hpp | 6 +- .../packets/ContainerSetSlotPacket.cpp | 8 +- .../packets/ContainerSetSlotPacket.hpp | 6 +- source/server/ServerPlayer.cpp | 8 +- source/server/ServerPlayer.hpp | 4 +- source/server/ServerSideNetworkHandler.cpp | 2 +- source/world/gamemode/GameMode.cpp | 4 +- source/world/gamemode/GameMode.hpp | 2 +- source/world/inventory/ChestMenu.cpp | 6 +- source/world/inventory/ChestMenu.hpp | 2 +- source/world/inventory/CompoundContainer.cpp | 14 ++-- source/world/inventory/CompoundContainer.hpp | 8 +- source/world/inventory/Container.hpp | 16 ++-- .../ContainerContentChangeListener.hpp | 2 +- source/world/inventory/ContainerListener.hpp | 5 +- source/world/inventory/ContainerMenu.cpp | 77 +++++++++---------- source/world/inventory/ContainerMenu.hpp | 16 ++-- source/world/inventory/CraftingContainer.cpp | 30 ++++---- source/world/inventory/CraftingContainer.hpp | 8 +- source/world/inventory/CraftingMenu.cpp | 10 +-- source/world/inventory/CraftingMenu.hpp | 2 +- source/world/inventory/FurnaceMenu.cpp | 10 +-- source/world/inventory/FurnaceMenu.hpp | 2 +- source/world/inventory/InventoryMenu.cpp | 10 +-- source/world/inventory/InventoryMenu.hpp | 2 +- source/world/inventory/ResultContainer.cpp | 23 +++--- source/world/inventory/ResultContainer.hpp | 8 +- source/world/inventory/SimpleContainer.cpp | 12 +-- source/world/inventory/SimpleContainer.hpp | 10 +-- source/world/inventory/Slot.cpp | 10 +-- source/world/inventory/Slot.hpp | 16 ++-- source/world/item/Inventory.cpp | 46 +++++------ source/world/item/Inventory.hpp | 12 +-- source/world/item/ItemStack.cpp | 2 +- source/world/tile/entity/ChestTileEntity.cpp | 4 +- source/world/tile/entity/ChestTileEntity.hpp | 2 +- .../world/tile/entity/FurnaceTileEntity.cpp | 4 +- .../world/tile/entity/FurnaceTileEntity.hpp | 2 +- 53 files changed, 260 insertions(+), 260 deletions(-) diff --git a/source/client/gui/Gui.cpp b/source/client/gui/Gui.cpp index 07755c51f..7f6d0dda8 100644 --- a/source/client/gui/Gui.cpp +++ b/source/client/gui/Gui.cpp @@ -349,22 +349,22 @@ void Gui::handleClick(int clickID, int mouseX, int mouseY) void Gui::handleScrollWheel(bool down) { - SlotID slot = m_pMinecraft->m_pLocalPlayer->m_pInventory->m_selectedSlot; + Container::SlotID slotId = m_pMinecraft->m_pLocalPlayer->m_pInventory->m_selectedSlot; int maxItems = getNumUsableSlots() - 1; if (down) { - if (slot++ == maxItems) - slot = 0; + if (slotId++ == maxItems) + slotId = 0; } else { - if (slot-- == 0) - slot = maxItems; + if (slotId-- == 0) + slotId = maxItems; } - m_pMinecraft->m_pLocalPlayer->m_pInventory->selectSlot(slot); + m_pMinecraft->m_pLocalPlayer->m_pInventory->selectSlot(slotId); } void Gui::handleKeyPressed(int keyCode) @@ -387,21 +387,21 @@ void Gui::handleKeyPressed(int keyCode) int maxItems = getNumSlots() - 1; if (m_pMinecraft->useTouchscreen()) maxItems--; - SlotID* slot = &m_pMinecraft->m_pLocalPlayer->m_pInventory->m_selectedSlot; + Container::SlotID* slotId = &m_pMinecraft->m_pLocalPlayer->m_pInventory->m_selectedSlot; if (slotR) { - if (*slot < maxItems) - (*slot)++; + if (*slotId < maxItems) + (*slotId)++; else - *slot = 0; + *slotId = 0; } else if (slotL) { - if (*slot > 0) - (*slot)--; + if (*slotId > 0) + (*slotId)--; else - *slot = maxItems; + *slotId = maxItems; } return; } diff --git a/source/client/gui/screens/IngameBlockSelectionScreen.hpp b/source/client/gui/screens/IngameBlockSelectionScreen.hpp index 097af0f64..0167b1229 100644 --- a/source/client/gui/screens/IngameBlockSelectionScreen.hpp +++ b/source/client/gui/screens/IngameBlockSelectionScreen.hpp @@ -43,7 +43,7 @@ class IngameBlockSelectionScreen : public Screen void keyPressed(int key) override; private: - SlotID m_selectedSlot; + Container::SlotID m_selectedSlot; bool m_bReleased; bool m_bClickedOnSlot; Button m_btnCraft; diff --git a/source/client/gui/screens/inventory/ChestScreen.cpp b/source/client/gui/screens/inventory/ChestScreen.cpp index 2df77fff9..aa105aaa0 100644 --- a/source/client/gui/screens/inventory/ChestScreen.cpp +++ b/source/client/gui/screens/inventory/ChestScreen.cpp @@ -34,11 +34,11 @@ SlotDisplay ChestScreen::_createSlotDisplay(const Slot& slot) switch (slot.m_group) { case Slot::CONTAINER: - return SlotDisplay(8 + (slot.m_slot % 9) * slotSize, 18 + (slot.m_slot / 9) * slotSize, slotSize); + return SlotDisplay(8 + (slot.m_stackId % 9) * slotSize, 18 + (slot.m_stackId / 9) * slotSize, slotSize); case Slot::INVENTORY: - return SlotDisplay(8 + (slot.m_slot % 9) * slotSize, (rows * 18) + 13 + (slot.m_slot / 9) * slotSize, slotSize); + return SlotDisplay(8 + (slot.m_stackId % 9) * slotSize, (rows * 18) + 13 + (slot.m_stackId / 9) * slotSize, slotSize); case Slot::HOTBAR: - return SlotDisplay(8 + (slot.m_slot % 9) * slotSize, 89 + rows * slotSize, slotSize); + return SlotDisplay(8 + (slot.m_stackId % 9) * slotSize, 89 + rows * slotSize, slotSize); default: return SlotDisplay(); } diff --git a/source/client/gui/screens/inventory/ClassicCraftingScreen_Console.cpp b/source/client/gui/screens/inventory/ClassicCraftingScreen_Console.cpp index f81d568a5..54d2d2a9f 100644 --- a/source/client/gui/screens/inventory/ClassicCraftingScreen_Console.cpp +++ b/source/client/gui/screens/inventory/ClassicCraftingScreen_Console.cpp @@ -37,11 +37,11 @@ SlotDisplay ClassicCraftingScreen_Console::_createSlotDisplay(const Slot& slot) case Slot::OUTPUT: return SlotDisplay(308, 97, 64, true); case Slot::INPUT: - return SlotDisplay(62 + (slot.m_slot % 3) * slotSize, 64 + (slot.m_slot / 3) * slotSize, slotSize, true); + return SlotDisplay(62 + (slot.m_stackId % 3) * slotSize, 64 + (slot.m_stackId / 3) * slotSize, slotSize, true); case Slot::INVENTORY: - return SlotDisplay(28 + (slot.m_slot % 9) * slotSize, 240 + ((slot.m_slot / 9) - 1) * slotSize, slotSize, true); + return SlotDisplay(28 + (slot.m_stackId % 9) * slotSize, 240 + ((slot.m_stackId / 9) - 1) * slotSize, slotSize, true); case Slot::HOTBAR: - return SlotDisplay(28 + (slot.m_slot % 9) * slotSize, 379, slotSize, true); + return SlotDisplay(28 + (slot.m_stackId % 9) * slotSize, 379, slotSize, true); default: return SlotDisplay(); } diff --git a/source/client/gui/screens/inventory/ContainerScreen.cpp b/source/client/gui/screens/inventory/ContainerScreen.cpp index ef9fe17e4..cdfc1fa35 100644 --- a/source/client/gui/screens/inventory/ContainerScreen.cpp +++ b/source/client/gui/screens/inventory/ContainerScreen.cpp @@ -140,7 +140,7 @@ void ContainerScreen::initMenuPointer() { Slot* slot = *it; //@NOTE: Selects the first hotbar slot - if (slot->m_slot == 0 && m_pMinecraft->m_pLocalPlayer && slot->m_pContainer == m_pMinecraft->m_pLocalPlayer->m_pInventory) + if (slot->m_id == 0 && m_pMinecraft->m_pLocalPlayer && slot->m_pContainer == m_pMinecraft->m_pLocalPlayer->m_pInventory) { _selectSlot(slot); break; @@ -266,18 +266,18 @@ void ContainerScreen::slotClicked(const MenuPointer& pointer, MouseButtonType bu { Slot* slot = _findSlot(pointer.x, pointer.y); bool outside = pointer.x < m_leftPos || pointer.y < m_topPos || pointer.x >= m_leftPos + m_imageWidth || pointer.y >= m_topPos + m_imageHeight; - int index = -1; - if (slot) index = slot->m_index; - if (outside) index = -999; - if (index != -1) - slotClicked(slot, index, button, index != -999 && quick); + Container::SlotID slotId = -1; + if (slot) slotId = slot->m_id; + if (outside) slotId = -999; + if (slotId != -1) + slotClicked(slot, slotId, button, slotId != -999 && quick); } } -void ContainerScreen::slotClicked(Slot* slot, int index, MouseButtonType button, bool quick) +void ContainerScreen::slotClicked(Slot* slot, Container::SlotID slotId, MouseButtonType button, bool quick) { _tryPlayInteractSound(); - m_pMinecraft->getLocalPlayerGameMode()->handleInventoryMouseClick(m_pMenu->m_containerId, index, button, quick, m_pMinecraft->m_pLocalPlayer); + m_pMinecraft->getLocalPlayerGameMode()->handleInventoryMouseClick(m_pMenu->m_containerId, slotId, button, quick, m_pMinecraft->m_pLocalPlayer); } void ContainerScreen::slotClicked(const MenuPointer& pointer, MouseButtonType button) @@ -317,7 +317,7 @@ void ContainerScreen::keyPressed(int keyCode) const SlotDisplay& ContainerScreen::getSlotDisplay(const Slot& slot) const { - return m_slotDisplays[slot.m_index]; + return m_slotDisplays[slot.m_id]; } void ContainerScreen::onClose() @@ -369,5 +369,5 @@ bool ContainerScreen::SlotNavigation::next(int& x, int& y, bool cycle) bool ContainerScreen::SlotNavigation::isValid(ID id) { Slot* hovered = m_pScreen->_findSlot(); - return !hovered || hovered->m_index != id; + return !hovered || hovered->m_id != id; } diff --git a/source/client/gui/screens/inventory/ContainerScreen.hpp b/source/client/gui/screens/inventory/ContainerScreen.hpp index 274120153..67a7c2dd9 100644 --- a/source/client/gui/screens/inventory/ContainerScreen.hpp +++ b/source/client/gui/screens/inventory/ContainerScreen.hpp @@ -73,7 +73,7 @@ class ContainerScreen : public Screen virtual void initMenuPointer() override; virtual void slotsChanged(Container* container); virtual void slotClicked(const MenuPointer& pointer, MouseButtonType button, bool quick); - virtual void slotClicked(Slot* slot, int index, MouseButtonType button, bool quick); + virtual void slotClicked(Slot* slot, Container::SlotID slotId, MouseButtonType button, bool quick); void slotClicked(const MenuPointer& pointer, MouseButtonType button); public: diff --git a/source/client/gui/screens/inventory/CraftingScreen.cpp b/source/client/gui/screens/inventory/CraftingScreen.cpp index 3a66ffc37..f2ebbea25 100644 --- a/source/client/gui/screens/inventory/CraftingScreen.cpp +++ b/source/client/gui/screens/inventory/CraftingScreen.cpp @@ -32,11 +32,11 @@ SlotDisplay CraftingScreen::_createSlotDisplay(const Slot& slot) case Slot::OUTPUT: return SlotDisplay(124, 35); case Slot::INPUT: - return SlotDisplay(30 + (slot.m_slot % 3) * slotSize, 17 + (slot.m_slot / 3) * slotSize); + return SlotDisplay(30 + (slot.m_stackId % 3) * slotSize, 17 + (slot.m_stackId / 3) * slotSize); case Slot::INVENTORY: - return SlotDisplay(8 + (slot.m_slot % 9) * slotSize, 84 + ((slot.m_slot / 9) - 1) * slotSize, slotSize); + return SlotDisplay(8 + (slot.m_stackId % 9) * slotSize, 84 + ((slot.m_stackId / 9) - 1) * slotSize, slotSize); case Slot::HOTBAR: - return SlotDisplay(8 + (slot.m_slot % 9) * slotSize, 142, slotSize); + return SlotDisplay(8 + (slot.m_stackId % 9) * slotSize, 142, slotSize); default: return SlotDisplay(); } diff --git a/source/client/gui/screens/inventory/FurnaceScreen.cpp b/source/client/gui/screens/inventory/FurnaceScreen.cpp index b28b6d538..d3c3c0fb3 100644 --- a/source/client/gui/screens/inventory/FurnaceScreen.cpp +++ b/source/client/gui/screens/inventory/FurnaceScreen.cpp @@ -44,13 +44,13 @@ SlotDisplay FurnaceScreen::_createSlotDisplay(const Slot& slot) switch (slot.m_group) { case Slot::INPUT: - return SlotDisplay(56, 17 + (slot.m_slot % 2) * (slotSize * 2)); + return SlotDisplay(56, 17 + (slot.m_stackId % 2) * (slotSize * 2)); case Slot::OUTPUT: return SlotDisplay(116, 35); case Slot::INVENTORY: - return SlotDisplay(8 + (slot.m_slot % 9) * slotSize, 84 + ((slot.m_slot / 9) - 1) * slotSize, slotSize); + return SlotDisplay(8 + (slot.m_stackId % 9) * slotSize, 84 + ((slot.m_stackId / 9) - 1) * slotSize, slotSize); case Slot::HOTBAR: - return SlotDisplay(8 + (slot.m_slot % 9) * slotSize, 142, slotSize); + return SlotDisplay(8 + (slot.m_stackId % 9) * slotSize, 142, slotSize); default: return SlotDisplay(); } diff --git a/source/client/gui/screens/inventory/InventoryScreen.cpp b/source/client/gui/screens/inventory/InventoryScreen.cpp index 680aefcfc..45f05a8b3 100644 --- a/source/client/gui/screens/inventory/InventoryScreen.cpp +++ b/source/client/gui/screens/inventory/InventoryScreen.cpp @@ -135,16 +135,16 @@ SlotDisplay InventoryScreen::_createSlotDisplay(const Slot& slot) case Slot::OUTPUT: return m_pMinecraft->getOptions()->m_classicCrafting.get() ? SlotDisplay(352, 83, 54, true) : SlotDisplay(); case Slot::INPUT: - return m_pMinecraft->getOptions()->m_classicCrafting.get() ? SlotDisplay(221 + (slot.m_slot % 2) * slotSize, 67 + (slot.m_slot / 2) * slotSize, slotSize, true) : SlotDisplay(); + return m_pMinecraft->getOptions()->m_classicCrafting.get() ? SlotDisplay(221 + (slot.m_stackId % 2) * slotSize, 67 + (slot.m_stackId / 2) * slotSize, slotSize, true) : SlotDisplay(); case Slot::ARMOR: { const ArmorSlot& armorSlot = (const ArmorSlot&)slot; return SlotDisplay(m_pMinecraft->getOptions()->m_classicCrafting.get() ? 27 : 127, 29 + (Item::SLOT_HEAD - armorSlot.m_equipmentSlot) * slotSize, slotSize, true, -1, CONSOLE_ARMOR_SLOTS[armorSlot.m_equipmentSlot]); } case Slot::INVENTORY: - return SlotDisplay(28 + (slot.m_slot % 9) * slotSize, 233 + ((slot.m_slot / 9) - 1) * slotSize, slotSize, true); + return SlotDisplay(28 + (slot.m_stackId % 9) * slotSize, 233 + ((slot.m_stackId / 9) - 1) * slotSize, slotSize, true); case Slot::HOTBAR: - return SlotDisplay(28 + (slot.m_slot % 9) * slotSize, 372, slotSize, true); + return SlotDisplay(28 + (slot.m_stackId % 9) * slotSize, 372, slotSize, true); default: return SlotDisplay(); } @@ -157,16 +157,16 @@ SlotDisplay InventoryScreen::_createSlotDisplay(const Slot& slot) case Slot::OUTPUT: return SlotDisplay(144, 36); case Slot::INPUT: - return SlotDisplay(88 + (slot.m_slot % 2) * slotSize, 26 + (slot.m_slot / 2) * slotSize); + return SlotDisplay(88 + (slot.m_stackId % 2) * slotSize, 26 + (slot.m_stackId / 2) * slotSize); case Slot::ARMOR: { const ArmorSlot& armorSlot = (const ArmorSlot&)slot; return SlotDisplay(8, 8 + (Item::SLOT_HEAD - armorSlot.m_equipmentSlot) * slotSize, slotSize, false, 16 * ((Item::SLOT_HEAD - armorSlot.m_equipmentSlot) + 1) - 1); } case Slot::INVENTORY: - return SlotDisplay(8 + (slot.m_slot % 9) * slotSize, 84 + ((slot.m_slot / 9) - 1) * slotSize, slotSize); + return SlotDisplay(8 + (slot.m_stackId % 9) * slotSize, 84 + ((slot.m_stackId / 9) - 1) * slotSize, slotSize); case Slot::HOTBAR: - return SlotDisplay(8 + (slot.m_slot % 9) * slotSize, 142, slotSize); + return SlotDisplay(8 + (slot.m_stackId % 9) * slotSize, 142, slotSize); default: return SlotDisplay(); } diff --git a/source/client/multiplayer/MultiplayerLocalPlayer.cpp b/source/client/multiplayer/MultiplayerLocalPlayer.cpp index c025bccd6..a5ad680a5 100644 --- a/source/client/multiplayer/MultiplayerLocalPlayer.cpp +++ b/source/client/multiplayer/MultiplayerLocalPlayer.cpp @@ -141,9 +141,9 @@ void MultiplayerLocalPlayer::refreshContainer(ContainerMenu* menu, const std::ve { } -void MultiplayerLocalPlayer::slotChanged(ContainerMenu* menu, int index, Slot* slot, ItemStack& item, bool isResultSlot) +void MultiplayerLocalPlayer::slotChanged(ContainerMenu* menu, Container::SlotID slotId, Slot* slot, ItemStack& item, bool isResultSlot) { #if NETWORK_PROTOCOL_VERSION >= 5 - m_pMinecraft->m_pRakNetInstance->send(new ContainerSetSlotPacket(menu->m_containerId, index, item)); + m_pMinecraft->m_pRakNetInstance->send(new ContainerSetSlotPacket(menu->m_containerId, slotId, item)); #endif } diff --git a/source/client/multiplayer/MultiplayerLocalPlayer.hpp b/source/client/multiplayer/MultiplayerLocalPlayer.hpp index fffd41ec5..d635ff4a9 100644 --- a/source/client/multiplayer/MultiplayerLocalPlayer.hpp +++ b/source/client/multiplayer/MultiplayerLocalPlayer.hpp @@ -22,7 +22,7 @@ class MultiplayerLocalPlayer : public LocalPlayer, public ContainerListener void closeContainer() override; void refreshContainer(ContainerMenu* menu, const std::vector& items) override; - void slotChanged(ContainerMenu* menu, int index, Slot* slot, ItemStack& item, bool isResultSlot) override; + void slotChanged(ContainerMenu* menu, Container::SlotID slotId, Slot* slot, ItemStack& item, bool isResultSlot) override; private: bool m_flashOnSetHealth; diff --git a/source/client/network/ClientSideNetworkHandler.cpp b/source/client/network/ClientSideNetworkHandler.cpp index 3037a8b89..38b84790f 100644 --- a/source/client/network/ClientSideNetworkHandler.cpp +++ b/source/client/network/ClientSideNetworkHandler.cpp @@ -778,7 +778,7 @@ void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& guid, ContainerS return; pContainerMenu->m_bBroadcastChanges = false; - pContainerMenu->setItem(packet->m_slot, packet->m_item); + pContainerMenu->setItem(packet->m_slotId, packet->m_item); pContainerMenu->m_bBroadcastChanges = true; } @@ -801,7 +801,7 @@ void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& guid, ContainerS return; pContainerMenu->m_bBroadcastChanges = false; - pContainerMenu->setData(packet->m_slot, packet->m_value); + pContainerMenu->setData(packet->m_slotId, packet->m_value); pContainerMenu->m_bBroadcastChanges = true; } diff --git a/source/client/player/LocalPlayer.cpp b/source/client/player/LocalPlayer.cpp index 48ce80afa..a8dd0d66c 100644 --- a/source/client/player/LocalPlayer.cpp +++ b/source/client/player/LocalPlayer.cpp @@ -258,6 +258,9 @@ bool LocalPlayer::isSneaking() const void LocalPlayer::move(const Vec3& pos) { LocalPlayer* pLP = m_pMinecraft->m_pLocalPlayer; + if (!pLP) + return; + if (Minecraft::DEADMAU5_CAMERA_CHEATS && pLP == this && m_pMinecraft->getOptions()->m_flightHax.get()) { //@HUH: Using m_pMinecraft->m_pLocalPlayer instead of this, even though they're the same diff --git a/source/common/Utils.hpp b/source/common/Utils.hpp index d5e5b397a..b955c140f 100644 --- a/source/common/Utils.hpp +++ b/source/common/Utils.hpp @@ -546,8 +546,6 @@ typedef uint8_t TileID; // Rename "FullTile" to "Tile" typedef uint8_t TileData; -typedef uint16_t SlotID; - #define SAFE_DELETE(ptr) do { if (ptr) delete ptr; } while (0) #define SAFE_DELETE_ARRAY(ptr) do { if (ptr) delete[] ptr; } while (0) diff --git a/source/network/packets/ContainerSetDataPacket.cpp b/source/network/packets/ContainerSetDataPacket.cpp index 976a413c3..e003a6144 100644 --- a/source/network/packets/ContainerSetDataPacket.cpp +++ b/source/network/packets/ContainerSetDataPacket.cpp @@ -1,9 +1,9 @@ #include "ContainerSetDataPacket.hpp" #include "network/NetEventCallback.hpp" -ContainerSetDataPacket::ContainerSetDataPacket(int8_t containerId, int16_t slot, int16_t value) +ContainerSetDataPacket::ContainerSetDataPacket(int8_t containerId, int16_t slotId, int16_t value) : m_containerId(containerId) - , m_slot(slot) + , m_slotId(slotId) , m_value(value) { } @@ -17,13 +17,13 @@ void ContainerSetDataPacket::write(RakNet::BitStream& bs) { bs.Write((unsigned char)PACKET_CONTAINER_SET_DATA); bs.Write(m_containerId); - bs.Write(m_slot); + bs.Write(m_slotId); bs.Write(m_value); } void ContainerSetDataPacket::read(RakNet::BitStream& bs) { bs.Read(m_containerId); - bs.Read(m_slot); + bs.Read(m_slotId); bs.Read(m_value); } diff --git a/source/network/packets/ContainerSetDataPacket.hpp b/source/network/packets/ContainerSetDataPacket.hpp index e504c7cdd..84479d37e 100644 --- a/source/network/packets/ContainerSetDataPacket.hpp +++ b/source/network/packets/ContainerSetDataPacket.hpp @@ -8,16 +8,16 @@ class ContainerSetDataPacket : public Packet ContainerSetDataPacket() { m_containerId = 0; - m_slot = 0; + m_slotId = 0; m_value = 0; } - ContainerSetDataPacket(int8_t containerId, int16_t slot, int16_t value); + ContainerSetDataPacket(int8_t containerId, int16_t slotId, int16_t value); void handle(const RakNet::RakNetGUID&, NetEventCallback& callback) override; void write(RakNet::BitStream&) override; void read(RakNet::BitStream&) override; public: int8_t m_containerId; - int16_t m_slot; + int16_t m_slotId; int16_t m_value; }; \ No newline at end of file diff --git a/source/network/packets/ContainerSetSlotPacket.cpp b/source/network/packets/ContainerSetSlotPacket.cpp index b4bcf9049..bafe0f32a 100644 --- a/source/network/packets/ContainerSetSlotPacket.cpp +++ b/source/network/packets/ContainerSetSlotPacket.cpp @@ -2,9 +2,9 @@ #include "network/NetEventCallback.hpp" #include "network/PacketUtil.hpp" -ContainerSetSlotPacket::ContainerSetSlotPacket(int8_t containerId, int16_t slot, const ItemStack& item) +ContainerSetSlotPacket::ContainerSetSlotPacket(int8_t containerId, int16_t slotId, const ItemStack& item) : m_containerId(containerId) - , m_slot(slot) + , m_slotId(slotId) , m_item(item) { } @@ -18,13 +18,13 @@ void ContainerSetSlotPacket::write(RakNet::BitStream& bs) { bs.Write((unsigned char)PACKET_CONTAINER_SET_SLOT); bs.Write(m_containerId); - bs.Write(m_slot); + bs.Write(m_slotId); PacketUtil::WriteItemStack(m_item, bs, false); } void ContainerSetSlotPacket::read(RakNet::BitStream& bs) { bs.Read(m_containerId); - bs.Read(m_slot); + bs.Read(m_slotId); m_item = PacketUtil::ReadItemStack(bs, false); } diff --git a/source/network/packets/ContainerSetSlotPacket.hpp b/source/network/packets/ContainerSetSlotPacket.hpp index f3e547323..86f66dd9c 100644 --- a/source/network/packets/ContainerSetSlotPacket.hpp +++ b/source/network/packets/ContainerSetSlotPacket.hpp @@ -9,15 +9,15 @@ class ContainerSetSlotPacket : public Packet ContainerSetSlotPacket() { m_containerId = 0; - m_slot = 0; + m_slotId = 0; } - ContainerSetSlotPacket(int8_t containerId, int16_t slot, const ItemStack& item); + ContainerSetSlotPacket(int8_t containerId, int16_t slotId, const ItemStack& item); void handle(const RakNet::RakNetGUID&, NetEventCallback& callback) override; void write(RakNet::BitStream&) override; void read(RakNet::BitStream&) override; public: int8_t m_containerId; - int16_t m_slot; + int16_t m_slotId; ItemStack m_item; }; diff --git a/source/server/ServerPlayer.cpp b/source/server/ServerPlayer.cpp index 14ae29b6e..ae3b17226 100644 --- a/source/server/ServerPlayer.cpp +++ b/source/server/ServerPlayer.cpp @@ -123,20 +123,20 @@ void ServerPlayer::refreshContainer(ContainerMenu* menu, const std::vector= 5 // @TODO: See my gripes in ContainerMenu::slotChanged // But ultimately this is a bandaid for the fact that the client has authority over the inventory in PE if (!isResultSlot && slot->m_group != Slot::INVENTORY && slot->m_group != Slot::HOTBAR) - m_pLevel->m_pRakNetInstance->send(new ContainerSetSlotPacket(menu->m_containerId, index, item)); + m_pLevel->m_pRakNetInstance->send(new ContainerSetSlotPacket(menu->m_containerId, slotId, item)); #endif } -void ServerPlayer::setContainerData(ContainerMenu* menu, int id, int value) +void ServerPlayer::setContainerData(ContainerMenu* menu, Container::SlotID slotId, int value) { #if NETWORK_PROTOCOL_VERSION >= 5 - m_pLevel->m_pRakNetInstance->send(new ContainerSetDataPacket(menu->m_containerId, id, value)); + m_pLevel->m_pRakNetInstance->send(new ContainerSetDataPacket(menu->m_containerId, slotId, value)); #endif } diff --git a/source/server/ServerPlayer.hpp b/source/server/ServerPlayer.hpp index 2f08d5191..95d761a16 100644 --- a/source/server/ServerPlayer.hpp +++ b/source/server/ServerPlayer.hpp @@ -19,8 +19,8 @@ class ServerPlayer : public Player, public ContainerListener void take(Entity* pEnt, int count) override; void refreshContainer(ContainerMenu* menu, const std::vector& items) override; - void slotChanged(ContainerMenu* menu, int index, Slot* slot, ItemStack& item, bool isResultSlot) override; - void setContainerData(ContainerMenu* menu, int id, int value) override; + void slotChanged(ContainerMenu* menu, Container::SlotID slotId, Slot* slot, ItemStack& item, bool isResultSlot) override; + void setContainerData(ContainerMenu* menu, Container::SlotID slotId, int value) override; void doCloseContainer(); void setContainerMenu(ContainerMenu* menu); diff --git a/source/server/ServerSideNetworkHandler.cpp b/source/server/ServerSideNetworkHandler.cpp index 485a9ab00..d7df9b99c 100644 --- a/source/server/ServerSideNetworkHandler.cpp +++ b/source/server/ServerSideNetworkHandler.cpp @@ -656,7 +656,7 @@ void ServerSideNetworkHandler::handle(const RakNet::RakNetGUID& guid, ContainerS { case Container::FURNACE: case Container::CONTAINER: - pContainerMenu->setItem(packet->m_slot, packet->m_item); + pContainerMenu->setItem(packet->m_slotId, packet->m_item); break; default: break; diff --git a/source/world/gamemode/GameMode.cpp b/source/world/gamemode/GameMode.cpp index 4f30701d8..c68b3bd5b 100644 --- a/source/world/gamemode/GameMode.cpp +++ b/source/world/gamemode/GameMode.cpp @@ -126,9 +126,9 @@ void GameMode::attack(Player* player, Entity* entity) player->attack(entity); } -ItemStack GameMode::handleInventoryMouseClick(int containerId, int slotNum, MouseButtonType button, bool quick, Player* player) +ItemStack GameMode::handleInventoryMouseClick(int containerId, Container::SlotID slotId, MouseButtonType button, bool quick, Player* player) { - return player->m_pContainerMenu->clicked(slotNum, button, quick, player); + return player->m_pContainerMenu->clicked(slotId, button, quick, player); } void GameMode::handleCloseInventory(int a, Player* player) diff --git a/source/world/gamemode/GameMode.hpp b/source/world/gamemode/GameMode.hpp index 8b0b58bcd..693a91713 100644 --- a/source/world/gamemode/GameMode.hpp +++ b/source/world/gamemode/GameMode.hpp @@ -42,7 +42,7 @@ class GameMode virtual bool canHurtPlayer(); virtual void interact(Player*, Entity*); virtual void attack(Player*, Entity*); - virtual ItemStack handleInventoryMouseClick(int, int, MouseButtonType, bool, Player*); + virtual ItemStack handleInventoryMouseClick(int containerId, Container::SlotID slotId, MouseButtonType, bool, Player*); virtual void handleCloseInventory(int, Player*); virtual bool isCreativeType() const { return true; } virtual bool isSurvivalType() const { return false; } diff --git a/source/world/inventory/ChestMenu.cpp b/source/world/inventory/ChestMenu.cpp index 7766dd63c..a0b061981 100644 --- a/source/world/inventory/ChestMenu.cpp +++ b/source/world/inventory/ChestMenu.cpp @@ -32,16 +32,16 @@ bool ChestMenu::stillValid(Player* player) const return m_pContainer->stillValid(player); } -ItemStack ChestMenu::quickMoveStack(int index) +ItemStack ChestMenu::quickMoveStack(Container::SlotID slotId) { ItemStack item = ItemStack::EMPTY; - Slot* slot = getSlot(index); + Slot* slot = getSlot(slotId); if (slot && slot->hasItem()) { ItemStack& slotItem = slot->getItem(); item = slotItem; int rows = m_pContainer->getContainerSize() / 9; - if (index < rows * 9) + if (slotId < rows * 9) moveItemStackTo(slotItem, rows * 9, m_slots.size(), true); else moveItemStackTo(slotItem, 0, rows * 9, false); diff --git a/source/world/inventory/ChestMenu.hpp b/source/world/inventory/ChestMenu.hpp index 04eb68da5..8206e24ba 100644 --- a/source/world/inventory/ChestMenu.hpp +++ b/source/world/inventory/ChestMenu.hpp @@ -10,7 +10,7 @@ class ChestMenu : public ContainerMenu ChestMenu(Container* inventory, Container* container); bool stillValid(Player* player) const override; - ItemStack quickMoveStack(int index) override; + ItemStack quickMoveStack(Container::SlotID slotId) override; private: Container* m_pContainer; diff --git a/source/world/inventory/CompoundContainer.cpp b/source/world/inventory/CompoundContainer.cpp index 21adf1e2d..58efc6e30 100644 --- a/source/world/inventory/CompoundContainer.cpp +++ b/source/world/inventory/CompoundContainer.cpp @@ -8,10 +8,10 @@ class CompoundContainer::ChildListener : public ContainerContentChangeListener { } - void containerContentChanged(Container* container, SlotID slot) override + void containerContentChanged(Container* container, StackID stackId) override { if (m_pOwner) - m_pOwner->setContainerChanged(slot + m_offset); + m_pOwner->setContainerChanged(stackId + m_offset); } private: @@ -52,7 +52,7 @@ std::string CompoundContainer::getName() const return m_name; } -ItemStack& CompoundContainer::getItem(int index) +ItemStack& CompoundContainer::getItem(StackID index) { if (index >= m_pLeftContainer->getContainerSize()) return m_pRightContainer->getItem(index - m_pLeftContainer->getContainerSize()); @@ -60,7 +60,7 @@ ItemStack& CompoundContainer::getItem(int index) return m_pLeftContainer->getItem(index); } -ItemStack CompoundContainer::removeItem(int index, int count) +ItemStack CompoundContainer::removeItem(StackID index, int count) { if (index >= m_pLeftContainer->getContainerSize()) return m_pRightContainer->removeItem(index - m_pLeftContainer->getContainerSize(), count); @@ -68,7 +68,7 @@ ItemStack CompoundContainer::removeItem(int index, int count) return m_pLeftContainer->removeItem(index, count); } -void CompoundContainer::setItem(int index, const ItemStack& item) +void CompoundContainer::setItem(StackID index, const ItemStack& item) { if (index >= m_pLeftContainer->getContainerSize()) m_pRightContainer->setItem(index - m_pLeftContainer->getContainerSize(), item); @@ -81,12 +81,12 @@ int CompoundContainer::getMaxStackSize() return m_pLeftContainer->getMaxStackSize(); } -void CompoundContainer::setContainerChanged(SlotID slot) +void CompoundContainer::setContainerChanged(StackID stackId) { for (ContentChangeListeners::iterator it = m_contentChangeListeners.begin(); it != m_contentChangeListeners.end(); ++it) { ContainerContentChangeListener* listener = *it; - listener->containerContentChanged(this, slot); + listener->containerContentChanged(this, stackId); } } diff --git a/source/world/inventory/CompoundContainer.hpp b/source/world/inventory/CompoundContainer.hpp index a429f1ceb..195f718ca 100644 --- a/source/world/inventory/CompoundContainer.hpp +++ b/source/world/inventory/CompoundContainer.hpp @@ -25,15 +25,15 @@ class CompoundContainer : public Container std::string getName() const override; - ItemStack& getItem(int index) override; + ItemStack& getItem(StackID index) override; - ItemStack removeItem(int index, int count) override; + ItemStack removeItem(StackID index, int count) override; - void setItem(int index, const ItemStack& item) override; + void setItem(StackID index, const ItemStack& item) override; int getMaxStackSize() override; - void setContainerChanged(SlotID slot) override; + void setContainerChanged(StackID stackId) override; bool stillValid(Player* player) const override; diff --git a/source/world/inventory/Container.hpp b/source/world/inventory/Container.hpp index 1d5f84de1..3e0c98e1b 100644 --- a/source/world/inventory/Container.hpp +++ b/source/world/inventory/Container.hpp @@ -17,6 +17,8 @@ class Container public: typedef uint16_t Size; + typedef int16_t SlotID; + typedef uint16_t StackID; public: enum Type @@ -32,23 +34,23 @@ class Container public: virtual Size getContainerSize() const = 0; - virtual ItemStack& getItem(int index) = 0; - virtual ItemStack* tryGetItem(int index) + virtual ItemStack& getItem(StackID stackId) = 0; + virtual ItemStack* tryGetItem(StackID stackId) { - if (index >= 0 && index < getContainerSize()) - return &getItem(index); + if (stackId >= 0 && stackId < getContainerSize()) + return &getItem(stackId); else return nullptr; } - virtual ItemStack removeItem(int index, int count) = 0; - virtual void setItem(int index, const ItemStack& item) = 0; + virtual ItemStack removeItem(StackID stackId, int count) = 0; + virtual void setItem(StackID stackId, const ItemStack& item) = 0; virtual std::string getName() const = 0; virtual int getMaxStackSize() { return C_MAX_CONTAINER_STACK_SIZE; } // Was called setChanged in Java - virtual void setContainerChanged(SlotID slot) = 0; + virtual void setContainerChanged(StackID stackId) = 0; virtual bool stillValid(Player* player) const = 0; virtual void addContentChangeListener(ContainerContentChangeListener* listener) {} diff --git a/source/world/inventory/ContainerContentChangeListener.hpp b/source/world/inventory/ContainerContentChangeListener.hpp index ed0f7139f..80266d0fd 100644 --- a/source/world/inventory/ContainerContentChangeListener.hpp +++ b/source/world/inventory/ContainerContentChangeListener.hpp @@ -8,5 +8,5 @@ class ContainerContentChangeListener virtual ~ContainerContentChangeListener() {} public: - virtual void containerContentChanged(Container* container, SlotID slot) = 0; + virtual void containerContentChanged(Container* container, Container::StackID stackId) = 0; }; diff --git a/source/world/inventory/ContainerListener.hpp b/source/world/inventory/ContainerListener.hpp index 79a63bb7e..7d53d7795 100644 --- a/source/world/inventory/ContainerListener.hpp +++ b/source/world/inventory/ContainerListener.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include "Container.hpp" class ContainerMenu; class ItemStack; @@ -13,6 +14,6 @@ class ContainerListener virtual void refreshContainer(ContainerMenu* menu, const std::vector& items) {} virtual void refreshContainerItems(ContainerMenu* menu); - virtual void slotChanged(ContainerMenu* menu, int index, Slot* slot, ItemStack& item, bool isResultSlot) {} - virtual void setContainerData(ContainerMenu* menu, int id, int value) {} + virtual void slotChanged(ContainerMenu* menu, Container::SlotID slotId, Slot* slot, ItemStack& item, bool isResultSlot) {} + virtual void setContainerData(ContainerMenu* menu, Container::SlotID slotId, int value) {} }; \ No newline at end of file diff --git a/source/world/inventory/ContainerMenu.cpp b/source/world/inventory/ContainerMenu.cpp index 5a43b8521..15148fc6d 100644 --- a/source/world/inventory/ContainerMenu.cpp +++ b/source/world/inventory/ContainerMenu.cpp @@ -41,7 +41,7 @@ void ContainerMenu::_clearSlots() void ContainerMenu::addSlot(Slot* slot) { - slot->m_index = m_slots.size(); + slot->m_id = m_slots.size(); m_slots.push_back(slot); m_lastSlots.push_back(ItemStack::EMPTY); @@ -67,14 +67,14 @@ void ContainerMenu::sendData(int id, int value) (*it)->setContainerData(this, id, value); } -void ContainerMenu::broadcastChanges(SlotID slot) +void ContainerMenu::broadcastChanges(Container::SlotID slotId) { - ItemStack& current = m_slots[slot]->getItem(); - if (m_lastSlots[slot] != current) + ItemStack& current = m_slots[slotId]->getItem(); + if (m_lastSlots[slotId] != current) { - m_lastSlots[slot] = ItemStack(current); + m_lastSlots[slotId] = ItemStack(current); for (ContainerListeners::iterator it = m_listeners.begin(); it != m_listeners.end(); ++it) - (*it)->slotChanged(this, slot, m_slots[slot], m_lastSlots[slot], isResultSlot()); + (*it)->slotChanged(this, slotId, m_slots[slotId], m_lastSlots[slotId], isResultSlot()); } } @@ -101,15 +101,15 @@ void ContainerMenu::slotsChanged(Container*) broadcastChanges(); } -void ContainerMenu::containerContentChanged(Container* container, SlotID containerSlot) +void ContainerMenu::containerContentChanged(Container* container, Container::StackID stackId) { - // containerSlot is an index within a specific container, but m_slots contains + // slotId is an index within a specific container, but m_slots contains // slots from multiple containers. We need to find which slot in m_slots corresponds // to this container slot by matching both the container and the slot index. for (size_t i = 0; i < m_slots.size(); ++i) { Slot* slot = m_slots[i]; - if (slot->m_pContainer == container && slot->m_index == containerSlot) + if (slot->m_pContainer == container && slot->m_stackId == stackId) { if (m_bBroadcastChanges) broadcastChanges(i); @@ -138,38 +138,33 @@ std::vector ContainerMenu::cloneItems() return content; } -Slot* ContainerMenu::getSlotFor(Container* container, int index) +Slot* ContainerMenu::getSlotFor(Container* container, Container::StackID stackId) { for (std::vector::iterator it = m_slots.begin(); it != m_slots.end(); ++it) { Slot* slot = *it; - if (slot->isAt(container, index)) + if (slot->isAt(container, stackId)) return slot; } return nullptr; } -Slot* ContainerMenu::getSlot(int index) +ItemStack ContainerMenu::quickMoveStack(Container::SlotID slotId) { - return m_slots[index]; + return slotId >= 0 && slotId < int(m_slots.size()) ? getSlot(slotId)->getItem() : ItemStack::EMPTY; } -ItemStack ContainerMenu::quickMoveStack(int index) +void ContainerMenu::moveItemStackTo(ItemStack& item, Container::SlotID slotFrom, Container::SlotID slotTo, bool take) { - return index >= 0 && index < int(m_slots.size()) ? getSlot(index)->getItem() : ItemStack::EMPTY; -} - -void ContainerMenu::moveItemStackTo(ItemStack& item, int slotFrom, int slotTo, bool take) -{ - int index = slotFrom; + Container::SlotID slotId = slotFrom; if (take) - index = slotTo - 1; + slotId = slotTo - 1; if (item.isStackable()) { - while (item.m_count > 0 && ((!take && index < slotTo) || (take && index >= slotFrom))) + while (item.m_count > 0 && ((!take && slotId < slotTo) || (take && slotId >= slotFrom))) { - Slot* slot = getSlot(index); + Slot* slot = getSlot(slotId); ItemStack& slotItem = slot->getItem(); if (slotItem && slotItem.getId() == item.getId() && (!item.isStackedByData() || item.getAuxValue() == slotItem.getAuxValue())) { @@ -189,22 +184,22 @@ void ContainerMenu::moveItemStackTo(ItemStack& item, int slotFrom, int slotTo, b } if (take) - --index; + --slotId; else - ++index; + ++slotId; } } if (item.m_count > 0) { if (take) - index = slotTo - 1; + slotId = slotTo - 1; else - index = slotFrom; + slotId = slotFrom; - while ((!take && index < slotTo) || (take && index >= slotFrom)) + while ((!take && slotId < slotTo) || (take && slotId >= slotFrom)) { - Slot* slot = getSlot(index); + Slot* slot = getSlot(slotId); ItemStack& slotItem = slot->getItem(); if (!slotItem) { @@ -215,14 +210,14 @@ void ContainerMenu::moveItemStackTo(ItemStack& item, int slotFrom, int slotTo, b } if (take) - --index; + --slotId; else - ++index; + ++slotId; } } } -ItemStack ContainerMenu::clicked(int slotIndex, MouseButtonType mouseButton, bool quickMove, Player* player) +ItemStack ContainerMenu::clicked(Container::SlotID slotId, MouseButtonType mouseButton, bool quickMove, Player* player) { ItemStack result = ItemStack::EMPTY; @@ -231,7 +226,7 @@ ItemStack ContainerMenu::clicked(int slotIndex, MouseButtonType mouseButton, boo Inventory* inv = player->m_pInventory; - if (slotIndex == -999) + if (slotId == -999) { if (!inv->getCarried().isEmpty()) { @@ -255,19 +250,19 @@ ItemStack ContainerMenu::clicked(int slotIndex, MouseButtonType mouseButton, boo { if (quickMove) { - ItemStack quickMoved = quickMoveStack(slotIndex); + ItemStack quickMoved = quickMoveStack(slotId); if (!quickMoved.isEmpty()) { result = quickMoved; - Slot* slot = getSlot(slotIndex); + Slot* slot = getSlot(slotId); if (slot && slot->hasItem() && slot->getItem().m_count < quickMoved.m_count) - clicked(slotIndex, mouseButton, quickMove, player); + clicked(slotId, mouseButton, quickMove, player); } return result; } - Slot* slot = getSlot(slotIndex); + Slot* slot = getSlot(slotId); if (!slot) return result; @@ -380,12 +375,12 @@ ItemStack ContainerMenu::clicked(int slotIndex, MouseButtonType mouseButton, boo return result; } -void ContainerMenu::setItem(int index, ItemStack item) +void ContainerMenu::setItem(Container::SlotID slotId, ItemStack item) { - m_slots[index]->set(item); - if (!m_bBroadcastChanges && index >= 0 && index < (int)m_lastSlots.size()) + m_slots[slotId]->set(item); + if (!m_bBroadcastChanges && slotId >= 0 && slotId < (int)m_lastSlots.size()) { - m_lastSlots[index] = ItemStack(m_slots[index]->getItem()); + m_lastSlots[slotId] = ItemStack(m_slots[slotId]->getItem()); } } diff --git a/source/world/inventory/ContainerMenu.hpp b/source/world/inventory/ContainerMenu.hpp index 45d1aa360..ca1a115fc 100644 --- a/source/world/inventory/ContainerMenu.hpp +++ b/source/world/inventory/ContainerMenu.hpp @@ -28,20 +28,20 @@ class ContainerMenu : public ContainerContentChangeListener void addSlot(Slot* slot); virtual void addSlotListener(ContainerListener* listener); void sendData(int id, int value); - virtual void broadcastChanges(SlotID slot); + virtual void broadcastChanges(Container::SlotID slotId); virtual void broadcastChanges(); virtual void removed(Player* player); virtual void slotsChanged(Container* container); // Called getItems in PE and Java std::vector cloneItems(); - Slot* getSlotFor(Container* container, int index); - Slot* getSlot(int index); - virtual ItemStack clicked(int slotIndex, MouseButtonType mouseButton, bool quickMove, Player* player); - virtual ItemStack quickMoveStack(int index); - virtual void moveItemStackTo(ItemStack& item, int slotFrom, int slotTo, bool take); + Slot* getSlotFor(Container* container, Container::StackID stackId); + Slot* getSlot(Container::SlotID slotId) { return m_slots[slotId]; } + virtual ItemStack clicked(Container::SlotID slotId, MouseButtonType mouseButton, bool quickMove, Player* player); + virtual ItemStack quickMoveStack(Container::SlotID slotId); + virtual void moveItemStackTo(ItemStack& item, Container::SlotID slotFrom, Container::SlotID slotTo, bool take); - void setItem(int slotIndex, ItemStack item); + void setItem(Container::SlotID slotId, ItemStack item); void setAll(const std::vector& items); virtual void setData(int id, int value); @@ -59,7 +59,7 @@ class ContainerMenu : public ContainerContentChangeListener virtual bool isPauseScreen() const { return false; } public: - void containerContentChanged(Container* container, SlotID slot) override; + void containerContentChanged(Container* container, Container::StackID stackId) override; protected: std::vector m_lastSlots; diff --git a/source/world/inventory/CraftingContainer.cpp b/source/world/inventory/CraftingContainer.cpp index edfc5a04c..3e36eb922 100644 --- a/source/world/inventory/CraftingContainer.cpp +++ b/source/world/inventory/CraftingContainer.cpp @@ -1,9 +1,9 @@ #include "CraftingContainer.hpp" -CraftingContainer::CraftingContainer(ContainerMenu* menu, int width, int height) : - m_items(width * height), - m_pMenu(menu), - m_width(width) +CraftingContainer::CraftingContainer(ContainerMenu* menu, int width, int height) + : m_items(width * height) + , m_pMenu(menu) + , m_width(width) { } @@ -16,9 +16,9 @@ uint16_t CraftingContainer::getContainerSize() const return uint16_t(m_items.size()); } -ItemStack& CraftingContainer::getItem(int index) +ItemStack& CraftingContainer::getItem(StackID stackId) { - return m_items[index]; + return m_items[stackId]; } const ItemStack& CraftingContainer::getItem(int x, int y) @@ -36,11 +36,11 @@ std::string CraftingContainer::getName() const return "Crafting"; } -ItemStack CraftingContainer::removeItem(int index, int count) +ItemStack CraftingContainer::removeItem(StackID stackId, int count) { - if (index < 0 || index >= getContainerSize()) return ItemStack::EMPTY; + if (stackId < 0 || stackId >= getContainerSize()) return ItemStack::EMPTY; - ItemStack& item = m_items[index]; + ItemStack& item = m_items[stackId]; if (item) { ItemStack removed; @@ -48,13 +48,13 @@ ItemStack CraftingContainer::removeItem(int index, int count) if (item.m_count <= count) { removed = item; - m_items[index] = ItemStack::EMPTY; + m_items[stackId] = ItemStack::EMPTY; } else { removed = item.remove(count); if (item.m_count == 0) - m_items[index] = ItemStack::EMPTY; + m_items[stackId] = ItemStack::EMPTY; } m_pMenu->slotsChanged(this); @@ -64,16 +64,16 @@ ItemStack CraftingContainer::removeItem(int index, int count) return ItemStack::EMPTY; } -void CraftingContainer::setItem(int index, const ItemStack& item) +void CraftingContainer::setItem(StackID stackId, const ItemStack& item) { - if (index >= 0 && index < getContainerSize()) + if (stackId >= 0 && stackId < getContainerSize()) { - m_items[index] = item; + m_items[stackId] = item; m_pMenu->slotsChanged(this); } } -void CraftingContainer::setContainerChanged(SlotID slot) +void CraftingContainer::setContainerChanged(StackID stackId) { } diff --git a/source/world/inventory/CraftingContainer.hpp b/source/world/inventory/CraftingContainer.hpp index f450e2c9f..ea8c16a4e 100644 --- a/source/world/inventory/CraftingContainer.hpp +++ b/source/world/inventory/CraftingContainer.hpp @@ -17,15 +17,15 @@ class CraftingContainer : public Container virtual ~CraftingContainer(); uint16_t getContainerSize() const override; - ItemStack& getItem(int index) override; + ItemStack& getItem(StackID stackId) override; const ItemStack& getItem(int x, int y); std::string getName() const override; - ItemStack removeItem(int index, int amount) override; - void setItem(int index, const ItemStack& item) override; + ItemStack removeItem(StackID stackId, int amount) override; + void setItem(StackID stackId, const ItemStack& item) override; - void setContainerChanged(SlotID slot) override; + void setContainerChanged(StackID stackId) override; bool stillValid(Player* player) const override; private: diff --git a/source/world/inventory/CraftingMenu.cpp b/source/world/inventory/CraftingMenu.cpp index 728aad397..cdf3ed95f 100644 --- a/source/world/inventory/CraftingMenu.cpp +++ b/source/world/inventory/CraftingMenu.cpp @@ -70,19 +70,19 @@ bool CraftingMenu::stillValid(Player* player) const return !(player->distanceToSqr(Vec3(m_pos.x + 0.5f, m_pos.y + 0.5f, m_pos.z + 0.5f)) > 64.0f); } -ItemStack CraftingMenu::quickMoveStack(int index) +ItemStack CraftingMenu::quickMoveStack(Container::SlotID slotId) { ItemStack item = ItemStack::EMPTY; - Slot* slot = getSlot(index); + Slot* slot = getSlot(slotId); if (slot && slot->hasItem()) { ItemStack& slotItem = slot->getItem(); item = slotItem; - if (index == 0) + if (slotId == 0) moveItemStackTo(slotItem, 10, 46, true); - else if (index >= 10 && index < 37) + else if (slotId >= 10 && slotId < 37) moveItemStackTo(slotItem, 37, 46, false); - else if (index >= 37 && index < 46) + else if (slotId >= 37 && slotId < 46) moveItemStackTo(slotItem, 10, 37, false); else moveItemStackTo(slotItem, 10, 46, false); diff --git a/source/world/inventory/CraftingMenu.hpp b/source/world/inventory/CraftingMenu.hpp index fc8e9bd83..ba0531d3a 100644 --- a/source/world/inventory/CraftingMenu.hpp +++ b/source/world/inventory/CraftingMenu.hpp @@ -13,7 +13,7 @@ class CraftingMenu : public ContainerMenu void slotsChanged(Container* container) override; void removed(Player* player) override; bool stillValid(Player* player) const override; - ItemStack quickMoveStack(int index) override; + ItemStack quickMoveStack(Container::SlotID slotId) override; private: const TilePos m_pos; diff --git a/source/world/inventory/FurnaceMenu.cpp b/source/world/inventory/FurnaceMenu.cpp index b3c3cb2f3..b31d453eb 100644 --- a/source/world/inventory/FurnaceMenu.cpp +++ b/source/world/inventory/FurnaceMenu.cpp @@ -68,19 +68,19 @@ void FurnaceMenu::setData(int index, int value) m_furnace->m_litDuration = value; } -ItemStack FurnaceMenu::quickMoveStack(int index) +ItemStack FurnaceMenu::quickMoveStack(Container::SlotID slotId) { ItemStack item; - Slot* slot = getSlot(index); + Slot* slot = getSlot(slotId); if (slot && slot->hasItem()) { ItemStack& slotItem = slot->getItem(); item = slotItem.copy(); - if (index == 2) + if (slotId == 2) moveItemStackTo(slotItem, 3, 39, true); - else if (index >= 3 && index < 30) + else if (slotId >= 3 && slotId < 30) moveItemStackTo(slotItem, 30, 39, false); - else if (index >= 30 && index < 39) + else if (slotId >= 30 && slotId < 39) moveItemStackTo(slotItem, 3, 30, false); else moveItemStackTo(slotItem, 3, 39, false); diff --git a/source/world/inventory/FurnaceMenu.hpp b/source/world/inventory/FurnaceMenu.hpp index 42025cf41..d51d0dd2e 100644 --- a/source/world/inventory/FurnaceMenu.hpp +++ b/source/world/inventory/FurnaceMenu.hpp @@ -14,7 +14,7 @@ class FurnaceMenu : public ContainerMenu void addSlotListener(ContainerListener* listener) override; void broadcastChanges() override; void setData(int, int) override; - ItemStack quickMoveStack(int index) override; + ItemStack quickMoveStack(Container::SlotID slotId) override; private: FurnaceTileEntity* m_furnace; diff --git a/source/world/inventory/InventoryMenu.cpp b/source/world/inventory/InventoryMenu.cpp index 1e5b2c37e..754b391c7 100644 --- a/source/world/inventory/InventoryMenu.cpp +++ b/source/world/inventory/InventoryMenu.cpp @@ -73,19 +73,19 @@ bool InventoryMenu::stillValid(Player* player) const return true; } -ItemStack InventoryMenu::quickMoveStack(int index) +ItemStack InventoryMenu::quickMoveStack(Container::SlotID slotId) { ItemStack item = ItemStack::EMPTY; - Slot* slot = getSlot(index); + Slot* slot = getSlot(slotId); if (slot && slot->hasItem()) { ItemStack& slotItem = slot->getItem(); item = slotItem; - if (index == 0) + if (slotId == 0) moveItemStackTo(slotItem, 9, 45, true); - else if (index >= 9 && index < 36) + else if (slotId >= 9 && slotId < 36) moveItemStackTo(slotItem, 36, 45, false); - else if (index >= 36 && index < 45) + else if (slotId >= 36 && slotId < 45) moveItemStackTo(slotItem, 9, 36, false); else moveItemStackTo(slotItem, 9, 45, false); diff --git a/source/world/inventory/InventoryMenu.hpp b/source/world/inventory/InventoryMenu.hpp index 683d02083..6c8a2df1c 100644 --- a/source/world/inventory/InventoryMenu.hpp +++ b/source/world/inventory/InventoryMenu.hpp @@ -13,7 +13,7 @@ class InventoryMenu : public ContainerMenu void slotsChanged(Container* container) override; void removed(Player* player) override; bool stillValid(Player* player) const override; - ItemStack quickMoveStack(int index) override; + ItemStack quickMoveStack(Container::SlotID slotId) override; public: CraftingContainer* m_pCraftSlots; diff --git a/source/world/inventory/ResultContainer.cpp b/source/world/inventory/ResultContainer.cpp index 1cf3be2d6..52a6caeb3 100644 --- a/source/world/inventory/ResultContainer.cpp +++ b/source/world/inventory/ResultContainer.cpp @@ -13,7 +13,7 @@ uint16_t ResultContainer::getContainerSize() const return 1; } -ItemStack& ResultContainer::getItem(int index) +ItemStack& ResultContainer::getItem(StackID stackId) { return m_item; } @@ -23,24 +23,23 @@ std::string ResultContainer::getName() const return ""; } -ItemStack ResultContainer::removeItem(int index, int) +ItemStack ResultContainer::removeItem(StackID stackId, int) { - if (index == 0) - { - ItemStack result = m_item; - m_item = ItemStack::EMPTY; - return result; - } - return ItemStack::EMPTY; + if (stackId != 0) + return ItemStack::EMPTY; + + ItemStack result = m_item; + m_item = ItemStack::EMPTY; + return result; } -void ResultContainer::setItem(int index, const ItemStack& item) +void ResultContainer::setItem(StackID stackId, const ItemStack& item) { - if (index == 0) + if (stackId == 0) m_item = item; } -void ResultContainer::setContainerChanged(SlotID slot) +void ResultContainer::setContainerChanged(StackID stackId) { } diff --git a/source/world/inventory/ResultContainer.hpp b/source/world/inventory/ResultContainer.hpp index 14228c1fb..9ff3b9cab 100644 --- a/source/world/inventory/ResultContainer.hpp +++ b/source/world/inventory/ResultContainer.hpp @@ -12,14 +12,14 @@ class ResultContainer : public Container virtual ~ResultContainer(); uint16_t getContainerSize() const override; - ItemStack& getItem(int index) override; + ItemStack& getItem(StackID stackId) override; std::string getName() const override; - ItemStack removeItem(int index, int amount) override; - void setItem(int index, const ItemStack& item) override; + ItemStack removeItem(StackID stackId, int amount) override; + void setItem(StackID stackId, const ItemStack& item) override; - void setContainerChanged(SlotID slot) override; + void setContainerChanged(StackID stackId) override; bool stillValid(Player* player) const override; private: diff --git a/source/world/inventory/SimpleContainer.cpp b/source/world/inventory/SimpleContainer.cpp index d6ce69a77..6974e4956 100644 --- a/source/world/inventory/SimpleContainer.cpp +++ b/source/world/inventory/SimpleContainer.cpp @@ -2,7 +2,7 @@ #include "ContainerContentChangeListener.hpp" #include "ContainerSizeChangeListener.hpp" -SimpleContainer::SimpleContainer(int size, const std::string& name) +SimpleContainer::SimpleContainer(Size size, const std::string& name) : m_items(size) , m_name(name) { @@ -13,12 +13,12 @@ uint16_t SimpleContainer::getContainerSize() const return uint16_t(m_items.size()); } -ItemStack& SimpleContainer::getItem(int index) +ItemStack& SimpleContainer::getItem(StackID index) { return m_items[index]; } -ItemStack SimpleContainer::removeItem(int index, int count) +ItemStack SimpleContainer::removeItem(StackID index, int count) { if (!m_items[index].isEmpty()) { @@ -43,7 +43,7 @@ ItemStack SimpleContainer::removeItem(int index, int count) return ItemStack::EMPTY; } -void SimpleContainer::setItem(int index, const ItemStack& item) +void SimpleContainer::setItem(StackID index, const ItemStack& item) { m_items[index] = item; if (!item.isEmpty() && item.m_count > getMaxStackSize()) @@ -57,12 +57,12 @@ std::string SimpleContainer::getName() const return m_name; } -void SimpleContainer::setContainerChanged(SlotID slot) +void SimpleContainer::setContainerChanged(StackID stackId) { for (ContentChangeListeners::iterator it = m_contentChangeListeners.begin(); it != m_contentChangeListeners.end(); it++) { ContainerContentChangeListener* pListener = *it; - pListener->containerContentChanged(this, slot); + pListener->containerContentChanged(this, stackId); } } diff --git a/source/world/inventory/SimpleContainer.hpp b/source/world/inventory/SimpleContainer.hpp index 2df4756a4..93454c140 100644 --- a/source/world/inventory/SimpleContainer.hpp +++ b/source/world/inventory/SimpleContainer.hpp @@ -8,15 +8,15 @@ class SimpleContainer : public Container { public: - SimpleContainer(int size, const std::string& name); + SimpleContainer(Size size, const std::string& name); public: uint16_t getContainerSize() const override; - ItemStack& getItem(int index) override; - ItemStack removeItem(int index, int count) override; - void setItem(int index, const ItemStack& item) override; + ItemStack& getItem(StackID index) override; + ItemStack removeItem(StackID index, int count) override; + void setItem(StackID index, const ItemStack& item) override; std::string getName() const override; - void setContainerChanged(SlotID slot) override; + void setContainerChanged(StackID stackId) override; bool stillValid(Player* player) const override; void addContentChangeListener(ContainerContentChangeListener* listener) override; void addSizeChangeListener(ContainerSizeChangeListener* listener) override; diff --git a/source/world/inventory/Slot.cpp b/source/world/inventory/Slot.cpp index 81beb469a..26d435595 100644 --- a/source/world/inventory/Slot.cpp +++ b/source/world/inventory/Slot.cpp @@ -1,9 +1,9 @@ #include "Slot.hpp" -Slot::Slot(Container* container, int slot, Group group) : - m_pContainer(container), - m_slot(slot), - m_group(group) +Slot::Slot(Container* container, Container::StackID stackId, Group group) + : m_pContainer(container) + , m_stackId(stackId) + , m_group(group) { } @@ -18,6 +18,6 @@ bool Slot::canSync() const void Slot::set(const ItemStack& item) { - m_pContainer->setItem(m_slot, item); + m_pContainer->setItem(m_stackId, item); setChanged(); } diff --git a/source/world/inventory/Slot.hpp b/source/world/inventory/Slot.hpp index f4286639d..55ef1ae39 100644 --- a/source/world/inventory/Slot.hpp +++ b/source/world/inventory/Slot.hpp @@ -1,5 +1,7 @@ #pragma once +#include + #include "Container.hpp" class ItemStack; @@ -17,7 +19,7 @@ class Slot ARMOR }; - Slot(Container* container, int slot, Group group = CONTAINER); + Slot(Container* container, Container::StackID stackId, Group group = CONTAINER); virtual ~Slot(); virtual bool canSync() const; @@ -26,23 +28,23 @@ class Slot virtual bool mayPlace(const ItemStack& item) const { return true; } - virtual ItemStack& getItem() { return m_pContainer->getItem(m_slot); } + virtual ItemStack& getItem() { return m_pContainer->getItem(m_stackId); } virtual bool hasItem() { return !getItem().isEmpty(); } virtual void set(const ItemStack& item); - virtual void setChanged() { m_pContainer->setContainerChanged(m_slot); } + virtual void setChanged() { m_pContainer->setContainerChanged(m_stackId); } virtual int getMaxStackSize() const { return m_pContainer->getMaxStackSize(); } - virtual ItemStack remove(int count) { return m_pContainer->removeItem(m_slot, count); } + virtual ItemStack remove(int count) { return m_pContainer->removeItem(m_stackId, count); } - virtual bool isAt(Container* cont, int s) { return cont == m_pContainer && s == m_slot; } + virtual bool isAt(Container* cont, Container::StackID stackId) { return cont == m_pContainer && stackId == m_stackId; } public: Container* m_pContainer; - int m_slot; // the position in the attached container - int m_index; // the position in the ContainerMenu::m_slots vector + Container::SlotID m_id; // the position in the ContainerMenu::m_slots vector + Container::StackID m_stackId; // the position in the Container::m_items vector Group m_group; }; \ No newline at end of file diff --git a/source/world/item/Inventory.cpp b/source/world/item/Inventory.cpp index e3b999582..bc512143d 100644 --- a/source/world/item/Inventory.cpp +++ b/source/world/item/Inventory.cpp @@ -247,23 +247,23 @@ int Inventory::addResource(const ItemStack& item) } } -ItemStack Inventory::removeItem(int index, int count) +ItemStack Inventory::removeItem(StackID stackId, int count) { - ItemStack& item = getItem(index); + ItemStack& item = getItem(stackId); if (!item.isEmpty()) { if (item.m_count <= count) { ItemStack removed = item; - setItem(index, ItemStack::EMPTY); + setItem(stackId, ItemStack::EMPTY); return removed; } else { ItemStack removed = item.remove(count); if (item.m_count == 0) - setItem(index, ItemStack::EMPTY); + setItem(stackId, ItemStack::EMPTY); return removed; } @@ -369,14 +369,14 @@ bool Inventory::hasUnlimitedResource(const ItemStack& item) const return true; } -ItemStack& Inventory::getItem(int slotNo) +ItemStack& Inventory::getItem(StackID stackId) { - assert(slotNo >= 0 && slotNo < getContainerSize()); + assert(stackId >= 0 && stackId < getContainerSize()); - if (size_t(slotNo) < m_items.size()) - return m_items[slotNo]; + if (size_t(stackId) < m_items.size()) + return m_items[stackId]; else - return m_armor[slotNo - m_items.size()]; + return m_armor[stackId - m_items.size()]; } ItemStack& Inventory::getArmor(Item::EquipmentSlot slotNo) @@ -394,15 +394,15 @@ int Inventory::getSelectedItemId() return getItem(m_selectedSlot).getId(); } -void Inventory::setItem(int index, const ItemStack& item) +void Inventory::setItem(StackID stackId, const ItemStack& item) { - if ((size_t)index >= m_items.size()) + if ((size_t)stackId >= m_items.size()) { - m_armor[index - m_items.size()] = item; + m_armor[stackId - m_items.size()] = item; } else { - m_items[index] = item; + m_items[stackId] = item; } } @@ -426,11 +426,11 @@ void Inventory::pickItem(int itemID, int data, int maxHotBarSlot) { Item* selectItem = Item::items[itemID]; - if (!selectItem) return; + if (!selectItem && itemID != TILE_AIR) return; for (size_t i = 0; i < m_items.size(); i++) { - if (!m_items[i] || m_items[i].getId() != itemID || m_items[i].getAuxValue() != data) + if (m_items[i].getId() != itemID || m_items[i].getAuxValue() != data) continue; if (i < size_t(maxHotBarSlot)) @@ -440,7 +440,7 @@ void Inventory::pickItem(int itemID, int data, int maxHotBarSlot) return; } - if (m_pPlayer->isCreative()) + if (m_pPlayer->isCreative() && selectItem != nullptr) { ItemStack oldSelected = getSelected(); setItem(m_selectedSlot, ItemStack(selectItem, 1, data)); @@ -466,17 +466,17 @@ void Inventory::selectItem(int itemID, int maxHotBarSlot) } } -void Inventory::swapItems(int indexA, int indexB) +void Inventory::swapItems(StackID stackIdA, StackID stackIdB) { - std::swap(getItem(indexA), getItem(indexB)); + std::swap(getItem(stackIdA), getItem(stackIdB)); } -void Inventory::selectSlot(int slotNo) +void Inventory::selectSlot(SlotID slotId) { - if (slotNo < 0 || slotNo >= C_MAX_HOTBAR_ITEMS) + if (slotId < 0 || slotId >= C_MAX_HOTBAR_ITEMS) return; - m_selectedSlot = slotNo; + m_selectedSlot = slotId; } int Inventory::getAttackDamage(Entity* pEnt) @@ -629,12 +629,12 @@ GameType Inventory::_getGameMode() const return m_pPlayer->getPlayerGameType(); } -void Inventory::setContainerChanged(SlotID slot) +void Inventory::setContainerChanged(StackID stackId) { for (ContentChangeListeners::iterator it = m_contentChangeListeners.begin(); it != m_contentChangeListeners.end(); ++it) { ContainerContentChangeListener* listener = *it; - listener->containerContentChanged(this, slot); + listener->containerContentChanged(this, stackId); } } diff --git a/source/world/item/Inventory.hpp b/source/world/item/Inventory.hpp index 09a5c7882..779b1302d 100644 --- a/source/world/item/Inventory.hpp +++ b/source/world/item/Inventory.hpp @@ -41,14 +41,14 @@ class Inventory : public Container bool add(ItemStack& item); void tick(); - ItemStack& getItem(int slotNo) override; + ItemStack& getItem(StackID stackId) override; ItemStack& getArmor(Item::EquipmentSlot slotNo); ItemStack& getSelectedItem(); int getSelectedItemId(); - void setItem(int index, const ItemStack& item) override; + void setItem(StackID stackId, const ItemStack& item) override; void setSelectedItem(ItemStack item); - ItemStack removeItem(int index, int count) override; + ItemStack removeItem(StackID stackId, int count) override; bool removeResource(int id); void setCarried(const ItemStack& item); @@ -56,8 +56,8 @@ class Inventory : public Container void pickItem(int itemID, int data, int maxHotBarSlot); void selectItem(int itemID, int maxHotBarSlot); - void swapItems(int, int); - void selectSlot(int slotNo); + void swapItems(StackID stackIdA, StackID stackIdB); + void selectSlot(SlotID slotId); int getAttackDamage(Entity*); @@ -83,7 +83,7 @@ class Inventory : public Container return "Inventory"; } - void setContainerChanged(SlotID slot) override; + void setContainerChanged(StackID stackId) override; void addContentChangeListener(ContainerContentChangeListener* listener) override; void removeContentChangeListener(ContainerContentChangeListener* listener) override; diff --git a/source/world/item/ItemStack.cpp b/source/world/item/ItemStack.cpp index 0ced312cf..9ef6b1258 100644 --- a/source/world/item/ItemStack.cpp +++ b/source/world/item/ItemStack.cpp @@ -19,7 +19,7 @@ ItemStack::TAG_REPAIR_COST = "RepairCost", ItemStack::TAG_ENCHANTS = "ench"; const ItemStack ItemStack::EMPTY; -#define C_INVALID_ID -1 +#define C_INVALID_ID 0 void ItemStack::_init(int id, int count, int auxValue) { diff --git a/source/world/tile/entity/ChestTileEntity.cpp b/source/world/tile/entity/ChestTileEntity.cpp index e18c18130..40610cbe8 100644 --- a/source/world/tile/entity/ChestTileEntity.cpp +++ b/source/world/tile/entity/ChestTileEntity.cpp @@ -25,8 +25,8 @@ bool ChestTileEntity::stillValid(Player* player) const return player->distanceToSqr(m_pos + 0.5f) <= 64.0f; } -void ChestTileEntity::setContainerChanged(SlotID slot) +void ChestTileEntity::setContainerChanged(StackID stackId) { - SimpleContainer::setContainerChanged(slot); + SimpleContainer::setContainerChanged(stackId); TileEntity::setChanged(); } diff --git a/source/world/tile/entity/ChestTileEntity.hpp b/source/world/tile/entity/ChestTileEntity.hpp index 0fb177245..73bfd2779 100644 --- a/source/world/tile/entity/ChestTileEntity.hpp +++ b/source/world/tile/entity/ChestTileEntity.hpp @@ -13,5 +13,5 @@ class ChestTileEntity : public TileEntity, public SimpleContainer { bool stillValid(Player* player) const override; - void setContainerChanged(SlotID slot) override; + void setContainerChanged(StackID stackId) override; }; diff --git a/source/world/tile/entity/FurnaceTileEntity.cpp b/source/world/tile/entity/FurnaceTileEntity.cpp index 28e7ead28..8962225c3 100644 --- a/source/world/tile/entity/FurnaceTileEntity.cpp +++ b/source/world/tile/entity/FurnaceTileEntity.cpp @@ -116,9 +116,9 @@ bool FurnaceTileEntity::stillValid(Player* player) const return player->distanceToSqr(m_pos + 0.5f) <= 64.0; } -void FurnaceTileEntity::setContainerChanged(SlotID slot) +void FurnaceTileEntity::setContainerChanged(StackID stackId) { - SimpleContainer::setContainerChanged(slot); + SimpleContainer::setContainerChanged(stackId); TileEntity::setChanged(); } diff --git a/source/world/tile/entity/FurnaceTileEntity.hpp b/source/world/tile/entity/FurnaceTileEntity.hpp index 0a50b691e..c249797fa 100644 --- a/source/world/tile/entity/FurnaceTileEntity.hpp +++ b/source/world/tile/entity/FurnaceTileEntity.hpp @@ -16,7 +16,7 @@ class FurnaceTileEntity : public SimpleContainer, public TileEntity public: void tick() override; bool stillValid(Player* player) const override; - void setContainerChanged(SlotID slot) override; + void setContainerChanged(StackID stackId) override; void load(const CompoundTag& tag) override; void save(CompoundTag& tag) const override; std::string getName() const override; From 32ef69255ddf45e0cbac7cbdf4a7a02683af4c9b Mon Sep 17 00:00:00 2001 From: Brent Da Mage Date: Thu, 14 May 2026 02:18:50 -0500 Subject: [PATCH 42/63] fix --- source/world/item/Inventory.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/world/item/Inventory.cpp b/source/world/item/Inventory.cpp index bc512143d..02f42ba70 100644 --- a/source/world/item/Inventory.cpp +++ b/source/world/item/Inventory.cpp @@ -300,7 +300,7 @@ bool Inventory::removeResource(int id) // Doesn't exist in PE void Inventory::tick() { - for (size_t i = 0; i < m_items.size(); i++) + for (SlotID i = 0; i < m_items.size(); i++) { ItemStack& item = m_items[i]; From 4e4a327430494babeee70f3110a1c530d3848396 Mon Sep 17 00:00:00 2001 From: Brent Da Mage Date: Thu, 14 May 2026 02:38:23 -0500 Subject: [PATCH 43/63] Fixed mismatched types --- source/client/gui/Gui.cpp | 28 +++++++++---------- source/client/player/LocalPlayer.cpp | 9 +++--- source/client/player/LocalPlayer.hpp | 2 +- source/client/renderer/ItemInHandRenderer.cpp | 4 +-- source/world/entity/Player.cpp | 2 +- source/world/item/Inventory.cpp | 28 +++++++++---------- source/world/item/Inventory.hpp | 6 ++-- 7 files changed, 40 insertions(+), 39 deletions(-) diff --git a/source/client/gui/Gui.cpp b/source/client/gui/Gui.cpp index 7f6d0dda8..f3b5ed054 100644 --- a/source/client/gui/Gui.cpp +++ b/source/client/gui/Gui.cpp @@ -349,22 +349,22 @@ void Gui::handleClick(int clickID, int mouseX, int mouseY) void Gui::handleScrollWheel(bool down) { - Container::SlotID slotId = m_pMinecraft->m_pLocalPlayer->m_pInventory->m_selectedSlot; + Container::StackID stackId = m_pMinecraft->m_pLocalPlayer->m_pInventory->m_selectedStackId; int maxItems = getNumUsableSlots() - 1; if (down) { - if (slotId++ == maxItems) - slotId = 0; + if (stackId++ == maxItems) + stackId = 0; } else { - if (slotId-- == 0) - slotId = maxItems; + if (stackId-- == 0) + stackId = maxItems; } - m_pMinecraft->m_pLocalPlayer->m_pInventory->selectSlot(slotId); + m_pMinecraft->m_pLocalPlayer->m_pInventory->selectSlot(stackId); } void Gui::handleKeyPressed(int keyCode) @@ -387,21 +387,21 @@ void Gui::handleKeyPressed(int keyCode) int maxItems = getNumSlots() - 1; if (m_pMinecraft->useTouchscreen()) maxItems--; - Container::SlotID* slotId = &m_pMinecraft->m_pLocalPlayer->m_pInventory->m_selectedSlot; + Container::StackID* stackId = &m_pMinecraft->m_pLocalPlayer->m_pInventory->m_selectedStackId; if (slotR) { - if (*slotId < maxItems) - (*slotId)++; + if (*stackId < maxItems) + (*stackId)++; else - *slotId = 0; + *stackId = 0; } else if (slotL) { - if (*slotId > 0) - (*slotId)--; + if (*stackId > 0) + (*stackId)--; else - *slotId = maxItems; + *stackId = maxItems; } return; } @@ -690,7 +690,7 @@ void Gui::renderToolBar(float f, float alpha) Inventory* inventory = player->m_pInventory; // selection mark - blit(-1 - hotbarWidth / 2 + 20 * inventory->m_selectedSlot, -23, 0, 22, 24, 22, 0, 0); + blit(-1 - hotbarWidth / 2 + 20 * inventory->m_selectedStackId, -23, 0, 22, 24, 22, 0, 0); // chat and pause button for mobile devices if (mc->isTouchscreen()) diff --git a/source/client/player/LocalPlayer.cpp b/source/client/player/LocalPlayer.cpp index a8dd0d66c..dfb8a4d1d 100644 --- a/source/client/player/LocalPlayer.cpp +++ b/source/client/player/LocalPlayer.cpp @@ -23,15 +23,17 @@ void LocalPlayer::_init() // multiplayer related m_lastSentPos = Vec3::ZERO; m_lastSentRot = Vec2::ZERO; + m_lastSelectedStackId = m_pInventory->m_selectedStackId; // multiplayer related -- end m_renderArmRot = Vec2::ZERO; m_lastRenderArmRot = Vec2::ZERO; - m_lastSelectedSlot = m_pInventory->getSelectedItemId(); } LocalPlayer::LocalPlayer(Minecraft* pMinecraft, Level* pLevel, User* pUser, GameType playerGameType, int dimensionId) : Player(pLevel, playerGameType) { + m_lastSelectedStackId = 0; + field_BEC = 0; field_BF0 = Vec3::ZERO; field_BFC = 0.0f; @@ -43,7 +45,6 @@ LocalPlayer::LocalPlayer(Minecraft* pMinecraft, Level* pLevel, User* pUser, Game field_C14 = 0.0f; field_C18 = 0.0f; field_C1C = 0.0f; - m_lastSelectedSlot = 0; m_pMoveInput = nullptr; m_pMinecraft = pMinecraft; @@ -343,9 +344,9 @@ void LocalPlayer::tick() { sendPosition(); - if (m_lastSelectedSlot != m_pInventory->m_selectedSlot) + if (m_lastSelectedStackId != m_pInventory->m_selectedStackId) { - m_lastSelectedSlot = m_pInventory->m_selectedSlot; + m_lastSelectedStackId = m_pInventory->m_selectedStackId; const ItemStack& item = m_pInventory->getSelectedItem(); m_pMinecraft->m_pRakNetInstance->send(new PlayerEquipmentPacket(m_EntityID, item.getId(), item.getAuxValue())); } diff --git a/source/client/player/LocalPlayer.hpp b/source/client/player/LocalPlayer.hpp index 076c5abe1..c5dbdacff 100644 --- a/source/client/player/LocalPlayer.hpp +++ b/source/client/player/LocalPlayer.hpp @@ -56,6 +56,7 @@ class LocalPlayer : public Player // multiplayer related Vec3 m_lastSentPos; Vec2 m_lastSentRot; + Container::StackID m_lastSelectedStackId; // multiplayer related -- end public: @@ -71,7 +72,6 @@ class LocalPlayer : public Player float field_C18; float field_C1C; int m_nAutoJumpFrames; - int m_lastSelectedSlot; Minecraft* m_pMinecraft; IMoveInput* m_pMoveInput; Vec2 m_renderArmRot; diff --git a/source/client/renderer/ItemInHandRenderer.cpp b/source/client/renderer/ItemInHandRenderer.cpp index 0501474af..8d6b73b11 100644 --- a/source/client/renderer/ItemInHandRenderer.cpp +++ b/source/client/renderer/ItemInHandRenderer.cpp @@ -396,7 +396,7 @@ void ItemInHandRenderer::tick() ItemStack& item = m_pMinecraft->m_pLocalPlayer->m_pInventory->getSelectedItem(); - bool bSameItem = m_pMinecraft->m_pLocalPlayer->m_pInventory->m_selectedSlot == m_lastSlot && m_selectedItem == item; + bool bSameItem = m_pMinecraft->m_pLocalPlayer->m_pInventory->m_selectedStackId == m_lastSlot && m_selectedItem == item; if (item.isEmpty() && m_selectedItem.isEmpty()) bSameItem = true; @@ -424,7 +424,7 @@ void ItemInHandRenderer::tick() if (m_height < 0.1f) { m_selectedItem = ItemStack(item); - m_lastSlot = m_pMinecraft->m_pLocalPlayer->m_pInventory->m_selectedSlot; + m_lastSlot = m_pMinecraft->m_pLocalPlayer->m_pInventory->m_selectedStackId; } } diff --git a/source/world/entity/Player.cpp b/source/world/entity/Player.cpp index 72480b501..c9e745d6a 100644 --- a/source/world/entity/Player.cpp +++ b/source/world/entity/Player.cpp @@ -266,7 +266,7 @@ void Player::tick() const ItemStack& Player::getCarriedItem() const { // This only gets the first row slot - /*ItemStack* item = m_pInventory->getItem(m_pInventory->m_selectedSlot); + /*ItemStack* item = m_pInventory->getItem(m_pInventory->m_selectedStackId); if (ItemStack::isNull(item)) return nullptr; diff --git a/source/world/item/Inventory.cpp b/source/world/item/Inventory.cpp index 02f42ba70..100ad7199 100644 --- a/source/world/item/Inventory.cpp +++ b/source/world/item/Inventory.cpp @@ -11,7 +11,7 @@ Inventory::Inventory(Player* pPlayer) : m_items(C_NUM_INVENTORY_SLOTS), m_armor(C_NUM_ARMOR_SLOTS) { m_pPlayer = pPlayer; - m_selectedSlot = 0; + m_selectedStackId = 0; } Inventory::~Inventory() @@ -274,7 +274,7 @@ ItemStack Inventory::removeItem(StackID stackId, int count) int Inventory::getSlot(int id) { - for (size_t i = 0; i < m_items.size(); ++i) + for (StackID i = 0; i < m_items.size(); ++i) { if (!m_items[i].isEmpty() && m_items[i].getId() == id) return i; @@ -300,7 +300,7 @@ bool Inventory::removeResource(int id) // Doesn't exist in PE void Inventory::tick() { - for (SlotID i = 0; i < m_items.size(); i++) + for (StackID i = 0; i < m_items.size(); i++) { ItemStack& item = m_items[i]; @@ -308,7 +308,7 @@ void Inventory::tick() { if (item.m_popTime > 0) item.m_popTime--; - item.getItem()->inventoryTick(&item, m_pPlayer->m_pLevel, m_pPlayer, i, i == m_selectedSlot); + item.getItem()->inventoryTick(&item, m_pPlayer->m_pLevel, m_pPlayer, i, i == m_selectedStackId); } } } @@ -386,12 +386,12 @@ ItemStack& Inventory::getArmor(Item::EquipmentSlot slotNo) ItemStack& Inventory::getSelectedItem() { - return getItem(m_selectedSlot); + return getItem(m_selectedStackId); } int Inventory::getSelectedItemId() { - return getItem(m_selectedSlot).getId(); + return getItem(m_selectedStackId).getId(); } void Inventory::setItem(StackID stackId, const ItemStack& item) @@ -408,7 +408,7 @@ void Inventory::setItem(StackID stackId, const ItemStack& item) void Inventory::setSelectedItem(ItemStack item) { - setItem(m_selectedSlot, item); + setItem(m_selectedStackId, item); } void Inventory::setCarried(const ItemStack& carried) @@ -436,14 +436,14 @@ void Inventory::pickItem(int itemID, int data, int maxHotBarSlot) if (i < size_t(maxHotBarSlot)) selectSlot(i); else - swapItems(i, m_selectedSlot); + swapItems(i, m_selectedStackId); return; } if (m_pPlayer->isCreative() && selectItem != nullptr) { ItemStack oldSelected = getSelected(); - setItem(m_selectedSlot, ItemStack(selectItem, 1, data)); + setItem(m_selectedStackId, ItemStack(selectItem, 1, data)); if (!oldSelected.isEmpty()) addResource(oldSelected); } } @@ -460,9 +460,9 @@ void Inventory::selectItem(int itemID, int maxHotBarSlot) continue; if (i < size_t(maxHotBarSlot)) - m_selectedSlot = i; + m_selectedStackId = i; else - swapItems(i, m_selectedSlot); + swapItems(i, m_selectedStackId); } } @@ -471,12 +471,12 @@ void Inventory::swapItems(StackID stackIdA, StackID stackIdB) std::swap(getItem(stackIdA), getItem(stackIdB)); } -void Inventory::selectSlot(SlotID slotId) +void Inventory::selectSlot(StackID stackId) { - if (slotId < 0 || slotId >= C_MAX_HOTBAR_ITEMS) + if (stackId < 0 || stackId >= C_MAX_HOTBAR_ITEMS) return; - m_selectedSlot = slotId; + m_selectedStackId = stackId; } int Inventory::getAttackDamage(Entity* pEnt) diff --git a/source/world/item/Inventory.hpp b/source/world/item/Inventory.hpp index 779b1302d..a5a8e5b8c 100644 --- a/source/world/item/Inventory.hpp +++ b/source/world/item/Inventory.hpp @@ -57,7 +57,7 @@ class Inventory : public Container void pickItem(int itemID, int data, int maxHotBarSlot); void selectItem(int itemID, int maxHotBarSlot); void swapItems(StackID stackIdA, StackID stackIdB); - void selectSlot(SlotID slotId); + void selectSlot(StackID stackId); int getAttackDamage(Entity*); @@ -72,7 +72,7 @@ class Inventory : public Container bool contains(const ItemStack&) const; - uint16_t getSelectedSlotNo() const { return m_selectedSlot; } + StackID getSelectedSlotNo() const { return m_selectedStackId; } // v0.2.0 name alias ItemStack& getSelected() { return getSelectedItem(); } @@ -98,7 +98,7 @@ class Inventory : public Container public: Player* m_pPlayer; - SlotID m_selectedSlot; + StackID m_selectedStackId; private: ItemStack m_carried; From 6e4f426ca55f02a16bd348b55dcc732d625ce005 Mon Sep 17 00:00:00 2001 From: Brent Da Mage Date: Thu, 14 May 2026 02:49:48 -0500 Subject: [PATCH 44/63] fix --- source/world/tile/MusicTile.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/world/tile/MusicTile.cpp b/source/world/tile/MusicTile.cpp index 372077028..d0b7d6427 100644 --- a/source/world/tile/MusicTile.cpp +++ b/source/world/tile/MusicTile.cpp @@ -94,5 +94,5 @@ void MusicTile::triggerEvent(Level* level, const TileEvent& event) } level->playSound(event.pos + 0.5f, "note." + soundType, 3.0f, std::pow(2.0f, (note - 12) / 12.0f)); - level->addParticle("note", Vec3(event.pos.x + 0.5f, event.pos.y + 1.2f, event.pos.z + 0.5f), Vec3(note / 24.0f, 0, 0)); + level->addParticle("note", Vec3(event.pos.x + 0.5f, event.pos.y + 1.2f, event.pos.z + 0.5f), Vec3(note / 24.0f, 0.0f, 0.0f)); } From 3bd62e787832c4a8b87664f32a773107ccd7f23f Mon Sep 17 00:00:00 2001 From: Brent Da Mage Date: Thu, 14 May 2026 03:03:57 -0500 Subject: [PATCH 45/63] Rename --- source/client/network/ClientSideNetworkHandler.cpp | 2 +- source/network/packets/ContainerSetDataPacket.cpp | 8 ++++---- source/network/packets/ContainerSetDataPacket.hpp | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/source/client/network/ClientSideNetworkHandler.cpp b/source/client/network/ClientSideNetworkHandler.cpp index 38b84790f..1191d205a 100644 --- a/source/client/network/ClientSideNetworkHandler.cpp +++ b/source/client/network/ClientSideNetworkHandler.cpp @@ -801,7 +801,7 @@ void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& guid, ContainerS return; pContainerMenu->m_bBroadcastChanges = false; - pContainerMenu->setData(packet->m_slotId, packet->m_value); + pContainerMenu->setData(packet->m_id, packet->m_value); pContainerMenu->m_bBroadcastChanges = true; } diff --git a/source/network/packets/ContainerSetDataPacket.cpp b/source/network/packets/ContainerSetDataPacket.cpp index e003a6144..0e2731fa9 100644 --- a/source/network/packets/ContainerSetDataPacket.cpp +++ b/source/network/packets/ContainerSetDataPacket.cpp @@ -1,9 +1,9 @@ #include "ContainerSetDataPacket.hpp" #include "network/NetEventCallback.hpp" -ContainerSetDataPacket::ContainerSetDataPacket(int8_t containerId, int16_t slotId, int16_t value) +ContainerSetDataPacket::ContainerSetDataPacket(int8_t containerId, int16_t id, int16_t value) : m_containerId(containerId) - , m_slotId(slotId) + , m_id(id) , m_value(value) { } @@ -17,13 +17,13 @@ void ContainerSetDataPacket::write(RakNet::BitStream& bs) { bs.Write((unsigned char)PACKET_CONTAINER_SET_DATA); bs.Write(m_containerId); - bs.Write(m_slotId); + bs.Write(m_id); bs.Write(m_value); } void ContainerSetDataPacket::read(RakNet::BitStream& bs) { bs.Read(m_containerId); - bs.Read(m_slotId); + bs.Read(m_id); bs.Read(m_value); } diff --git a/source/network/packets/ContainerSetDataPacket.hpp b/source/network/packets/ContainerSetDataPacket.hpp index 84479d37e..629e81c4c 100644 --- a/source/network/packets/ContainerSetDataPacket.hpp +++ b/source/network/packets/ContainerSetDataPacket.hpp @@ -8,16 +8,16 @@ class ContainerSetDataPacket : public Packet ContainerSetDataPacket() { m_containerId = 0; - m_slotId = 0; + m_id = 0; m_value = 0; } - ContainerSetDataPacket(int8_t containerId, int16_t slotId, int16_t value); + ContainerSetDataPacket(int8_t containerId, int16_t id, int16_t value); void handle(const RakNet::RakNetGUID&, NetEventCallback& callback) override; void write(RakNet::BitStream&) override; void read(RakNet::BitStream&) override; public: int8_t m_containerId; - int16_t m_slotId; + int16_t m_id; int16_t m_value; }; \ No newline at end of file From b0adc869999f13d0230d6941296b31c0986c3fbd Mon Sep 17 00:00:00 2001 From: Brent Da Mage Date: Thu, 14 May 2026 03:06:14 -0500 Subject: [PATCH 46/63] Finish rename --- source/server/ServerPlayer.cpp | 4 ++-- source/server/ServerPlayer.hpp | 2 +- source/world/inventory/ContainerListener.hpp | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/source/server/ServerPlayer.cpp b/source/server/ServerPlayer.cpp index ae3b17226..77e70d54b 100644 --- a/source/server/ServerPlayer.cpp +++ b/source/server/ServerPlayer.cpp @@ -133,10 +133,10 @@ void ServerPlayer::slotChanged(ContainerMenu* menu, Container::SlotID slotId, Sl #endif } -void ServerPlayer::setContainerData(ContainerMenu* menu, Container::SlotID slotId, int value) +void ServerPlayer::setContainerData(ContainerMenu* menu, int id, int value) { #if NETWORK_PROTOCOL_VERSION >= 5 - m_pLevel->m_pRakNetInstance->send(new ContainerSetDataPacket(menu->m_containerId, slotId, value)); + m_pLevel->m_pRakNetInstance->send(new ContainerSetDataPacket(menu->m_containerId, id, value)); #endif } diff --git a/source/server/ServerPlayer.hpp b/source/server/ServerPlayer.hpp index 95d761a16..dc385be2b 100644 --- a/source/server/ServerPlayer.hpp +++ b/source/server/ServerPlayer.hpp @@ -20,7 +20,7 @@ class ServerPlayer : public Player, public ContainerListener void refreshContainer(ContainerMenu* menu, const std::vector& items) override; void slotChanged(ContainerMenu* menu, Container::SlotID slotId, Slot* slot, ItemStack& item, bool isResultSlot) override; - void setContainerData(ContainerMenu* menu, Container::SlotID slotId, int value) override; + void setContainerData(ContainerMenu* menu, int id, int value) override; void doCloseContainer(); void setContainerMenu(ContainerMenu* menu); diff --git a/source/world/inventory/ContainerListener.hpp b/source/world/inventory/ContainerListener.hpp index 7d53d7795..a1b4b34cc 100644 --- a/source/world/inventory/ContainerListener.hpp +++ b/source/world/inventory/ContainerListener.hpp @@ -15,5 +15,5 @@ class ContainerListener virtual void refreshContainer(ContainerMenu* menu, const std::vector& items) {} virtual void refreshContainerItems(ContainerMenu* menu); virtual void slotChanged(ContainerMenu* menu, Container::SlotID slotId, Slot* slot, ItemStack& item, bool isResultSlot) {} - virtual void setContainerData(ContainerMenu* menu, Container::SlotID slotId, int value) {} + virtual void setContainerData(ContainerMenu* menu, int id, int value) {} }; \ No newline at end of file From 2ac7665453c4af6cd556badeaef218dc4d5bcd95 Mon Sep 17 00:00:00 2001 From: Brent Da Mage Date: Thu, 14 May 2026 03:20:49 -0500 Subject: [PATCH 47/63] Fixed furnace status desync --- source/server/ServerPlayer.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/source/server/ServerPlayer.cpp b/source/server/ServerPlayer.cpp index 77e70d54b..1e2aecba1 100644 --- a/source/server/ServerPlayer.cpp +++ b/source/server/ServerPlayer.cpp @@ -42,6 +42,9 @@ void ServerPlayer::tick() { Player::tick(); + if (m_pContainerMenu) + m_pContainerMenu->broadcastChanges(); + if (m_health != m_lastHealth) { m_pLevel->m_pRakNetInstance->send(m_guid, new SetHealthPacket(m_health)); From be520e1e1e77c1d76689b1c6a69534dce19f90dc Mon Sep 17 00:00:00 2001 From: Brent Da Mage Date: Thu, 14 May 2026 04:20:23 -0500 Subject: [PATCH 48/63] Fixes --- source/server/ServerPlayer.cpp | 15 +++++++++------ source/world/entity/Mob.cpp | 2 +- source/world/entity/Pig.cpp | 2 +- source/world/inventory/Container.hpp | 2 +- source/world/inventory/ContainerMenu.cpp | 4 ++-- source/world/inventory/FurnaceMenu.cpp | 13 ++++++++++--- source/world/inventory/FurnaceMenu.hpp | 2 +- 7 files changed, 25 insertions(+), 15 deletions(-) diff --git a/source/server/ServerPlayer.cpp b/source/server/ServerPlayer.cpp index 1e2aecba1..c48efbcfa 100644 --- a/source/server/ServerPlayer.cpp +++ b/source/server/ServerPlayer.cpp @@ -65,7 +65,7 @@ void ServerPlayer::startCrafting(const TilePos& pos) void ServerPlayer::openContainer(Container* container) { - LOG_I("Client is opening a container"); + //LOG_I("Client is opening a container"); _nextContainerCounter(); @@ -83,7 +83,7 @@ void ServerPlayer::openContainer(Container* container) void ServerPlayer::closeContainer() { - LOG_I("Client is closing a container"); + //LOG_I("Client is closing a container"); #if NETWORK_PROTOCOL_VERSION >= 5 m_pLevel->m_pRakNetInstance->send(new ContainerClosePacket(m_pContainerMenu->m_containerId)); @@ -94,7 +94,7 @@ void ServerPlayer::closeContainer() void ServerPlayer::openFurnace(FurnaceTileEntity* furnace) { - LOG_I("Client is opening a furnace"); + //LOG_I("Client is opening a furnace"); _nextContainerCounter(); @@ -129,10 +129,13 @@ void ServerPlayer::refreshContainer(ContainerMenu* menu, const std::vector= 5 - // @TODO: See my gripes in ContainerMenu::slotChanged - // But ultimately this is a bandaid for the fact that the client has authority over the inventory in PE - if (!isResultSlot && slot->m_group != Slot::INVENTORY && slot->m_group != Slot::HOTBAR) + if (!isResultSlot) + { + // @TODO: See my gripes in ContainerMenu::slotChanged + // But ultimately this is a bandaid for the fact that the client has authority over the inventory in PE + //if (slot->m_group != Slot::INVENTORY && slot->m_group != Slot::HOTBAR) m_pLevel->m_pRakNetInstance->send(new ContainerSetSlotPacket(menu->m_containerId, slotId, item)); + } #endif } diff --git a/source/world/entity/Mob.cpp b/source/world/entity/Mob.cpp index 8a4dca3d3..e216f2d9f 100644 --- a/source/world/entity/Mob.cpp +++ b/source/world/entity/Mob.cpp @@ -788,7 +788,7 @@ void Mob::lookAt(Entity* pEnt, float a3, float a4) -rotlerp(m_rot.y, x2 * 180.0f / float(M_PI), a3))); } -Entity *Mob::getLookingAt() const +Entity* Mob::getLookingAt() const { if (m_entLookedAtId == 0) return nullptr; diff --git a/source/world/entity/Pig.cpp b/source/world/entity/Pig.cpp index 7ba77895e..9a0caa988 100644 --- a/source/world/entity/Pig.cpp +++ b/source/world/entity/Pig.cpp @@ -16,8 +16,8 @@ Pig::Pig(Level* pLevel) : Animal(pLevel) m_texture = "mob/pig.png"; setSize(0.9f, 0.9f); // some dataitem stuff - setSaddle(true); } + int Pig::getDeathLoot() const { return (isOnFire()) ? diff --git a/source/world/inventory/Container.hpp b/source/world/inventory/Container.hpp index 3e0c98e1b..77ac4480f 100644 --- a/source/world/inventory/Container.hpp +++ b/source/world/inventory/Container.hpp @@ -57,4 +57,4 @@ class Container virtual void addSizeChangeListener(ContainerSizeChangeListener* listener) {} virtual void removeContentChangeListener(ContainerContentChangeListener* listener) {} virtual void removeSizeChangeListener(ContainerSizeChangeListener* listener) {} -}; \ No newline at end of file +}; diff --git a/source/world/inventory/ContainerMenu.cpp b/source/world/inventory/ContainerMenu.cpp index 15148fc6d..c24737b64 100644 --- a/source/world/inventory/ContainerMenu.cpp +++ b/source/world/inventory/ContainerMenu.cpp @@ -103,7 +103,7 @@ void ContainerMenu::slotsChanged(Container*) void ContainerMenu::containerContentChanged(Container* container, Container::StackID stackId) { - // slotId is an index within a specific container, but m_slots contains + // stackId is an index of an ItemStack within a specific container, but m_slots contains // slots from multiple containers. We need to find which slot in m_slots corresponds // to this container slot by matching both the container and the slot index. for (size_t i = 0; i < m_slots.size(); ++i) @@ -425,4 +425,4 @@ void ContainerMenu::setSynched(Player* player, bool isSynched) m_unsynchedPlayers.erase(player); else m_unsynchedPlayers.insert(player); -} \ No newline at end of file +} diff --git a/source/world/inventory/FurnaceMenu.cpp b/source/world/inventory/FurnaceMenu.cpp index b31d453eb..2b5fd2e27 100644 --- a/source/world/inventory/FurnaceMenu.cpp +++ b/source/world/inventory/FurnaceMenu.cpp @@ -60,12 +60,19 @@ void FurnaceMenu::broadcastChanges() void FurnaceMenu::setData(int index, int value) { - if (index == 0) + switch (index) + { + case 0: m_furnace->m_tickCount = value; - else if (index == 1) + break; + case 1: m_furnace->m_litTime = value; - else if (index == 2) + break; + case 2: m_furnace->m_litDuration = value; + break; + default: break; + } } ItemStack FurnaceMenu::quickMoveStack(Container::SlotID slotId) diff --git a/source/world/inventory/FurnaceMenu.hpp b/source/world/inventory/FurnaceMenu.hpp index d51d0dd2e..2a1dd1ba0 100644 --- a/source/world/inventory/FurnaceMenu.hpp +++ b/source/world/inventory/FurnaceMenu.hpp @@ -2,7 +2,7 @@ #include "ContainerMenu.hpp" #include "world/entity/Player.hpp" -#include +#include "world/tile/entity/FurnaceTileEntity.hpp" class FurnaceMenu : public ContainerMenu { From 230814c8ff21ed30e8fab1a3c7b8ccf8865afd1d Mon Sep 17 00:00:00 2001 From: Un1q32 Date: Thu, 14 May 2026 10:29:07 -0400 Subject: [PATCH 49/63] chmod +x --- platforms/ios/build.sh | 0 platforms/macos/build.sh | 0 2 files changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 platforms/ios/build.sh mode change 100644 => 100755 platforms/macos/build.sh diff --git a/platforms/ios/build.sh b/platforms/ios/build.sh old mode 100644 new mode 100755 diff --git a/platforms/macos/build.sh b/platforms/macos/build.sh old mode 100644 new mode 100755 From d980f1d0bf2817f9a5913be3c12fd200354f6595 Mon Sep 17 00:00:00 2001 From: Brent Da Mage Date: Fri, 15 May 2026 01:56:33 -0500 Subject: [PATCH 50/63] Cleanup --- source/server/ServerPlayer.cpp | 2 +- source/world/level/Level.cpp | 15 +++++++-------- source/world/level/Level.hpp | 2 +- source/world/level/levelgen/chunk/LevelChunk.cpp | 2 +- source/world/tile/entity/ChestTileEntity.hpp | 4 +++- source/world/tile/entity/FurnaceTileEntity.cpp | 5 +++-- source/world/tile/entity/MusicTileEntity.cpp | 6 ++++-- source/world/tile/entity/MusicTileEntity.hpp | 2 +- source/world/tile/entity/TileEntity.cpp | 6 ++++-- source/world/tile/entity/TileEntity.hpp | 1 - source/world/tile/entity/TileEntityType.cpp | 8 +++++--- source/world/tile/entity/TileEntityType.hpp | 6 +++--- 12 files changed, 33 insertions(+), 26 deletions(-) diff --git a/source/server/ServerPlayer.cpp b/source/server/ServerPlayer.cpp index c48efbcfa..9b8c96df1 100644 --- a/source/server/ServerPlayer.cpp +++ b/source/server/ServerPlayer.cpp @@ -171,4 +171,4 @@ void ServerPlayer::setContainerMenu(ContainerMenu* menu) refreshContainer(m_pContainerMenu, m_pContainerMenu->cloneItems()); m_pContainerMenu->broadcastChanges(); } -} \ No newline at end of file +} diff --git a/source/world/level/Level.cpp b/source/world/level/Level.cpp index 62a6de457..e3129f1d7 100644 --- a/source/world/level/Level.cpp +++ b/source/world/level/Level.cpp @@ -40,7 +40,7 @@ Level::Level(LevelStorage* pStor, const std::string& name, const LevelSettings& m_bCalculatingInitialSpawn = false; m_pChunkSource = nullptr; m_pLevelStorage = pStor; - m_tileEntityList = TileEntityVector(); + m_tileEntities = TileEntityVector(); m_bUpdatingTileEntities = false; m_randValue = 42184323; m_addend = 1013904223; @@ -269,7 +269,7 @@ TileEntity* Level::getTileEntity(const TilePos& pos) const const TileEntityVector* Level::getAllTileEntities() const { - return &m_tileEntityList; + return &m_tileEntities; } void Level::setTileEntity(const TilePos& pos, TileEntity* tileEntity) @@ -285,7 +285,7 @@ void Level::setTileEntity(const TilePos& pos, TileEntity* tileEntity) return; } - m_tileEntityList.push_back(tileEntity); + m_tileEntities.push_back(tileEntity); LevelChunk* pChunk = getChunk(pos); if (pChunk) pChunk->setTileEntity(pos, tileEntity); @@ -308,7 +308,7 @@ void Level::removeTileEntity(const TilePos& pos) return; } - Util::remove(m_tileEntityList, tileEntity); + Util::remove(m_tileEntities, tileEntity); if (LevelChunk* pChunk = getChunk(pos)) { @@ -1251,7 +1251,6 @@ void Level::removeAllPendingEntityRemovals() { m_entities.erase(ent->hashCode()); } - } for (EntityVector::iterator it = m_pendingEntityRemovals.begin(); it != m_pendingEntityRemovals.end(); it++) @@ -1852,9 +1851,9 @@ void Level::tickEntities() } m_bUpdatingTileEntities = true; - for (size_t i = 0; i < m_tileEntityList.size(); i++) + for (size_t i = 0; i < m_tileEntities.size(); i++) { - TileEntity* tileEnt = m_tileEntityList[i]; + TileEntity* tileEnt = m_tileEntities[i]; if (!tileEnt->isRemoved()) { @@ -1866,7 +1865,7 @@ void Level::tickEntities() if (ch) ch->removeTileEntity(tileEnt->m_pos); - m_tileEntityList.erase(m_tileEntityList.begin() + i); + m_tileEntities.erase(m_tileEntities.begin() + i); i--; delete tileEnt; diff --git a/source/world/level/Level.hpp b/source/world/level/Level.hpp index aee5bd5bf..f4e871e9a 100644 --- a/source/world/level/Level.hpp +++ b/source/world/level/Level.hpp @@ -246,7 +246,7 @@ class Level : public LevelSource MobSpawner* m_pMobSpawner; std::map m_entityCountsByCategory; - TileEntityVector m_tileEntityList; + TileEntityVector m_tileEntities; TileEntityVector m_pendingTileEntities; }; diff --git a/source/world/level/levelgen/chunk/LevelChunk.cpp b/source/world/level/levelgen/chunk/LevelChunk.cpp index 2d3cfb5a6..9f7368dca 100644 --- a/source/world/level/levelgen/chunk/LevelChunk.cpp +++ b/source/world/level/levelgen/chunk/LevelChunk.cpp @@ -717,7 +717,7 @@ void LevelChunk::addTileEntity(TileEntity* tileEntity) { setTileEntity(tileEntity->m_pos, tileEntity); if (m_bLoaded) - m_pLevel->m_tileEntityList.push_back(tileEntity); + m_pLevel->m_tileEntities.push_back(tileEntity); } void LevelChunk::setTileEntity(const ChunkTilePos& pos, TileEntity* tileEntity) diff --git a/source/world/tile/entity/ChestTileEntity.hpp b/source/world/tile/entity/ChestTileEntity.hpp index 73bfd2779..61c21ef49 100644 --- a/source/world/tile/entity/ChestTileEntity.hpp +++ b/source/world/tile/entity/ChestTileEntity.hpp @@ -4,10 +4,12 @@ #include "world/level/Level.hpp" #include "world/inventory/SimpleContainer.hpp" -class ChestTileEntity : public TileEntity, public SimpleContainer { +class ChestTileEntity : public TileEntity, public SimpleContainer +{ public: ChestTileEntity(); +public: void load(const CompoundTag& tag) override; void save(CompoundTag& tag) const override; diff --git a/source/world/tile/entity/FurnaceTileEntity.cpp b/source/world/tile/entity/FurnaceTileEntity.cpp index 8962225c3..5b0358c7b 100644 --- a/source/world/tile/entity/FurnaceTileEntity.cpp +++ b/source/world/tile/entity/FurnaceTileEntity.cpp @@ -5,7 +5,8 @@ #define C_BURN_TIME (200) FurnaceTileEntity::FurnaceTileEntity() - : SimpleContainer(3, "gui.furnace"), m_litTime(0), m_litDuration(0), m_tickCount(0) + : SimpleContainer(3, "gui.furnace") + , m_litTime(0), m_litDuration(0), m_tickCount(0) { m_pType = TileEntityType::furnace; } @@ -161,4 +162,4 @@ int FurnaceTileEntity::getLitProgress(int height) bool FurnaceTileEntity::isLit() { return m_litTime > 0; -} \ No newline at end of file +} diff --git a/source/world/tile/entity/MusicTileEntity.cpp b/source/world/tile/entity/MusicTileEntity.cpp index d38630bd7..ef8414fbf 100644 --- a/source/world/tile/entity/MusicTileEntity.cpp +++ b/source/world/tile/entity/MusicTileEntity.cpp @@ -1,7 +1,9 @@ #include "MusicTileEntity.hpp" #include "world/level/Level.hpp" -MusicTileEntity::MusicTileEntity() : TileEntity(), m_note(0), m_bOn(false) +MusicTileEntity::MusicTileEntity() : TileEntity() + , m_note(0) + , m_bOn(false) { m_pType = TileEntityType::noteblock; } @@ -43,4 +45,4 @@ void MusicTileEntity::play(Level* pLevel, const TilePos& pos) instrument = 4; pLevel->tileEvent(TileEvent(pos, instrument, m_note)); -} \ No newline at end of file +} diff --git a/source/world/tile/entity/MusicTileEntity.hpp b/source/world/tile/entity/MusicTileEntity.hpp index 9a8a1a60a..2bc17ee8a 100644 --- a/source/world/tile/entity/MusicTileEntity.hpp +++ b/source/world/tile/entity/MusicTileEntity.hpp @@ -18,4 +18,4 @@ class MusicTileEntity : public TileEntity public: uint8_t m_note; bool m_bOn; -}; \ No newline at end of file +}; diff --git a/source/world/tile/entity/TileEntity.cpp b/source/world/tile/entity/TileEntity.cpp index 8cf5078c1..0acab7361 100644 --- a/source/world/tile/entity/TileEntity.cpp +++ b/source/world/tile/entity/TileEntity.cpp @@ -2,7 +2,9 @@ #include "common/Logger.hpp" #include "world/level/Level.hpp" -TileEntity::TileEntity() : m_bRemove(false), m_pLevel(nullptr) +TileEntity::TileEntity() + : m_bRemove(false) + , m_pLevel(nullptr) { } @@ -103,4 +105,4 @@ Tile* TileEntity::getTile() const std::string TileEntity::getId() const { return (m_pType) ? m_pType->getName() : "Unknown"; -} \ No newline at end of file +} diff --git a/source/world/tile/entity/TileEntity.hpp b/source/world/tile/entity/TileEntity.hpp index 2a3666fc7..7396bebb8 100644 --- a/source/world/tile/entity/TileEntity.hpp +++ b/source/world/tile/entity/TileEntity.hpp @@ -47,4 +47,3 @@ class TileEntity Level* m_pLevel; TilePos m_pos; }; - diff --git a/source/world/tile/entity/TileEntityType.cpp b/source/world/tile/entity/TileEntityType.cpp index 18589f526..84a902783 100644 --- a/source/world/tile/entity/TileEntityType.cpp +++ b/source/world/tile/entity/TileEntityType.cpp @@ -36,16 +36,18 @@ const TileEntityType* TileEntityFactory::getType(const std::string& name) return _types[name]; } -TileEntityType::TileEntityType(const std::string& name, CreateFunction func) : _name(name), _function(func) +TileEntityType::TileEntityType(const std::string& name, CreateFunction func) + : m_name(name) + , m_function(func) { } const std::string& TileEntityType::getName() const { - return _name; + return m_name; } TileEntity* TileEntityType::newTileEntity() const { - return _function(); + return m_function(); } \ No newline at end of file diff --git a/source/world/tile/entity/TileEntityType.hpp b/source/world/tile/entity/TileEntityType.hpp index d1ad2b6ff..aec0cb513 100644 --- a/source/world/tile/entity/TileEntityType.hpp +++ b/source/world/tile/entity/TileEntityType.hpp @@ -42,8 +42,8 @@ class TileEntityType TileEntity* newTileEntity() const; private: - std::string _name; - CreateFunction _function; + std::string m_name; + CreateFunction m_function; template static TileEntity* CreateType() { return new T(); } @@ -53,6 +53,6 @@ template TileEntityType* TileEntityFactory::registerTileEntity(const std::string& name) { TileEntityType* type = new TileEntityType(name, &TileEntityType::CreateType); - _types[type->_name] = type; + _types[type->m_name] = type; return type; } \ No newline at end of file From 1cc8289ec995bc7f4d2d84f505bb9d04282f6078 Mon Sep 17 00:00:00 2001 From: Brent Da Mage Date: Fri, 15 May 2026 02:07:35 -0500 Subject: [PATCH 51/63] Fixes --- source/world/tile/Tile.cpp | 2 +- source/world/tile/TopSnowTile.cpp | 10 ++++------ source/world/tile/TopSnowTile.hpp | 2 +- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/source/world/tile/Tile.cpp b/source/world/tile/Tile.cpp index f8d1848d0..6cba7591c 100644 --- a/source/world/tile/Tile.cpp +++ b/source/world/tile/Tile.cpp @@ -1311,4 +1311,4 @@ Tile *Tile::musicBlock, *Tile::furnace, *Tile::furnaceLit, - *Tile::chest; \ No newline at end of file + *Tile::chest; diff --git a/source/world/tile/TopSnowTile.cpp b/source/world/tile/TopSnowTile.cpp index 9bec5bc70..380ab3653 100644 --- a/source/world/tile/TopSnowTile.cpp +++ b/source/world/tile/TopSnowTile.cpp @@ -87,13 +87,11 @@ void TopSnowTile::tick(Level* level, const TilePos& pos, Random* random) void TopSnowTile::playerDestroy(Level* level, Player* player, const TilePos& pos, TileData data) { - float dispersion = 0.7f; + constexpr float dispersion = 0.7f; - Vec3 offset ( - (level->m_random.nextFloat() * dispersion) + (1.0f - dispersion) * 0.5f, - (level->m_random.nextFloat() * dispersion) + (1.0f - dispersion) * 0.5f, - (level->m_random.nextFloat() * dispersion) + (1.0f - dispersion) * 0.5f - ); + Vec3 offset(level->m_random.nextFloat(), level->m_random.nextFloat(), level->m_random.nextFloat()); + offset *= dispersion; + offset += (1.0f - dispersion) * 0.5f; ItemEntity* pItemEntity = new ItemEntity(level, Vec3(pos) + offset, ItemStack(getResource(data, &level->m_random), 1, 0)); pItemEntity->m_throwTime = 10; diff --git a/source/world/tile/TopSnowTile.hpp b/source/world/tile/TopSnowTile.hpp index 398ad0f02..8bd2a75bf 100644 --- a/source/world/tile/TopSnowTile.hpp +++ b/source/world/tile/TopSnowTile.hpp @@ -25,7 +25,7 @@ class TopSnowTile : public Tile void neighborChanged(Level*, const TilePos& pos, TileID tile) override; bool shouldRenderFace(const LevelSource*, const TilePos& pos, Facing::Name face) const override; void tick(Level*, const TilePos& pos, Random*) override; - virtual void playerDestroy(Level*, Player*, const TilePos& pos, TileData data); + void playerDestroy(Level*, Player*, const TilePos& pos, TileData data) override; bool checkCanSurvive(Level*, const TilePos& pos); }; From 0ea7435409770c7d5c08f39adf49b03f62cfa31d Mon Sep 17 00:00:00 2001 From: Brent Da Mage Date: Fri, 15 May 2026 02:44:03 -0500 Subject: [PATCH 52/63] Fixes --- platforms/android/AppPlatform_android.cpp | 28 +++++++++---------- platforms/android/AppPlatform_android.hpp | 2 ++ platforms/windows/AppPlatform_win32.cpp | 17 +---------- platforms/windows/AppPlatform_win32.hpp | 3 +- platforms/xdk360/AppPlatform_xdk360.cpp | 15 ---------- platforms/xdk360/AppPlatform_xdk360.hpp | 3 -- platforms/xdk360/main.cpp | 2 -- source/client/app/AppPlatform.cpp | 2 +- source/client/app/AppPlatform.hpp | 2 +- source/client/gui/components/OptionList.cpp | 5 +++- .../gui/screens/OptionsScreen_Console.cpp | 8 ++++-- .../gui/screens/inventory/FurnaceScreen.cpp | 14 ++++++---- .../gui/screens/inventory/FurnaceScreen.hpp | 4 +-- source/client/options/Options.cpp | 4 +-- source/client/options/Options.hpp | 2 +- 15 files changed, 43 insertions(+), 68 deletions(-) diff --git a/platforms/android/AppPlatform_android.cpp b/platforms/android/AppPlatform_android.cpp index e8bcd9368..d555cb618 100644 --- a/platforms/android/AppPlatform_android.cpp +++ b/platforms/android/AppPlatform_android.cpp @@ -117,6 +117,20 @@ std::string AppPlatform_android::getDateString(int time) return std::string(buffer); } +void AppPlatform_android::setVSyncEnabled(bool enabled) +{ + EGLDisplay display = eglGetCurrentDisplay(); + if (display == EGL_NO_DISPLAY) + return; + + eglSwapInterval(display, enabled ? 1 : 0); +} + +bool AppPlatform_android::isVSyncSwitchable() const +{ + return eglGetCurrentDisplay() != EGL_NO_DISPLAY; +} + SoundSystem* AppPlatform_android::getSoundSystem() const { return m_pSoundSystem; @@ -141,20 +155,6 @@ void AppPlatform_android::setScreenSize(int width, int height) m_ScreenHeight = height; } -void AppPlatform_android::setVSyncEnabled(bool enabled) -{ - EGLDisplay display = eglGetCurrentDisplay(); - if (display == EGL_NO_DISPLAY) - return; - - eglSwapInterval(display, enabled ? 1 : 0); -} - -bool AppPlatform_android::isVsyncSwitchable() const -{ - return eglGetCurrentDisplay() != EGL_NO_DISPLAY; -} - void AppPlatform_android::initAndroidApp(android_app* ptr) { m_app = ptr; diff --git a/platforms/android/AppPlatform_android.hpp b/platforms/android/AppPlatform_android.hpp index 832490e67..a4c242f56 100644 --- a/platforms/android/AppPlatform_android.hpp +++ b/platforms/android/AppPlatform_android.hpp @@ -29,6 +29,8 @@ class AppPlatform_android : public AppPlatform int getScreenHeight() const override; void showDialog(eDialogType) override; std::string getDateString(int time) override; + void setVSyncEnabled(bool enabled) override; + bool isVSyncSwitchable() const override; // Also add these to allow proper turning within the game. void recenterMouse() override; diff --git a/platforms/windows/AppPlatform_win32.cpp b/platforms/windows/AppPlatform_win32.cpp index 4d1925758..8f5a1bb17 100644 --- a/platforms/windows/AppPlatform_win32.cpp +++ b/platforms/windows/AppPlatform_win32.cpp @@ -135,21 +135,6 @@ void AppPlatform_win32::showDialog(eDialogType type) m_DialogType = type; } -std::string AppPlatform_win32::getDateString(int time) -{ - time_t tt = time; - struct tm t; - // using the _s variant. For a different platform there's gmtime_r. This is not directly portable however. - gmtime_s(&t, &tt); - - //format it with strftime - char buf[2048]; - strftime(buf, sizeof buf, "%b %d %Y %H:%M:%S", &t); - //strftime(buf, sizeof buf, "%a %b %d %H:%M:%S %Z %Y", &t); - - return std::string(buf); -} - bool AppPlatform_win32::doesTextureExist(const std::string& path) const { return isRegularFile(path.c_str()); @@ -467,7 +452,7 @@ void AppPlatform_win32::setVSyncEnabled(bool enabled) #endif } -bool AppPlatform_win32::isVsyncSwitchable() const +bool AppPlatform_win32::isVSyncSwitchable() const { #if MCE_GFX_API_OGL return true; diff --git a/platforms/windows/AppPlatform_win32.hpp b/platforms/windows/AppPlatform_win32.hpp index bac0f7eda..b702b4768 100644 --- a/platforms/windows/AppPlatform_win32.hpp +++ b/platforms/windows/AppPlatform_win32.hpp @@ -37,7 +37,6 @@ class AppPlatform_win32 : public AppPlatform int getScreenWidth() const override { return m_ScreenWidth; } int getScreenHeight() const override { return m_ScreenHeight; } void showDialog(eDialogType) override; - std::string getDateString(int time) override; bool doesTextureExist(const std::string& path) const override; // From v0.1.1. Also add these to determine touch screen use within the game. @@ -83,7 +82,7 @@ class AppPlatform_win32 : public AppPlatform void createWindowSizeDependentResources(const Vec2& logicalSize, const Vec2& compositionScale); void swapBuffers(); void setVSyncEnabled(bool enabled) override; - bool isVsyncSwitchable() const override; + bool isVSyncSwitchable() const override; static MouseButtonType GetMouseButtonType(UINT iMsg); static bool GetMouseButtonState(UINT iMsg, WPARAM wParam); diff --git a/platforms/xdk360/AppPlatform_xdk360.cpp b/platforms/xdk360/AppPlatform_xdk360.cpp index daf1feec7..7ad6320ed 100644 --- a/platforms/xdk360/AppPlatform_xdk360.cpp +++ b/platforms/xdk360/AppPlatform_xdk360.cpp @@ -123,21 +123,6 @@ int AppPlatform_xdk360::checkLicense() return 1; } -std::string AppPlatform_xdk360::getDateString(int time) -{ - time_t tt = time; - struct tm t; - // using the _s variant. For a different platform there's gmtime_r. This is not directly portable however. - gmtime_s(&t, &tt); - - //format it with strftime - char buf[2048]; - strftime(buf, sizeof buf, "%b %d %Y %H:%M:%S", &t); - //strftime(buf, sizeof buf, "%a %b %d %H:%M:%S %Z %Y", &t); - - return std::string(buf); -} - bool AppPlatform_xdk360::isTouchscreen() const { return false; diff --git a/platforms/xdk360/AppPlatform_xdk360.hpp b/platforms/xdk360/AppPlatform_xdk360.hpp index 62535274a..a72bcf740 100644 --- a/platforms/xdk360/AppPlatform_xdk360.hpp +++ b/platforms/xdk360/AppPlatform_xdk360.hpp @@ -24,7 +24,6 @@ class AppPlatform_xdk360 : public AppPlatform int checkLicense() override; int getScreenWidth() const override { return m_ScreenWidth; } int getScreenHeight() const override { return m_ScreenHeight; } - std::string getDateString(int time) override; // From v0.1.1. Also add these to determine touch screen use within the game. bool isTouchscreen() const override; @@ -54,8 +53,6 @@ class AppPlatform_xdk360 : public AppPlatform bool initGraphics(unsigned int width, unsigned int height); void createWindowSizeDependentResources(unsigned int width, unsigned int height); void swapBuffers(); - void setVSyncEnabled(bool enabled) override; - bool isVsyncSwitchable() const override; private: int m_ScreenWidth; diff --git a/platforms/xdk360/main.cpp b/platforms/xdk360/main.cpp index 4353d31c7..6e932bce3 100644 --- a/platforms/xdk360/main.cpp +++ b/platforms/xdk360/main.cpp @@ -53,8 +53,6 @@ void __cdecl main() if (!g_AppPlatform.initGraphics(Minecraft::width, Minecraft::height)) goto _cleanup; - g_AppPlatform.setVSyncEnabled(true); - g_pApp = new NinecraftApp; g_pApp->m_pPlatform = &g_AppPlatform; g_AppPlatform.m_externalStorageDir = "savedrive:"; diff --git a/source/client/app/AppPlatform.cpp b/source/client/app/AppPlatform.cpp index 0454015c4..6c3f10e31 100644 --- a/source/client/app/AppPlatform.cpp +++ b/source/client/app/AppPlatform.cpp @@ -326,7 +326,7 @@ void AppPlatform::setVSyncEnabled(bool enabled) { } -bool AppPlatform::isVsyncSwitchable() const +bool AppPlatform::isVSyncSwitchable() const { return false; } diff --git a/source/client/app/AppPlatform.hpp b/source/client/app/AppPlatform.hpp index 6837dd59f..8a68246f7 100644 --- a/source/client/app/AppPlatform.hpp +++ b/source/client/app/AppPlatform.hpp @@ -111,7 +111,7 @@ class AppPlatform virtual std::string getClipboardText(); // Graphics settings virtual void setVSyncEnabled(bool enabled); - virtual bool isVsyncSwitchable() const; + virtual bool isVSyncSwitchable() const; void _fireLowMemory(); void _fireAppSuspended(); diff --git a/source/client/gui/components/OptionList.cpp b/source/client/gui/components/OptionList.cpp index 26e7afa1f..d85ca3378 100644 --- a/source/client/gui/components/OptionList.cpp +++ b/source/client/gui/components/OptionList.cpp @@ -181,7 +181,7 @@ void OptionList::initVideoMenu() { Options* pOptions = m_pMinecraft->getOptions(); int currentIndex = -1; - int idxPano = -1; + int idxPano = -1, idxVSync = -1; OPTIONS_LIST_VIDEO_GRAPHICS; OPTIONS_LIST_VIDEO_EXPERIMENTAL; @@ -190,6 +190,9 @@ void OptionList::initVideoMenu() if (!Screen::isMenuPanoramaAvailable()) m_items[idxPano]->setEnabled(false); #endif + + if (!m_pMinecraft->platform()->isVSyncSwitchable()) + m_items[idxVSync]->setEnabled(false); } OptionHeader::OptionHeader(const std::string& text) diff --git a/source/client/gui/screens/OptionsScreen_Console.cpp b/source/client/gui/screens/OptionsScreen_Console.cpp index 934283210..a811200aa 100644 --- a/source/client/gui/screens/OptionsScreen_Console.cpp +++ b/source/client/gui/screens/OptionsScreen_Console.cpp @@ -83,9 +83,10 @@ ControlsPanelScreen::ControlsPanelScreen(Screen* parent, Minecraft* mc) : PanelS OPTIONS_LIST_CONTROLS_FEEDBACK; OPTIONS_LIST_CONTROLS_EXPERIMENTAL; + m_layout.m_elements[idxController]->setEnabled(false); + if (!mc->isTouchscreen()) m_layout.m_elements[idxSplit]->setEnabled(false); - m_layout.m_elements[idxController]->setEnabled(false); } void ControlsPanelScreen::removed() @@ -96,7 +97,7 @@ void ControlsPanelScreen::removed() SettingsPanelScreen::SettingsPanelScreen(Screen* parent, Options& options) : PanelScreen_Console(parent) { int currentIndex = -1; - int idxPano = -1; + int idxPano = -1, idxVSync = -1; OPTIONS_LIST_GAMEPLAY_GAME; OPTIONS_LIST_GAMEPLAY_AUDIO; @@ -108,6 +109,9 @@ SettingsPanelScreen::SettingsPanelScreen(Screen* parent, Options& options) : Pan m_layout.m_elements[idxPano]->setEnabled(false); #endif + if (!m_pMinecraft->platform()->isVSyncSwitchable()) + m_layout.m_elements[idxVSync]->setEnabled(false); + (void)currentIndex; // compiler will warn about an unused variable sometimes if this isn't here } diff --git a/source/client/gui/screens/inventory/FurnaceScreen.cpp b/source/client/gui/screens/inventory/FurnaceScreen.cpp index d3c3c0fb3..9b500370b 100644 --- a/source/client/gui/screens/inventory/FurnaceScreen.cpp +++ b/source/client/gui/screens/inventory/FurnaceScreen.cpp @@ -3,7 +3,9 @@ #include "renderer/ShaderConstants.hpp" FurnaceScreen::FurnaceScreen(Inventory* inventory, FurnaceTileEntity* container) - : ContainerScreen(new FurnaceMenu(inventory, container)), m_inventory(inventory), m_furnace(container) + : ContainerScreen(new FurnaceMenu(inventory, container)) + , m_pInventory(inventory) + , m_pFurnace(container) { m_uiTheme = UI_JAVA; } @@ -15,8 +17,8 @@ void FurnaceScreen::tick() void FurnaceScreen::_renderLabels() { - m_pFont->draw(m_furnace->getName(), 66, 6, 0x404040); - m_pFont->draw(m_inventory->getName(), 8, m_imageHeight - 96 + 2, 0x404040); + m_pFont->draw(m_pFurnace->getName(), 66, 6, 0x404040); + m_pFont->draw(m_pInventory->getName(), 8, m_imageHeight - 96 + 2, 0x404040); } void FurnaceScreen::_renderBg(float a) @@ -28,13 +30,13 @@ void FurnaceScreen::_renderBg(float a) blit(m_leftPos, m_topPos, 0, 0, m_imageWidth, m_imageHeight, 0, 0); int p; - if (m_furnace->isLit()) + if (m_pFurnace->isLit()) { - p = m_furnace->getLitProgress(12); + p = m_pFurnace->getLitProgress(12); blit(m_leftPos + 56, m_topPos + 36 + 12 - p, 176, 12 - p, 14, p + 2, 0, 0); } - p = m_furnace->getBurnProgress(24); + p = m_pFurnace->getBurnProgress(24); blit(m_leftPos + 79, m_topPos + 34, 176, 14, p + 1, 16, 0, 0); } diff --git a/source/client/gui/screens/inventory/FurnaceScreen.hpp b/source/client/gui/screens/inventory/FurnaceScreen.hpp index 5b84e36d3..4d8d7c389 100644 --- a/source/client/gui/screens/inventory/FurnaceScreen.hpp +++ b/source/client/gui/screens/inventory/FurnaceScreen.hpp @@ -15,6 +15,6 @@ class FurnaceScreen : public ContainerScreen SlotDisplay _createSlotDisplay(const Slot&) override; private: - Inventory* m_inventory; - FurnaceTileEntity* m_furnace; + Inventory* m_pInventory; + FurnaceTileEntity* m_pFurnace; }; diff --git a/source/client/options/Options.cpp b/source/client/options/Options.cpp index c19f74190..6c454aae0 100644 --- a/source/client/options/Options.cpp +++ b/source/client/options/Options.cpp @@ -675,8 +675,8 @@ void Options::initResourceDependentOptions() if (!Screen::isMenuPanoramaAvailable()) m_menuPanorama.set(false); - if (!m_pMinecraft->platform()->isVsyncSwitchable()) - m_vSync.set(false); + if (!m_pMinecraft->platform()->isVSyncSwitchable()) + m_vSync.set(true); } const std::string& OptionEntry::getDisplayName() const diff --git a/source/client/options/Options.hpp b/source/client/options/Options.hpp index 4ddd869a5..9f99e457f 100644 --- a/source/client/options/Options.hpp +++ b/source/client/options/Options.hpp @@ -498,7 +498,7 @@ class Options OPTION(m_viewBobbing); \ OPTION(m_anaglyphs); \ OPTION(m_blockOutlines); \ - OPTION(m_vSync); \ + OPTION(m_vSync); idxVSync = currentIndex; \ OPTION(m_fancyGrass); \ OPTION(m_biomeColors); \ OPTION(m_dynamicHand); \ From 73696d9b68f22bcb2f7fba6447126ac456e1f1dc Mon Sep 17 00:00:00 2001 From: Brent Da Mage Date: Fri, 15 May 2026 03:04:36 -0500 Subject: [PATCH 53/63] Fixes --- source/client/renderer/Chunk.cpp | 2 ++ source/world/entity/Entity.cpp | 20 ++++++++--------- source/world/entity/Entity.hpp | 4 ++-- source/world/inventory/CompoundContainer.hpp | 23 ++++++++------------ 4 files changed, 23 insertions(+), 26 deletions(-) diff --git a/source/client/renderer/Chunk.cpp b/source/client/renderer/Chunk.cpp index aed0ab5c7..73d7dfaaa 100644 --- a/source/client/renderer/Chunk.cpp +++ b/source/client/renderer/Chunk.cpp @@ -214,6 +214,8 @@ void Chunk::rebuild() break; } + // get TileEntity diff and update m_globalTileEntities (renderable TileEntities) accordingly + std::set newSet(m_tileEntities.begin(), m_tileEntities.end()); TileEntityVector toAdd, toRemove; diff --git a/source/world/entity/Entity.cpp b/source/world/entity/Entity.cpp index 9502ddaca..dfdd6fa73 100644 --- a/source/world/entity/Entity.cpp +++ b/source/world/entity/Entity.cpp @@ -25,8 +25,8 @@ void Entity::_init() m_bInAChunk = false; m_viewScale = 1.0f; m_dimensionId = DIMENSION_OVERWORLD; - _riderId = 0; - _ridingId = 0; + m_riderId = 0; + m_ridingId = 0; m_bRiding = false; m_bBlocksBuilding = false; m_pLevel = nullptr; @@ -487,7 +487,7 @@ void Entity::baseTick() if (const Entity* riding = getRiding()) { // if you were riding an entity and they no longer exist, stop - if ((!riding && _ridingId > 0) || riding->m_bRemoved) + if ((!riding && m_ridingId > 0) || riding->m_bRemoved) { setRiding(nullptr); } @@ -1002,7 +1002,7 @@ void Entity::rideTick() float rotX = m_rideRot.x * 0.5f; float rotY = m_rideRot.y * 0.5f; - float lookLimiter = 10.0f; + constexpr float lookLimiter = 10.0f; rotX = Mth::clamp(rotX, -lookLimiter, lookLimiter); rotY = Mth::clamp(rotY, -lookLimiter, lookLimiter); @@ -1084,10 +1084,10 @@ void Entity::ride(Entity* newRiding) Entity* Entity::getRiding() const { - if (_ridingId <= 0) + if (m_ridingId <= 0) return nullptr; - if (Entity* riding = m_pLevel->getEntity(_ridingId)) + if (Entity* riding = m_pLevel->getEntity(m_ridingId)) return riding; return nullptr; @@ -1095,10 +1095,10 @@ Entity* Entity::getRiding() const Entity* Entity::getRider() const { - if (_riderId <= 0) + if (m_riderId <= 0) return nullptr; - if (Entity* rider = m_pLevel->getEntity(_riderId)) + if (Entity* rider = m_pLevel->getEntity(m_riderId)) return rider; return nullptr; @@ -1106,12 +1106,12 @@ Entity* Entity::getRider() const void Entity::setRider(Entity* rider) { - _riderId = (rider) ? rider->m_EntityID : 0; + m_riderId = (rider) ? rider->m_EntityID : 0; } void Entity::setRiding(Entity* riding) { - _ridingId = (riding) ? riding->m_EntityID : 0; + m_ridingId = (riding) ? riding->m_EntityID : 0; setSharedFlag(FLAG_RIDING, riding); } diff --git a/source/world/entity/Entity.hpp b/source/world/entity/Entity.hpp index d68ec04e3..df739bd14 100644 --- a/source/world/entity/Entity.hpp +++ b/source/world/entity/Entity.hpp @@ -243,8 +243,8 @@ class Entity } private: - Entity::ID _ridingId; - Entity::ID _riderId; + Entity::ID m_ridingId; + Entity::ID m_riderId; protected: SynchedEntityData m_entityData; diff --git a/source/world/inventory/CompoundContainer.hpp b/source/world/inventory/CompoundContainer.hpp index 195f718ca..556fde4d2 100644 --- a/source/world/inventory/CompoundContainer.hpp +++ b/source/world/inventory/CompoundContainer.hpp @@ -10,33 +10,28 @@ class CompoundContainer : public Container private: class ChildListener; - std::string m_name; - Container* m_pLeftContainer; - Container* m_pRightContainer; - ContentChangeListeners m_contentChangeListeners; - ChildListener* m_pLeftListener; - ChildListener* m_pRightListener; - public: CompoundContainer(const std::string& name, Container* c1, Container* c2); ~CompoundContainer() override; +public: uint16_t getContainerSize() const override; - std::string getName() const override; - ItemStack& getItem(StackID index) override; - ItemStack removeItem(StackID index, int count) override; - void setItem(StackID index, const ItemStack& item) override; - int getMaxStackSize() override; - void setContainerChanged(StackID stackId) override; - bool stillValid(Player* player) const override; void addContentChangeListener(ContainerContentChangeListener* listener) override; void removeContentChangeListener(ContainerContentChangeListener* listener) override; + +private: + std::string m_name; + Container* m_pLeftContainer; + Container* m_pRightContainer; + ContentChangeListeners m_contentChangeListeners; + ChildListener* m_pLeftListener; + ChildListener* m_pRightListener; }; From a316b0b363e6e839d28fb28cd4298b45613dda21 Mon Sep 17 00:00:00 2001 From: Brent Da Mage Date: Fri, 15 May 2026 05:32:19 -0500 Subject: [PATCH 54/63] Container replication bugfixes & cleanup --- GameMods.hpp | 1 + source/client/app/Minecraft.cpp | 12 +----------- .../gui/screens/inventory/FurnaceScreen.hpp | 2 ++ .../client/multiplayer/MultiplayerLocalPlayer.cpp | 5 +++-- source/server/ServerPlayer.cpp | 15 +++++++++++---- source/server/ServerSideNetworkHandler.cpp | 12 +++++++++++- source/world/entity/Player.cpp | 13 ++++--------- source/world/entity/Player.hpp | 2 +- source/world/inventory/ContainerMenu.cpp | 2 ++ source/world/item/Inventory.cpp | 7 ++++--- 10 files changed, 40 insertions(+), 31 deletions(-) diff --git a/GameMods.hpp b/GameMods.hpp index f3b2234c9..c025366ca 100644 --- a/GameMods.hpp +++ b/GameMods.hpp @@ -21,6 +21,7 @@ // Features (major changes) //#define FEATURE_GFX_SHADERS // Loads and uses Shaders from the assets folder for rendering. #define FEATURE_NETWORKING // Enables multi-player through RakNet +//#define FEATURE_SERVER_INVENTORIES // Enables server-sided/server-authoritative player inventories. // TODO: Add to the LevelSettings struct //#define FEATURE_CAVES // Generates caves around the world. diff --git a/source/client/app/Minecraft.cpp b/source/client/app/Minecraft.cpp index 5d88cb7cb..66336ede2 100644 --- a/source/client/app/Minecraft.cpp +++ b/source/client/app/Minecraft.cpp @@ -621,17 +621,7 @@ void Minecraft::tickInput() } else if (getOptions()->isKey(KM_DROP, keyCode)) { - ItemStack& item = m_pLocalPlayer->m_pInventory->getSelected(); - if (!item.isEmpty()) - { - ItemStack itemDrop(item); - itemDrop.m_count = 1; - - if (m_pLocalPlayer->isSurvival()) - item.shrink(1); - - m_pLocalPlayer->drop(itemDrop); - } + m_pLocalPlayer->drop(); } else if (getOptions()->isKey(KM_TOGGLEGUI, keyCode)) { diff --git a/source/client/gui/screens/inventory/FurnaceScreen.hpp b/source/client/gui/screens/inventory/FurnaceScreen.hpp index 4d8d7c389..2ac9c3b25 100644 --- a/source/client/gui/screens/inventory/FurnaceScreen.hpp +++ b/source/client/gui/screens/inventory/FurnaceScreen.hpp @@ -7,6 +7,8 @@ class FurnaceScreen : public ContainerScreen { public: FurnaceScreen(Inventory* inventory, FurnaceTileEntity* container); + +public: void tick() override; protected: diff --git a/source/client/multiplayer/MultiplayerLocalPlayer.cpp b/source/client/multiplayer/MultiplayerLocalPlayer.cpp index a5ad680a5..ee1d313b3 100644 --- a/source/client/multiplayer/MultiplayerLocalPlayer.cpp +++ b/source/client/multiplayer/MultiplayerLocalPlayer.cpp @@ -78,10 +78,11 @@ void MultiplayerLocalPlayer::heal(int health) { } +// @PARITY: From Java +// Uncomment when we have fully server-authoritative inventories /*void MultiplayerLocalPlayer::drop() { - // @PARITY: From Java - m_pLevel->m_pRakNetInstance->send(new PlayerActionPacket(PlayerActionPacket::DROP_ITEM)) + m_pLevel->m_pRakNetInstance->send(new PlayerActionPacket(m_EntityID, PlayerActionPacket::DROP_ITEM)); }*/ void MultiplayerLocalPlayer::hurtTo(int newHealth) diff --git a/source/server/ServerPlayer.cpp b/source/server/ServerPlayer.cpp index 9b8c96df1..c31d4442a 100644 --- a/source/server/ServerPlayer.cpp +++ b/source/server/ServerPlayer.cpp @@ -131,9 +131,13 @@ void ServerPlayer::slotChanged(ContainerMenu* menu, Container::SlotID slotId, Sl #if NETWORK_PROTOCOL_VERSION >= 5 if (!isResultSlot) { +#ifndef FEATURE_SERVER_INVENTORIES // @TODO: See my gripes in ContainerMenu::slotChanged // But ultimately this is a bandaid for the fact that the client has authority over the inventory in PE - //if (slot->m_group != Slot::INVENTORY && slot->m_group != Slot::HOTBAR) + if (slot->m_group == Slot::INVENTORY || slot->m_group == Slot::HOTBAR) + return; +#endif + m_pLevel->m_pRakNetInstance->send(new ContainerSetSlotPacket(menu->m_containerId, slotId, item)); } #endif @@ -153,7 +157,7 @@ void ServerPlayer::doCloseContainer() else LOG_W("Container is missing @ doCloseContainer!"); - setContainerMenu(nullptr); // m_pInventoryMenu on Java, nullptr on Pocket + setContainerMenu(m_pInventoryMenu); // m_pInventoryMenu on Java, nullptr on Pocket } void ServerPlayer::setContainerMenu(ContainerMenu* menu) @@ -161,10 +165,13 @@ void ServerPlayer::setContainerMenu(ContainerMenu* menu) if (m_pContainerMenu == menu) return; - SAFE_DELETE(m_pContainerMenu); + if (m_pContainerMenu != m_pInventoryMenu) + SAFE_DELETE(m_pContainerMenu); + m_pContainerMenu = menu; - if (menu) + // we shouldn't be changing the containerId of the InventoryMenu + if (menu && menu != m_pInventoryMenu) { m_pContainerMenu->m_containerId = m_containerId; m_pContainerMenu->addSlotListener(this); diff --git a/source/server/ServerSideNetworkHandler.cpp b/source/server/ServerSideNetworkHandler.cpp index d7df9b99c..f6dce0b2a 100644 --- a/source/server/ServerSideNetworkHandler.cpp +++ b/source/server/ServerSideNetworkHandler.cpp @@ -647,10 +647,20 @@ void ServerSideNetworkHandler::handle(const RakNet::RakNetGUID& guid, ContainerS return; ContainerMenu* pContainerMenu = pPlayer->m_pContainerMenu; + bool isInventory = false; + + // @HACK: LocalPlayer inventory seems to always have Container ID 0 + if (packet->m_containerId == 0) + { + pContainerMenu = pPlayer->m_pInventoryMenu; + isInventory = true; + } + if (!pContainerMenu) return; - if (pContainerMenu->m_containerId == packet->m_containerId) + if (pContainerMenu->m_containerId == packet->m_containerId + || (isInventory && packet->m_containerId == 0)) { switch (pContainerMenu->m_containerType) { diff --git a/source/world/entity/Player.cpp b/source/world/entity/Player.cpp index c9e745d6a..f5f54b016 100644 --- a/source/world/entity/Player.cpp +++ b/source/world/entity/Player.cpp @@ -520,16 +520,11 @@ void Player::setRespawnPos(const TilePos& pos) m_respawnPos = pos; } -/*void Player::drop() +// @PARITY: From b1.2_02, doesn't exist in PE +void Player::drop() { - // @PARITY: From b1.2_02, doesn't exist in PE - // Isn't called anywhere, but is overriden in MultiplayerLocalPlayer with a PlayerActionPacket - ItemStack* item = getSelectedItem(); - if (!item) - return; - - drop(m_pInventory->removeItem(*item, 1)); -}*/ + drop(m_pInventory->removeItem(m_pInventory->getSelectedSlotNo(), 1)); +} void Player::drop(const ItemStack& item, bool randomly) { diff --git a/source/world/entity/Player.hpp b/source/world/entity/Player.hpp index 786bd7c8d..06b3f7b87 100644 --- a/source/world/entity/Player.hpp +++ b/source/world/entity/Player.hpp @@ -68,7 +68,7 @@ class Player : public Mob void causeFallDamage(float level) override; virtual void animateRespawn(); - //virtual void drop(); // see definition + virtual void drop(); virtual void drop(const ItemStack& item, bool randomly = false); virtual void startCrafting(const TilePos& pos); virtual void startStonecutting(const TilePos& pos); diff --git a/source/world/inventory/ContainerMenu.cpp b/source/world/inventory/ContainerMenu.cpp index c24737b64..b2c083130 100644 --- a/source/world/inventory/ContainerMenu.cpp +++ b/source/world/inventory/ContainerMenu.cpp @@ -125,12 +125,14 @@ std::vector ContainerMenu::cloneItems() for (std::vector::iterator it = m_slots.begin(); it != m_slots.end(); ++it) { Slot* slot = *it; +#ifndef FEATURE_SERVER_INVENTORIES // @TODO: I really do not like this. // Firstly, inventories shouldn't be owned by the client // Secondly, we shouldn't be checking types directly like this // Ultimately this HAS to have two different storages, one for the inventory and one for the container if (slot->m_group == Slot::INVENTORY || slot->m_group == Slot::HOTBAR) continue; +#endif const ItemStack& item = (*it)->getItem(); content.push_back(item); } diff --git a/source/world/item/Inventory.cpp b/source/world/item/Inventory.cpp index 100ad7199..49706590e 100644 --- a/source/world/item/Inventory.cpp +++ b/source/world/item/Inventory.cpp @@ -268,8 +268,8 @@ ItemStack Inventory::removeItem(StackID stackId, int count) return removed; } } - else - return ItemStack::EMPTY; + + return ItemStack::EMPTY; } int Inventory::getSlot(int id) @@ -371,7 +371,8 @@ bool Inventory::hasUnlimitedResource(const ItemStack& item) const ItemStack& Inventory::getItem(StackID stackId) { - assert(stackId >= 0 && stackId < getContainerSize()); + // stackId < getContainerSize() trips when the RemotePlayer dies + //assert(stackId >= 0 && stackId < getContainerSize()); if (size_t(stackId) < m_items.size()) return m_items[stackId]; From 995e44a8558cae0cc6760229db9cbcf3a04b9678 Mon Sep 17 00:00:00 2001 From: Brent Da Mage Date: Sat, 16 May 2026 00:29:57 -0500 Subject: [PATCH 55/63] Fixed multiplayer inventory drop on death --- .../multiplayer/MultiplayerLocalPlayer.cpp | 8 +++----- source/client/player/LocalPlayer.cpp | 4 ++++ .../network/packets/SendInventoryPacket.cpp | 19 ++++++++++--------- .../network/packets/SendInventoryPacket.hpp | 13 ++++++++++--- source/server/ServerSideNetworkHandler.cpp | 2 +- source/world/entity/Player.cpp | 5 +++++ source/world/item/Inventory.cpp | 6 +++--- source/world/item/Inventory.hpp | 2 +- 8 files changed, 37 insertions(+), 22 deletions(-) diff --git a/source/client/multiplayer/MultiplayerLocalPlayer.cpp b/source/client/multiplayer/MultiplayerLocalPlayer.cpp index ee1d313b3..526fd0ee5 100644 --- a/source/client/multiplayer/MultiplayerLocalPlayer.cpp +++ b/source/client/multiplayer/MultiplayerLocalPlayer.cpp @@ -101,16 +101,14 @@ void MultiplayerLocalPlayer::hurtTo(int newHealth) void MultiplayerLocalPlayer::die(Entity* pCulprit) { #if NETWORK_PROTOCOL_VERSION >= 4 - SendInventoryPacket* pPkt = new SendInventoryPacket(); - pPkt->m_entityId = m_EntityID; - pPkt->m_bDropAll = true; + SendInventoryPacket* pPkt = new SendInventoryPacket(m_EntityID, true); - uint16_t size = m_pInventory->getContainerSize(); + Container::Size size = m_pInventory->getContainerSize(); // 0.3.0 if (size > 9) { - for (int i = 0; i < size; i++) + for (Container::StackID i = 0; i < size; i++) { pPkt->m_items.push_back(m_pInventory->getItem(i)); } diff --git a/source/client/player/LocalPlayer.cpp b/source/client/player/LocalPlayer.cpp index dfb8a4d1d..2915304fb 100644 --- a/source/client/player/LocalPlayer.cpp +++ b/source/client/player/LocalPlayer.cpp @@ -61,7 +61,11 @@ LocalPlayer::~LocalPlayer() void LocalPlayer::die(Entity* pCulprit) { #if NETWORK_PROTOCOL_VERSION >= 4 +#ifdef FEATURE_SERVER_INVENTORIES m_pInventory->dropAll(); +#else + m_pInventory->dropAll(m_pLevel->m_bIsClientSide); +#endif #endif Player::die(pCulprit); diff --git a/source/network/packets/SendInventoryPacket.cpp b/source/network/packets/SendInventoryPacket.cpp index 2e1888ae4..067cfee83 100644 --- a/source/network/packets/SendInventoryPacket.cpp +++ b/source/network/packets/SendInventoryPacket.cpp @@ -2,11 +2,10 @@ #include "network/NetEventCallback.hpp" #include "network/PacketUtil.hpp" -SendInventoryPacket::SendInventoryPacket(int32_t entityId, const std::vector& items, bool dropAll) +SendInventoryPacket::SendInventoryPacket(int32_t entityId, bool dropAll) : m_entityId(entityId) - , m_items(items) , m_count(0) - , m_bDropAll(dropAll) + , m_extra(dropAll ? EXTRA_DROP_ALL : 0) { } @@ -17,9 +16,9 @@ void SendInventoryPacket::handle(const RakNet::RakNetGUID& guid, NetEventCallbac void SendInventoryPacket::write(RakNet::BitStream& bs) { - bs.Write((unsigned char)PACKET_DROP_ITEM); + bs.Write((unsigned char)PACKET_SEND_INVENTORY); bs.Write(m_entityId); - bs.Write(m_bDropAll); + bs.Write(m_extra); bs.Write((uint16_t)m_items.size()); for (size_t i = 0; i < m_items.size(); i++) @@ -31,15 +30,17 @@ void SendInventoryPacket::write(RakNet::BitStream& bs) void SendInventoryPacket::read(RakNet::BitStream& bs) { bs.Read(m_entityId); - bs.Read(m_bDropAll); + bs.Read(m_extra); bs.Read(m_count); + m_count = Mth::Min(m_count, 512); + // End early, processing the rest of this (like PE does) is just a recipe for disaster // Plus, it doesn't really matter, as PE just sends 512 empty items anyways - /* - m_items.resize(m_count); + + m_items.reserve(m_count); for (uint16_t i = 0; i < m_count; i++) { m_items.push_back(PacketUtil::ReadItemStack(bs, false)); - }*/ + } } diff --git a/source/network/packets/SendInventoryPacket.hpp b/source/network/packets/SendInventoryPacket.hpp index 954783c76..1559c1eb1 100644 --- a/source/network/packets/SendInventoryPacket.hpp +++ b/source/network/packets/SendInventoryPacket.hpp @@ -7,14 +7,21 @@ class SendInventoryPacket : public Packet { +public: + enum Extra + { + EXTRA_NONE, + EXTRA_DROP_ALL + }; + public: SendInventoryPacket() { m_entityId = 0; m_count = 0; - m_bDropAll = false; + m_extra = 0; } - SendInventoryPacket(int32_t entityId, const std::vector& items, bool dropAll = false); + SendInventoryPacket(int32_t entityId, bool dropAll = false); void handle(const RakNet::RakNetGUID&, NetEventCallback& callback) override; void write(RakNet::BitStream&) override; @@ -23,5 +30,5 @@ class SendInventoryPacket : public Packet int32_t m_entityId; std::vector m_items; uint16_t m_count; - bool m_bDropAll; + uint8_t m_extra; }; diff --git a/source/server/ServerSideNetworkHandler.cpp b/source/server/ServerSideNetworkHandler.cpp index f6dce0b2a..4cdfe4cf0 100644 --- a/source/server/ServerSideNetworkHandler.cpp +++ b/source/server/ServerSideNetworkHandler.cpp @@ -595,7 +595,7 @@ void ServerSideNetworkHandler::handle(const RakNet::RakNetGUID& guid, SendInvent pPlayer->m_pInventory->replace(packet->m_items); - if (packet->m_bDropAll) + if (packet->m_extra == SendInventoryPacket::EXTRA_DROP_ALL) pPlayer->m_pInventory->dropAll(); } diff --git a/source/world/entity/Player.cpp b/source/world/entity/Player.cpp index f5f54b016..97f052c4f 100644 --- a/source/world/entity/Player.cpp +++ b/source/world/entity/Player.cpp @@ -173,6 +173,11 @@ void Player::die(Entity* pCulprit) if (m_name == "Notch") drop(ItemStack(Item::apple), true); } + +#ifndef FEATURE_SERVER_INVENTORIES + // don't drop items on the server, leave it to SendInventoryPacket + if (m_pLevel->m_bIsClientSide) +#endif #if NETWORK_PROTOCOL_VERSION <= 3 m_pInventory->dropAll(m_pLevel->m_bIsClientSide); #endif diff --git a/source/world/item/Inventory.cpp b/source/world/item/Inventory.cpp index 49706590e..050be99bb 100644 --- a/source/world/item/Inventory.cpp +++ b/source/world/item/Inventory.cpp @@ -121,9 +121,9 @@ void Inventory::prepareSurvivalInventory() #endif } -uint16_t Inventory::getContainerSize() const +Container::Size Inventory::getContainerSize() const { - return uint16_t(m_items.size() + m_armor.size()); + return (Size)(m_items.size() + m_armor.size()); } void Inventory::clear() @@ -372,7 +372,7 @@ bool Inventory::hasUnlimitedResource(const ItemStack& item) const ItemStack& Inventory::getItem(StackID stackId) { // stackId < getContainerSize() trips when the RemotePlayer dies - //assert(stackId >= 0 && stackId < getContainerSize()); + assert(stackId >= 0 && stackId < getContainerSize()); if (size_t(stackId) < m_items.size()) return m_items[stackId]; diff --git a/source/world/item/Inventory.hpp b/source/world/item/Inventory.hpp index a5a8e5b8c..9899c1d46 100644 --- a/source/world/item/Inventory.hpp +++ b/source/world/item/Inventory.hpp @@ -28,7 +28,7 @@ class Inventory : public Container void prepareCreativeInventory(); void prepareSurvivalInventory(); - uint16_t getContainerSize() const override; + Size getContainerSize() const override; void clear(); void replace(const std::vector& items); From 7b0fc51a7a426cb36b14bdc4dc31ce4f86aa11ba Mon Sep 17 00:00:00 2001 From: Brent Da Mage Date: Sat, 16 May 2026 03:43:49 -0500 Subject: [PATCH 56/63] Added Console theme support for ChestScreen --- .../gui/screens/OptionsScreen_Console.cpp | 16 ++- .../gui/screens/OptionsScreen_Console.hpp | 4 +- .../gui/screens/inventory/ChestScreen.cpp | 103 ++++++++++++++---- .../gui/screens/inventory/ChestScreen.hpp | 4 + .../gui/screens/inventory/InventoryScreen.cpp | 6 +- .../network/packets/SendInventoryPacket.cpp | 7 +- .../network/packets/SendInventoryPacket.hpp | 2 +- source/world/inventory/SimpleContainer.cpp | 4 +- source/world/inventory/SimpleContainer.hpp | 2 +- source/world/tile/ChestTile.cpp | 65 +++++------ source/world/tile/entity/TileEntityType.cpp | 21 ---- source/world/tile/entity/TileEntityType.hpp | 12 +- 12 files changed, 149 insertions(+), 97 deletions(-) diff --git a/source/client/gui/screens/OptionsScreen_Console.cpp b/source/client/gui/screens/OptionsScreen_Console.cpp index a811200aa..ef505cdb0 100644 --- a/source/client/gui/screens/OptionsScreen_Console.cpp +++ b/source/client/gui/screens/OptionsScreen_Console.cpp @@ -19,9 +19,9 @@ OptionsScreen_Console::OptionsScreen_Console(Screen* screen) : void OptionsScreen_Console::_buttonClicked(Button* btn) { if (btn->getId() == m_btnControls.getId()) - m_pMinecraft->setScreen(new ControlsPanelScreen(this, m_pMinecraft)); + m_pMinecraft->setScreen(new ControlsPanelScreen(this, *m_pMinecraft)); else if (btn->getId() == m_btnSettings.getId()) - m_pMinecraft->setScreen(new SettingsPanelScreen(this, *m_pMinecraft->getOptions())); + m_pMinecraft->setScreen(new SettingsPanelScreen(this, *m_pMinecraft)); else if (btn->getId() == m_btnCredits.getId()) m_pMinecraft->getScreenChooser()->pushCreditsScreen(this); else if (btn->getId() == m_btnResetToDefaults.getId()) @@ -73,9 +73,9 @@ bool OptionsScreen_Console::handleBackEvent(bool b) #define HEADER(text) do { m_layout.m_elements.push_back(new OptionHeader_Console(text)); currentIndex++; } while (0) #define OPTION(name) do { options.name.addGuiElement(m_layout.m_elements, m_uiTheme); currentIndex++; } while (0) -ControlsPanelScreen::ControlsPanelScreen(Screen* parent, Minecraft* mc) : PanelScreen_Console(parent) +ControlsPanelScreen::ControlsPanelScreen(Screen* parent, Minecraft& mc) : PanelScreen_Console(parent) { - Options& options = *mc->getOptions(); + Options& options = *mc.getOptions(); int currentIndex = -1; int idxSplit = -1, idxController = -1; @@ -85,7 +85,7 @@ ControlsPanelScreen::ControlsPanelScreen(Screen* parent, Minecraft* mc) : PanelS m_layout.m_elements[idxController]->setEnabled(false); - if (!mc->isTouchscreen()) + if (!mc.isTouchscreen()) m_layout.m_elements[idxSplit]->setEnabled(false); } @@ -94,11 +94,15 @@ void ControlsPanelScreen::removed() m_pMinecraft->saveOptionsAsync(); } -SettingsPanelScreen::SettingsPanelScreen(Screen* parent, Options& options) : PanelScreen_Console(parent) +SettingsPanelScreen::SettingsPanelScreen(Screen* parent, Minecraft& mc) : PanelScreen_Console(parent) { + m_pMinecraft = &mc; + int currentIndex = -1; int idxPano = -1, idxVSync = -1; + Options& options = *mc.getOptions(); + OPTIONS_LIST_GAMEPLAY_GAME; OPTIONS_LIST_GAMEPLAY_AUDIO; OPTIONS_LIST_VIDEO_GRAPHICS; diff --git a/source/client/gui/screens/OptionsScreen_Console.hpp b/source/client/gui/screens/OptionsScreen_Console.hpp index 3a7a3cbe7..1d2c164cb 100644 --- a/source/client/gui/screens/OptionsScreen_Console.hpp +++ b/source/client/gui/screens/OptionsScreen_Console.hpp @@ -6,7 +6,7 @@ class ControlsPanelScreen : public PanelScreen_Console { public: - ControlsPanelScreen(Screen*, Minecraft*); + ControlsPanelScreen(Screen*, Minecraft&); void removed() override; }; @@ -14,7 +14,7 @@ class ControlsPanelScreen : public PanelScreen_Console class SettingsPanelScreen : public PanelScreen_Console { public: - SettingsPanelScreen(Screen*, Options&); + SettingsPanelScreen(Screen*, Minecraft&); void render(float) override; void removed() override; diff --git a/source/client/gui/screens/inventory/ChestScreen.cpp b/source/client/gui/screens/inventory/ChestScreen.cpp index aa105aaa0..7b04b1d37 100644 --- a/source/client/gui/screens/inventory/ChestScreen.cpp +++ b/source/client/gui/screens/inventory/ChestScreen.cpp @@ -6,40 +6,101 @@ ChestScreen::ChestScreen(Container* inventory, Container* container) : Container m_pInventory(inventory), m_pContainer(container) { - constexpr int defaultHeight = 222; - constexpr int noRowHeight = defaultHeight - 108; m_containerRows = m_pContainer->getContainerSize() / 9; - m_imageHeight = noRowHeight + m_containerRows * 18; +} + +void ChestScreen::init() +{ + if (m_uiTheme == UI_CONSOLE) + { + constexpr int defaultHeight = 412; + constexpr int noRowHeight = defaultHeight - 126; + m_imageWidth = 432; + m_imageHeight = noRowHeight + m_containerRows * 42; + } + else + { + constexpr int defaultHeight = 222; + constexpr int noRowHeight = defaultHeight - 108; + m_imageHeight = noRowHeight + m_containerRows * 18; + } + + ContainerScreen::init(); + + if (m_uiTheme == UI_CONSOLE) + m_topPos = Mth::Max(24, m_topPos - 48); } void ChestScreen::_renderLabels() { - m_pFont->draw(m_pContainer->getName(), 8, 6, Color::TEXT_GREY); - m_pFont->draw(m_pInventory->getName(), 8, m_imageHeight - 96 + 2, Color::TEXT_GREY); + if (m_uiTheme == UI_CONSOLE) + { + m_pFont->drawScalable(m_pContainer->getName(), 28, 20, Color::TEXT_GREY, 2.5f); + m_pFont->drawScalable(m_pInventory->getName(), 28, m_imageHeight - 237 + 8, Color::TEXT_GREY, 2.5f); + } + else + { + m_pFont->draw(m_pContainer->getName(), 8, 6, Color::TEXT_GREY); + m_pFont->draw(m_pInventory->getName(), 8, m_imageHeight - 96 + 2, Color::TEXT_GREY); + } } void ChestScreen::_renderBg(float partialTicks) { - m_pMinecraft->m_pTextures->loadAndBindTexture("gui/container.png"); - currentShaderColor = Color::WHITE; + if (m_uiTheme == UI_CONSOLE) + { + blitNineSlice(*m_pMinecraft->m_pTextures, ScreenRenderer::PANEL_SLICES, m_leftPos, m_topPos, m_imageWidth, m_imageHeight, 32); + } + else + { + m_pMinecraft->m_pTextures->loadAndBindTexture("gui/container.png"); + currentShaderColor = Color::WHITE; - blit(m_leftPos, m_topPos, 0, 0, m_imageWidth, m_containerRows * 18 + 17, 0, 0); - blit(m_leftPos, m_topPos + m_containerRows * 18 + 17, 0, 126, m_imageWidth, 96, 0, 0); + blit(m_leftPos, m_topPos, 0, 0, m_imageWidth, m_containerRows * 18 + 17, 0, 0); + blit(m_leftPos, m_topPos + m_containerRows * 18 + 17, 0, 126, m_imageWidth, 96, 0, 0); + } } SlotDisplay ChestScreen::_createSlotDisplay(const Slot& slot) { - constexpr int slotSize = 18; - int rows = m_pContainer->getContainerSize() / 9; - switch (slot.m_group) - { - case Slot::CONTAINER: - return SlotDisplay(8 + (slot.m_stackId % 9) * slotSize, 18 + (slot.m_stackId / 9) * slotSize, slotSize); - case Slot::INVENTORY: - return SlotDisplay(8 + (slot.m_stackId % 9) * slotSize, (rows * 18) + 13 + (slot.m_stackId / 9) * slotSize, slotSize); - case Slot::HOTBAR: - return SlotDisplay(8 + (slot.m_stackId % 9) * slotSize, 89 + rows * slotSize, slotSize); - default: - return SlotDisplay(); + if (m_uiTheme == UI_CONSOLE) + { + constexpr int slotSize = 42; + int rows = m_pContainer->getContainerSize() / 9; + switch (slot.m_group) + { + case Slot::CONTAINER: + return SlotDisplay(28 + (slot.m_stackId % 9) * slotSize, 50 + (slot.m_stackId / 9) * slotSize, slotSize, true); + case Slot::INVENTORY: + return SlotDisplay(28 + (slot.m_stackId % 9) * slotSize, (rows * 42) + 45 + (slot.m_stackId / 9) * slotSize, slotSize, true); + case Slot::HOTBAR: + return SlotDisplay(28 + (slot.m_stackId % 9) * slotSize, 224 + rows * slotSize, slotSize, true); + default: + return SlotDisplay(); + } } + else + { + constexpr int slotSize = 18; + int rows = m_pContainer->getContainerSize() / 9; + switch (slot.m_group) + { + case Slot::CONTAINER: + return SlotDisplay(8 + (slot.m_stackId % 9) * slotSize, 18 + (slot.m_stackId / 9) * slotSize, slotSize); + case Slot::INVENTORY: + return SlotDisplay(8 + (slot.m_stackId % 9) * slotSize, (rows * 18) + 13 + (slot.m_stackId / 9) * slotSize, slotSize); + case Slot::HOTBAR: + return SlotDisplay(8 + (slot.m_stackId % 9) * slotSize, 89 + rows * slotSize, slotSize); + default: + return SlotDisplay(); + } + } +} + +void ChestScreen::renderBackground() +{ + if (m_uiTheme == UI_CONSOLE) + return; + + ContainerScreen::renderBackground(); } diff --git a/source/client/gui/screens/inventory/ChestScreen.hpp b/source/client/gui/screens/inventory/ChestScreen.hpp index e5b4a92b8..5a9ccaf2c 100644 --- a/source/client/gui/screens/inventory/ChestScreen.hpp +++ b/source/client/gui/screens/inventory/ChestScreen.hpp @@ -6,12 +6,16 @@ class ChestScreen : public ContainerScreen { public: ChestScreen(Container* inventory, Container* container); + void init() override; protected: void _renderLabels() override; void _renderBg(float partialTicks) override; SlotDisplay _createSlotDisplay(const Slot&); +public: + void renderBackground() override; + private: Container* m_pInventory; Container* m_pContainer; diff --git a/source/client/gui/screens/inventory/InventoryScreen.cpp b/source/client/gui/screens/inventory/InventoryScreen.cpp index 45f05a8b3..12db8a335 100644 --- a/source/client/gui/screens/inventory/InventoryScreen.cpp +++ b/source/client/gui/screens/inventory/InventoryScreen.cpp @@ -25,8 +25,10 @@ void InventoryScreen::init() void InventoryScreen::renderBackground() { - if (m_uiTheme != UI_CONSOLE) - ContainerScreen::renderBackground(); + if (m_uiTheme == UI_CONSOLE) + return; + + ContainerScreen::renderBackground(); } void InventoryScreen::_renderLabels() diff --git a/source/network/packets/SendInventoryPacket.cpp b/source/network/packets/SendInventoryPacket.cpp index 067cfee83..376255384 100644 --- a/source/network/packets/SendInventoryPacket.cpp +++ b/source/network/packets/SendInventoryPacket.cpp @@ -5,7 +5,7 @@ SendInventoryPacket::SendInventoryPacket(int32_t entityId, bool dropAll) : m_entityId(entityId) , m_count(0) - , m_extra(dropAll ? EXTRA_DROP_ALL : 0) + , m_extra(dropAll ? EXTRA_DROP_ALL : EXTRA_NONE) { } @@ -34,11 +34,8 @@ void SendInventoryPacket::read(RakNet::BitStream& bs) bs.Read(m_count); m_count = Mth::Min(m_count, 512); - - // End early, processing the rest of this (like PE does) is just a recipe for disaster - // Plus, it doesn't really matter, as PE just sends 512 empty items anyways - m_items.reserve(m_count); + for (uint16_t i = 0; i < m_count; i++) { m_items.push_back(PacketUtil::ReadItemStack(bs, false)); diff --git a/source/network/packets/SendInventoryPacket.hpp b/source/network/packets/SendInventoryPacket.hpp index 1559c1eb1..df53b6ac8 100644 --- a/source/network/packets/SendInventoryPacket.hpp +++ b/source/network/packets/SendInventoryPacket.hpp @@ -19,7 +19,7 @@ class SendInventoryPacket : public Packet { m_entityId = 0; m_count = 0; - m_extra = 0; + m_extra = EXTRA_NONE; } SendInventoryPacket(int32_t entityId, bool dropAll = false); diff --git a/source/world/inventory/SimpleContainer.cpp b/source/world/inventory/SimpleContainer.cpp index 6974e4956..2b5c0f07b 100644 --- a/source/world/inventory/SimpleContainer.cpp +++ b/source/world/inventory/SimpleContainer.cpp @@ -8,9 +8,9 @@ SimpleContainer::SimpleContainer(Size size, const std::string& name) { } -uint16_t SimpleContainer::getContainerSize() const +Container::Size SimpleContainer::getContainerSize() const { - return uint16_t(m_items.size()); + return (Size)(m_items.size()); } ItemStack& SimpleContainer::getItem(StackID index) diff --git a/source/world/inventory/SimpleContainer.hpp b/source/world/inventory/SimpleContainer.hpp index 93454c140..cf11cb4ef 100644 --- a/source/world/inventory/SimpleContainer.hpp +++ b/source/world/inventory/SimpleContainer.hpp @@ -11,7 +11,7 @@ class SimpleContainer : public Container SimpleContainer(Size size, const std::string& name); public: - uint16_t getContainerSize() const override; + Size getContainerSize() const override; ItemStack& getItem(StackID index) override; ItemStack removeItem(StackID index, int count) override; void setItem(StackID index, const ItemStack& item) override; diff --git a/source/world/tile/ChestTile.cpp b/source/world/tile/ChestTile.cpp index a71916d53..3c6f36e58 100644 --- a/source/world/tile/ChestTile.cpp +++ b/source/world/tile/ChestTile.cpp @@ -13,10 +13,10 @@ int ChestTile::getTexture(const LevelSource* level, const TilePos& pos, Facing:: if (face == Facing::UP || face == Facing::DOWN) return m_TextureFrame - 1; - int id_north = level->getTile(pos.north()); - int id_south = level->getTile(pos.south()); - int id_west = level->getTile(pos.west()); - int id_east = level->getTile(pos.east()); + TileID id_north = level->getTile(pos.north()); + TileID id_south = level->getTile(pos.south()); + TileID id_west = level->getTile(pos.west()); + TileID id_east = level->getTile(pos.east()); bool isDoubleNS = (id_north == m_ID || id_south == m_ID); bool isDoubleWE = (id_west == m_ID || id_east == m_ID); @@ -45,8 +45,8 @@ int ChestTile::getTexture(const LevelSource* level, const TilePos& pos, Facing:: left = id_west == m_ID; TilePos side = left ? pos.west() : pos.east(); - int id_behind = level->getTile(side.north()); - int id_infront = level->getTile(side.south()); + TileID id_behind = level->getTile(side.north()); + TileID id_infront = level->getTile(side.south()); if ((Tile::solid[id_north] || Tile::solid[id_behind]) && !Tile::solid[id_south] && !Tile::solid[id_infront]) front = Facing::SOUTH; @@ -68,8 +68,8 @@ int ChestTile::getTexture(const LevelSource* level, const TilePos& pos, Facing:: left = id_north == m_ID; TilePos side = left ? pos.north() : pos.south(); - int id_behind = level->getTile(side.west()); - int id_infront = level->getTile(side.east()); + TileID id_behind = level->getTile(side.west()); + TileID id_infront = level->getTile(side.east()); if ((Tile::solid[id_west] || Tile::solid[id_behind]) && !Tile::solid[id_east] && !Tile::solid[id_infront]) front = Facing::EAST; @@ -137,28 +137,29 @@ void ChestTile::onRemove(Level* level, const TilePos& pos) return; } - for (int slot = 0; slot < ent->getContainerSize(); ++slot) + for (Container::StackID slot = 0; slot < ent->getContainerSize(); ++slot) { ItemStack& item = ent->getItem(slot); - if (!item.isEmpty()) + if (item.isEmpty()) + continue; + + constexpr float spread = 0.05f; + + TilePos offset( + (m_chestRandom.nextFloat() * 0.8f) + 0.1f, + (m_chestRandom.nextFloat() * 0.8f) + 0.1f, + (m_chestRandom.nextFloat() * 0.8f) + 0.1f + ); + + while (item.m_count > 0) { - TilePos offset( - (m_chestRandom.nextFloat() * 0.8f) + 0.1f, - (m_chestRandom.nextFloat() * 0.8f) + 0.1f, - (m_chestRandom.nextFloat() * 0.8f) + 0.1f - ); - - while (item.m_count > 0) - { - int toRemove = std::min(m_chestRandom.nextInt(21) + 10, static_cast(item.m_count)); - item.m_count -= toRemove; - ItemEntity* itemEnt = new ItemEntity(level, pos + offset, ItemStack(item.getItem()->m_itemID, toRemove, item.getAuxValue())); - float spread = 0.05f; - itemEnt->m_vel.x = (double)((float)m_chestRandom.nextGaussian() * spread); - itemEnt->m_vel.y = (double)((float)m_chestRandom.nextGaussian() * spread + 0.2f); - itemEnt->m_vel.z = (double)((float)m_chestRandom.nextGaussian() * spread); - level->addEntity(itemEnt); - } + int toRemove = std::min(m_chestRandom.nextInt(21) + 10, static_cast(item.m_count)); + item.m_count -= toRemove; + ItemEntity* itemEnt = new ItemEntity(level, pos + offset, ItemStack(item.getItem()->m_itemID, toRemove, item.getAuxValue())); + itemEnt->m_vel.x = (double)((float)m_chestRandom.nextGaussian() * spread); + itemEnt->m_vel.y = (double)((float)m_chestRandom.nextGaussian() * spread + 0.2f); + itemEnt->m_vel.z = (double)((float)m_chestRandom.nextGaussian() * spread); + level->addEntity(itemEnt); } } @@ -192,18 +193,18 @@ bool ChestTile::use(Level* level, const TilePos& pos, Player* player) if (!tileEnt) return false; - ChestTileEntity* chest = static_cast(tileEnt); - Container* container = static_cast(chest); + ChestTileEntity* chest = (ChestTileEntity*)tileEnt; + Container* container = dynamic_cast(chest); for (int rel = Facing::NORTH; rel <= Facing::EAST; rel++) { - TilePos relPos = pos.relative(static_cast(rel)); - if (level->getTile(pos.relative(static_cast(rel))) == m_ID) + TilePos relPos = pos.relative((Facing::Name)rel); + if (level->getTile(pos.relative((Facing::Name)rel)) == m_ID) { TileEntity* nearbyTileEntity = level->getTileEntity(relPos); if (!nearbyTileEntity) continue; - Container* nearbyContainer = static_cast(static_cast(nearbyTileEntity)); + Container* nearbyContainer = dynamic_cast(nearbyTileEntity); if (rel % 2 == 0) container = new CompoundContainer("Large chest", nearbyContainer, container); else diff --git a/source/world/tile/entity/TileEntityType.cpp b/source/world/tile/entity/TileEntityType.cpp index 84a902783..8e513dfe0 100644 --- a/source/world/tile/entity/TileEntityType.cpp +++ b/source/world/tile/entity/TileEntityType.cpp @@ -30,24 +30,3 @@ void TileEntityFactory::teardownTileEntities() } _types.clear(); } - -const TileEntityType* TileEntityFactory::getType(const std::string& name) -{ - return _types[name]; -} - -TileEntityType::TileEntityType(const std::string& name, CreateFunction func) - : m_name(name) - , m_function(func) -{ -} - -const std::string& TileEntityType::getName() const -{ - return m_name; -} - -TileEntity* TileEntityType::newTileEntity() const -{ - return m_function(); -} \ No newline at end of file diff --git a/source/world/tile/entity/TileEntityType.hpp b/source/world/tile/entity/TileEntityType.hpp index aec0cb513..bce47445f 100644 --- a/source/world/tile/entity/TileEntityType.hpp +++ b/source/world/tile/entity/TileEntityType.hpp @@ -16,7 +16,7 @@ class TileEntityFactory public: static void initTileEntities(); static void teardownTileEntities(); - static const TileEntityType* getType(const std::string& name); + static const TileEntityType* getType(const std::string& name) { return _types[name]; } public: template @@ -35,11 +35,15 @@ class TileEntityType typedef TileEntity* (*CreateFunction)(); public: - TileEntityType(const std::string& name, CreateFunction func); + TileEntityType(const std::string& name, CreateFunction func) + : m_name(name) + , m_function(func) + { + } public: - const std::string& getName() const; - TileEntity* newTileEntity() const; + const std::string& getName() const { return m_name; } + TileEntity* newTileEntity() const { return m_function(); } private: std::string m_name; From 2b17911edb1c17d2ba8bb96468ac1d2572b62717 Mon Sep 17 00:00:00 2001 From: Brent Da Mage Date: Sun, 17 May 2026 04:08:56 -0500 Subject: [PATCH 57/63] Improved parity with Console Edition * Fixed text label parity of ChestScreen * Removed background rendering for FurnaceScreen on console * Added WIP Console FurnaceScreen * Fixed label rendering to be in parity with TU1 --- source/client/gui/components/SliderButton.cpp | 2 +- .../gui/screens/inventory/ChestScreen.cpp | 6 +- .../gui/screens/inventory/ContainerScreen.cpp | 8 +- .../gui/screens/inventory/FurnaceScreen.cpp | 123 ++++++++++++++---- .../gui/screens/inventory/FurnaceScreen.hpp | 4 + source/client/renderer/ScreenRenderer.cpp | 6 +- source/client/renderer/Textures.cpp | 2 + 7 files changed, 116 insertions(+), 35 deletions(-) diff --git a/source/client/gui/components/SliderButton.cpp b/source/client/gui/components/SliderButton.cpp index c51698e89..8689349f2 100644 --- a/source/client/gui/components/SliderButton.cpp +++ b/source/client/gui/components/SliderButton.cpp @@ -69,7 +69,7 @@ void SliderButton::renderBg(Minecraft* mc, const MenuPointer& pointer) if (m_uiTheme == UI_CONSOLE) { blitSprite(texs, "gui/console/Graphics/Slider_Track.png", m_xPos, m_yPos, m_width - 2, m_height, nullptr, 0.0f, 0.0f, m_width - 2); - blitSprite(texs, "gui/console/Graphics/Slider_Track.png", m_xPos + m_width - 2, m_yPos, 2, m_height, nullptr, 0.0f, 0.0f, 2.0f); + blitSprite(texs, "gui/console/Graphics/Slider_Track.png", m_xPos + m_width - 2, m_yPos, 2, m_height, nullptr, 0.0f, 0.0f, 2); if (isSelected()) blitNineSlice(texs, "gui/slider_highlight.png", m_xPos - 3, m_yPos - 3, m_width + 6, m_height + 6, 5); blitSprite(texs, "gui/console/Graphics/Slider_Button.png", m_xPos + int(m_value * float(m_width - 16)), m_yPos, 16, m_height); diff --git a/source/client/gui/screens/inventory/ChestScreen.cpp b/source/client/gui/screens/inventory/ChestScreen.cpp index 7b04b1d37..5fc0d08f4 100644 --- a/source/client/gui/screens/inventory/ChestScreen.cpp +++ b/source/client/gui/screens/inventory/ChestScreen.cpp @@ -35,8 +35,8 @@ void ChestScreen::_renderLabels() { if (m_uiTheme == UI_CONSOLE) { - m_pFont->drawScalable(m_pContainer->getName(), 28, 20, Color::TEXT_GREY, 2.5f); - m_pFont->drawScalable(m_pInventory->getName(), 28, m_imageHeight - 237 + 8, Color::TEXT_GREY, 2.5f); + m_pFont->drawScalable(m_pContainer->getName(), 28, 20, Color::TEXT_GREY); + m_pFont->drawScalable(m_pInventory->getName(), 28, m_imageHeight - 234 + 8, Color::TEXT_GREY); } else { @@ -74,7 +74,7 @@ SlotDisplay ChestScreen::_createSlotDisplay(const Slot& slot) case Slot::INVENTORY: return SlotDisplay(28 + (slot.m_stackId % 9) * slotSize, (rows * 42) + 45 + (slot.m_stackId / 9) * slotSize, slotSize, true); case Slot::HOTBAR: - return SlotDisplay(28 + (slot.m_stackId % 9) * slotSize, 224 + rows * slotSize, slotSize, true); + return SlotDisplay(28 + (slot.m_stackId % 9) * slotSize, 226 + rows * slotSize, slotSize, true); default: return SlotDisplay(); } diff --git a/source/client/gui/screens/inventory/ContainerScreen.cpp b/source/client/gui/screens/inventory/ContainerScreen.cpp index cdfc1fa35..603dfbb7c 100644 --- a/source/client/gui/screens/inventory/ContainerScreen.cpp +++ b/source/client/gui/screens/inventory/ContainerScreen.cpp @@ -212,13 +212,13 @@ void ContainerScreen::render(float partialTicks) if (!name.empty()) { int w = m_pFont->width(name); - int tx = m_menuPointer.x - m_leftPos + 12; - int ty = m_menuPointer.y - m_topPos - 12; + int tx = m_menuPointer.x - m_leftPos + 10; + int ty = m_menuPointer.y - m_topPos - 32; if (m_uiTheme == UI_CONSOLE) { - blitNineSlice(*m_pMinecraft->m_pTextures, ScreenRenderer::POINTER_TEXT_PANEL_SLICES, tx - 6, ty - 6, w * 2 + 12, 28, 8); + blitNineSlice(*m_pMinecraft->m_pTextures, ScreenRenderer::POINTER_TEXT_PANEL_SLICES, tx - 6, ty - 6, w * 2 + 12, 34, 8); MatrixStack::Ref tooltipMatrix = MatrixStack::World.push(); - m_pFont->drawScalable(name, tx, ty, -1); + m_pFont->drawScalableShadow(name, tx, ty + 4, -1); } else { diff --git a/source/client/gui/screens/inventory/FurnaceScreen.cpp b/source/client/gui/screens/inventory/FurnaceScreen.cpp index 9b500370b..82981752d 100644 --- a/source/client/gui/screens/inventory/FurnaceScreen.cpp +++ b/source/client/gui/screens/inventory/FurnaceScreen.cpp @@ -7,7 +7,18 @@ FurnaceScreen::FurnaceScreen(Inventory* inventory, FurnaceTileEntity* container) , m_pInventory(inventory) , m_pFurnace(container) { - m_uiTheme = UI_JAVA; +} + +void FurnaceScreen::init() +{ + if (m_uiTheme == UI_CONSOLE) + { + m_imageWidth = 432; + m_imageHeight = 436; + } + ContainerScreen::init(); + if (m_uiTheme == UI_CONSOLE) + m_topPos = Mth::Max(24, m_topPos - 48); } void FurnaceScreen::tick() @@ -17,43 +28,103 @@ void FurnaceScreen::tick() void FurnaceScreen::_renderLabels() { - m_pFont->draw(m_pFurnace->getName(), 66, 6, 0x404040); - m_pFont->draw(m_pInventory->getName(), 8, m_imageHeight - 96 + 2, 0x404040); + if (m_uiTheme == UI_CONSOLE) + { + m_pFont->drawScalable(m_pFurnace->getName(), 26, 21, 0x404040); + m_pFont->drawScalable(m_pInventory->getName(), 26, m_imageHeight - 238 + 2, 0x404040); + + m_pFont->drawScalable("Ingredient", 44, 68, 0x404040); + m_pFont->drawScalable("Fuel", 106, 162, 0x404040); + } + else + { + m_pFont->draw(m_pFurnace->getName(), 66, 6, 0x404040); + m_pFont->draw(m_pInventory->getName(), 8, m_imageHeight - 96 + 2, 0x404040); + } } void FurnaceScreen::_renderBg(float a) { - currentShaderColor = Color::WHITE; + if (m_uiTheme == UI_CONSOLE) + { + currentShaderColor = Color::WHITE; + blitNineSlice(*m_pMinecraft->m_pTextures, ScreenRenderer::PANEL_SLICES, m_leftPos, m_topPos, m_imageWidth, m_imageHeight, 32); + blitSprite(*m_pMinecraft->m_pTextures, "gui/console/Graphics/Arrow_Off.png", m_leftPos + 224, m_topPos + 100, 72, 48); - m_pMinecraft->m_pTextures->loadAndBindTexture("gui/furnace.png"); + blitSprite(*m_pMinecraft->m_pTextures, "gui/console/Graphics/Flame_Off.png", m_leftPos + 147, m_topPos + 96, 48, 48); - blit(m_leftPos, m_topPos, 0, 0, m_imageWidth, m_imageHeight, 0, 0); + int p; + if (m_pFurnace->isLit()) + { + p = m_pFurnace->getLitProgress(12); + blitSprite(*m_pMinecraft->m_pTextures, "gui/console/Graphics/Flame_On.png", m_leftPos + 195, m_topPos + 141, -48, -(p + 2) * 3, nullptr, 0.0f, 3.0f, 48, (p + 2) * 3); + } - int p; - if (m_pFurnace->isLit()) - { - p = m_pFurnace->getLitProgress(12); - blit(m_leftPos + 56, m_topPos + 36 + 12 - p, 176, 12 - p, 14, p + 2, 0, 0); + //p = m_pFurnace->getBurnProgress(24); + //blit(m_leftPos + 79, m_topPos + 34, 176, 14, p + 1, 16, 0, 0); } + else + { + currentShaderColor = Color::WHITE; + + m_pMinecraft->m_pTextures->loadAndBindTexture("gui/furnace.png"); + + blit(m_leftPos, m_topPos, 0, 0, m_imageWidth, m_imageHeight, 0, 0); - p = m_pFurnace->getBurnProgress(24); - blit(m_leftPos + 79, m_topPos + 34, 176, 14, p + 1, 16, 0, 0); + int p; + if (m_pFurnace->isLit()) + { + p = m_pFurnace->getLitProgress(12); + blit(m_leftPos + 56, m_topPos + 36 + 12 - p, 176, 12 - p, 14, p + 2, 0, 0); + } + + p = m_pFurnace->getBurnProgress(24); + blit(m_leftPos + 79, m_topPos + 34, 176, 14, p + 1, 16, 0, 0); + } } SlotDisplay FurnaceScreen::_createSlotDisplay(const Slot& slot) { - constexpr int slotSize = 18; - switch (slot.m_group) + if (m_uiTheme == UI_CONSOLE) + { + constexpr int slotSize = 42; + switch (slot.m_group) + { + case Slot::INPUT: + return SlotDisplay(154, 57 + (slot.m_stackId % 2) * ((slotSize + 5) * 2), slotSize, true); + case Slot::OUTPUT: + return SlotDisplay(312, 97, 64, true); + case Slot::INVENTORY: + return SlotDisplay(28 + (slot.m_stackId % 9) * slotSize, 230 + ((slot.m_stackId / 9) - 1) * slotSize, slotSize, true); + case Slot::HOTBAR: + return SlotDisplay(28 + (slot.m_stackId % 9) * slotSize, 369, slotSize, true); + default: + return SlotDisplay(); + } + } + else { - case Slot::INPUT: - return SlotDisplay(56, 17 + (slot.m_stackId % 2) * (slotSize * 2)); - case Slot::OUTPUT: - return SlotDisplay(116, 35); - case Slot::INVENTORY: - return SlotDisplay(8 + (slot.m_stackId % 9) * slotSize, 84 + ((slot.m_stackId / 9) - 1) * slotSize, slotSize); - case Slot::HOTBAR: - return SlotDisplay(8 + (slot.m_stackId % 9) * slotSize, 142, slotSize); - default: - return SlotDisplay(); + constexpr int slotSize = 18; + switch (slot.m_group) + { + case Slot::INPUT: + return SlotDisplay(56, 17 + (slot.m_stackId % 2) * (slotSize * 2), slotSize); + case Slot::OUTPUT: + return SlotDisplay(116, 35); + case Slot::INVENTORY: + return SlotDisplay(8 + (slot.m_stackId % 9) * slotSize, 84 + ((slot.m_stackId / 9) - 1) * slotSize, slotSize); + case Slot::HOTBAR: + return SlotDisplay(8 + (slot.m_stackId % 9) * slotSize, 142, slotSize); + default: + return SlotDisplay(); + } } -} \ No newline at end of file +} + +void FurnaceScreen::renderBackground() +{ + if (m_uiTheme == UI_CONSOLE) + return; + + ContainerScreen::renderBackground(); +} diff --git a/source/client/gui/screens/inventory/FurnaceScreen.hpp b/source/client/gui/screens/inventory/FurnaceScreen.hpp index 2ac9c3b25..337ea6a9c 100644 --- a/source/client/gui/screens/inventory/FurnaceScreen.hpp +++ b/source/client/gui/screens/inventory/FurnaceScreen.hpp @@ -7,6 +7,7 @@ class FurnaceScreen : public ContainerScreen { public: FurnaceScreen(Inventory* inventory, FurnaceTileEntity* container); + void init() override; public: void tick() override; @@ -16,6 +17,9 @@ class FurnaceScreen : public ContainerScreen void _renderBg(float a) override; SlotDisplay _createSlotDisplay(const Slot&) override; +public: + void renderBackground() override; + private: Inventory* m_pInventory; FurnaceTileEntity* m_pFurnace; diff --git a/source/client/renderer/ScreenRenderer.cpp b/source/client/renderer/ScreenRenderer.cpp index d6e406953..f71b40e6a 100644 --- a/source/client/renderer/ScreenRenderer.cpp +++ b/source/client/renderer/ScreenRenderer.cpp @@ -121,7 +121,11 @@ void ScreenRenderer::blitSprite(Textures& textures, const std::string& texture, void ScreenRenderer::blitSprite(Textures& textures, const TextureAtlasSprite* sprite, int x, int y, int width, int height, mce::MaterialPtr* materialPtr, float u, float v, int uvWidth, int uvHeight) { - if (!sprite || !sprite->m_pAtlas) return; + if (!sprite || !sprite->m_pAtlas) + { + assert(!"Invalid spite ptr provided to blitSprite!"); + return; + } TextureAtlas& atlas = *sprite->m_pAtlas; diff --git a/source/client/renderer/Textures.cpp b/source/client/renderer/Textures.cpp index 98fb53535..2a9d16fb8 100644 --- a/source/client/renderer/Textures.cpp +++ b/source/client/renderer/Textures.cpp @@ -166,6 +166,8 @@ Textures::Textures() : addSprite("gui/console/Graphics/Armour_Slot_Feet.png", m_guiAtlas); addSprite("gui/console/Graphics/Arrow_Off.png", m_guiAtlas); addSprite("gui/console/Graphics/Arrow_Small_Off.png", m_guiAtlas); + addSprite("gui/console/Graphics/Flame_Off.png", m_guiAtlas); + addSprite("gui/console/Graphics/Flame_On.png", m_guiAtlas); addSprite("gui/console/Graphics/MainMenuButton_Norm.png", m_filteredGuiAtlas); addSprite("gui/console/Graphics/MainMenuButton_Over.png", m_filteredGuiAtlas); addSprite("gui/console/Graphics/ListButton_Norm.png", m_filteredGuiAtlas); From 26db0457b78306e74e66461c5ba57c5c8d1fa681 Mon Sep 17 00:00:00 2001 From: Brent Da Mage Date: Sun, 17 May 2026 04:14:27 -0500 Subject: [PATCH 58/63] Minor tweaks to furnace labels --- source/client/gui/screens/inventory/FurnaceScreen.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/client/gui/screens/inventory/FurnaceScreen.cpp b/source/client/gui/screens/inventory/FurnaceScreen.cpp index 82981752d..1679ca5af 100644 --- a/source/client/gui/screens/inventory/FurnaceScreen.cpp +++ b/source/client/gui/screens/inventory/FurnaceScreen.cpp @@ -33,8 +33,8 @@ void FurnaceScreen::_renderLabels() m_pFont->drawScalable(m_pFurnace->getName(), 26, 21, 0x404040); m_pFont->drawScalable(m_pInventory->getName(), 26, m_imageHeight - 238 + 2, 0x404040); - m_pFont->drawScalable("Ingredient", 44, 68, 0x404040); - m_pFont->drawScalable("Fuel", 106, 162, 0x404040); + m_pFont->drawScalable("Ingredient", 40, 68, 0x404040); + m_pFont->drawScalable("Fuel", 102, 162, 0x404040); } else { From 3e6d9905c7d13b01e607e647c37ed2a4dca66a16 Mon Sep 17 00:00:00 2001 From: Brent Da Mage Date: Sun, 17 May 2026 04:20:49 -0500 Subject: [PATCH 59/63] Added arrow animation to Console FurnaceScreen --- source/client/gui/screens/inventory/FurnaceScreen.cpp | 3 ++- source/client/renderer/Textures.cpp | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/source/client/gui/screens/inventory/FurnaceScreen.cpp b/source/client/gui/screens/inventory/FurnaceScreen.cpp index 1679ca5af..b28a8a18d 100644 --- a/source/client/gui/screens/inventory/FurnaceScreen.cpp +++ b/source/client/gui/screens/inventory/FurnaceScreen.cpp @@ -60,7 +60,8 @@ void FurnaceScreen::_renderBg(float a) blitSprite(*m_pMinecraft->m_pTextures, "gui/console/Graphics/Flame_On.png", m_leftPos + 195, m_topPos + 141, -48, -(p + 2) * 3, nullptr, 0.0f, 3.0f, 48, (p + 2) * 3); } - //p = m_pFurnace->getBurnProgress(24); + p = m_pFurnace->getBurnProgress(24); + blitSprite(*m_pMinecraft->m_pTextures, "gui/console/Graphics/Arrow_On.png", m_leftPos + 224, m_topPos + 100, (p + 1) * 3, 48, nullptr, 0.0f, 0.0f, (p + 1) * 3, 48); //blit(m_leftPos + 79, m_topPos + 34, 176, 14, p + 1, 16, 0, 0); } else diff --git a/source/client/renderer/Textures.cpp b/source/client/renderer/Textures.cpp index 2a9d16fb8..7d9446f7b 100644 --- a/source/client/renderer/Textures.cpp +++ b/source/client/renderer/Textures.cpp @@ -165,6 +165,7 @@ Textures::Textures() : addSprite("gui/console/Graphics/Armour_Slot_Legs.png", m_guiAtlas); addSprite("gui/console/Graphics/Armour_Slot_Feet.png", m_guiAtlas); addSprite("gui/console/Graphics/Arrow_Off.png", m_guiAtlas); + addSprite("gui/console/Graphics/Arrow_On.png", m_guiAtlas); addSprite("gui/console/Graphics/Arrow_Small_Off.png", m_guiAtlas); addSprite("gui/console/Graphics/Flame_Off.png", m_guiAtlas); addSprite("gui/console/Graphics/Flame_On.png", m_guiAtlas); From 29edcbf733661397c32c4e24eadc9755cb0652d2 Mon Sep 17 00:00:00 2001 From: Brent Date: Sun, 17 May 2026 21:46:12 -0500 Subject: [PATCH 60/63] Updated Xcode Project --- .../xcode/NBCraft.xcodeproj/project.pbxproj | 148 ++++++++++++++++-- .../gui/screens/inventory/ContainerScreen.cpp | 8 +- 2 files changed, 144 insertions(+), 12 deletions(-) diff --git a/projects/xcode/NBCraft.xcodeproj/project.pbxproj b/projects/xcode/NBCraft.xcodeproj/project.pbxproj index 062801938..bf8e79643 100644 --- a/projects/xcode/NBCraft.xcodeproj/project.pbxproj +++ b/projects/xcode/NBCraft.xcodeproj/project.pbxproj @@ -938,6 +938,36 @@ 84E1C9F02E7FDC89007D2F5D /* VegetationFeature.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84E1C9ED2E7FDC89007D2F5D /* VegetationFeature.cpp */; }; 84E26C952F440CD600E38DF2 /* AddEntityPacket.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84E26C932F440CD600E38DF2 /* AddEntityPacket.cpp */; }; 84E26C962F440CD600E38DF2 /* AddEntityPacket.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84E26C942F440CD600E38DF2 /* AddEntityPacket.hpp */; }; + 84E2A3C22FBAAC3A0026780B /* FurnaceScreen.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84E2A3C02FBAAC3A0026780B /* FurnaceScreen.cpp */; }; + 84E2A3C32FBAAC3A0026780B /* FurnaceScreen.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84E2A3C12FBAAC3A0026780B /* FurnaceScreen.hpp */; }; + 84E2A3CC2FBAAC700026780B /* ContainerContentChangeListener.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84E2A3C42FBAAC700026780B /* ContainerContentChangeListener.cpp */; }; + 84E2A3CD2FBAAC700026780B /* ContainerContentChangeListener.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84E2A3C52FBAAC700026780B /* ContainerContentChangeListener.hpp */; }; + 84E2A3CE2FBAAC700026780B /* ContainerSizeChangeListener.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84E2A3C62FBAAC700026780B /* ContainerSizeChangeListener.cpp */; }; + 84E2A3CF2FBAAC700026780B /* ContainerSizeChangeListener.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84E2A3C72FBAAC700026780B /* ContainerSizeChangeListener.hpp */; }; + 84E2A3D02FBAAC700026780B /* FurnaceMenu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84E2A3C82FBAAC700026780B /* FurnaceMenu.cpp */; }; + 84E2A3D12FBAAC700026780B /* FurnaceMenu.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84E2A3C92FBAAC700026780B /* FurnaceMenu.hpp */; }; + 84E2A3D22FBAAC700026780B /* FurnaceResultSlot.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84E2A3CA2FBAAC700026780B /* FurnaceResultSlot.cpp */; }; + 84E2A3D32FBAAC700026780B /* FurnaceResultSlot.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84E2A3CB2FBAAC700026780B /* FurnaceResultSlot.hpp */; }; + 84E2A3D52FBAAC860026780B /* NoteParticle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84E2A3D42FBAAC860026780B /* NoteParticle.cpp */; }; + 84E2A3E92FBAAD6A0026780B /* ChestTile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84E2A3D62FBAAD6A0026780B /* ChestTile.cpp */; }; + 84E2A3EA2FBAAD6A0026780B /* ChestTile.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84E2A3D72FBAAD6A0026780B /* ChestTile.hpp */; }; + 84E2A3EB2FBAAD6A0026780B /* ChestTileEntity.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84E2A3D92FBAAD6A0026780B /* ChestTileEntity.cpp */; }; + 84E2A3EC2FBAAD6A0026780B /* ChestTileEntity.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84E2A3DA2FBAAD6A0026780B /* ChestTileEntity.hpp */; }; + 84E2A3ED2FBAAD6A0026780B /* FurnaceTileEntity.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84E2A3DB2FBAAD6A0026780B /* FurnaceTileEntity.cpp */; }; + 84E2A3EE2FBAAD6A0026780B /* FurnaceTileEntity.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84E2A3DC2FBAAD6A0026780B /* FurnaceTileEntity.hpp */; }; + 84E2A3EF2FBAAD6A0026780B /* MusicTileEntity.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84E2A3DD2FBAAD6A0026780B /* MusicTileEntity.cpp */; }; + 84E2A3F02FBAAD6A0026780B /* MusicTileEntity.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84E2A3DE2FBAAD6A0026780B /* MusicTileEntity.hpp */; }; + 84E2A3F12FBAAD6A0026780B /* TileEntity.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84E2A3DF2FBAAD6A0026780B /* TileEntity.cpp */; }; + 84E2A3F22FBAAD6A0026780B /* TileEntity.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84E2A3E02FBAAD6A0026780B /* TileEntity.hpp */; }; + 84E2A3F32FBAAD6A0026780B /* TileEntityType.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84E2A3E12FBAAD6A0026780B /* TileEntityType.cpp */; }; + 84E2A3F42FBAAD6A0026780B /* TileEntityType.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84E2A3E22FBAAD6A0026780B /* TileEntityType.hpp */; }; + 84E2A3F52FBAAD6A0026780B /* EntityTile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84E2A3E32FBAAD6A0026780B /* EntityTile.cpp */; }; + 84E2A3F62FBAAD6A0026780B /* EntityTile.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84E2A3E42FBAAD6A0026780B /* EntityTile.hpp */; }; + 84E2A3F72FBAAD6A0026780B /* FurnaceTile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84E2A3E52FBAAD6A0026780B /* FurnaceTile.cpp */; }; + 84E2A3F82FBAAD6A0026780B /* FurnaceTile.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84E2A3E62FBAAD6A0026780B /* FurnaceTile.hpp */; }; + 84E2A3F92FBAAD6A0026780B /* MusicTile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84E2A3E72FBAAD6A0026780B /* MusicTile.cpp */; }; + 84E2A3FA2FBAAD6A0026780B /* MusicTile.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84E2A3E82FBAAD6A0026780B /* MusicTile.hpp */; }; + 84E2A3FC2FBAAD7D0026780B /* Facing.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84E2A3FB2FBAAD7D0026780B /* Facing.cpp */; }; 84E4BFB62AE9869A0023E16A /* Logger.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 840DD6282AC810620006A435 /* Logger.cpp */; }; 84E4BFB82AE9869A0023E16A /* Mth.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 840DD62D2AC810620006A435 /* Mth.cpp */; }; 84E4BFB92AE9869A0023E16A /* Random.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 840DD62F2AC810620006A435 /* Random.cpp */; }; @@ -2715,6 +2745,36 @@ 84E1C9ED2E7FDC89007D2F5D /* VegetationFeature.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = VegetationFeature.cpp; sourceTree = ""; }; 84E26C932F440CD600E38DF2 /* AddEntityPacket.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AddEntityPacket.cpp; sourceTree = ""; }; 84E26C942F440CD600E38DF2 /* AddEntityPacket.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = AddEntityPacket.hpp; sourceTree = ""; }; + 84E2A3C02FBAAC3A0026780B /* FurnaceScreen.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FurnaceScreen.cpp; sourceTree = ""; }; + 84E2A3C12FBAAC3A0026780B /* FurnaceScreen.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = FurnaceScreen.hpp; sourceTree = ""; }; + 84E2A3C42FBAAC700026780B /* ContainerContentChangeListener.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ContainerContentChangeListener.cpp; sourceTree = ""; }; + 84E2A3C52FBAAC700026780B /* ContainerContentChangeListener.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ContainerContentChangeListener.hpp; sourceTree = ""; }; + 84E2A3C62FBAAC700026780B /* ContainerSizeChangeListener.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ContainerSizeChangeListener.cpp; sourceTree = ""; }; + 84E2A3C72FBAAC700026780B /* ContainerSizeChangeListener.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ContainerSizeChangeListener.hpp; sourceTree = ""; }; + 84E2A3C82FBAAC700026780B /* FurnaceMenu.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FurnaceMenu.cpp; sourceTree = ""; }; + 84E2A3C92FBAAC700026780B /* FurnaceMenu.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = FurnaceMenu.hpp; sourceTree = ""; }; + 84E2A3CA2FBAAC700026780B /* FurnaceResultSlot.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FurnaceResultSlot.cpp; sourceTree = ""; }; + 84E2A3CB2FBAAC700026780B /* FurnaceResultSlot.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = FurnaceResultSlot.hpp; sourceTree = ""; }; + 84E2A3D42FBAAC860026780B /* NoteParticle.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = NoteParticle.cpp; sourceTree = ""; }; + 84E2A3D62FBAAD6A0026780B /* ChestTile.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ChestTile.cpp; sourceTree = ""; }; + 84E2A3D72FBAAD6A0026780B /* ChestTile.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ChestTile.hpp; sourceTree = ""; }; + 84E2A3D92FBAAD6A0026780B /* ChestTileEntity.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ChestTileEntity.cpp; sourceTree = ""; }; + 84E2A3DA2FBAAD6A0026780B /* ChestTileEntity.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ChestTileEntity.hpp; sourceTree = ""; }; + 84E2A3DB2FBAAD6A0026780B /* FurnaceTileEntity.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FurnaceTileEntity.cpp; sourceTree = ""; }; + 84E2A3DC2FBAAD6A0026780B /* FurnaceTileEntity.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = FurnaceTileEntity.hpp; sourceTree = ""; }; + 84E2A3DD2FBAAD6A0026780B /* MusicTileEntity.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MusicTileEntity.cpp; sourceTree = ""; }; + 84E2A3DE2FBAAD6A0026780B /* MusicTileEntity.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = MusicTileEntity.hpp; sourceTree = ""; }; + 84E2A3DF2FBAAD6A0026780B /* TileEntity.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TileEntity.cpp; sourceTree = ""; }; + 84E2A3E02FBAAD6A0026780B /* TileEntity.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = TileEntity.hpp; sourceTree = ""; }; + 84E2A3E12FBAAD6A0026780B /* TileEntityType.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TileEntityType.cpp; sourceTree = ""; }; + 84E2A3E22FBAAD6A0026780B /* TileEntityType.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = TileEntityType.hpp; sourceTree = ""; }; + 84E2A3E32FBAAD6A0026780B /* EntityTile.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = EntityTile.cpp; sourceTree = ""; }; + 84E2A3E42FBAAD6A0026780B /* EntityTile.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = EntityTile.hpp; sourceTree = ""; }; + 84E2A3E52FBAAD6A0026780B /* FurnaceTile.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FurnaceTile.cpp; sourceTree = ""; }; + 84E2A3E62FBAAD6A0026780B /* FurnaceTile.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = FurnaceTile.hpp; sourceTree = ""; }; + 84E2A3E72FBAAD6A0026780B /* MusicTile.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MusicTile.cpp; sourceTree = ""; }; + 84E2A3E82FBAAD6A0026780B /* MusicTile.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = MusicTile.hpp; sourceTree = ""; }; + 84E2A3FB2FBAAD7D0026780B /* Facing.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Facing.cpp; sourceTree = ""; }; 84E4BFAE2AE9854B0023E16A /* libCommon.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libCommon.a; sourceTree = BUILT_PRODUCTS_DIR; }; 84E635C32FAF3D8000D2C708 /* VertexBufferStateBase.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = VertexBufferStateBase.cpp; sourceTree = ""; }; 84E635C42FAF3D8000D2C708 /* VertexBufferStateBase.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = VertexBufferStateBase.hpp; sourceTree = ""; }; @@ -3798,12 +3858,8 @@ 840DD6562AC810620006A435 /* world */ = { isa = PBXGroup; children = ( - 84E7BF2A2F286A20002D3936 /* CompoundContainer.cpp */, - 84E7BF2B2F286A20002D3936 /* CompoundContainer.hpp */, - 84E7BF2C2F286A20002D3936 /* Container.hpp */, - 84E7BF2D2F286A20002D3936 /* ContainerListener.cpp */, - 84E7BF2E2F286A20002D3936 /* ContainerListener.hpp */, 840DD6572AC810620006A435 /* entity */, + 84E2A3FB2FBAAD7D0026780B /* Facing.cpp */, 8477B3A82C4DC3B3004E1AC5 /* Facing.hpp */, 840DD6682AC810620006A435 /* gamemode */, 84E7BF092F286A08002D3936 /* inventory */, @@ -4095,14 +4151,15 @@ children = ( 840DD6CF2AC810620006A435 /* BubbleParticle.cpp */, 840DD6D02AC810620006A435 /* ExplodeParticle.cpp */, - 8470AF3B2BE9B6FA00BCA54E /* FireworkParticle.hpp */, 8470AF3A2BE9B6FA00BCA54E /* FireworkParticle.cpp */, + 8470AF3B2BE9B6FA00BCA54E /* FireworkParticle.hpp */, 840DD6D12AC810620006A435 /* FlameParticle.cpp */, 840DD6D22AC810620006A435 /* LavaParticle.cpp */, - 840DD6D42AC810620006A435 /* Particle.hpp */, + 84E2A3D42FBAAC860026780B /* NoteParticle.cpp */, 840DD6D32AC810620006A435 /* Particle.cpp */, - 840DD6D62AC810620006A435 /* ParticleEngine.hpp */, + 840DD6D42AC810620006A435 /* Particle.hpp */, 840DD6D52AC810620006A435 /* ParticleEngine.cpp */, + 840DD6D62AC810620006A435 /* ParticleEngine.hpp */, 840DD6D72AC810620006A435 /* RedDustParticle.cpp */, 840DD6D82AC810620006A435 /* SmokeParticle.cpp */, 840DD6D92AC810620006A435 /* TerrainParticle.cpp */, @@ -4128,32 +4185,39 @@ 840DD6E12AC810620006A435 /* tile */ = { isa = PBXGroup; children = ( - 84B9D8A22F3BE0B200FD67C4 /* CropsTile.hpp */, - 84B9D8A32F3BE0B200FD67C4 /* CropsTile.cpp */, 840DD6E22AC810620006A435 /* BookshelfTile.cpp */, 840DD6E32AC810620006A435 /* BookshelfTile.hpp */, 840DD6E42AC810620006A435 /* Bush.cpp */, 840DD6E52AC810620006A435 /* Bush.hpp */, 84E1C9BF2E7FDC26007D2F5D /* CactusTile.cpp */, 84E1C9C02E7FDC26007D2F5D /* CactusTile.hpp */, + 84E2A3D62FBAAD6A0026780B /* ChestTile.cpp */, + 84E2A3D72FBAAD6A0026780B /* ChestTile.hpp */, 840DD6E62AC810620006A435 /* ClayTile.cpp */, 840DD6E72AC810620006A435 /* ClayTile.hpp */, 840DD6E82AC810620006A435 /* ClothTile.cpp */, 840DD6E92AC810620006A435 /* ClothTile.hpp */, 84DED1B52F30970A004001C5 /* CraftingTableTile.cpp */, 84DED1B62F30970A004001C5 /* CraftingTableTile.hpp */, + 84B9D8A32F3BE0B200FD67C4 /* CropsTile.cpp */, + 84B9D8A22F3BE0B200FD67C4 /* CropsTile.hpp */, 84E1C9C12E7FDC26007D2F5D /* DeadBush.cpp */, 84E1C9C22E7FDC26007D2F5D /* DeadBush.hpp */, 840DD6EA2AC810620006A435 /* DirtTile.cpp */, 840DD6EB2AC810620006A435 /* DirtTile.hpp */, 840DD6EC2AC810620006A435 /* DoorTile.cpp */, 840DD6ED2AC810620006A435 /* DoorTile.hpp */, + 84E2A3D82FBAAD6A0026780B /* entity */, + 84E2A3E32FBAAD6A0026780B /* EntityTile.cpp */, + 84E2A3E42FBAAD6A0026780B /* EntityTile.hpp */, 840DD6EE2AC810620006A435 /* FarmTile.cpp */, 840DD6EF2AC810620006A435 /* FarmTile.hpp */, 84E1C9C32E7FDC26007D2F5D /* FenceTile.cpp */, 84E1C9C42E7FDC26007D2F5D /* FenceTile.hpp */, 840DD6F02AC810620006A435 /* FireTile.cpp */, 840DD6F12AC810620006A435 /* FireTile.hpp */, + 84E2A3E52FBAAD6A0026780B /* FurnaceTile.cpp */, + 84E2A3E62FBAAD6A0026780B /* FurnaceTile.hpp */, 840DD6F22AC810620006A435 /* GlassTile.cpp */, 840DD6F32AC810620006A435 /* GlassTile.hpp */, 84E1C9C52E7FDC26007D2F5D /* GlowstoneTile.cpp */, @@ -4180,6 +4244,8 @@ 840DD7072AC810620006A435 /* LiquidTileStatic.hpp */, 840DD7082AC810620006A435 /* MetalTile.cpp */, 840DD7092AC810620006A435 /* MetalTile.hpp */, + 84E2A3E72FBAAD6A0026780B /* MusicTile.cpp */, + 84E2A3E82FBAAD6A0026780B /* MusicTile.hpp */, 840DD70A2AC810620006A435 /* ObsidianTile.cpp */, 840DD70B2AC810620006A435 /* ObsidianTile.hpp */, 840DD70C2AC810620006A435 /* OreTile.cpp */, @@ -5010,6 +5076,8 @@ 84E022EC2F2A0211003C8FFE /* ContainerScreen.hpp */, 84DED1962F3095B8004001C5 /* CraftingScreen.cpp */, 84DED1972F3095B8004001C5 /* CraftingScreen.hpp */, + 84E2A3C02FBAAC3A0026780B /* FurnaceScreen.cpp */, + 84E2A3C12FBAAC3A0026780B /* FurnaceScreen.hpp */, 84E022ED2F2A0211003C8FFE /* InventoryScreen.cpp */, 84E022EE2F2A0211003C8FFE /* InventoryScreen.hpp */, ); @@ -5045,6 +5113,23 @@ path = base; sourceTree = ""; }; + 84E2A3D82FBAAD6A0026780B /* entity */ = { + isa = PBXGroup; + children = ( + 84E2A3D92FBAAD6A0026780B /* ChestTileEntity.cpp */, + 84E2A3DA2FBAAD6A0026780B /* ChestTileEntity.hpp */, + 84E2A3DB2FBAAD6A0026780B /* FurnaceTileEntity.cpp */, + 84E2A3DC2FBAAD6A0026780B /* FurnaceTileEntity.hpp */, + 84E2A3DD2FBAAD6A0026780B /* MusicTileEntity.cpp */, + 84E2A3DE2FBAAD6A0026780B /* MusicTileEntity.hpp */, + 84E2A3DF2FBAAD6A0026780B /* TileEntity.cpp */, + 84E2A3E02FBAAD6A0026780B /* TileEntity.hpp */, + 84E2A3E12FBAAD6A0026780B /* TileEntityType.cpp */, + 84E2A3E22FBAAD6A0026780B /* TileEntityType.hpp */, + ); + path = entity; + sourceTree = ""; + }; 84E7BF092F286A08002D3936 /* inventory */ = { isa = PBXGroup; children = ( @@ -5052,13 +5137,26 @@ 84E7BF0B2F286A08002D3936 /* ArmorSlot.hpp */, 84E022FE2F2A0245003C8FFE /* ChestMenu.cpp */, 84E022FF2F2A0245003C8FFE /* ChestMenu.hpp */, + 84E7BF2A2F286A20002D3936 /* CompoundContainer.cpp */, + 84E7BF2B2F286A20002D3936 /* CompoundContainer.hpp */, + 84E7BF2C2F286A20002D3936 /* Container.hpp */, + 84E2A3C42FBAAC700026780B /* ContainerContentChangeListener.cpp */, + 84E2A3C52FBAAC700026780B /* ContainerContentChangeListener.hpp */, + 84E7BF2D2F286A20002D3936 /* ContainerListener.cpp */, + 84E7BF2E2F286A20002D3936 /* ContainerListener.hpp */, 84E7BF0C2F286A08002D3936 /* ContainerMenu.cpp */, 84E7BF0D2F286A08002D3936 /* ContainerMenu.hpp */, + 84E2A3C62FBAAC700026780B /* ContainerSizeChangeListener.cpp */, + 84E2A3C72FBAAC700026780B /* ContainerSizeChangeListener.hpp */, 84DAF5732F3464B400D67310 /* ContainerType.hpp */, 84E7BF0E2F286A08002D3936 /* CraftingContainer.cpp */, 84E7BF0F2F286A08002D3936 /* CraftingContainer.hpp */, 84DED19A2F3095FD004001C5 /* CraftingMenu.cpp */, 84DED19B2F3095FD004001C5 /* CraftingMenu.hpp */, + 84E2A3C82FBAAC700026780B /* FurnaceMenu.cpp */, + 84E2A3C92FBAAC700026780B /* FurnaceMenu.hpp */, + 84E2A3CA2FBAAC700026780B /* FurnaceResultSlot.cpp */, + 84E2A3CB2FBAAC700026780B /* FurnaceResultSlot.hpp */, 84E7BF102F286A08002D3936 /* InventoryMenu.cpp */, 84E7BF112F286A08002D3936 /* InventoryMenu.hpp */, 84E7BF122F286A08002D3936 /* ResultContainer.cpp */, @@ -5883,6 +5981,7 @@ 84A2FF182DB61D440090CE3E /* SoundPathRepository.hpp in Headers */, 84EB09FB2EE2CCA8008FB007 /* TextureData.hpp in Headers */, 84B1E0302E04FD4500ED000A /* ArrowRenderer.hpp in Headers */, + 84E2A3C32FBAAC3A0026780B /* FurnaceScreen.hpp in Headers */, 84AA8C2B2B32F3F3003F5B82 /* LightUpdate.hpp in Headers */, 84AA8C2D2B32F3F3003F5B82 /* PatchManager.hpp in Headers */, 84AA8C2F2B32F3F3003F5B82 /* RenderChunk.hpp in Headers */, @@ -5941,6 +6040,8 @@ 84E7BF292F286A08002D3936 /* Slot.hpp in Headers */, 84E1C9DC2E7FDC26007D2F5D /* TallGrass.hpp in Headers */, 84DED1AC2F309611004001C5 /* Recipe.hpp in Headers */, + 84E2A3FA2FBAAD6A0026780B /* MusicTile.hpp in Headers */, + 84E2A3F82FBAAD6A0026780B /* FurnaceTile.hpp in Headers */, 84BF638B2AF186C8008A9995 /* Dimension.hpp in Headers */, 84BF638C2AF186C8008A9995 /* Explosion.hpp in Headers */, 84BF638D2AF186C8008A9995 /* Level.hpp in Headers */, @@ -5958,6 +6059,7 @@ 84DED1AB2F309611004001C5 /* FurnaceRecipes.hpp in Headers */, 84BF63962AF186C8008A9995 /* Feature.hpp in Headers */, 8477B3AD2C4DC3F6004E1AC5 /* TilePos.hpp in Headers */, + 84E2A3D32FBAAC700026780B /* FurnaceResultSlot.hpp in Headers */, 84BF63972AF186C8008A9995 /* LargeCaveFeature.hpp in Headers */, 84E7BF1B2F286A08002D3936 /* ArmorSlot.hpp in Headers */, 84E7BF252F286A08002D3936 /* ResultSlot.hpp in Headers */, @@ -5982,8 +6084,11 @@ 84BF63A42AF186C8008A9995 /* LevelStorage.hpp in Headers */, 84E1C9DA2E7FDC26007D2F5D /* SoulSandTile.hpp in Headers */, 8445E7A82D769329008DC834 /* Sheep.hpp in Headers */, + 84E2A3F02FBAAD6A0026780B /* MusicTileEntity.hpp in Headers */, 84DED19D2F3095FD004001C5 /* CraftingMenu.hpp in Headers */, + 84E2A3F22FBAAD6A0026780B /* TileEntity.hpp in Headers */, 84BF63A52AF186C8008A9995 /* LevelStorageSource.hpp in Headers */, + 84E2A3EA2FBAAD6A0026780B /* ChestTile.hpp in Headers */, 84BF63A62AF186C8008A9995 /* MemoryChunkStorage.hpp in Headers */, 84BF63A72AF186C8008A9995 /* MemoryLevelStorage.hpp in Headers */, 84E7BF1F2F286A08002D3936 /* CraftingContainer.hpp in Headers */, @@ -6009,9 +6114,11 @@ 84BF63B42AF186C9008A9995 /* DirtTile.hpp in Headers */, 84BF63B52AF186C9008A9995 /* DoorTile.hpp in Headers */, 84BF63B62AF186C9008A9995 /* FarmTile.hpp in Headers */, + 84E2A3CF2FBAAC700026780B /* ContainerSizeChangeListener.hpp in Headers */, 84DED1B82F30970A004001C5 /* CraftingTableTile.hpp in Headers */, 8445E7A62D769329008DC834 /* MobCategory.hpp in Headers */, 84BF63B72AF186C9008A9995 /* FireTile.hpp in Headers */, + 84E2A3D12FBAAC700026780B /* FurnaceMenu.hpp in Headers */, 84BF63B82AF186C9008A9995 /* GlassTile.hpp in Headers */, 84BF63B92AF186C9008A9995 /* GrassTile.hpp in Headers */, 84BF63BA2AF186C9008A9995 /* GravelTile.hpp in Headers */, @@ -6039,10 +6146,13 @@ 84E1C9D82E7FDC26007D2F5D /* PumpkinTile.hpp in Headers */, 84BF63CA2AF186C9008A9995 /* Sapling.hpp in Headers */, 840823DC2F4FF4B80097F6A5 /* Tool.hpp in Headers */, + 84E2A3CD2FBAAC700026780B /* ContainerContentChangeListener.hpp in Headers */, + 84E2A3EE2FBAAD6A0026780B /* FurnaceTileEntity.hpp in Headers */, 84CCBC942E61880600E251AF /* EntityFactory.hpp in Headers */, 84BF63CB2AF186C9008A9995 /* SpongeTile.hpp in Headers */, 84BF63CC2AF186C9008A9995 /* StairTile.hpp in Headers */, 84E1C9D22E7FDC26007D2F5D /* DeadBush.hpp in Headers */, + 84E2A3F42FBAAD6A0026780B /* TileEntityType.hpp in Headers */, 84BF63CD2AF186C9008A9995 /* StoneSlabTile.hpp in Headers */, 84BF63CE2AF186C9008A9995 /* StoneTile.hpp in Headers */, 84E7BF1D2F286A08002D3936 /* ContainerMenu.hpp in Headers */, @@ -6064,6 +6174,7 @@ 848CCB462F3A38E800031D1F /* MobSpawner.hpp in Headers */, 84AA8B792B32F3B5003F5B82 /* Animal.hpp in Headers */, 84AA8B7B2B32F3B5003F5B82 /* Chicken.hpp in Headers */, + 84E2A3EC2FBAAD6A0026780B /* ChestTileEntity.hpp in Headers */, 84AA8B7D2B32F3B5003F5B82 /* Cow.hpp in Headers */, 84AA8B7F2B32F3B5003F5B82 /* Creeper.hpp in Headers */, 84AA8B8B2B32F3B5003F5B82 /* Monster.hpp in Headers */, @@ -6084,6 +6195,7 @@ 8470AF2B2BE9B60A00BCA54E /* EntityType.hpp in Headers */, 8445E7A42D769329008DC834 /* EntityTypeDescriptor.hpp in Headers */, 84B9D89F2F3BE0A900FD67C4 /* ArmorItem.hpp in Headers */, + 84E2A3F62FBAAD6A0026780B /* EntityTile.hpp in Headers */, 84B9D8A02F3BE0A900FD67C4 /* HoeItem.hpp in Headers */, 84B9D8A12F3BE0A900FD67C4 /* SeedItem.hpp in Headers */, 8470AF2D2BE9B60A00BCA54E /* MobFactory.hpp in Headers */, @@ -6875,6 +6987,7 @@ 84B8AEFD2AF1896F008DE93D /* InvalidLicenseScreen.cpp in Sources */, 84B8AEFE2AF1896F008DE93D /* JoinGameScreen.cpp in Sources */, 844336FB2F405DCA00EB3115 /* VerticalLayout.cpp in Sources */, + 84E2A3C22FBAAC3A0026780B /* FurnaceScreen.cpp in Sources */, 84B8AEFF2AF1896F008DE93D /* OptionsScreen.cpp in Sources */, 84B8AF002AF1896F008DE93D /* PauseScreen.cpp in Sources */, 84B8AF012AF1896F008DE93D /* ProgressScreen.cpp in Sources */, @@ -7058,6 +7171,7 @@ 84BF63102AF18631008A9995 /* Mob.cpp in Sources */, 84BF63112AF18631008A9995 /* Player.cpp in Sources */, 8445E7A52D769329008DC834 /* MobCategory.cpp in Sources */, + 84E2A3ED2FBAAD6A0026780B /* FurnaceTileEntity.cpp in Sources */, 84E7BF222F286A08002D3936 /* ResultContainer.cpp in Sources */, 84BF63122AF18631008A9995 /* PrimedTnt.cpp in Sources */, 84BF63132AF18631008A9995 /* TripodCamera.cpp in Sources */, @@ -7077,10 +7191,12 @@ 84E1C9F02E7FDC89007D2F5D /* VegetationFeature.cpp in Sources */, 84BF631D2AF18631008A9995 /* TilePlanterItem.cpp in Sources */, 84BF631E2AF18631008A9995 /* Dimension.cpp in Sources */, + 84E2A3D22FBAAC700026780B /* FurnaceResultSlot.cpp in Sources */, 84BF631F2AF18631008A9995 /* Explosion.cpp in Sources */, 84BF63202AF18631008A9995 /* Level.cpp in Sources */, 84C6142A2F323602008AC5BE /* BowItem.cpp in Sources */, 84C6142B2F323602008AC5BE /* WeaponItem.cpp in Sources */, + 84E2A3F32FBAAD6A0026780B /* TileEntityType.cpp in Sources */, 84BF63212AF18631008A9995 /* Biome.cpp in Sources */, 84BF63222AF18631008A9995 /* BiomeSource.cpp in Sources */, 84BF63232AF18631008A9995 /* ChunkCache.cpp in Sources */, @@ -7104,6 +7220,7 @@ 84B1E03F2E04FD7900ED000A /* Zombie.cpp in Sources */, 84BF632E2AF18631008A9995 /* LargeFeature.cpp in Sources */, 848CCB472F3A38E800031D1F /* MobSpawner.cpp in Sources */, + 84E2A3D02FBAAC700026780B /* FurnaceMenu.cpp in Sources */, 84BF632F2AF18631008A9995 /* OreFeature.cpp in Sources */, 84BF63302AF18631008A9995 /* PineFeature.cpp in Sources */, 84BF63312AF18631008A9995 /* ReedsFeature.cpp in Sources */, @@ -7113,6 +7230,7 @@ 84BF63352AF18631008A9995 /* ImprovedNoise.cpp in Sources */, 84BF63362AF18631008A9995 /* PerlinNoise.cpp in Sources */, 84BF63372AF18631008A9995 /* Synth.cpp in Sources */, + 84E2A3F92FBAAD6A0026780B /* MusicTile.cpp in Sources */, 84B9D89C2F3BE0A900FD67C4 /* ArmorItem.cpp in Sources */, 84B9D89D2F3BE0A900FD67C4 /* HoeItem.cpp in Sources */, 84B9D89E2F3BE0A900FD67C4 /* SeedItem.cpp in Sources */, @@ -7123,7 +7241,9 @@ 84BF633B2AF18631008A9995 /* ChunkStorage.cpp in Sources */, 84E1C9EE2E7FDC89007D2F5D /* CactusFeature.cpp in Sources */, 8445E7A02D769329008DC834 /* EntityCategories.cpp in Sources */, + 84E2A3CE2FBAAC700026780B /* ContainerSizeChangeListener.cpp in Sources */, 84DED1AA2F309611004001C5 /* FurnaceRecipes.cpp in Sources */, + 84E2A3CC2FBAAC700026780B /* ContainerContentChangeListener.cpp in Sources */, 84BF633C2AF18631008A9995 /* ExternalFileLevelStorage.cpp in Sources */, 84BF633D2AF18631008A9995 /* ExternalFileLevelStorageSource.cpp in Sources */, 84E1C9D72E7FDC26007D2F5D /* PumpkinTile.cpp in Sources */, @@ -7161,6 +7281,7 @@ 84BF63512AF18631008A9995 /* HitResult.cpp in Sources */, 84BF63522AF18631008A9995 /* Vec3.cpp in Sources */, 84BF63532AF18631008A9995 /* BookshelfTile.cpp in Sources */, + 84E2A3F52FBAAD6A0026780B /* EntityTile.cpp in Sources */, 84E7BF1C2F286A08002D3936 /* ContainerMenu.cpp in Sources */, 84BF63542AF18631008A9995 /* Bush.cpp in Sources */, 84DED1B12F309611004001C5 /* ShapelessRecipe.cpp in Sources */, @@ -7181,15 +7302,19 @@ 84BF63602AF18631008A9995 /* InvisibleTile.cpp in Sources */, 84BF63612AF18631008A9995 /* LadderTile.cpp in Sources */, 84BF63622AF18631008A9995 /* LeafTile.cpp in Sources */, + 84E2A3EB2FBAAD6A0026780B /* ChestTileEntity.cpp in Sources */, 84BF63632AF18631008A9995 /* LiquidTile.cpp in Sources */, 84BF63642AF18631008A9995 /* LiquidTileDynamic.cpp in Sources */, + 84E2A3F72FBAAD6A0026780B /* FurnaceTile.cpp in Sources */, 84BF63652AF18631008A9995 /* LiquidTileStatic.cpp in Sources */, 84BF63662AF18631008A9995 /* MetalTile.cpp in Sources */, 84BF63672AF18631008A9995 /* ObsidianTile.cpp in Sources */, 84CCBC932E61880600E251AF /* EntityFactory.cpp in Sources */, 84BF63682AF18631008A9995 /* OreTile.cpp in Sources */, + 84E2A3D52FBAAC860026780B /* NoteParticle.cpp in Sources */, 84B1E0392E04FD7900ED000A /* Arrow.cpp in Sources */, 84BF63692AF18631008A9995 /* RedStoneOreTile.cpp in Sources */, + 84E2A3F12FBAAD6A0026780B /* TileEntity.cpp in Sources */, 84BF636A2AF18631008A9995 /* ReedTile.cpp in Sources */, 84BF636B2AF18631008A9995 /* SandStoneTile.cpp in Sources */, 84BF636C2AF18631008A9995 /* SandTile.cpp in Sources */, @@ -7201,6 +7326,7 @@ 84BF63722AF18631008A9995 /* Tile.cpp in Sources */, 84BF63732AF18631008A9995 /* TntTile.cpp in Sources */, 84E7BF1E2F286A08002D3936 /* CraftingContainer.cpp in Sources */, + 84E2A3E92FBAAD6A0026780B /* ChestTile.cpp in Sources */, 84BF63742AF18631008A9995 /* TopSnowTile.cpp in Sources */, 8445E7A32D769329008DC834 /* EntityTypeDescriptor.cpp in Sources */, 84BF63752AF18631008A9995 /* TorchTile.cpp in Sources */, @@ -7225,6 +7351,7 @@ 840823DA2F4FF4B80097F6A5 /* CoalItem.cpp in Sources */, 84E7BF242F286A08002D3936 /* ResultSlot.cpp in Sources */, 84E7BF202F286A08002D3936 /* InventoryMenu.cpp in Sources */, + 84E2A3FC2FBAAD7D0026780B /* Facing.cpp in Sources */, 84AA8B8C2B32F3B5003F5B82 /* PathfinderMob.cpp in Sources */, 84AA8B8E2B32F3B5003F5B82 /* Pig.cpp in Sources */, 84AA8B962B32F3B5003F5B82 /* WaterAnimal.cpp in Sources */, @@ -7235,6 +7362,7 @@ 8470AF2C2BE9B60A00BCA54E /* MobFactory.cpp in Sources */, 84E1C9E92E7FDC72007D2F5D /* SlabItem.cpp in Sources */, 84E7BF362F286A6A002D3936 /* ItemStack.cpp in Sources */, + 84E2A3EF2FBAAD6A0026780B /* MusicTileEntity.cpp in Sources */, 8470AF3C2BE9B6FA00BCA54E /* FireworkParticle.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/source/client/gui/screens/inventory/ContainerScreen.cpp b/source/client/gui/screens/inventory/ContainerScreen.cpp index 603dfbb7c..0ce0919c9 100644 --- a/source/client/gui/screens/inventory/ContainerScreen.cpp +++ b/source/client/gui/screens/inventory/ContainerScreen.cpp @@ -212,16 +212,20 @@ void ContainerScreen::render(float partialTicks) if (!name.empty()) { int w = m_pFont->width(name); - int tx = m_menuPointer.x - m_leftPos + 10; - int ty = m_menuPointer.y - m_topPos - 32; + int tx = m_menuPointer.x - m_leftPos; + int ty = m_menuPointer.y - m_topPos; if (m_uiTheme == UI_CONSOLE) { + tx += 10; + ty -= 32; blitNineSlice(*m_pMinecraft->m_pTextures, ScreenRenderer::POINTER_TEXT_PANEL_SLICES, tx - 6, ty - 6, w * 2 + 12, 34, 8); MatrixStack::Ref tooltipMatrix = MatrixStack::World.push(); m_pFont->drawScalableShadow(name, tx, ty + 4, -1); } else { + tx += 12; + ty -= 12; fillGradient(tx - 3, ty - 3, tx + w + 3, ty + 8 + 3, 0xC0000000, 0xC0000000); m_pFont->drawShadow(name, tx, ty, -1); } From 7101f41a950b4573f0d24bf976ec26a52f89486d Mon Sep 17 00:00:00 2001 From: Brent Da Mage Date: Sun, 17 May 2026 22:20:26 -0500 Subject: [PATCH 61/63] Fixed PlayerEquipmentPacket handling --- source/client/network/ClientSideNetworkHandler.cpp | 7 ++++++- source/server/ServerSideNetworkHandler.cpp | 5 +++++ source/world/item/Inventory.cpp | 2 +- source/world/item/Inventory.hpp | 2 +- 4 files changed, 13 insertions(+), 3 deletions(-) diff --git a/source/client/network/ClientSideNetworkHandler.cpp b/source/client/network/ClientSideNetworkHandler.cpp index 1191d205a..fd44a7ca8 100644 --- a/source/client/network/ClientSideNetworkHandler.cpp +++ b/source/client/network/ClientSideNetworkHandler.cpp @@ -601,7 +601,12 @@ void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& rakGuid, PlayerE return; } - pPlayer->m_pInventory->pickItem(pPlayerEquipmentPkt->m_itemID, pPlayerEquipmentPkt->m_itemAuxValue, C_MAX_HOTBAR_ITEMS); +#ifdef FEATURE_SERVER_INVENTORIES + // will need to be reworked for proper server-sided inventory support, pick the proper slot, not just any item + pPlayer->m_pInventory->pickItem(pPlayerEquipmentPkt->m_itemID, pPlayerEquipmentPkt->m_itemAuxValue, C_MAX_HOTBAR_ITEMS);; +#else + pPlayer->m_pInventory->setSelectedItem(ItemStack(pPlayerEquipmentPkt->m_itemID, 1, pPlayerEquipmentPkt->m_itemAuxValue)); +#endif } void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& rakGuid, InteractPacket* pkt) diff --git a/source/server/ServerSideNetworkHandler.cpp b/source/server/ServerSideNetworkHandler.cpp index 4cdfe4cf0..b43cadf2d 100644 --- a/source/server/ServerSideNetworkHandler.cpp +++ b/source/server/ServerSideNetworkHandler.cpp @@ -396,7 +396,12 @@ void ServerSideNetworkHandler::handle(const RakNet::RakNetGUID& guid, PlayerEqui return; } +#ifdef FEATURE_SERVER_INVENTORIES + // will need to be reworked for proper server-sided inventory support, pick the proper slot, not just any item pPlayer->m_pInventory->pickItem(packet->m_itemID, packet->m_itemAuxValue, C_MAX_HOTBAR_ITEMS); +#else + pPlayer->m_pInventory->setSelectedItem(ItemStack(packet->m_itemID, 1, packet->m_itemAuxValue)); +#endif redistributePacket(packet, guid); } diff --git a/source/world/item/Inventory.cpp b/source/world/item/Inventory.cpp index 050be99bb..68309426d 100644 --- a/source/world/item/Inventory.cpp +++ b/source/world/item/Inventory.cpp @@ -407,7 +407,7 @@ void Inventory::setItem(StackID stackId, const ItemStack& item) } } -void Inventory::setSelectedItem(ItemStack item) +void Inventory::setSelectedItem(const ItemStack& item) { setItem(m_selectedStackId, item); } diff --git a/source/world/item/Inventory.hpp b/source/world/item/Inventory.hpp index 9899c1d46..29651111d 100644 --- a/source/world/item/Inventory.hpp +++ b/source/world/item/Inventory.hpp @@ -47,7 +47,7 @@ class Inventory : public Container int getSelectedItemId(); void setItem(StackID stackId, const ItemStack& item) override; - void setSelectedItem(ItemStack item); + void setSelectedItem(const ItemStack& item); ItemStack removeItem(StackID stackId, int count) override; bool removeResource(int id); From 863c89d74e621a0b4dc01aad1b1d8e795469536a Mon Sep 17 00:00:00 2001 From: Brent Da Mage Date: Sun, 17 May 2026 22:56:12 -0500 Subject: [PATCH 62/63] Added start destroy player action replication, allows noteblocks to work --- .../network/ClientSideNetworkHandler.cpp | 2 +- source/server/ServerSideNetworkHandler.cpp | 24 +++++++++++++++++++ source/world/gamemode/SurvivalMode.cpp | 16 +++++++++++++ 3 files changed, 41 insertions(+), 1 deletion(-) diff --git a/source/client/network/ClientSideNetworkHandler.cpp b/source/client/network/ClientSideNetworkHandler.cpp index fd44a7ca8..4e0b7f409 100644 --- a/source/client/network/ClientSideNetworkHandler.cpp +++ b/source/client/network/ClientSideNetworkHandler.cpp @@ -603,7 +603,7 @@ void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& rakGuid, PlayerE #ifdef FEATURE_SERVER_INVENTORIES // will need to be reworked for proper server-sided inventory support, pick the proper slot, not just any item - pPlayer->m_pInventory->pickItem(pPlayerEquipmentPkt->m_itemID, pPlayerEquipmentPkt->m_itemAuxValue, C_MAX_HOTBAR_ITEMS);; + pPlayer->m_pInventory->pickItem(pPlayerEquipmentPkt->m_itemID, pPlayerEquipmentPkt->m_itemAuxValue, C_MAX_HOTBAR_ITEMS); #else pPlayer->m_pInventory->setSelectedItem(ItemStack(pPlayerEquipmentPkt->m_itemID, 1, pPlayerEquipmentPkt->m_itemAuxValue)); #endif diff --git a/source/server/ServerSideNetworkHandler.cpp b/source/server/ServerSideNetworkHandler.cpp index b43cadf2d..a940f6ade 100644 --- a/source/server/ServerSideNetworkHandler.cpp +++ b/source/server/ServerSideNetworkHandler.cpp @@ -485,6 +485,23 @@ void ServerSideNetworkHandler::handle(const RakNet::RakNetGUID& guid, UseItemPac pPlayer->swing(); } +// added specifically to allow Noteblocks to work, but ideally should just be a part of ServerPlayerGameMode +bool _startDestroyBlock(Level& level, Player& player, const TilePos& pos, Facing::Name face) +{ + ItemStack& item = player.getSelectedItem(); + if (!item.isEmpty() && item.getItem() == Item::bow) + return true; + + TileID tile = level.getTile(pos); + + if (tile <= 0) + return false; + + Tile::tiles[tile]->attack(&level, pos, &player); + + return true; +} + void ServerSideNetworkHandler::handle(const RakNet::RakNetGUID& guid, PlayerActionPacket* packet) { puts_ignorable("PlayerActionPacket"); @@ -499,6 +516,13 @@ void ServerSideNetworkHandler::handle(const RakNet::RakNetGUID& guid, PlayerActi switch (packet->m_action) { + case PlayerActionPacket::START_DESTROY_BLOCK: + _startDestroyBlock(*m_pLevel, *pPlayer, packet->m_tilePos, packet->m_tileFace); + //m_pMinecraft->getPlayerGameMode(*pPlayer)->startDestroyBlock(pPlayer, packet->m_tilePos, packet->m_tileFace); + break; + case PlayerActionPacket::STOP_DESTROY_BLOCK: + //m_pMinecraft->getPlayerGameMode(*pPlayer)->stopDestroyBlock(pPlayer, packet->m_tilePos, packet->m_tileFace); + break; case PlayerActionPacket::STOP_USING_ITEM: pPlayer->releaseUsingItem(); break; diff --git a/source/world/gamemode/SurvivalMode.cpp b/source/world/gamemode/SurvivalMode.cpp index c43feb939..6e5b9a84b 100644 --- a/source/world/gamemode/SurvivalMode.cpp +++ b/source/world/gamemode/SurvivalMode.cpp @@ -42,6 +42,14 @@ bool SurvivalMode::startDestroyBlock(Player* player, const TilePos& pos, Facing: if (tile <= 0) return false; + // @PARITY: This is in MultiPlayerGameMode on Java, but we aren't equipped to move to that at the moment +#if NETWORK_PROTOCOL_VERSION >= 6 + if (m_pMinecraft->isOnlineClient()) + { + m_pMinecraft->m_pRakNetInstance->send(new PlayerActionPacket(player->m_EntityID, PlayerActionPacket::START_DESTROY_BLOCK, pos, face)); + } +#endif + if (m_destroyProgress == 0.0f) { Tile::tiles[tile]->attack(&_level, pos, player); @@ -128,6 +136,14 @@ bool SurvivalMode::continueDestroyBlock(Player* player, const TilePos& pos, Faci m_destroyCooldown = 5; m_destroyProgress = 0.0f; m_lastDestroyProgress = 0.0f; + + // @PARITY: This is in MultiPlayerGameMode on Java, but we aren't equipped to move to that at the moment +#if NETWORK_PROTOCOL_VERSION >= 6 + if (m_pMinecraft->isOnlineClient()) + { + m_pMinecraft->m_pRakNetInstance->send(new PlayerActionPacket(player->m_EntityID, PlayerActionPacket::STOP_DESTROY_BLOCK, pos, face)); + } +#endif return destroyBlock(player, m_destroyingPos, face); } From 51603273cf1b2e3cf4d047872029a5d8563c0448 Mon Sep 17 00:00:00 2001 From: Brent Da Mage Date: Sun, 17 May 2026 23:12:14 -0500 Subject: [PATCH 63/63] Fixed a bug which caused players to spawn underground in multiplayer --- source/server/ServerSideNetworkHandler.cpp | 41 ++++++++++++++++------ 1 file changed, 30 insertions(+), 11 deletions(-) diff --git a/source/server/ServerSideNetworkHandler.cpp b/source/server/ServerSideNetworkHandler.cpp index a940f6ade..c333c9164 100644 --- a/source/server/ServerSideNetworkHandler.cpp +++ b/source/server/ServerSideNetworkHandler.cpp @@ -154,17 +154,36 @@ void ServerSideNetworkHandler::handle(const RakNet::RakNetGUID& guid, LoginPacke m_pendingPlayers[guid] = pPlayer; - StartGamePacket sgp; - sgp.m_seed = m_pLevel->getSeed(); - sgp.m_levelVersion = m_pLevel->getLevelData()->getStorageVersion(); - sgp.m_gameType = pPlayer->getPlayerGameType(); - sgp.m_entityId = pPlayer->m_EntityID; - sgp.m_time = m_pLevel->getTime(); - sgp.m_pos = pPlayer->m_pos; - sgp.m_pos.y -= pPlayer->m_heightOffset; - - sgp.write(bs); - m_pRakNetPeer->Send(&bs, HIGH_PRIORITY, RELIABLE_ORDERED, 0, guid, false); + // Ensure Player is spawned above-ground + { + Vec3& pos = pPlayer->m_pos; + while (pos.y > 0) + { + pPlayer->setPos(pos); + if (pPlayer->canSpawn()) + break; + pos.y++; + } + + Vec3 pos2 = pos; + pos2.y -= pPlayer->m_heightOffset; + pPlayer->moveTo(pos2, pPlayer->m_rot); + } + + // Send StartGamePacket + { + StartGamePacket sgp; + sgp.m_seed = m_pLevel->getSeed(); + sgp.m_levelVersion = m_pLevel->getLevelData()->getStorageVersion(); + sgp.m_gameType = pPlayer->getPlayerGameType(); + sgp.m_entityId = pPlayer->m_EntityID; + sgp.m_time = m_pLevel->getTime(); + sgp.m_pos = pPlayer->m_pos; + sgp.m_pos.y -= pPlayer->m_heightOffset; + + sgp.write(bs); + m_pRakNetPeer->Send(&bs, HIGH_PRIORITY, RELIABLE_ORDERED, 0, guid, false); + } #if NETWORK_PROTOCOL_VERSION <= 2 // emulate a ReadyPacket being received

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 literal 54213 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 W0pC&I?SueJAtDe^1>pYz0sTMJrJ5N4 diff --git a/platforms/sdl/sdl2/android/gradle/wrapper/gradle-wrapper.properties b/platforms/sdl/sdl2/android/gradle/wrapper/gradle-wrapper.properties index 099f1c8c0..37f853b1c 100644 --- a/platforms/sdl/sdl2/android/gradle/wrapper/gradle-wrapper.properties +++ b/platforms/sdl/sdl2/android/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,7 @@ -#Thu Nov 11 18:20:34 PST 2021 distributionBase=GRADLE_USER_HOME -distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip distributionPath=wrapper/dists -zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip +networkTimeout=10000 +validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/platforms/sdl/sdl2/android/gradlew b/platforms/sdl/sdl2/android/gradlew index 9d82f7891..1aa94a426 100755 --- a/platforms/sdl/sdl2/android/gradlew +++ b/platforms/sdl/sdl2/android/gradlew @@ -1,74 +1,127 @@ -#!/usr/bin/env bash +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ############################################################################## -## -## Gradle start up script for UN*X -## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# ############################################################################## -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS="" +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done -APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" +MAX_FD=maximum -warn ( ) { +warn () { echo "$*" -} +} >&2 -die ( ) { +die () { echo echo "$*" echo exit 1 -} +} >&2 # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; esac -# Attempt to set APP_HOME -# Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi -done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null - CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" + JAVACMD=$JAVA_HOME/jre/sh/java else - JAVACMD="$JAVA_HOME/bin/java" + JAVACMD=$JAVA_HOME/bin/java fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME @@ -77,84 +130,120 @@ Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else - JAVACMD="java" - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." + fi fi # Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac fi -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) -# For Cygwin, switch paths to Windows format before running java -if $cygwin ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) fi - i=$((i+1)) + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg done - case $i in - (0) set -- ;; - (1) set -- "$args0" ;; - (2) set -- "$args0" "$args1" ;; - (3) set -- "$args0" "$args1" "$args2" ;; - (4) set -- "$args0" "$args1" "$args2" "$args3" ;; - (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac fi -# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules -function splitJvmOpts() { - JVM_OPTS=("$@") -} -eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS -JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" -exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/platforms/sdl/sdl2/android/gradlew.bat b/platforms/sdl/sdl2/android/gradlew.bat index 8a0b282aa..93e3f59f1 100644 --- a/platforms/sdl/sdl2/android/gradlew.bat +++ b/platforms/sdl/sdl2/android/gradlew.bat @@ -1,4 +1,20 @@ -@if "%DEBUG%" == "" @echo off +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @@ -8,20 +24,24 @@ @rem Set local scope for the variables with windows NT shell if "%OS%"=="Windows_NT" setlocal -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= - set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init +if %ERRORLEVEL% equ 0 goto execute echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. @@ -35,7 +55,7 @@ goto fail set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe -if exist "%JAVA_EXE%" goto init +if exist "%JAVA_EXE%" goto execute echo. echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% @@ -45,44 +65,26 @@ echo location of your Java installation. goto fail -:init -@rem Get command-line arguments, handling Windowz variants - -if not "%OS%" == "Windows_NT" goto win9xME_args -if "%@eval[2+2]" == "4" goto 4NT_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* -goto execute - -:4NT_args -@rem Get arguments from the 4NT Shell from JP Software -set CMD_LINE_ARGS=%$ - :execute @rem Setup the command line set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* :end @rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd +if %ERRORLEVEL% equ 0 goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% :mainEnd if "%OS%"=="Windows_NT" endlocal diff --git a/thirdparty/SDL2/src b/thirdparty/SDL2/src index 9959e840b..cd6fbfbb0 160000 --- a/thirdparty/SDL2/src +++ b/thirdparty/SDL2/src @@ -1 +1 @@ -Subproject commit 9959e840b6b74a19a0ca716cc4249b6ca8512b0e +Subproject commit cd6fbfbb035452a991b9762ecbb888b48ec84c1f From f7814a974100b33910463793a5a184afea863116 Mon Sep 17 00:00:00 2001 From: Brent <43089001+BrentDaMage@users.noreply.github.com> Date: Fri, 20 Feb 2026 17:27:58 -0800 Subject: [PATCH 31/63] Fixed crash (#497) --- source/client/renderer/Textures.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/source/client/renderer/Textures.cpp b/source/client/renderer/Textures.cpp index d1e1a8e86..5fdc17b52 100644 --- a/source/client/renderer/Textures.cpp +++ b/source/client/renderer/Textures.cpp @@ -61,8 +61,11 @@ size_t _mipTagStart(const std::string& path) constexpr size_t mipSuffixLength = MIP_TAG_SIZE + 2; std::string extension = Util::getExtension(path); + size_t len = path.length() - extension.length(); + if (len <= mipSuffixLength) + return 0; - return path.length() - mipSuffixLength - extension.length(); + return len - mipSuffixLength; } bool _isMipmap(const std::string& path) From 09e60277f84f3fd08d6ad02c906686d17d0493f1 Mon Sep 17 00:00:00 2001 From: Un1q32 Date: Sat, 21 Feb 2026 17:29:20 -0500 Subject: [PATCH 32/63] XDG base directory spec compliance (#498) --- platforms/ios/build.sh | 6 ++---- platforms/macos/build.sh | 4 ++-- platforms/sdl/sdl1/main.cpp | 17 +++++++++++++++-- platforms/sdl/sdl2/main.cpp | 15 ++++++++++++++- 4 files changed, 33 insertions(+), 9 deletions(-) diff --git a/platforms/ios/build.sh b/platforms/ios/build.sh index 43bd17764..8e4a951e5 100755 --- a/platforms/ios/build.sh +++ b/platforms/ios/build.sh @@ -85,10 +85,8 @@ else fi # ensure we use ccache for the toolchain build ccache="$(command -v ccache || true)" -printf '#!/bin/sh\n - exec %sclang "$@"\n' "$ccache " > bin/remcpe-clang -printf '#!/bin/sh\n - exec %sclang++ "$@"\n' "$ccache " > bin/remcpe-clang++ +printf '#!/bin/sh\nexec %s clang "$@"\n' "$ccache" > bin/remcpe-clang +printf '#!/bin/sh\nexec %s clang++ "$@"\n' "$ccache" > bin/remcpe-clang++ chmod +x bin/remcpe-clang bin/remcpe-clang++ if [ -n "$outdated_toolchain" ]; then diff --git a/platforms/macos/build.sh b/platforms/macos/build.sh index 441fcd2a8..3e31399a6 100755 --- a/platforms/macos/build.sh +++ b/platforms/macos/build.sh @@ -109,8 +109,8 @@ else fi # ensure we use ccache for the toolchain build ccache="$(command -v ccache || true)" -printf '#!/bin/sh\nexec %sclang "$@"\n' "$ccache " > bin/remcpe-clang -printf '#!/bin/sh\nexec %sclang++ "$@"\n' "$ccache " > bin/remcpe-clang++ +printf '#!/bin/sh\nexec %s clang "$@"\n' "$ccache" > bin/remcpe-clang +printf '#!/bin/sh\nexec %s clang++ "$@"\n' "$ccache" > bin/remcpe-clang++ chmod +x bin/remcpe-clang bin/remcpe-clang++ if [ -n "$outdated_toolchain" ]; then diff --git a/platforms/sdl/sdl1/main.cpp b/platforms/sdl/sdl1/main.cpp index 2bc5c4b62..4e08fc1b9 100644 --- a/platforms/sdl/sdl1/main.cpp +++ b/platforms/sdl/sdl1/main.cpp @@ -92,10 +92,23 @@ static std::string getStoragePath() #ifdef _WIN32 pathBase = getenv("APPDATA"); #else - pathBase = getenv("HOME"); + const char *xdg_data = getenv("XDG_DATA_HOME"); + if (xdg_data) + pathBase = xdg_data; + else + { + xdg_data = getenv("HOME"); + if (!xdg_data) + { + LOG_E("HOME not set"); + pathBase = ""; // current working directory + } + else + pathBase = ((std::string)xdg_data + "/.local/share").c_str(); + } #endif - if (pathBase == nullptr || pathBase[0] == '\0') + if (!pathBase) pathBase = ""; // just use the current working directory std::string path(pathBase); diff --git a/platforms/sdl/sdl2/main.cpp b/platforms/sdl/sdl2/main.cpp index 0fc0713a4..b7780d5bd 100644 --- a/platforms/sdl/sdl2/main.cpp +++ b/platforms/sdl/sdl2/main.cpp @@ -415,7 +415,20 @@ int main(int argc, char *argv[]) #elif defined(ANDROID) storagePath = SDL_AndroidGetExternalStoragePath(); #else - storagePath = getenv("HOME"); + const char *xdg_data = getenv("XDG_DATA_HOME"); + if (xdg_data) + storagePath = xdg_data; + else + { + xdg_data = getenv("HOME"); + if (!xdg_data) + { + LOG_E("HOME not set"); + storagePath = "."; // current working directory + } + else + storagePath = (std::string)xdg_data + "/.local/share"; + } #endif storagePath += "/.reminecraftpe"; From 33a6b7bf3c14b4191a6fb5373cd94c9b935f077b Mon Sep 17 00:00:00 2001 From: Brent <43089001+BrentDaMage@users.noreply.github.com> Date: Sat, 21 Feb 2026 15:34:20 -0800 Subject: [PATCH 33/63] Fixed ClassicCraftingScreen_Console positioning (#499) --- source/network/Packet.hpp | 5 +++-- thirdparty/SDL2/src | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/source/network/Packet.hpp b/source/network/Packet.hpp index 4d7a538d5..d072e8e65 100644 --- a/source/network/Packet.hpp +++ b/source/network/Packet.hpp @@ -13,12 +13,13 @@ #include "BitStream.h" #include "MessageIdentifiers.h" -#define NETWORK_PROTOCOL_VERSION_MIN 6 // the packet IDs changed completely between 2, 3, 4, 5, and 6 +#define NETWORK_PROTOCOL_VERSION_MIN 6 // the packet IDs changed completely between 2 thru 6 //#define NETWORK_PROTOCOL_VERSION 2 // 0.1.0 (actual client crashes with unrecognized tiles) //#define NETWORK_PROTOCOL_VERSION 3 // 0.2.0 (actual client crashes with unrecognized entities) //#define NETWORK_PROTOCOL_VERSION 4 // 0.3.0 //#define NETWORK_PROTOCOL_VERSION 5 // 0.3.2 -#define NETWORK_PROTOCOL_VERSION 6 // 0.3.3 +#define NETWORK_PROTOCOL_VERSION 6 // 0.3.3 +//#define NETWORK_PROTOCOL_VERSION 7 // 0.4.0 //#define NETWORK_PROTOCOL_VERSION 29 // 0.12.1 class NetEventCallback; diff --git a/thirdparty/SDL2/src b/thirdparty/SDL2/src index cd6fbfbb0..9959e840b 160000 --- a/thirdparty/SDL2/src +++ b/thirdparty/SDL2/src @@ -1 +1 @@ -Subproject commit cd6fbfbb035452a991b9762ecbb888b48ec84c1f +Subproject commit 9959e840b6b74a19a0ca716cc4249b6ca8512b0e From d4dc905be548907905861443096a82fa1a505934 Mon Sep 17 00:00:00 2001 From: Un1q32 Date: Sat, 21 Feb 2026 18:50:11 -0500 Subject: [PATCH 34/63] Enabled Auto-Jump by Default for Touchscreens (#500) --- source/client/options/Options.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/client/options/Options.cpp b/source/client/options/Options.cpp index 9f226131f..8003e67ba 100644 --- a/source/client/options/Options.cpp +++ b/source/client/options/Options.cpp @@ -80,7 +80,7 @@ Options::Options(Minecraft* mc, const std::string& folderPath) : , m_flightHax("misc_flycheat", "options.flightHax", false) , m_playerName("mp_username", "options.username", "Steve") , m_serverVisibleDefault("mp_server_visible_default", "options.serverVisibleDefault", true) - , m_autoJump("ctrl_autojump", "options.autoJump", false) + , m_autoJump("ctrl_autojump", "options.autoJump", mc->platform()->isTouchscreen()) , m_debugText("info_debugtext", "options.debugText", false) , m_blockOutlines("gfx_blockoutlines", "options.blockOutlines", false) , m_fancyGrass("gfx_fancygrass", "options.fancyGrass", true) From a8eb167a2c656af3a52dc84362186e83909922fc Mon Sep 17 00:00:00 2001 From: Brent Da Mage Date: Tue, 12 May 2026 02:53:08 -0500 Subject: [PATCH 35/63] Remove bullshit --- ...b725a90e35b4da109644cc57d0c013d7.patch.txt | 185 - ...fa9bb9e1ec79591b9d92700b524777e0.patch.txt | 1554 --- ...65261fe97c2e41196d3b636e0171a1e9.patch.txt | 190 - ...bc5bfe4a76c2c89dfa94d7ac1e7a26df.patch.txt | 501 - ...3bc6fa100cd978fda4b14d6cc0cec4e0.patch.txt | 6842 ---------- 478.patch.txt | 10726 ---------------- 6 files changed, 19998 deletions(-) delete mode 100644 01_02783ae6b725a90e35b4da109644cc57d0c013d7.patch.txt delete mode 100644 02_4d5d1bbffa9bb9e1ec79591b9d92700b524777e0.patch.txt delete mode 100644 03_14bb937365261fe97c2e41196d3b636e0171a1e9.patch.txt delete mode 100644 04_d80620fdbc5bfe4a76c2c89dfa94d7ac1e7a26df.patch.txt delete mode 100644 05_a55606e83bc6fa100cd978fda4b14d6cc0cec4e0.patch.txt delete mode 100644 478.patch.txt diff --git a/01_02783ae6b725a90e35b4da109644cc57d0c013d7.patch.txt b/01_02783ae6b725a90e35b4da109644cc57d0c013d7.patch.txt deleted file mode 100644 index 279d343ce..000000000 --- a/01_02783ae6b725a90e35b4da109644cc57d0c013d7.patch.txt +++ /dev/null @@ -1,185 +0,0 @@ -From 02783ae6b725a90e35b4da109644cc57d0c013d7 Mon Sep 17 00:00:00 2001 -From: Sam -Date: Wed, 18 Feb 2026 08:49:39 -0500 -Subject: [PATCH] CMakeLists & Xcode - ---- - .../Minecraft.xcodeproj/project.pbxproj | 56 ++++++++++++------- - source/CMakeLists.txt | 6 +- - 2 files changed, 40 insertions(+), 22 deletions(-) - -diff --git a/platforms/macos/projects/Minecraft/Minecraft.xcodeproj/project.pbxproj b/platforms/macos/projects/Minecraft/Minecraft.xcodeproj/project.pbxproj -index dcc3c8727..7600da5f0 100644 ---- a/platforms/macos/projects/Minecraft/Minecraft.xcodeproj/project.pbxproj -+++ b/platforms/macos/projects/Minecraft/Minecraft.xcodeproj/project.pbxproj -@@ -989,11 +989,6 @@ - 84E7BF272F286A08002D3936 /* SimpleContainer.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84E7BF172F286A08002D3936 /* SimpleContainer.hpp */; }; - 84E7BF282F286A08002D3936 /* Slot.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84E7BF182F286A08002D3936 /* Slot.cpp */; }; - 84E7BF292F286A08002D3936 /* Slot.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84E7BF192F286A08002D3936 /* Slot.hpp */; }; -- 84E7BF2F2F286A20002D3936 /* CompoundContainer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84E7BF2A2F286A20002D3936 /* CompoundContainer.cpp */; }; -- 84E7BF302F286A20002D3936 /* CompoundContainer.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84E7BF2B2F286A20002D3936 /* CompoundContainer.hpp */; }; -- 84E7BF312F286A20002D3936 /* Container.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84E7BF2C2F286A20002D3936 /* Container.hpp */; }; -- 84E7BF322F286A20002D3936 /* ContainerListener.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84E7BF2D2F286A20002D3936 /* ContainerListener.cpp */; }; -- 84E7BF332F286A20002D3936 /* ContainerListener.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84E7BF2E2F286A20002D3936 /* ContainerListener.hpp */; }; - 84E7BF362F286A6A002D3936 /* ItemStack.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84E7BF342F286A6A002D3936 /* ItemStack.cpp */; }; - 84E7BF372F286A6A002D3936 /* ItemStack.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84E7BF352F286A6A002D3936 /* ItemStack.hpp */; }; - 84E8DCE52E84ACBC00B30789 /* libRenderer.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 8406FD292AF1814500B09C1D /* libRenderer.a */; }; -@@ -1287,6 +1282,15 @@ - 84EEED642F0A01B700F741B6 /* ViewportOrigin.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84EEED622F0A01B700F741B6 /* ViewportOrigin.hpp */; }; - 84EEED682F0A02F300F741B6 /* ErrorHandlerOGL.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84EEED662F0A02F300F741B6 /* ErrorHandlerOGL.cpp */; }; - 84EEED692F0A02F300F741B6 /* ErrorHandlerOGL.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84EEED672F0A02F300F741B6 /* ErrorHandlerOGL.hpp */; }; -+ 84F0DA762F45FAE100906960 /* ContainerContentChangeListener.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84F0DA702F45FAE100906960 /* ContainerContentChangeListener.hpp */; }; -+ 84F0DA772F45FAE100906960 /* ContainerListener.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84F0DA722F45FAE100906960 /* ContainerListener.hpp */; }; -+ 84F0DA782F45FAE100906960 /* Container.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84F0DA6F2F45FAE100906960 /* Container.hpp */; }; -+ 84F0DA792F45FAE100906960 /* CompoundContainer.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84F0DA6D2F45FAE100906960 /* CompoundContainer.hpp */; }; -+ 84F0DA7A2F45FAE100906960 /* ContainerSizeChangeListener.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84F0DA742F45FAE100906960 /* ContainerSizeChangeListener.hpp */; }; -+ 84F0DA7B2F45FAE100906960 /* ContainerContentChangeListener.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84F0DA712F45FAE100906960 /* ContainerContentChangeListener.cpp */; }; -+ 84F0DA7C2F45FAE100906960 /* CompoundContainer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84F0DA6E2F45FAE100906960 /* CompoundContainer.cpp */; }; -+ 84F0DA7D2F45FAE100906960 /* ContainerListener.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84F0DA732F45FAE100906960 /* ContainerListener.cpp */; }; -+ 84F0DA7E2F45FAE100906960 /* ContainerSizeChangeListener.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84F0DA752F45FAE100906960 /* ContainerSizeChangeListener.cpp */; }; - 84F4880E2EEA4F5C00309B1E /* FixedPipelineStateNull.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84F487F02EEA4F5C00309B1E /* FixedPipelineStateNull.cpp */; }; - 84F4880F2EEA4F5C00309B1E /* FixedPipelineStateNull.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84F487F12EEA4F5C00309B1E /* FixedPipelineStateNull.hpp */; }; - 84F488102EEA4F5C00309B1E /* FogStateNull.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84F487F22EEA4F5C00309B1E /* FogStateNull.cpp */; }; -@@ -2766,11 +2770,6 @@ - 84E7BF172F286A08002D3936 /* SimpleContainer.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = SimpleContainer.hpp; sourceTree = ""; }; - 84E7BF182F286A08002D3936 /* Slot.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Slot.cpp; sourceTree = ""; }; - 84E7BF192F286A08002D3936 /* Slot.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Slot.hpp; sourceTree = ""; }; -- 84E7BF2A2F286A20002D3936 /* CompoundContainer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CompoundContainer.cpp; sourceTree = ""; }; -- 84E7BF2B2F286A20002D3936 /* CompoundContainer.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = CompoundContainer.hpp; sourceTree = ""; }; -- 84E7BF2C2F286A20002D3936 /* Container.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Container.hpp; sourceTree = ""; }; -- 84E7BF2D2F286A20002D3936 /* ContainerListener.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ContainerListener.cpp; sourceTree = ""; }; -- 84E7BF2E2F286A20002D3936 /* ContainerListener.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ContainerListener.hpp; sourceTree = ""; }; - 84E7BF342F286A6A002D3936 /* ItemStack.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ItemStack.cpp; sourceTree = ""; }; - 84E7BF352F286A6A002D3936 /* ItemStack.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ItemStack.hpp; sourceTree = ""; }; - 84EAE8DD2AF1EAA1000894E8 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; -@@ -3069,6 +3068,15 @@ - 84EEED622F0A01B700F741B6 /* ViewportOrigin.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ViewportOrigin.hpp; sourceTree = ""; }; - 84EEED662F0A02F300F741B6 /* ErrorHandlerOGL.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ErrorHandlerOGL.cpp; sourceTree = ""; }; - 84EEED672F0A02F300F741B6 /* ErrorHandlerOGL.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ErrorHandlerOGL.hpp; sourceTree = ""; }; -+ 84F0DA6D2F45FAE100906960 /* CompoundContainer.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = CompoundContainer.hpp; sourceTree = ""; }; -+ 84F0DA6E2F45FAE100906960 /* CompoundContainer.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = CompoundContainer.cpp; sourceTree = ""; }; -+ 84F0DA6F2F45FAE100906960 /* Container.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Container.hpp; sourceTree = ""; }; -+ 84F0DA702F45FAE100906960 /* ContainerContentChangeListener.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ContainerContentChangeListener.hpp; sourceTree = ""; }; -+ 84F0DA712F45FAE100906960 /* ContainerContentChangeListener.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ContainerContentChangeListener.cpp; sourceTree = ""; }; -+ 84F0DA722F45FAE100906960 /* ContainerListener.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ContainerListener.hpp; sourceTree = ""; }; -+ 84F0DA732F45FAE100906960 /* ContainerListener.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ContainerListener.cpp; sourceTree = ""; }; -+ 84F0DA742F45FAE100906960 /* ContainerSizeChangeListener.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ContainerSizeChangeListener.hpp; sourceTree = ""; }; -+ 84F0DA752F45FAE100906960 /* ContainerSizeChangeListener.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ContainerSizeChangeListener.cpp; sourceTree = ""; }; - 84F487F02EEA4F5C00309B1E /* FixedPipelineStateNull.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FixedPipelineStateNull.cpp; sourceTree = ""; }; - 84F487F12EEA4F5C00309B1E /* FixedPipelineStateNull.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = FixedPipelineStateNull.hpp; sourceTree = ""; }; - 84F487F22EEA4F5C00309B1E /* FogStateNull.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FogStateNull.cpp; sourceTree = ""; }; -@@ -3806,11 +3814,6 @@ - 840DD6562AC810620006A435 /* world */ = { - isa = PBXGroup; - children = ( -- 84E7BF2A2F286A20002D3936 /* CompoundContainer.cpp */, -- 84E7BF2B2F286A20002D3936 /* CompoundContainer.hpp */, -- 84E7BF2C2F286A20002D3936 /* Container.hpp */, -- 84E7BF2D2F286A20002D3936 /* ContainerListener.cpp */, -- 84E7BF2E2F286A20002D3936 /* ContainerListener.hpp */, - 840DD6572AC810620006A435 /* entity */, - 84358B1A2F4551290077EA44 /* Facing.cpp */, - 8477B3A82C4DC3B3004E1AC5 /* Facing.hpp */, -@@ -5083,6 +5086,15 @@ - 84E7BF092F286A08002D3936 /* inventory */ = { - isa = PBXGroup; - children = ( -+ 84F0DA6D2F45FAE100906960 /* CompoundContainer.hpp */, -+ 84F0DA6E2F45FAE100906960 /* CompoundContainer.cpp */, -+ 84F0DA6F2F45FAE100906960 /* Container.hpp */, -+ 84F0DA702F45FAE100906960 /* ContainerContentChangeListener.hpp */, -+ 84F0DA712F45FAE100906960 /* ContainerContentChangeListener.cpp */, -+ 84F0DA722F45FAE100906960 /* ContainerListener.hpp */, -+ 84F0DA732F45FAE100906960 /* ContainerListener.cpp */, -+ 84F0DA742F45FAE100906960 /* ContainerSizeChangeListener.hpp */, -+ 84F0DA752F45FAE100906960 /* ContainerSizeChangeListener.cpp */, - 84E7BF0A2F286A08002D3936 /* ArmorSlot.cpp */, - 84E7BF0B2F286A08002D3936 /* ArmorSlot.hpp */, - 84E022FE2F2A0245003C8FFE /* ChestMenu.cpp */, -@@ -5955,7 +5967,6 @@ - 84BF63862AF186C8008A9995 /* Inventory.hpp in Headers */, - 8477B3B52C4DC414004E1AC5 /* ChunkTilePos.hpp in Headers */, - 84BF63872AF186C8008A9995 /* Item.hpp in Headers */, -- 84E7BF332F286A20002D3936 /* ContainerListener.hpp in Headers */, - 84BF63892AF186C8008A9995 /* TileItem.hpp in Headers */, - 84BF638A2AF186C8008A9995 /* TilePlanterItem.hpp in Headers */, - 84E7BF292F286A08002D3936 /* Slot.hpp in Headers */, -@@ -5969,6 +5980,11 @@ - 84BF638F2AF186C8008A9995 /* BiomeSource.hpp in Headers */, - 84BF63902AF186C8008A9995 /* ChunkCache.hpp in Headers */, - 84BF63912AF186C8008A9995 /* ChunkSource.hpp in Headers */, -+ 84F0DA762F45FAE100906960 /* ContainerContentChangeListener.hpp in Headers */, -+ 84F0DA772F45FAE100906960 /* ContainerListener.hpp in Headers */, -+ 84F0DA782F45FAE100906960 /* Container.hpp in Headers */, -+ 84F0DA792F45FAE100906960 /* CompoundContainer.hpp in Headers */, -+ 84F0DA7A2F45FAE100906960 /* ContainerSizeChangeListener.hpp in Headers */, - 84BF63922AF186C8008A9995 /* LevelChunk.hpp in Headers */, - 84BF63932AF186C8008A9995 /* PerformanceTestChunkSource.hpp in Headers */, - 84E023012F2A0245003C8FFE /* ChestMenu.hpp in Headers */, -@@ -6074,7 +6090,6 @@ - 84BF63D22AF186C9008A9995 /* TorchTile.hpp in Headers */, - 84BF63D32AF186C9008A9995 /* TransparentTile.hpp in Headers */, - 84BF63D42AF186C9008A9995 /* TreeTile.hpp in Headers */, -- 84E7BF312F286A20002D3936 /* Container.hpp in Headers */, - 84BF63D52AF186C9008A9995 /* WireTile.hpp in Headers */, - 84AA8B512B32F39A003F5B82 /* BinaryHeap.hpp in Headers */, - 84AA8B532B32F39A003F5B82 /* Node.hpp in Headers */, -@@ -6115,7 +6130,6 @@ - 84358B062F4550B30077EA44 /* MusicTileEntity.hpp in Headers */, - 84358B072F4550B30077EA44 /* TileEntityType.hpp in Headers */, - 8470AF3D2BE9B6FA00BCA54E /* FireworkParticle.hpp in Headers */, -- 84E7BF302F286A20002D3936 /* CompoundContainer.hpp in Headers */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -@@ -7067,7 +7081,6 @@ - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( -- 84E7BF322F286A20002D3936 /* ContainerListener.cpp in Sources */, - 84BF630C2AF18631008A9995 /* Entity.cpp in Sources */, - 84BF630D2AF18631008A9995 /* FallingTile.cpp in Sources */, - 84BF630E2AF18631008A9995 /* ItemEntity.cpp in Sources */, -@@ -7146,7 +7159,6 @@ - 84358B122F4550CC0077EA44 /* ChestTile.cpp in Sources */, - 84358B132F4550CC0077EA44 /* MusicTile.cpp in Sources */, - 84BF63382AF18631008A9995 /* LevelListener.cpp in Sources */, -- 84E7BF2F2F286A20002D3936 /* CompoundContainer.cpp in Sources */, - 84BF63392AF18631008A9995 /* Material.cpp in Sources */, - 84BF633A2AF18631008A9995 /* Region.cpp in Sources */, - 84BF633B2AF18631008A9995 /* ChunkStorage.cpp in Sources */, -@@ -7260,6 +7272,10 @@ - 84E78C7B2B58B3E000D515EF /* Rocket.cpp in Sources */, - 84E78C832B58B5FB00D515EF /* RocketItem.cpp in Sources */, - 845BBCB62F2EA81700C7232C /* ToolItem.cpp in Sources */, -+ 84F0DA7B2F45FAE100906960 /* ContainerContentChangeListener.cpp in Sources */, -+ 84F0DA7C2F45FAE100906960 /* CompoundContainer.cpp in Sources */, -+ 84F0DA7D2F45FAE100906960 /* ContainerListener.cpp in Sources */, -+ 84F0DA7E2F45FAE100906960 /* ContainerSizeChangeListener.cpp in Sources */, - 84E78C872B58B66B00D515EF /* RocketLauncherTile.cpp in Sources */, - 8470AF2C2BE9B60A00BCA54E /* MobFactory.cpp in Sources */, - 84E1C9E92E7FDC72007D2F5D /* SlabItem.cpp in Sources */, -diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt -index 853b0897c..057f3ac44 100644 ---- a/source/CMakeLists.txt -+++ b/source/CMakeLists.txt -@@ -360,7 +360,10 @@ add_library(reminecraftpe-core STATIC - world/item/crafting/SingleInputRecipe.cpp - world/item/crafting/ShapedRecipe.cpp - world/item/crafting/ShapelessRecipe.cpp -- world/ContainerListener.cpp -+ world/inventory/ContainerListener.cpp -+ world/inventory/CompoundContainer.cpp -+ world/inventory/ContainerContentChangeListener.cpp -+ world/inventory/ContainerSizeChangeListener.cpp - world/inventory/ContainerMenu.cpp - world/inventory/InventoryMenu.cpp - world/inventory/CraftingMenu.cpp -@@ -373,7 +376,6 @@ add_library(reminecraftpe-core STATIC - world/inventory/CraftingContainer.cpp - world/inventory/ResultContainer.cpp - world/inventory/SimpleContainer.cpp -- world/CompoundContainer.cpp - world/item/DoorItem.cpp - world/item/ItemStack.cpp - world/item/RocketItem.cpp diff --git a/02_4d5d1bbffa9bb9e1ec79591b9d92700b524777e0.patch.txt b/02_4d5d1bbffa9bb9e1ec79591b9d92700b524777e0.patch.txt deleted file mode 100644 index a16d982be..000000000 --- a/02_4d5d1bbffa9bb9e1ec79591b9d92700b524777e0.patch.txt +++ /dev/null @@ -1,1554 +0,0 @@ -From 0b8e1b54f12d104d4b0255986a08a36a8da74293 Mon Sep 17 00:00:00 2001 -From: Vimdo -Date: Wed, 18 Feb 2026 03:36:51 -0500 -Subject: [PATCH] Chat & Pause buttons for touch screens (#485) - ---- - game/assets/gui/touchgui.png | Bin 33357 -> 37494 bytes - source/client/gui/Gui.cpp | 43 ++++++++++++++++-- - .../screens/IngameBlockSelectionScreen.cpp | 38 ++++------------ - .../screens/IngameBlockSelectionScreen.hpp | 2 - - 4 files changed, 48 insertions(+), 35 deletions(-) - -diff --git a/game/assets/gui/touchgui.png b/game/assets/gui/touchgui.png -index 9b97bf25ce9e971331a7903e501324e790c002af..46cd63191bd60e3b49e663aa25c6f2528dc0ad5a 100644 -GIT binary patch -literal 37494 -zcmV*4Ky|-~P)3vmGHUM;pLnrF*(7^kaHHvK~$njl$AVythucD|DAI~-Kwt6)o=PS@66Zpx~p#8x^?TGb5B0(h=UeeFb&gS1H*66 -zzq-DxDLHb*68Uv{T{*!qucM=*UTyR_WBEF3YisjmP(kXfqX!l*Spr?%UA}ZWRiLG% -z1-g5>;j8!FO(Q=1$YWvd{Q1z{-o9VWLsL^Dw6}M_r#|s9U;Eux>#A1Xt9xGm`q~Cy -zjN`RsS#BcWCYV+}BB1wE2@nB@#-gxk(6C(wm~=) -z+OOuJt}YCbNW}Mk_tmgTV|n`glAJ!C(Y7zwyR?H4jEG0G6-Iio?EISGDq9z2+5>-_ -z@Q(iEF_SLh@6*bF#j&3+MgWbNfOAY1zs3@H%rCR3J+t)nkpWBH(o}6MZS2*8V%F8) -zo_|(-KkUe3@{Xj~w(j?ebD+F+6F`SHNY`eK0fCN>|a*az2wtX -zs!Z~NV%F7QBn92gTu1QYU!Q}+k2=n?-3x#DlTv&QOeFL6>3*+f#u6WW=IytKe_!9c -z1D4O9MQ5J`1!AY1HCQv^xLcL-=6<75fX&G<8itsA7s5(OAvJ{ -z`|ZiUS6p@peCvBpz_NKW`O%DP|IPqBry>8^ExYKNe1wxrR?hQoA|R1jM|=s|v8?vK -ziU5YeIHe94g|B!M0Shp-us|)8>gMUV%DUP!7KfSbjr9G+=bwcmjy|5g|Mj^)DrDX@5-qB) -zO5W?bt-E3IoL>6=>>qyzM;~_*eMf>IVZ-*laO5$C`_kw2SATpyFY8|b+@?SxCdf?` -z+d(dH=>YaG4uEzjZFk9-ruytpkU_C0hi0O(`7M53z3uEX;eVg{1cVr+h9CdzDVW{W;yAuzaGcH+3zfdt%ieh*eDDAL7#7ZS -zdN6L7~H~|P4#4_z~Iso_LG)+xgY(Oq`y#um%vm)9shvwed -zQdt}f@hg{duE_DJw%~FP&_?FgR{I6ztgClia4tOm=Raf}D+ThtSBF){HAmWfUUK0D -z@cgsRao{m!NpM(WoYoorzH(mQdi;^hEd~@}>XXP{iTIEghyVmgRtxNZIsh~mm->C$G1xriJ%Ny+scC -z?A^Yc?N>X-9Y*YDHcb;ui+{*-j=KVC4l|akJJ}JCgOJ56+bK^-XG}d9(-uRvdpF}Z -zbk2ixFN5sKb&Lq_Ycb7usJ#=djXLcZKEns6-O6vsT2~I!d-jXL9XFS_pueYrE7Ie= -z;!ccznf@-+I3)01y^vq;3-mc>R@Pv6w@mnxI;P^wqV;7GFjkQZk`r)OYAG|wV?m@v_ady-GtL} -zcV>CsS>43TL&5%*y-4komT+yVmJYzRu9*JU`C{~Uf$}zjl*7>)pmPEFUc1+OHH9Al -ze`K8)WiR5iP&*=NB2KJ_%r -z5WWh}5fM1SDPpoJ%=Z`CTm6U+)%{SrscF--%fPGa$1?1a!hd_gYs2 -z<)*uI?hHC2+dH)apw=3_SLS@Lx<)=51j35l8l95PKl600J%9h_oq0O^UI$}ms;yv9Xv8BF$R -z6BowpzL@BbNjZ@TlacmUcOYN??`JS|PTnhftjw`fSV9>lS-1V=lS5fm -z`QnoASiBp)ZVbnoLWP@D6kd7xrCDo1_Hotam#KF4HHQ6unY|v$d9TQYNUi<3r=OOy -z9iEfz@S1#m=9*XCgRcmL&T3ns^N<4^Ism3*HRrDH8b=9Ax?HotNL91AT<`?rjW+aFzltDyvBhE06E;MqUetpA!VJK@+9?RH-8d+pX;uzVhm*h3<4 -z%n2tu+NgC<;(OJ#{sWOz!S}y+JOSNJJiCxr%z(OY{&{6WmM;|aldG*Sz;ys53Ga{j -z09dvaV809os*WE(p>?%p%P#f(+V{MxV1L-=k#9e)ejj&Y9>QM2dmV@*)%UYcdrQH& -zIQz6y;i>=owWEJe$TP13!5L&n_&Ky&?&v?iMF8OkV5raj1hi1J9mceHG`+c>5dgDF -zM6b}gdhZqQF1gQQt||4sp81wjwYJ60LowI5=ke~3^EOF -z@QVeVURO?_c`di#O0^(ZQ=^%NS^&Qhw-~P}w}03)OcD?QXlm(zg^LzJZ(nc4cYWb6 -z&%*o#3!$^S=S}er`}+D|?!3A1ng93%{XXQdBVpeB1<=viQE}f-KlxKwaquB9YxW!; -zM!N5ztE&t8X3T&O-g!GLUAzcdI{V0d_snxI$UC-Q{Oo@z1JG;hH$rT9&zoW{rV0=N -z7#|;l&6_vTko_P6U7elK(AWUcXl%+&2t|xBuE9p+pEY|H^!N9x^CQDUD&3SHM4+>? -z3mO_5Ar_5R+}C7-bdyCpKO%p3PY;X?535m(5A7ku-mzn6-jN}Jc;-*f(f3+ok>Bh< -z1R#-!!`LYJLIhIDBs4ZQRmHWm63`JlA8g*R9#ZKP^!4_^VTT_E!^6Wc&_AHANW`Kr -zJZS%}gb3igV;#F%la_VF&Id33?FFj*(>G%#EL*-D#>d8dk5-$8>Vnb%_(cScIpHMe -z?Cyd+d;5!MJ$ZnO`HhVmV8P-gd2QZ{mM((>4mbdwe(GlskHsCxuja}pB5?lOFM{3~ -zGogQPZLz(WP~+tn{{{yic6i>oI_!v}Xy^ao_rD9VXv`D&Uw`dYSg>eu-uroR2)n+{ltf_b)~ywB0o%6k@MCPg11{x`_BI$A&c90qLqVvktApJ;cM#$53h9(Y -zAQ%inXIE$0L*2P!JN>P>nhXq}y`#gy(tAODeLZn}_#E{O4OIy%o`&jy$_OMOnM^=5 -zG665W_|lth+9Ttm@Z7V{L8O3gqM@Myg252%+O-qr&R+;c#6w2mrKo^uXdJOQ5T} -ztBBUM3#jku?&*fF-g|e=jL}yDFfld)&8_|hEBFQDqeCUtK5TAoArT-{g_`onMQq>t -z1{^SNf&UNc18m;7zNF`FYwv)~8#j_TR1Jv6qOfVxCJ5JsYjVDdh5Cki7#|z2sWD}V -z0N{~QsSw1YQ5YW?g1W{=Xl`zTNDY~^&?*s&PLO+OZ(mnY&xZ5S*wh5kXcX$|!oJGq -zg9Cd>8QIm{L;8r}k?Q1oF9#+j#-V@DZV~}ktXx%8*f)rsEiy4t6=DhE9VX&&Shsdv -zRr;SS!1>9U9cdpy#Uwj2J_e~|0(xi7Ci{Opku2gWC!pd8gEdmAl;?T0yQ5rQa=)1j -zj1CPzb88!!s!*|4tZzbHeLd+z0s-d~WhL({A(VFK_H8gRF$RYoaTLs-JC6uusZn_7 -z(>@W25 -zsi!(rOaL01nxLzzn~-Z{Y#h?*bjf|-4*QL7JxmANCFmLMBsO@qs`f0O!8#!m3r4db3oo -zuEp_l5g;pj6e|5nGB?(5!!)DNh*R13>VGU9{rX|8%1d(upfibZi3Zp9(()= -zxcXg}LLwF;jdRh!c%~yWoq@E;6SNgGKJG9C(Aw4tJ9g~Ik?LIJ({m+#v>n<_UM>b7 -zKqMYT77&p)o){8@$UbL-smjh+lum;?=I}8Xl-}x{7hg==4b?Buvaw>HcinZj%3!#6 -zU;ui%I>BOwc*DGJeT+PvCXd-i0uYNuz+?sy68)K&92Y(>GweK^0@^z|Vb7l3#lM?r -zs0nbaK1TUXOHU4AmRVQ=cjQle=5s_qK$+N>`dEap1_VO^cAM@gdOw#9!?^a!8p>TKsmNa~=cHkEwFnVP@O2&OeW*+71S2 -z=cOt*BApu&(cAA>O=1Fa2q-u69QDq=^)Z4rhItDXL32wRW%G4ElNy(t1CX`$OjQF9 -zW%X*v04fLNRR6HjbZe6VgcXXt5(zppsU&d$dPbLOe1=EIVAG~8!3_km5>0GKV5I=h%=-t1J29nNr*%vkV@HvjTa>` -zAsh-rkHg``;w-+&AdyT#BoZOyMn#_vr0}#X$UsIaC#XM+$>{5~L$eXVah}k*skr;^ -zdypySr+UsxdX6le&%TA6TUNNf5fbqjn5iTL!gU4NTzo-a`^KYUw=%&9@S>B~UU>yX -zA`?_0HWUtXL`|n55DdaMzx!RtAkt$~LUfBN$#8|1#S1VB-9Ugbc<_;L!u9XHnq+TF -zRG-B@Nk+CTb$?z9j_Wh7z2ZF(v!bOb5A#4)p%)9Ft+N}(#>OBzJ`PQ-ZFzMSIJR&o -z1aTt(V=-Hkk3<2-+|twpyLay;qDb!{9D>G%24d}J&gg?wI$Z=;W)G%2Gn5y4KPS$67cY&-y((44cA^nhHGnfHu?HTe4t)UQKXqj}92JXcL;IX?Oc#z5= -zgDI+^1_HFRbI&kF3-@b}KBnyQrUjyU3za3}<(f^4N@Sy)XNi{{6a)d=1keyAj0A>A -z`~1~MzCmNu?Zkut$bH~Fa2HLb;o)yS2G?D6B_-XcoD<;X!l@LvC4ch(A|ycZSd>^j -zL~=_bBPLh3)Aw`V#xvwlNjlTku-|&?&Gh%H4?INT)H^S^gf#zXYQ<+mTYVtl9=}!> -zPbdea84>%#v0z^hJ@^piIe6zK7ir2)d4FD=_>S9eg}c6dFIW@&_rTx)ba!^C3fig! -zxue7zH##!Qs+xdXO+SFgzV}4du`9F6llcRsY$}rl+zR0SXcHc)tAmO0aUrMM$7Q9} -z`yPCVu0eT{c5j0tt)auE1vt`XVRUlHB -z$`JYGJ?+w2+?fVHIA=HBbOXHOl6TU)p{T@7vwXgps->q_ov|F}@Pi6Te0IDC_Je&Q -z@}s4IBa)@sGinnc0*yofWBVr}6J$N<9~@M-veXw#mA~eb0OrP{_HjvIr<=4NPWX@UCsI)@#0 -zd}0DV^sgVM-Gwo}qX$W2pHT>yN@tX9pD%ho4FQ956H>m%Yt!TuB=!_>E-hG8Sz<{N -z1|Z_W<$U}bFgSOBM8~epCWuVNR5b|id+1>jM+(`KL2jb@rwC~8=z@;UE|Rq+$Q>4u -zH0hGyo25&5`jb+uHC+a{Ufe`_aUbUdyt2sVL&iYxv#o=htqdGR7m1Zs{8m$ -zuH@JgPb3b6Y1MG`omh!5hQMr3wzs&!D(Av^0Ned8uF=0IrR2D4? -zsZ5%7Hd%?-G6lv<3DuAs58fe3|; -zIMVS+BCqi7Rl7F!iz&)7RwgGMP-!{@vZL76P+!jp4%6>t|CRRzp%cJoLyiF-AruVI -zd$zz*1L8EpVEtibu+$7CUo+_`=4w+0;n%Jy7Ah0@^Q -zFc`)glokpJfV9~wKYk_`Bsg|#^p8g~6)FiadT`4dZ>WWDiZ}rGUlch|u3flbJ}L9i -z4&Kz%1U$r_k?9c$2zmS&Twj1}Ead=@KCcYThLwh_TJ}SU-&y!*Y3D?94AdDL8znbg -zG?6yBl#!AfP(2Ie#OJ~1!010Tr=mt5>{jw>$1(0Xm?5OXIYJM=26BfEWGM~#4lcYT -zQFIVI8O9%|J7~{qX^PLRd2s<|1x6z9```U8?~I;!^2s@oAB6qf5T)NI1sGK2UOeVv -zif{mhCID^O|Mu~Jh0lNC3*=OS=Bq}L6E2qkHKaafZ~P(<9Kg$t=aT;Rwp7>2#0k!;Ou2O@sY3>!tBBRqHOrI_`M3Fn=A9>tI!d*l&NPnljcncm~hYu0aokN@jO -ziFN+qhyR88VQeuWKRUYrfRgp)Ju{gUvBBtSjL&oY(MLfj7*d^k-3L5gR1uq9PGH0d -zS+Hl@Hkh+;5h*Rl$0L+J3&nwaPCYXGm*zPVg+Kq{_mIQ@pCW|rp4oF@czA?3h>rGl -zr8KmOfa8hzd6SE6*#Gyx`#or=Zy*=sT+glBA#%+LW(AH;jOHD1V{;pWP<`$Ol6#bT -z4aa>g8(9VRGfCAHp|`k?h-SJ3TZ+p9Wp!Z6$Z7LfmPQ;bOXvzD7bx1{rofmWM3xh; -zYRnoSL;z1TD0nfl@Spg%e}&I}?(@Xr#^P~k!K^2S(`P`k_PCI7zD_*x1UUYvBXg#Z -z^1Ac#C5!2Ugy56^{t5V(kNqp{j7yg+A$GT|t(Dqw?K&qw*uSg?aQv~y!HLHn3@83Q9Jf`^0n*Cghyh4yaS@?L{)FRBfRm3qwrpAGz@itqTB$rNM%q{LlZW -zG0DIAo&$9S0YTT15De_v4&5{609eC14agz_r0f&K6LayWY70{Qn(d>lUY>Cf<}KA~`9(y!m-2#D$@ME;YGKZcu} -zq=wMlr>$@X2WO`>S-zMTn)uA8KM5cH$VYivs_;Ri4- -zqdLqjY}o7`=?&y>%h|#;+`=q5DcJ+StTb@}D93knv_o^Ei3ork1>b3Hh2Bqo{9~u( -zMVaq%-;W{^1Dp`>ds@)HYa8^;oC7c{WmnF+A`u|Ow}dw!je#d=LBh_>rum;uIzi=w -z=U-j_O#T)Hrf;${0*{I2ki3t+`IdCn -z$-qe`ArcA&oa`)zMNV{ -z=?|-z&I;E8`cec89fm0^e`kNW}-gh4-;(!X;I)8Tm-G{cCRny~ -zDYUh=@Nwc8&wN`=iLPXYT+hWs$R^@@^K7%tW!H%cBGuJ$sd-0j2%C7$W$od+c15P;lD35z6 -zH`4Tw4F>rE -z7&VttTf(oO`2)Q5oYVaAPQ@Z2h71%I3HU`(;a5KsCh_dJ=iYzADwb52A1Ai!umvH&jwb{i5 -zag_NH_cILJ&j67>B{B?=ORJXmkaoq437`7h=UgthdeDbA=s`H32%XKoQ?_1_DD?cd -zzg-041Vzq#OUd0J6^#}bA@De+M;jd-r2rf~y3XsPqVe*y(Veh}jtP;2q9`52OT=Bs -zyn+Ug43yEFt8cr7I01C_LfwF)UvS>5DuDs+pdPosN(mxDbwI-#6>MaiE|ngs29=8% -z0nj!(6)9!soa-MPrWD!3LqmKQWL#^FGB!^sZ9p=W78#?tVvC~rs29WTPu<55!FJ-W -z3P_afo_>EjXc4sQ=zno8B~pT6-{gS^me}=86>y{+lqgI%2pQ?9k);ci2MpI=CnzGI -z9W{j;?lC8vXeaO#>B;R-+}sNRiB>!jC-1#{V3XEjz~cIcJgtcXFnIVeg(edbU?TId -z0k_|FYf-^T)!+r(N?|7=l@=&mEwKF(z{YlM@UvnaP>PC4RM -z84txZ3~5}(47`R45aAhq05T{*v+VvH-#rm5KM~Nc!l@;$Lqr}*xmr3TN$VOpB#Gdn -zAjS<~P?97;E~|qrZ#u`8FB%Xu0?lm!8?b~wxSAEd@ce7l1wZI>o -zI7S4*zpTpTKq;{R2xq?K)Qa!vM;~smUF>TIO0vrZMXIe-QUutLaxlZa?gBh1hpwZS -zvgc%gbX(H(4ndJ-JY{Ae7!2_I1G-aB8O2NraRBx%%cJs4u|v|l$jM=LL@~3nk*`{c -zHGOPe;tM$3kQ|Jx3V(5rO(tupDonBsod`}f?!}lL*H9x+k{ctI`r_fM%7{2nw|$5yw<* -z+!jnzRRt2m@W5Tm^4eHu{gnHGHn3` -zWKoo;)-EsMqH9G@%q6mkza(-p%h`?40Wd8`gu1K-l>jyeP+&gL;bvf9h--`k0qE}P -zfad0AQOJWo8OpvG@yB;sOL+z6cKOS(4HW}+94j}gNhc(M@I3Pn)0A!;>CrrB2?r%q -z5@H^F&|k7PK5ride#8VDYi%UsUVNtfn@K}78lj*c?81iBjSckgNIai9dSUfbhMT!99<2;{Uds{mI4AXbHkR6$NT&6`yyatDcD0hHg -zK+%M9|8wON)W}rlh-$#Ta0IHxy^MT$CiW3{R|obW -z04onVkdA%%uK$96{J=lMmaSWfr9!`QEMBo>@gkL=1H@uVq__g`Lm&GneC|`9u8J6d -znTc*%y&76uT1nw12>=qFwD1QoDIrG;wrtq~^^M%TcG+c@!bGX(b*MN)O6uSoc;%9RK0o-)cjqtz&UnR7~#H)%6@Z_xvp_~)>@g1Ca%3Fv?BjLo|-Qm!L(Vg`T -z5DLX$aBzq?0VI&WdHQLZQ|S}nb^l4SUrd9~z&QZ7W-^b)b>%_J=_BEl*Ve#2U%m_e -z`GX%JVt~Hy;ZRVd -z(4g$xxFch2KWv9VL{@_ZL&GD`(cV_kJlR$m4C)C`YyiF^bTw9Fw155dFC6a6 -zy4$kX98??jg)4AX?p3p{z!d-zgXK$?Kqwp}_BEM^^THs4fP|1Or>wLFeBeVLpWul*rm|i+(NMdlh0u}2@H@l#O|nYLZG9H_fk+Og_FHQmTPq3HSprG9GE6_|N_N_aS2IKxB3 -zuyNC77#ka>>rojf7at~;2vVxB0KESLAL7w)(MWkOO!Rr%ogbink?X+w=#~e(H}_by -z$o`L3f|Bmz@aOhs25j@4hfh8_OW`vIL+Zo*?G2#>3=vYHg&-D>0f^+Byh4yc#1r%K -z<1<7eQS!eZ8Xk4{;Csgh*vD~>^>Y_qbP>0Ngo4o6SWok!TLQ>SGLxq4%j1yrWHqm4%&&fuQ{o0enuuR%v3x5=YalglA1RzNryBBc_V(=quc&w#@nh442z;7CNk -z6?fz6+v(f`_udQ9iTp$Wp8 -zw*C@;kIf1Wz?GcC~r(mJp -zpple=UgN{{RTr+KJOfC0kqBIT*`+pd)T0okKZ7Zv7r}zZzw=%4S;(ZlU4JLhvC{$7 -z%%=0fJ8y@dJoyXf{i@^%NCctAQW|(#0gyFK?M^}8j9DIMb%PH#Gev|ge(|COv~+ED -zoG0Iqzd=j_)}U)>2tqoQf`7Q_<}3vsY4H=73*R1I#$u#TK)ICH0br^<2#Y|_(vn;< -z?&yOeg0ep7E?JIT_x3X}uvfVU!>?^0w;CAij^iS*#8U?RCa7$V12BmkyNJr&?c91^>BTEzF)g#$d#ptvjL -z9SGP6kGnoDe(Foq7)&HHu=5J)P30V?h&gwDBM;RN~tZCBrE$KS&=V5GeSOW78d43NAbbaO*8MscVPH^5BHA -zk>P!y$O=knM>+z7iEAuMHo`dtLuvPwvMZ^7 -zPbq@HM3rFlKd1}w;+EIka1&R4xl2X^gJ>iIdv|UZWzO9cI-3aSLav! -zR62So0#V!<8ycYWaFeS9GzxqQw*k-J#q_PU2l-pO4obK_uWo7IfP(LGU5a4txkNJLaw795Yo6Df6$Q4mtbMmT{W -z*d`>10wTX;`(;`(8bg3DXbk&rs1N7V3?DFmA>{PlC2Xd-cOaO7UWLe?xfd70YTe!CgtX^_CkQp=nTDwWO3Nng(rvA6#Likc}=j!W1kW8i_ -z91g0w|0$76Pg)D`nqV;C5E&#f;qpEgEhO^ef|cyQ%m;{n>%&2aM3Xt61oH!S_VmI0 -z1&iRhXa5Lo?dW=YXznK$e(;uwmX09p;+U4a5KxJA%oNn$Pc1R$K4UBk;cenL$vXup -zX9JY*Ri-O<=S#qytiG`YI=gyd^TxH%5RY>uPjyuQ4&ptfA?I$g??6QZPK-~GyxG~= -z1!EHvP~Y4NwgOxhQ?v^+%9@90r{3E?NIwFil1xKGqap$+p#!jlXC5Z$z!C6gRkTAOImmIKG@z -z47b#XW4|Ngl-lw{ -zG6`d2V?+ROz8V{yJFw0P$jCfY0D46c=Dt^H*P@OAm5ms9%uJS-okx8S`g?Tu&8A`_ -zzyHnu!KyD{k7NOwbx#UiLo&X)kQG?<$B}{l8F>){<#Rj -zuL%bY;s}s%y!`i9;Q1F`BKbXm0XC^LCm>pkE6V>ogOLeWUUe1s1z@r$q_&$#rw-tM -z4_tXDTz~Uw_|Xr(Pa=WKK%j~gAo8Pz9SLA@z_smYYU>{uq^inTy*Cnxi4a?Nh%Te& -znwW@CJV1Z{APfx-=WJihwdh*xeXZ1s@UY*O=H@K_e=optV^>^rE&S@|Kcn>O)q-+7 -zMr-m@tA#<(PCjGCOnT|KqqTbi{yoG6VIY07PR6QVQO5 -z(=9}>uDa%GShs#1%$+xn!d4L>+uNFHhfXHZDTv$puejn0IRDHuN{R*8wsk8Uar80J -z+dG5&2$D%#gg^or3Uh8EmPkS{TvwFhOCsCW4eMdn`~_NtUw$o`5xE$XQ4x#il!I== -z+jneNQH12Si-ANQMMXj5pwPO2q1t#YT5~!(I_X?fQ-TVAI`a`o9|#M6{KNl8qFh=i -z0Sm$A&2PYpgAak3vu4r#kU;wlG%sexml%?L2^%+TfQ5?|*3_7Y0Ce~ELVJ4)43CWQ -z`syWmT^)MxO4=pA^UX)$Jy%{$yD`e{qvI1~7Q`@J>~3Ii2yS1!8s2u^+3?z$weXFv -zKLR)1d@C4K)7>J4ABvp;D(%h9esv)OV(}zkO^tWI>pk%H^Ur}-Uta@{Jp6UI?tRx2 -zOU;ZRELpq|BGCydYt`A=L0rd`S6l@boPB1=fk230M;vtwi6?0hv!K8TQrRqu)+<5d -zO*U+b_5;}Y#%7p_L||xG$=a!Ov*1$DC*KmqK`i*yQ%{kFqVIrtTsJ^PJsY91K_dWS -zcr+f|;Td;f(ytw1BXli_PmT5UkZf)uF$60Vlfl8V;D=8$<4rfgx(%D5vA&LM@e3Es -zhZ}F;+T{z+KZpKef)U)AT3VVQk;teW5_f$jstut?fO0)9=CQE|y!Dim=p0^)JLNaN -z{xB{2>uv=V-pc|wAcimsGYacJWLoB -zZ96w_f?4wy5@&=l66WYQ5lZw8NMw>QGBOIkeDX;?e#T(umd&vEz=Mc8GTq5crMF+k -zq@5DsJCA*fa|w9u8=GMM(&aEB;s{t*7jan4%@z>=e25=>|NB`=$>KO0t}7TA7${?g -zi-S#@N~_g34aI|~3*g-C4OX7l)!_%PqU*l)&_fPo6S}6p?VPhnWA3<*Velj!8I)Hi -zAY5lV#iWE1FP=!!A|_KF)f(XAI_%(8^m*~+S3K``(FNy~y$p;hX`HDvSUQawPQeKv -zVlE^3IT2@6s}IGTpa0~i!e!bJ&Qk{Mf~d&j{tmpFZ?;0u61tW&aVQuUfMOla9%P!{-Gu2*mZk{c)lC|jclB}HC1C^GvP -zkTwGl@?c9#xt|}9ZMHK-U{Y~FG4{eI0v#T=tY7Oe4cS?DELFFkN_UIz7^s6G!dThY -zooJM2ur)-kffU5zQPPhn|BH<2c|Wf3D~|y1TmjXMa5?X)eNc+$y79(fq9yy|LL -zw=Mv)=gc9GLfnBdj~?m|+FRQQ0j|F0TDaisvqWsV-H$z#9OJWOq8rmr$mkAhvtyeC -z7%DrC<3>J#&Iv${-gPZ@MwD2am>7p-GC?sHj4CfTsoME6fhYM*1Ps#TOMe2?C!{Iv -z!25pt#P6U4z~|nvZ5t^@r!t_U9Qw=D5;!;j=K7J-|5-ck5+1|>?;XJzX=lJ;ha60Z -z_SiQcg{!Z5FJ%=%n>|Ieg@RxNgRpe*Lb&CYTj9cU&Q|g~cCm5&8koOmi7Ho>b9~zU -zydN|VerMSLv$O6{{(&;wUs*ub;`Ihg<2FnoXTPyw9n7A$h@y%pst_HZkiZ2Dp1`xd -zf!h7(2R|fT2$}-%+R>o_Xzl2vd;__loM(VZiUv$35)g|_r5U~Ju#503fWE{fv^^`tsss`@!>`&!!TD1i93^kTN -zd7pCt<-qP;JK&U4Pv>>?CwWn1DFIEZtQv!*01M+u7MFv9uOx`*h5!AJQk%Gh~}nd$|X1r -z)8Ga{0Q59pP8b^-kRdkPTZEK?DCKTXpC*lKe` -ztdXHX`nhw*Ht6c=hICUi&+uCVFvbA(?9L9Q^$T8l@kLm)Xc4q`WZRwfdoqy{eN^nO -zsRZu0^_26lmJrS>?AR~(!!y5!gI277?w;(Nh|^H};M}ShPZu4FL?8y-UWw+HU^hTy -ze3X)J)Dj?)?b)*nX3m`L>!_%>NjeQnmo4)o0-)LHG+T2(b7P|a*ZlL+0t5ZkiwBs7>VV1$ -z>{SPV^45iTRhwFsAu)g9(C{$Ko;e$0K8B*&L;#2rkdO=cnbak)$*-MPEFb|u&IX;V -z(Aml_(1`$Y0=|!1SX!!KZTb9{@5ws$Pq(l3{XQU`qt;je!g@0YyNdEH5&#)tR*Qi7 -z^>Cckm{>^!)~{RFSRf8y -zZ~tD;=D3q#W}nS(Y=U@=$^SYaQf}Y5mC}e;h5gpTQ5);M+-wzCTbkbpVo}t8frY231zWXQKAF!@L5a%&(u$)VBGBJHB8}LblK3 -z@9DXknn0#xM=;p_<aFUeK2?4T=>j? -ze1e|lki(9IdGi-QM`uUHPxthbKc#e9vu4kEQ_NLYR~Pinm_haS{Df+90F*S3oR0E!3oQFgs^3R$*OQrHg7#SW?QHg#K -z0nEbO(AY@k)~PpF86Ib87=-*-A!%edJBv~+L!pXJZ+0L8z!f;)3lT^qlhD}MRCD%D -z9kKJl<_+t~bFQzq4-Py0Fc=;lc9^%YAja^Z{ksw(fb)(e!fK7{)Db%$y!5vhC{(v^ -z#!OhYd^wDdjZxY$-=T^e04b1NpjT4%6D+HS{Qx)-7$qXGcaOJHuoyVzgp;7NyQ}6L -zVQ?{T*|e!(l;fhM%ixT&&VfC9c2kJ0mu*`TfvsD&P#W}d;rzE>1idq63O2NM01Je? -z`uCR#&edT@90liIa3Q?0c_YN4vFadyfzoD`evkm*Cnf$$f>4$Fxl2LU063I@k`vgv -zb!$ajz_#r>YU&{+;M8}tw^6SAe1M4?Fw}VW&K<(Em#r+5M1U#_b#<0Krky*s)8CrQ -z$io2IJ30!o^q7SRIX--j`i6!n7juBz-hQU{bAX5iuooW&v|#@AmtLIu_FOlhJM(kT -zK1T^N^8sec#nR=wcI||@^A}R`4GB7H7>`D%V(yyP*A}Ggg@d0;X;Eq@Rq5C4-P;dq -zUVRltM#u8@g?=Ka!`QZU3mmj+6~wVzn9oqnd;lpg^R`ufhNOHjoey9NL32wBvG+@t -z*EyZRBghW)Y2ng>GR*z#Wpx}1I~to-hp~4`ZWwIxeXrH -zKLlH4p>%5;)uvs}rs6@VDa8miLFn&*%Fg|U_?K_)bHBWHV1*e=Vqh;@TZ@0y<05Oa -z4M#`Dh|;I}ZD`dFe<%hESVFN506BKJ8yn(E5U@P^U#JbVSifF@?7zjmLMQEPG&{X= -zr=;KpipTIG986|P2mMng6t9-*4XLl{&ERt~+#gX3nxp;1!6(V8EVCnBAgO>=yUET? -zEANMbbSHDJ-#-3)WyZl%YnZ9!Xi3(x?eMTVGnlH83vs<)n0h7NcR4dc#U7h1E -zCAe}hL8Mfr__xnfa~{|jgN!!qI`pwR!A?3}z~aH)S4&Bwlu~ZUG_HcCnt3nQ*o+`$ -zSgp~9&4veNxWz?L>MU9h>Dlh~e3_s@c@dc_ZEYPvu7SLQ&?lp8#lPz|?FSR8zWeFb -z<+?Wax67scFE+;mG&DwmL(QSAcHQLWwiX!okTc8gLst8@BW&R_1|{_89ffFskB+;4 -z-|_!NTt+}?B5dl}_+ubdLk2O%iN+_7#@|}f_2^(u%vmz}RqlzsYf>lfsoYW1;^H9% -zli|z3X>9Fis#Pb$7vHCIm(0I~1{8A(i@r%g;;kkVunl%gb*+Mu$xZ^V74Cni{s!o>__c$j596g0?Si -zE{h>_5EiA|S*NC4#S870HEpSl6tPP{f;1gNFKeiuV>`jT4>Yt3@GyP0)AOsap1*8E -z13f&*w5MF$@V^2_LGlcLHvygMccCl3?#)vy -z?8`WR9_v&kZSZ4oaqLd@e2TD1tETV~f-TrNj!lO9sbc>XsmOZR&rxu4a(2jK3+lr= -zejl0-14=yg)DYsFd}o|>#?MHxsWLXu4{gTIkAr(woI>&Edle(2_a3mLy6APoqUbMT -z*ZHk*K$jj`KdxewQLu@Qs1?T@PBrq@&xzaw$M>&wyORk9Ke^m}0*qOuJyE2ZjdqK! -zo_Hv9m{Ba=cGqQqRr^EecbdZtX>8LB2G_x- -zZ~B#Ch+doQr)l57R5rSW_Y+sWZ@)9(M0fWv0ysNNU@*5ekfv@Aw*ZXD$LCSupXS!q -zLH-}@tX31cVc(S^_qF|AdUmoR67u+QgKEOzWhgQBm_jb*_gkQ_nYivs -zKury7wWKz1pR1i|#!h@A1;xzX8uRfYcXm0LVmoR@v%Mgzs|EVE`}U|(13k4V89#>Hc&}lf6(~vZ@=7W(9@{Cz8?iDe`JPK$MyQ<%!(U02^(VWd;{No -zG(^hD)u`0w@f{ne{v$=zQyPcN*;_9Y+_dqJcTA&hI~{pMi|d(Ocilt;eIF!&@1D?W -z#Ud-_-V$`R-9#YD)Wbyl&hR6XU0hKu!SC1^yQIoiDu?a>A+nVk5$fv#Ffuy&dw2B1 -z!!O6uE>0Ew&jnX=h|1O*K}-R5ydVIiPHaUMpA~i@MvR&{`WQWGW&4~Gue@zd|I5>E -zQ>?gltF0iTPD{|i(F4Ca2F}%f>!p8qI2_Z!K@s&*jTKIiIN4DpIa$|{G*X&&og3Tp -z1J167uvQV1r+9<+bCXKm{?*g2!Kcv-nM$X+Q`guYx#Lme-0LN9)kU%xR{@BRp-k{X#`9Y -z@1S&OuHArj>B?`3@)WZnGnhKYzhbiV#e2W|iKrwcLK+Rns9@P?5TlGU!Xyl=tqH+l -zCK*OnEg0L@OpE}}Fp97#?=ZBp4d`tH+!i`S5XNr(rJ`d?gx#a2{faSL?tFaS*v2}) -z;dWjjrZ}4rs6Nw#3mJE@l}!Q9%%b=^6{rgnAAp5qBFrMq$YX57*O@v&(V|i~4*J3g -z$WwT@Ev6_{WLOoQbtZO=tjB%_lmZ2Ah6~^a2=9J|Sji%5i@@L}!K3=Tr*}bMw_To; -zmGyu~Z$iagFp)Tg*tS)wmhp4TT~A6f{;^D2ngJuDVnCxYP-LX`bdY#W3ieR6`0^4I -z&TY-`28S2x5foFGxu*~Ui)pPDejkGTB^kUwMTv9KO#hOPmgv?Be5u+Q!Pb>>nF;W; -z4^q2bepH7bZZ?vg=JEWPzwfZrc5(##sE7CsZOg`*IF5nC>Vv8&-?XcZ1_#q6pI#v7 -zvtp$pBP3YGg8n*m_&Zxl%>LD;44&#zYTAz%f*C(H8UQcq`giK#P)yQ6 -zDrF)_&+pOhr@>%?Q3r -z0{M%@gN&&>91K!vTHqfvq^lE7XU0r_5muef0J&f=%{RQ -z67NVQtIM+>e}7>5kVW#rNpN!yDgDaXzhCl?nGfl -zP)YEQYu?#2T%=@6^;W1fP<$8(^bv*Y$-NU5I<))_iW)p;Yqf*Vk?Oy2UM3lP#kq3P -zkl^rjI1JV= -z8!Ouy+(OCRCNKP0UKyxfH_sVRHGt_&ekH)O;{~mzyHWR3C`>+*A6`_nr*E=955)Tv -z+e=p#lMjV``<8}t_p4}Bzy$rTO4hK<53A)mWeOCSagd`FZ6F0%R}g_n5Wt|G66$dc -z5!@Facm1P;eLw@X%*xG;K$GEx5NY6;OwJ^TP=1o$P-+1!!uxSVQG2OH#UMf^=9jU= -z97m_!Q&h2ftEUXYEQDg*nGVtqzeC%GQ*?)5FDjKv2PIlKOcUU14;K(fFhXRYqtH)>JB*C745ldb)m^5Y(vL2GePv=+`tTEChfEQ51qbd`^8tde!*XoDA&1_{ -zpeqc~7BS)Q6EZ?OV3EFr5_T)_tc$!r%z<)PMA=gV^gJKBwv;SVyV{iG8Y#Yf~jFOsWiA}RI4F~AA4w6Trbn2+?^4T=oOnHKzo?|wE -z^aaG4vSZ8{JL -zvlMi=DkdX&6SueuHa@8fltq)v{dfl2np0;47iqKzEn@3PxuXDHICB{u5@%``V&FVE -z>{ck;)|m(y&+z@w0W1XJvSeoy7=4UU{>n!1TxbU%7lvF-Lo)(iuLmiycKw~Mrai3+ -z(+ZjNIqobz?|i!zrkm^;V22a!&;`6`9}00L^=6FBJ`fPBfBV)cVFLNV -z?*BK8JII(*cqBbvQGi(p6EVSzv>#K3#G`Twfwd@>Bspd6olmpwb@y2U9VAQ(Cmk}{ -zVYF^4Gj4|#CtJjRm7V=b?f{?tG!!ZWA0jVptb%wnvCL^%bWZy+qTV+-6xX7oOYUto -z;yzojx7n!}W1MZYCYQrxY6W#!ne-Eu5N8R|H4JM -z<)t`DQ=>Sj`;6*U94iUk?h`e(y8=+>CIb3|L6Xfdl5e0rvX!^8oQ -z!6p|jzt?;mGzK5g6Lw<_gbOiPVf=@le$l5#Ak{mfr! -zhyWKCkswEo86PuR(hx%bpr`1CGR}WbxqzBS6K1yNaKzUxN&*SO6eO2@Bk)^w&ziD< -zR6L}cy;Tz)`!RCxPr}9+YB3Q?jy&3Obb`4E6sHVaj8nuj+(qYSSZT<9228Sg~S5sJ={cSMO5Ga%f&P?QL2Cemq{4ri(rJSGUq<`d3$ouM*@;;e`_sA7`3?DQAR}hmB@NHU4Uq -z=Vkv0qzqZcdNc&NPNli~Kfr(^>)PM!_-gZhV8f(6==^J)V -zF`(bj(0qQkLCMLmpAcBg!Nxar50@Ew$Uh@Nz!&>7*&6_|c&wTqkm9)jru=2=aQ2NQIYb)Dg(cFP+x(kPr&EE~?DaF!3(x?-*} -zP?MB=zOtS>(Li22yxM7U!<*f7!rovXUIzkpYdX@RNJuadXls!>ioE-0PIw(cY -z%m{+H;|U!fGRb-%AUWFbXOss1drE#BQs{)`4=hklil*NPDanp8Q*4vc#-heu*T%d*gBk>a88Pu5g)8OU7{#j{eT9#o -zLog{EA^;tx{3|8`Yd0Glas#WrtQLu0$Ti`=r=gQuPpYjMIhI0)Y=;v%gzHg!;Zf}0 -zlx`;gXAOgO#}rq76K5iCP8vYsi%!);*(g+{Q?!alSJF=-i9YGd@K$A=ptZ24;``72 -zKZ@Vuv8nS4%Oi*L8|uu)$B&jLl~5r0kY9;QHO9W}HA(vd>LzE&?#_BSmmrS!A^vE4 -ziI)j>xj?+NVxlVH;VM#+{rLvkF;+rb_|<)V$vN++i>{ulxVILz%4Ht$jr18|0Mj -z;dMIhE_QExV0yMmYp$O(Og%y8tRhr&Ot9OeeI=E?X;$|b@Z|Iw9fx1VrZ~Q(zY*!@ -zg{BKMaXn6uY#9~oyVmPz$OCW4gbrYz<|ln8#(f3oAlH|tK*FOpTaK6W-W+$@&lgss -ze&&n_>D)E}_L;&(D_?Uua*4s_AvM%`0d8SZX|dQfc6&CF?apH)i{0?^T5u4D>f_gnCRy3NGR4XQ_VlVsc-<7&gIYM=i@=Y9Qpy^FLFPbt8tvtozy@rOWL -z(JKph7B$s3n|C>|{^VXhH4jutl?_weXqwib5wg;VvB&w{pVF(CKLfYzm*NB(k_Hc8 -zRgr(26jw>0={g@xhyD~eCNMBbF%p$6NtO{s!}1H~O~KwY-MOaK^`2K#GrS-5*LHv! -znLIg^@;dN;N}P}`^d3GMjp+N9-9-eiGirPb>Bi{yyL9U^VkRIz+da)HWB6Qy{;tmm -z9|i+$U?r?*~%VJ}qQgSz_p$63oV) -z>3#k*;Yd4r#TP!em#l*vT)0F*W;;gszn}EtAdQ -z0l67rEnLd|FJGDYuiUW!IEts#d$vbhG=gZrm*6EZ!y^7w&~`#jlo}({s+)wDTz*8o03zUzH>23oHyV@(Tqy!UD#;!A)W7w#zZ>bNVf@DOZ -zh3J0J(PAqGq>4)B*i3j{)-O)iA7*^0UlL^>`=-2BmsK#dO}-1Bw$%!U^J?+8TGi*9 -z=7FpI_yni2qdyRqEV`XA{KUdOqb -z-UMi12)J#?P2vX;WBU0}ItSF@e6G_syS422`llmE__2QIG!mm|w-nIt+8f|uXn87K -zvzbi*3;dH&9FwjJ)zoZ6Jf5zrY&CS|UUqG>C$3CLn+Q!AeJRm#I$r3)Xm+kTPB#W! -z-#ESn8ju#dSo2pgP>X!#HD7vI?7Iv|9OBgN@J@Al;(%+dd|E#Zn0eB5SIG{|TRc7B -z$lim*$~z?9A~fI84XsUZwO3ZWJ7I%^M>EwKc}ocXVKz4wQ@}~T>>okc$_O$Tmn-g8q1)n-6vDR-eR+Cc7#w};V?1iicR^&}>ZlX#Pdcc)y -zb|7^;KRs@jj&b0^}Q2t2yJ6r0zRS1Z@|(SpA`-311NSQl|zQE%|7c`c~xiCWCY -z@@m{4^L_);76kr;goMC+RSn2cE@`buCILi9nm-hne?{g0V9EAyR8H{x;kPrfGzH*n -zbWz%h{Xj|tVE9l-btwd8UPd){2)<-qny%lUxauFYplk&h$K>s50?m5|6oL%?WwzR} -zW|&@&5D}O&-8hv$VEKAqQ@Pv?>%YTH2<~#a-QU-9$MMiJEFVInK=^^Yz~&|t?HY8W -zYpA2cuaMV6Fo6>6&=?rs&5Nr7WiZQb2etMqMyZ9SASy} -zF02LIBZr=23vCj}L$-N8HyD^h%|5J?@3lJ(^T`q1qBWklRZ(e3Tp8OQBV1KnEUl$& -zGAsC@Qhrz=_eRX^o7wnT%O0=?e)P)liY~x%ivs&oxW0S4=E^ouNjUYoStoptjo`=40*r?@wTsCQ~@9jO9y(fmz^mIa?lT%xU -z!)>RFh~nc -zIUUVd*3eCbCOA}hX3f#?%OUcmg%r?=oQ1~eH}MzUY-H>=$e-8t#;gZmn$g$ -z7e{%cXNt^(z=k7mcVwJpw&8l87`^#7%U*!!^_=7b>Xh4>w9m0IZqYD%^6glQcRy(N -ztOoZRMpL#i15~@w;pXZls!oG#AmXIpDxad1>47^7DOkSMZw_vH-`%W#$?^;NnM_&{ -zzRFy)=AOkC!R>iBZC6}9b36Hk<1l)%ZsK2))}1Hs<0pznuR2@awl -z$6=RgvOa?T%b9P_^JNnp{s<=g*-PlgUhpcZhv(_1JhwVEI?2Vr9xZ25U2@SDg -z=e4$VezwzHjiLC2(>#8<2FcA^fVR=|D&6wH&@E_vx -zl;H#)R`1+j1Eaxa9YLDV8|m(0g0wl~W*YuK=;I>4QWwr3w}tLpW7~g$j-Xx -znWs)^O?&D3|A7}lWa3V?C4LCpV_L-wD{Ix(!|3F*c6`rJE(ZgrmHzsl7pQmM=P&mN -zc-Kv>$XSaqqfV#-Z^%2?CY=}S9cjk1+tR|`=o`-sw_E+4oe-hKZSfP<8~?CS_)QW8 -zlOQr=_(a-W{yz#*V)y%wtIOly`z@q>-}gZgJ@59fRW_Z$q-rz$)q7v#;xXlcKOR{P -znz$yV6fRzV*l&%f32>;SE~>l_3Xq_U5m381H1--lHSc>bcHa~VXu*)zw?-{GwA($e -zvO=IxmOU(8@nj7W?H4BNQ>l;=4;l}FZArS|{ZrKrQk-Jq%gZc)f}%I}>zDJ_usbmS -zr*z8p52`?D;D4V}*;+XL^w-{e -zoa&8sx_PSdd5whCse0U1Z&P!%b~_6v`DD?3c|e^1nV-G-G?0{Gd>aJ-XT{HZA39^G -z(Bp?<9)D+rSo)FKoCox~_(DaS5_LXyS4|Ikzcmn4{gqogL|Z7=NnT%=Z~W+>F}6hT|(P(Z;UASEOq` -zx5)Ne|4i@Jy^*uTrzVGu6+d4ZqZ`cH@0q~5Yvd!4>mSaBd=z2ML&3e~@|tHr+Z5_D -zA2K`p$R(z87iWjx^7-C#xs^=w#kBgRz8>sJYb~V3gJ(Z@YTM=%`6E1dKP%(BR?0;E+oDX)}KptzW7nYApo|C3G?}cN#nn}F2F)u%d -z!AySJ=4FR(jr$Bx6euy9F}@iS{D1hk@k68Epa%{JfU#SK%aHrnmmT$g^@Fz8)jK8h -z%$islVm#MI&kdTvFx)@cRlNh;Y#-rC*!gU>Nk3>esw>utl@BD;hu@K7A2GA-&&h>2 -zTq$BGMIS4OpCsT?*Z2?pT6Lm9w1x -zB3)e>jO)VEP(OExuCn#pMxE-i+Zp)P9uOG&$8p=hFl${tvrRmI(qQ68*?(>nQ0D}m -zD%UmBAh^YOQE_kaABrshA?xTB_rP_40sCVDvejLN; -zJjtnz=`5FToirV2@@CxhzZO*?T-Q8UBMlVD2#x2{%Y+4PJ$cRh5$H9)bF4WZ$`&%Mi8`HjcAoE{l~+Qf_dtw34U?Rh`haK2Q{6a$!a-=8nJzz1U|%;E{_G7$9T)$|3J -zwEK#QpH7mK2s`{MURv;Jvjug8Ll?bZ>fjpNSl>v5ExS{_F24HdIKb4Eip_3 -zijEK5Z#2h8VpBTp&E0T+PG%t2u#0_n@=u48847-*Kc*;KpVFfQ3@r!MLx9T~V5Nfq -z^K8M{$VYqo4UjJlSsN=yn>d|B1PU-JMbK>x;rAbe?~c4o4KDPQG?Tb8A_~lQ)mzQO3BsKr{6ywF5HA@geF0wC3KZhR}p -zFe77-7P?fviPyv~{^$?-! -zj3hp^YR$f<{e%Z=loKdWx>3pfv{DH035M6c%y&_)qbVl&55-``ONE5W`9vJwZ1s!I -zj|4hFqkw(MtikHW#wz3enMYxnsN8m%idjzH?cVlF+o9hZHvB(;E|TQf!P}y*u2d3$ -zL!#xDh=@}Sp!5I8lEtDs@Hh;YZ}wS*xM3(vn+O%b(}1!Depw};bg}$b=`CvJigUKS -zsxNUm45>S2XT1hM#AX?`D$=QQ#v`=v0$$yz2nV_`UWoW`X$XP*Z#R8FcM~b_@1Boy -zGpgTjhLiQ*2nfBFw%rd;swtxLGCxB%KbgV6-oIFqx|Am`pNor|%|wxoneF9hzu-Y` -zEu|=0wNP}W|I&ZzebCmc9lbF%&nQg^{41*l=DeR`$RPokmTjJgD26Me>PfQXB8kB* -zUq*>fCWuy#?Z$)kIs6`?qXQVhoaO&IlTDw0W9kHWPWM1lJ)Xlb5`CN@vh>vC-9#U1 -zQkMucm22z5nH*n%S84ITL|>*~@(;%KES*!oi?K<;S3(JdBkJlcGgjOHz1CknYtM)! -z_7PCt(vi{>s%);~r&gc7p8EI{!!E=R^V5T4cZB?l7ml(Q@uU0kqk0kBR44ZGt2bnH -z=yA9*5=60ndy3H{@DL1N8HXYa$*PeEL>WiFK|uaHgM%Z7icZuN*^yDDJSC_5UgUPh -z?3%oHamMGWsHjMy#`4th*r8EXTGmolR%Yf|_#AfdOQYT6Qcc18ah{{T#Pvo+nBqmD -z@Z>Y?bkVM>4D78$HfkLYb|5$JZF)K&u-}=ZvjnWN6xeh}tv!WHlPu`@yaH>vc%AN- -zxV}WVI1>{%duKt$YP2-vzM&p>gnrL?NGgY|CQwSv2&$jwYI^K&3DYd~ -zG#Lb}HZ3UM%0ZGOZ#T3wz#g`nLhHiDk?8#KJzPHABC_*ltm8?4G(JG?($f!~dE!;2$50|5OUCw~I$713b8&U})GhVwJ!BJN6RoJMR>ZT(8W1xce=I3XylC}m^Cz)>T=8j$|m>?iP6yP;^6 -z(<$U(3vcl-X>ar6!|7^*hInzv`a*#VbP{tKS1b)9H2~!J;ehZwAPWsgpMND@bKPpBpVPYTI -zneWrevBuWJ>SfR$8by$D{>P@3uUm^MF~u}VJFJ%N-)!u)Ks4i9?Lqc*mvrM78`Lb6 -zYE!-j^3Ma;Y~*@QSQqB;jnD;RFErfrl$-#vFx}I1wXdZ3SO(L!K0d?(`dvR=80r)8 -zt2|#P1>nCW7sbB!^n5^Ksz*?@RBkkZEWdcv<|@T9jYJc-6L?i`z1&26cPQaEaGdkp -z_*6aUaIw&~4`kmk)$46*SQXG(3HC-pX^pdGbB&`ou&mb86y -z_LnZb&-6y+_o8LmCpj)nW!KwP;yUl;ZtC%mrOhu~g3xkua^W2sqJ&eIyfEzk_##W9 -z`!c3%cAV+evzlr_LosZOCjJinO#S-Y%hn92nRj#!xt -zGf)EVJ&?UdVJ3$x(7c43gf>_@g5vsrK8*L&TP<617FD+K%c$wzu5{2}AIuR?AW#T5 -zLVXT2c4E}g#zZb7+xL1VR7rIj9n&tRr)Q-G8Z|?0e52r!<{%p>#M`bxM?-HLE-66) -zp9RpKPUP%6)}y1NJb!h3B!8ldQATl1x|SB-={yZwvl@;1?$`9X5(SU!aazwSaap)d -z^e*1K-(&?*%1=v`f*XX0wgv+U-^n?u&bbwn9=6^qV~G4~6nGszYO9Vfg+CiNmtCR_ -z>tHWgA@aP(thf?KDM)OiWrEn|djZZ{IhsKoM@s~Zn1-zzO1WLf#97u<3-24#+*6kG -zhAz)wiyGsB5p5s)q0TCoIciBBnh@lhf&8u)ln1O}ONw3?g?YrgL7yd@pOPF4BRN@+-Og2qKY1!za!B1O|ah4(k&oyC;x`IPmpCTAWyRkgM@52(v -z1#{dWL@wO$Hh{ffmBm-qX?%L6%7IkI7RE>SA<8ruJp2{dFAM*XJ@x -zmvM{`qZQJNSc`!*qvC#GAT(!?U!KZTr}wrE -zu69&vn!npG`&7JcubHWZ)Fb1Mf^IM3B;?;ca4oUWXD7WcZYkoHi~^PeS{XEBGs#jH -zf2ID_@w^fRti~H#U3I<;d?I>(7H0dhzdPZJd0t5*sWHZ=FmZOyL;ck}+1gMpzpS{; -zWw3E_!q{-zC0}ztJSSzmprn9gEg(?fl5tKwMI?eU3LwNF+(HtTFI0Fn2#TPVQ^xc7 -zLDDY=WEJFBd<@fbqb@2EANnoTsa>%!%O7yR5i4R;cD(yY3^{3axZwbY?0E!pFG#Y6 -zNzR0BRKkvF;KSTO3gZIX2)kxB63HLYuX0}h`S|HAMdG`EcN|^wNZ>*^Zk4~uk0Wb~ -zIuUE?4Pz}Cy=eT~@ofVKDOb?Vi!^q}&p45;*tW9Qf7Jv`_nfic?hAi9nb)s~vdKd> -zSuw&Wd7Ynbk_JhcNJU$eH4GA-$Yw#VDl-+$39ck1Kz#AKAg|M~mAfxw@x`-l1VoD# -zYW4NI>)t`Z$&SY%`kV!l$YZ%?VcVlR{eInE*_X37G6XfK9kE8-OV+)$GOISWXs8W9 -zl=F6|uZDm_tlb~)ze&rz3IOpLUKX*|dESHnaq_lT-`ANNQ -zq-0_z{Xys1pS8YX6^(JRuWT@y5QuT-I75hmxk8^&H-iCU&r!%0`ifw&xY#Kx9`SK1*7S?z -zLmxnl(2f0H0;GFae8d$ODt!@AA(uU|u)mQ{MQ)Slg{d-Ia#j&Pu(_D7Ewb#d6?CL? -z+2X>4Xr~|ODs`b0xhaNzBGV3QLO4^v%9Hjy*AU^XFjS{8Cc)MUkYs(>+tmyIQl5~1dXiI -z{v(Kr!d`2kz-|~^fHcvuJF6SLx+$yrm5ILU#IaR>aKA>p)&x(4J{R|#3>JfYzWPor -zzIY(lXGMPHzEDc8=ecNI+;9Y>FT#5x^};?Rt(K|}J6aR#>u>t;Y_=6N)wQkMqD^ksGZv`=N{3;7gbrS7V?RIRddJ2#MWbs3dx#`$BvaFysA8sx -z+`UPB80S1=&LHmceDDi_tDHQQDkRX+AoKck`i*@YTn-ui3D8H2Rr3JJ5=qK(Ng=o+@ll-=u**nK -znBlRcO6Jum?zBodPO&x3c;+DecI#P$lB_6{P4{5)Zg!}hk-HP`Js+Zh9y?19^saSM -zDr(pVTHDNTjU<+_7u1cms829w@g8;f*niT-U#N%Q`hX}9-}XPB%MxyBJb}N=-tWLq -zlgCR1E6avsMl3R5$nyLcCn)w0<6ZG1fr1#_k^*hIpI?$8WOa(MU*yECm;UML)B4xo3DsX%Q<|&XV|e@b07~2>39q>V>yJuRv=R@b1LI3`R9kz&y_+FEzJX1F(2%bA -z`Fe?(tlgvhY=N!}m6!N^8h9`AqwQC@owM>5WC-wv4oPxdOD2LAHmi?#QP6Ixqt8Y` -zL`hFB)&B921nbNN!EO(LC#f~LDt7LP`!-3=L!P$NAfO&x9P&0n%SC%(TPuoIFs)Q< -zuSbb3gQOJmD~h&!%M@vV9axh%(j_I6d*EcPQv&iidv|%OX~{^IgH<31hLbP|Qg20O -zir`0kNezQTA7~@8B=&(HQzS+kqaoFl!##y$shCf@`tkgOQlWm1gLN)IUh*p?_!vw? -zyN%~(1qeDIsTOn-Ho&V#s%s(1tmJTVh81aq -z9tYcdC?6RZglsNG7Qzg?`XSbO(dK`}yX%vR*7xHN=*6O25UwCK`&_vD^6i29h;SPG -zAHvZ^8q4#$cn`%_dHN*7`}>BcnMFc`wx^Tg@;Nj@!^~yN7$jUd7aPJvcqK3eR#0vK -zP@xQ8`=24+tveJlK)YyQrC(?o4nrDucZlF6^law5ZufT*DhL5{JW=03jXw&zGuqxhIm(Qx?(r$#8^;Os1yg3M;l3 -zqy=K#%Fp?!i0OB!N9=MEyvYN4m2277MnVyg-&D3L0b#rZ@u(Xet)^tSMvyN89vWSo -ztj>u8?}*5fdXWDP6d^-j_|gzg6D)N7D|w$rlCO2UWKdsX{}-x7q&A4NN^1r&LmhvQ -zfI-?GrSKKX6GLKgZBcL)UazN)?mFEmPfoWK@Lb=EtSF}T+-t!`T(&m&!#$UGJ -zAPocB{OV%n2yu`F$gJGOSP`=20_VO%5j8+fMKz@u9}$y26LaO_0v^DJ+rZp<*h^EW -zGeEzojm|(XEKrCYTZE#s3yvp7!sTIow>3c74=D*WWm|*X4I7hi*D$uNvFyoPwC}lv -zNog4S3D2{YbdhzYL%B%u?zjYXz|q6o{S_n=-S{MAmL&6eW;Dl*Gk^ -z&LIoB;P1rq08dan&%0;4r&a=8SI8lurXKWmq3aTwqBFAN)?A3jyMy3#DDG0 -zLXtAP2&e>go{Q^1byGjBN(Ma^5)5d_N|a|dc2l^beFY@_6C(eoqrGN^LcQ3#D?`Xg -z0ks@q^xL5^i5w!p*R>|OMwA0(>}a44-4Bpkg$P4H9Eui_{s;PzxK9|4=$)43{xu2k -zM2&F?f^mhssE6%zN4tcs{?aLhXT)2%x!B=83Wo<{K$#Hd^#(!lFC;)7b5Js%i>k?M -z;e9o}Y3n`*4jvti`vRH|$}un{d#lC&GpIMbFlZ{iTgHLM@`HOVv!?eY1pMn-=Hfvm -zcIN4zbpcx>)M}$@zIrIuH!&6(5>N8}VnKta>)OrndE8hnXwo3d`jVIoq!kb)JSNiK -zt!hwqVSqQUR|1lgx_xBaj0#!j5nVN9Z~PBPGO-#!*j^I~u@&cPOOl6N3E=G?2Mz3x -zKLPP?{t|D|NP>wi8JtNI_qjm7@V~LFLcm3g!zdk!LYS_{sdHk(E^CqR=bgDntlepA -zJTtE&7|Q*?K6f{3+q9e}Msodb7ndGHojo>W^`h5O;}N%^4=<#&Yj1s6ZOx+9Z+d~; -zkc$nu!Jq)r^+@wE@m3&4q*QI;dQ46}98*0D)^((|WNUWvd_2peU!;=6ha5-9=P2Y$ -zCjaIA?pAYh0rP83jCat-dd=WV7G#-xMaBK%WiHMdn=lr>g9BeB$IJ!fRrkfnR}laq9TO&c)oF1^BQV>0O#=H;ua;P~u(Yqa`RtdZ -zx}^jdoV}lfg1)ejUb$O!b~cBixSALkB3&k~|B-!6u_ghdsr(LkRSZ6F`Rf+?u8(oO -z%@dC?AJ@VPZ1jGtWvI^e2`3c6hxlsafs>dFc<+Ud7& -zo(L3#iZ^b8UfP|HjY?0!;NXYq?hp6P4+dYsUxkZvwxLAVlve -z7lUIj;Lb|c6x=19eofU53@=oa@gr!P_hOSwzr89xfHqr65_9BKk8whT3(C7xQrcE< -zbx~51H(UlzpMX*}SVMRKp#X~teaswz6CG@Wr -z-F7T2BhqtB%>;|ts0G6f&BSm)>8%b)Zi$di=8v`hD!@J3=?RELuC{i`{)w1!wMeSU -zs~yQ1h>(K@iD+Btfa<-k(f|o -zZMzDM5t)mZg?Q`^-wL!YIX=cN#+NiDumzTqo}tW2Y1LL~E+@PM(do9XS~_Gvq)`$+ -zf8Mq`mE*8@7a1MOnaBuo)cBD&pShtO!v6-TT|y1|F{B=mpcE5_oDiKt;={Z4HRq^h -zxG#WAjC0IVR(1#p6g1#y$}y|r!Ngf$2u)u53G{G1I6UmKM5TFiMwNnPH-+3md}{5y -z4^fgkszVIU0 -zJB-C?!PX7p0bw&^Z)c2Z==AEkiHOs(jJSCc#^lONoG7Ve0aH;#c(YQIBagYd^y0ai -zp^5V%Bm*zO^OjgBBz#D-Dk^ohjh=)=-NM8_+_s^=d`yX>Mr|C}vYW&Mh?_2Xz4NYC -zf)Tfrdm|gh=ERvVTdoFu-?C-T^!4=M3nkd6pZ5L=A@iT~b2N=wc|{(XkQ1n1p%nD5NtTsVO|khEpNi6~ -z%fpQZ$5D&h>;znideBY;(S-cpI%M^B#vTn?)oH|JC%cE1#7n=IEyAbP6vLq-Qv$wt -zO#K)=1)mAfZ-`a;-x5S*GX9ooLO{-p<&of0spH1uH!;Y#jm}Z5j(qSP?Qsz>&5fdz4xb! -z#}+x19IbXzn>>gB-3LSG^uBXZ!k}6I`ru9#h^CGVjYdh!E?T;J`A$=oBrn) -zmvW|-hK8e6Xq<>(a_jjMrPazfX<$@nnA?-G?+%ZUWTmVZm7J}=Dr|YRaehGq2(5*AxdD|-t* -zjXxpII_pRO{uFbS{lOEqZ|iglY#WpK#QVInVj9XF&= -zHt8h293f%$+n@H+QYw!GMXn62x@wg6)gG#n4CJnx1Tej|%F4Fdj#jEzTHEBo2QC>y(-x> -z*>B5JU|f}EYbcK2z}xuuU%MDS0013XRzg%Y(mO-9kUKZfh1mDaVP=&`e5R*fU0vP4 -z$qDgmA-T6b)7ceJPPM -zSo`A{biyyr1n)m>k?3z?K!-iSi-tBfQ7LHyvUzbs%Bl(DLc^;C1t%(lTb1aJ~$vesymEhWu@5bW6;Uf -zZQ9wDI2WQ(3vAh1uwyga?5ql_uy9%b1Eq+BkbMqBdb*m}j{QNtM3-yF?s>^A;g!3g -zFYF+b^6u}8K9f}Sp(QC9TX6n+dpkD@vrjT?* -z*@iBB%*U**sVNDECbyav5%Zb`v8}fJ%8@wAJGZ=;Y=%e>Dn|GpUjudZPPS#qY5dG1zrjK -zp8%N08qz9-PBd(jq$}J#4WfwL(fs=yzn@`EIba+b6}8V1!1~e -z4xDoZqB#aK*PjcCSSo)&iv4YsbHHK8t|}~wEDev~JY{3RSOzq1^$Vt8 -zY(g|DAMYVehaWj^8?IcD?ZE7AWv_L70;gf~aa-C=oVa40@oP%YgDQGy+a3Bpi}XSf -z_#j4s1GtUK$T>z$bPMq{p&J*^J3*)$qz(OzLfR1DVM`VX1wrCmgA(C5N>BQRr3g-y -zc#N2POGyWw>ITHp0Z0CU!AV(-;Fe!}oQVY&0Hx?Z$JSASTrmhG1tu=Ep6^j|kmI(X -zP;_-6QlSWfqiVe4e$mwyr|(;k>;`1ek@%5lKOMy%O6ut{iE^dYYFn_nnzkKS=Y*8N -zloAbnRkj?W`;1#bg!id#I>E+{WKg)088F5H+*p|SpKgyq!P5Bq9Tdub -zz5f{;fJ!8NH*bih2mhpj%ms*)$c`(U%|(tmCZ8K{AO}W{i^xT-qzl+Zs6|NGA~isz -z?J74N1+@ri?f0qPS`&&TmtICP@?l7P3E%D%p;M<5G^NZnr+{qU6p -zgK-5?#FrI>J!)FQ^$C5|k}S4Fc-Q*tohT-ZC_`(9}50PiGtU`Iq^5nNd!P}ZvgqSH=9zwC4Jwv^uF*n -z5(HLFkhoyP9_#z&eQFzsZVC}V6lt(xEJi6}AP<)T_Y)91J#h#d+yLUW1G8d4_j8@| -zg@TB>e0=j9h|oRCXW#kRgIfne5&%c_VN%KMx8EV-^Nj$x(2Ig%xGi*No+63-Ey@Kj -z=?QaRDIqx8x{ucsR3MRlB^9%6DAAoH2ea^o>NkK!e#Z`4kemA=4~4Kv`taC@=y?=H -z*lOb*WD!$TCveQljq5N`nG7X1DH33a`w1*g5xn_@1*-iMq;ER$ci!h#Zrl4=$K|59*ic0 -zvH>Xuz1e^h51fR$%FYWA!qTodssBsvEQ4?y?UyE`RC+n-_=Xk3Blkz~-SdOy9_-iWdV`FRQX3KE0z?AEO+W-?BRVt>1`%1b9k2~RBA-wNKh0zn>3%CH4$c8~T+<)8$GOT! -z-)RI0%|<#T0y3IVssStw@ONM3A&`Y%N$0p;fUtq2!8HOMWt?OiL$f8Nm3A8_P-l8( -z9~`;sD185W-=^;XU6Mys9K7(`RLBGgA;#G2)hW+D>euTT2yhc2?H&ciT@=e@ICSU` -zTsU_c#;{(2*p>DHv9PhRL7SeQp6(#m(R0z?`F4H^V10d^)|r`^>AJwENJ=^zaI3mS -z;j*){bFjI!1?9>V1O{*}{^`NmMineO2(hGO8-;|RZtU`vtFXAZpEx#g?Lv?*z{x&n -z2cYB1S8uCwVTjde5Nx+Rasx8U-f6a>Ty$wZ{I{v%=F7^%Kq>&L6rK;4mzH2^W=5@N -zYYBq-7O1`8eR9`VbRDi&t8o1Olkmz*KZ39R&%b~t-u@0)SllnI0LI@+DCEa$vFsU^ -zMH{b72IoV4^?=kG%%O;bX0t(dim0kEY%teaTU#e}ZePM4v6_(dv?wF5=Tj2V{Qg65 -zx8^pH<1_+0jSsOw28%q -zMao`7@qeshZr_4Nt3?mBSS-MX5o*byz=)VQN4Zphi*LLR^YaT(EZFp6F&+`S-jMbC -z8QBpxksH?=lH->#CIF;55VBbdBhUw=biqMH_=1wr&+-YGUwiEw?3)cD>Ns8j_klzJ -z_ju*XRd~yTC!v5wEw=Pq7TkaGA$afm-Uq+=$xp!3ANeS$1YtLTB0S|WvVvpYL!2Ot -z2!lJ_sc)03Fb*UEXs47G@`PZmdc8){*Jg~w2TCo(L?th#JlMj)+uv0l`#d62MQn3hs@+V&i -zS<90EE#gnS?QNRNpvcV@ef}2|CqfR1K||~jU}DIG1r3jguy~zN4L=2i{R(i^SPCF2 -zxH>BF^MCY*Iv>6p;5|L{?qBH`aq-PJ;gQGR3X6*epzXCoCAN@27O -z$&l``nyT#MILXh8B>*|c&5;0h>J4xlm#PNE0UpQf!y`xTgvrW;F8R)YnV%0l_!c;L -z+ihe}Z8UbTjV6loEZTSBxO6Nknw!7*J419#^tcJ;m~LW!nz?QU5H`*)F2c;rB&@A( -zlDr$&IOCxQPtqq1yzt_WNk?aI07nUGW1#ByN6~&oS2YNcRc`7#O+t8z?Kc8>7)(Q1 -zOIi4JR-zjJ{$Ke;n4Fr<{5UZ%@R7&f3Z-%x+S|3TzG%BpE|s9RT~83*q}Sx~>*rM& -zuw+1YD~jI_KJwU2lK@a!LqfQI<9gRCp8wu=VX{(*MezGio|5$mhPdM#Aw(#mh?#VZ -zkDG`a9oOx;UUT(=M;|ygl!u|&@$zcC4wx0NIt{vw&>n!qpr|-FyAvhHX5YT#k}|2s -zm9YYjt&!iNaASE)1{yhrXP`w@Z;*H#y6pg1t?d5zD6Fq{_J+g#Y;M(z-(eljG7O!) -z{<@a(M5M77I)FwgPr1q=muCm3d>wAEiI9YkaNwOEm_INZ1l;Yy$mcJP$Z!W-7Ojh -zMnQV1Px?Qr2W(VaU0Hz>C+>q%sWj03?E#vHV$qJ?%7+9ppnG;IdqJLH>h&o8qpK7P -zh2Dz5B0Spx;`IM+V2lezR3{yef;rAyzmIYK8YOcTv@(?i?*+&u{_KliOl2>~eeF^a -z{@#1vJ+%2d{r!HZCt`ndrMDO438r3OnHX24$g|&r>>xD~5Jir(h?O*M(Cf2~>k8BR -z7T}&^$6#(go9AV6auVhj=HWkn?mw^_Feo!=soxeBzP1b(m=$S}r3Qz|mwXu>e#=RC -z_$?21EUaB>bRCA{+8vLCj?=gBTEYsGV$+!;zb5QYsU31&J9I8I7R+NdUld+wNO0-= -zS-3PLzjQZv_B#$4_67-K-hT^1;hm7%&>r7Jsf0n8s3M35q8a71|s -zw-bYyOZ0V}$ghvbTo?_|J$Lq1ICAtDS^GN(hPjTpUhIwbqW_(#ztP`*R@1!&yCNlR -ztgXP*%)U@h2H@Z=54{?N%=a(~;moEQ0DXx6d4TDHOcMX0k8x=LjmKD`|LmD;t-?`4 -z=M?y%r~tzTbf4`sc3^RS4%Rj{WS5&;5K#Y4Z7|3Po+{EZJ39+owe6p#SpA`jRHpl| -z8$x#*mz7W<)1azW8C6EKx!I08RFoUomqXv -zYnj2{as&YL_c6$4VSWxSUD=(o&$lX|Td`0oL1m%>t1+DECjlbFr)TIzkKeuKNqc-y -zuR>Qq)e&9guA2>gZH%LFLJ~kS@!Z^4r=0gO>i3@vdWM*(ES*jyBFEALKe^fY1vrGo -zN*WE)Z)#1&6WGke$@quK8!%sCYI>SVrTIOYj_kp$1Az)KfDOQu -z0CaCW^#c)WeRYL?US7Hgb93|1o~V$;cobmM0=Rb78k*?&qZeL)qeqXz%)U+|O(^P* -zsvEaqLvhzu137M)x*UxX!f`3}VZdL1^ILG@fd|O1ya&4j4jBL)3gF5DwknMe9V&n& -zbqg&0R0kREihb(cd4O7VLw5uk6+k4rcI^rrJa{PA9bq&cM$6rM{CF%8V5Gv&&JLEd -zmL;ui)qJGw)klU{n&5yBUV@7@D`4m07hil4PMtcnM+9~WujPO%VC+ZKqy`wUrxi=JaV;kBQBXYo2QE$s18|8`ZFK -zF<A{of&M<0jtS9KfjesBgKVEwpcrv$WOYcaU%0xq -zMpcaRgd`%6g!~rU60DnB;Gr8J0YJ%yn!4l&dqiM&LB;_%V7pQkkVZXQ|913mN2&|1 -zt;K%)ZP9nb27rY95hJsMCfmZ05U8;XyciNNVs1L8*|#u91`a7y#2 -zf>6m<#X|l;4on38=5Ky9viJX!0*LS!*WudP2)S!H;Yh&i3Ax}@|IcyY!t3X$hW@Am -zBITvaZ$jG>!x6vFzox&Am*eBF`B`DrBjFD@yl()YT8WMYmdc{cck&ig*H?D;hJFfR -zP%nU`)CW)mfY`yuKlNKl<1w`kt3Z5h#2@uPRMxmJ5Ap{V -z=k3g)z_u>p1nWO9t;hhdnsGO${vKls0btuAGsSUl}VVGoJ{$YY*o5h{(H|o -z_rT=zluQTS`vdL49^6`hHYlNNR9WEMx$|)M(Yxu&TQV155BA`v8nCxTV+WcU>#t&| -uzH?`#U2Bg3?7<$~VsPFtN)H48@c#i|X5sYUzOw8900003vmGHS$-Tm^LH}f7dLxxGjHNcR=A`%7He^&t|2MK~BK}5u~u8N4Li0CQ^h$yn^ -zCf8qG!x~t^z<}~AGE71SW`;>GcfU?m_y5khp>9=IcUN~;zv;*H8H!h3b?e?+r|v!X -z+!NWM2QK2!hBkP>@NC8NtLs9^(t{SO)AqRXgAcD`V`E`uOq{VwmCc$pE0#_rsIu9+ -zz@o*AVQ^?LmP%&|^!4?@&@Myp&AacS9v`ywNZ4ziePCc1Ub#LYDaW+?D4pJp;0CEXY*d-b29Xz9`RRyPR3FtgM5{cDQJVfO-9A -z9H?#Tm#!24lkWkR9#srvMNQbT@2GJV{H{Rg72lO9JTUc*Epc6YjRz>UV%~CZ3}QWWUt8@%d@6ficcF -zfU5`!7tgz*MPr~l^y2=}7(WGwGOqrxV!1wl^U@QNgVDf`|AbxT2j3e*M=e^QQ5E{DfJx}-PnwY)Zn9)rC -z-wkPx0a)C)#mt`Qd)&O{tAm54kzn4G$C>(a=vErU8BhQ|G(wqD%|GE*D -z9$|bFVg@)Z{zzj9uf{hX7#Lt-TtHyrGvhiBBLr~@GX)30m_BwUU;zS(lYkD#_(o1S -zgp`^5`Wf@wEbYIpuJPb=HA^)o^E -z!-^+~kRWWC$kH0iCplTH~{CLa~6E}Uw#Pt?Y*nG7eWxt$^YWZo9G!| -zIaROv%S-DC5&<=t&55tTxK>g=)-8Z#F+r(qtNIhKBVabu0}D(7(1g{nrJ+gh9Ccis -ze$t8X>{CzB`5&KI4u>4>=K@TkfMu&ez2Eh`v)`pB`BP7LT!2}RS+*+90&SL)0fbHd -za-cYZxVQjG0Bo==3+$b33&3n(#z29clmLi#j=$bo#?|KWEbKbaP3M1Hu^bLP{AfCV -z=BdZ^WZpEIiBwl3-*wgc*I>~ebLo8fm)I}G5m;JCjyz}qjk$?SnShV{t^c#tQ32QcNg?GR6?<3U}1+TTN -z{vpt?AW#qh#&${qP_jSeYOlC+5dcs95HHj^uHNys)8IdU`g7QQu+MjW!}bZf7Zn=) -zuIK#2JK$gb?LT3`u6_%~b(^-+y~yJuCcL8AZyD3!hCvVj+ai`}XOjU02dC?Dk`{Bv -zt)c;dFs>{Dn9nd^hIp@hZN?ofy)M;2fT2M!%bK-7nKp#9y2e+D0QHQkzkmDNV8xS< -zm0YW--7?=*YXL$ai2t+R@pf3T+}{tp=G75+0__f5?z>}Z0p5D_QWo3>L{Rmq$=}HK -zp&}3guxYPm?|d=D|3TWZf0$UXWG9iEmPE&dM8d -zgyKOO14%P7Y1+rVE*pIiuOrJcQI?E@j92tG$fSiI_wb;A8=cUNFxfn(c7)-PY2_x3 -zWuywwofb8q_btuO2(rt#;+ESkSS5x_C9V0sweP#qH+AW6X+OM?qWTdw`4wP2+Ps(h -z>k`+DZ^7L#^{Q{=A~C4M;)kvNNHF5n0?=N~RGm~NEUe@t>_SB1#47&u7gAq|g+J^6 -zt+c`3MHc8DzOy+{xHO1ydBD0Z1oW+vWjt`fn9Sw^#LWxxIqp3Y(jYgg4;JFSawTnZ -z9^MDtyjv$nQyEIf75KiT?{fquE$+7w)INO0Q;$dB0>ty!p6|O#u8y0y0fi#N%0gat -z4FMm@9}tvW;z>g@5UeY3BH9e#HB1$#Ci$#cvzU1;j3`9eHq-)w13zSS{4sAXI|{e;K*uidZ}-t(@%r}IaC^zX1xO5{-`c-T?Zw*rlP*A>R< -zPnriQeEjiGf37tYs2NxqVM|ggUc*-VFu-j?yoNx`BHgJi9T>_Ar4$<;xTAzprEGR( -z(H7{tCY$eTtn?|91?Fm<=Oyg8=!+pPlWZ^;wOZ)V1BS4OyYizsrhiV&c!*Lq91mfy -zUgp_Ay`gOz<@~0|+*d2TcN1g)Vq951&%BcElWqN@dc{T^`?+VmQ -z&7*)&tiXC7M}y9M9~*3P@=+kZt3yXwU;rzg@PvM^jb`A?(@xgU;Q3E}{!5rIZ55s( -zB5+K__jgThvPD6916qO|wmh#VC`V#g_#Mq~m+S%}ZYO+fe(7D`wcu93H2yRmQ4TuH -zf5Ce{`Q8d}zs98uAiyk!K?gW -zP@91wVg3NPpD$_Lfar`mT#3BS@5yzQhD -z%a+4?svJI3sm^kEH7)oeOz4ub4H^&mK*|6tsk1E+n*{`E{&xLWgJoFgQ>AqWuR>^v -zFkoeBt6rnxRxB{NQxe(iq^7`45dtdWOXG@_jEtuq^H~3tFK>h+j}3eu6W>x57pI+g0{r}ce&ee@6e{3VPH+aL9)3RMcF_AT?k#|{1Mpbpoe5~5 -z7%PX%Wjvsri~zVzB6`)v)urd3TXUUJo~iY_p7NFxjIz=25apR5UJpHRQIu%Q)4B?4 -zcVLuOPG#Z`4B5)CcFkH?uyBFVLk>={2${^}PKAd*|M^dFz<~$EUhj??SFQ40n}ydn -zfd~MKD)YH4}x|?_WRcpmEhI -z-!%-ctp@GHAZ-XjTr6mNT={|UT5rOYWE*D`z6)T^&@M9@GZN;_n+JRCy%*f}=}*yxHm=HpiK``x7DC^g -zd1StO%G=&v@ws38^uH(q&|g-sg{hIvJN6}ZG9UslF)&4;`hMn*R0h_qTL-C*R8#7oF6iv)go*Kqmg-Z< -z763v*DpiAQCIb_r!_d*)4ZXcRkj`YL^(86+O4_F~lVlz`civ#6cSC%1_w+y}lYx$o -zln*~`T=}6AuHx)hE}Ml_FRqHc?&$<1kYUl0b|f^+0i-9!p^(qP+}(C3opv@?yFd%o -zPUx&rC=^2PW6%fOYt~$^}y9JneJi;&Ic -zz;Q~$Td9Di$wUJB`}#tkF9)rLt2RszH2lT7dU{}RaEJ!X==cN_i^ZDjz*Xac?|xqf -zj=#p_BL`{3=Z-$^1h8Wal-e+^IH$>7 -zunRDiEuEZ&LXqCDtFsfvCnjnl0e&a1nim<}DXQ;Ip6ooJf;h -z1HR(MRU29#0x|#*X%(fF!`n8)z~J1bT6f_|<4;0g&I?$e9LWTX6iQi4CLzu#9LS$-q} -zxqJcA=`;~eR3ZBMg+e^e!NIJcDq9r?7#$xcJS)I&gc)*>U60y+y|{`C6gN22V^-38 -zzzk6XraHSJmz@H)kOw=}QFX72ALv`(eozns#|6vAbjdDUa>4nKPES&X*kmdtCTg(= -zb|L}a`TqC8!AXxr9l_?>l8>c*GLso!I0u_CxaWcIz!jHXMCP|#W}n46DegPxx_y-k -z4)^GnoPQon@l0*P!y2I$T;V~=F|6iDg3md0cXqXY4N!7c0`(a{y -z6z1=?C#_hO2K7IG>wXa()v_RETQornjsy2U_+4gm8;RJ*LXwfn -zk4XxNs}fFZyn?H~ov>&nW6XwB3gCf<9u`3!og!R$=_OR>*S>W>)!{O+FUAbINPmij -z0^IxHLvY2#7nbE5%0aoD1dZSd@uZ%A-}~QBY`r2DIQc$z>*wfMTH$+opy(7Koz75C -zu6qCbY0^$jWy#jVs -zjTn^(RLkGcD5%7tuLm_?tGz=-JAhhr;dlMYpHHm=Bq$}F2EuP_Z| -zG;qK5;6qy1jElQ%Wah;IL~1b$GzWm(o(&eb4i^$ss@|eeuCfY!^MP+uA5A^6C?axT -z<2_v2?)%O|aM^_yz*MGroIo{PnZGZ+;tKKJ%*#lOw{Q9AM`)XK`{(ZZ=t>vi+Q-?3tyr-ue|1J17H63KmM_gz^6a+ -zIRMESvn^4vRv>3DCVIVx&(E*ba6LO#KU00b_zjn{P -z^bAfkyoM7P|60bL&8Eylwp5&XlfjXB_>fpA8GvHp9r>5Z6oABBFiR2$Ahnm8{Ekff -zr1rGTQ<;OEuyKP7_dfU?;u??>FyEu>5%{N|R$S@lPE&?rX4eTp}Oa#$wI3<Is#Byy2T3se?IVFkB9KFq;A|H1 -zg#wICOw^65*r23J=KB-8SGET%nq2gWr=JP5Wg3En=bX~N!y1U#?v4OCo0#uV`bs>y -zdwZd$uMavqJA8WBiOEU0`4gWc7E=x81WD{2Jpl_vN9*?0ibLy@C00nazsF~bWE3RO -zr0x|Zi>gvADZ&6mJj6a{1U1(Ve -zdj1n1|0o$?ArYYOTQ9C+0+m?I=Akdca+hpZ~b(QN=pxxFZ -z02Kgzy;0H~+}Ca&8G)Byeg(E|+pY(dn#^j_GMVz*`z=;|JQQ(IdE4+ghcpR}Ui~e} -zN~2Iw4PKnkG^tghQdvLgcNo3l_~d-NU9$r#0Wb-2joZ^;#t1bs}%2FAw6U^1Ph -z32c+_P_#k%XeKE?$_>x}#8!M1~!g$CWUa-K0;JA -zU?f~I@h)2_Toxmzrta=8soL}EAp&Ws)xx|k?vWGFR2Cf9sUB!sNKI)gf>M3b8U*Sz -z>lUk*C09>Q90Sr&6h>CVSEc(H75IQT^CJ22cXo78RNzW&OxNU5t9b17wxA(E-OfO0 -zX1aa*Fb%Gb&JH2~N@uU^_=PM^@T%s%6*&V`L7|W^m&?QYmtWSCR|o@H$zR-Z;9hpY -zzWY$BDb$1a^z`U$Ly?SznB)$GUc6XzX)xnTM{6BJwPAC7)~f;TtH;O3DMJmaNPD`w -z!JNr7I0BPea6CogijWmYb*kUo))WSMzmZ5F9&l12jygIGLSU3r!b-%CT4gP?!2l#; -zjlVNS1x6z9zrX)|#d60Ue|*`=4@$Nn(}Yh8Flb6!lqz+mZ~)a1kV+=uA3ymC_`)4` -z5En3+PLs}A7z7CliWAby%<8Q+c#Z=aC&RnXIR{2YM@br89t1o4v5$WeR_1f$O}B8t -z0;&%OxZ?;7f@*{I*32^?5^Xwg)DcI*F-II;_1$Dn+wgle;z~>?4wI7;Mxf|!SL{K#l8at(3qm{FnjiF$mX)t79SWO&cjmn!bTe&N5D1r -zfHH%D{(ebN%fDYaT&-HOmJoI5;q!2UM;Z0yWp{OQ0K8JA)jCtIEs3Ad20|J%FSWEK -zpueMG$TmuFfc+LOBFX>cWCm8RSqG_9CnW6zB$7!(79fk$gcoMLxT2%c&lzt!1G3pH -z9ChfS#6gq;)#}RCYv7Zg_!zOyAO6TkrN6);n*4pVB~aXhrQ39z0>CYgS -zn<{H)q+O{PgN6Ga1S2D(G=XOi3}}I&M+AItCGL}8RkVBmJ>P=P&Q3Da^4_mDT!D-V -zoS7V}D0%7boy8#8S@ymXzJZeQGtFN5x)Yp$L1k&l{4CQey#?S<%48|I0ST5scYsUI -zmdh=i?k|^eFfasyEd&YgrK>?HW^poo>K{J=pTGSJkjv*tYTSoWPb|M4T(R~zkP&~! -z9(xQNeb`cM?N$j?tNSlrLcD7dPG -z(A}49n^W1u-b9;`{gSz#COFYcvz+? -z_z0~hUkQH7Fkt0wSL=VJcQs@@`VR}4-rClsH6fA^upHoo6noPQy!hfOc*DU5%hi~X -zl%J7g+#)K81KDO49COSuaQsn6*1Z=xV98<{ytjV(pWxu_`M~Ho~k9td9&g_ho@`KT~=pcvv7cZorx83?__{c{;MkPB^2a!Ag -zEeFv6rdV)@MMHh|amOA9#~pQq_fBdb7pPZtN`I&zi&-bGP!Nd(03CHuh_`)sI}FaA -zL#ea7H31MjI@e4hhesa%KJCA$588~lGOp0I8psT~2xta?rcp1iT?KnDTmmpM>`?$q -z2qKvgZ-7gV0}YUH@{>vspl)0#Sn>SoN9!G+umiLJ>b-Ub2m}Pt824Re#!be|4GzGf -z1^dD=haV0HA9Mii8A)%CHvDoMAdGb!IPUo4;kcuu0Ip=9cm^~6cKvO8KN#${coDUI -z+vjeDTsB86H9FCVIl}cTWV3l#`o=dD`Zes4eNV}B#y3KMpqk&jIVK^75#Og0O{BO?@ul$@7bhe!whrE}MT -zZ{Pm_wS|NVositX{hg_FJ++(2H0 -z3dl{kyQgvkAy5mh(3EycnjYh7o9alAC*L;0 -zF@W0-wI#sqz=oIB!r}uDCOOIEL>f9eI>i=53II{%xP5qp1gE$Kz;iCQQ0Rw%!o(c` -zrqx|F_{M$TZn%dpu3l49`KvCwI8wQ$nMyYR{uycNgvz}HV{q&3chHJ~7JJBY;-Kv^ -zB>6>GxbNb-=JN$O`Q($~n8OdN+DpY@mIVo_oUfjb;fqc_AJY@Gr-ATkP(KkuX+G6p&mrb!Oz})yEabp9hHW26wjU001BWNkl=xq15gPUg(_ZOC -zX$b#7MK&gfAmN*!1ktw$P3xlRIAr!w_sers -zY9;W3%1ObJW&S4b1>xegb^8c~eMU<(E}eWR5Tz|%5Lehj!I7(>ScS=(Pcer^eHK`< -zE$NJCbVK_mtLp)LSq2MWp5tT}x@=Ash}Z|Q>bsh~CE=hYf|cNE>~Qfxyan7+{6;=$gkj&?ytFt>+T%{6KxCu+RS|eV_DT#=H -za1KJkibR0Q=zd4RRXm`Hj23>D3MO~#v@#{6%-=_KC#bk-5g?w_3Lw>DfdM7o34Q5N -z07wEO5MF)4jaBG?5=llIPnRoM*f6i0y@L@7>O$!JM> -z<=3Xa%9U7Y669Z(fuo#*co9;>N!i9;yfUuh0*~RUK7E#5c{%*@x4(~QS_vX+0Z1T3Y(CIr6OK00uOD^XT>e%XX)fx>=L(_q#R-Cy7EVB}Vy`XH -zUo>R1B+^kvkVuG(LpfAcmI*ag=|fe+mHJHqJSWp46jyIgiI_~`DZ~RH@?ftOf_>GY -zM9PF0qpcN3j!6X%E$!QM3~JNIJ_?-jmJ=FoGmbifptcFE1U2d9gm1kNu&Mp7Q{_!h&ozyl<5#byN;OBIAua$_>qqVuj5RNoZm?{%o;)c->}36}P4I#xB$_KZp5ZE%`~leu=r2_iTkoK=yP -zDv>q@p24MO1<6Rpq=_OkYT>d6^1dk?3^02!Y=HVJGr4mWqt5%!+9tYsR7`7^+QOg~7 -z+z!`XcfIhjU$Tf)RupW*WiWf7k8JVtg`x-o%2aMY@jM3c=++UaKq0}kdNP@vRGAzi43D<+pK -zOw5VFP6lS7&U}Q+A|_$i`E!LSvRxV+#3>di@(|v~JOW)^oz$;i{rtZv-8fnX -zvcPKEbp8A5C?wd776Kl0!2a|x?)>r>#lXctA5IaLec(FU3LLQie$Z{$*CW?JjwX&U -zenDG&lM84R92xyLVJFGvKy68c6uwAu2omBD-M5{5Qf&P&L0HrwT8l8c -z3Tz>g`;lBIs7hJ&hVHg^m({P_$89xB)TzceD -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 -zf8uf979@AV0r&@}b$PCr%d -zD~)z6ORP{1?pG%v)!`Y%6r>O@o6FORMyfpEx?gX8!{5;H$3Oi!w7qw}{fxSYfmK1` -z+z77FCjp57mUmEPS`Y_EtUrJXu-BjjJ}aKE#`@S8@W18>j}jUqnIJ -z>NV?VuRDA809ajJ(q5g@>MtVuSWt9rNTy1idTME}6+m+W2@Jmlz;hWG-7=+nwr$f! -zY0c+P-k~D;IMPrDGy4VujZ*AcCh{R7VMVvzIkRU84ueV*L))_xu@8e-;gF?A!e0CA -z6PlQGCIKdh85o!ipZ(OwTW36iw6r4Nu9W)9;Du!fdoXmoxaP{=78qJDC`k=q+{6{_ -zfd}q`3og0{UR-U%9((LT>M@IC05)`{QZUdzK!U=HF1`fLJndAYuSO?~I(f+@SDS|F -zM=)I&v-Ew+8^jIg8nBcOOqua%hyz!+EpmtxutRXAtO=Dx8BqR3?9=4r1O*B~p9@AR -z*dCobUj^_ay9wKZIdcYO!FTcF9Zm9R>2Mg9TOJve4u#Rd4@8(w*ZA}5}Q_3U}59;A#Z#G4M5C?f6>L4lEV=0dGq8xoB%74fF+9- -zz%|#rAKr2LX}DzNrZO1{ -z>N=AlkxaDQc4>$6ez=bZ5*~gh)JO&Q^Y8k*xBIT)z{XHnbB6|DWNgeQ6ni}a3FI)w -zVG$tZI;7*Dl7U7Q<@BB8(&?&!*CTBi0DsT=Y5>1 -zK=06EmXb3v#`9~NHo%ho4j^^ejt*EkU4X(vQt7#tjeVo$G#@Y@0~&Hy&QR_aO{7d-dJ=V0N&g)lH%>77kA -zLmO8nRK!&qS{?#wK%ozwN~d88guW8hFNq<5^u!ni-)JR3OTo>XH^HvE?jCEeD7i_o -z2ut?c&luPNXyb|?60RDAun0yo5k&08*6O$~jBYR#G&!!2MPAB_dDlXKLNW65=kfL} -zJ2eF(+qc517hiO#Z?Wh#S7}m9Li6_@Qf;#oi9c_h>a1fJU-gX1|Ilyl_w} -z53^PTjIT$@IGcc2HLzb?`GUUI##K0k;cBMB|NX@;XZ$-v1>yjhB?lw~f;1a3)$6dAOg_SHyai#TnKaL&26~3XP;gU -z`|P`bbkMH{m^W`8g&n)?)1QJit|~(iS8+izIRK~3maZu)bqi`TNCq%5F%B=iv<|jx -z4rgy`g~2&<$X9(Pg2+7}Rs|f;yY0T44&~j(l^?3%Y9;{6M1sf81jGSgWSH?Wpn;9h -z$m&+e=b^j1r{(mWB6on{&tC>!TC?>n58iypn_*;R1h#G6rVF=mLLZD0&<~Q`!rrf^Gruk$5$**RNmSP|sd@bwg9a -zLpKVD`q=}ss@8&m8_<3BwT&CfCgEt~N35>sv -zHr}r{Koi}kmOlmQ%v8lU!$`U4dADiPM%Zhg1(b(CK^s?osD!JypqcRiLMJ?jrnJ?H -z@c?F=JMeTtZ(ko}Ia;!RXHy3C>j|2UD`KIW;|)kzkj>m@S_aSr6=@541IEg;O^&y& -zE%?OPa82cVd;4H?WLT%~jvLyzY7Hpl3y?45Azvs!J}-`)2}-sAAd^Mbk{KwQ$-unq%)Zo`oMXZ%1qKeZ|=OoNbg43cY1mtlgUsdR}(*NT=}6AuHu4P>;SH~ -z<^#38bI2Cphd=t)n(DacYj;H|*NWbN4Q~O`6XQ_G=V0z`yOU0v5_z-;pxyr1@HXh} -zp9S;g?@Gd|OqD&Y0!(#_Q}GpdBH^`{n=q4o*7Z_^Rbv(HW@W -zFV@x5LqRZbz>JPhw3HzQM#)9V^w7`{#n#7Mg;72A@VTRpJAu-a#|3R%H6L10qtePC -zLxKg^T8N(R%CZxr$Hv5v7;?IvlwwT-bltTraElJb&<7r^e=5>#|Ezx4uwjEQ>TMfW -zC6ATGRa{WZ767*~UdSep8J2?4kVO&`l@yi9IB#6Ok4|P|BtY?!p-6uL((9|y5Up=5 -z?+nD0OGJ&GY*qhvgjoZFkm~FJCz~s4_gx9fttf3=l|r*t-&zO&_JN~gmQE2RCMrOH -zE+`_PC%2g4bcN|@E$RXAD1xd9>oqbgMH&Y!=nGIH1VovSQ^*V2_Bu%Vm`J7|GnJF( -zt91W9)@54{?W)S-}#lh3?d-t!*$ozCPRJ3u^TSJn^_=Blj$q%aulY<^U%iAH5EU$N{vXFF*+g -zfYwUPPC#F8FJ#l>(9=JANA_=r0IQJlx{WJ8RKitU&`ft=(i->@rq;K$ivq3a3s6=I -zkm~G)Ty_fFLLTf?M?0%b1!&`{x!_7inr4{ihRH3O)mAH}Hw!lel2pB`w;v|PM`8YM -zBDuuQ1mz;bv~g7m(-TF9tN$d+uB7o&SSOQ`h%;`ivnrLbc%IUsBj?717ND%%fD{av -z>E=mD$h>~291*IN5gt5;!y|$a -z%$i<85GStU0^Uq#pfcWo&1j1QFCkAm8UzPmg|M~qT6b?Rq!J0}?Cc=VsVFc!HVRKa -z^9(FKdj5MLlbL=(FgCg!dV2dPXI~^KH>=<#6(g>=;}Tw$U4I=E -z3psEK4t(a;TWJe{gTA|~3$~9;FCmB(S22N#(9;wlV6j+2zM2~mXoh(M;!1#$?ppV? -zW256RGCWL^-Lh;_6+iIZhvD7_A0lUy^yDO^(rPxKzdo+CyY99dq?+ulRx7S>pcS1W -zytrx=>EM~o;A0=V1#bTEP1I-P3eZ^ySE+5=hhcPVys7=4PF&Rva^fg|02j;+frQd4 -zzB?g{x>GJDU|9yhYomloa8>_g8Mm}=tp*i^fcm{$R~m;zWAzkEIGLV;fq~f+ej0V= -zxKeV*f&0GuFmTs}%P+d1sapf2wi9C`FmL|uU|Tj#%qZ^=RSg(cwiaBeXD27qFfl#> -zUEN*el;bi7KK9X%07k6-+-`28>ONLr-rHO+;JAr*PnwTW+R7^tszU4^vZFO$fGb -z-ySO=XlYz2Az(AYf`?^tYMRY{xE5gPL5m|dw&MBKu~OTz96(t9yQrX~;I1T(IDlO_ -zu(4ho#CF04x9F0})V;m)001BWNkl(jX=xON1a23Y1-TUu< -zFgf0!pe^bKtjD#eFayXP1NMz?(Q1g-Q6t^+#$HT+u#HU?gaPX -z?l8DJ1b2daaNT*o-93Bu2TY%5ZujlJUAL-Q%T}$d>b?EN6dN5?WU|%O?yQ#sT?Wv_ -z<2|Mc;hLCiaa^lG6!Ht}ujNaUG?HoU7r240=UskEp57Lm!25;ISd!K(Kt^w>YfrD>8a0m!V&t*r&3}@z?OE82HRY2T(P9QmcA299FdM^hr4ee?8}TOi2)&BM15w9V}Far#%JC_nq=@MDNad -z8dGHbt;qPfO}1PA_p9uw&C_9F6K8J6{4pciRW`zz_B#Opyew2cMsmG5zLD9%bi+i| -zts@CDqmeGPFpIx4@jrmkN%2sTgN)oC?AfqA>IcrtyjNcwce5B-A}0@|aN>Z^A9NYx -z#k(j@LSd9kQP?`eAdBE5T^kP!cC~J)uou2ZGDxQWM2jDqVer8g3nbXn+wKdIQRx1= -zNXB7Go(kxY8-(`jDte*UQ9TRsm3;Y`dVsJZG-Ey2YF#~`Z9hnv$f#r>%;U_s`bmij -zT^=5Pcc~bynb72WfAUr5J|+ZU%?aT3(dWBy -zxyNKo$FF;eoB$vn$Wdj3f|c|`!ob_C0#&`NX4cF}&rc>wo+5@}axD4b(S@&m~=4&fc&63C&AyY?qS(DO;e -zc|m_J)}zpok&%@I!So$V0Vrk0ra&=tk3F*6$+nZChufysS~6e14%62^)nMU_Hf;CK -z;M#sPIJ_TASft@WxJ7-ja^n4}Oo-wXTapsMSL6DCx(elG3+E%=k4!!&YUq=?YVia^ -zlpclacYk}w(c@~i{C&_;)dvLrfRGsuA8@`#`@gT6MF~OozfmdRRt+;*=b0y*B*oA0 -z1jCme6s637+Z*{6hna3udvkml*_H*6B;?L`x2Rrfg@8+5W}Q8=iwd-ob_h*&nE5aL>yp}l9N6cd6KP9b+}7B8gG<*f)z_z$XS>RgWH>tj1&0O2+SGk` -zJ{M@Rw|sD1KIU_B>+1B!;gjf?h%<2ghagxg4)kY|YqXs8xhWH1anP$7>Ocx#0?q92 -z<1R^8EH3>!l>LG(D`Xb0rB8wiREzr*2_dLdA*-#wSptJHFw6&1En9I{o!y@L4ZScR -zo<~uZBi*1qy5yVB>i2$E5~VTm=f`5#RnaTvdyZk~CBXjTF4>h$%mdtnHoNhxaYS3Q6LvA2p#!Dfx+|{bT^q -zo(1ad5PKNnXo1D0BM`@^>D|?P!SxFKTd5~AJJ4Yo!$p_HDM(0B4!#_SbLIq>Vo80k -z(>eg`nb^JK9FP4Zs|DKkIK+&xoDFG`K308?*-;L(AQC$q*s;M%noK)lp~h+vD;lCt -zs=gZtp9wmd(0C}~4Up5v^;{N%cYkC=Il_0bK%AwF2K-*8Z-t#)AzVp&nv{aCc?ay? -zFnR@O>f=7Rdkv&HNa*0FqI+L7dwp;c9N>!SXa{Gg=Ssnq+=zTkb5r->rh5G47N8Z; -z5Q;a_;(JP1!U^3-^}PKKf_DJI@R+&Y#uOB51tpYR<8ICTo%8sU7B)a3QP?5P0aZ28 -zLSgK@<7WIsiH(Hne_OPPov5%Tz=X(kAj9KQXRit|#2yiU$nKm= -zC{Z~Y7&xu?NrG2esz>`%Px$&R4@?h76#`?**m=0**ZwQfGPdIDg~CELhv6Y+O|&#T -zTdlAhRStWZT_`~*NQ@F=6kq6qNK~dxGj0$XznBO2(c`BH%<-vd9eQ>o80a+u_=43$Oypz4 -ze!WRIeW}ajsS#&oF&GOg>|2t8PAN#FvPTj4Mv~HsD(TtG0L>394T;?;YK4^&!wM69 -zvs^Z6MgDj`+qS1a6QRuu|AVAuB#7NWFshU?gZOiODe+?A?8hoLDj;f&zX|fljgkXZ -z*|7;F0fNA-owELoZ)}M_T^|A)^1BZv<+!Giclqkz8q!ihe))=WABHm}X3d!@lf=e| -zWuq?P;+5}_F7{D3E^2-fr>a34qR7P5&=wYVN(Py248Xw!|C>@t8)R*4yWg&BtxSa0 -ztpKLR0jwthVaf-=93PNoAv#B5TrOEu^u3la6AFIWGJQKPDZ29bxG&gV^zuC!K~dp$ -zs92g&++~Uam$VwdUwcgj1KQxri) -z{RXZ5ih%7xu3$#4t^X{i<6RY-ddB)IfsLkk3`_3*&4JiZ7 -zkP{)PG#qUU?UF25aRrq?2r%DP -zvq&e;6jQT>WWu13t$5|)?TZ?c)gt==+&|bkZ=5L&%=7QxexV7RdbcX6>UngWT7jf9 -zwW**0Xs<)u3-(*HPpEPrPClJI(FXFQN4g83e7>%4!xIy=zuWngDS)^1Gs%T8AfKyC -zxU3(53$jqLd|r{}u2?%vT>gfcDpzC-nrjz*Jye539BP>!*PQQy@~@}h(J!~}nNNa< -z*ssP~@S~Dm5IB9+ZF^HBY&o;lbN?zFGO*cI9Z_3pzre;IkU^)F$;A0a&?# -zbUCd}1!&ed`TV0Uh5OvUHl7qrPa5mv{OiR|T2;Nnlefk#cQ+n?DLUl1?jh8x_*?54OXU51)$O9A6DaldTXg3EnX7GB8YLbt5t9b>YKC!V>+aMLj+{3oS5= -z*van&gIBeK)Es!6z -z6MeK`-a1%!?=^4XU!xej{gV|7mNzPM~cJxs~(ZUl4nNgud9U9_|l; -zDhigT`4wJBJDUt8^kvF7T;XdMI^rGTGZQwmzBB4ZI1+1D6wQZ?ldy{~ZD6>+m&=|) -z7)m&e{n0+H$vR~@0r7t)Wp-q*mq*P7niT2_ce8RCFS -z=#?wi0F*5|8EDHB)bsSA?;kz=c0L)rL1;KOo1M(ei%q6T-ne#_#~#C7JES#nNT2`- -zk(BeIQ?zhji5zXf$XNdn9+K8)Q?g15)G87~;eORCvTBo$!pX#FUcoTKAzINd4yWZS -zi|nE|U)PI{ -zbOm_Gt$4^qyn$!>xE@W&bG!%cmENoOYvYr(TdPlaSyJDq<@5|UHvCJ_*3j4wmleNl -z7&f*_wBh2}K3VUOo9&Dn**L_4Y%dsE@>b~LWR@r;arIRp3pAAyA&;$+bOV|H%&hdG -z7<>AXUJU%eXL%E&}s{vb+%e=3Ix<*Lcd~YZ~yMQwfX>nl@x>8h_w}7A^7KR{rO=~ -z`F&b+AaK)2Ucl?B2V2MGl1wa^i8&fR3SP7y0CNGzK!w~B6Ua$zd4Xxz^SiQ0_hS@M -z_IK;v3l68P6R8KBK@8I&>rFw&9;m^zT1?2`6KByv#vJ2j)DEe3E`u3XuuD1t+&9cw -zPlY6t?a*oMdsij~($I<>fidLuGRDC+`j(FHM8Y3KjYMhz8k-`gbtUhV#_hg($>U-}V`5nI2Vtqh%r+_zY=& -z9}?wn+smeE9K3FF4`AR;R_Od~B=I?|RN6PzizxZetEeZnCZO+LRK{TJy|E#UTKyQY -z<)wTi$Z=?a;#+{0>Cm=ITQs;`qB|@Yw!?g|9E$d-D364f`YGCKCxZ&Da;23Ibc7OBk>vJFOzb_|$Cf%3 -z>{H8=Ac-dYehZWFYf?+g%z$i6V#f)4$(eTFH3@&o*Tr15v({Ps4c}k@1T3&f(WZ} -zc0mM;dZFH<|IsE^vY&RQPn<7{rLd`%&s`aW>+azW -z0<&o(m#EUCUj66KI#H$Fe6}^Uqkjb5#^pUB){(h+X2OBn7wBL+557sl3>1LV?|8j4ZL$Fk~mko%36*hLoPN!KNc8vfG_TODso!bjXmCH5ECtf<+dw?m3 -zd_0ojMWSDv@Mg(avrB@CO%o_T>enDEx~o1M&OiAC&JOHbYg1Ur|q^7D=CL(Di^i`0_s -zp8%xO***h$)z#u0PC8u-30CU5l(dYOwwYd?b -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 -zoK-~74%^CymA^DW!sRi@1y5qqPjw;2*@ -zrinieTn02d5qP9jILU$!D!8^cStz0FF3IFSG^u$yUG4EhNBJ$N3KBE-xDv|VU)pIP -z3#nfs0y_5Fe7}B0f=O$(SMW}(gDFI;p*q7f8>=91a$un=9HLl(x)j`3J_7VdA)yJ= -zU)VL~r(tjW6ia!*>H8NrvfL_j2LHf}N;d(aaVt)$ -z%h;i})TcAHbc*I$$jAct6!_*T<}qeX7rl?p*fXoSKc3rw_01tdA64;A`e7lKPI9Jk -z+(;97e8i{{M%z2*oODu?)Eq>V%0P&HSsDqI@R{li_?3zaGj)+VqW#5S3DP%+hyuoo -z?cH-_y#drdHpG=h`j&is)0);C34HCOc6X(^*9d{)pcU4_VTH?3ZsCsKfR(HHIuj;CMDp@4aG(>CtG_7 -zoq~iZyOUqY@@W6;$h<52%g?;jYXhVjuAx5@wXIcW4#9!uzI*^w8ROWktR@*3uMj@Y -z0lyj^18JC2E#OYxtnbi}>q%=7P`7&dacw?ydA(<*k*lhk@C6r+I+o_Q$L3?q1VT8H -zWc@LuqK*V3xonaHry=TRJkP89CT3-!(bO0Zh+;@ztxKDZl9zlvEi{#e -z%gY*x9KMNC!~jjH1Oq(`hTPa0vKCX!5n5_Fj1e}G<~}Y3BIOK;Rd?2QL=zi)<%x#{ -z*H}i%b()Z_sm<26f)T;-buhEZc}4V9R9_xK43Eh35-EyP_2MSXQ>}4h{hkWVKkJn! -zt`drnt-iei2ZNE)lvbfkiGH(2cWVWFdnAce+?y4mFnQ{xOI~Xm=~mp(@tCAta>CU~ -zS=txp;h?iCS2F10h<t6(Zzf7WO^~MDSL@aRJNob7g!P0~X}X&jRR<%P);B2GY!&gbQx!JC -z(J^bOTudT{`!*I}tdd9 -ziI)Z;%HJ#X-t9n|8sM;OyP(6v7plBTZndH$J-&Iq*Zi5{95BZRal$f;G6V5-YOKa9 -z9~1lOxdrbz(9eJc*Mo~(|KMuxBY -zv;1B@kKMlar<(VbM#J|p441xV0cBg=j~k6|abzNf>Q&M5KVyv;n_+jjtAjcdH8EL4 -zKFsl~HankodV^9bxbx4x06fYgByTP$pWwJ56i@tF;;$FH>(HDfNeB*`OtSly+|wi9 -zzQdJ`4MQDrU%#s|0X$v1&>Sn5NNlX_uD<}icSAFCx09_}FJv4pY!PP15JBIqnR7{y)Nhgn1T3HE?MdDG`W} -zzQm3gc%)E{1!aK+H%xqaVEranlvwt)H)KNJW-!x6oA|I$ybC-&Q>QW&Q5aF7%CNXN -zvO-GOL0BGZM~h=?x0|j_ux8FDUgR;m{D^0?O6}%pMvVndC|S_xp9a?MJIZ(cCH>@- -zCddXQ2YYv|fqW7b?0%b>R;^af?hnWHF9&HjS=<2jJ=!p0TZJm`?p@VLuKT)ERzXs) -z$KAFaX9w}NL!nE|MLHv3_1AD7U}zXV{z|XXlI%zxsyz*FIa-Hm^nwu&O(L(0& -z`IhoW4n1~J!A+E{A(A2g&nkhTb|gFjf&I|O$5QI`vG!G^+DE_MPdNHG#B181H`mff -z?LICbOBw*R>qc9~s-l=;-BpDrn$C`4paXUida^K~<|ND5%Ki69b3p(DO -zBj_$#&9d@n@gI&QSsaNYQS{iDpZ_)yfQ5B3U*0%N8n+!pc^9zzP_1#4BN`;KF{ALkvziT!J-yS3-q>u}#@kse3DuWN -zh|tneGD3eYJA*7P#kx>gIK5T4eaYQ_!XWBK(8^1PMfN=kxBmhKj~;|V{}W_Lv?DCC -zH&I^H<%yUiUlezu{B>-gbsD>7d#JpY{$;J1YHiC|NwTbAO;m|Y7*RHk_(%6=j-c1h -z<*lowgWu(|6KJVYd-o-EdOigW(i$*3P->TUg6P}WObn@KaXvZp4Z)F_Nr6$&Q{cQ! -z=awOlET5pPZ_~*h)`|y0{HcQGMO7=GB?-6^82jLsNfwV4eyZw4Y>TS*;_~ivW8JSx -zo^bZ<&@d&!&eAt45>k@7NmDx`&ArAZDy7Of!;ZG0|I?p2!{e;Ya`S*2qFoWI9SZi= -z$LF0ZRJu&Hwk#>B4p}+U_O(+X?O^bUkEqQsp#kwA)ky0V;rva0O)3ovm@$QR0hq18 -z1NdClh3>M4K10aFRoK*NZop17?evE?I&Xsj*@-BJI}itikkFySvpeW) -z0uYCRm?u4UXGE$D%hqD5N5v=oT-^md`=jPWhqB9~V(xzz&P?=aq$wzEMo%|2`P>e< -zRy=DQ$WiPT+s3INXu!DHlte<<;rFiI8Tgyg4 -zp*Pd*lLE#pnkHo$5&m0{l8tR@^zbXIERLZyna(Fjcu{^eMvrY;W5}MR7Gm`}5#izM -zlS{{J%apbNyUI%EhdY8ojlvbkbi+lrv+HcuF0ot)UWtKlC<$7ns?Nx-+z(9Cx^#!` -z#ORw5P|51aY+QWs4|Vi5gN32tV(zo}(dgO39+pw=$G*)pL9DTzppL*6&EV#H4+2}& -zjn&|b3(Co1g+EcaVlQ0JJIPpFFfvopJb6ghV5u9r0;)ruu+)jFo|6)ddiU$9+w>#XyN)t;8WC`N{dP5k|d -zw|tKLCi}ns4u@;jBgysIbHT?aK2=Vk_|YRzL+un!lAg=xEXXyopW~E -zQlwq1fe4G_846n0sn7yd2ud+vF5aZ#4}j8EZdt?05V-%rc*pKuHF2a7eC%rr -zPAgf-(Tl(VoU3Z(V*XZnsJfjhFhN{i(&K4B`vcxx{Kr9k2{317ASYGxo1L=yzPLmP -z#tK~7?!d(M8;y4^Y}yzHoQrOLHVyktT8srdtD03)AwZfBA|6?4K-p72e0 -z`K<+7Rwsk_zwZ8IJGn{*@)hH8Rj<~hOK9=MP(gsDa+ftVLmzFP)1}h^=f}uXHEqa* -z#FGo2$@aLY8bqj74i55AL{u^kpfR^29iqh)qnc^KTqx1!YpT6#Jm{i5BQy~{#j~KQ -zIp95><3^YKa^>=AU6*V6LDpcRs@>v=7byW@bQe*zg@@{__Pah+kAU+epsu3>_NwQ# -z_xkS$S&@?PsdEb8Kz#(}W1sgFrLDUSzOfMvXi8ALMTwUM!C#0pq9p2iWlGPKGv_li -zB?8j$CuD;3Wi?$QBP2+3Fi#EEA)0&Qj1>*v@r6X`FTTUun!e3_JUCsy5S_f&pxZ03HJ -z7>6}l%`{~Uv~LLG^}NS_ZfNAt$ZG`<`@5S%5BdUHomfFP`;T*8yT)*&&$LDnM7)mu -z!^5ykC}Id0>}BbRQQSM%du(hesiFE!HQHR*=LO?i3|W5qN=jG5B6{H7vqd)agjFl}*iv -z`%#I3bbWivQQdBa^oAbZK5+(XpiImJ$;R3@I-1bW4$onrot7@QDyzl=6$;fP#i6H~ -z1Xm9gn1O1LIs!qAJv)C`L-V(lp#=KF^()8ig!79 -zVJp-Qzl0Mk-u8iO+7W_E42RXX!{~<0w+c`2V4y8S*x(PHCM;*?PwHv!`d#f8Cc8SD -zRd#GUJl_mC8g#!-gv{nirpXP#yv9>sU{|AhM9CK|)L47!YVvew(-za{en}_bZdpVB -z50$BYM4k6nEp7yV5zv)%3S|?*jbAc?DtK>ssUYp -z9#3IyuxxVRE4o{)%LVA~5^%6;A`LlM4JpMUjPChMOH7}{QI*)Dj%507cVUFEE#i0Q -zf>$3Ff@kDLh%^TU%5QmS@ZM+!qGo=X6l{mO6n!WwGFtP@X#y}AB&rM-pcmgYSCc8c -z;!OU(i(k7wIRxpHi3WUR3XYT$G|Qe2isBpERfjWrpLI*|&MjQOmL1DMU3({?u|PQg -z!4t9lX>~rwR3%lS) -zZb8+0Joq0p!&0PNk#k=Q@-!7b{)}(9$~T}o3a+li6zVy-a(nuyU+Q$NBLErCSEree -zM);`=MF@|a@M=p5ih1E%K;n-h`P{%?F75|>A`NDz#nKv; -zD5LAKS2N;|#2-I}>^<;Y`9mAf=u -z^>%n4_0+;>uWMVAaf%?Xm}q+NhygZN%LvE16qL2dZKroU*S+^g#NlB -z5KegTx!!%KK075sGA-I@bh0B%b7V#{e+*4q5aMElY1nz_MOH3*`Q-M>5e+{4&Cg>qSUlK%j|^h1AX!=UH*uF!y~EU%_TIAoDeuXwQnC`@Tk -zNQXFyRk#cS!Q_XpK!jHQmK|b;FQ3AjRLU`@_uU+0fY}OsHGUBp43I*1`VEtp -zv&Rz)Mh>4=;$H)%A8R95fI1V` -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 -N0#8>zmvv4FO#tfi_}2gc - -diff --git a/source/client/gui/Gui.cpp b/source/client/gui/Gui.cpp -index d8e94fcdf..38ccbf65c 100644 ---- a/source/client/gui/Gui.cpp -+++ b/source/client/gui/Gui.cpp -@@ -10,6 +10,7 @@ - #include "client/app/Minecraft.hpp" - #include "client/gui/screens/IngameBlockSelectionScreen.hpp" - #include "client/gui/screens/ChatScreen.hpp" -+#include "client/gui/screens/PauseScreen.hpp" - #include "client/gui/screens/inventory/InventoryScreen.hpp" - #include "client/renderer/entity/ItemRenderer.hpp" - #include "client/renderer/renderer/RenderMaterialGroup.hpp" -@@ -307,6 +308,29 @@ void Gui::handleClick(int clickID, int mouseX, int mouseY) - if (clickID != 1) - return; - -+ // @TODO: add InGamePlayScreen at some point -+ if (m_pMinecraft->isTouchscreen()) -+ { -+ int cenX = GuiWidth / 2; -+ int scaledMouseX = int(mouseX * GuiScale); -+ int scaledMouseY = int(mouseY * GuiScale); -+ -+ if (scaledMouseY >= 1 && scaledMouseY < 19 && scaledMouseX >= cenX - 19 && scaledMouseX < cenX - 1) -+ { -+ m_pMinecraft->setScreen(new ChatScreen(false)); -+ return; -+ } -+ -+ if (scaledMouseY >= 1 && scaledMouseY < 19 && scaledMouseX >= cenX && scaledMouseX < cenX + 18) -+ { -+ if (m_pMinecraft->isGamePaused()) -+ m_pMinecraft->resumeGame(); -+ else -+ m_pMinecraft->pauseGame(); -+ return; -+ } -+ } -+ - int slot = getSlotIdAt(mouseX, mouseY); - if (slot == -1) - return; -@@ -646,9 +670,6 @@ void Gui::renderToolBar(float f, float alpha) - - m_blitOffset = -90.0f; - -- // chat -- //blit(width - 18, 0, 200, 82, 18, 18, 18, 18); -- - int nSlots = getNumSlots(); - int hotbarWidth = 2 + nSlots * 20; - -@@ -670,6 +691,19 @@ void Gui::renderToolBar(float f, float alpha) - // selection mark - blit(-1 - hotbarWidth / 2 + 20 * inventory->m_selectedSlot, -23, 0, 22, 24, 22, 0, 0); - -+ // chat and pause button for mobile devices -+ if (mc->isTouchscreen()) -+ { -+ textures->loadAndBindTexture("gui/gui2.png"); -+ -+ currentShaderColor.a = 0.5f; -+ -+ blit(-19, -GuiHeight + 1, 200, 82, 18, 18, 0, 0); // chat -+ blit(0, -GuiHeight + 1, 200, 64, 18, 18, 0, 0); // pause -+ -+ currentShaderColor.a = alpha; -+ } -+ - textures->loadAndBindTexture(C_BLOCKS_NAME); - - int diff = mc->isTouchscreen(); -@@ -704,7 +738,8 @@ void Gui::renderToolBar(float f, float alpha) - - int Gui::getNumSlots() - { -- if (m_pMinecraft->isTouchscreen()) -+ Minecraft& mc = *m_pMinecraft; -+ if (mc.getOptions()->getUiTheme() == UI_POCKET) - return 6; - - return 9; -diff --git a/source/client/gui/screens/IngameBlockSelectionScreen.cpp b/source/client/gui/screens/IngameBlockSelectionScreen.cpp -index 16a91edf3..1875b5ba9 100644 ---- a/source/client/gui/screens/IngameBlockSelectionScreen.cpp -+++ b/source/client/gui/screens/IngameBlockSelectionScreen.cpp -@@ -7,8 +7,6 @@ - ********************************************************************/ - - #include "IngameBlockSelectionScreen.hpp" --#include "PauseScreen.hpp" --#include "ChatScreen.hpp" - //#include "CraftingScreen.hpp" - //#include "ArmorScreen.hpp" - #include "client/app/Minecraft.hpp" -@@ -18,8 +16,6 @@ - std::string g_sNotAvailableInDemoVersion = "Not available in the demo version"; - - IngameBlockSelectionScreen::IngameBlockSelectionScreen() : -- m_btnPause("\xF0"), // 3 lined bars -- m_btnChat("\x01\x27"), // face and comma - m_btnCraft("Craft"), - m_btnArmor("Armor") - { -@@ -187,31 +183,21 @@ bool IngameBlockSelectionScreen::isInsideSelectionArea(int x, int y) - - void IngameBlockSelectionScreen::init() - { -- m_btnPause.m_width = 25; -- m_btnPause.m_xPos = m_width - m_btnPause.m_width / 1.05; -- m_btnPause.m_yPos = 0; -- if (m_pMinecraft->isTouchscreen()) -- { -- _addElement(m_btnPause); -- } -- -- m_btnChat.m_width = 25; -- m_btnChat.m_xPos = 0; -- m_btnChat.m_yPos = 0; -- if (m_pMinecraft->isTouchscreen()) -- { -- _addElement(m_btnChat); -- } -- - /*m_btnCraft.m_width = 40; - m_btnCraft.m_xPos = 0; - m_btnCraft.m_yPos = 0; -- _addElement(m_btnCraft);*/ -+ if (m_pMinecraft->isTouchscreen()) -+ { -+ _addElement(m_btnCraft); -+ }*/ - - /*m_btnArmor.m_width = 40; - m_btnArmor.m_xPos = m_btnCraft.m_width; - m_btnArmor.m_yPos = 0; -- _addElement(m_btnArmor);*/ -+ if (m_pMinecraft->isTouchscreen()) -+ { -+ _addElement(m_btnArmor); -+ }*/ - - Inventory* pInv = getInventory(); - -@@ -288,13 +274,7 @@ void IngameBlockSelectionScreen::render(float f) - - void IngameBlockSelectionScreen::_buttonClicked(Button* pButton) - { -- if (pButton->getId() == m_btnPause.getId()) -- m_pMinecraft->getScreenChooser()->pushPauseScreen(); -- -- if (pButton->getId() == m_btnChat.getId()) -- m_pMinecraft->setScreen(new ChatScreen(true)); -- -- /*if (pButton->getId() == m_btnCraft.getId()) -+ /*if (pButton->getId() == m_btnCraft.getId()) - m_pMinecraft->setScreen(new CraftingScreen(m_pMinecraft->m_pLocalPlayer));*/ - - /*if (pButton->getId() == m_btnArmor.getId()) -diff --git a/source/client/gui/screens/IngameBlockSelectionScreen.hpp b/source/client/gui/screens/IngameBlockSelectionScreen.hpp -index a14e41fe0..097af0f64 100644 ---- a/source/client/gui/screens/IngameBlockSelectionScreen.hpp -+++ b/source/client/gui/screens/IngameBlockSelectionScreen.hpp -@@ -46,8 +46,6 @@ class IngameBlockSelectionScreen : public Screen - SlotID m_selectedSlot; - bool m_bReleased; - bool m_bClickedOnSlot; -- Button m_btnPause; -- Button m_btnChat; - Button m_btnCraft; - Button m_btnArmor; - std::vector m_items; diff --git a/03_14bb937365261fe97c2e41196d3b636e0171a1e9.patch.txt b/03_14bb937365261fe97c2e41196d3b636e0171a1e9.patch.txt deleted file mode 100644 index 536565452..000000000 --- a/03_14bb937365261fe97c2e41196d3b636e0171a1e9.patch.txt +++ /dev/null @@ -1,190 +0,0 @@ -From dafd4c772a1b4bf86379ede612487cd0ace01691 Mon Sep 17 00:00:00 2001 -From: Un1q32 -Date: Wed, 18 Feb 2026 18:58:15 -0500 -Subject: [PATCH 1/2] Fixes in macOS artifact build (#491) - ---- - platforms/macos/build.sh | 12 +++++++----- - 1 file changed, 7 insertions(+), 5 deletions(-) - -diff --git a/platforms/macos/build.sh b/platforms/macos/build.sh -index aac05da6d..441fcd2a8 100755 ---- a/platforms/macos/build.sh -+++ b/platforms/macos/build.sh -@@ -199,6 +199,7 @@ for target in $targets; do - arch="${target%%-*}" - case $arch in - (i386) -+ target_cflags="$cflags -march=pentium-m" - export REMCPE_SDK="$old_sdk" - set -- -DCMAKE_EXE_LINKER_FLAGS='-framework IOKit -framework Carbon -framework AudioUnit -undefined dynamic_lookup' - platform='sdl1' -@@ -213,9 +214,9 @@ for target in $targets; do - tar -xzf ../sdl1src.tar.gz - cd "SDL-1.2-$sdl1_commit" - if [ -n "$DEBUG" ]; then -- opt='-O2' -- else - opt='-O0' -+ else -+ opt='-O2' - fi - ./configure \ - --host="$arch-apple-darwin" \ -@@ -235,9 +236,10 @@ for target in $targets; do - printf '%s' "$sdl1ver" > sdl/sdl1ver - rm -rf "SDL-1.2-$sdl1_commit" - fi -- cflags="$cflags -I$PWD/sdl/include" -+ target_cflags="$target_cflags -I$PWD/sdl/include" - ;; - (arm64*|x86_64*) -+ target_cflags="$cflags" - case $arch in - (arm64*) - export REMCPE_SDK="$arm64_sdk" -@@ -269,8 +271,8 @@ for target in $targets; do - -DCMAKE_CXX_COMPILER="$platformdir/macos-c++" \ - -DCMAKE_FIND_ROOT_PATH="$REMCPE_SDK/usr;$PWD/sdl" \ - -DCMAKE_SYSROOT="$REMCPE_SDK" \ -- -DCMAKE_C_FLAGS="$cflags" \ -- -DCMAKE_CXX_FLAGS="$cflags" \ -+ -DCMAKE_C_FLAGS="$target_cflags" \ -+ -DCMAKE_CXX_FLAGS="$target_cflags" \ - -DWERROR="${WERROR:-OFF}" \ - "$@" - make -j"$ncpus" - -From 6bb698128e88be6516de290484538b414ca845da Mon Sep 17 00:00:00 2001 -From: Brent <43089001+BrentDaMage@users.noreply.github.com> -Date: Wed, 18 Feb 2026 21:56:37 -0800 -Subject: [PATCH 2/2] Xbox 360 / Console Bug Fixes (#492) - -* Added 720p fallback resolution for Direct3D 9 device initialization. This fixes crashes that would occur when launching the game in 1080p on the Xbox 360. -* Added a hack to fix screens being shrunk infinitely when using Java screen scaling with the Console UI theme. -* Removed gameTips.animalFollow from tips, since the localization string was missing ---- - game/assets/lang/en_us.json | 2 +- - game/assets/texts/tips.json | 3 --- - platforms/xdk360/main.cpp | 12 +++++++++-- - source/client/app/Minecraft.cpp | 10 ++++++--- - .../renderer/hal/d3d9/RenderContextD3D9.cpp | 21 ++++++++++++++++--- - 5 files changed, 36 insertions(+), 12 deletions(-) - -diff --git a/game/assets/lang/en_us.json b/game/assets/lang/en_us.json -index f4499f2d2..337c2ace6 100644 ---- a/game/assets/lang/en_us.json -+++ b/game/assets/lang/en_us.json -@@ -28,7 +28,7 @@ - "loadingTip.junkboysFace": "No-one at Mojang has ever seen junkboy's face.", - "loadingTip.largeChest": "Placing two chests side by side will make one large chest.", - "loadingTip.lavaSmelt": "A single bucket of lava can be used in a furnace to smelt 100 blocks.", -- "loadingTip.legacyInfo": "You'll get the latest info on this game from minecraft.net!", -+ "loadingTip.legacyInfo": "You'll get the latest info on this game from our Discord!", - "loadingTip.lightMelt": "Blocks that can be used as a light source will melt snow and ice. This includes torches, glowstone, and Jack O'Lanterns.", - "loadingTip.minecart": "Get to destinations faster with a minecart and rail!", - "loadingTip.mineconCalifornia": "Minecon 2016 was in Anaheim, California, USA!", -diff --git a/game/assets/texts/tips.json b/game/assets/texts/tips.json -index 2117623b4..341869de0 100644 ---- a/game/assets/texts/tips.json -+++ b/game/assets/texts/tips.json -@@ -191,9 +191,6 @@ - { - "translate": "loadingTip.updateInfo" - }, -- { -- "translate": "loadingTip.animalFollow" -- }, - { - "translate": "loadingTip.mineconLondon" - }, -diff --git a/platforms/xdk360/main.cpp b/platforms/xdk360/main.cpp -index 2af6b639a..6e932bce3 100644 ---- a/platforms/xdk360/main.cpp -+++ b/platforms/xdk360/main.cpp -@@ -12,8 +12,16 @@ void _setSize() - { - XVIDEO_MODE VideoMode; - XGetVideoMode(&VideoMode); -- Minecraft::width = Mth::Max(VideoMode.dwDisplayWidth, 1280); -- Minecraft::height = Mth::Max(VideoMode.dwDisplayHeight, 720); -+ Minecraft::width = Mth::Max(VideoMode.dwDisplayWidth, 640); -+ Minecraft::height = Mth::Max(VideoMode.dwDisplayHeight, 480); -+ -+ // Hardcoded 1080p check to avoid failed D3D device creation attempt -+ if (Minecraft::width == 1920 && Minecraft::height == 1080) -+ { -+ // too big, D3D9 Device creation will fail -+ Minecraft::width = 1280; -+ Minecraft::height = 720; -+ } - } - - void _onKeyboardClosed() -diff --git a/source/client/app/Minecraft.cpp b/source/client/app/Minecraft.cpp -index 7eb11009f..3b7cf56e9 100644 ---- a/source/client/app/Minecraft.cpp -+++ b/source/client/app/Minecraft.cpp -@@ -1059,11 +1059,15 @@ float Minecraft::getBestScaleForThisScreenSize(int width, int height) - #define USE_JAVA_SCREEN_SCALING - #endif - #ifdef USE_JAVA_SCREEN_SCALING -- int scale; -- for (scale = 1; width / (scale + 1) >= 320 && height / (scale + 1) >= 240; ++scale) -+ // @HACK: the scaling code for Java/Pocket Screens when using the Console theme is pretty broken -+ if (m_pOptions->getUiTheme() != UI_CONSOLE) - { -+ int scale; -+ for (scale = 1; width / (scale + 1) >= 320 && height / (scale + 1) >= 240; ++scale) -+ { -+ } -+ return scale; - } -- return scale; - #endif - - if (height > 1800) -diff --git a/source/renderer/hal/d3d9/RenderContextD3D9.cpp b/source/renderer/hal/d3d9/RenderContextD3D9.cpp -index 67a496aba..3c69065f8 100644 ---- a/source/renderer/hal/d3d9/RenderContextD3D9.cpp -+++ b/source/renderer/hal/d3d9/RenderContextD3D9.cpp -@@ -167,12 +167,12 @@ void RenderContextD3D9::createWindowSizeDependentResources(HWND hWnd, unsigned i - { - d3dpp.BackBufferWidth = width; - d3dpp.BackBufferHeight = height; -- d3dpp.BackBufferFormat = D3DFMT_X8R8G8B8; -+ d3dpp.BackBufferFormat = D3DFMT_A8R8G8B8; - d3dpp.BackBufferCount = 1; - d3dpp.EnableAutoDepthStencil = TRUE; - d3dpp.AutoDepthStencilFormat = D3DFMT_D24S8; - d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD; -- d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_ONE; -+ d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_ONE; // basically V-Sync - if (hWnd) - { - d3dpp.hDeviceWindow = hWnd; -@@ -181,8 +181,23 @@ void RenderContextD3D9::createWindowSizeDependentResources(HWND hWnd, unsigned i - } - - DWORD flags = D3DCREATE_HARDWARE_VERTEXPROCESSING; -+ LOG_I("Creating IDirect3DDevice9 with %dx%d size...", width, height); - HRESULT hResult = m_pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, NULL, flags, &d3dpp, *m_d3dDevice); -- ErrorHandlerD3D9::checkForErrors(hResult); -+ -+ if (hResult == E_OUTOFMEMORY) -+ { -+ width = 1280; -+ height = 720; -+ d3dpp.BackBufferWidth = width; -+ d3dpp.BackBufferHeight = height; -+ LOG_I("Failed to create IDirect3DDevice9! Falling back to (%dx%d)...", width, height); -+ HRESULT hResult = m_pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, NULL, flags, &d3dpp, *m_d3dDevice); -+ ErrorHandlerD3D9::checkForErrors(hResult); -+ } -+ else -+ { -+ ErrorHandlerD3D9::checkForErrors(hResult); -+ } - - m_d3dDevice->SetRenderState(D3DRS_SEPARATEALPHABLENDENABLE, TRUE); - m_d3dDevice->SetRenderState(D3DRS_TWOSIDEDSTENCILMODE, TRUE); diff --git a/04_d80620fdbc5bfe4a76c2c89dfa94d7ac1e7a26df.patch.txt b/04_d80620fdbc5bfe4a76c2c89dfa94d7ac1e7a26df.patch.txt deleted file mode 100644 index e3fbf91f3..000000000 --- a/04_d80620fdbc5bfe4a76c2c89dfa94d7ac1e7a26df.patch.txt +++ /dev/null @@ -1,501 +0,0 @@ -From d80620fdbc5bfe4a76c2c89dfa94d7ac1e7a26df Mon Sep 17 00:00:00 2001 -From: hellosammu -Date: Thu, 19 Feb 2026 10:17:18 -0500 -Subject: [PATCH] todo fix putting item stack in chests not relaying count to - clients when placing back (have to go to work waaaah) - ---- - .../multiplayer/MultiplayerLocalPlayer.cpp | 2 +- - .../multiplayer/MultiplayerLocalPlayer.hpp | 2 +- - source/server/ServerPlayer.cpp | 10 ++-- - source/server/ServerPlayer.hpp | 2 +- - source/server/ServerSideNetworkHandler.cpp | 1 + - source/world/inventory/CompoundContainer.cpp | 56 ++++++++++++++++++- - source/world/inventory/CompoundContainer.hpp | 11 ++++ - .../ContainerContentChangeListener.hpp | 2 +- - source/world/inventory/ContainerListener.hpp | 3 +- - source/world/inventory/ContainerMenu.cpp | 52 ++++++++++++----- - source/world/inventory/ContainerMenu.hpp | 2 +- - source/world/inventory/SimpleContainer.cpp | 2 +- - source/world/item/Inventory.cpp | 20 +++++++ - source/world/item/Inventory.hpp | 8 ++- - source/world/tile/entity/ChestTileEntity.cpp | 1 + - 15 files changed, 144 insertions(+), 30 deletions(-) - -diff --git a/source/client/multiplayer/MultiplayerLocalPlayer.cpp b/source/client/multiplayer/MultiplayerLocalPlayer.cpp -index 36750209d..772734bf5 100644 ---- a/source/client/multiplayer/MultiplayerLocalPlayer.cpp -+++ b/source/client/multiplayer/MultiplayerLocalPlayer.cpp -@@ -141,7 +141,7 @@ void MultiplayerLocalPlayer::refreshContainer(ContainerMenu* menu, const std::ve - { - } - --void MultiplayerLocalPlayer::slotChanged(ContainerMenu* menu, int index, ItemStack& item, bool isResultSlot) -+void MultiplayerLocalPlayer::slotChanged(ContainerMenu* menu, int index, Slot* slot, ItemStack& item, bool isResultSlot) - { - #if NETWORK_PROTOCOL_VERSION >= 5 - m_pMinecraft->m_pRakNetInstance->send(new ContainerSetSlotPacket(m_pContainerMenu->m_containerId, index, item)); -diff --git a/source/client/multiplayer/MultiplayerLocalPlayer.hpp b/source/client/multiplayer/MultiplayerLocalPlayer.hpp -index 6ab1a580b..fffd41ec5 100644 ---- a/source/client/multiplayer/MultiplayerLocalPlayer.hpp -+++ b/source/client/multiplayer/MultiplayerLocalPlayer.hpp -@@ -22,7 +22,7 @@ class MultiplayerLocalPlayer : public LocalPlayer, public ContainerListener - void closeContainer() override; - - void refreshContainer(ContainerMenu* menu, const std::vector& items) override; -- void slotChanged(ContainerMenu* menu, int index, ItemStack& item, bool isResultSlot) override; -+ void slotChanged(ContainerMenu* menu, int index, Slot* slot, ItemStack& item, bool isResultSlot) override; - - private: - bool m_flashOnSetHealth; -diff --git a/source/server/ServerPlayer.cpp b/source/server/ServerPlayer.cpp -index a2a5d7cc5..d5bc21ebc 100644 ---- a/source/server/ServerPlayer.cpp -+++ b/source/server/ServerPlayer.cpp -@@ -14,6 +14,7 @@ - #include "world/inventory/ChestMenu.hpp" - #include "world/level/Level.hpp" - #include "world/tile/entity/FurnaceTileEntity.hpp" -+#include "world/inventory/Slot.hpp" - - ServerPlayer::ServerPlayer(Level* pLevel, GameType playerGameType) - : Player(pLevel, playerGameType) -@@ -122,13 +123,13 @@ void ServerPlayer::refreshContainer(ContainerMenu* menu, const std::vector= 5 -- if (!isResultSlot) -- { -+ // @TODO: See my gripes in ContainerMenu::slotChanged -+ // But ultimately this is a bandaid for the fact that the client has authority over the inventory in PE -+ if (!isResultSlot && slot->m_group != Slot::INVENTORY && slot->m_group != Slot::HOTBAR) - m_pLevel->m_pRakNetInstance->send(new ContainerSetSlotPacket(menu->m_containerId, index, item)); -- } - #endif - } - -@@ -162,5 +163,6 @@ void ServerPlayer::setContainerMenu(ContainerMenu* menu) - m_pContainerMenu->m_containerId = m_containerId; - m_pContainerMenu->addSlotListener(this); - refreshContainer(m_pContainerMenu, m_pContainerMenu->cloneItems()); -+ m_pContainerMenu->broadcastChanges(); - } - } -\ No newline at end of file -diff --git a/source/server/ServerPlayer.hpp b/source/server/ServerPlayer.hpp -index 874a0f1cc..2f08d5191 100644 ---- a/source/server/ServerPlayer.hpp -+++ b/source/server/ServerPlayer.hpp -@@ -19,7 +19,7 @@ class ServerPlayer : public Player, public ContainerListener - void take(Entity* pEnt, int count) override; - - void refreshContainer(ContainerMenu* menu, const std::vector& items) override; -- void slotChanged(ContainerMenu* menu, int index, ItemStack& item, bool isResultSlot) override; -+ void slotChanged(ContainerMenu* menu, int index, Slot* slot, ItemStack& item, bool isResultSlot) override; - void setContainerData(ContainerMenu* menu, int id, int value) override; - - void doCloseContainer(); -diff --git a/source/server/ServerSideNetworkHandler.cpp b/source/server/ServerSideNetworkHandler.cpp -index b88fc72d0..0651c8037 100644 ---- a/source/server/ServerSideNetworkHandler.cpp -+++ b/source/server/ServerSideNetworkHandler.cpp -@@ -640,6 +640,7 @@ void ServerSideNetworkHandler::handle(const RakNet::RakNetGUID& guid, ContainerS - switch (pContainerMenu->m_containerType) - { - case Container::FURNACE: -+ case Container::CONTAINER: - pContainerMenu->setItem(packet->m_slot, packet->m_item); - break; - default: -diff --git a/source/world/inventory/CompoundContainer.cpp b/source/world/inventory/CompoundContainer.cpp -index ea4d4dd1c..21adf1e2d 100644 ---- a/source/world/inventory/CompoundContainer.cpp -+++ b/source/world/inventory/CompoundContainer.cpp -@@ -1,8 +1,45 @@ - #include "CompoundContainer.hpp" - -+class CompoundContainer::ChildListener : public ContainerContentChangeListener -+{ -+public: -+ ChildListener(CompoundContainer* owner, int offset) -+ : m_pOwner(owner), m_offset(offset) -+ { -+ } -+ -+ void containerContentChanged(Container* container, SlotID slot) override -+ { -+ if (m_pOwner) -+ m_pOwner->setContainerChanged(slot + m_offset); -+ } -+ -+private: -+ CompoundContainer* m_pOwner; -+ int m_offset; -+}; -+ - CompoundContainer::CompoundContainer(const std::string& name, Container* c1, Container* c2) : -- m_name(name), m_pLeftContainer(c1), m_pRightContainer(c2) -+ m_name(name), m_pLeftContainer(c1), m_pRightContainer(c2), m_pLeftListener(nullptr), m_pRightListener(nullptr) - { -+ m_pLeftListener = new ChildListener(this, 0); -+ m_pRightListener = new ChildListener(this, m_pLeftContainer->getContainerSize()); -+ -+ if (m_pLeftContainer) -+ m_pLeftContainer->addContentChangeListener(m_pLeftListener); -+ if (m_pRightContainer) -+ m_pRightContainer->addContentChangeListener(m_pRightListener); -+} -+ -+CompoundContainer::~CompoundContainer() -+{ -+ if (m_pLeftContainer && m_pLeftListener) -+ m_pLeftContainer->removeContentChangeListener(m_pLeftListener); -+ if (m_pRightContainer && m_pRightListener) -+ m_pRightContainer->removeContentChangeListener(m_pRightListener); -+ -+ delete m_pLeftListener; -+ delete m_pRightListener; - } - - uint16_t CompoundContainer::getContainerSize() const -@@ -46,8 +83,11 @@ int CompoundContainer::getMaxStackSize() - - void CompoundContainer::setContainerChanged(SlotID slot) - { -- m_pLeftContainer->setContainerChanged(slot); -- m_pRightContainer->setContainerChanged(slot); -+ for (ContentChangeListeners::iterator it = m_contentChangeListeners.begin(); it != m_contentChangeListeners.end(); ++it) -+ { -+ ContainerContentChangeListener* listener = *it; -+ listener->containerContentChanged(this, slot); -+ } - } - - bool CompoundContainer::stillValid(Player* player) const -@@ -55,3 +95,13 @@ bool CompoundContainer::stillValid(Player* player) const - return m_pLeftContainer->stillValid(player) && m_pRightContainer->stillValid(player); - } - -+void CompoundContainer::addContentChangeListener(ContainerContentChangeListener* listener) -+{ -+ m_contentChangeListeners.insert(listener); -+} -+ -+void CompoundContainer::removeContentChangeListener(ContainerContentChangeListener* listener) -+{ -+ m_contentChangeListeners.erase(listener); -+} -+ -diff --git a/source/world/inventory/CompoundContainer.hpp b/source/world/inventory/CompoundContainer.hpp -index 1c3fd0fea..a429f1ceb 100644 ---- a/source/world/inventory/CompoundContainer.hpp -+++ b/source/world/inventory/CompoundContainer.hpp -@@ -1,17 +1,25 @@ - #pragma once - -+#include - #include - #include "Container.hpp" -+#include "ContainerContentChangeListener.hpp" - - class CompoundContainer : public Container - { - private: -+ class ChildListener; -+ - std::string m_name; - Container* m_pLeftContainer; - Container* m_pRightContainer; -+ ContentChangeListeners m_contentChangeListeners; -+ ChildListener* m_pLeftListener; -+ ChildListener* m_pRightListener; - - public: - CompoundContainer(const std::string& name, Container* c1, Container* c2); -+ ~CompoundContainer() override; - - uint16_t getContainerSize() const override; - -@@ -28,4 +36,7 @@ class CompoundContainer : public Container - void setContainerChanged(SlotID slot) override; - - bool stillValid(Player* player) const override; -+ -+ void addContentChangeListener(ContainerContentChangeListener* listener) override; -+ void removeContentChangeListener(ContainerContentChangeListener* listener) override; - }; -diff --git a/source/world/inventory/ContainerContentChangeListener.hpp b/source/world/inventory/ContainerContentChangeListener.hpp -index 675f1b221..ed0f7139f 100644 ---- a/source/world/inventory/ContainerContentChangeListener.hpp -+++ b/source/world/inventory/ContainerContentChangeListener.hpp -@@ -8,5 +8,5 @@ class ContainerContentChangeListener - virtual ~ContainerContentChangeListener() {} - - public: -- virtual void containerContentChanged(SlotID slot) = 0; -+ virtual void containerContentChanged(Container* container, SlotID slot) = 0; - }; -diff --git a/source/world/inventory/ContainerListener.hpp b/source/world/inventory/ContainerListener.hpp -index a159ae5db..79a63bb7e 100644 ---- a/source/world/inventory/ContainerListener.hpp -+++ b/source/world/inventory/ContainerListener.hpp -@@ -4,6 +4,7 @@ - - class ContainerMenu; - class ItemStack; -+class Slot; - - class ContainerListener - { -@@ -12,6 +13,6 @@ class ContainerListener - - virtual void refreshContainer(ContainerMenu* menu, const std::vector& items) {} - virtual void refreshContainerItems(ContainerMenu* menu); -- virtual void slotChanged(ContainerMenu* menu, int index, ItemStack& item, bool isResultSlot) {} -+ virtual void slotChanged(ContainerMenu* menu, int index, Slot* slot, ItemStack& item, bool isResultSlot) {} - virtual void setContainerData(ContainerMenu* menu, int id, int value) {} - }; -\ No newline at end of file -diff --git a/source/world/inventory/ContainerMenu.cpp b/source/world/inventory/ContainerMenu.cpp -index 7e52498df..936cb1d3e 100644 ---- a/source/world/inventory/ContainerMenu.cpp -+++ b/source/world/inventory/ContainerMenu.cpp -@@ -54,11 +54,6 @@ void ContainerMenu::addSlot(Slot* slot) - void ContainerMenu::addSlotListener(ContainerListener* listener) - { - m_listeners.insert(listener); -- -- // Not done on PE -- /*std::vector snapshot = cloneItems(); -- listener->refreshContainer(this, snapshot); -- broadcastChanges();*/ - } - - void ContainerMenu::sendData(int id, int value) -@@ -74,7 +69,7 @@ void ContainerMenu::broadcastChanges(SlotID slot) - { - m_lastSlots[slot] = ItemStack(current); - for (ContainerListeners::iterator it = m_listeners.begin(); it != m_listeners.end(); ++it) -- (*it)->slotChanged(this, slot, m_lastSlots[slot], isResultSlot()); -+ (*it)->slotChanged(this, slot, m_slots[slot], m_lastSlots[slot], isResultSlot()); - } - } - -@@ -101,12 +96,36 @@ void ContainerMenu::slotsChanged(Container*) - broadcastChanges(); - } - -+void ContainerMenu::containerContentChanged(Container* container, SlotID containerSlot) -+{ -+ // containerSlot is an index within a specific container, but m_slots contains -+ // slots from multiple containers. We need to find which slot in m_slots corresponds -+ // to this container slot by matching both the container and the slot index. -+ for (size_t i = 0; i < m_slots.size(); ++i) -+ { -+ Slot* slot = m_slots[i]; -+ if (slot->m_pContainer == container && slot->m_slot == containerSlot) -+ { -+ if (m_bBroadcastChanges) -+ broadcastChanges(i); -+ return; -+ } -+ } -+} -+ - std::vector ContainerMenu::cloneItems() - { - std::vector content; - - for (std::vector::iterator it = m_slots.begin(); it != m_slots.end(); ++it) - { -+ Slot* slot = *it; -+ // @TODO: I really do not like this. -+ // Firstly, inventories shouldn't be owned by the client -+ // Secondly, we shouldn't be checking types directly like this -+ // Ultimately this HAS to have two different storages, one for the inventory and one for the container -+ if (slot->m_group == Slot::INVENTORY || slot->m_group == Slot::HOTBAR) -+ continue; - const ItemStack& item = (*it)->getItem(); - content.push_back(item); - } -@@ -247,8 +266,6 @@ ItemStack ContainerMenu::clicked(int slotIndex, MouseButtonType mouseButton, boo - if (!slot) - return result; - -- slot->setChanged(); -- - ItemStack& slotItem = slot->getItem(); - - if (!slotItem.isEmpty()) -@@ -289,6 +306,7 @@ ItemStack ContainerMenu::clicked(int slotIndex, MouseButtonType mouseButton, boo - if (carried.m_count <= slot->getMaxStackSize()) - { - std::swap(carried, slotItem); -+ slot->setChanged(); - } - } - else if (slotItem.getId() == carried.getId()) -@@ -309,6 +327,7 @@ ItemStack ContainerMenu::clicked(int slotIndex, MouseButtonType mouseButton, boo - inv->setCarried(ItemStack::EMPTY); - - slotItem.m_count += count; -+ slot->setChanged(); - break; - } - case MOUSE_BUTTON_RIGHT: -@@ -325,6 +344,7 @@ ItemStack ContainerMenu::clicked(int slotIndex, MouseButtonType mouseButton, boo - inv->setCarried(ItemStack::EMPTY); - - slotItem.m_count += count; -+ slot->setChanged(); - break; - } - default: -@@ -358,6 +378,10 @@ ItemStack ContainerMenu::clicked(int slotIndex, MouseButtonType mouseButton, boo - void ContainerMenu::setItem(int index, ItemStack item) - { - m_slots[index]->set(item); -+ if (!m_bBroadcastChanges && index >= 0 && index < (int)m_lastSlots.size()) -+ { -+ m_lastSlots[index] = ItemStack(m_slots[index]->getItem()); -+ } - } - - void ContainerMenu::setAll(const std::vector& items) -@@ -366,6 +390,10 @@ void ContainerMenu::setAll(const std::vector& items) - for (size_t i = 0; i < n; ++i) - { - m_slots[i]->set(items[i]); -+ if (!m_bBroadcastChanges) -+ { -+ m_lastSlots[i] = ItemStack(m_slots[i]->getItem()); -+ } - } - } - -@@ -397,10 +425,4 @@ void ContainerMenu::setSynched(Player* player, bool isSynched) - m_unsynchedPlayers.erase(player); - else - m_unsynchedPlayers.insert(player); --} -- --void ContainerMenu::containerContentChanged(SlotID slot) --{ -- if (m_bBroadcastChanges) -- broadcastChanges(slot); --} -+} -\ No newline at end of file -diff --git a/source/world/inventory/ContainerMenu.hpp b/source/world/inventory/ContainerMenu.hpp -index 481d199be..45d1aa360 100644 ---- a/source/world/inventory/ContainerMenu.hpp -+++ b/source/world/inventory/ContainerMenu.hpp -@@ -59,7 +59,7 @@ class ContainerMenu : public ContainerContentChangeListener - virtual bool isPauseScreen() const { return false; } - - public: -- void containerContentChanged(SlotID slot) override; -+ void containerContentChanged(Container* container, SlotID slot) override; - - protected: - std::vector m_lastSlots; -diff --git a/source/world/inventory/SimpleContainer.cpp b/source/world/inventory/SimpleContainer.cpp -index 62d57425c..d6ce69a77 100644 ---- a/source/world/inventory/SimpleContainer.cpp -+++ b/source/world/inventory/SimpleContainer.cpp -@@ -62,7 +62,7 @@ void SimpleContainer::setContainerChanged(SlotID slot) - for (ContentChangeListeners::iterator it = m_contentChangeListeners.begin(); it != m_contentChangeListeners.end(); it++) - { - ContainerContentChangeListener* pListener = *it; -- pListener->containerContentChanged(slot); -+ pListener->containerContentChanged(this, slot); - } - } - -diff --git a/source/world/item/Inventory.cpp b/source/world/item/Inventory.cpp -index e0471397b..a14c88e24 100644 ---- a/source/world/item/Inventory.cpp -+++ b/source/world/item/Inventory.cpp -@@ -4,6 +4,7 @@ - #include "common/Logger.hpp" - #include "nbt/CompoundTag.hpp" - #include "network/Packet.hpp" -+#include "world/inventory/ContainerContentChangeListener.hpp" - - #include "Item.hpp" - -@@ -627,3 +628,22 @@ GameType Inventory::_getGameMode() const - { - return m_pPlayer->getPlayerGameType(); - } -+ -+void Inventory::setContainerChanged(SlotID slot) -+{ -+ for (ContentChangeListeners::iterator it = m_contentChangeListeners.begin(); it != m_contentChangeListeners.end(); ++it) -+ { -+ ContainerContentChangeListener* listener = *it; -+ listener->containerContentChanged(this, slot); -+ } -+} -+ -+void Inventory::addContentChangeListener(ContainerContentChangeListener* listener) -+{ -+ m_contentChangeListeners.insert(listener); -+} -+ -+void Inventory::removeContentChangeListener(ContainerContentChangeListener* listener) -+{ -+ m_contentChangeListeners.erase(listener); -+} -diff --git a/source/world/item/Inventory.hpp b/source/world/item/Inventory.hpp -index 876d06183..09a5c7882 100644 ---- a/source/world/item/Inventory.hpp -+++ b/source/world/item/Inventory.hpp -@@ -1,5 +1,6 @@ - #pragma once - -+#include - #include - #include "GameMods.hpp" - #include "world/inventory/Container.hpp" -@@ -19,6 +20,8 @@ class Player; // in case we're included from Player.hpp - - class Inventory : public Container - { -+private: -+ typedef std::set ContentChangeListeners; - public: - Inventory(Player*); - virtual ~Inventory(); -@@ -80,7 +83,9 @@ class Inventory : public Container - return "Inventory"; - } - -- void setContainerChanged(SlotID slot) override { } -+ void setContainerChanged(SlotID slot) override; -+ void addContentChangeListener(ContainerContentChangeListener* listener) override; -+ void removeContentChangeListener(ContainerContentChangeListener* listener) override; - - bool stillValid(Player* player) const override { return true; } - -@@ -100,4 +105,5 @@ class Inventory : public Container - - std::vector m_items; - std::vector m_armor; -+ ContentChangeListeners m_contentChangeListeners; - }; -diff --git a/source/world/tile/entity/ChestTileEntity.cpp b/source/world/tile/entity/ChestTileEntity.cpp -index 323cfa7eb..e18c18130 100644 ---- a/source/world/tile/entity/ChestTileEntity.cpp -+++ b/source/world/tile/entity/ChestTileEntity.cpp -@@ -27,5 +27,6 @@ bool ChestTileEntity::stillValid(Player* player) const - - void ChestTileEntity::setContainerChanged(SlotID slot) - { -+ SimpleContainer::setContainerChanged(slot); - TileEntity::setChanged(); - } diff --git a/05_a55606e83bc6fa100cd978fda4b14d6cc0cec4e0.patch.txt b/05_a55606e83bc6fa100cd978fda4b14d6cc0cec4e0.patch.txt deleted file mode 100644 index c55e240e3..000000000 --- a/05_a55606e83bc6fa100cd978fda4b14d6cc0cec4e0.patch.txt +++ /dev/null @@ -1,6842 +0,0 @@ -From cff514fb41805a8a248b0f8499759246d5c6a247 Mon Sep 17 00:00:00 2001 -From: Brent <43089001+BrentDaMage@users.noreply.github.com> -Date: Thu, 19 Feb 2026 20:23:31 -0800 -Subject: [PATCH 1/9] Fixed Xbox 360 Hanging on Startup (#493) - -Fixed device storage selection UI not rendering, which would result in the game hanging when loading the options. -This would only affect devices with more than one storage device attached. ---- - platforms/xdk360/AppPlatform_xdk360.cpp | 28 +++++++++++++++---- - .../renderer/hal/base/RenderContextBase.cpp | 8 ++++++ - .../renderer/hal/base/RenderContextBase.hpp | 2 ++ - .../renderer/hal/d3d9/RenderContextD3D9.cpp | 21 ++++++++++++-- - .../renderer/hal/d3d9/RenderContextD3D9.hpp | 2 ++ - 5 files changed, 54 insertions(+), 7 deletions(-) - -diff --git a/platforms/xdk360/AppPlatform_xdk360.cpp b/platforms/xdk360/AppPlatform_xdk360.cpp -index 0cf1313db..a0fb29f2f 100644 ---- a/platforms/xdk360/AppPlatform_xdk360.cpp -+++ b/platforms/xdk360/AppPlatform_xdk360.cpp -@@ -51,8 +51,17 @@ const XCONTENTDEVICEID& AppPlatform_xdk360::_getSaveDeviceId(LocalPlayerID playe - if (deviceId != C_SAVEDEVICE_ID_NONE) - return deviceId; - -+ /*mce::RenderContext& renderContext = mce::RenderContextImmediate::get(); -+ // This is required in order for the system UI to render in a blocking context -+ renderContext.suspend();*/ -+ -+ // Create event for asynchronous writing -+ HANDLE hEventComplete = NULL; //CreateEvent(NULL, FALSE, FALSE, NULL); -+ - ULARGE_INTEGER iBytesRequested = { C_SAVEDEVICE_MINIMUM_FREE_BYTES }; - XOVERLAPPED xov = {0}; -+ if (hEventComplete) -+ xov.hEvent = hEventComplete; - - deviceId = C_SAVEDEVICE_ID_PENDING; - DWORD result = XShowDeviceSelectorUI( -@@ -63,7 +72,10 @@ const XCONTENTDEVICEID& AppPlatform_xdk360::_getSaveDeviceId(LocalPlayerID playe - &xov - ); - -- if (result != ERROR_IO_PENDING) -+ if (result != ERROR_IO_PENDING -+ // Only block if we could get an event handle -+ || (hEventComplete && XGetOverlappedResult(&xov, NULL, TRUE) != ERROR_SUCCESS) -+ ) - { - LOG_W("Failed to open save device for player %d", playerId); - deviceId = C_SAVEDEVICE_ID_ERROR; -@@ -72,9 +84,13 @@ const XCONTENTDEVICEID& AppPlatform_xdk360::_getSaveDeviceId(LocalPlayerID playe - // Wait for the save-device handle - while (XHasOverlappedIoCompleted(&xov) == FALSE) - { -+ swapBuffers(); -+ // This only ends up getting run if the above event handle couldn't be created and waited on - sleepMs(20); - } - -+ //renderContext.resume(); -+ - return deviceId; - } - -@@ -154,7 +170,8 @@ void AppPlatform_xdk360::beginProfileDataRead(LocalPlayerID playerId) - XCONTENT_DATA contentData; - _getXContentData(contentData, playerId); - -- XUID xuid; -+ // Fuck this check, it just fails sometimes for no apparent reason -+ /*XUID xuid; - BOOL playerIsCreator; - XContentGetCreator(playerId, &contentData, &playerIsCreator, &xuid, NULL); - -@@ -162,7 +179,7 @@ void AppPlatform_xdk360::beginProfileDataRead(LocalPlayerID playerId) - { - LOG_E("Current player is not the creator of, and therefore cannot write to, the requested profile data!"); - throw std::bad_cast(); -- } -+ }*/ - - // Mount the device to the "savedrive" drive name - DWORD result = XContentCreate(playerId, "savedrive", &contentData, XCONTENTFLAG_OPENEXISTING, NULL, NULL, NULL); -@@ -187,8 +204,9 @@ void AppPlatform_xdk360::endProfileDataRead(LocalPlayerID playerId) - { - if (m_currentSavingPlayerId == -1) - { -- LOG_E("Tried to end profile data read for player %d, but no one is saving any data!", playerId); -- throw std::bad_cast(); -+ LOG_W("Tried to end profile data read for player %d, but no one is saving any data!", playerId); -+ //throw std::bad_cast(); -+ return; - } - - XContentClose("savedrive", NULL); -diff --git a/source/renderer/hal/base/RenderContextBase.cpp b/source/renderer/hal/base/RenderContextBase.cpp -index 165041986..7bcdfcf1a 100644 ---- a/source/renderer/hal/base/RenderContextBase.cpp -+++ b/source/renderer/hal/base/RenderContextBase.cpp -@@ -104,6 +104,14 @@ void RenderContextBase::endRender() - { - } - -+void RenderContextBase::suspend() -+{ -+} -+ -+void RenderContextBase::resume() -+{ -+} -+ - void RenderContextBase::swapBuffers() - { - } -diff --git a/source/renderer/hal/base/RenderContextBase.hpp b/source/renderer/hal/base/RenderContextBase.hpp -index a949fa134..5b869f843 100644 ---- a/source/renderer/hal/base/RenderContextBase.hpp -+++ b/source/renderer/hal/base/RenderContextBase.hpp -@@ -50,6 +50,8 @@ namespace mce - void setRenderTarget(); - void beginRender(); - void endRender(); -+ void suspend(); -+ void resume(); - void swapBuffers(); - void lostContext(); - -diff --git a/source/renderer/hal/d3d9/RenderContextD3D9.cpp b/source/renderer/hal/d3d9/RenderContextD3D9.cpp -index 3c69065f8..329005ee7 100644 ---- a/source/renderer/hal/d3d9/RenderContextD3D9.cpp -+++ b/source/renderer/hal/d3d9/RenderContextD3D9.cpp -@@ -1,5 +1,6 @@ - #include - #include "RenderContextD3D9.hpp" -+#include "compat/PlatformDefinitions.h" - #include "common/Logger.hpp" - #include "renderer/hal/d3d9/helpers/ErrorHandlerD3D9.hpp" - -@@ -117,7 +118,8 @@ void RenderContextD3D9::beginRender() - #if MCE_GFX_D3D9_SHADER_CONSTANT_BUFFERS - m_d3dDevice->GpuOwn(D3DTAG_VERTEXSHADERCONSTANTS); - m_d3dDevice->GpuOwn(D3DTAG_PIXELSHADERCONSTANTS); --#else -+#endif -+#if !MC_PLATFORM_XBOX360 - m_d3dDevice->BeginScene(); - #endif - } -@@ -126,11 +128,26 @@ void RenderContextD3D9::endRender() - { - #if MCE_GFX_D3D9_SHADER_CONSTANT_BUFFERS - m_d3dDevice->GpuDisownAll(); --#else -+#endif -+#if !MC_PLATFORM_XBOX360 - m_d3dDevice->EndScene(); - #endif - } - -+void RenderContextD3D9::suspend() -+{ -+#if MC_PLATFORM_XBOX360 -+ m_d3dDevice->Suspend(); -+#endif -+} -+ -+void RenderContextD3D9::resume() -+{ -+#if MC_PLATFORM_XBOX360 -+ m_d3dDevice->Resume(); -+#endif -+} -+ - void RenderContextD3D9::swapBuffers() - { - HRESULT hr = m_d3dDevice->Present(NULL, NULL, NULL, NULL); -diff --git a/source/renderer/hal/d3d9/RenderContextD3D9.hpp b/source/renderer/hal/d3d9/RenderContextD3D9.hpp -index d337803c5..d5c303fdc 100644 ---- a/source/renderer/hal/d3d9/RenderContextD3D9.hpp -+++ b/source/renderer/hal/d3d9/RenderContextD3D9.hpp -@@ -66,6 +66,8 @@ namespace mce - void clearContextState(); - void beginRender(); - void endRender(); -+ void suspend(); -+ void resume(); - void swapBuffers(); - - bool supports8BitIndices() const; - -From ec296519ed77f172ff5754b74f8a4cafc8de6d4f Mon Sep 17 00:00:00 2001 -From: Vimdo -Date: Thu, 19 Feb 2026 23:46:31 -0500 -Subject: [PATCH 2/9] Mipmap Icons for Android (#488) - ---- - game/assets/{ => app/icons}/icon.ico | Bin - game/assets/app/icons/icon.jpg | Bin 0 -> 24462 bytes - game/assets/{ => app/icons}/icon.png | Bin - game/assets/app/icons/mipmap/icon_144x144.png | Bin 0 -> 12364 bytes - game/assets/app/icons/mipmap/icon_192x192.png | Bin 0 -> 20170 bytes - game/assets/app/icons/mipmap/icon_48x48.png | Bin 0 -> 2007 bytes - game/assets/app/icons/mipmap/icon_64x64.png | Bin 0 -> 3071 bytes - game/assets/app/icons/mipmap/icon_72x72.png | Bin 0 -> 3808 bytes - game/assets/app/icons/mipmap/icon_96x96.png | Bin 0 -> 6294 bytes - game/assets/icon.jpg | Bin 24427 -> 0 bytes - game/assets/icon_64x64.png | Bin 7689 -> 0 bytes - platforms/android/project/app/build.gradle | 25 ++++++ - .../project/app/src/main/AndroidManifest.xml | 1 + - platforms/ios/build-ipa.sh | 2 +- - .../Minecraft.xcodeproj/project.pbxproj | 2 +- - platforms/sdl/base/AppPlatform_sdl.cpp | 2 +- - platforms/sdl/sdl2/CMakeLists.txt | 2 +- - platforms/sdl/sdl2/android/app/build.gradle | 22 ++++++ - .../android/app/src/main/AndroidManifest.xml | 3 +- - .../drawable-v24/ic_launcher_foreground.xml | 31 -------- - .../res/drawable/ic_launcher_background.xml | 74 ------------------ - .../res/mipmap-anydpi-v26/ic_launcher.xml | 5 -- - .../mipmap-anydpi-v26/ic_launcher_round.xml | 5 -- - .../src/main/res/mipmap-hdpi/ic_launcher.webp | Bin 2682 -> 0 bytes - .../res/mipmap-hdpi/ic_launcher_round.webp | Bin 4342 -> 0 bytes - .../src/main/res/mipmap-mdpi/ic_launcher.webp | Bin 2172 -> 0 bytes - .../res/mipmap-mdpi/ic_launcher_round.webp | Bin 2924 -> 0 bytes - .../main/res/mipmap-xhdpi/ic_launcher.webp | Bin 4010 -> 0 bytes - .../res/mipmap-xhdpi/ic_launcher_round.webp | Bin 6554 -> 0 bytes - .../main/res/mipmap-xxhdpi/ic_launcher.webp | Bin 5382 -> 0 bytes - .../res/mipmap-xxhdpi/ic_launcher_round.webp | Bin 9466 -> 0 bytes - .../main/res/mipmap-xxxhdpi/ic_launcher.webp | Bin 7624 -> 0 bytes - .../res/mipmap-xxxhdpi/ic_launcher_round.webp | Bin 12522 -> 0 bytes - platforms/windows/MinecraftClient.Win32.rc | 4 +- - .../MinecraftClient.Win32.vcxproj | 2 +- - .../MinecraftClient.Win32.vcxproj.filters | 2 +- - platforms/xdk360/ReMinecraftPE.xlast | Bin 16354 -> 16422 bytes - 37 files changed, 57 insertions(+), 125 deletions(-) - rename game/assets/{ => app/icons}/icon.ico (100%) - create mode 100644 game/assets/app/icons/icon.jpg - rename game/assets/{ => app/icons}/icon.png (100%) - create mode 100644 game/assets/app/icons/mipmap/icon_144x144.png - create mode 100644 game/assets/app/icons/mipmap/icon_192x192.png - create mode 100644 game/assets/app/icons/mipmap/icon_48x48.png - create mode 100644 game/assets/app/icons/mipmap/icon_64x64.png - create mode 100644 game/assets/app/icons/mipmap/icon_72x72.png - create mode 100644 game/assets/app/icons/mipmap/icon_96x96.png - delete mode 100644 game/assets/icon.jpg - delete mode 100644 game/assets/icon_64x64.png - delete mode 100644 platforms/sdl/sdl2/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml - delete mode 100644 platforms/sdl/sdl2/android/app/src/main/res/drawable/ic_launcher_background.xml - delete mode 100644 platforms/sdl/sdl2/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml - delete mode 100644 platforms/sdl/sdl2/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml - delete mode 100644 platforms/sdl/sdl2/android/app/src/main/res/mipmap-hdpi/ic_launcher.webp - delete mode 100644 platforms/sdl/sdl2/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp - delete mode 100644 platforms/sdl/sdl2/android/app/src/main/res/mipmap-mdpi/ic_launcher.webp - delete mode 100644 platforms/sdl/sdl2/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp - delete mode 100644 platforms/sdl/sdl2/android/app/src/main/res/mipmap-xhdpi/ic_launcher.webp - delete mode 100644 platforms/sdl/sdl2/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp - delete mode 100644 platforms/sdl/sdl2/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp - delete mode 100644 platforms/sdl/sdl2/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp - delete mode 100644 platforms/sdl/sdl2/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp - delete mode 100644 platforms/sdl/sdl2/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp - -diff --git a/game/assets/icon.ico b/game/assets/app/icons/icon.ico -similarity index 100% -rename from game/assets/icon.ico -rename to game/assets/app/icons/icon.ico -diff --git a/game/assets/app/icons/icon.jpg b/game/assets/app/icons/icon.jpg -new file mode 100644 -index 0000000000000000000000000000000000000000..16ca8e5db82360d6db6f6a2611232d8908405499 -GIT binary patch -literal 24462 -zcmb4KWmMfvw7s~y7Ax-VuEpKGxVsfEZpGc*-MzrYy|lPrphz$7F7JLn-dgYPJ6S6$ -znPko+nUlSDa%MhOKXw3^3NrFC04OME01adTd~5)u05H)1*?(&oh{3|c{%45r@Nn=* -zh{(uDh)77ts2HfoDCj6iNN8AS=opxoSeVGD*f`jjIFN13|C~VmR}%&n5mFHo1qlUG -z`v0^KAOH&y3IGLwfx-enV?n`SL4Aw>NC5ySI4Fn#{@0)ZP%yA?2=ItVki+7b0B9&^ -zIA~ZHcvv_{`;hw3FtAtvIBW_IcnJg?N_9jtH(V;tkmO=Xjpj={^T9t?NL;)4)b1$+ -zgrQOp4_LaqjKE*3xx@VcN@OQ<%emm1hP#>Ye< -zSBH<;65k+4yJ6-}_fP%HxV)9*!8hXA?|Kw~lAd+}b^HwCT<7myyvm=jv0KV&h8d(tgj8#~?~HK6^gshF?R6XnVM!u;<$aL3g9F=0_>A2^u=V6mM* -z0RmqbU=^qtt5UGVS4N{=b`?w+zRsRxNSaZ{^YZmQegKRp`f-G;WT}-+CHc{pdtYFp -z1QggSTx-eic-ql)g{ny-aZ~aDWqO}ynkn@rmjQ;v7=;518_1f6ST#I9x;Ixt+u -z*j(1KWcF_WIZ@&wpqEzP0w0XV)L)el3z>EkS@E?mUPkZpj_Vs{-2; -zp3!O4@-CZpHjA8Hgv??#zs5Y-Als%;vmQ9Rn7ahVoW1ODwh#fdx%h`>{)8gVqjkR~ -zP%o@W58>Hs|2|uj$qi-epwoTvSfew_TTo!5RHLb-HK!m{OQCg?mAJ(JB{D@8c6 -zZo~weV4iW|u)xMpa!FdHl5pSXul#dcqBHpqJFiV~?gc(FqmuFz#_hxtXrpt}_k;h! -zw%|j-RdT;-!meVmZbg0}#s8KNVnY799F2K8GFIp1%GWuE^`LRDWFgs8y!@_5Q~&4^ -zARxzp_(vu7kkml>{2R*}$`smx%V! -z!~g~rTuuE|hvsBGV@y|gPyw075OUVJ;V)^Lv41kh-e+ea{n6evzs$&rrpn-&?odL` -zUiL-}TI|g#HAZ9Mo5@%4o9{)M327A7kkjsDir38@ufIOtONt_ApX{Yb8SVZeib2-=NxoKvDBg -zT)df8WqBV2z8;a+fRfM&E-6qT#sVcgIxz^S3#XuOAK|D=IynHoorO@VaYptB%L_R* -z4N+?3z4f}sg)Bp*mMiUOr4rR3t1TNNBmN2 -zn^P)HU7h5(^`4c0TU4xfimH%P@RB_G61#J!fb7~3*K`aZZKw2@;_LD+;b;b}zBYe4 -zEkDoJG^$6m!&wwz?hkRf==daDhJb8~m5!15@0Foe-HKUdBcRuu7HKL8C}XZk%x$YE -z>pr8L*8CI{U`l5xnQl&|CD%Mjn=csLjnKb64mt=|I?qB>QqMQAPfrh?w-h$Ie`5(% -zNB;D8K164*t%^|(;VC$<5rj>A@MXlzYKDY>BZRCr3H|8yA|?X5#l}uUlEz$dGym<% -z78*|$j+@MJzfM8bp#c8_zzh!!|4pZ$aH<@ioQo&s;r(okNYw{rn6r2@Ke>Uy6dRWdiZvt}nqiDj__&wfKh -z*vWZsR|aVKyr;a4Yur}1hj0Vq)ZoSg3n1P|`1{?BoaRfGJP(YyQ0#^yB?Vy0x>`Jt -zx9jw$Y50Nc5f{{Glq0`_6%IBbjGF^7RtR?P*GlAp5vN~>U+VjyDC5xB%2pZP&>4hk -zWhQK-^`U>dvldLBT?7E85W~j+G8Lx40gOqqhv5P&lIoobnorqU_IypmfgD~U={$T& -zw!6vtuzna_Be78G7!)WA3aogOUx66M9Qu`ON?PY3)G0W{=Lmsk9{~OkJ7nO_H_c&z -zCf?;w4OFxcAR(8`t2K902+8Bh*pll{32Y{1G-#?D!IsAtARQ#2om|BjLK@NPTO2I2 -zlrP}l;%>>@Hv~@zp|Yf6Yc+oWR^<)2)#=s5g|N|AzJj=}hd5fyy*0Q|?d3CgrFmaQ -z_%slj&P4}=DZx{I7RXFKe%G5QlOWW)lYmu_A!e;mw`ku0;nyU;Wrr3jPmuP$XJJO{%y*)AOd_~_ -z0OAnUd2p4hlklirDibTn^^miCk*Q>bt*`vz?*W%$D|k85dW;F`rGj19R7{3{+#Fsf -zN2SY{40g1uYH@3sn(Ppv^oRi>1NB$XOY8@JpKuJAD~>mayWjX<`?ErjO=}$v$%$ww -z`fR4#vl;;~qs=(T63VXF_*_Z*^hvSsCdC(S9aIBIL)I1k{nf>(@vuo9-F@Q^Aar$P -zqHxP-JJS|e6UEOzJKcgB{a^N!2h{}#_2-CMc~-<-oA_P#rP)L}1RH(5a|4r((ZVv^k9M)T%#}2% -zDi9uRh*v{!Bh2BoUw4cvGBv5iz|yp~c@fwA -zD(oQH%&ISDb=BbLKTLvBQAPZU7&?Y6gfr1a6U@g4yXvxPnvw@QKayf^oxos`XCaQ%m;)&59IkjV=hp<@r@-SM@SP83T -zSB)pR2?}Hur`$|^x;hIPMZ;`d8H4{yN;GJNDA%6o5aP0eY4?=2EvkJK_eG1m3wE;G -z#HE5QkCBQ=hTmY40$e)g;m2VQY=E6bEVJ=9_DaYxa5TajTUz -zV@TO=m6R8!)i*a&YdovB43DL$4?u#H*Q!581uYSDh&%nC_3I}=UC>)IZ`(}G<}POH -zRo7@uJ4~1}6fgoiSQ&!Ngph#v)WXh#0@t}>%7I5-kHj{WxInZi;naXIXqt3(^T?RQSOQ@7P^9@X3Y(V4w -zmK*$)tw8210E}>E9Md5)WrphO8-ry^URuC}VUB(6uaAUfvS&Ebn5MTj>zn-m{DsE7 -zny35g<^Tm=@CWt7GFX-?Vq%|FJP3dP04zXbR+xAr>V~+Zd=tiGr|@&kE&b=+ASsR&eT@h=Z9m^%zWusElBj2_OA0(a3!FH4~;64s|wtQiUExzL_=M(0NJ!_5V|#H82FpxvH_tg~a%fF& -zN8K8=3>xl{_x`C6{3E@l!wT!=Q(R;ZI{;In`OM`aPrmX{{sWN7G{;GhMc&2I!AYE+ -zpafT}tdP7JJxTSLaD)$lu1e9K+K18#!QXtH(+RNfyki;@DJLXauS*dLzFJJ0LP -z-KR30o%!gnG&B%+_uXRC>6a!pog5LrP^&ow%|T;5ZMyWsQt1P?AG$!$xUm{SJCZ!r -z#Clg}zhD{gn_Wz$`?~ot%kiFNg0dMT1&0c -zl!tDM3*YI1#>n9x0Jbc(7m%ax%8#lpFIt@&fSs))X@X^8ASXe``)AFd%VO -zgO(r;_zXh%S@QfXv|eGbu4n-b#++RBB19x3N>|;Q=jq;fK0P~-)k&ci-x%NYsuPq8 -zU2I9E!5O(=>Ps9aTV@hK+NU_h#=Vt^FBh_H*bEz!^;~bY)R+0Sxmve|Pc2H26z=vW -z%lj}48&SxQ)iBHRbnUZ-wSb5+caoyKhNe{_>=<9Y7<_8Ll$sd5uG)z_mmx};IeYDY2R|;`pNC2Yt!J@YgQ}~w(RC*l* -ze{3MkX6~+}Us~~7$4Hvy3GXTiqTCelT+9ejQzMl-H$L(MPyowdjGW&Ij38$EMhA`e -zOvnZ|(alQ-jU)ews23t;s0p$W%s0OQBmP-bKwd7Uwn|+FU=84GB=_n06X}n8RTy8d9!&;qI=QM- -z*+bS~P;MIxRkOk6=ptK=?&3l6T#nqDu!q``QUwx~fra}YyJm9EB&a@M^EmC1Au_X- -zIU!Hd4k!A+gDUl>Tc*b9%b5}ocO;rsgKZS_SB@GtYD^oQpm>`D=D=CG;zEgNEWyQHLl|0@sLw2&SR& -zl{ilF66u@Hp~gg+{>q+k3hY?CO9kuglsU%f}aE9Up(z8-cu))CEYS34a@ -zlQ4ArU6eiadE?-a;hT!77c#S9PEU8%*UoucT}4&m5brZCn=aSav8#Y>gbD)Tpv6@Y -zOhi=t(A4<%l&PL^SV|{nX1y7s6#Eu5$;-J5p6)K&J>_) -zJ-K3mkj1Z`nhKp)v?6789#W1P8`Gj)P$)k>jTs?PawDd@$=#0)mR4?2_C -zw-<>-q-m?WfZ8 -zCG)f%v{2dmaZHmseqv&=Wrh_D_O&%il-+b~rN2-~xK)A7;V^d{9J6A3I%BswET;J! -zabKjOia!91oqUh=IDSk5bx@;aawB#W!@uLc*ACzHD6@L!XsJrvBbC)8wbKv0Z-U-I -z!sr|)qMy-R6gttq6&KkIG%f|a>=}!V*8cn6Om+AH@SfUy^EP_!Nw%&wRzTZ?aejF3 -z+NdYG^`*gzqeq5j-OjH+3qj!$B(guy7OG|6=zqklKDheMw@La90UCd==!LnHa-HbR -z#ViD6Scjr}BM(uBp&E!RC&Y3Oj3`TvkO(}32wL}A4|fR>;8_TApJ%jLGPP@2eCOti -zspJUlu&FDBR4AG{huSC`~-sFOL%H`^Tiy*4Yp#iBisW& -zW`nF2a8Y4HAOtF}Ti|#q-vf}ADczj*wGPged;B@8 -z$^@gaX5~x&@(2?bmP_&t8#~cK1zK)NWF5w+;-;thS_!vZt4SS7F;!fD!t36cA8|ya -zBSQG|m}~DTPto8Xg3c~3B=NZlVa+Suw*PRgEl;lEh#&JWIu1u!d~X{`b_B19XpnC9 -zp9TkMX?3MG8@||67hFwE4nCPhne$#pU+C3_R+j2{w~{^5T8SlUp{@j|i=moU7BHmf -z2wEkqM$TGRW?{xG288mMGauQDEyQCAwiO1dO1uzIU(s{F*gTIkXJS1kd@BWyy)lz0hbiI -zq^*5Gp|dN6-U}yXEB3fkgDZuWzoA(OG6R1HEOnGw#|kh)*$5^Of-RCth%(gON6=VN -zz;JUD2x>~F`9nll84lE@sU%O0#H-Xv2(s<5p-NL(BSdH6ts~K>yj@c0Ro16S(^B(7 -zVyNs>UNl^4f-8lpys^60_j4h1BnS^qW-SgWfC)Z}u449dN?3QJnMg>$-f>Fg4yvo2 -zUl;cH3E|sH_x1w#AitP>6Jr>R5*nU^l{twx`hNm0ioRa${NO|Vx5xw_RUqv=#JItj -zy@l7UYCM^FV4US-Eh?L^{l_`=+VAJ*$4}KGjK&nD8TcgRPvWuYC*3znv^-a3tlnV9 -zN=E@iWGX40KWiY?87r3+*`0Os%|-`c21%sjF?YkuW=W^nbQ|kf8S7D5$7GjgMEnFA -z2w{vMU$|uP=gVwvY4Fr<6OR4z#u&pW!%NlM+=kIvFS0OXH^scd;u=Ia?$*`TSkS&p -zaA85^TKFTcbD^qoq$vFvN;!%g#bU`1eqc8ZPKp%`P=K@_u|vjIu9Iodz|G?F -zUFPUmvR64%rcJ|_`(L<>i`n|dMSRcUO43Y;XgL5~j>-ibf;^@zRjG(LXb#m)zz2-r -zQ`3BMK!GyV3jVGOL6Rf03hYH}ofi`b_S>hxtFes%A&`SSm+r)8xat965y4RudCGL^ -zaBB3wj)-5iQ?8+dvP>nrnl74PUoR=-$=g%_eVMnwh>YzAU<8g4%8q2n_#8AYkt6gg -zBx19Dav5PuE@U4YULL}SBY -zM#oOtf4(<4r!5uz_WK!x)nCNCnbjw&R|m+ULwEg+oc#Yz2WTC4k{a%Ue{%C8aV065 -zBHkh3Og*q6^pt;%w^N7>AI0m5 -zzgwarhw!#0uwkWMd71I6vs6o9AgvSa(=`R^bO9riuqy{vliG3?4KrM_qX+aTMD>hOa?%KwPMun1Vd#-W^C=Tx{2vx -zLv@5W=SF>Q{bNKD^lcBtE_2QfJA1u*`3AjiR=tGB3W)eh7xHfs96jaotc#43ix -zeeF~$o;4?F;S`ibx-~+ljx+%HnY})F(9Euo^$o!kAmES&$*+V -zFixWOnjg@zX}D!I^W_q(8%tXF4F5yHR9DzvaRq_<@y&$3dz{-M{up51uqVg*pp -zGH(q1)v?`r;Cz)y}$z#Z%XR!1ZAD8P1adQ_Z*w3@hZ11MA+fXQ-M1viA7Jn7I -zWkCo{3$)6Y&K)dN%#>B~P30z#!bo_Q|hN$FAe(FOiKtz`jc(WJ4MPol_ -zrA)qypW#`vk&}S!F?av+UK?@vgl?7cnTm6o@!xccIUZ)4)j}(pxKY4W+DiMLft7uY -zMj#aitbarM<=W1QyiWz!aYFr%_L6Dp3b~w|>f&bCgGMX9F5}))rzUEP&80w2?!q-k -zKd*MC;J;d`%Xo?K0aReJ&XesL@ae9@e(|K#Tkcd91MLzKZYyQdNQeWvqMx%P($`vW -z6MngclR}?v`wLp@XGn6vq1&n-@lhgmel}A=pPV(3_~|L7x8jvyq1DJ^oa!?7udJ8( -z##Cs%q)mWD^x55mF-YkO4J6m@lwJ*;E6@Jrg8G*`S7#@MuNN3KSM-V~vy9c=owYD} -z7zMbVEj+6c?#q2HL4D2-;LSgq)od+ySu7NnyD?IE3f>wA^_=Lhj1I_qy~q*Z8UF$? -zBiZI5l0Ixi{Pti&dlfFM8n{EZ(r7wV&#J)6X^-bc0bk#qA~*a^_1LTuG7$aTR_b(F -z&$@6yC$PAr-)ZkGV=1i@T -zNA6M!{ztnB5T{#kbM!P8+|<)Y#+{_d!b!aoZXl>$^7;U%JJf!n<|etv8t^%~Gd3^X -zogN}WkSc_>7#Yw!vi}|lD(P@iU!QZHfp!e^8 -zw|s*Le^UNa3Hztt(@5#JXIfP$lVVUPKqP>q`ASZ_{z4`J6WQLzq~&q@^L-lq`Byxn -zs1--{xhe?c+9~J_3_^X5EwgP6*fyFWki4F~zhJ;0iG4>wmjNcb77;fS*54gRVPc#; -zJlyHyI{i~Zx3_jw%L%-^G#X~VvOn@bb1iFGYq=}cQQ6(NFcU1y<|DwR -zZRDpB_lMQF=`tP-DoMXOQNFVXxQJ%@IWD>aL_TZh~T9YN%`Ai{g5u)#{5i(z3MvQ|sS-;qRd8UFq;%sbLLT>{xyrdFXk9b`0_)&+w%{eEqbSPBls -z%EP(|)zh#M-PzAzSPT-yx~VP;Ii0jTcJ9>}@r -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 -zTk;2#g%WU9P5WDzxG5pqi2^5Jm~@s}*F -z)RV9y4hc%lbpLGlslHPGiuFP6wlZt!ZYt-8 -zXhuLcqczy){db}-MsB4B@e7&HlLrXt_CyJx7DL`Y+$Q<^S!uNyFw$N>Vy(%_Q=b(_24 -zH-Z}2D!-P_;8!E_7@3AP9~DoJNR(9(>7QA+xMZ-xjqF$%Yy -zNz2ZhNu7A}S^0|7lJ3hi&|tLg6@RgC*6iuMD`8qxizuN*Nik^j+vdjV{jg%1r+Zbt -zo}CpRjg#5!kn0fC)RHg24!SIym?s1l<8-e%eyLK3(JUP+dq)B?OUoM+2AMFC+47?| -zT|s{vGnTH3II?5=F?NK3{TKfAH0oDd%dDaWaw3SI6p?5XK?KRs -zicA2XlV8|^(jIwMv~kr|4&5{k+7!R(+f&(O83)fd(>^~e;$HJXkC3=$7Vv|=-JvnL -z+lwv0*yYP&4U(I_;Qi}kyR2<6`Pp*X5`9N;zTrx!Xc4N|WJwsYbUhIDD;F%!IoaAH -zyunk(CEVW5({JgiE}lW5AQ|PWyai=G^~nq87-sH#0P{IY-ATt#AZ-i4&5JTAJe_!b -z`WJwWiWYPFEW6wJ#Y!-~?#7^s&@qI%fVjG~gnW|IOZK|n@ulk7u)KAqzhR)<&W-_G -zHD#%ftTAY)Dh=~l>HN9v=SZZvK4F*xNV&PZqpj~)i;<^2Ht#5?TCID=BTp<1G3ie* -zz!i%FyU(z~Gx$75FtA$C=E!w%$Ve=?9tJfUz%!g0lg{ga7ZmARClZ))yPq$)b|AEJ -zu%tiAsX)c(*!qvDFX32U3%e)+#!=JxIPhBxc!1#&a+FV -z=&M2080`*k{@4fQ*vKRkzGoelV9$Ch#7wt5&9gkMDdUzpYD#RhPbEcdBcUL@Gs?s6 -zw4K$72TuO@%lJ$Fi&l*qFOmzKUjeLeJUlcXnsy*_g`6#VhfFl>XgInH%br>o$AiP# -z(fOxm05`@J+|MP-WXoTnBMq8##!FERKzzo6IxPMO7;0fk8G)TSx&YSYM|@+@x~wXW -zqJB#C0Us-i=n^nBz=j^g$6~OkR?t2%!$o?4w6%|QN(6(AcsdBgIz})|;bi!kms0qF -zljWJE7fe+qOpzR)FU+A~S6oqUz*NSK`1G6Df;5W_HURE?HPvoBJzAx^s{-AdN~1%*p<`f2NpYQ3DhhJxW{eNzY}ck9ir~t%)o*Oc+Yv$AJ9RQ -z8D2$o_+8XByWs0J!w2U1a84!J0L1FV2<-)P`u_k9yhZ6Hj-NNlDq-`trD}FclP>y$ -zD2$gP#8~OJtByefud?wkN$PTKs|;_dTOQ@mKYg>Y1#QC2s*WcW>N|MA?=N*0qqZIka7qz53v|4oevu* -zswR2nhFNGSnIsA~ZA2b93bFyiva|D5fKtdxGLlfMx_%_N=EBC~ -z7BG_OP8|!mUcQG8P(d -zzXWrNwS}k^beRfPq?wB{f(@)Khvo&F@wC$%x~@wS4Z_;z-v^q}vVp#DWhFjaRTF|* -z;X&9R-w`#MqA$QX3d>Q~dYUw4Vr~Vu!RE1EZ|F`6jq=LrZbFlN{fFg)&R|s@p-Eez -z%d;rln|lDOZ+t-3VM98dAbxBGt4{`K@|LBJMwR3WtE7^~`~mv`?|fG3wAv-d3oKvI -zE)8R(oqzA1R(nL8ROZu~nH#77PJXu{&HIFRS?0t?+fXOlb#W4`_aUq_lyU{KMZX -z**r7&SxVeJomI5!SDZ9BkkK6Fpg2sOK%8Jo?G9 -z1L^+&xls;~MXqcSm39OZYh%t^@!d!ow=8(9(GPJDUOP3?cmDv$6Tumk-qX6au(S-Q -z-y)?j1EGPMz~Y!;Bah~6@Ic(%u(8HtDR_q%%y)I=N9Gk1)*Tr=j53xg`oyi}IutxfZASLWu+-i3F{Y=`ycaaao -zKk2CYr#Y&rLmpv~X3bV;X-GO~$~1CA5jXUy7qB}VVQUvRPqVhGHNNyTH^$jCOV9j4D(ZN -z9iyGVB}(ixzM?^}+W5@z6YV}RuDNjxkR((v9l$Ld&T=9-Th6O>VR2rSdzz%uBn@Jr -z4b}Xz#*!p*T;F(7=lyLTRMchN7t)y?dmV0PO;1Ie*H+X+OXt&r{{U$agoz_vOaWta -zv1T^JEc#inZA<&gj4fevnAV3n9o(F_xWJGxzyTKBIP}3%_Vl);Ho4AYSl2i-k@<_H -zxVXTO+zjafYG=8xOn6(>Pn6Knbu>BFT~4uORZBHJZAB!`pb}(^n}K~Q6n+zJ@rh|~ -zch~I2YL2F9Jj=v6jc^CEhdY_)vc|NdbJpw|TC1tLgU`;`sLfV*TH7_kbA5+-${uhrO|2EsghC4ZooChlNJ6 -zeOYKnc|6@$L8{}O{=0sdn@(eeVW~~ckVopVBs05eXo(%bJfD19Aab$YaZGOIRq5(n -z*+C&q?nfAU+7%AFrZ&nnj-dvtFd*y)@Abj0r6G9EQL@i%B}oK=MUKM;uvEp}Bg$2} -z1h)H&U-LX+NjoK|@U2E^(NPjKV7J%oaRVh*WaO)H)QV@< -zNLgAv!y5t*xFd0jpG;M0Ys7-Cv%I{W;u;Lc5zynqrnM+IO?N96$=*Js)A0UP3}(i -z<7`HkgF1Z*Zl^|A36rLGh~%CYx2gUz^&9JVP@!zvdWa|eN04AgC`vbQ)-hU0Fqd^$|01HW9@n^)fX?b*wHVz16kUL9>k@S^gCh8Z+ -z8fkKw9-2uNqAwe@()RVQ`Gv7n=SQJ>0ID|6zoGO}vHVu+_U;DkGO!K~65S35(6=#Im=%JTUp*d?upV}fCj*N -z4ZmgUoj>vKq+2!DF0`oH?#2j!Jpdtuk6}HOmHs&OhH29hbhUnYm^25=N<>DG%m&48rjaTloH~5pX#{It@zF^Zq41Lp$1IJV|C9i($aI-k~m%| -z^8;5)B_(4`DorSunI>j^d+&3x=N%(DT`ErxZs0}$0vA7b_`%j{AN?f?REMB*j7-Kz -zA5aIf^}R3R{{U8WW?sqk`CiBAQC+S+w|YX>TbAe`kkCKf*+vETnW@=rVij3)yJ~f>wg=ctbf94 -zGYpcS?Q~^isHzi5A*o_@IDavm5KsASx3<{TbiV~v>7HvTyyfGZdrKijDd9r$%YJgGb0Hr}4xgJu`ZTIkrs0;Bfu61{dQefNBnE))UN27&8W&v5L)=C(Z6C(*G~Hd?DCHlJR(}k4ydf@ -zEbArDJf{_N<+-4Gc_ah=;7L+eV)wB|(!ArEy70$e41Jp_wxuZ=Lz$2}OG`kG2$H=% -z%ke9ubvji8`jr2}wa -zw1q3Wf$@M~&(xA<-g3Tf@t0BXk5M}OJ!twa)t)Jq{{ZkVacjMZ2PPMunkdG2vpuc&cJW -z(<2qrdjQ0(@7w+G7L&3IN!cM#6^dT>=D-VnX9k%|OywD@U~KYTt+40wzrGfPw01_d -zafrcnBz(L70LBq-WV9K=V^nXY*@^>w*7m`rJshH1r_`*3IQ&BGwZASfw56a%QWdXp -zX#+7kki37s4G7Q_pKWWhmtqd$&3r8iIs#8us03*P?fKvhgc8jD9>_;aOCO1+h -zGE_}Y4Z$`y_x}J)OHz<>j!3o|K7gM1V5tM$3d-7clhQa)a1faZdCrZfIbdyk5C}v9 -z5_yB&$~~{JBLESE0T@gs1X#$6ZSBY9hX{oK0BG1chE*JZaI~XJkk26@bc1oZ-+V19 -zIzkaCS1eIT8-L>t6N$ocE~1`Pnspo4Y(e^9h}{w0F_~_`K`qH7-uNLJQMAQE3!B?u -z+@H@15)G1^&ZIV>e&hjsC`(Fjw6L=WlH42aZTaEpPfrQbQGdEOZfxA&+W{TYE$*C8 -zAtjx(05|Xd02ojZS~yH96^S?80i>SzG^0v!GNHAKn~T`)snY)%L$f{p4fqCF*ai|xq54do!-Qzct75Vv96s32bfY6}2H -zY5+dARt$cJ_rlVap2$AZuGat)*b;0o^ul@|DH^Gs`v5=7eZO2SDQSc^CGJVE-p&62 -zj5Om3NhD|28v;17HYe%rgaPFM9#a{JBrwpdE$x4%93xL;JfLP40Q+294L)$AZ60B> -zl9t#3Y%rMFW`=e;V|}}jM);94!FdCG5yDZzZ#8eI5B4#MJBr$H-H%MhamaPfDXw4*$Jy82=$UJV{Z5~@}7=S`K~u< -z7z4)k{qRCiI7mCOv1Mz2HIMw^LLoXhjEB;s+m|0zD8%qD9&@x?aHSa73afbcB*h;eY`5AoGF% -zL;w4YuFq!2u8u -zw4tn~HY_$8c)<{u5Qz(=ivq3k?||hal$urk7aJRpdB6eyNJ?ZjyAXQ=f*~4VAV~)L -zpWm?<5S>e^`bi;o-A>prmJ%$W2X$+o=Cr8>!7;)yZ%P_A`$`+7LgQd052QZ -z?Slto;2n{MEC5YGM<5-rY@-pnb(jY_mh3p~afKK`7(?a&OoAbPeq#0ycJ=v7 -zZ>^3LC8vo(Sr0NWH`p9YDo|0wtAd)8?{Ij;N{LSIW3vVWkzh#t@Bn}oag|%r%WHp| -z7Q(s_mQiA)h56jy2`_Xm1MGPHFvb%YNnT>W1T2ay0ovFI2!N5*d%cCdumq7Tu=+%Ek!Z=8XQG;`!us;3qFI6v9D(eW=P;K+M#Bic_LXM=} -zI34d}d@`9G(HUfAJOlC@oDdSy6A2~fP(nBt!2u8w^Q6*NLd2c;!2^V89487SBg$Jh -z%AaOOQbwiNlO17)Gub`dFv`0M-;jA~#E0+>0o; -zHUj4a1fU}tS+%o(E!=;$7ouK@Ju$NqTV?Dp@}-n*B|U%)%lTnJNCzfkA!{iFeD}kE -zfB;e5gITuyu+vjD$kYjckvABMi0qO{bF#mumDT+)5P~5sN-nw$+xfeGco2|cwuxsK -zKb^38!Z*w+R~%{I-0_PQsr=~)2)5g}!2udZ>7GdxX*+!m01yBuqn(YfBNBGEVSt1d -zr63?%PUpEeK_Eh57+u_{9qe$)l*#O!s#6waZPBPt7a2lvAmKrxhyIkbLd!tueu9HIv3T4hi}#p>V?cEJgT%6Rn=cN<_R -zK8VYqQ*TdegK%)6Mi3U#p&LmZh-^PqA(XW%jRYU47dSmp5tKlwQ=voKzkDxrz0(77 -z6(l3&Y&0W7sfrXYZ5v45OE)E2hWd8uS>SYJ8_r#9s!@6~i*xi|S{P0Q%34;b}76RS$-wqOpK>O^5 -z+x~cd>O<8V&96#GqyGSrH^KrE0x}Yn(%MX#t-u2a1b`@2MUD3S@XBQy4H1S9ao_2M -zxkzND4yMg}TZI?Hf|(J7;IlQ&&ASX6BV;xzeME&luXBM3-7o50M11fBfrNudpeaGO -zz7!__sR&tC{$RNN8{pY3lSQXsiolLb90%1S>Vf1Xh;7I8!3scjPLii`eQNYSJB>uQXMZltzIAH2{?l6J`a+17`0|^&j -z1xdjGAc3+?Bi_yi-N!f}A`&u0ETFR=d=OCuCjnSlO^?590Rt(e^8#rS5~sESx&$Qx -zxaQ;ia1`9DHSR1(l`GV3bb@5~Kyt0r$X&OG|@fa4++~l#WtXDs;7)+~7?5BUg)2 -z5NT&3#aM5J2b7@4%#mF{i9HNBz7J%!*+g%{E&jM_UdW?;Hzi5>;S`D{C`e?u_ZT4v -zgJ>i10DfCxM5Pj*NsYAH!`xs2Ak-dl1Jup2*o-(*(x6hJ(5nMGP*{(Rg0URbF5R?lE -z6K`>Vj>$)58%cNjrP~Y+>4DuyjH7a(oFWuRDaehK=_OCG;NfKh2yVeF@&5q*4lwQTNDTj4Xx&OKNBu)siSVB*?9xcT6O -z;RWn0Fv4L=8#zKXgYqMcBf5)#l;8k8TYWH+_E8&y?lCA+g(Od7&9M>`ToFrS+yZa} -zn<_ZGv}5KW-?%(snL8;NJ0fI2JuS}|N+6WT{#N8)=YWYp%5f}`HsmP504JmXj!k5O -zT#z^;0EC1j+H?X27bqL3))sG7h#xFSl_KG?oJV!I78s1G$w8rk+~AcdQM9ie!jo?H -T#Gn-*&`^R%+kch>KwtmaZ6gae - -literal 0 -HcmV?d00001 - -diff --git a/game/assets/icon.png b/game/assets/app/icons/icon.png -similarity index 100% -rename from game/assets/icon.png -rename to game/assets/app/icons/icon.png -diff --git a/game/assets/app/icons/mipmap/icon_144x144.png b/game/assets/app/icons/mipmap/icon_144x144.png -new file mode 100644 -index 0000000000000000000000000000000000000000..8fcd3675380646b4e395947a0e1ac786e264bd5f -GIT binary patch -literal 12364 -zcmV-SFtg8zP)u00GX)I=0|^5?EFY(V -zdZHr$oCN?lIyxpLBrzu-t&WMHF#(9O`gs6BL`M8z -z07Z+3b6QpWngAOm5QdG7St$-qRaK83188n;m-zKNetIBENOBnqNt>5SYb|ss7h~De -zqi6tCIT{R_7NT?jyb=Hw -zH8Zza1WvK5h)NA)NEw-2IY$=@&pZQ*=Hba;1IkYXMg{_|wzir>I(y*TTowv)qZuZHM9{16%;MH#FMcouMBSCvKraO -z7RKO)CV6g>O|tHxVYai&`YmP|@?f$Y7zP4E=HHy}T>0gtTGlVi($Tr+o_njaGUa*O -zHhl8dxwdhwcX{LU;MwnwozI4w0^!Dbf=$baYzyI{+TnM~hS*BIu<{-)& -zj*8nTZ(xdI_V_r<1i0lo2N1R(tD(%dqQ%1*+%&;8c?gY6)Ah@r0U -zVN4Xk&nL$vv2iXhOJ^@og|;nHky@v416GPu;mL`{!P -zJP=5g2y3cF_l&$d^aO=WC!l8_Ncl8@eqcsXYRawguU -zB1%#-w|kP*M49JRAT%a5!H -zV=6LqlN2u2Z1cS2@+2WkJU-ks98o30;mvD8Q#shF8emkt_kCCO{lIQDDw~YrdZ0C4 -zsmsrU&&$-7AZm4A0c}SKl@_ttV=4=-fWna>E)^*4BqW!Tcn6OogY=eST%xwL@=YZf -zOVa@A2Kb2`vhD;AniMn%N^a38RXEd%e?cHv4SB<=RN|K==#>E@MyO|%6~ZdO_)!Tt -zt&-lMI_}5YDQ3{6w2CpeWJkDY1sV5ac6A*YHguKnaDuoh2au&S+2%g9LQ@FA%92^y -za(1S;NvKE)Cz>Z@P+62xw&)c|Q#VYB8nJ|G!GSiE-5x`#`bH10q~=a!#lAz?gQ=hU-DhNVU&3p2S -z{OT3HUV0wDLEMHIK-Z75cH$&i2wKUf9HpYWle-Kb+!rGn*5aZ!L{yjw1O{KW -zhGB7q_nc6CQZD#5-O5a8$m@jn?c~6 -z4LHgyxbp6#YFd-?{1Q|Tb-mq=wVAYQRA|TYX%)+8AyD0`G)=hTfp8rs?eXjsOOK?ZU(*Z)0p6a@uPiu(6{yCvSM%_@Cfr=}$ -zXNwWB>bqC)bR1j4fe5y7GgX|3uL%*3qKs`UJy2Gg#?&hXXtpp!3SB00z;idVK0~^8 -zio2_)^Lks)=kuqn*@=uYZQaA*D~!p33?AOsvL+{{$tfikZJNSlXTZt2X0g{cq2(1-5Q_lH9_ -zblqWqA(HF>#OSf?pfb;GVi*XJY%MqQR}&S?I9uUaBH=8XWwXU%gaKhoDElCFLy)pt -zl}Pn{KAjpEgEVK~B{^j-CCP&DoKAn2gZ{LDjL)DhfCK=AtIR-25|shf>nd1j!SL1Qf~tEny`O@~KDObocB)QN0XIg&y{NIc2~9j)I#C3o -zsMZPHVe)h&G>%2Gng&A_GX25IY2Hc)7eNa<5j^g1pfMb96PHNmU+G -zl#$^C(Z$Ltod7MfHb1ZZL~m$g%CtyZX!Lwc#0r=QQ~%~?S9 -z>-GM8-S5wWbv4!%VR+$_+Um8*5CgW=ytElP{3ujG7pgBOna{+HR>6PEG9AJaKd( -z!gPg_>+!tbK@rOyY$H5vb}wYwL2fkkuY)R^IyL!?dICYCT&u;S@2x0tW;ofTrdo~~ -zT7J=xoKb)t3=e>`X4Ec!`fAbBktln(fb4hs<7sy~?T))6J1?JiAjFNVA&OQyj)$lQ -z)65EfXxNzVRyM^Yw@9bi#AxmxOP8(_v3e;kLTT{Q%fy>x;~%w$7^LpR-J>UpGosKbqLhlVzty`c;otEVi{%D -z44P#9+AZ%#L?TERKMB%Tf~I56ZT82VaGb{Fbb5Y1{r25ozyJNeKmK-FpXJYPy*oXh -zpz}Hnjd~)R0FiJ(a|9-jnl2KFCwt%RC?YErD)yB7uXqGW3<*^I1fsDcnvXT){$`5j -zy$E_16s^ki>4 -zefXMZ3;XvBMgC8S#>kD3o-{^G_S30~hZz%)cH#y|9~qy2`SzRdfB50sZ~pVIe_p54 -z^L6+9@%bosfY|v1(=4m8)*sMMYGzY#_qx_=K3I-tBcT7YWqq%0V`_g`oO8Y#Wn+;(Vk#_xq*KR^3na4qW$i#DPCmJ*PE5qpdoi2F -z*%W}1Q5t=;@Pp1_2N@%Sx=vuo7{=`p5#wLzZEuFWBC9jZ0V$7$RV*lJEqHmP0L)gM -zM_5u+Kqm20Wll&{q@-T!0Mg-$I2~nK99_htH2LTwdc8vwbsbFM({$Pp5$=+}c!LCf -zWrg2l4@)70nw3568q3;ys337k_^wrzt-BA$z*I#ppnP8<)o=7J(=1BjXg-@p>BTFb -zC4>l(AOHxS{1zEVOcSRSO#H}MpvwR$v#$dx6H2*M(wOA6R8sj<@PwQcd4;C7Ys%H? -z?z5Vg(R?~fv)Oby&9XkoNNCP3%9e-Q7@7> -znZ?m4nU1E>CApx54?vKiz+!v3h4Z)D?hisp;2I-b5Q(lKLq9(%2#G2di-Lq;xQrY% -zGltxts8K|Q?b3s&E`#7&DC>IFZ}fVUM+SMeH5kz;!%~5FcR?@4w*-Mn(deredW~5h2|q|4RIk2QM2=C -z2IWYuX5gc%{}3yRAHf44UjQ*c4MfNZY#kr;2A(oByyYsWKI4H+R*)i>A=?OZeKoX> -zEyOi{IQD%*l@J#4l}wMP(a@+SiHbLbg) -z-%C)?Wksd0d@b1M_FVIo%U0%bVM2(pySSY`?v{FsB0_12+i?mhQ -zb$$PRG>wx8TA!p@`jNLctBtDA1<1>)N_#OguI_gjC5O&lh$Ubqml5Lai=rJx!dVLc -zGD?P;L}=9L%C)p=CSi3RC-L+GOdX@wvaf+_w#d;iXSVHtyBa=#c^)Z^rd$b+OSv>? -z-ua})Ld;9dR#7j}veGh4*D*17!L>kX_sj4)6W9Q>NeeOm80o5p17 -zl*Rp3hJa3|?u)#l8=HNT#3rVW7J%hfbp#M4iF=sZ?&J<+rU24q=`6K_Au3$QBj7Y3@VA;M%XB18sPr~)X}TZ-(5MDC`kYY$J1tB-O|ln_ -zrcffN$T$7EjJ75baeU%9&aA=LLYX{PqmYnrO=%gS;sVMyt6Wn2EeQ)4c9OTlF5_e2 -zb`7?8aD0`)=1*Zij_1*c11Af~Hvh -z22PQ93{N$j5%5`Lxniu@|8P9C5W(v#KF*G>j$y2m=v)nuH8QzRTfoVKpQgJGM3rQm -z*V0KS>bgvs=&J_weSl~o3#!&U`aD!lAmbTSU#z~%eDIMSSXlH~0;Z0x((_uM!kO=n -zx)eeoM1aAi*rSfkz*C<@nbaOo=qV)y6Xk%ui;pBsxqVhLB18@fQk}ML0Cz~p6=qZ{OU6Q~fb_=R57ybkf=MQ-X%|o>LTKhpg -zj-&W7kbmCmSv$WSszKW6rOr2ovm_HAc085Bew%}m$A=>QwE(<4j -z4>AHnf@~vivm8(*U(3@XzZ2Rn?T>}X)4F1vn#?7DY@D~4ic(YO|e-oi%#k{XpFX%mylgEwXcrvzV -z1d4yzpNz{tO(yrscPIP5pa2Sm@_2F(+6R-#=MNt~Powz5#plnTFFsVQ@!sBKZ!$Ru -ztO?fM-rl&*x48P&fjT*G{(W|Ia&&ZZ`mFWr1ZO8FNA;h*1t8qB^7xM}uC*tJZ{_Td -zhxh&M!S(@bzVhqp>W+PHu)0^=U+o^017xkdGuU+8?bY^byS?!tPTK!#Z{VxxK3MIp -zcDvo(;O-`m-PH%JNmthD_}&9`ve`L$_w-*_PfyQIzdZdf&YzxDj6$9=&|>B7)*9Et -z`-cy4*jn3q`1tnbaA&+yS-DwR?+*7$~wb=XM4Cg9Co`OM(=TK -zciR9o>27wr!{M&e+QnP#;qZaIzs<9D_n`G)*v4<-%N2l&{^csSU-ckj+U -z$G&@Ke|yj#-?h(9U%os#!pqso>6e#h&z?Q|yP<|fHMv$gNY>-~e0j{YNm1_)Ha2jj -zAOBCcwST3NC1JD?--54*2#Vgs2tI;}6GI+-P#}p-(k+IjbDAbC9OM9k67dm5qVcWk -zYm!a&xa*i1lZmr4b7tp1+^Qy%{b}#Pvby_r*Vol`t1h=cI4{T183NBpq$G~_dXtpO -zU}wEKeJ$-x5=*z`a^i&dW-KO)MUQ9MNzO|*s{s&chR1FYC2#bv@iHQFypfRg-9I*u -zP;71Ne%{>1gX%)XFHB*{>@VZ*XWx&%-gvd~eEj`-GgLMbqa{g_{XQuw2$G+Y+31iy -z%1V+DXuC#fBsND(0v)*g$K}=jn^S>3yt=x)(vyrwa9@i_QKKQpN{mw$qy`2~Wo%Vu -zHH=L$Ca|Kxa=!K9EyT}qGk27(yGfy=ps#5c-JHN4aRG -zR`N)GlfXsAK7o&xX;?nm+{Sujs*&~vNE;0SB)vQR=aUnf-(UOj>^DOE9^OOC>IRiM -z=fS}7(6zlXXr6!>vWV-IdK0don -z$Sp+!K>9{7?r-A&ix#w7jWiuqH`2UJU|)<}qxl$dfN>9d^5)^h4$Xgj^W?=l@DR`P -z-W(w+LyTh)9DS!5p?PIBq^Qo?fW+nO#{R8~&4XVzR|s{!qG-F7&0ilG&Irk?=i{%T -zZQScuw2k!#6eW7-oT!b-xmw1<=Ijh?svI6Vrq{}H)E{8^B%}jcmgQ4OjXNxTHu31; -zJ2PuQCMPW$UW2`4CCownMjY-)PEi;;rRt^p>c8~0xC0k#$=0bs0=@HNd -zy662b;28+)S$JN=4ADu)7>qXJ2hEIv^1*P^Lne^C91nm*p3e~N&cut4J3PNX@$~Qe -z(3$tXA{h=MKgn|$lBB&ERSbjQjNgQe4H=nZ9~kxPkcqa4#b`Hgfwn>{4LEyLDIQYk -z)iCI1*HOGeaZiDfLA$^qXo2tX2nZZ>4@TDP -zIwT;>LNL^zaE#caycAFqOw>7#kR&k_0S}vjs#fXG$Se(ZIW~8C_LK19(LO6gp@#f> -zkT5hL6$QZJ6FdRp7d$bNkvut4_;O5OnDKtfqT9Oo{X>7Jy@8xVCi;N&W97U~{T0x< -zl7+@G3*i`p%JF)=S7PN1M7*A|$gJciIT}Qa7W`DD1>BaQSrL372~auy^V6eKK@fPz -zqatAhItc>YDN2Av#Pr~oY1ynSWU8pKK-3eUDAm^P=g;SsX1l(2W#!k;O7++x>OxTK -zUqkdLA+)|xC~42%zk<>Qg#(5lG4YTR^#TYYd3lk-nJ9`sR$|cu>6= -z!tf9;AY||rGEuaXW|+}K -z&wQ(@3Qe!>G}3{S07CC -zs1DHJ1EN8N>z-zu?);yIwq42uNfEAZ`&{2PySpux-=404v~+7Nhv&YN)(3Q{qy2()QM>sD;VH6=~eHLV?`I-Rbjq^7jJwZ65Yy|hKw)iW?~`+AqI -zwCj3LS6A27&i>BH(}{if_ipF%?%mt%r8lnM(A^lg-c?aC&{fgeQc+P~)KSsiUaG5T -z*Xde{%R4$+OG}%q)kWpyCB?;XlS%{gR@LYz0N`5>TUt7F*xI4pXm9W8 -z=^5x5xPALReD2?U`L)HT16`?Ok;zsm-nIF -z?iuLnx&fdn6dQ1>t-6Yqj@FKr`tsuHqT=G>qM{k<+ -zl}{%SKth3l7YAi8j%qr-7f&SOclVd(g2BjKcy)PxIXtxx{IZvr-rJjwC(`kBJ`sm* -z(})}QDahd5mv`@{pygBfY(A7n1x=+=S+E_NoJ2roO=vO{(r7^Rb=GCEZwR_(CnvM{ -z1Yk<16Y&J#OeBVxM8e59-JqnMPKWP@;PUe7YGie0COCh*8a`fFh&*#Lez((Y%MK0> -zyPZth?e@Vr@##c59gnBMYj8aT5z2zmNo4F#!rf={usNw#TMY=vU{VMc3ZuGDA(}R7 -z70yK!zHN6f!zhPsNbRuM98R~-?Q^=_qV04pyto(+hnK?P<;dJ}aDFBdoDY9F^B=n1 -z!^1WkbObnuFp(U#G0tJ9JMD0!)3$tDGx$PNHL38NwAw0wWip}tI;1b7hfJpKDg>o5 -zb$0^@WPB;yH7)YY91ODF;BxL^5d;|V`NUg{@AT)@@ZxH4X7PA+e(BG-h46B4CVV_M -zGj;qT!!WjCTbtAEbowwmVT!^DuG={Xesc_J8x;bW2KkbWja3T4{#q5XdHd=hu3ZL& -zTc%M6i+$aVNUv8|-I}U4WZ{VnvTRqkH3R>fS{ffZ4Z -zkajdggrYpS#26fgl0ZowHv44EmfC~J1SW%(oSEv#7>H2&gxh&Q{~eqF>EtqxpQczWVU$ -zK)6A)UV~f)?C@Zp%XhejXLoOl%VoE=C=5!~I9<=tFo#Kl=#+Y(^le7?gK -z(}yqjUtqhxb91yB^(K=(sF}{H!F&kIx3}PEFd2;ogVAs>9pBEz)oj!sRtr!#{dRxy -z8yA8h@~`mmimSq2^It35c8!XxmxYLSpUbj6F8LxLo|1J&KYsUgk1IZRcjZ3=dt7Dn -zSFYLE-+A$uucxb7zXu>N-yhFWdKL==sk)sG2XlPP#=SX8QLi@~&Sw3=WH7BJUyhEi -zUmhO3fJ?6q_PLl~f9K`?v*+9VC&ivD`{G}LxFn2x>^=R_Y5ag1zV+no9{*W^v8(Zx -ztNWjzDB$bA=>0aAUOmMD2jfYI;WF|= -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 -z?E0M6gDtczBXq~>;Z(}Thq3$ -zAPt*4wZZE=Qhwcwd`ekm+7sf-Bfe<#&T9Ob{d8csZZcq?Wha@^O6h8!dcN)jnaDwb -zl;Te=@{?GXg~0mTg*=}hXH+Q~(khBF_j`vW390;k5*w`2QYzY*Qd*SD*jNY -z*Ky@MO_ap|hZlkqtS38Kce-z|92Ce-Eo7N!r22DEhabFPK!A-e2=@u(&VVDo8o84S -zDmD)`ta4tojRK1tt5kB@S-9*SiA5mTbaf%9ItFOU>p0nH<_3_^OB)2Uvo2LK{D5xZ -zR;MS7GsqBEbNQJ=<2&e8BR0r4!D{=d0?~3O#7*p9V=F-D>}$%js@q#%>gn_grt=ic -z+#S2T65P`Ov*6vVh>NZ4Vy?dkDBa}48s4^kJZwom_KjNkcn_#Vm|2HL1hI=d1Vk5I -zS1__22nWKnG?%C5-nr-G$pv>HHr3MqzxJA{cYYSTrW)wZMAlkl%;3^v$zP;#l`TW9 -ze(VL^{4rFi-0gX4Sl!~58fmfO0`zn3c3&mUwYv&Y&n7Qnqa6^loX(=@4Ll&aCD)WQ -zcB+lxqK@>~6JI!x)+X6k+P&Koa@jmaZeS6lKGKPm#8Fs!NCV&X6b5mX{v{4Tz|tAf -zY&KpPYjTaK72PT^GNERf>@1lL5L)BLl0FB@+AQp`)oz((JkRrH1I!o+@dlmTvcz5` -znAtr~zaobwaLsG=kSZ%nm>lu)MnVU-Ure#c5cL3yYP82W`sar-@dgkw5D|^Kq|){S -zg3wZmB;RMJ%JB>sQyjgJoC88utfhm_k)p6;vQW2X1t65)O`n>P(9i7*nEF5812x!i -zzEB@Byh^pSZ10DF6aqyoA10snzwU=_@x438BoUUw>4awC8@-}&6K(F27#s+A8EgT3 -zhN)OO#Fs$kJ2wk5DZzFk*BV0%8sg(PcCc68KAnisLf4fOS;*$ -zp}Od>d%R611hX!W_Hr(C5Kpe$oJX3|uVm)DX21%y>0V<0HDh?VN8Xgr61qB5h6b%m#4h{|u -z6b=d;92^NA2?z)YBP1jrA0G}a4h07V8WIc-Fb^#s77#KJ0003eCnhl?7$G4cDk>^3 -zFE2PJ8yy`T3kL)gJroi*5)(QT7eW;gG!YyZ5;Zk71Plb5c5pu}AQ%)7m;wL>5Cs)S -z7a|)LqZk38r>CNNa-V;BJ~}xlAQ~+t9-t=zn|pPl1^}ajel;s6F-aVea%`V9157g? -zh6Dkoh=ZR(0bfc*rGI$%2mliq3qe3X`fC9AEC7vaV^csosf~#98~`&s7jITlr?0O! -zR3V^rYI*vo009s#PqD%p33TJ$1QhriI0$f -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 -z3nUqAiH<)XP){Q*v9{=3sY4Pok} -z02Q>H_9eQSIO`BD+)`3Walb69Zhm>l1bE@ChUkAd%)mn4(Dyx0Fd79V5QRUN0tHvA -zh30P*A?k6G@Q!C44{^+B=SjlKO7tPI;PWIsF0Fkz5kR18#=mAapt9KotZi*LX!mDp -zP%u>Rs6S!Hake3i+Gk0_$&UFx!VAelVORmMeS34{FgKQjq3gSz?{~Fr=dA3G?(<4K -zkaTpO87bmTlR5swi#iW{lFWf%&U?_%(#VutIwhw9#(74QuX)zOyho_efXgMipWnuz -z*3(|2M1zN627Nb=d*g4QgpUN?e}n}*5IV$qA2J74(FUUNBgI0Uo${g>nee4Ojt2!#A?g0PiIo9u -zi0&MneH|4P-!%wsqww8ILK8wICt^-D7SLi*g*;y<=mieGo8a+S{^#c-aL&(1o^}LH -z)5#%>Up|5-2U%uK}5V;%*)+xe_Vb&{*tbahiP@mV=%~*#cIJ~ -zJX0Q%G*5Cm(8 -z;Hw;{kQ!>`C<<&Y$Jscsx>3lBmF{({7B8>wUv}UA*!{Wy)Z;eag6t=8!jIG=={6rs -zw3!&`Ld)GmqYE+!y)nJF3msO?Mk1c|D%-$Hs9x7W$PGEk!ccu|EnxAx+DEum8S$^s -z7Esm!L8@sM6T{+%V+9zi)oTCv<*|JE`uO{|Z!c@I7bIVi&V_BB*z5@WZnNH6jqYh} -z*c&nx6AS?acl&aDt-`MWaJOPtyU{03XDF#1-srUI1=fS?9nrxY-*5LPm$7-#qDV#w^cz3*xYR1AbO6*WAY -z1SY5f;InP#2tbF1+|bI%`w@d@DX?Vzsh)zM)~MrDAXpzlv3fc`zPw#FZy(p|>GXKn -zoG$;`W@`Dp0Puf^(@5;13jzHi=TPR(cnWU1pJ>-b{*e0veXBC4B`P)Xpv@3KI8N;} -ziGa@2YKigRQ2aF;R#WoE&`Ib7${>g%(9wSX#A8=pUav1No73*Syk3!wX~u40e(LF+ -z($Tqom;PfQqUvc{w5KzG;(ovS3PJF#hIp1qLsZlN@OP(jW0fVSYbi8h1!+|o5ty1> -zt;rje#)u+`RU$x<)EKU(-DdMxo_3e7Z=37s<8hiMDU0~9A!Q*5w()B49Tb%4HCX|` -z^YhnDd3pKxKs#LC*2oRw-~V&3;Vo6m6u8`tse-07$aJKzxG|)mI;B7P<_a-%mQCLJDp$f?$kN-#xyRWqEyhE?-ZZ*XPS- -zttLltQFkQuC@=u+?V;78dYv9x{i3OBXDlJEqRjk7fxcX4GyMMme#C-f=zBM_kC&dP -z$hTeqG`At6Tys%IN*%K#&>Jb@{OR)kx&eU8^Xq1Jyq -zcWr79Yx|}mZ^@gv}h?8-wCO`)A?SZl!f1e)aGe@wE{H#iF7XOb; -z>w9qaxj7pRtGs1+`7_|_{KD10oqen9g?3A^` -zkcXPBV<_9Dg|elOE#x2UJ?DO(tVyFxG?vFdzWd!@=bS6`09aXgGY8ZF+F(4uBV@v& -z1P!Shp`oywIa-F?Ak84AiP+AcY-AGoRshhS&R~v!*Cen;yWXJ -zwd@xX46)B{KTZRK4ooNO#n50PM?7IkRVyThtN{iZg`t!Kj5l1sYqVpDpiE^5G_Oq8 -zuSSRgQtVf4Q7xOxV&0V1)o!=kNe-~~yM91WBnBko6gn|A0zu{00L3dRJmsqWr8%*X -zHiAwZLJ|)m8Zem*Ze;UN>7_?80Qu~6<=#<(qqK5LT|&H{(TbV!GjYJ%?WUAA1~6B) -zbyc^m7yuNC9ZyrdNbI4sI!wtZ8GQzqTFdD^?O6gfgKX?Jn-NFqfrpWnVq*I8iD# -z@f0`sw&+M%b~<*13Wt)8GDlCdx7r_Bo8<7>qG@Y_xy?!*70cZj=|9q#Bvg7bWnwV^ -z5jE2s5Epo80T@Z-kkQZqT%X&nkg|gkdk@6%M2cwC!*MjA!_h{hfFXv^7>)r-i59G_ -zAk#R~&3$(Jty -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 -za-niiBCGB)UN?+HG~&_Q+n;V1 -zU0v2d+OTw=WHe!tgKVku(#Ic4r81W!r|~MNOeq)}jpoJ-;@FbUlXB$J_;!15c5iR{ -z>z6*I{@&mqB`ft<{Iliw+}kKWQ|M=^QjMt{Su2sxr871@Rx)(jLLAknc5D=i@Ace2 -zoOSp2Tdh+~g4XvN#Bg0G7ytifdQ*!iMCZJD*PgG=r2_+UI)`<9Nhyn>`Dhb72>wB- -zmju?VO=&tuB9F^-x-JtxFfkfVUtqm#Dt*7dw|#j3>1zT<_O`cwJ>0`gD4VgQF9WBZ -zH!=~%?#B|{#ih53;-SP>rCbL3SbnP@+&RtDG-O-Ve -zr#-xbk^m8^c>bvR&1tOBV-#pMCkW<4xGV}t6uMj$#%U-^&GZYEQDrSY@1b+3ajB -z*PuQ_LeCfSMFMc~u#u=KVwfZO1)>@*Q%jZ%L%(V*xo#LOS*1*7-W6`>hW-Ni87Jw& -zo98=7;lAx8xNmp{IS!!4)!dYiwwzhTvuS2J!efq6_1%z94Bn9HEs91}Nw*PY<}xM* -zaM{{_uhYC@_6#Jr?CqA2e2qN*a%K$o-B|g7=k4||3bbpQ408j)vL>ZRBlYf4QlTV% -znydJnCsncv5X1}RG9f={2tZfzWC?Ss$n6Wv`{&PC0p4FSk%2W4Opka0*h!F?lz -zD#R!v3#qhO)vCtKra4l|4&QT|4oF}pxsjZ8wT}>RU9K1?A%-Mqb%`-})30A{4)+Z~ -zqN_FSxsC$xyn0bXOiKq2Tf}j>o2r}^RD2TGm8v)n`A0_g!DWa6SU}>0V7dt7iuHnv -zaHFy}d+_PYzd!0Y&_DcyqH+ghstX2|S0coQB3Xshr=+0JQaq8dy_S?s`gXd)o -zfb&U`RbXoY(HC71Q=vmdGDHR8kS9^MvPxmKR?0bH8=m!NRkc0l9Kot}ic{!j1>5=dI953F7fQeC3wXXlAlxwjBZZESCR8Jt_(*e=Z^Zswpi6I{>1QDbw9OWUQTUmubNKVfnkrF#~M -z9ZtP^^X|ry$KSOyYVViZH!vy?2}%8mwv9ZOu#6?NJ6GC4%a+m8?lG(Nf7}=S -z&0T6-rZv&bAZ2h=OJ_AnYg@>slRi#BkH6kOPC^+^QvDNhP!PKd+gnJJZ{phef_4hn -zl)XO9=+^9<*_+KSXusmS0i)*co~w?**I1GlN)Qdd-WnR649J7LHcZ%E>8wj{*TbP%vk7)f -zyG#bGj2p3%H5<%&gWhB`8Vs&9T9Y+QPETfW^(M(JvD_q2wY5h9c+cH({hb&*ktRUT`oO*%golm6*j^V8F}r>AswyJLQS+CQoD -zpB&VVYPE0o@9!TS)M^I@wS(7Fw{H2P`U(I1bkwe!n!i0|GTHt9$*9o)?L>{nsKf7{ -za^Syza#E)^nC&{R*RGocWz1&1-e}SrHTP+Pntm3B6=FcvZZ?C9T}Ie%0`{Jm07n=m -zK%(~Oq~EV|z+%cjH9a*oO=KQ)zJ2@d>RptTJ1gu1_Qwb8+REA*TVz@G$K(C`FORnN -zx3;$S_TC|aNsE&;51#- -z;78aIrin9an1bfH=S@#2E4RjRw!(#1t}x4XKs3JR}+ikb#& -zkw}9q8lW3l;zD}xzIQI1|9pDd{P3aKJilmOeBjH+XJ^GTltzP9ccT^qrYPVXg`m4Lus>2(N+KtA7-B>6XGy@jGxS?7tpz761 -z0sgu~*1?c2m{6^DC6)*jpfO99MW~PyqegwH2%1UK_8Rk5U6o})Zq-G(!e9Qb?%pUoDwTEaf7 -zHIz!FW>S@uCsxQ*5O3*rcH^6%Fcau@`%E$(NajF;crw7`C@K$sR5D3Xpxs+uxF`#) -zmLe)5$H_cjZ`Zl9EOVSFKceWJJm{UoOa}^AD5jLp!#{k-k-+?9t}pciedwe>mq}GB -zh24}dld1T^7ON)}OBFmm=!#UV5QOkZ=9kI;GmzOffUv=7@7B^7JWpQvRl!eRo%bgr5i#cy#c@hqXPC*+$X!Jym -zM!*STt=w*>$Cm-6Gl*TB^~6#$n2S&7Jx-122d^>wn6mnOvk{V(9Py0cZ_c=L*17Hu -zAr3MGy%Uckup_y|#Ay;b=q#GPW=Ala@%-CURcK$y5H$)f3oV{2H#w!H4tku@Q|baI -z$m*c2$Uhy`HZzv>0AZGpI6~!-e*iCNvP31pCD^DtltLz;dp3pOS>!B$>P{ch?VQet -zCeQ=;sbGz13EUA4x)_Vtka2@pIAnf;*^w9o6h#;jh-!)x=nfJ)us7~4CTm}x^GaJ3 -zAY6F9CAU=>!{(w5v4o2A&=oC?>$M@Y1cm?b5sa9*@hO99;}wt`#bO-aAp8KDWHcd{ -z4k;3E4a&;wdslNlNq>&OdBO3V{Uz3&GfkMT0oSW|2FJw2F+!jd@-Kq=@7! -zn$RxjBpq1n(_7!3%k_(ToiEqb3tlKgtk-#tD~swCbO>A$9q1Mju7@(Gs2An3s`S*; -z*7>93{Nk6Jg!_S=nZy7h(d7;hmgB -zVF*P$1ja%rX6OL~HY2~#iAnz8cPCuObRf>GW1{xrqIC4rX-iS-f}r+#Jzl8ygl3Zy -zFFWTgkrVlL83g)3=If%uD{xi!pa&GS#n+X#A~xSQ;$L9kLk#6e(mjp7$G)Xuw7}61 -z$&@h^Sic$)BWq?m72<_exlM!}+2;y&AWHr8}Os -zd(-X_66zdDsk3Nzdso-L?~a@CN~z& -z->u&JYW4mr{#yUh)4%LL{nxs|B5$MndjH|GonH~S!^Hqo?s#wm1<{%Po3FfFduT79(2c7M@P%$Y;p7Z#ecFm%ig{Jte;c9 -zprfAuh(Lbu$bNGF7|#9SyPtk~hza=-?*Rhu>OQL9`~dls&_JwIDr=Rs^~%axrGg6F -zEBkov?|WW(-A5C^^410J{p){ze)k?#^uf;A?R2km)0;x-z3y$ZH96YDOpDBUGlb58 -z<7m*no=i~34Vi`}%i#j$#L;9t93V=1?b(1E42H9-%j50ka(PnA^DIdc^cC+ufBwhM -zFL>*%=Rfj(2ag}$TgBhkDDPL+R_>{uzW3_Ylc!HmSAUIfN^8|}r5cv2YFEOdf(=}V -z;@m|pi1>)3sGgu`&+`0Y^JYG8cRE+?0h0R7<@WB`Xk+_&Jo_>qboRQP@oa&tw7fjt -z+}OP84ZbW`1@&fM5Yt_7b9;HYSnOen9(LNF=EK3>vbk|`a?(iCyjH7cS;9^OFv8EM -zAousnY@^oURj-0J2!{IpI^W>$6(U-@zfvlrgDQtX5O_479fjG9l01%+dV&uzEKl=1 -zO&g8cWIF70#=Z9Zs(X31+2V<3{zm5TdOSqweAJoG=hv6JBmVMd^X&K8__ui*2#&_> -zMSHwB+}*|MUzRNKAgoDmI7C37{rBJfey!H8;ULfwsK|hVIA{&p0-vU-%Yk#Di*j|{ -z^DFnO>uW3PYbBUYji40_;B;)p&d5bclA`5e3j@>hT7$;p+5dR4J?TwvCfA$I!{gI8 -z8yn{rCns-OXB!iQ0Vdz&<>A>ztJT_k^QP6@KD@jcEM_+o5OZ}2q?=8w-ah^1wucM~ -z279yiUUxnloE`u{qtO5lDH^AGT$i3;245W{0WlDd5U^GALTF~adS3wW5MX*9?#9+? -z71e`fG@k≤uTMa1KjON2Bwtw;vl@Sb@jZ#fObch`iZsZ69BJIN#aX>Tm6wZXb^} -zmTe@@<+6E9T$^Z-N3GWG;pXHTJ;J9M8lNr_(qQrd`-bsoO5rf{WNDtz7IdyIj@8J* -zLP$g?LSG0HKWhrQeU_lZ+G5|HYbx-Yc$%uI6*&zm2VHuFMd6Rl&(6gKyGiF8_o0c -z6S^N1=QVZ7S(+wUMj>aBk>Q8}$s~*daS*Ee_q-BA6iDNQ!pGP;!!@Ddb?gGN1Q>XQ -zz5y3-5|xzJ^a(zz0Rle|1cRhcrID$cUV(4tA%n?V2k0nk^oI>?G_)%S7w?&Rd&wa|B7tk; -zD3Fj4*PtQ@Ew0NYT2F8}h9#1}G)VbpNgbAFags4j!!MZ>l@`az802X!%Z-{Eot9g= -zB4sv`T!}V`o5rVtRx`_OQTr3aDfgFa^MvllL1N_+c^R1|j3QDH-aWihLIsf}#0)+$ -zdjbe@Ssgrr7D54wfMQetPo8ntz?AWnj^Gg~2?GcmUC@9FLda!m&RmuWQeL>BrhtXx -z#cL{#^Sa!CZR@F=$eltK6b9lm=DDMY8J1_MI9nkaN?OaAiYH^JHEK?nsG}rSWME~2 -zAZLuw_?$$M-L#J22W6m+aFD?nMDY1q@=QPsl2YF@4lEiMhlWlbJ{YBoJH0eCNK*_=IjD!y2XJ -zo9S#_mc@Wbl(BL#AZR!NVs)mug^Mti13-q%Cl0s=T7~TihjF`zhjo#~0!`JE0Uaq4 -zN>1|@79=vx2yxvNlY$Fk!$t-fMpyx3rL;tOi#9X3B0E&f#8-felemD&CY`Elov{*l`>a3so4$ -z4sKwTL96)^3pgi0=@aI28cBps_Ar)UrHX1 -zKI!2XLKm5)6gevQoac-J0^u2DY{DtMNY(~TT4mrPj$%8h&6EdIXpo#pBZ*rH#HJz& -zPst!s5*0*BRN2xo3S;LA@3Dd?u{xsN_;YbUM~A9QX;M&xyBLV<%y+;IJuE2=6voPW -z@fI0(7zO_W9eCh6g_osreWj#OIEE!}Y>radAdw^@Qpr>3IU@&-aHM|?lU1eA8UQ@Y -zp)1O2*aHZfD746mRuDqfxIFQ#WUSizu1?`$=mtZ`0xK|2G1#O_!>fbX7h-`)AhsHw -z-@{_@8Ywq8ch`_)`a(J_e4)Sq0M^_D;CWuP;(5NW>N=>RQ1aLy7(&k4fe@&kr4i1y -z#3I)Dp6Mu*!$YxSOOc>ChJZ;_xLLhN!T;?p^y`eJ=#M<;?4QX%fp!C^(aM&?=-O%Ifro&>Wvv+G2odY9u3naDz99H_d{Om@`%QhzU=xH -znq(3~R-DHRPxyBxI!^4>YJl)}lp*y=_UyBQ -zeGN66+;@^!DhN -z1fyji-mXC?81gfnrxObVXwTZ^og% -zt*!x|4q)+D7zb3DC(*%LCEzkozXbnd+RSns$AKU!aDkElK>>3A5QX~HP5S;Oc7zW{&@{%pC&3js9Tjo_nv0_-+9H$7ah~-jkD;arWH5uv=DboXRx4fMBVcu}Nec -zV9$HsDGpyf!wH>deMI-%@&&4;%X);H`~V-TxTEsND1=kL`DyGS;IP2$eM^}&_FAuH -z*P73g$$)-yErV>_u?-ExD#txsBDiky$ZBP;ZKmIupC4#;C#W8?<0q+|5x(vWgcHEq -zSE{<5B_LWobM=!sz7o@UB>OpHZr>@8zt&N;o;Ho0wBn5&tL)b2=UZ1WAGqw^y5N+fQv{$UHpR5=^x3WYSE`SgEF3|G;984yA -zEG`FSp&+`(?Q^vL)+1awcS{{=|CX -zdhHnnt0KIkKl|776@3+t6tNlptbOk&sj$|y>xo`2y-e(0kg$?%)542r6>f;Yc5CeI -zOPnC{$7s5_WS9&vmaf#o@-JPo`~WsGCt}XfKaa5_dj+9eP_h+TcF%=#?0qL7uuFgR -zPhBln000b5NklLUr3^W -zYkO~_TPA4vye_AprwekRcM-MrDN|?Y-^tblMKgL|%7C1@&?h~hqL#n8(h56-4N|2! -zn6tEso=(|7Po)StOTE>ZCLl_eozg_Dil!+b;Ar=_ex+6p@MCr$85_t#F5>}M5miRr -z$gO-ow8oTgazK=IA{3ht>`Qb%)n>$m$SHi-yOz9?T0yRRg$6wa?d*lVK7a`bT`u<4aQS7qLHtTyJ>+t~^Yt>!I>VckI4D~rqYAQ2bufj;cJ@mv -zh%B?q{HYA-Sy?1QKv1&e9cWwT;mUXQhZYL5sq{`wBz8%OE2R@D){^9OTQR0XU=dIm -z(qQBP_0uvdscMi)9=-xWx8r0N;AxT{i%?5T#REb#YwN3b>ni9XX%k>!R$yaaH$dr) -zPex=V7~kCgBHD;L1>&O&;OIh@A5B(EEf -zRUj3L@n=(M<`T^S?z0bt?*VYTR*(T`YHPw(6?937P(OxEX=hP}!4GWnY#GBlJ -zoM$;igyp^u1HCHh7Us{d1}hBt(oP1=>&c?WWgcG$I8A -zv6^B6f@kN0D_0VkjffUvfr&Km?pe71EHN{&Lhl=O+p%-S$!i240ufTBG7O_A74ihvg`OP>W#w)=_rAs)NI`&M -zuvkj~9bgn|BNXt!U?+}l#hxwgfY}7gNs^2P3pXfQEfGP&iVKXuq*QeO0+upjX9suP -zQ&YfO$G0Z(#Ta--q1uKVm|KL4-IUwFC}HlWMC*vBDo8lKN{kRUty_yIZ|AcDyG4=p -zu9ZOsc8N0Y_2JZJ`YcM8mi*vv}8Ub`GtdpM`` -zbWIZ_1R+b}e4PWn`kS~`?&{R?7>2lXLZ%6$O_m09XsI$C)UP@OFUf=4*3xy!@@Qka -z=S3iwX+K?M10|nMbtj?#3&P1NoS+J8MkJ-g`O9Lsk80`(DB?0al`(_u6}8n&W*6b5 -zWISFI&U9WDKPoL9PEIJg!Joz{XI^7FD5##hLeai}r(mDzUJB6;*Me(_PHt-)lEt1f -zkHryj4jqt;!r2iQz{vv7F?>6w?UuQ0JvzF(un+VrCSDNQ^oBcpzn$bl{P@&oYSc5keN;o<0K%k`ECEP?x9&*5t -zUjW|=8dU;#XXkCv411skP_CjX&C{fj{gy-!7Tl!*OhH|Mj%>1(L4UDkw@4*Nr0gFaPfwoV51|6 -zs5+9h&uaKdCYM_Z14J3W_0;DNJ=yp@(2Q^veHElxS1_5x%aL(it@GpN19r|9` -z$Z^)zqFdEMLD7+X&@ziqk2uhXK^Apj$k!;c8@1)0g+qf`?L&lGpZ*RzU9{=VGDhaRP73Ct+Na3+>DJ4q -zDCJT8@OIA}sRRAsK0tq)#`GcxUdPvnwy${^>YsGL`r5woKKSj@D0L9!YkP}Y!c0Yl -zNl77`c76HU#4*rIQ&81nAS@mzKDgkNUB9`v4!Gk{5JvaJB;Ysq{7zcW>Qp#oiMNqlb{TeXzxj2Smlk`g{=)c?A+mfXf+ -z7;01$Bmzb@Ugb7(|5FX$hjM3%E}F!$CGq{Jw$yTA+9Kxtr(;c6Vf<17fJZChLsvi^ -z@$Apf)VYEpfZ{-IdcCUQ^fE8}*{-D;|Eb4)4HNcxCXH%_K&HmA=2)=huGXRha9WTZeKHXP5~*h-*M>K@`pzLoM?kbwFm(r0(>0avNjvIL -zZ#FiN55#eDndC}Q=RwWgM~&a9L(0_Yo$Lif#R -zb%o`lm6|atX=N<+l1%h8EHWcr84I5Cl(|vIYAp~l(&LE0l6-B&WlP!rT85MF+hP`i -z#ZfZ_*1JN9-@NQjxmDyRgm_I5udVr8X3mSU2dQIuIv>&05Q-q0^srht?M3FMI)fXx -zy6_ay^37N5j@gh3A{K*_xrR;UQ;Cgxd!50;PclaIOWYoN*p;?A+R|vTJI@>xy3*SU -z=CP8z<@^Syt+L-lwt-O1N-v}^3nwqV{nbj1+|<4Gy?xWiY%pEwE*5z_HMBO -z&<$2*@I6^-r0_=8nF{Cxu7U7kvs7jsRX~zfF5Cj?Qp(&$_}Yx1t(+XPSrwk0aw$vx -zqgD_qw_4GjiwizrdlZeKC8lPvAlauU`mSYWv@gi5G*8M=7q8xEDVQ=Hxra3%tih=( -zq|MH4E0roTi^GpDhN-;+)9TFi6 -z6d)m2HW5Zb7IAfUHfUvP&dKRp0a>l7W=~9*RX~_R2Fp7ECN&d+h=_?P2LE9IoPBm; -zAqtRkYp^B&^C` -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 -zrON5T!lQ}8&4O*m3&$_KtulSC&dk -zmD1Ab!GyEn@MB8sRj}E>YnHj+({ul~2jTEEsp$32ZJs^bt~~oxs=NW@>(}>3NAHhH -z=cUrp`LjGEgj69vQvVmjF!S^M{r4E};Mmx}!0_nE$jIc>)Z>SzN8dJ{%*hE -zf3ops>CO4K{PMS@B}BCG@bT2-$mqbpz}VQB7uDzuhZ#5zllvrWiVU2`j~`DxD%{ff -zcIM#b_~GN@o6Xscy_<(q52q%97#$ua#^B&!FEfnUQG6imfk-1xG?RBs2kh9S^D~|u -zcA`K86cPT0ode+vJ2v{$MhHK0XLL;fdVFk*@dAUx3nj)d*mlRX&7qPFj1Jq;(&5}7 -zz@T@KQlSA~K^hzzw25F2j&-qK@4zqY*d5otV0s}X05GD^=)nHpY}lZ;%LUdBb%3Z| -zCLF-Z2=|7uKmr&sfY^oM6zB$LpB%Q=+lAUA<0k)b;DI}Ap99^~0~!K0E$onb1KqO? -zCtQF==0Q)7`!_cn+&%8F8-8JTAkfnj4uI_7XmohJ#I$3i6O8)cf!pnV@V^quV97xs -z1foE}9F`kZbI57_|1bA78l$4{=$=Hfy7N4az=nS|*9G(a-l6tX|LMG2BkJ^l`jc6+ -z0a3gWlVg)zJRGX^f>cZ0)asfQC~xdtzxSE8%o=J>ndcz@#g>qUkuxx!WWi->tzRB@ -zuP%u+%ZPN(wi({APRpa>Q|U1W#K_oWz(gtybbixE7sseJ4L4dd91)ahLdFz0nBtOH -z^Iq}9)^9JgEx0vSkxWOY2ptYGxEYAb3J)f(oYBQmA6|zUlF4!b$U`ONYOWXgRIGUMQ*3I_-p2pts;77z;u76%C&2^0It!0- -zY^#WZngl9HRFqJxQvkzG$gJ{#0i -z0^&aaRVfX6?dP6Q1FkdyKP(a`D-*6p2>W;dcHh}cotb1m59n$GR~!d)8V7xXf-+%V -zjY}w|YhEb_0KXamMudP~s;5T~05x=R104l@UK~dp5hY4U{)_-ciifQ)HIx7V49!VI -zK~z}7RF?^J+E^Ba)seCgQIT3G3J69mBLs-@rfL{UF>*10!Qchs-LaYS5+`0_XZ13f -zad*d^boNx1>CVi*opYZYcg+Qa%UGYzJ@4rG!by_kgbl+o48w68OQeG;iug$sRW+eT -z)o22xTE@osgc_DNHXPN7grqP|3!PU)-~citBTSUAV>y;#iNpvfgh)iAgaSy6s)iJ{ -zEY*rca7s8FVUR*0(kDa!3WJ5;T99HnYQPXx5h~O~lyRbXhel|FZdJ4l2_VA=6cs{( -z5Q&6H5vy?6AT^?8ffIld1Og^aDAf}2f@;+QCL}?cA&b*F_#GmUfP2IND3QyV -zIh1JzZ>j|pI75RZDxFpx5g3P6Ec+)Yi;~&GJ66h!eJh(Cm(ono8HqBP9)O#) -zrm8~@Wr@U5lsHWTNRu7I3A1eg=8IJVJ1<&nc07;(p}dD%QMXYH8(f1FunEj -z?jO1096&iwlduUOtH>odDfi56p63*SRrZX=cyjzq7g%cxqvfA=M`8rBW1j9<36}G4 -z1RMfoDT$oIah$@6XbPvvFkf&@{0#|~?>@LVxt$5Jk`JwO{=B!ZPOU5%Lp -zIb2Z5WL9I*bcvLTQIfFyrad=OE|*8kuQRqut6;{(Xt&$K4kX7(b37aL22e7|K%A^m -zqT+EKotZW$K>+4PN~O}m=}0E$>bYVj=cVkTEoWOUE-%vWfbWWirU~;&C^Q -zI3fX5MVLLhRbCh=+Nl&4&h&Bw(sb$zsy&VMMv8cz-FJcxn>E=(0ODSbtv}W9+**0G -z^waJ;h=~=4GiC}4V7oFUH<$8sjkP8OtudRk=V@ZG#Pz5-i18!568tXv9V&$*fh -zjtd1@P5wWR*rI()QxHp&%m(VrZ2HkQ=>l;rE4%cmEC`zH>H!E5NCtLu=d5#8Y*T^R -z<%QERJyy)5bT^kV<*c5{#Q@R;+-t5&AW9O?^T{(<5GEl@{^8bx^(nozwmZF5-kr{6 -z$Z69q#&p>WHVHke;esynC;;)_U`e3a`XQ4kSJq=OPhKl6lzti=v5RxVF*BAKw&O1H -zgk1(<1W4C{Bm+f|GZ;rIvk0Y(t77$R1P~qpwMMrv3q>xD8!;7k*#;6U5w*rZ{5gj{ -zq|3NE+i-E0=B5D}Ew8~Z&!zHmIryvTvY-fO+fShAoP(xVCeD^lr)@=+TO*}%d7(7Y -z^30T+l|9<`TupaV9`-}pE0{~)K>2*09eK7Jz&IqHYY9qH$tr8RyIbY0HR=cWICcb^ -zfL>ftTG>`h)>;D3yam8~b9A&lcXV{HJ-a9K`F3roX)iDHno?`eR^Goam)^gx+KZJ+ -zEw)#wEVi4?`f|I{taQGtuCBiQxVrk}E>9pXzr1s_v%kOdW`DWXSZ=ke&Dln#Qf(_O -zztiz+duzwlS}j$nH~pn*rCu-k&01$@&#$-td$Ric#fy^{FYXoyl+5oRY#%Nj9oYGN -z6CYO_i*3JKX*3gX}0Un)+$T>tX`}5ON;f{wcX?6t&#j*y|cH} -ztStMPX1$2W*Q(wFxRbn#L!m&oTONHK?(ZLN7gKG2HlH7AWSV<}ekMOu_a|!2Ki-YL -ztCZg5Yc;9St?jvfu~x||LbBs8DW9J3p$DIy-2Hl&E!)AH?ZfRiJ3Bkui`8y2-|@Tk -z77r={?} -z9{=(9Brki(iL5YzOerZ|UK$)&``5tWP$8@H9w&G_ea1EmZ~WQS2YO49AE2_d?FVt> -zap+Cx^n%}^&k6te(dkRdGqG{%KXa8!-sf2Tv_@3>EEfI{{HCEqxt#y -zhj@GVXk}%7{%?Qn|Ni@)D?QhH`g^WiyL=HKjQQaWNgN*+$3^woty|A-4c!_VT3?@i -zmRUcYy7TMQ)aKNkJENW1!Svwz`uaa^(PMu8+4%UxMp&KL7$4sVV?0hKF^OWR#Vkwn -zdK!ZsETKVWeeCq*otb<0Z~r&<=A>+B7+MIQDFeX5NSw`NM99b&hAz4{eixa -z&~)io!HCxF{xWv@&&|on$G0~hZ{B`9IWskLXX@qC)3Nm-1{%cm7|5_Nxnkl1s23Cr -zJ7LSf=w#3=tDCP*?{7}d+}oVGKlz`jdy|`ZP2Qfly?JNq>DnMcXzs%3k7=L25IOV} -z;^KFgH*Q?KeD&JK#>Ta4m&V7(fBCKSzaq=$HEtse!`2m_iLc9=Lo*26F?rg2=a)|<_C -zdwy|qcX#vU@Av=SyxiY@d%gVY=4JPB_i%H0`MA5=@9+P)IBYkY)w~!ML)&t-eeZLs -zrfHhTl^~ovoWtSb>G|q}*5O -zIE{Be8PdR04 -zd(WSTw|!0?Qbu4(xzEA~?2q7r7MbU|7*H(f`=PC?Y-3I_vNhV2y|93NpHlCw_e_ag -zi<~5~FZ%nGk7Q{Utge#vL`D;1Ox^}-b4ZhbJ?UV47TqP-Rs||!a>iE9n#guEHJ0#@Toa)2&NQwuIYtZJ -zK?*`Lslu6nXHk}7#zkjx)pSnjoN1P@*ca$17N&`=v$#&ZMW<8-s)KnH8iFL_HV0if -z;tgVngxu&Q>>?L+HH@3i*rr1bm6oytRyfW9FOf>|sKQ%QvDJ|j7Qsyih@{J=Yg{qQ -z8D8+NEOzWfIJhP@od~Qt8A?5-Vv!wPSN0n~tSlSrIw}lYh>{0l^JRnVqQa1$gLXO) -zjtEu-!Y$dwr*XIErKxmrFW8wR>v+(!dA|%1j$uptE7fp&5{-@el_~Cl0EXm -zb#PHxzzxw$M_C;v;84U-L4@oo?dp_Ayr4@+v6rUMH8!sB1zw_uXU>&E@hfrWlKUTA -W;ndt$ps*+a0000Pr3nB{^5DOO=7!NHE2?+`!BO?hO2@Vbp9~KWH7!e*F9VR9w -z5D*UyCk+!02@eks4l51-009jt4H66q3Ly$68xs-|5)d;GDjpRh84@ilEE^je6%Gmz -zFc1a@1}Gd9DJdv0A{Y!N3@soQG%zp{HW3*a85|rO6FCwG4+R+#3^^wqGbI@mKot}` -z6DT4eoHhcWdUG~8ISdR35JeZIfO(lK06#4s_+tR0K>=%2Q2ccO_f-Iwc5a~)0QyD% -zL^w81K06sH5Kl%#jB90?AOWq4g!nK385|6gac6a1Sc79<8Y2&m6$36U6YB~987V01 -zA^>1ZM@u#wtA>BCkBJT#7^Ncsj|BmCAP6Kr7Ixy?6H6IxG7zXS0yjb(5EBVCQX(lx -z8B$hOqpGTMczA`2i@BGOPAL(khJi6O8N@XMEC>c;#=&pc(t{`jr?0OM7zyq_0Haj_ -z>rns`F)s=Z4qL9Oo1&s}MG}rm3QL@rp=Anz?&%vd6Jcd$JzOPt5(zdI5iez8N0N>? -zb8tpzER>s@q+0-ul$6m#0NYjo*>wcJR0JbYPhl4iid09(5&-;`0RM>qpk75q5CW3? -z`9OkxthBVfjw%=c02V$;L_t(&-c(l4YvX7Zv_S`9B7}Y&By9A{NGw9avP5JPBeSxh -zMFLuMG1DxDK1^U>AxR%P+FZ`(B=Ud6oM-_>lbW*F2Y(A3y&3)Oco@%x>fo -zQOcQN7&AWcn9XjUCXpOPO;aMIWXm>@%mxn;w#hxS$tBM>bI;HDNb@SiMKbMo@66P1 -z?C42CCVrV1hHRIzY#SrMOoV^BnUp1f%Se_>CT*6I-!vXd@fxIUdY2hs4dWuzlF2;RK#j`)7=A?3V6 -z+$yc`L -z$j^PGU**-RTH&w_z6s(z!MW_&jBdpF3Jm^C=tbvmz>wfSt`I1E39wB8SK>kyzB&kT$A-@I48fr~WEk -zfgln73NN&s2(tvAvhmZPya(TiybWy%mM(cF17XP7L~uyfvNAcMBcNFc{q!MBCXK*V -zb+x_P;tDT7IyUl$jsXCYZQ21Pm;*(cHWlG7XOo!#fPtYO99wtruwWW`8yfY+Q4UWpPfnOk4QY0$Sv^m2Y0$_GIqxfymD4TVy>vc^GKp;Uc_gB?b -zT5VTH5q?XYl~1$b15#c=IH*K6bBbDGOSA!C&fJX&ikV+(652|4NoG+Xd@YU+Vc5vb -zX9KRTw#V)9cpRz_B++Dfg*lY4TDS%;GfI*;Mtp?jJW3{fKaV{I7pLAAz}VNtzTWR0 -zony)OAkNisiz0}y-5ys^tZzaJz&4wtq&YFEB$yIXjAQo)fH4v870;%-@Jx$f9~1#k -zvEK*5zNjS*q^er6g2VA}00pa5M!|i$XU2TvdZW -z6(06G;_gcySgu -zyaI$eIv%zMRB+fHp(I2zq2R5AN$;~6IY>)*mSY7%u#=?%z${yG6*RK#)K0;WVYuK$ -z{189GH(SwVQgk2yl?n+T(d#of`(u*Xc;O?`Y=~7Qs4exaHD_Bf-sdCtEIA!E^usMN5EWI2MlXAUu?cRxc2Az2nZ=gnIE6lTS*&NFA^@+2o5qt=#Y*kB5KXq93WEb-lY{=j<9x9Ciq6d3Y3`RVmhB|+GIMN -zYU2@}!^tcl#9EB0!F~^?SnRszwtMc{{=;uO06;?FF{nfgsp2>H98mQ%20tO -z0Os@gbjr(osx0~EsjKT5wCiStDq2nF1x~O7Mco;v|BkMNG+_viV7By1+!q;1Ru`x@ -z5ETfkO$UJSj1JM3j)Sd}DORu;?F@z%i`~E5wr^3yQuCpr4nJu!3!%qtlMM80;oqo9nmj{kKzpYQKHjY*feEhr2ZZ3P@n1 -zXR%^Af*(?}962KU6LeZ&P3N(dvAv2_6jYF2Gw<-l%INFo?tJ_3*gbyOZ5Bnb*btyr -z2>|no_^JS?F@A;;27tL(4^z0%qIMK(PxUws8G3Q^2Eu%Md+NIL^TYio00aRb1pxH5 -zaV$lz#h7G#i}7Qz5=hYiK=EY4&l&k)2}Lm~loh}Uhx2oL`u5!SpLT21hQF*;buR#F -zJ#&N{YR6jAR?A`Mj7@e20L6_*KWBrTg$D)*L|GsDuIu{KZU6pyjX?qL0RT*GLUcr_ -znSbqSQ_bb<94Z(A*y9&N$3GziLNIz6ZT?r^wf9|56|w1vOI6a%sN5>3w;GA_7d>-d -zS0K)S;r_r7MX-kbuz?opp*??Vznr@M{avAMHsCr#2LdZ|!O!rS0#4#r9qVN;&tLiA -z1!ElG$eVrsOz6U++jGa)xGYh;hOX~f*%yRhs-H($8ram0zlk<{e<{2 -zH#AB?(<}$NigmTt@BTR5p1X(s;q+dGf_4~0SQ+aDTnYUw#3_)Pn3L8h(-(l(AahC< -zwzh=roB{!?Vxc+!Xxmf!<=lU!f_4ikXuVsYLdNHARSP(Y0<<#C9mEfS`Q_ivT>NVq -zV`L-utj1K(ogiEkcVGLS!>;RZH=N!Wb=(ypEF|OdnbkoXk#pt%u42BVoxqEE-o)J -z3~1OKWIEZUEmN(oaRpkZ$Z*KtMPkpb|Gr)KnBNc=ypXEwxg6QoCRO -z#P6NO&hvaPyR$nxJHOw1nHhFo8h^;br&;6Gi{+0%TCYNJZBTe-0sJ9MPi<|zot%Vn -zDpwl~(@255jPEao(*&Z=>Ms&L=B~x=wlm+lzuD~!bIoT5@wbMd3^#MQC1G^KCGcQ# -zcxh?)@DPgOMPT5EsRGqby?rvw{U^^4H$v -z%C!I8TR%Y{V|p&__0G)AHT!0K^jvVpOXwr%R_~0rRQA0;Y+qPdK*kwk=}c7NX7pUT -z)|d7pLZKjhb5S(-^X0*ZrKLBUFE~LvuXY=QW -zo#BHw<#?_Eflz<-?bNTF5&2i9R&r7lnFIYJGP_$ENzul%uXHAWqH{uTFvu+b-5+o- -zZx5knCY7#4^${kRO8GkM%uJ&G_KoI%y;k^Parnn3dUUWf{NqjOMTjyGpa(0{xp%p1 -zdn?nEMujkJ_tyB0bShdEU|M}??Q|tx1>-5Y}e67}H#d+pS3iy$EqLOjz$v -zH!dvyy&Mhq|FezgU;??BibR^GgPb&MS0Ih1UdpBX=+VxPH%O|0Ac&AqQFlX#V*tpx -zshodnYdV*E03NMDLgY&&ZrTGkIHs8>ZS=YWD8YRC6#n)k>JESVB)iWfP!z068Efp3 -z8&P`=fxm$g42LfM9Ny7O2?)aG&gOvzED#d4So^8HJqW?CsY!n@QmGfBNZLyWBf`KO -zO;n&#CV;r$TrmA<+0D$Q(|2X52*gF>P5Y{uaD60bB=E}YV*_yKAc@x=vaWJvsV4td(cA+RYH7Sk5Gl?qM`s+T7f58ySTkwd|rI{{DsSZ -z2n1I|#Ui;}VvsaYl!4T#NR!QK-QBfj+VcHF%l-Y!L$8KLvKe@0YTMnlwe6a8luE5O -z804bjvIddp{JAS-#pf?zLh%Ja#sqzReOLPWL}fj@yFER-x}Kg4NOmHdotVh(?@vrj -zjO|ZkCz7YGwe{om_!{eaaddRFKQ@-hPmCSy?;q_S!8_SZHe(`tdU^l}3b>&}Wo4_Y -zK+suK)LW%oExFa(*VkLr*K58xkk97_#`19K$mcB%hh>0n*$#PFmW_v8&XChHpENog -zElu|vmb@c5HZX<{V9SH5y}je(MZHD6J%BYnuIvN?rL4-#=ZbKW5hooRoBo20>b({| -zpn%^@C!e2&*m&$qi2dU9#A42v^F@+wYHD#f9LZ$TfnT!RyVufGEh~}1W*;ZNaNdB= -z*|h^fi5VwPZxuCGSL3$oCfISeSPF@Q4tjR<{Tj=HXF~DMPLIdsWU(3Z+oP{(a}P8B$wZ>hn!9i -z=p)awR$FuHng`6SQx&Hp?Xp#rV35F(k|E5_cE4ibr0u-DJQ{ucSj3GeBTcBHJ -zoVkXI47Q~>@kGBKonL329#U{ArMd)Sd8xBq2`b;IuDW!ovr|x9EV>TTAeR(` -zq9~H=(G85gH~~+vC-dE(*)QN9XFPuL`7`@99$Wt!i@Q!vUBKdW9Y6jzV{J3(V1V}Z -zHWsa}uAVW71U)742D#|^ -zl`;XPqA<2jhr!$0+H5d!ts~a1uGx>Xj%+qNGLp>?b@vZFPddi3`RA|Z)Kmtw?i3FsM)M+t3hBvj@Fa_oSimomz{=pqz -z8^Om&W+0#G8sQ}~J2W&qmcea+kGp~k&Nz5efI}|O@#zOB=QY81cbd#h)fP -zZPo&q6`Ojd1L%XBZ8qo)Mu4lpS)__GNMMB$0KN)Lz=mKN9jV3k;M;=WXPA|b4o3J* -zt+o9hxz=VXh!*!Hv7fLR5`l`uw)w~ -z8$>XmL?XFFgh>Ds=nQh)fXA|gAJX_IQA>UccvPjv%mxD$av?r59-gs8;FCaiC_vG4qbH8oxt^4ko02N;Q!j?ylC7JT6Jsqt?WN*m^fY1A$dG{pYJq#SC0+hS*lkH3T?sH!XxaSm2JptYq -z20ke{J6VrAY?9PDV{Q+2Ivc>8DcE^#H3yuXJV)N5OXUnI$iIyCK0nFtod9Icme9yp -zu8gNyIN6~xXF_Z?I1(|>Jfb_(0A&ujP;aXub?vA#3=@jFry#&L>tqFmT(rX3+gYR= -z+q5&$&N60mBh%sF>K1>X!=g=8wp8`Sdc_=T+)d2dCrtXHX{2NJq~QxxPlg&=|PE+ILwJIZpSG= -zoKc248H__XvNLv5Qw-)aNbQCq5KnmFe{`8|Y -zm!^F!l{FyGskHC?S-=j<@nBpW!V(-~tz|m_MIDDKOoTfOXT33FnUHxAV)vQ1x=Qc7LXR|&>RGz(^N*yN>9bi9ti%A4xTILZ4+d -zlri1QtTYrVY#uEet9nhVy|UD*_wwaXv|9Q^6osek0f6EFOjC*xx+N*yBPOMy*eJ@x -zy|bW+#Q|bhe54Nrns_NhO6X(T#IeqGUB6aI6jx)U5rVTe1GeGKoPBUUoB#j- -M07*qoM6N<$g3=yxMgRZ+ - -literal 0 -HcmV?d00001 - -diff --git a/game/assets/icon.jpg b/game/assets/icon.jpg -deleted file mode 100644 -index 10f3fe3296d033d79f7aaf2ff33c0499d230c572..0000000000000000000000000000000000000000 -GIT binary patch -literal 0 -HcmV?d00001 - -literal 24427 -zcmb4qWl&sA6Yc_wOK^90cS&$}cMTBS3GQye9fHf^1QrSIPH11$c)5qyTWR|CRrK;h+VG0Q|2Y -zAt1mbAS0omAR{3oqo932LqWwrMMg%)LdW=kiHU`Yf`*NQjfn%j$NWzS%zr)MfJo4e -zn5f98(Axjc^4*+1fPjdE3~l~| -z34n!xg@;~1AUqrp77hRd3kSpkz++Q!AxI$NP-|Eqx#4n$rqDi{(9zZ#{U?U+R -z61ICY<#lbm7xT!InbYe;!ZqK4!noN!B9#~7zy0mD|0Mi4Oy00B!j*LW{Df0JUD9hwI={6Q -zUC(JTX%NL)5Js@^Q#K5>{`fM0WHsO&&<4i6%6Cn)a==S7Xw)bw7)To%DAY=jWMfcB -zXfBoQDd@I&2SCu$L-uKsI4?)4i3{-C`8PfUyZQfSK#_pK#9pUsbO>_u4+h^2PUx%5 -zk|o2hhKNxn>h6RDHjZF_j6bO-Tz;H8^FoLJHgs6HP$dxJi3t5MzXR@tl -zy~#A2`>SFG0OLnzWKQ{Eo@tR3+ZZVc#T5en^lHLq3wE -z>h^gLkfEe9)5^`7w7@8+J=PVaO&eY2MX+8=gYs;&4T8$x8E?ExCsY?uAQMZl*amUw -z6?Dr8D6xURRR|mzqPXpg3_C9FR&JxjmUU&k4C#tWtnEgTFYxVuuO0-XrP94J0a~MN)=0bg?`tA6pEBH -z!6=@BI*Rp1lE?jZJN%5ZX~NP+c34t@$|Rg*41IWZ^rruBgs^hTKWqXWiO1oXXYxZ} -zXeYI|CT=^iB~ZxHN>{j@B;`8hx!RxhDmhWzA($0=`K7J*Wz`}z8F}(cr9|!VJcmak -z#R1txg;vBxXlqpGG>P$QUAEET_Gv^}tU244eL4R;yPh4x-=l9Hk&F?G%PCPmiSQ!l -z5CKk4yMI_ItCfzg)w$d{$+gcR5WtO8w~jy3q`eNm5h~{%?;0N3|$@Q?Py(9rbD(t2|-Uxk1d)on+v6^#y)HK)?60XZ{*-1Ds -zDCMECR*O4las+1_;Yg9WpLO-U02nKX{?DY?0%~*AKL)%|SMwZM)gZ81$Tu1BRhoV4 -zCkoxs0W_QP2dN -z*NYOZ>@j@<^(H_6@0=6{O`j+hV8jQK=l=T4l{!Rs(vXn7ejA}l!Y%E@Q*qChlFzdE -zYkdgqO9nc5d09&>5?=Y?(heK?3H_Jg8BWzP -zI6A?m56p~9WH%6#dWrM4ru&B>1qLrlY{EUNmZcgNn93k$PbF5)-gnn;I;r|cmo|A-fAlBRHd`8_rpaNy1L?Y`5Cba -zkD{nA=7O74bc%k#%vtl|Hs;7D{_lWF!Wbc~7?d6rMR|=e$o -z0=BvGn>gY;Z!-F*!?R_xP6$I+FWXGnReuMGUp?a8!1m|gxPA~ZQB~RdZTVGHOO=z7 -z(+oGbmp+I?zKR4tOn3YF{ZL2-wgt023D!1vRa(+~Lbh7s(aCMirynVOC4Z_kFxpX2 -z(VB=sog`j=bti&qrFx*@n#3a%y8#wHj+3`1&AjqXuRmMJU}QN -z3mFYXw!%rg_ro~(eLrK8)`gad5_i?2i#h+Z<*^a7{aNzdz;3c!(Q|k#epm^qBw|I1 -zGu~Nu2*rlw54Gm(>P-{{S_Y{NjAzI@Krqyfqy{c@?GxHaUrK|Pv8>8FAUN+gkSVMj -zxs@8hbw=Mrz9%)XiHzB>v0NHo0bhu$pMVzR-S!9go3pGCou)+fva}*o^Cdp81JNSF -zEn>j^(`$N0sSGwK+^UK4)a&#rh%O_OE9`;W8+fq#!ikZjuuiZnk0=qaSA*{{v9ANed5!f-k4fKIo!3n)U{>RR_zo7 -z2_B3R!~P1Y&B5DqhXWO%czO4{1B!d0pD`4rqLYnoIBs=o$mRKdxQ@9JHZGF{$ZtMs -z2}APo+88U2S-ISYpJL*Tph6qWNa-#%IzzpXA%MorWMS>f8{%^A79Xl3nSmCZG7+iC -zsz3q3q~pYWQ0M*lrMb`;;$W6`9fLE_h@m+K($4aB-m2Gq`2a!lquG?M(=?ZKmZ$VL -z`+gKF;6>nABj0heei#@9^*caNYy>la7M^CqQe{&HLYV=_em^vp0kDwM&vP0T3?0ax -z@TFD>+x&L-${DMQb5eFPaDTsz4@#9m;u&qbMzs*%6k`69@X>N)p1I9Z<_>C=EcgFd -zn>!civeG6ZW`p2!f&xbfK%ZHoz9=lB?&@68;lI87<;fdDsnjXpW*dG14RRy -zVEeSg$CPuK_5@<=#X>|s+z#8M>>4sBX~oR5M5YiKZpFu&?SD_u-jQrA!((P1p4VCB -zjTF~J4bk4S{ -zB7InxwXYwy9T~{Az6@mOx0UYOuXHqmUnr8^0Sj0z2b$g!QSvJ@l5Oe`Muq?rLG6J& -z6iOp(`MX{*FhziLHK0N^(vmLji4`e1nC*T@NGrkGTm{EOPku+_n|fPgehwF_(UO?C#piUEKT~;~Zxcv@d{hJ*!9e;ItEPj!J-nY@ -zR^HNLlwrcS+;xibF6MQdIosLO%Bt5i_o%Cp$I$i}&69Q>jLv}Q4 -zlL#CL%v}Owh7?nSXFtyeOe?bzVq)MjaMn^*e-@_$M2V)vAGImz;E`2N7}Hb3IVf#x -z8sRf9MkdBKeHj;+m)L{F>_YR5UJdU654^2nCNh!525X$4cTjA@Vz&~a3?)l6ZTp)` -zI%;NWkVLvViy57sIwGm`=#WMv>lo7eN5X0ni2ZtoFE8CDah^ -zI?#ff^I(TioAz#YEIaa9011_3zx73zy`VCQ!VGlHI9_iAH3mh<_q(C>1@0P;2uZoUc*`Y2>wY*`97Tcd -z`rrk;UN0-Gc+N(n!0C`7?XYy(Ud9h%P44B`f(W}`PumDF^K}=!;ASA^lCC6qt>Eo_ -z3rrOAiE=yTH5d$}{l@VV$kNvVyc5L=MYS34{20~7Y9DueP?j;RR7w+zBoE6)t^nw8 -zGSplAxt(W~^6w*M!-7jdl-37FehHxU(6{MESlXTX>%ks8@V`vh-t0T=Wg|-1jeHy~ -z;;)`Wlou8v**z>sDqQ+QpwJ%0<=Y}eB -z#OjtNnX{!4F$v=_2b3j;{U(NKWTwMY9Tf`{RF56h`sOFJ{jMa+bqGDAgS6Fo9JM96 -zg%t%iDr(HQ8H8kmEc)T+0iK?#>nC_s?LuYd;grugqbhiU^a(Zu&`8Wh@&p@wZmI9I?< -zK4%{`2b}<{nDQj8MhIqn(_W!m03sX{GMzE2EBh3YJi^Kj1hW4QaMZ2SxMoz_xxwr! -z1;F6*i}fD%&@Nolo=>#?j#3Ci4TF$xgE@%wXE0!b^O029#7TD~`9KKEJzJwtg<-SY>W}(QLqS)-Bx%$UNf-BVW8AzIJxHi$6|=VZ7@kswn+ARQzm^-{ -z3e7Q`%XlpQPeb2=TQ8`ba-1z>*JM-GHaz -zdaPV`0GR)^Dngp7){}z@3I1@@`=Ab*2nm>VPHRdfqgj!sSrahHv@tlaN}bMFvf&ex -zqh+JFq(#(rp64>c>HQ-NzQ-SlO7zoBuirt@GC@_SY6Ipu<^G -zs-1_5Wkl<#AK*RSnasE51hP9R*5I4qn_sqFPKT;DQ)qHWEtva~B*>MSzL0e*P4J-$ -zSP!RCe$70Qh|RuZG4gP+Ua7U$IZj-pnvIqS6g?n&EE)>K*ox1zArs@5Tg*vP(^8?u -zp4ZOxPFz2!#n>r?q5!{MU)h&s%XA*#MNN4XC -zpf71dS=tfJ2D({yQ-15DZZ4agMjHXGxn#vuaU-LnOdDbkkXiqz6o(6mXk`(Bu{u1` -zOEjS-m#~%7y{Kw6B$)YhV~=c-u2_|mTQW^FToN6_r8;KkwFx;5#SK;`$i^x)MiBgi -zE(Fe7-Irk-U%*E_Xuiry=0)2=!JMSMhnuK#BxqubaFGOC_8xzDB3>pM26_86gg -zZohH=gz3JZqSi_|dfayAq#`Ure3Tu4t|F3jM^GyQ(`QLUio5_MEqCSC@|r8q>O|3+ -z9O~1K2;Wt)fykmV}faiWB2yMXU?d59VVwsd&6HmO4~pI-F-XxqH!5CwN`Y$#a^$Ky3TCSvAy(zBwsQ@D1#ly> -zlZHbwob1K5=rhPLFy>LE$FQyi-B*X$b+QkTSoWuW5ZTlEX7l`HvbC}}X4oblpU+;` -z)y|#+VDN7=pj?IpqLdKcPRXSke}(B75i{vSrA`{rS)wCTHmvxPOkIrf -z!GY}Z1KrGx)^+Y?bCm;+eOX%?u4_Hyn+8So-rZ0n;imL*L1Rs(;zS!OXg26X9$+aE -z2miXbuH31?Jk8pd(Hiy+=wU@ms1mx4v?WgO{QJmIaLEVctMM}+bOFCMQfc(Od{019 -z79@)n#LP*C+ODQIjUFJQd8#-m)EBM+0O(Mw6HzS!g*~`aogckLcO-p(VRQ6CU@^1P -zom^e13^b|Z)KTHPYcYVVoCrH4X2~3(s8nIsY{_Bh_xNBS%d|RmnewuOtB6@sU&IS$ -z7+#hVnF!|2hZR`dZ-YEcRNigfvx1oE(tz&aI*abMJ7^ctu-s2W`0X?r?e`CTkv(!| -z>GdXJc*;B|6S8>KTEzGZ03}~mM3i}8#~xVpe3;iE7~5)HHucx4*x~gDb$SBu8wGdc -zkz_eHs+U;#Pa;|}sYQNWTi<)#@f^>4eYrx~j>tgKwkQ_LN-dW9)S51JJd -za=GcyLKJU8{&Um6tqrNZRdy(9XgID61>8dAMN9piO) -zG&;@&Diy&qgz(_|C5x3G3x1<&_6RlA;IDT;hEd*&ZfrMR{xabAJgp^f^6p>RPdcTy -zohtOcx!SXmjP$7>$;Fa)z?)Fd<#pd)fZu5JpwWEYph?viqVs>ZW*;B2)GqCxC~D;& -zdpEDzKAEhD)t4E7^fVJU;GFN@9yZvB*SHmF2my>J6%Z#!B%Pf3zcm@SXGBq(_kEG5 -z9Pz#su3m-@m<>9Biqw6q;`(iiP*mAkNrnHg4pa4LQL?wuX(-AtO*5>_se`$sW=-H3 -zM?V}Cf3ti}7^arrRUko^JcPjF5s8?tV&+=3&H(92ze4pSNZkpFf8z;%)^A==DYCaU -zLWc_}?f?etNCq)=wNF$7o8Gc96X|GNhs-6pm>i5xw;hqxkaKFw>jA3)=8|4?t&yui -z?U;p0TEWx31bq=t3W4L~FFTUSsm5^T9REXKoc!2%7|y~DT7T2r301`Ut=onIIO(&1C&0=kVhaP-G$ -zqr8efWwm9Kl$nKwNC9JXo?B{MMg`rhvVCE0MS&Et;7aLK;=^{#Js?~Q)>BAemXU7! -znNyy^P@JXwkI|`= -z*3Y2xe0`Z1cbL&)(4!-P#ikvZxr20X)k}!kx-rs|T3>Il+hRw9k8&7*Bq67zqC(_* -zh+I9A*KkXSnORwFN2A$9BQMY5VQnhbNab*)$jnZyXX(A>-52Sggv?dmuSw9c1!YSG -zdB$7{-0qlptJl||sK!-OEc&Z6qZ&aHs`7Cw@QNU2Nq1f2 -z>wgFfzb~R18Zz3EjA{nBc!VNlKx+xp4q>s7IjIEc9{VC3IA(AAB7RRHx`iSwSIe$P -zLp|QO=DJrTT_E(y`Z;$f3RTTtLdwDleV#AO%w&aHFASwkHqp*!;v~;6ds6{&iu+z)yO1lS -zWCfN&$Zm*C&v)VkaG&^4`VcjvogTwqMgz9Rt(}OXO>H}L?NYqp>7^7TGWC9`sj0~U -z{Ze-9jD_>g$6qmoj>V|AekVHEF0xmxz#6Mcg<$H*$(2Jk`0ElCll%vBe_wNx0x0iR -zt@)JLB~op$tclL_baM1`k=9CVzWgb6;lxiGyoUV=*(X&kb#|^MzB@;=vK5KmL;Mxi -z%tDgaXAjXvbFe{!C8wtMp?uY2K8Cu!p62O4#c_W2Ow5}{M;mJ;-$E(|Qyi?8{1AF~ -z)8VDq(E)|X`;j{zIm&gj3~O21g+>)<9aWUhNLUx#fin$`pS@iq`=16A!07sJaz%-eTtix@%R -zwBI7`Ot?C#4Iz_lp1I`<8g(O_e5!|-YR*{0+4?-?MBJIe; -zGLr_p3>ZXztrX6_suaFGv^j)P~A`Ly6!6)PQn?PH+-SV+| -z@uT4~%*-^B^_2U%y@7dVJ)ey%FXg-k%04EQ|biB%`$oXD29&>LsEvQxR)u?{!4d -zpVys2VvTomKtob)6#?PfB5XkswKqZIAuJBZ^NwWPloHtm6-EivdSzo~Xt(_#fyz!! -ziD+$uLhv#OM!yu`E`8jAdI -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 -zu_X8O`vb60xm`Jk>k&+s%5- -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 -z%~5SwQanR0ag044H+!{?_mEvwfA;v$ -zSOU9f>#eVe6T3@RPaL%Cv4z7486oG}>WJOE1Zr`eyk5nz^J*ADiLlvQ@fQ0{rOM*X -z<_GzkmK5zp;&9%Ssn3$08*GJrWHfC7wpzU&AO0ZT8b_XoGn(>#6Pl*yhO;H#%V<=1 -z2WZWb(AR=M--?B~KH;DY&=~qoj9QLrZa&_Udk^)-^Z@3$&Ol#7>oyL5&bThp*z5%) -zxD@K8^VpwX5|2B)13r*_`fZ)+!Yp#6qHHAJb4SoP0+&>Y{ZqtGHj;jxoqZmV))9&w -zsKyLVhtnYZjE#C$*~7Kj?BraE^6)2zh6J&-ud&SoLbwhJWkyJpNKYP47rfeshaoX* -z66>wJ9TXlwM{M(KxwO_?q~j_5Yvr)MOb$K0$$%1q9q#-(@FS0Yj3#zJ&WeU^Oer3` -z0(rJ9dTP(mFI69^jf$tfGLDtZ=$KbKcn-Sj3z)Z{andxONJ-P!$J2V$B#0qEbrtIu -zIXwfs%2H`f!)gVVt8h2qR6h9(Ezp}=V=;LrYTHvX(r-h~Bm?&&?#}j_NIseP79Etiie{J2k*$JL&?-19MmYZTjg~9Xx!u)YiBA9^6hR^fWWH*XieXRRK6K}q8S9!YV%V+iRyAGGDM_cDX|1K`PC_+!efUnOt(g!_6fc(P{K -z{@b7$THJ8PF-*LntTu(dZtLs)u-awdT;Ni79_kiJbp;=97-dwzFXIo`_%vN~3+N7= -z_;rDDt;MHNty8XoR8ivW6sNR-c){8}9CLvJGKP;1o;|)=I=tT_%ahs{7ekcr79Byy -zfx`0OqRIaDQK?^|R=OwP#64y$0J58N=B3z}$m|!Mga)R|m0i9SoqFL0>2fV}m+XlU -zUP6Eh&vu})w*&$=;RZGJYsoEwB<3RNapKJ7Z9j`1$4VLHj>%hI)XV!3T0{XvZ4s^z8$Se -zuQSxlTL|O3+W$~<9cjbA_0H*G&D{(ZSkcG|TxTBsw|O`kuSF1n{`&6k%`R!AX{iyRCwZC(E0=dX1!V#rr;` -zBcufCHFY3Bg6ladO-rZntK;~L%_G&sFifdG(1|n8I*|DLTOMSkh15Fz93P0}wr+W| -zEPer@q-_L1|E1T0m|;8EUiqhd;&X&NZCd0a_#XWH?fX&WTM@Gv+J%Mf1Gxen$8X)T -zIw(a9PHgnekcjB&Oe=FP*0z515^C`h?mu)X9K~@s#-gQXT8w38jh8Gslh<#_c}M)MXJUzW!%|Def!GU3o|(`j -zrMNEl*$L~$#`%VkgC%X@QH)R|KXFk&3m1*BZ(N^}MbE+h)u(NwHU&*Y^^l6s9i|=y -zIza?1W9!Yn$jGGXz6rzM(3YDlX7-H;+w^qrl&_tc5qRB|gJ(~gYwOtk!ly-qZHh^b -zfTJCR6q=u`d;gfCdoA8*=pCeJ{#jJ=Hn69$qX#q+>(UCW^c0ny3BV4Ky0--h9(muQ -zvsl`HT7Yx9B^RWHg}F10o_`0RJpFl&XK$G!i~ZmY30bHRr2UB?5o2P-^5lP1pPt5= -zC0sWB!(NJK(n!nbKu!u531(gsS7ahk9+|bMz9dlG_jctsg~l;k)ub(VUJ*i6zb`|M -z+nY(gIjqDyKTFFIMbsu%`WB1mG6;c$QYm(~cshA3oOXPPVxrFDt>nl#bLQ%)^DU#i -ztg^C{Ddn@061AyP8iiRjmVgIN? -zAqxucwDt?jJ1Ssz62KpeWnH2bNEFBRz!B1FM!Oe2I!FEy)G>2yntDvN=v#}>%TeHD -zbPz^L<$23!9-(gtfh4iWmq>1u6= -zQ8N0)BlxLLaCM9pr^l8o!+h3{Wk|VAZ2E_29n)T}u)U&ViyEDb6H76T3D3mfgQ2f3 -z?f(fN9^c^|JNx4+;V=3vS~O_BNG@@F2C=~K@X&r~*n!R#XtwGdG}5)8;^;3fd1zxE -z4-acc=AE1X-wJMLlI0R*kI<3o%=%{~sD_|EV?!MleFO|OFs4>uXI^fgHMvpW6i~XZ -z+M}qSlD$Aj%HsMYj16Q6VeE0(tU49+H{%>MSEvh1Soc(*NI2Jnpo~MLV+76>Pl>s8 -zFNhg7S(;IF#ZqL+5y|rW$|5T3iY?d*h|#}3lY0 -zGxD-d31K5?Hh229_z7DfoFn({B4mg)&0~4zdBeWIIvkwk@M(HGqpEt=WVe_}8YIaJK -zF8YHgjF+{DvD3}RAc5D}c$cL0Irw_R8|s$Fp)P^@?VX4#n7r_3U+L7}R;{a?#%|^f -zZ5!@ek+&maJB1n=vlCQOPV%F|rV5DO0<3|Ds5mxdAfM(Q*xLXe(Dsl)$RNl*#9*#; -zR2wF$CVAzGS!gMlBnmfeL>_rOcLwLU#DFo55KJ8Mm|CdQQP87Si4elzumJI6zSrR4 -zmYvFoa2N`@rb>Bg+Bb@!bdUkLO*S_@w>|zbGu$f34#Tpm^HqdW$XOXlC{>+5GTifF -zV{wZbNpz?@4KTI)#?H=)SnA(>t@g#)R6yNAsUVa5J8k)!5(*_sNv@5d3E=G4wZ*m= -zQ3_KjVF+~*4wu<^J2&45%3>2)ff`jzL?2cE0Pfs(!)Qs_P$V{88b-&t;}Z(B>QG8t -z1-|=>Vy$6n1zjdntw}K~$_O^v+~JCWX8dh5hc2s<#NOa?aCxm8C>!SXPE+NzQ8*=m -z3w?q9xJIi=i*MQvMiiE#uJtr%%Ea6b_+Dcb=KhG-rsT^jrMU`C{{TVyVDp$2N2pR# -z=yL4FHzwY|D%;->b!H)*PLMw~0=1`uG$03!Z3ag}&#{2>O0Qbdit4*R@aI(ey -z0^rs~-rXz6JqE68V&fFEP*ZN53{-xzdG -zpI71P*O!mwXfAQ~0Dbbh?w{cvz0_+86zv|@5DYwNFk%R4`Hl$LrwgQXmx6S81w}Vd -zb;WfgkgKeTBjq#6NHm*)b}F+eBEf(vb|-6t#&v%hRlW|k>5U-77<)tW?Io}{+-1A#Ji0$^ZD?KN`rk4w&!Sw4G^)?_(^o^Dw~4M(31 -z1L^+&+^C00BG)7YWg7w*+Sv1k{C80Xt-~HGG(+4(7m3-flm7s)CxSU$?K`V$3qZ<^ -z6e?2yIv5$O4k?BbGZFJPcp!6C)1Dw@z9aQqleR@&o77HZrE1#6ilRD43QD{b+(e-A -zU7pLyUPfK-N2}2+!$m<%2s&FWE*+fx+)tovoq^eA)2V3NZz9&KSQ+yMf>`GQKq1bV -ze8vMqO!2`i%DX%8k1)-;Yc=T%(=v{(RY)VuGdXGKcLvPq11xf-&GhQi_rz#Yq2fAJ -zuISfm3r0&=9RC2Ja4u=}iCemKD0q_EjqR0Tsk`WFOYzve7c|BR0)3Ug;pNG?YKy9R -zgQnx^oZ_mb40(n{n>AUXr6Agjqemn$6MssPdjqk?7O``5`#WmBO&|=mqXTt2{qh{w -zJJif9CR7)fZnw98!?mp-vZXNHZU#+2;O0HR43i*983{9A6``Z(oZWMZS>l2TrG;_~ -z$-IZAmP7gFXi_sPF0-L50tK~&t{uOJ-B$jQpg|-9<}>CF1ArV|q!{^*ONrdAV`*4h -z^$JDR+RBaq`HlI>4dJaWsiCGmVobR5RZoPpy;1)Fr0VJVe=nt)xuBAy$5E1@o@#Bw -zv~#$*Qo9W=sE}+s<2S@lwD`ul<-{^Tkx;;Q0JL*C$d6UK>bF)G73o*Gs!bw5)+!J+ -ze=M=2i5!5+C(8DNY_&USlsMc -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 -z2W_;R8f6+}VK^miCgipEJMV=FLPwG&Wj` -z4U#>iB>V%1zRB;hU16I}`VTg@`r$+(5i>fFFK{^E+W-jwiCmL*(yXIndtpLELK0Bi -z-EVKx4=LqjWk}F!8-L{kkJkb32tDB;c8zZx><-u>cSK3b!6cHnAOL%u5djbaR%0E2 -zB(XLk+~D0CqGkxzI@NoNZG0${qE>$lwf3?74h*9ye91o{aQkil0B#5ffw~r#Q3_Fu -z4ThdDLU9RDykr&yPse-*DHthQRlU)NI*2!(WuP{f-EP -zw8tn5A?8L0us6V*6maU}KxI3VelUtogskS~%oq+ufg=D20auw-y)3r3{n)l4T?or4 -zu~I_(-+V37Lm&<;cs;no7))Vew8el3SwOG{YhWNCfU&g}yV!ld-vIPLO0z@0pg7rw -zY$z!~LaxVe&%W4jnGlT$XDUGqea;a`_gIQSu#DL4?|`7B3+o^@k_PuSz=WwNzn1BB -z?A($1U?Z{!T*je)wuh)z%TcAlv5W5j&`z&?BigUI)#M -z@W@Pph|41(;2&T&I3OjYO2H(&+#wtb;DCq=Jm;}V3m=X&yq)-U)uo)At7ZKT?7{Y05@;f0)DA(R?)2D=k>M?^9gU5 -zP_Ddb-?{CJAzKN2=}8f-w(c-#2+}tx9ohQpsaNa -zp%@?|=lbEyAYjN?SKlYR(nKUFVOg^~RP5%LE3JyEt%0;Nuc5AsIi0$k-LtB|QF9~Quf -zSf!BNhwgU4f}RR3 -zL6~VzSH}keN-`VAc4Y^!#Lnu&x>b$I^)lf~M1+Bro7CEE-%apGWDy7?+*umi;z~o+ -z8%wWBI*q@Wo8bWo2F60kWJ_r>7PkNlAP@mUsw_#^;h>{s(HL<0f#>aoxkzNB4y|Um -z7T^MK;u9c@ECrdaeOC6u*&89T2h>PY+WXc3-p`=h0#x~;P1WIrU -zG8R?8m@Yq-`{8Vs%Olfp5HMlRqk#IM-l!f!sH<*0t%48&#F2-jNOb)6!3ZG~nrsTb -z=idNGEK4hprElK&3TGoJ!to$&AnN{@P*Q?ZVnnsiLa)$&u9mmD6fZUzXd5r73ox2iPQ@0e -z?hjqb_ln|CbCft%wKNvERl%mnGWL0o^+k7q2zUUn_5akHJu)|Jc2_IgoF>8&5 -z@Ce~C93v8iu$4CV7*W|M?3-zB{{X^Wu_*4C7|L-OLAg**+Yy9_N_@5lNhp1f7&4HM -zgqBsa#c}>%cEgF;K5(&Q17XGpL?(x<+Sq$3Y_WkJlOY4xU_eNomb$3@aL=k|JECCB -z+=6@ID2NC&Z~3qG!<9KsK_o$n9sPj9a672D6P1o+Ni`tS4&w_Dkb*LnLIAjsvtS4T -zGq^Xg#FUcXe^BEIB=4DI(omkm3KVqaP@O0MD%UuBscwj{Pg0HkaFX8z3(D>WtNxo| -zfPmDR#k7D=-vlQJZ*73v3?>tiv*;sOKLl}vMpRq`q5uW;ZT7-Q`X-4ZwZpNFBxj2?$u)bOHgwNN$^0S-n=U_{615xjrMh+zSjwRdR%*1G&SM -aDG9cf^_kuwX$0VIj!k?hcE)`{EMZ-4|FSSdhitC0MW!2patH -z-TSIu)$6KLeY&ToyZ=qkOw<<@Ih@yIuK@r6j)J@l=tY104;aV*01^m$f%?Tjas|mr -z0cs~G4qpsZOUciY06;?m)`KzHi;d|duj2{;;P(9oBqA2Yz!xW}o2<5*x}$}ghp~$} -z;ES=fgBz!ctOgA~CpRZIcQnSkiI<=@b8Q6+1qC($#*2XlAVWd}AiXff7m)v7{$D#R -zNGSi~e`yH7jf4z%^P<5ou#Q0eAHUrT|4+^#NPq*B|EK@|ssO;tBJ>pi^(7X_!wW>p -z|KGg-)kMqx-&|=g{69f{$&CCzksx8A7xsU5aRY(>t?Hc^-(en9b^rhrFa;S&4Q)C7 -zOiXCWynDuJH<#myUnZ4>DJkk$pjq&k3DgYzHw)9W2_r2;k(E4~kb#kPkESY#ry^Q0 -zzA9yjsEkk-LMIy?JzUAsU@leJ(2RB~vN2TE%(0J6D<&I}{aJM`50o$nrV@&nZL`VQH2` -zI^4OiYC5i-CW~Xn#RmIPT;Em+c^5gI%=tq+`^}5j-PwCyjOSG)K+1PUyCW_xtFlxQ -zoB?i-N-=nbyC6D*k0S2(v{h()`F5@Wx`X+K5il_Ms>AT9G2p3ra3uN`O;C*TOq%MIS{BPC64~A0^Dy#=fvGDN|hKCcIwz%YU|w%5u%`MX2&^`M*Wqxh+A -zXspx_gntW6QGEki#hT&sc->hQEmd3(1XOM}ep`E(^-&dD)r)2$Fov%jB~UE(7nm4t -z-bna7KYFDx4jDM!-0v@bW4E=v*?ib4K}^Sx`}0s-&)V6$Ug*Ir=8vaaWJhr*TP=KO -zH=MQ+G_mWzbE<0oQMr!po1pbWcrGQF+%{yv;_a@=%pv&d(S3TIKz%pfoE^5c0xPt- -zgQO7qHRy5kk2IKy4>w6#$gUPSn`IcvRp^ZgKJ5i{xrfFYhAe-i2tpKueeOxKe&l25 -zS^V@SqjlN)_lNi~RxF9g7`steO()9-gAczgowso1mYq^YAX;x$(M}bzwq74J%84Gp -zzwhskkCE%tI0WzDV`o7sDMp=-88@9D*J5UoM~bFA3InAronMb8=2Znh7O7!yF2P(5q=^yAdgbI*H8`?Q5z -zVL`<%TcEi*(+DbyW{+MpS9wym -zoGs@Exo?S1ov+sz0NHOVA|tYTb&H`g{xQu^yHDSfmfq^xQ9^zzYP>1?aE=P8FaJ@F -z>qs#96F)Vpewk}j_dX`ESUinF&;^G($_ZWhA0h=Iip_u|$zs7q{VQIbt_6+Y)+}L} -zzfbZVZ+MgPk=Q$#(y9h1s|!NdOyKL;%Mkz&=aIY2Phh*mgbJPF$z{_)MMBglJW~FSCjTJa-sCwLIA5w@b~yEY%qh61Ifj1K?8*f0mEHCc6%zz(r>2D -zG&{YHRu@SYY-Hxhkhb6G4SQi>S3K -zc}AjQTc5`Tsh(yG#U6jJw{Aj?#+K)=t|*mOpSPo=j$nU-zMSu+=mL{ -zv5Ekf@%qF{>aO1t>5boRh>dk0Z*8u=YaZY=7Um@?GZ^;(qU7Laxg%eRkl!8c9BXcN -z)KBBibO&={Dub|+G8r*(#qdUil5Mmd2K6HGq1jeE(#?c%+(Ki!S_n~ZiRt2-ZY?~Z -zHba)=>^#H$xG1Y0YBxh@1+MlF0S(LW0{l{KdDqn@^D={9tRMhQJg(%HeTH>K0sB9w -z>3I@WF@)vd@>HlH#aN0QjIKKBAVcC9(ykeA$bi2+T(!T&+>+)erK_nkogP`9N))e~ -zge5s_OjljUJ%BqF33S05##W)5Xhw-cZN%s<&|Y&&B2~$}4qBrMWilC`Gx5V2rt4sc -z1-Ll8`?afl#1olK$AJZfYt-#3AgoOzOVR}5BA*2U2FSRSOT8pp -zq+bpHe4{mGE`J?SiDO;Sv<;XoE?_&LM?qRlL=l*_l1TbABKCA6T+a(X*t -zwN8t~__kBw5j%@cAbB$^8g06IdC!9Q_Xf03&iQL#U#yuIOHVUC%F@|>`lvj1(1XE` -z8Ys4$^siRBh@`mYg4fS8XeLBp8?265oCeA_a0cGd=?0cPoI4R?5_o*f=oVf$ -zL5dMWXIt$f^qAMkemWk`H8Si)lcIF!M+&iXm+E{ -zVexB-74{E}&YdzDYgTK*rDkYBw#)2a|I~E4C`Xr=O1?jL(R4*1mcl9n}v -zx0`#8|F-9v0j-So+pKJrNorS9%~gFm%EX2i!d=P0-%C($?^4aCx2g7Q9o6-NhqGmp -zJv$ICn=+pjztg*t6p)cI?E -zLRTajTCFUnW$&T|an^%0CSM?D8j=Mdqt{F|L2yD(1_kjh@q=?iki}2}bp$8zS>~^5pj)wY5^<2*l$dlwlGJ -z9T~!|yA6QDX?->vtx}KgyGMp0RX*CyMW-myRHgDvY)o -zC4?G%M>j_CD8*fdzT0ky`uLyrtqoYXLJBuduQkQ*)H#h)?aQJ4}cDpZrxxJGgQ;NQBV|G_oN -z^I^y4YST^DxQZaBg4>CJuSFH(wAbu->(eE(gm72m%<;`r -zZ<%-)rlI)Vgc)o`HEFMDKW#GSc_hzoXZy~gX+5*+csAr3e3}~(-gUhtxWO^xY-*aE -z-!^5HE$(x@G_u@tv*C4wDBhHK8cW#cTz&e<8}aF+g-Ao6J%vwUo%22Jy6J&h;2km) -z-Cn}2$+%r@=^5rOpbH}G#^cgIbbnPdFG&c8Ynh>JzyI8BUB+7+(aMWhE{6Kb++9}N -zggqRZq9P*sgmpA=Lx8x#+Xegj$`>U|k89s2f-++=i#lafI}1e({Vv8wsXmBjl!t14 -zMNUF3wTe&jcbq%KOExA$4>jZ_iQjFEVR`@^0T8$K9Ov&5qZjL~AC3~g-QD~?A!Ilw -z{n{*_-)zRV`sI(n9WxPnWkc!rhq=q9ilNV+R{DntNhVZ#Wg$;eQr8S_PByjvl-~uN^Twb-m^7R$?4_P*uU -z2Wx!%=`AI^SeYE-_J$D+%Nz?W^pDeFY8cquQ5KgX+g*XCHA_5B6sPR3krbyr+_}Uz -ziQLoHK&!b?pMaED!>(92DCqL;K`x>SivxYB@24LjS{hoHq>_=RlD@eRDMe&oNXmeF -zMHqgl=c~%&ahGonL>1_%_7BK*he-Ncy(%FPYrCMq;ZCWQxwd%S>5?6JWjwt2T%(UE -ziS|0!lK7;gvfPg--;0hliYR#7+Xrz>=xVx@ll~P8%ySiT{A*8pdx-s(*Q&Gv3-hy* -zkw37)%bbLy!E=CdxU$ML@!yP#_>>8+59@Fw>Z9!!SwEB_{ITU|ZS8qqqUlc-pUh_F -z;`}eY=FaG7%9VT9NVGJjXAo@1U|!OPI{r@_1?460D4G~bpx5=^V=|j$@$E@7=hZ=w -z9qDREJ^Pk7B)Uw<>ctx-tXW%Ndp{#VnU- -zE_z91Lzxz;5g{39nVtjDzj+>m@~xL?8Tso4(&v7IW{Brv9^~R84GLSBM%l7@V!%+F -zR~a&~G#pY$!bm+oRC?&L%@c3s-QZ=S=0mkuOXS$o!9NBJ#x)b!B^TVw#WB+qcWuK_ -zUp?nrH#7RlOIPpo7kqrz+xwICdrH>kgDUPjj2e7dqb=l0<{P*S3hLJT;F)JD0!&MM -z8M?O=ws$#RiK=8Qa^ -z+MAW4jtETktz6LeWl?a$shA!MQdp#I)O+_$eGJm~jKVm}vm2ucQNERPM6 -zuY6QU! -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 -z2MH;pqYHrt<@o)iO2RfaW@O6M -zMxyEvmb{a8l{el(g5mhSkmRGtkDr-_ysX3E6z=!qy5dMd!(Gy3NJAM@OUQ(-wb(qD -zC0vWKp5|b6s6xMf|EU-$gNG?0beuESd)`?w9{Niz&mkAPJSOZC9Vxc$?!MV7Z0)qH -zaPs3sYJ^GrlaJWH6+9`qW3CaqOr5k?wdMmU!_ItuesM{v986S!;W69kq3T3Mbrkgo -zsDK1PqiakAcFHo(H)wLq=VD6?OxNJ6_N8ysly}?aw|X_0_G;u31tE$b1kC8W!Ja52 -zh~i6R0UN7$Qwr&E{9QSeLJ*K(xYA$n98t+l&I~doXQRt2Md8R`+6#ia(`EpPDCl3~ -zS@7u;1oHjb0A8%1B9m8 -jDEQP1VxM(DN6#p^j{G)gSVZLieP$}is>swz83q3zIq$^K - -diff --git a/platforms/android/project/app/build.gradle b/platforms/android/project/app/build.gradle -index 3929a7673..d5f75a6f6 100644 ---- a/platforms/android/project/app/build.gradle -+++ b/platforms/android/project/app/build.gradle -@@ -11,6 +11,31 @@ android { - from '../../../../game/assets' - into 'src/main/assets' - } -+ sourceSets.main { -+ // Another hack to automatically get our icons -+ assets.srcDir '../../../../game/assets/app' -+ def iconDir = file('../../../../game/assets/app/icons/mipmap') -+ def resDir = file('src/main/res') -+ def densityMap = [ -+ 'icon_48x48.png' : 'mipmap-mdpi', -+ 'icon_72x72.png' : 'mipmap-hdpi', -+ 'icon_96x96.png' : 'mipmap-xhdpi', -+ 'icon_144x144.png': 'mipmap-xxhdpi', -+ 'icon_192x192.png': 'mipmap-xxxhdpi' -+ ] -+ -+ densityMap.each { fileName, folderName -> -+ def sourceFile = new File(iconDir, fileName) -+ def targetDir = new File(resDir, folderName) -+ if (sourceFile.exists()) { -+ targetDir.mkdirs() -+ // Copies and renames it to just "icon.png" so AndroidManifest.xml is happy -+ new File(targetDir, 'icon.png').bytes = sourceFile.bytes -+ } else { -+ println "Warning: Could not find ${fileName} in ${iconDir}" -+ } -+ } -+ } - - applicationVariants.all { variant -> - // Ensure the custom task is executed before building the APK -diff --git a/platforms/android/project/app/src/main/AndroidManifest.xml b/platforms/android/project/app/src/main/AndroidManifest.xml -index 513a5bc65..b14d45171 100644 ---- a/platforms/android/project/app/src/main/AndroidManifest.xml -+++ b/platforms/android/project/app/src/main/AndroidManifest.xml -@@ -9,6 +9,7 @@ - android:hasCode="false" - android:theme="@android:style/Theme.NoTitleBar" - android:label="@string/app_name" -+ android:icon="@mipmap/icon" - tools:targetApi="21"> - - -diff --git a/platforms/ios/build-ipa.sh b/platforms/ios/build-ipa.sh -index 2ca216901..b38987894 100755 ---- a/platforms/ios/build-ipa.sh -+++ b/platforms/ios/build-ipa.sh -@@ -37,7 +37,7 @@ cp -a \ - "$platformdir/precompiled"/* \ - "$assetdir" \ - "$apppath" || true --mv "$apppath/assets/icon.png" "$apppath/assets/app/launch"/* "$apppath" -+mv "$apppath/assets/app/icons/icon.png" "$apppath/assets/app/launch"/* "$apppath" - rm -rf "$apppath/assets/app" "$apppath/assets/.gitignore" - cd "$ipadir" - rm -f "../$ipaname" -diff --git a/platforms/macos/projects/Minecraft/Minecraft.xcodeproj/project.pbxproj b/platforms/macos/projects/Minecraft/Minecraft.xcodeproj/project.pbxproj -index 7a625e3e8..d8e42801d 100644 ---- a/platforms/macos/projects/Minecraft/Minecraft.xcodeproj/project.pbxproj -+++ b/platforms/macos/projects/Minecraft/Minecraft.xcodeproj/project.pbxproj -@@ -2359,7 +2359,7 @@ - 849371362EE51E6700081C5A /* Default-Landscape~ipad.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "Default-Landscape~ipad.png"; path = "../../game/assets/app/launch/Default-Landscape~ipad.png"; sourceTree = ""; }; - 849371372EE51E6700081C5A /* Default.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = Default.png; path = ../../game/assets/app/launch/Default.png; sourceTree = ""; }; - 849371382EE51E6700081C5A /* Default@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "Default@2x.png"; path = "../../game/assets/app/launch/Default@2x.png"; sourceTree = ""; }; -- 8493713D2EE51F2A00081C5A /* icon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = icon.png; path = ../../game/assets/icon.png; sourceTree = ""; }; -+ 8493713D2EE51F2A00081C5A /* icon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = icon.png; path = ../../game/assets/app/icons/icon.png; sourceTree = ""; }; - 849488342C9284DA006DB706 /* Lighting.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Lighting.cpp; sourceTree = ""; }; - 849488352C9284DA006DB706 /* Lighting.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Lighting.hpp; sourceTree = ""; }; - 8495E4872AF0905B00A06901 /* AppPlatform_iOS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppPlatform_iOS.h; sourceTree = ""; }; -diff --git a/platforms/sdl/base/AppPlatform_sdl.cpp b/platforms/sdl/base/AppPlatform_sdl.cpp -index 30de0ac72..ecacb41fb 100644 ---- a/platforms/sdl/base/AppPlatform_sdl.cpp -+++ b/platforms/sdl/base/AppPlatform_sdl.cpp -@@ -145,7 +145,7 @@ void AppPlatform_sdl::_setIcon(ImageData& image) - void AppPlatform_sdl::_setDefaultIcon() - { - ImageData data; -- loadImage(data, "assets/icon.png"); -+ loadImage(data, "assets/app/icons/icon.png"); - _setIcon(data); - } - -diff --git a/platforms/sdl/sdl2/CMakeLists.txt b/platforms/sdl/sdl2/CMakeLists.txt -index 6c067ae3b..f575bacfa 100644 ---- a/platforms/sdl/sdl2/CMakeLists.txt -+++ b/platforms/sdl/sdl2/CMakeLists.txt -@@ -96,6 +96,6 @@ if(NINTENDO_SWITCH) - - nx_create_nro(reminecraftpe - NACP reminecraftpe.nacp -- ICON "../../../game/assets/icon.jpg" -+ ICON "../../../game/assets/app/icons/icon.jpg" - ) - endif() -diff --git a/platforms/sdl/sdl2/android/app/build.gradle b/platforms/sdl/sdl2/android/app/build.gradle -index b16082fc3..7d8bb604c 100644 ---- a/platforms/sdl/sdl2/android/app/build.gradle -+++ b/platforms/sdl/sdl2/android/app/build.gradle -@@ -30,6 +30,28 @@ android { - sourceSets.main { - java.srcDir '../../../../../thirdparty/SDL2/src/android-project/app/src/main/java' - assets.srcDir '../../../../../game' -+ // Automatically get our icons -+ def iconDir = file('../../../../../game/assets/app/icons/mipmap') -+ def resDir = file('src/main/res') -+ def densityMap = [ -+ 'icon_48x48.png': 'mipmap-mdpi', -+ 'icon_72x72.png': 'mipmap-hdpi', -+ 'icon_96x96.png': 'mipmap-xhdpi', -+ 'icon_144x144.png': 'mipmap-xxhdpi', -+ 'icon_192x192.png': 'mipmap-xxxhdpi' -+ ] -+ -+ densityMap.each { fileName, folderName -> -+ def sourceFile = new File(iconDir, fileName) -+ def targetDir = new File(resDir, folderName) -+ if (sourceFile.exists()) { -+ targetDir.mkdirs() -+ // Copies and renames it to just "icon.png" so AndroidManifest.xml is happy -+ new File(targetDir, 'icon.png').bytes = sourceFile.bytes -+ } else { -+ println "Warning: Could not find ${fileName} in ${iconDir}" -+ } -+ } - } - externalNativeBuild { - cmake { -diff --git a/platforms/sdl/sdl2/android/app/src/main/AndroidManifest.xml b/platforms/sdl/sdl2/android/app/src/main/AndroidManifest.xml -index f580f1b9e..85550509d 100644 ---- a/platforms/sdl/sdl2/android/app/src/main/AndroidManifest.xml -+++ b/platforms/sdl/sdl2/android/app/src/main/AndroidManifest.xml -@@ -22,8 +22,7 @@ - - - -diff --git a/platforms/sdl/sdl2/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/platforms/sdl/sdl2/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml -deleted file mode 100644 -index da1dcd94e..000000000 ---- a/platforms/sdl/sdl2/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml -+++ /dev/null -@@ -1,31 +0,0 @@ -- -- -- -- -- -- -- -- -- -- -- -\ No newline at end of file -diff --git a/platforms/sdl/sdl2/android/app/src/main/res/drawable/ic_launcher_background.xml b/platforms/sdl/sdl2/android/app/src/main/res/drawable/ic_launcher_background.xml -deleted file mode 100644 -index ca3826a46..000000000 ---- a/platforms/sdl/sdl2/android/app/src/main/res/drawable/ic_launcher_background.xml -+++ /dev/null -@@ -1,74 +0,0 @@ -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -diff --git a/platforms/sdl/sdl2/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/platforms/sdl/sdl2/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml -deleted file mode 100644 -index bbd3e0212..000000000 ---- a/platforms/sdl/sdl2/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml -+++ /dev/null -@@ -1,5 +0,0 @@ -- -- -- -- -- -\ No newline at end of file -diff --git a/platforms/sdl/sdl2/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/platforms/sdl/sdl2/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml -deleted file mode 100644 -index bbd3e0212..000000000 ---- a/platforms/sdl/sdl2/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml -+++ /dev/null -@@ -1,5 +0,0 @@ -- -- -- -- -- -\ No newline at end of file -diff --git a/platforms/sdl/sdl2/android/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/platforms/sdl/sdl2/android/app/src/main/res/mipmap-hdpi/ic_launcher.webp -deleted file mode 100644 -index 3896fbcd7d24a1eaa3f8f706a0ba817fa3b5c429..0000000000000000000000000000000000000000 -GIT binary patch -literal 0 -HcmV?d00001 - -literal 2682 -zcmV-=3WfDjNk&F;3IG6CMM6+kP&iCw3IG5vN5ByfO(<&H22#8IQvQT@V2J4d1eEqR -zS?zp)LwJycv46Bv3pR};IjXyRGvPy6{^&M*2gGqCNmA1ZNsB(`v{cfQU+Onl28qDSY0C)>7F -z$F^;&)INqxwq!OL7V}zO2KS7_axMSzuK@6hzzW9-05iL`iLM -zY}+=rJ#*g|pTF3)ZQHiWDWwlTk_My+X@OaD&9-g7|A!aPpi%gdQ&obrlm;CFi4q|r8mP!27$jJ1 -zfMf5zkajT6urVSDWCoZQnGk3!rV^MaKfzk&Hn@ENH7(&jGRY=ynC{ -zMwSGsuAUtOq=gHxBMFv8PxLiK0d^)4eHa=RX)$Rj;U{ScfCLWAwhRcaVeo(Sg@#e1 -z8Iy+z02YPN$qA4N22`8~!N7$;h*&E;E+Sr<(zP&rt1r4WJfRyVjj0BRf)O|bUxSud0PP2M0 -zT$rO5eiHY8D;$I@8obdL3{ax(xKRx-NaSF1hWnk%*YMA0QuintLN6L5aA**^sRkrh -zE@Zez=06OB1Hd}KgbW7sMc#oYK;RJK+-7M9cr9R4O+I4s$+>nXe|$KRfP?_3P(*@j7iRK3 -z<8LxL4zO)`oWDPod$-Q$#EjnqsAYISZaWZH+EtpxLr1sHas>TMzRct&=ms1fli_~( -z-6MF4hS_?6#cv$Fkn4Bx_eV22mcz5q+)-l%!<9Tfr|&>X*A$Zj|c2P%~EdM$Klxl>eGj!0pRBKIsJ&^H|cg{Q`|Iv=o5EH -z7%C)zquH5jw=q04=b}qP!0acEUP$*(0s#dfG@_CV&C^FlP!OC106Ss7Has*J&O3vh -z2AosFJ-Kml_V-4ODtb{SU?gOxmn*O12^rzQhA87YrEakMxqJ;d2b^1jRj!;iYFVyP -z)UDwPDllfb5u`-vvuoh<_UnLVv;w(f5jgv4~Dh~1b6QCpTC{<+6F5vMExYh{gvw|8J$da^7ut1An8XFZeo?7 -z#m&9==b3LT_dfeQ!rZ1}DZO(`#aP)R(go}v2LWs>!lff8JOA+FK=vAyASWr)iib -zXjeu_9w8_Q5}{oVUGf^L06_)Pu7)w*F$gFigWV5?I$b!$NLp+ZNgj26mN^OvKmdS* -ziXo{WWGp}+DX$#_3Iqd`2`Lc}2T8_Bn*(W*jh67R^UKCj1|Z6SlFGOM1cZtJ+@K%; -zOHIK^Ol6eVNH8SENf=50N=s;%Byn!ja@ySkz$3foH|Ae6(qbHx0KtKZ;0ghN@(wXT -zGE|%pfD_H|G+v{oq}?fMApsb~$r*;wV)}wf4EH7;?=atM{q?hA{6AXrg0ysyY -zLO?JGg+(BtOK3+Bs2~^zDnJ24rBXOEog7c-i#&!YmIx5eyV}fMIB7XbTv4z{oG9Zw -zC_@*5D;NYri#nW82oz3$5{j~kRak(6D|kXENGPZmZcnofz-DT2Cr)~tbdwzcAy5H; -z;6gwQ1&Vqm=NwEa0A#3RlnM%j0LmYV&|$*>O}MLaBqs?%rffTsn1~bAI4QrZ#4Cvb -zmSRAq!KML{+?jLaoHguVB&8svAyCBE4k|+0k#Ry%4GQ7r>z?orXPiOYQiy97(v@$i -zSi)=N$xeVlz*clO{^g7t6eKP{$uh)F9AX6kp~4jbgd&Gt)@BjS(RAbi)d1s)S^|dw -zlmIz*WRB{LI}C@EP&mO@fC@sYl)1&=xJE&te5*XULP0}9pke`dVs@IP|Mi8(c$QA$ -zm6L)ip_2@-0FZDqbJEWQf&(JsoH`UPfPg_YR;9uaNGQq>JBI>DCRGxOCn?UwVdBv->}GDU3#kebvW7DAX;i^PrkS~WSDZ9abcFn{fFdtbNF -zl*yuS&Lz1jcrkG5e%EiE(coQm -z6HQx{0RSMRDY=(GLP#CCB^Xdi$SuNEIm^Q=A%iC;>P}6JjRN%$<`qvg=njTJk#ZJl -z{cSwHwDRT{%8OL%0?OMD`~be+^w@u4^|^aT8XZXr0N8XbZAT!<$SrYSoijOUD*#;a -zG{H>VJ;>0Tl4R^q&_%teI}InyBB2cl6(?umx9RDfkpuX6&9BjOE8`ZepN;gx@XPhC -z_K+LdcdZ>cDd;~m!|B#yNPqz}2fcVLk_w=?&|rNWBmkT`-8@tn2PDV=mUa=(Mq>a5 -zBu7X~FiD=Jp@=kfKfi8X-fYg`^?jN^k0732s_i>#x}gi2w|_4S3jD#_V+((LNYM}T -o^uO<$x?O(j7-x;0C9rKZxjdq3{`v3;pXb8a#jQ0c;QuHA06AE@J^%m! - -diff --git a/platforms/sdl/sdl2/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/platforms/sdl/sdl2/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp -deleted file mode 100644 -index 2ad8e1bbf6cc4118601a0935a9ec93dbfca30f97..0000000000000000000000000000000000000000 -GIT binary patch -literal 0 -HcmV?d00001 - -literal 4342 -zcmVZJ3xp>>UgdF#$3Y6;XtC -zltL>!RNhkj|6io#{s(bE*4=S;XJzJb_m$c5pWpD`XRm`pa(8!^=9R<=-~gK4BHa$4 -z$vPLHT61R;(UYEm8#s-|4ICO-(;sUj&wc}lG?z$QE{|c7NLuvAo#=(kT_bmQ -zcXxMpcXxMphu7VvY435Vu4ZP%nkgm0FN9xK_|k=T;D?oX=9CR=rD&H -zXx!c1-64WZB6oKua$9%DAtalKaNXT~u{oQ%;g$S%uVBydjW*j_-<>;nv1r{j(s)m; -z%eJ-cjHItGt#6`^M&FO5rYo+f7!+qJtg&s|wr$%s4q(}~owm7C&tTdE($N>dR)vnh -zCWMX*42A>&!canJL6}K{#_r@A1wDwcsrtP8^Nb6SO6(%UiCPq)#^gk5`PFNWx|?G# -zc9zo(0D+Q>rYIXuA??vk8oz0fF{Q*92p9ZuY5@{HP^f6VCL2(WiMF+;%;6~_F4Q5KGD#pR)`Cxo& -zaLayt8i3pO)U(e+kRY1tCecF*3709fP@Jn>F~*j`81E{QTwrQhT+6-&KA+JatrB|& -zfDXsEQb?7qT}4x9_2Y@F5KMBRO`%o0VhXA1KzfKadDYzB0Xk8Hw3NScVS+IW( -zR^?q1iWsuT+9Ul -zj1S=%&x0@kQ%KrR6vXwCv0O!=!N355vJWoN0+b|-m9^49+GR?IGDZ+sBw1lZEK}td -zjxJ6hI%S{L-}dXb(dvJ}9B*SOe&pGDJ`c4u3=rzskf??=l8`~oxy%cM`o8sM6*v3@ -z129MV>(z-~LF_s6L%_+l5>ynIZlyR^k2fpw@hnlYv_Eaz!T=@|=?w#GBzj%5AHxzU -z2$B}R?9V$&k(?yVQSPf11Rkb;6BrM8`p9Z(No_4h35d@n1GK?-FJ63#r?~)V*=}vI-6y -z?Jo#oddWs+W9QvOHF`$3#

6!Uuyi-m-cA(XD#oQr5~M)%yr4LnA7T}L!FK8E?0 +zQe+t*LkNjzYBIl)*CAwW+-8l`i>Ozg0NGCBFAg|GDEsS$={3yM+g +zS4qo1*6Wq?|KiL@K{@{}1L{XMH_PS~Oa!)GERq(#7vgd73RPw|v}e0M2Y-z$moX1r;Yw1fKw0zDB|(4n~=O% +zqo1MEmFP4LQwK0Dl?Orm_$%{$nvf9ZF%-o9fGp# +z(;b#pD7xc{`8rO>n3&IFbXymb(!p!|ET@`X`;E-+_0IFA89`XFC00Nl&|iW3Q!_ew_a)Eb|+b +z7<^89+HxYSO3^D&s1i2OysT{#GXIJw8UX=(zN-3V^iozDjj40BYQ^>5PjHv?M-E9A +zW@y}yS1<>=l3tah$lCu15TxVKf-n)lqpC)?IqxGAj#E;7w=vMYJ5t6CDW_Br^XFjQ +zNBT-cyalt5UhM>MACk}w4skvoERt}19^J;k-+xd}b0l>Fq|+TLS;83XGk@CEyxB8{ +zxB;99VDBQTCSHp}M0GmUUgNtdpg +z64fY!>-l-!jRdY)CYkLcn>Hea>66klc$Pdc))ErlqinK@zJ1Z=90BMo0)$jS_7aP1 +z%vF&w-GBPjlV*PJ`tCs|W62s89uJIll +z;+VG0SyTJ=>2PlEFe4yT;dmap=s9BXBfdl~s5Y +zX8l7dFoo^{%VDrbg#f*0I8JFA6hlYU5=-1B)wKAElSmAcSJ2J7Zg(($z?xXM;Y5I_ +z#BOjCi)=_qAakH~>i~geu8D&r2E9w*7=a?7XUlk|&=ok}ql9XU4X&B$@v@Fm4joI} +zcAHe!$TX>>nx?a~dALkZfpD_E2li=VML&rU1r+CyTxgCrbBXMn97i)}W~ohr6%isZ +zPbQva5?E%QW#MR6))BczsJeW{5zs_PkuM^Oh)FCVkX+BCQEfB?7HFA#JsAA9}flbu&*!{ilTuy8pvB)vid|C31nek +z9{7sDDt&uTSC5}x&Hep$OyioWtWhW +zlXYw?R}~6HSu#!2Nm9R=`ANW`o(jT@EcNv1AhqO`xGa26s!&jKR~2?6iRFnU!JeaP +zN|vfs7uu_Zz(HWznrGVbkV2Lc9=n;86n^7+cw`fPZRhZG^r7~rb;ovmCO0Lsa#Mjv +zOHCya1@r+GOEs!{3-)WboVJGNgVcJZEectpRHVAFiRJy-50!iB+yk6K09(O#?gI$W +zBBqzV4`K{0C5!t#UJ}R*1G~x$B^5bzqyM8gZT~Bmr8$1cJE>91(t4<)y!(s*Epz$s +z5V)HgSQP;xS$_KMn%p$;IARno$cSxhzse0I6BwMHJ!+O}RXG**>lQOGYBzpJp-8;5 +z)(Y>rohQWj^4aqR)y?f4U`eH3xbz}G-Lg&|PLyi8ghIl?|H!dzsM-ygz`?l!2DgUm +z6v>&MYZO@rGrgW%Q_3%H=*!DWi$8xh^&cWwR*7dGg9y;5uFb0}l#(YQ?Hu}c;sBk_ +zp8fhsb2NjfnuejDp`-q-Oj~YadbQ+{WX`lyw|jdn0yHl1%wF(rE4aIe$IoE|Xx8MO +z-OT~3mgFNL?c6eT3poF?OoGU5YgHuBwzjqPb#)}vgad6j&;`Ce@D09woTEC<-Pp+C +znG-o(`{Bb9<V|f{>g%S8{9*>{lJ_WRnDEddAYk4I4i+ +zlS-1ZaZhcVP{O(M={x23;x(G~*17_py+$EG)69SU_wK%5e9)9U_&avxq8to!;rJHp +zS3O0ti-I#fGSlE0)$FNIlog)L^66k&_bH?Q|9#RFc;hU}dJk}S-VdKb2+$A#I<>HH +z?sPIMD7t~a!x-k8t!%I{)sV +zy8&!*d0)8lX7lc&J_58U^v2cLt_Jn1^{!48Wat=ISF+Hl8Z6ZnjPvT@Wk`D^QH;l> +zOq1%IM9#?R0CueZ$u00P)^eeo8J08J2}V>NrWm-o;uzk5ZM +ze1@lxhwHvSWBP2#13^?1iv-* +z{kgn&8Q$2c<-^@~^Y@9}AT8Wi6#Rc2gQDPho+ONLiJ`eo)`}KxF +zjqA^MGA-PLB8Cx@3}feggs8U63B2dwPK0FU|YW-+>M{^=9i`i=(WHF +zPy5C@hqxri`}?kQ!2UWXj~j6QFOO$0Q8~PMuam{k&w3}lGj*CYso#0h#zUVoXdX8< +koa~Y?(B0=0aTxZzFThdau02-~hmYOY&ildX&#`$+0ccHYjsO4v + +diff --git a/platforms/sdl/sdl2/android/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/platforms/sdl/sdl2/android/app/src/main/res/mipmap-mdpi/ic_launcher.webp +deleted file mode 100644 +index 9db1cf006e380fa04d7c174d9f2fd224a3c76844..0000000000000000000000000000000000000000 +GIT binary patch +literal 0 +HcmV?d00001 + +literal 2172 +zcmV-?2!r=hNk&F=2mk`6^*ehR&b=1LzHQq!ZQI(mm}~8t +zp3AmvucH;#p*1gIT?a40JoC0M!1g|~?Y+nB7_An+wQZ}mZQE8%rOnBr}n}m(QehxJuZNM`1D=-N%8D^P&aiR7c}twm%d%u{r_dA`z^uh +z5$hWOuzH^LzW{*M0jwVYfECsNfYmds{|f-Do?-o88Aq%So*h(F6x5~8Ububo6Q3RRb5CNSPkb?g|8KqZ)9Nz^-rr|+8Wy9C +zELRR^{Br<68eIW^bQE0y&=5=jfQ|_O&{3oqYO3wHox}G}+;8ITWtS1O0Px{bz+cxb +z$vFmLbRaDzKnDO!Xb1o%KnK7C=m01TbO68vbf;=dCrM*Rj#>PDV|N4+{`arTA~gx0 +z&65b^B?8O3Fel4p0t8S1fB*`B79kdZpa1{?6aXNALdT$!d3OeK3QLy5>@Xdm4g%0f +z76E`p0ssJu0LThj0H6^70E+-XhdvqL&XUdQ-J1V9=6@e_AZU>V06{|lNR~Gs= +zuinqej{^V-3FsI!=97T!oh6$)UCc0{0fJxv04Kl8wKwGt-^jcU)Evc_20#j-1y)D7 +z>`wmhR`xCm0Ho1S!$2dL7J$Xo5~yV?5I{j>0l<7R$G_u>%lO+*qLR+uvFH(0=5^M; +zTD?QK;z9oO(b&I?=@j|0uDOx4!1lG` +zs=8VKbsT;f00Jxkk_0x|(2E;eIztD7h5!e5zUlIZ-XZ}75jLr?B$?&fc60cx!|6`} +zNYO1OG-`U_0KnDKmRXGJo;eMHiwUofKTwx +z@AA{{j6+MZI*|S!wGGrn%~CsTi9|AA)nXkuDP6WQ~KC!Zs2K +z00^vIJTc!bE +zL}SOeZSbecanTK;NLC9Z2yI0a9XbS{NMLkU3J5wwUl-GZ^Zf_ACjr|#OE#ymKgJ$L +z^aUzf)Pl*lGyHLulL8_g02+ZxhXi941{#%&L9$d@l60Y#kq)eJ=qa#!=xS-}N$%+R +z%ap?)OAR5y>Ox4tl8l0tM3K-HNN502D>AYa@QF^zjX2CYG9c(cgcP0CqKK75(Ln_g +zx*))cbWp4D$-t_{^;!Q+PJ%!nMOy?|5(NY;30;tcmJ}UQG}_u^c}Sy{OIxM^614~b +zbf_>H*Wpl3Dy)QINffbUs3ky4p@j%)MLN-zqGkU7zkwY~Hm9_5-k$&fmP7>rf=*zC +zYr=8MDLN~O0vdrtEr1pl1Z|`i7<41a!GWg|PV2O^8p@i?z=*y;g~$Q`(nuDplB?vn +zaaK{Vv}Dj(3c^a#1z{-?S|p0plGXiMP_@(l^odVL(7gBDAG?qCeV)?d*cn!gHe{=8 +zHB8{+CFFbh@vMp@?*geliU}Q^{=FU-jwW6&8K0eDMR5 +zkL>oUvMYA!5Gj>Xsipvcfd&8=OmgMO-|(+WEt6U7Q6Z=vHM*@xx1{Jg=*w7@T!dfi +z;jPhI=kWk_p`UwF;?sD(?)Q#nCjP0vofzqCW`0B+TI1gUz>u;8fB|8JA%Dl1C$rci +zZ57fEYLziM3TUudDK6Chvi1DxtaA)K`<^8LjDMqDyKk&6cQ?CDpS#c(m`(sN{`((w +z>}hHM*e=050P6<)Z~u#&hI*uv(Ju0z8;6dlzWIk7y|0@Wy!UfY5*0STbog&AJ~0Bz +yHa7qOrW@FD2Jis5nE@9-`_do!|KQD^8&ud5^_73TQh!)t!zcehE773Bmm>gPn);Lg + +diff --git a/platforms/sdl/sdl2/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/platforms/sdl/sdl2/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp +deleted file mode 100644 +index ab7b6d59c2fad6caa5ffb72437bb3db0eaabea41..0000000000000000000000000000000000000000 +GIT binary patch +literal 0 +HcmV?d00001 + +literal 2924 +zcmV-y3zPIxNk&Fw3jhFDMM6+kP&iCi3jhEwFTe{B717rA&yXbLC9E!jH?SArJ>u~m +z?lU*Awr$&JcTXGT%3_<98Ie^zw(W_`tggC)7#yu_dw0}Wdz?MnXKU4-+O}=m+iQ)@ +zT${D_&deT~eM0TlwliY&^iTGiCK`nA8z*n9v1{A5ZQHi(_8jT9tr?Q^&7;q2%wMJ= +zBEK&p!|BYnZH{e&&TU)A_KdM@JE?4RwryLT%^zSpwQaM1-)E%OXXClgH5s^V+sM9- +z^fG*__XnIJYfgZ|niKP4V|l$?DqjPV;KX*aW^}hm8ucZdO!0hP)iUW}qa-eGmHok6 +z90|g6cF!P}wufvnAC!xuv?QJY1#?uDf1@kxmtR%{GUkAYPc}JbTl*%PV;moLRT+WL +zrs?!9&7^qEjM8 +zrHLvfK?^K^#N#VOAh4iC9$z^`@j0krX-6j(oHn#nj43MVO;gDmno2-Spuoe#q1_~KYk?Bt{NI}YXR4_FO{S&~4wguy7&l#eqMmi*)UubJDZFA+N3J0vaeBFYa31@1YYo-T#ASboYasBJ +z%Uyq|!1M()n|mZVjY=FmQ|8AR8#PE~sSMc!tM +z?}GejDou&yT&QnW;LTHUG{O^UUx`+X7)PN{)k$R`8XH&B%#4 +z+cOpSj5p+SC<;(u0eNos-Oa>;h#A#5kM;Kx~tiDpeiB1e1k#dkKbk5z%TO+CrHq*=Fmfatr%H| +zbgIweFW;?&ac)vIG4aJ$D+Z%Wl|TKEX%nk_{O&NmK{KgnMSPoX(RNFWJyX+$;)|d8 +z09B^+nHxw|)qnq#{P111|NgV)dvBR{Use0}4~i>&?g>z1_=uIoFLTXa=vW)ev_d;8 +zC^&xwE6uR~%rcEy^sy^eS~UQ1`_AMJti%K7*aG<$uQ4$hKBiy1+6ChUhI8{Ifq&zH +zt`sUfe23LUU3tc5=AxvF&sahA|Nn!hu1WfXJeQtg3zUBLQ3OI%rNBiL!tWEW+-rkz +zu5G)u5_HP#3Pc`nzWL_omld45tlF42b$It6yOaN4fwyPIVMo{AMR8vAaNH7fO3Mdyk4%S +ze-c)uQB;~72v9+1*@LsxkyBr(Nl`rGps6$^lygfChK)TpPsOv$TkV?2<-c*>bE?W{ +z(#U%}@fuIOrp3@6gzNZM>Q +zl{H)PMoZP}k>iOvi`03I%%W_iqp8zKr4kaMs49y-kplaD-1WPC*d3MtsH#Kwi2EHS +zvYAjzS*~7NzKC2$Bezb=Ee*bPPXdp`^J+A5H)=kiutU;KMtrS9S^^^dpft~2Y^8INw%`>3J(j|K{ +zt^pLZH^p;GVz_iV9@A$Y!;wd`;n`?-HEQ1VTJBEAuiNv@Bfl*2PlLe33vM(*8jX<1 +z2@RdF(2@k!{fc%+?imWtP!dDs8l>;&8V#*k(U~e4b(u6)WuCc98wr3Cf!zm&eg3Xl +ztX-8cm@t^K3oUWLSd|Me1e&BsBcC}-yr<8+`UCIY;3MC5$8R(V7*7IPt-xk0sM!dq +z*TQ-|Nw+7-BS}${<~4a1Dcc?OaHL8CbsUhfr-?jW78$!egKrr^mqmC+P^--i`SL?c +z&PF2;02P-h+D#@pbq9B!Nn$gKB=BstF87Q*@6hoHE#ExyFCstB@b`@V{X=3eGD#2nXy!R92S4EN!`M028Ym_J8hfVk5@j1q +zjRjrKg647!8#0Ch71YpFDQI*lFmQtMD5R(b`&KA1!dy+_YO>gq#evMx$YoPYfL2oDi8o|R%o`D} +z)90%MK-&hBAM8r3HZ}nP&~m=vbTC3-|B9fLB!DRo1d-U36wev0io}%ILP}z=g><$` +z`q1T#0#61>kQ(yendUAXdL+e$P7ZSo%BPRB{b+iSqdy$UV8rk|HKnK|9OApQSklX- +ztV=KcPeqJ&q=Jc5Fi1>!qbXCkZ|vuuh7V)!KAxFgXAPV(os+NGFHZo}A^`U3 +zB8_G7h=_gqp#QjuN+P1lLatIM5rx{;LAp9L5{6bT>EzN+YS-}V^kq{oS=&tjbiVOq +zC$@2`yV>Sk``w#`rjJ*rSiQY<#7j3L34o3{*$d2GFE@DzMc58LClCPT +z2!QH2kF*@()9>!X-FQBD_)E`+9CG*3Eg#7_xUKLUwv$f5B|0cS`I`OmZLC)_DaSr-5R!HRAG + +diff --git a/platforms/sdl/sdl2/android/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/platforms/sdl/sdl2/android/app/src/main/res/mipmap-xhdpi/ic_launcher.webp +deleted file mode 100644 +index f17e96f9144989d2bfa5009d02c45c96434d6c6e..0000000000000000000000000000000000000000 +GIT binary patch +literal 0 +HcmV?d00001 + +literal 4010 +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 +z1rP)Pyah=V1QrSr6eFYN5Mzod +zN>EOmAxL|>f$Fn9((d4Aiy|BKxtV-Twigg6l&iH(SHk(9F~IOyQf^WTgNR7?xSB>f +zf*8O=UV&iF*#HsO?l}M)MwZ}ao5CQF0MZu{1_Fc;R{%0+AZbZ!Ud07)C|OFlQ8_nR +zCIndl0D@LtDH8c!Ae~dtH76LAUhEJq5tFk32Tm<+giA^!Kq|x(K)NFiAnTm%G^B*v +zP6SB=HhQ47_23hC|8K?H^XKzQOlpp8#z+TU$7F@Ip+u3<-QMm93HNdaL;$xf5P`J1 +z4|bCsEGDy6(bj`!s~2Xxvpz>c&5U+?+XF8^4a=}D3`77C6aes*7T{+KK*VhWG74Hd +z%-8S5H$N?&S3&;50}mCL^wQ)1*)_7(G=qjl>A(vxhSkUwC5kky&6zE_`Or;90%^Gt +z6)5jAPQ0~Q}4_z*^Sb499f&cor> +z`7i$T`^_x~5L0_?Z$=Bc=h+|o?%uGx1ONc-;soHA!?fT50MM2I_yG(NP(X9DG|SXE +zg5O}+=l>KCpax@Z$GB0`h(!?*z#!QJ4}ezy@L$;gOW+&YU9^#T5t0|ZDQ +Q4*%~w3_$f((_e}J03oe7G5`Po + +diff --git a/platforms/sdl/sdl2/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/platforms/sdl/sdl2/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp +deleted file mode 100644 +index dfe0aa8d77bee0bd8b8a1bb73beec9082a18443f..0000000000000000000000000000000000000000 +GIT binary patch +literal 0 +HcmV?d00001 + +literal 6554 +zcmV;L8D-{DNk&GJ82|uRMM6+kP&iD582|t;U%(d-2?uQ>Nz$ci{}g}32O^^X6981@ +z0C+129tYI1F^|N}O=nC}B}sG^4mM>0NM@os^zhi`-<8Ahr{y?8bS-d!@*6DNMz-xV +z2T{tmwX+T=pzYQk~bMv|oG9*qKLW}jE*AJvB9Hnwe5S>NE!9o%9k`XNLArMQ%! +zLI0BwB1Ad}f8;L%gb-qHo#;f!MaC$ABo^C=& +zXCME`QKP{rf)MqD{NBQI{|ULCix>qqQIMF0#)OcD(0Q?!q8?FPUrE9OTWG}j#g!WQ +zNM8ddy;%rkV#1Z*Ml^Ee{oEes%oU5y1X;MEAjCM~io{8UQAZ(!5aNTAzFhTOJ#UO5 +z)w_!nW;fr1$&(R7u+Ltha01wsBYNs=T33rwz +zrXfg@8@C~hGHgar`+}V>_}aE@S(|O!)@?sR2o0&bp6;GrZuGp|T?c*0-Cf3f%w@>k +zonCHrcW+#1NeBV9ljV +z$}Qg)Tn~T>YHI44^wcip819n%-$M)ACPR|-M?_Zj*lUkxj&0kvZQDL~wryRlZQHhu +zVRuG^14ojaX8RJk0h{~+f`KPSCRZtl&64Z9Qh`Y?{~mCwopcnHZXulodXJ( +zSIg7zyehIuqI*9lM!seq#|BChvQK!;hF`e#l#{InMApc*OaCm`v;D8GhDAQqc^MJ_ +zIEZj{SeET=m;O^g)<^ +z%mH0N%0L$aDZm5_$iiuX>~j`myVSBFy|Y%T=EV-fl7aExRsmV^mgW{k8Ex-z)r&}B2Fi!wB +zvrOds14j+bCc*MJpjCIL`F6x7l>hsb5+pVxza +z2dGv+NmQ3Ak^0CT>kj~W(V|qHmr|H1c$sYB94cK;Fj4C%hm0%YU)?}o4qT^zx^e;m +z0wAT)tkeajL@laS`CZ9nD^B^V8(XtO1}0)@K!$46VI2i>5-mX)lCX5B!e`1rPy=wH +z4(ccZI#CiyoQueehoeCEJ_jdPud}4oVeV0_PkLBKuhK|-xCDmLuLDNvL{LYn5QJE2 +z&Yo3}I-ojrfAS=Z=D~>l-nj5C0nmMN3Xr55;h1rlqk>Ubh*3jzYq?Y*TR59W=yseR +zonN^-X6yG_-Bmf2`d>zWFcBgV1EMrEgm8e@8l!U-REyz<_PKz^7aVqugh>7a03=V0 +zo-=(j<(xBhD^j-ujxvJL!x{lYSpds1Isro!MI3A}0fV?XwIKoq1TkFSJ$C7W4DL9E +zF>+Re?!odnZ>)7&e8xVb@1Chw?cCfZ_XHqWkkTdv3^p|4p6Xu$hK{u<^9dMO!|D`~ +z1PqAKeEzR-`2SOni~TU~?_9Uun$>p;nsEZEgW6^fLFHDYtX5P45~2_jWD!C*%?QWf +zPy(uml!=*kBVA_#X%_f)s%sBNHi%(ej!J#4Q5ai++|KFzaT%{;&Fl?Fn;t$;w42*(5vQqd;E+{$ZTy5r?4V5er2 +z6t$7&MBA?&-w@OSIz}=%VW88j`g+x~_bz+&xgsXndgFE51DxPt1+)qwV2%~6Q6s?& +z`krr3PmO^7X>~uNSfQU{kp9W1m!h1IV;4lDdWQrQDE9IdKP<7W=#VNLKx7H +zQ?{VkdrUr=sIZ9U9e}v*$?N(}zrJpUy3#r(6qBT=RQZ_1VG!`pfAVtEEBDgIQ5 +zq#ff=z!S-dNsK38nfZM>yf +zuspu%>m3TMJ?@s|a+L~G7n3Ae!1_o8!2~=cPad9O^C>b?{Khk|4m+vh33!Z-h)5)! +zAP+NJCzX|hx9mSpz@uzNENl-*l16NZ@Ddicu9_XG^|)989nB9gpx-@rU!ghd`t(7_ +z8=DZ;A`S>dk?~UeISXA`=$h44kis>v7{QSG2zW?5Sm5EdXD>5KvjS=iz!jrQ9A$F%_}idC +zFjXw{&BdjUqhA>vnPwg}HU!ORcvAWI`O=&yx@wd2Bost|oKRPvQ#!gl6BqdzUFPbt +zx9e-(NPsDVy>si#mn{4k9h)LM%|H;c%rOXr8Lj|0^5K@MP_6`+2(Ulh`cl_`ayq5L +zF2#u%U-~dc?q7PFYlCMXN-u>hG1kEN!`_fW;;AgkJdQH(BxmPm%+jRnKg#$hrwzfV +za05ZuPWtryQy;*;chbbU4r?wwM^^svP~4EcaFvl>7Jx!7^&sG<1z#@B%gxPiHYI=w +zre7&}ku221nqi6Yz@Cs677!2>a;V}Fo36-UeC~foA33e8Ag_>XJH8o{<%tN>)C+|e +zLXR8y?=&HVbI*`mht^XL89>31GZa9@++V1C)%{EHo4ZH7Ns`LMfVOgu1cGBZ +z0)CqJ_H~bM=SSq;*)QiuX3#L~OH&jnzHanZ{9+2QL(q&P7Wa&_>p1L{N5BK3K%4%Gc_E-8yqLeNSy=Jm}pc$98*FKamL8`p=Z8x +zFm;HN2N&6YI}a|MW}g28))1gMO#?uYIwz?hG)DtCxMZkpJh_nG*9t{^4=KvX*kFk8 +z4JMGqUi3>t0!$y|a`5f3v6ae>Zy-)taIBdCRG>PqB($go0CM?$z=?c^Jb;}-AMJzf +z1Qnk{6qY0IA&y6|iCmaS9u7_>4!>n>EoFW@!84W*aS6Ojpr{O>sp?)p0yy#=1`Gi5 +zO$eh$>PhvWuERSZy>-?Cr!=CN0F^bKAWP-^Nb-`w2?cTXU$ML*MNT||(NjeeSW^wh +z4`N!XZk;1f2z{540VOiTr%vt`nxMp;EsI>7o|&o;LExl<6g&6w)3R@*P7{q0n4q|I +zGDY9oVZ%$aphj};#Q#M?3h3sP$yyeBXV~Q6Bx7yUj87bKb3DaoK*^s!wp&7y1SrHt +z3OeJ;bkHzM1Hg$!70L3N*}F#*j35mwsZWwjGAE*l8z-Z4engBBPq6bNZ!bFUEIs3o +z5NH_VZ+yImPxf(W#Wx&$%+^kv=EsC6l2Io)a{?$s$c-`ymU@tv!4t&y1%G~NL~I8L +z&^R8v;Dv3NB%m8ohvE0PY{3o-l^N$0TA$g5hv=7QZp5R}C&)UKTci<{s0$;Tm_eQR +zg0|(b`o$6Ipdtj1dvwCmOABfb9J!#rB{%d4TONtczPQ%jtl69uAdwKer!Z#ykQbnzOW5nkr&E* +z-`UK0IUbg#voWvaau=|-SZlMSof3F4qh68em;4AUt%99j2D?6Mh=OHT1$RsbbOjvj +zd<%}uG@Q|3#Zp*4Js`li+=C+dWh49Ln6k#S)85uzv@*Ik1Vo}42mBvFTZY|Y&o}-iFRIgG#(9s69)hiz*e!CK!9OGem%v>2Wz=Ki!A)uPk!tqy<|m_mz%&W +zfTB)4lA6lS{hegFl}!hX{K$QG^G7yal$TC=ql^Y-0ty^gi4g_M2N5szM)$nCeuM8L!ndIV+O +zd4KjV8#qE?K!_5EZC;-8XDVx;U<$wpn8-@ZpJfJ77Km>#^{^pCh2(Db{BsLv0}OCA7$8;lqEv +z0c)I^-C?#GM_*D!MmwZBF;fn)IQmIy5gF&71h5v8kr-|!s7aW9h3vdetc{A4Sc8Zt +zb|#$+h)SwvOxgVNVwhaRrXOSOdeojd_Oty2JA26_iqN42OSwh)fmuNuDifWPK#PP} +z*FX}0?jI$NpIu6BCI_fulad-rM;wAmDrm=yzYKOaJ#)?V{#RZc^Za8OGu#O-K!GI7U`~-+-3DFwRsr#Zi +zHB_H@yJ(X>dnFD)X +zq7WRdmTAko-hoJB;^$mSRw8;WXV0+UOB;8t*>z_5(oCQ4XesHq%F6eLVdV=Y*H0d;MRv +z&giJ2z +zrDFW_%M+PW*Qtdy$ctsBBKk`86(&L!Iqg^A(kZ9`7YJsDN_uDQxR+ZSzr=^3XZk>< +zPza&urZZBY=$1`?&s;vti-(Oldi&uTCf#g8W-4ayKJG4%&IXOxe+Vi74W&?l1ECNXbdL8BplH+9 +z&k~`iQ1Fchl13*aUr+q=vT8U#9J;%fF@!J+V=0%xA+jM3aiX6wwt?)tZd#uxX&?ce +z95D_Wa7>egZhGfm69c0YpC%DDGlkayptWP2zH8>C%{OBFwbkP0fC92;+V{+s1JQA^ +zMbuV9-ChN10MX03N}A1=B?iX~M>PD?LG^Ca;iPa)zH~*BqhlNT)@85$^}(`FoEfF~5yk=)&s=vy$|3;t +zxUj$v`M+f+i(ek$JlaeJ3)2uTW@9Lqz#%gFk6TaLYUr#HfkaY20Zq!&Am$+H3b2Rz +z66p0P4JE~&zX5Wf+X;|&ll!a&0MMcVi*woj*N?~;f%|!D19b}4NQiEFA0Gm&qSFM| +zF`xeMkxqh(C-MM2H!RY_W71no6|?0CM}bv)aUBCHWChv6Ap{KS&2Tr>z%WWgc)L*O +zvqV*S6EG*{kn7O(@QC!*iNEuFx=8yi1EkNggF@2B2vCJx_>{=gF>t0u(oo&O>84IU +zw;P=2Fg-(sPfjPjo)G6lC(Pj3WRh+GWKfccs~MUbNEjEG3!h(Cz<&%T{o3$uhrlp; +z25%Q>av0GygA$fY)5IVWcbNb!?Vk|ZixmdKbo6xuEtIL^E`)wrM@CHjAcCpMpVVzi +zowu75AY3e_0cfH7)Y}KUym;_O;?renn)hQekn9SOgrbTY_YCKl$Pmj>HWe@-hA2*_ +z3R$M?@)1fR5td;s{#cD?EP12Q@OR=)vWiym02hxGKD$}atgZpQ< +zerR}%021&ZKmYQn&u<^i3E>OcELaHTixhcdP1u8bS&cJtNxh;_j53GGTQoYuE=$Gu +zQ~V$%CZI%#N`y6%7h}+XR%2H`VB%u6>oUCcSuwwWhXjz&R0JTp4?jTyz%RV{gGYYw +z&C{PQ{W`}tx0wb|73YcAL@En$;sQ!!gYy7CJ^JMICz)(K1Bku +z#M7_9{$sq!MRaYEU6`L1^?z&6KhI=>1b}>U1PulGgVkWMKmTh7Hl66cOd@n +zvL#UsmK7&CqGSpAfW?QkC#8T0%!SZpuprWfKF&m>z$gWcQ#_GR^FgM8L{)o$nAPTp +zaTEjG>y25TbaH9H5E93VJ_HriDHW%(~MLow|@cltM9~$r21Q4CnO$cJxbdXx&l@mQy +zWl0$9o)|;5xc3{fw1dp=B(NkHm_Um)3~`>ANBm(l>5_jig5eZsz<~|aQIWdSb_$(j +z!D=m*c%d2}i2o@|gx8n;8;zns+*{QC@8hZMRhGpF17cvRwj$fXfCbI~IOAt>{VXty +zRDr;&X&fmQ%Xd-V94hQB8%6t*^<2qb)K{y329@mm5c|w9Ltp*PtGBPlXM3D& +z$5m_;HUa~!*bp?C5&)epfdQp+hS4*BR}7wriy~0?7(qvoZAV=Hf6ti;G|xzumX7~t +z0j*X`6=b!xuhOPLqJ>K?L{J67gYo9|IB9;&+f+5@#82%k;5t#$K +z2m;;4K|5$Q#0`RVC`QoLv9)8ghW)w9*krLd1gKk_%5)A_+W;-?CjdTyWFd-mw2C_V +zQXtcMe06`>qT!qh4ZitJ6aS4G4 +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 +zd_{j8-ao+Nf4TFsgm%Cdq`J!4UvtY#&MA+tA9 +zy>!~UF^8YZ^-C=OPi!CvolI^ZUm{;+e2i>qg}JC1 +zEH~EtGF1rxp_{``LWb4j&9BEQz?NirDHjg`_HG#We!~3iH9p3{otc~*`O;9%jgpA3N9DR|=$!vsoa7*UrIsR?t7oi4hLqao8d?<&XWkYZF +z1d|ikET?}M=Z^$zLGndzeV(0@Y>ucRV1AC{-wrVN6#H}dsg?DzQRUDv}2t*8|ku;G|hRpzdX0n=F_tQ%OD95?? +zN9LCS!%W`IGoK^t2K1Emkhgx3^%yY3gl3-kEO`&mTV`wH-XEx{fZ>IVCvM8*5TM5# +zKReD|UIYAunC#=$-J}epR5-5J0v2>sTFs*3tfE4aLCu@L&SDKPv@EyAe2tWWlvf{J +zfBFg4G~j*JSZ0jU=%QT6pBp@SV1Y-p45hwPbVI{lH2q +zZk-tKymj1pE&z-oA4+s`ez4^4SPqd8JbO#__LGzlJoj-Hdo!qI=OmSpR)j|8J3Rl} +zJbX3dVtlO1QJzlDTk@I6%YQm7cX{f1-t)nwENROAk=(qFshkGG*@58T?onmKaZLK7 +zJCq1`j40>1k3%rC&Jq@YjYo|GfpRd?4g%^$P&-EZv1(QS1`Hqv@5$bYsGC`r^Y);V +z0Ou4DkWOmQ(aPX;BR4i1c|U67NJjltRbxF20J{fQ)(sd!#!I~Nt2}xEL^xc@fn>cG +zq%8)GSDxOpw7FQY&Lt~1t= +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 + +diff --git a/platforms/sdl/sdl2/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/platforms/sdl/sdl2/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp +deleted file mode 100644 +index 1e79b1b04bc2e2bf3a1c0a09f0daaf5bcecf045e..0000000000000000000000000000000000000000 +GIT binary patch +literal 0 +HcmV?d00001 + +literal 9466 +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 +zVD;`_L_+m?mrzc#%Q6FsfQ3s_Rs${ +zQ7U%IhE$L$LWX4A2FT-~JIF?5PZw7rNUN2kB9mGgClf^9t}Dk+sSxQuQscKb5x7I!Q6@^K +zJ4d*3zT8=SShhtT^IIQLBB-83{c@r-7(|_-C}dDuPU}9dCJ3|m+Lc03$^N{{2owZ3 +zgA8QiR!Fe6cqrj&0Blq$QZA)_^4sJ&k^YbrAsyEBZE}e+X&$0IKAq`wmqQHD?xWos +zY)oE{3{U0oxv${oMsR6K8J!Lq=!+{H9!n#x+x_4hB?===amb@ONHDpVPWX_{xu7dHXMy +z%7C2f+;6=tk0X?zGm@zX&f^+BmE!43_S>&aUYSRI{NXX1K2=YrGa6jC^|3b$s!mrF +zPL-!ZDW(vkuiLw5punjDHj)yCB2Y`ceY$-y8iT*&Mnj5;M)5sBacf_?PN +zQ(9od%4b9GcA9Ec_SS&742D~ZW$QI(>WWAo7;NbrQ*VNIIish!1cF+!`|VoU^4X{T +zOjqVx#4kQ)xo_{^c2QlngGLz$w_3sWTd7rpf!Bl;!K!z@gl{=wccHJ7t#_<0d&R=VB=D1+B2WHe9*Z6}2a_1AY20Ov*C0int +zZ?OYEg4b}|V|#^QJ$Z3U- +zF%)GWlo5W`7|507uoXQjm@t~Xyv?WohJ1^KtuvCL*8g@k8~=s!Or;x~exxOY1li$+ +zoZGIWamJj7RVr2t8kPmI1r4JLpI{^;kH!&BSX&S=)mBj3vZE{@ar`<$V4A($DLr+C +zC~byGgOy1Il2F;ntc>o$Tcnd=6^kfgGTm9)<0Q&(NdJRulmk1wmePY}L9J$cf;oQO +znB!btn8G^>fu`EpeIp~3 +z>u?_~IL@!JA*?Dj=({E^MCHIL*AK%b$UFVQ6oD#Lt0HsleYl`ph0VDj-13BbD+pC^ +zeJU%}+5Hq)^%C-Qr|FaB$3jxE)A(H#hzrh>kkh>irpnrsM5#EtpG5k>>Q`rqzHeDt +zl*~kbtSqpjtfx{8X=q;hGj4l;>WRTHRA-cMnZ8h(1WVN9&1qR|Vj!jLwAb0sICMVk;mP`ADsHni8mauMW5JtPD$! +z^46ZbvH8neJHH&Wc;EoXY&-9L@4V}k5AXk&nGP69xI*(_`WwTOO@<0s(%DDOcQ6du +z@`JWmYCa6(xMp7e`6oev3)H;iiRt^-gFha&Z4TWS?G4~w`p&1FXJW@Rn@|>gNttb0 +zz@X)iKPi%V2U3C^zqN%UL`m$7P9|^u`qPPBfrHa~n|x45O{{ +zrmsGo(g!>+QJi)H^jYmSUjNb$_pb!I&g1#dzA&0@nq&ToQ%J8)LBKHaNg;8*!*M*V +zSkhAF7(nmU9yrYdaG%i8k;l$EzCpS15K^9g_d~m&&(aPDZ~W%S9cV8eQsKEze|f}Z +zaOjivou!etIUG&&EHzBj-cA{Fc|h|WUbz2%CBssH20iok{B^W-c*()La?s|}N>PjE +zNjJ_j9|-!c&mFd9jyW)$G>u}OaQAuaO<^^m$k1aqLr$CYQNg3>{g*Evk%Ud?rQ +zHb2vKc?A*FG@H?T&HjFRkZ7cx;tVNMjbt7D*FzmWpOY$;2t?sENBxALEHHb3B6?#Q +z7U)5q;Me69^yORyu`d6HNLo6^1T^a+B0~y5$Sk*kA*<6HSl5(AFb|L+V?C0zX^lS9 +zO$cwwmxvvOh1sVyG!!V9QO4k4Bkdh>kSbaXP^;m~7_y48>^yn)tVS?|OeYD7qKs6^ +z9vnv#CYwWTr@C(Fp;N>XMW2nq*7SCgfbg28&R +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 +zM_$y@NU5hQQxp2NGOU7 +zYZMJ(nj}!j9B3ofFLFkLG_weaDi$JP-Qg|@E2Oj>%a^+z8@FDfW&6WdMhnyK;n33P +zwh{<&rY}k+$=u^E*R*le%G_u~@rT)1t%uQ|MskMQ>8RCJRuKvb2Y|Rm(8`lSqi7P0 +zlADW=dC(!?F(Mp6M{rH31CExiCCV8eO#(d@Buu&8WlfW1)=W&^PgZM%9JX?jOC^if +z(3&RVN^oU1T4D%5q$uTKhXyu7tYoNUp-ayJjD@C8gIS!q27D#SVx^}Q7n6!=NYYS{ +zAQx6kQ!SLy97PwvYXm^Hhff!Aik^1maI+k=$}w>YwDD+xMSgSzoh#^u*3gTO?qtqu +zvu#Di58H|`gLI9gl_e4cmadnLmL5X*2858R_iH8s+nMgc>P8ob$zU+{^)|1f;!jG2vh~9zdcv$1Bn0w0SpRh!0ZhS4}JTgx075VO;;k# +zhK{rwmeedibYiz^NmaK`P?ime`V~5x?@32FiHML8LXe&28qN~F@XsW;@$q`3HP5Ef +zP$~|jlxOZ(%66Oi?7Yg**l7~B3s-Fwp|^;Tnh+{N#Azg@5<|j_fM_wOpp!UdiMuQV +zJ(BCU^rpJfoiJ_fQ@v?HbK5X;P6tyWAw)QXu%~)jz6a6h=xFx61tp9SF2TCvn +zBNd4^M>L}&2Q3cee7p2Gu~T!Is7pF#N26I7x=1(s(3^`+LYXPS_mSCG&uG*)!c5Rv +zMNg9?8F8gDI!Xu?A5x%{5!rV^f_1Bzkz2#C8mrZOjK<2`57s+TJ%>~@f#MB-1Rwy) +z3d34FL1R!_tNf4B5p))xWb#2JsANXI@1yjQ$Ob&u&5YcPfRr_1u&3F|keQ-uh@W(; +zWS-f+9|VD)8D$UC;$Ke + +diff --git a/platforms/sdl/sdl2/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/platforms/sdl/sdl2/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp +deleted file mode 100644 +index 5018e6b2da6d5019968fe84e0576502f61901e93..0000000000000000000000000000000000000000 +GIT binary patch +literal 0 +HcmV?d00001 + +literal 7624 +zcmV;(9XH}qNk&G%9RL7VMM6+kP&iDq9RL6?zrZgLO+aWQ$&sYYp0`!}3HR(<0ulY6 +z0RG`XX(^PJ!dd1Ayoe}*D1rhlrKM0>3SEjQf+&KkrL+`EN}*q|-%~^l1VwPQl!j|U +zp-Ztd=Q8I*@JW&tIZrJOUo_CAn1n!*;5n{_@<5WntrU`#gpi9OI?gz8oR>>WX-U!~ +zBy=gFIO8~zdGYe`a8|~L1Er-<0)<(x<=Y*8aRIlrZCT9{`X3G+zgz+RSLN8zl_Q3i +zU1l1Pn@EynTPfeq5;VsiBYs;nGWX +z4n=l0#j3Z-f(R?^{>xAV0`nw}h$CS_aA7P+-1E-r5d43_!*(V(g*SFTLo&Qqo* +ziu+uBc2N{@<>FLnfGkL9`?r~q`E251QYHRJNc<*5X_x8gU8+;~I4;dR;hd21 +z(^siNh*G7Bn5RZJzMe*($A=iJhX6cz;t832Cn3TUU3+$mr)}Hbwvn~%yl#*bWy!Ye +z3J#qr^vXG}HLLnFEB=C>tx*o6HA=%AXKS2u&arY%4(%W@;J&sdfg4vrAbX8gpCxR* +zi$XcAGONfkt0?QzwykX>-S78_EV*NMr#!Au)ifjn8D3^)X5Q_ZU(3fp2e3!5|CpJX +zahR#O+v^EAvC#VTz20lvR;|ajtz6oeQba6fX7XUA^;cdkW;9}Xu$)+OWHB=YexGJ< +zbggYuBsb4r)ion#W@U_#B4&n2xiTHyfSK=sF~yA8j_r(@nb|YlRj^>&s@i0GDtTx& +z1f!C){Re_y(FR4}eZNS5u=AlfK7U69B=DR+BkuVaiy5ASLB?)KJm(LHdp@Zw#R8ct +zOSLDCZ^L(0pmCqL@dvMbo}U=~tpNk413>(F9yGaw|Em-yu^nXWD=HViNW)fLB{z<%5Z+9(ZwL+ +zS~Px(gH&H;kP+_G4j(QZM81S=pR +zK$5_#Fmns=I#BV*vtY?QfA%Z43T`1kQVm!^ljKAsB0WG`40_UkSrQ#Sk9QbkBpXSF +z&+&B(GA_cUr0viOgNzH}`lO4Y`C*WefTSPoNDBfIDuu9(AR;n3!0jv*Y(H;g`hSbH +zyI&(PQLQMVz~%^c_;SHxP;}7{dr92^7lX`uh;Ot$w8J1HHj)f9cisOc@s +zusBAQ0gt6KgV+oim4Ib%f#%Ry85N)ung|A=)M~r>H=ldB>m7XInbO9{{@|5m;d|}B +zKlqLPB5vU*(}klBba378fBNx+@V)j2A1#-pcmwDcZR1t~liQp&sF-ZPmh8n6w!q($3-@snRufAodVQW<)87r%4otE)tIHKA|{p^}!6 +zh-F`xJ#JW>yXlv?48K3z*#x96PnHU^lv9a=gw^%CXs4d#K>C-kREFQ_@;w1W&nSXK +zZv>|hUJ4N)llv$@JiskVSa@*(aVMpCR|QTd6jCkSxxBbVDSC?mB~C4d`ZO&{k-My7 +z@iC_so_j2=xhqO=TX2yw0hn4Sw^;DY;m3s6L~x4XxCda@O_bmUG}4RU^a2RZN|BrK +zfA?VJRhYa}YluW>H6tNg&)Zra2j06~Dw2N<-3nL*0 +znRrWr;UWw&=txV%jw6E%Wcey-1vrLs;NXJsYcPEWmH~p{f&(52fQjX7w1JKr78xbt +zK)4{W$e@du1%p8b9@PPh$TG-4K|3@=1cMAT+6~AO8;cD5(hfDbjqYF>VDe5J&hT|4 +zJkosmD&jE+xh#ii2^azj|K7k&i_VA*=k+J0rO-!rcKA>22XJ8#O?t!wW>dKZYS8d&)sOx~^4 +z2k}CO>mexu1|eUeo+QCCZUB>HDV}Wk?B1XBD1|b*7{0~DNN^Yo)eYa^LL@p0RW?rM +zfp-CeFVdd^kHHWfX^j|MF9sPEw0z{N2TIcJ4)X^XgDETnB$p((Q$Jjc*kRF8m)jBz +zVHsq6!@G2Gc3w<-nD&5=KMuW}~>217BpI0}}pJuF>9l3n0OD}I5Pz8y;e +z^_}MeqU}J7W!yk)M6fgl#fCuA7x5q*5+bsM!7AdLT>f9k?mFAI2CyFpdEmvkB;@6X;}>x$ +zoOrH1o$D7vo_Yzb6z&7vRkk-nUcZ_*{*vw}oP5;f!XL*iwg#Mf*@6K+rR%yVr=Euh +zy!GObqFp!zfTlO$$|I25w~g+4_MWC}TW+1n<_Z8_c`Cgfk~EwKKzZv=!&jjTIrTy= +z|6%xsl4V;%c5YiR(APZwogbPOAJ3;VF8zvI+}zIAsTA8BIzbYWc)0)wB$aW~m-u#Q +zM_~&=^1w^ztc37f`W@*JoEBXEefrZ-EIW6Q^hQ7f5|@w+(^<*psnyPq02i(gl8hui +zAR$M?@WXN}w887>P1Adpq#tJqnQn~7xp#%^@8t5I;ncu4x$vW*Z2+B7PHdBPkT6Mq +z96Q72=IV^o9_NP1PF;Gt3=X*ZR$i$9Jp +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 +z4`QX4jBlJX +zBg04#D4eT~Y-i0KfvG^D=BOW@!_4H +z|KzW)d)##SA;W1IiKt|)Bp{^KKo?1YiNR7E)ZyZRAO;z2L46=i`0ws{S(i0KfdW() +zEs+ElkvVULgprwZ>^jmN8?AvloH3j*&#f=}1YkvKH+++oq!HHGBqI}>T9Dq-JPZG= +zJpp8(wDids7=0K^`L`j9MFhHaiY~T;%8%GR`px3XS$5M7oHTSHVK@i~4U3C9T3IB( +zVCjpb?MSlWLJTs#O4^Y0hKpC&V@NK;Cjt=erNwf+xMUDS0YXE@-1%&z)RNzke2G5{ +zA&Df%x2zaRXs8~agK#7%ks>Q|p$Z;)@I@JKl9%xQKlua$VWi8E@w_IfHazV +z+>>u_rJqa!M&DmN9(l|;c*5JbiG{~$Zo3q5Va^X-7fCMA# +zW*etuYbplQPP|o +z6&3_$0OO9o#>dQ-c2HarEo_=L7jz`VqKKN*lYm7A^-3Kk8MMncI0MTeB}9Ai^>RS? +z1UzQ59W4gkx~{iL-`@WR{wEOSm6#C(Mz=lK|MLJ{<3N=F + +diff --git a/platforms/sdl/sdl2/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/platforms/sdl/sdl2/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp +deleted file mode 100644 +index ded8d97c6205d5546bc8b24d7be8560e581545a6..0000000000000000000000000000000000000000 +GIT binary patch +literal 0 +HcmV?d00001 + +literal 12522 +zcmVNt@GW_e|aG39RF}y|Ce+`y! +zLqjn#dcbjQTiLR(my??p-1YaLihMOT|AIk$L-G)Zx|F@ce!h!F{PX*L{Enur +ze_-Y_V7#pqSGaU`Qz?R5C*`7MOS>Yr8SpKD4N!nsYbT|M2yX8^H3BIA1J&eYHWS&K +z?ZMwq!24~$-Z0t)9uL48_fW6IV@tDzUr0gBK{*PYvZgHGP$2?R&dCX`rA|kRk +z+k3swNp-*csUGndjIKw6T#ionV6J0ext`AH=WYT3qhne7EQd8~U$XTK0Hk^e03#x6 +z*4__m?`!8GTQ-1?QYp*10drm6HxsGr+3X-iWc0XHK&4Dthrr|2eZ&rzB3sW}nTN@; +z`UF*s7-{77S4Myl;6A`(>`;1+T{A$X8pd_Ve${;q_^~@FK#5kG1uvjc$bx#EB&RY3KsM$I$82G{~D9kw|`wPnTxpmhjf0$`b$_lE06dQT&n +zFeD~GEax=Rf?MaeN;B2#FlpxV*)n@0>pkzmKTjr~H}DVF;$YCWjUb0V?BCt(8AQYc +zbT;-15nMsz1G>b49vQsF)DA831FcHS*s)`17nZ_OJ5;<;Sqm>TBT_hf9w +z1a1G~NOFGvOr=UyD(vc-*DFYMr%cVcWL0 +zZQk#fWLvh3O`MD4s|}0`V`gUF4w;#^BW7kiXJ%fRnHiiEZHHvB{{QnNNwRF)k|ZJV +zeh>a{m8t_$osUl8l>j>DUv7aM*|ut>_x-rL`;ZLCf&7Q=?hfxrlx!=ABX91C67YZs +z&YSEHC07(MkK11)RB(FhDJkyA@Zvkw&t(FwMu|Y~ +zz4AaM^My8gDAv-3E8o|p@dzS8AV{JRD=)q$HNwA&_GBw4rmSjPPPyd@sbvk}G^C)S +zC9a7vdQFR}j;pGgwH9xd$2aaT-F=SU#Zm5!mef@<%e}|`_J491A;b%w{&BSD|GgMm +z?@p)tRug}lgQ45hXi4Fw30p}tlPM5n30y}6%uz&8BT*(Zsc=sUJsRq&-R(F`{d3$O +z{&1c+9}jlZKVB1*J(?Sb&5s^>i_!95EJO=I&u5``5IXddMQG#1yWh^L#c2`MtkS`H +z16QWlt|p6t)m&6wGcm(qEF4Wx$xI;tw7QPgiEDMK)15)r23&UKRb|iV#ueK4y1VZH +zVE46a9c}#!=?1h#6#kJuzC=3vFBSp>vgV5im+yVqbssrx%O}3C#U{d)-vvOBr`4&F +zpbGN)S3uzaIK6_K0s}xHVVf>U%5bQJc?$qIkH}SJ&S=#UGdQ`!QUAYQmi_P_YyN`1 +z{{@E#ShM(B-pTK6a?OJT2b5T^p%Vb2t%jC&4_lrj@ubn(u%Rbpyhj>OJh4g|N^xa? +z-AaR71fXD8!+C;N4CMu@Tyut?0_?DKvqOX?MjMXtv>WA5F~Z7k&Qb&WY+F^$o3waD +z;Rb>XfJhS~E2onnASx$qO6rj`5Mrh_fth4P;`$l@8QZF*8pv`JL0!W+WuDcko}5~} +z^&~-pi}Mo4SllSb*sQN2`bmY(f>E1~d5^`WcpJexHS8!=LO#3H<|CyhB~3e2Qh+~H +zaE#!e#j*W|;5M+&3lBK@@2)!puqLri +z6#zh~KDjVHNZ7q6QUs?K^MF>X!Ff)uBlJ=u3xU|*2es=>1ZlK2I*mXhqK(re;NIH2 +z>ED6rJxQ}g+Bo5=&n+cInk^E+Ua!>zy8#d(8Xgh^pvBJYTCG7uvqjKY+zK$GuOnCtfwg^5QO$P~TG^kvK{)>h-{4XXiVnKWLnV)^zE?-{#>7oJIM2r0G*nhe+VG>Ww +z?(W7P-RSWAp5U(+`{C1qdP6g)mWd}W{pOu}=ih#yh)~&}gB-b_01_l4qxfhMC6J*i +zj)^I?fs6+eWv2B}gp5Zij6~6B9TCo*l?gG^(w*{?_5Xb3-(CFp&-X@MVLBV)wE5@W +zI{xY_A1bT>62aJ~vJ^rIR3(+Ts3?JnPDBQqhGQJ=6Xucekc>wubS6Op8IK7_9h8wU +zJ2D=(;oX(d{ +z!-9DXQhOoLxkS8{p{HxbMD!m!g%aw?7y{`}GBNYtlnVw%Pf+_*R!ivJfwrj5Dzwl> +zQun06$asR(E3ihX6OW9Hhp2=SNt-A2N5*q=U_DHDl5u1_Dui*sH6ZYM17rVW0`M@wFL@lQRR=1W*osAwT5 +zt4lT@yOLM1lAn6^PxIm@y{yQL9M;f-5G{rGddJ)b_t*WDSCQcr^G))H_%eU_&V7xq +z0t$fE!lJzpxit?zeGOU?57yGpP)Iv}2=$gl#)f-ae(N8ue$pjJN|$L- +ze9zrx)K9(no~!=BV}CjNQb+1L_@}wR6I(gFhm35taNL{rMGwOd)|*tUWdiiX7&kM+P~6?a%5~Serh(Ya`)F +zqZk_{CM+Q1Byr{&v^9Z10JFT~y=is9`U$KZo!uqKI1NqW&OvCRH5?>{2(!kYI&5d2 +z=yVIu_KzjJ&g?~p8W`hVk*B%3zu7vl?Sw|tmh-sGl|*s0ju0{pVtkZ9QP!s>9wDwp +zz@t=*jH09U5rP_p;?aVe+$_u9k$efvbt+*YLQo^8R$4UT0AX2MI-cR`{oOKh_H{({ +zXy9T;eYbrw|3$~Vdm3#*>LR36AqLa!4>zq%oonAGb>imvL!Fw#2MR%h)YK-#sByDI +z#q^FGpuaBt_33Xx2pX7R{F%E*3=yV{@A+f@=+Z&+U&R_G!(aO1#mn`t`0Nh%hdfM> +z)>gHY`DC;>fu7lG8;p!HNYJmQ+ec`HPGzoof$jfs^JpzPg;J#6$T$fSM~Rql4386~ +z-Z*2TRv?+kt#OUPP-L7wFep=r6I5Asg(QB@zL!7ybBCmcL5BXJN;1_4?;Vv^-$>o; +ztbMH?_4rw>r@xiU?)f71!X(w3Jb@K3D%<~`zbg{-F3HZ+V6G3zfgc;Ic3N@6;tzUf +z*0;L!lXGIqTj49S8)rVc-E^h|Zus(C`*D^IU-G2P_R<$Gsj8)9hde!Q$oU{+@Dn3*!{6uC;Y~JY&IC6j*<2FEfC+QMub4Q6 +zqJE?$ki^-$59e=}eXUigr00vM`3wRVk9sHC@R!|9?5qPgwI=ZhOiX(_(%&<{&T4ko +zrC?SCA?c1j^Cp7yL9awrL1VZ3O<3hPmcDPXEU3JFDj`6H+{O +zb~c;?Q0iRsIcZ42u$oB1LDKuy7r*=U?efxJ#8qcBe(3*92J+!yC7TKKC*U)2F;8=huaCWd8pgJiL!z1HC*`}2TVd;fNrts`5*l9QrY3G +z%gzmRbU*^`=)Tzy^H7gtpi{|%-St&egulM>)7e>+<7Z|4l+&A@4=;=~Y0mE2vpIM6 +zHlB0O$vG!vNj$+7UwqNu3zN`YO+*iwwPRw3tO@9Z(ZJ`sU~vS}t84{<>QGQ0&b|0- +zTr%+MK_>rJFsq4BTC@mUH2eNLRl +zIcJ`zHB3hFC^sz3<}%)gNk~bcOCo^_pC^5^M4a!AN3VngyoiS-hL{5v1@+()x`Vmn +zwWrmdpJh@y`~BDa$==Q|3H`yZB$XM-5&`pm%WokLi%oF;3z-1#c^Zw2%n5@;L7EE33KU +zDy!^p@#B<7)!+3fV>--D(7HtBkx?qfgz%aooT!-LNR*aVA=(I2qEArF-j?*YCdfnv +z^r3vXqR4nslqe(!K1@8Q^UleRXMfC!BC=**oIFU$daJy#R{JAS%D5tfOruBYo@Hq9 +zjS`$*Z$guQV~X@I!<`A!5&a2`;UsPj0&@cWCBsbRV%%v3H+qh1R;Kp5#?8}FLb(+q +zlg7$KBK!oV2_dd}zf~$@tk_OI$+&l^6yP69v9}(kRf^cz#5WX&s3fdLZW-rAbe*JB +zP6qp%vkJ2C4HNUfCJa+66EbY1xMyck$xMW(ZzO}E4EKbo)^P8M8x#g;QmKTyuWXSSKco~Ij);R# +z)Vu3A6tJYoR+J$_OH3L=!Ag8K=W_iYT>nRQ)?#D?I7)G9U5Z+K7KuoagO?e}N*Y7s +z{e%@bLg$k&LHY-A9RW(Lcrkmf_DB>&f(w5?1xH|Z_vXdl=b79uo&}#htMS$I4t{az +zt4M%`^`!2fC`crorPX0*aXl+Y2vDFvj`^f})44ZA!tz`%#orjusssOj45%tdhmd +zD4}bQ8q^=#z|6_Zxu1aLviV7)FtX+-&Lv(BDNF{rVLX;0_H+5tNT9A;nypO_ijWo{ +z$EckGa)gydvK|v<8ot=lotoZ}k+2vEc&H$iF%$}uVG>}wy3fFl1nTxnzfBxaQh;1& +zWa6q&*&^9tnD!}gMSal^*&vgR>k)kn3ydhjHi3wzFljNN7_eZqpq)iTb?yAzxHNnvUpWD;n`f(;XS +z@M95C#ZE5%B44XG<0m&4O+9U@l{MCVhvAsTRMVmYKRD-FWFNFQ2J`q2P0Py2zw?X`Qp!xSCo*b@ngthfk2b$b&8W1@9)rxA_T?>dSQGik&|Z~d8FVlRm=k& +zPtiL3EMB{oB1I`4?##cexH0n)Cy;SkvU#zHD9R~<1}7M-f(NhUWxFT{hUA8ZaTVoG +zZxHs4PE<~m4Qzvy&T-6GGub;js!{&JFW^cZgNH!wW0#CyuvJd09rle*^zi#=@V;9V +zzm6H>PA=vFIvC=!Dtd7L0h0GRx@z?#YOm5*gnw@H3yA~&tONiX7sLM1H$K88f9V}I +z-NVOoBcU~p*@rMU!qA*9qPRQEt%BlEecZjD!WB(=C$B5th%U8`){9P5L2_h}2BXwj +z-sFM6Oqhjzq+Sb=Nnr54o-LI`=7fP3BRt94ic_(y=;xmNrdBqY+IG9~OYgh0eyx{I +z%O-{sb;)>51{vtD?>tDnoCVgW5$0hZsbifk{xR4IZ;m3ij0q@fAtoLfmXR^Fx6CbB +z%4Eklz2i2kD8iZFRemmSS3`UEzaH05GWs+bc|N*{W7LV2pFC@-w=<^r>SsuNY73GlqSojlq{yu9=^r7-C)f +zfn+*6*Hqao(hmAomSlK>n-morjs@G3w@IKmxP@BZ_}Y_UUumNkV6|e!CLV^7Ek8NH +zEOZgn-{=D9@#PqWX?YH{ +zFxbQPF+Vv2ibA%%p0bYG2+h1@Y6qL7^tu1PI?NE3qvA6hPKM8yHYDY!T&w43ec}Tc +zCy-^Y$TEEStEW!lJ;|e_v2gnP0BU?=p%~&6xElB>)2wrmMNHte6)V--S0`PweqG`U +zyNaxyzOVbn2PBB6sE%w_fz;4o#u-vxT%9v3@MPNCK&L-Zp8n1xbt(&Gm7%f9HDMv0 +zK1`6kHDmxAD6_Dh-c)pI23tr*lLNlW;|bf+>C49q)?;<93#h6f=abbbBtTM}48hkZ +zDH(~im6HU?-swYGJ$YXjuCF~27P~2g*D2!(hZ<6dntt_r@0Vr1(rQpHN+mOOiO|#_ +zF;IF$269KYp+HEnGe5Ebn8LARMh}ODZVDmJ9iO&&ZHTT?>ux$c3l$h@;s0NTIYYKV +z4OS*1+nrQ_+pAJ$pJ@b{+#H}$lAO_s_xAd8t`XWag$;Q1RoA%`t=+gfsp9|G@c87T +zxVJ!bQuuNaQPZH%NAC8SIk|W$kJOo2)@`U1A<1P0wZNH1lzH(RPZJOTUlU9jtTup+4x*XFNbq9WoNQw81cp_LCLKutiy|`NyYw#Dz3(W4Ffp +zT4GMSn$JYJBbPHlZF9;QfCI=jRyl_aP)ABUSTqEaX@X(tt6W8(%hkw_2VS3=-2K>e +z?2&m#jD%v8U!2G1wp3CE20kBT4>@0Dhhw$xr^9Tb7Fum&`iH9aB_5iRZ&eBtJ-klB +zj0$RdMJ#;{eva}{c&S7J5xY0YOoM)jFK1ekMrazHj9=#E)>bdqD2@r>b+sVhi<=aN +z4Glkh;5hRL)v#R$P?-R!c9)Bd!v}uooanshF_xbA`2Ne@jB`N=WgeNi@rS^~g*W7R +zDUTf<4?ZdE-#ppD@Lus$ke~H^IKuOg?j$A?aKDZ^{zAT@7 +zE^S1$5K!EWYcM%9yzN%|uld;Lf3owG113b0ZXTOTX{n7IC*ZD{3q#IO@@HufEi50era +zb)IIe5tD1@EHaJw9Ypj4DD37pLm!DITVd?Un)wL +zCx5-si3I7G_$1BbAK*3(Z6m7O*;)`+0`Jv0QpAAZH^+wauIS +z`R8^zut~sLa`q+M{+|)5>q&c1};9$Sb5F +zR13lf9v10=lG5&lBbbwIbqS@Hx|F-FWI!HPxfs7CN~oC0rj_ZPIi$b!y$0YM64Q4kv(}3JBD(Xg1VU+Zks`;5m15ov7;w=#56rEqjza?bHQqFBQ+oeDuo9t2jvz6 +z%#|j2d%Y_af^k|Qy_Hpi +z)Rho3|APuZ0t+#LBIXmA9FHS?3~ts<0Sm^Gf?`e#V8+_j@uC@Kh>(={#qfhRXE0P$5-(d6FJ2Bc4IMI&?#q9Ng-CNt2#R}02b`;)2N_L>d^vx{AR +zd?^sdRVg~1Axux9EYB^stY_=ZjKJUz9pDw63Ww#z$kZhS61wtac(mj#`vF588;3JZ +zY2YAaIvrm^q_Y&8knTM7(d+s?yCg2)8g7F>tjdvd<{1yGrPO6vihwFWI7?M|Jj%sh +zL?1Q(u@Am_OUIfF>7|Z{P=KV<>1dGP0J$YDvL+}=!Y5MT9RU_`F%J#WWGP%}D+_1| +zlxkf@dm*!{+#2NA+39oyejrk69E9|SO|i5LHdqKH?T_FSZ{qL@K)Q~2kt;?ALVIuT +zE_4G+0tU#sAJ1%phjjv<_CP6?tcj^$G%>wLS@=`%tHe>2cSBcDAzlV+WFxeplowlC +z%~^b}BsJ&KoaO}X1iw`7Q^vPanKoW_~c936ryzBDR#B#dy3 +z;!C9+X7IU8UQ6J&$N>0DO+gEqVE@`1eXit(h6G!xyr)`2 +z8Bneu`hXRP;xL)+Fo5NO3#fvIkw0lJFmo&omnk$$B9WNhv1yDmxKdWE1*Hh$6P@h7 +zU*#;S_0xyx@m+m$O^{28cLooBHupy~I4nw6kP~0><&nsx4!0vkf65XqyY9Od38*K# +z3(8UOKP)9z?V{{HrD*1A>J=-OLDb-&RH#yIPUDyc2H;AK_Za!;a5irm!^v^x@hSaG +zsZj*A)itFjC&F=5b9k_O5NKAB^HYZkCwxF$49%!Aj}ORLuM^ +zQ(OLW85qppN2?s=U&x~IbEOez`D+uWA-A?sxlM-4bf{|+>5ZcAl-8ffcf-0N*II$p +zsRA(C5bvO*ht#J(rCKP!HT2JP)tl>`N{S9RlT`7PC;Q^4$3Z&lO7R2K`x +zbJgpMh(#yHcchfBPQBxW3d}dp2a&W8?3Mq>3AD0~Dha8V(I}UFlS3ijsx-cb5-7uR +zrM_YHx}#<|z8=bsGx5-(;0OC<+TuKToHj=s9-~4w96X{Cp#v)j+G?<=$+hD+1xE)1 +z>YoFE?W%E#UZ2MP0^+}YQw@;^<#Y*rXeJs})nU^EB}b_yDI +z1J*)U)(m9C(jn~U?Oec1v7z%3h{t;y()i=a>-X`VR&Ps3vsWY0p&&s_TXrU +zJqh8Ja5F8svlMbbBg*_KmLcViDs4oBBPs#z>Et$BZ<&*vrjSwf9Fb1fxhEZ*9jNE( +zqETSxeC(=(ITbsEZE)OP$coCeMG@&!@k8#YXj23%6-eU)j?VlVh0Fk^v62!O4}<>i +zQbO%$(Nk%l0D*e0$I)tr#c0gi^XnRiQ&65jODmL|LgF?229onv85=8K1_`VNR_n)K!dMjbjo6rAyv_0 +zQOE>fJtnL1)DiIi8`y46HYD4&P`+lKydf#~AqY`q$DSw|<3kX>jyQ987hVSB}L_1m% +zMeTAYG$!m#+wEwkw#4e1{Wu@|_VLSaQTgtw_5Rx7D_iZv5|H_}Ok-#G0onP|g2WYmy08?ntflNEt +zfS7>MwfkRv_=u(PKX35I+ow28(tNgDB-6Bp01|{D8kq%vop9Zl6z;v`Ml1H)8~sjS +zRN@B3?Wtu$AVU&80t%x>Uie3-x~YJeG$;V(Oc6N3V_Oy!*w>ISg)Rq}vPcDSow>C} +zdG%V#lQ%G393~sgwo{UVS|D7$Ld +zo!Ky)oHc+L1SA9$>ba=8=^ZB#KR;)F$uYzubiO@p7Mhm(IdJv={^`Td7&dc;W{)|K5F)-0e<4Pw^AY!mmgbKw) +z%PBb=c5)35?@zW_hQRh%?oCboch%@7^x0(j+U8Z5}f~ +z;Nx!I>b8{SCgsBRDa}_-tlT4I?TA=8hzt6H8GWLuTcAgLyf+CGYk0588MZ12GXjo4er-YuKUI7XurC?FoBy^{z!3B +zryjyzw@cL$ktb^+>?SfZ2>j9#f%|T&+kAJ^sXW@GFcBFhO_y_DxU*cm9W0Ht%u#Y? +zyx2i&;l9PF$qJ!lrR9>%EirELL+QIs);-z<`XOWGW`kmBA-dBkD=y1 +zMOW$I-h{b%s`fjpqTiX2rI0z9M(S~AL3?!SA+ckRQM +zTM;kec!`Y;OQFiK3q%M0GkNG;GRHKDRZiECXs|u +zySzs2FyfHWj97_}VDD#o?gX7U2XBE`c@!eF@&Tu!kABnwT1e2Fw#;yUk?&9E>4dhl +z0TM$&fn8mqInzkjp)oO7nXc5mA7mL8Af6|&%L)acf;y}=S=NRI6#@z2d7(xi0R+v< +zN5C5)f$Nl#APCyfzNTgT*?XtR(D%AA{0|~=80{P9@v|6U0aeDj8QnL|mMst~oN|ic +zbXhgjV4FAqLjVyeXUz{Ymm=_PzvgSK32gC;VzCOWEI>p65GtGk;Lwz9R-N+*0T=+p +zTM-DO`C))rf{vB)hqw~PuSHQ?P10RI2| +Ak^lez + +diff --git a/platforms/windows/MinecraftClient.Win32.rc b/platforms/windows/MinecraftClient.Win32.rc +index 0837b53be..deb22a723 100644 +--- a/platforms/windows/MinecraftClient.Win32.rc ++++ b/platforms/windows/MinecraftClient.Win32.rc +@@ -52,9 +52,9 @@ END + // Icon with lowest ID value placed first to ensure application icon + // remains consistent on all systems. + #ifdef __GNUC__ +-IDI_ICON ICON "assets/icon.ico" ++IDI_ICON ICON "assets/app/icons/icon.ico" + #else +-IDI_ICON ICON "..\\..\\game\\assets\\icon.ico" ++IDI_ICON ICON "..\\..\\game\\assets\\app\\icons\\icon.ico" + #endif + + #endif // English (United States) resources +diff --git a/platforms/windows/projects/MinecraftClient.Win32/MinecraftClient.Win32.vcxproj b/platforms/windows/projects/MinecraftClient.Win32/MinecraftClient.Win32.vcxproj +index 7f723cc0e..8f8be420a 100644 +--- a/platforms/windows/projects/MinecraftClient.Win32/MinecraftClient.Win32.vcxproj ++++ b/platforms/windows/projects/MinecraftClient.Win32/MinecraftClient.Win32.vcxproj +@@ -80,7 +80,7 @@ + + + +- ++ + + + +diff --git a/platforms/windows/projects/MinecraftClient.Win32/MinecraftClient.Win32.vcxproj.filters b/platforms/windows/projects/MinecraftClient.Win32/MinecraftClient.Win32.vcxproj.filters +index e418f0bf7..e55463c4c 100644 +--- a/platforms/windows/projects/MinecraftClient.Win32/MinecraftClient.Win32.vcxproj.filters ++++ b/platforms/windows/projects/MinecraftClient.Win32/MinecraftClient.Win32.vcxproj.filters +@@ -42,7 +42,7 @@ + + + +- ++ + Resource Files + + +diff --git a/platforms/xdk360/ReMinecraftPE.xlast b/platforms/xdk360/ReMinecraftPE.xlast +index f9d9020a9effb15aaf90fa461889e4aae1c9c9f9..409edcc888385bf2544495fb3ab2f497ed9db5e9 100644 +GIT binary patch +delta 85 +zcmaD^9ZfAT%sZG_Zqo@f`y +F2mmBa7hwPZ + +delta 22 +ecmZ41!1$ +Date: Fri, 20 Feb 2026 00:59:54 -0500 +Subject: [PATCH 3/9] Restored Unused Camera Flash Texture (#494) + +--- + source/client/renderer/ItemInHandRenderer.cpp | 4 +- + .../renderer/entity/TripodCameraRenderer.cpp | 40 ++++++++++++++++++- + source/world/entity/Animal.cpp | 4 +- + source/world/entity/Animal.hpp | 2 +- + 4 files changed, 44 insertions(+), 6 deletions(-) + +diff --git a/source/client/renderer/ItemInHandRenderer.cpp b/source/client/renderer/ItemInHandRenderer.cpp +index 81edccd3e..03c1ebc24 100644 +--- a/source/client/renderer/ItemInHandRenderer.cpp ++++ b/source/client/renderer/ItemInHandRenderer.cpp +@@ -234,7 +234,7 @@ void ItemInHandRenderer::renderItem(const Entity& entity, const ItemStack& item, + t.vertexUV(1.0f, 0.0f, -C_ONE_PIXEL, texU_1, texV_2); + t.vertexUV(0.0f, 0.0f, -C_ONE_PIXEL, texU_2, texV_2); + +- SHADE_IF_NEEDED(0.8f); ++ SHADE_IF_NEEDED(1.0f); + t.normal(Vec3::NEG_UNIT_X); + for (int i = 0; i < 16; i++) + { +@@ -251,7 +251,7 @@ void ItemInHandRenderer::renderItem(const Entity& entity, const ItemStack& item, + t.vertexUV((i + 1) * C_ONE_PIXEL, 0.0f, -C_ONE_PIXEL, Mth::Lerp(texU_2, texU_1, i * C_ONE_PIXEL) - C_RATIO_2, texV_2); + } + +- SHADE_IF_NEEDED(0.6f); ++ SHADE_IF_NEEDED(1.0f); + for (int i = 0; i < 16; i++) + { + t.vertexUV(0.0f, (i + 1) * C_ONE_PIXEL, 0.0f, texU_2, Mth::Lerp(texV_2, texV_1, i * C_ONE_PIXEL)); +diff --git a/source/client/renderer/entity/TripodCameraRenderer.cpp b/source/client/renderer/entity/TripodCameraRenderer.cpp +index 15fc4ea67..5855de00f 100644 +--- a/source/client/renderer/entity/TripodCameraRenderer.cpp ++++ b/source/client/renderer/entity/TripodCameraRenderer.cpp +@@ -86,8 +86,46 @@ void TripodCameraRenderer::render(const Entity& entity, const Vec3& pos, float r + float time = getFlashTime(camera, a); + if (time >= 0.0f) + { ++ // pulse effect + currentShaderColor = Color::WHITE; +- currentShaderDarkColor = Color(1.0f, 1.0f, 1.0f, sinf(float(M_PI) * 2.0f * time)); ++ currentShaderDarkColor = Color(1.0f, 1.0f, 1.0f, sinf(float(M_PI) * 1.0f * time)); ++ ++ // restore camera flash texture ++ MatrixStack::Ref flashMatrix = MatrixStack::World.push(); ++ ++ const float radToDeg = 1.0f / MTH_DEG_TO_RAD; ++ ++ float yaw = m_modelPart.m_rot.y; ++ float pitch = m_modelPart.m_rot.x; ++ float forwardX = -sinf(yaw) * cosf(pitch); ++ float forwardY = sinf(pitch); ++ float forwardZ = -cosf(yaw) * cosf(pitch); ++ ++ flashMatrix->translate(Vec3(forwardX * 0.4f, forwardY * 0.4f + 0.7f, forwardZ * 0.4f)); ++ flashMatrix->rotate(yaw * radToDeg, Vec3::UNIT_Y); ++ flashMatrix->rotate(pitch * radToDeg, Vec3::UNIT_X); ++ flashMatrix->rotate(90.0f, Vec3::UNIT_X); ++ ++ t.begin(8); ++ t.normal(Vec3::UNIT_Y); ++ ++ static constexpr float U_RATIO = 1.0f / 64.0f; ++ static constexpr float V_RATIO = 1.0f / 32.0f; ++ ++ // calculate U and V coordinates ++ float texU_l = 48.0f * U_RATIO; ++ float texU_r = (48.0f + 15.99f) * U_RATIO; ++ float texV_u = 0.0f * V_RATIO; ++ float texV_d = (0.0f + 15.99f) * V_RATIO; ++ float x1 = -0.5f, x2 = 0.5f; ++ float z1 = -0.5f, z2 = 0.5f; ++ float y = 0.0f; ++ ++ t.vertexUV(x1, y, z2, texU_l, texV_u); ++ t.vertexUV(x1, y, z1, texU_r, texV_u); ++ t.vertexUV(x2, y, z1, texU_r, texV_d); ++ t.vertexUV(x2, y, z2, texU_l, texV_d); ++ t.draw(m_shaderMaterials.entity_alphatest); + } + + if (&entity == pHREntity) +diff --git a/source/world/entity/Animal.cpp b/source/world/entity/Animal.cpp +index 1c30c0614..8719bf4c2 100644 +--- a/source/world/entity/Animal.cpp ++++ b/source/world/entity/Animal.cpp +@@ -76,7 +76,7 @@ float Animal::getWalkTargetValue(const TilePos& pos) const + return m_pLevel->getBrightness(pos) - 0.5f; + } + +-bool Animal::hurt(Entity* pCulprit, int damage) ++/*bool Animal::hurt(Entity* pCulprit, int damage) + { + // Run around erratically for three seconds. + field_BA4 = 60; +@@ -85,7 +85,7 @@ bool Animal::hurt(Entity* pCulprit, int damage) + field_BB4 = 0; + + return Mob::hurt(pCulprit, damage); +-} ++}*/ + + bool Animal::removeWhenFarAway() const + { +diff --git a/source/world/entity/Animal.hpp b/source/world/entity/Animal.hpp +index 7a2830e6a..7d1873b89 100644 +--- a/source/world/entity/Animal.hpp ++++ b/source/world/entity/Animal.hpp +@@ -21,7 +21,7 @@ class Animal : public PathfinderMob + Entity* findAttackTarget() override; + int getAmbientSoundInterval() const override; + float getWalkTargetValue(const TilePos& pos) const override; +- bool hurt(Entity* pCulprit, int damage) override; ++ //bool hurt(Entity* pCulprit, int damage) override; + bool removeWhenFarAway() const override; + + int getAge() const; + +From b7aafe0f3e28cdb7a8e58770bbae1677e5a295fd Mon Sep 17 00:00:00 2001 +From: Un1q32 +Date: Fri, 20 Feb 2026 19:33:27 -0500 +Subject: [PATCH 4/9] Fix default UI theme, also fix Minecraft::isTouchscreen + always being false (#489) + +--- + source/client/app/NinecraftApp.cpp | 4 ++-- + source/client/options/Options.cpp | 2 +- + 2 files changed, 3 insertions(+), 3 deletions(-) + +diff --git a/source/client/app/NinecraftApp.cpp b/source/client/app/NinecraftApp.cpp +index a591064f5..1c7bd839c 100644 +--- a/source/client/app/NinecraftApp.cpp ++++ b/source/client/app/NinecraftApp.cpp +@@ -198,6 +198,8 @@ void NinecraftApp::_initAll() + m_pLevelStorageSource = new ExternalFileLevelStorageSource(platform()->m_externalStorageDir); + #endif + ++ _initInput(); ++ + m_pGui = new Gui(this); + m_pFont = new Font(getOptions(), "font/default.png", m_pTextures); + m_pLevelRenderer = new LevelRenderer(this, m_pTextures); +@@ -205,8 +207,6 @@ void NinecraftApp::_initAll() + m_pParticleEngine = new ParticleEngine(m_pLevel, m_pTextures); + m_pUser = new User(getOptions()->m_playerName.get(), ""); + +- _initInput(); +- + platform()->initSoundSystem(); + m_pSoundEngine = new SoundEngine(platform()->getSoundSystem(), 20.0f); // 20.0f on 0.7.0 + m_pSoundEngine->init(getOptions()); +diff --git a/source/client/options/Options.cpp b/source/client/options/Options.cpp +index 6cbc4bead..4e65f5d71 100644 +--- a/source/client/options/Options.cpp ++++ b/source/client/options/Options.cpp +@@ -59,7 +59,7 @@ static UITheme GetDefaultUiTheme(Minecraft* mc) + #if MC_PLATFORM_XBOX360 + return UI_CONSOLE; + #else +- return mc->isTouchscreen() ? UI_POCKET : UI_JAVA; ++ return mc->platform()->isTouchscreen() ? UI_POCKET : UI_JAVA; + #endif + } + + +From 343edc5f675522d7802e77505d17bfed7ca5fdbe Mon Sep 17 00:00:00 2001 +From: Un1q32 +Date: Fri, 20 Feb 2026 19:34:47 -0500 +Subject: [PATCH 5/9] Android build update (#487) + +--- + .github/workflows/artifacts.yml | 23 +- + .github/workflows/build.yml | 49 ++- + .github/workflows/publish.yml | 2 +- + build-wasm.sh | 4 +- + platforms/sdl/sdl2/android/app/build.gradle | 20 +- + platforms/sdl/sdl2/android/build.gradle | 6 +- + platforms/sdl/sdl2/android/gradle.properties | 1 - + .../android/gradle/wrapper/gradle-wrapper.jar | Bin 54213 -> 43462 bytes + .../gradle/wrapper/gradle-wrapper.properties | 7 +- + platforms/sdl/sdl2/android/gradlew | 307 +++++++++++------- + platforms/sdl/sdl2/android/gradlew.bat | 66 ++-- + thirdparty/SDL2/src | 2 +- + 12 files changed, 319 insertions(+), 168 deletions(-) + +diff --git a/.github/workflows/artifacts.yml b/.github/workflows/artifacts.yml +index fedf4758e..4e5ae906f 100644 +--- a/.github/workflows/artifacts.yml ++++ b/.github/workflows/artifacts.yml +@@ -50,6 +50,7 @@ jobs: + cmake .. -GNinja \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_INTERPROCEDURAL_OPTIMIZATION=ON \ ++ -DCMAKE_C_COMPILER_LAUNCHER=ccache \ + -DCMAKE_CXX_COMPILER_LAUNCHER=ccache \ + ${{ matrix.flags }} + cmake --build . +@@ -109,6 +110,7 @@ jobs: + # cmake .. -GNinja \ + # -DCMAKE_BUILD_TYPE=Release \ + # -DCMAKE_INTERPROCEDURAL_OPTIMIZATION=ON \ ++ # -DCMAKE_C_COMPILER_LAUNCHER=ccache \ + # -DCMAKE_CXX_COMPILER_LAUNCHER=ccache \ + # -DCMAKE_C_FLAGS='-m32' \ + # -DCMAKE_CXX_FLAGS='-m32' \ +@@ -215,6 +217,16 @@ jobs: + name: Android (${{ matrix.name }}) + runs-on: ubuntu-24.04 + steps: ++ - name: Get Time ++ id: get-time ++ run: echo "time=$(date -u '+%Y-%m-%d-%H:%M:%S')" >> $GITHUB_OUTPUT ++ - uses: actions/cache@v5 ++ with: ++ path: ~/ccache.tar.xz ++ key: android-artifact-${{ steps.get-time.outputs.time }} ++ restore-keys: android-artifact- ++ - name: Unpack cache ++ run: cd ~ && [ -f ccache.tar.xz ] && tar xf ccache.tar.xz || true + - name: Checkout Repository + uses: actions/checkout@v6 + with: +@@ -225,15 +237,20 @@ jobs: + java-version: '17' + distribution: 'temurin' + cache: gradle ++ - name: Install Dependencies ++ run: sudo apt-get install --no-install-recommends -y ccache + - name: Build + run: | + cd ${{ matrix.directory }} + ./gradlew assembleRelease ++ env: ++ USE_CCACHE: 1 ++ CCACHE_COMPILERCHECK: "%compiler% -v" + - uses: upup-company/apksigner-android@v1 + id: sign_apk + env: + ANDROID_KEYSTORE: ${{ secrets.ANDROID_KEYSTORE }} +- BUILD_TOOLS_VERSION: 33.0.1 ++ BUILD_TOOLS_VERSION: 36.1.0 + if: ${{ env.ANDROID_KEYSTORE }} + with: + releaseDirectory: ${{ matrix.directory }}/app/build/outputs/apk/release +@@ -254,6 +271,8 @@ jobs: + with: + name: Android (${{ matrix.name }}) + path: ReMCPE.apk ++ - name: Pack cache ++ run: cd ~ && tar cJf ccache.tar.xz .cache/ccache + + mingw: + strategy: +@@ -300,6 +319,7 @@ jobs: + cmake .. -GNinja \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_INTERPROCEDURAL_OPTIMIZATION=ON \ ++ -DCMAKE_C_COMPILER_LAUNCHER=ccache \ + -DCMAKE_CXX_COMPILER_LAUNCHER=ccache \ + ${{ matrix.flags }} + cmake --build . +@@ -341,6 +361,7 @@ jobs: + /opt/devkitpro/portlibs/switch/bin/aarch64-none-elf-cmake .. -GNinja \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_INTERPROCEDURAL_OPTIMIZATION=ON \ ++ -DCMAKE_C_COMPILER_LAUNCHER=ccache \ + -DCMAKE_CXX_COMPILER_LAUNCHER=ccache + /opt/devkitpro/portlibs/switch/bin/aarch64-none-elf-cmake --build . + mv platforms/sdl/sdl2/reminecraftpe.nro . +diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml +index cbbd0ab1a..f357b87d4 100644 +--- a/.github/workflows/build.yml ++++ b/.github/workflows/build.yml +@@ -46,7 +46,11 @@ jobs: + run: | + mkdir build + cd build +- cmake -GNinja -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DWERROR=ON ${{ matrix.flags }} .. ++ cmake .. -GNinja \ ++ -DCMAKE_C_COMPILER_LAUNCHER=ccache \ ++ -DCMAKE_CXX_COMPILER_LAUNCHER=ccache \ ++ -DWERROR=ON \ ++ ${{ matrix.flags }} + cmake --build . + - name: Pack cache + run: cd ~ && tar cJf ccache.tar.xz .cache/ccache +@@ -61,8 +65,8 @@ jobs: + - uses: actions/cache@v5 + with: + path: ~/ccache.tar.xz +- key: linux-${{ matrix.name }}-${{ steps.get-time.outputs.time }} +- restore-keys: linux-${{ matrix.name }}- ++ key: wasm-${{ steps.get-time.outputs.time }} ++ restore-keys: wasm- + - name: Unpack cache + run: cd ~ && [ -f ccache.tar.xz ] && tar xf ccache.tar.xz || true + - name: Checkout Repository +@@ -74,7 +78,11 @@ jobs: + sudo apt-get update + sudo apt-get install --no-install-recommends -y cmake ninja-build ccache + - name: Build +- run: ./build-wasm.sh -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DWERROR=ON ++ run: | ++ ./build-wasm.sh \ ++ -DCMAKE_C_COMPILER_LAUNCHER=ccache \ ++ -DCMAKE_CXX_COMPILER_LAUNCHER=ccache \ ++ -DWERROR=ON + - name: Pack cache + run: cd ~ && tar cJf ccache.tar.xz .cache/ccache + +@@ -165,11 +173,21 @@ jobs: + include: + - name: SDL 2 + directory: platforms/sdl/sdl2/android +- - name: Native +- directory: platforms/android/project ++ # - name: Native ++ # directory: platforms/android/project + name: Android (${{ matrix.name }}) + runs-on: ubuntu-24.04 + steps: ++ - name: Get Time ++ id: get-time ++ run: echo "time=$(date -u '+%Y-%m-%d-%H:%M:%S')" >> $GITHUB_OUTPUT ++ - uses: actions/cache@v5 ++ with: ++ path: ~/ccache.tar.xz ++ key: android-${{ steps.get-time.outputs.time }} ++ restore-keys: android- ++ - name: Unpack cache ++ run: cd ~ && [ -f ccache.tar.xz ] && tar xf ccache.tar.xz || true + - name: Checkout Repository + uses: actions/checkout@v6 + with: +@@ -180,10 +198,17 @@ jobs: + java-version: '17' + distribution: 'temurin' + cache: gradle ++ - name: Install Dependencies ++ run: sudo apt-get install --no-install-recommends -y ccache + - name: Build + run: | + cd ${{ matrix.directory }} + ./gradlew assembleDebug ++ env: ++ USE_CCACHE: 1 ++ CCACHE_COMPILERCHECK: "%compiler% -v" ++ - name: Pack cache ++ run: cd ~ && tar cJf ccache.tar.xz .cache/ccache + + mingw: + strategy: +@@ -230,7 +255,12 @@ jobs: + run: | + mkdir build + cd build +- cmake -GNinja -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DWERROR=ON -DCMAKE_TOOLCHAIN_FILE=../cmake/mingw-w64-toolchain.cmake ${{ matrix.flags }} .. ++ cmake .. -GNinja \ ++ -DCMAKE_C_COMPILER_LAUNCHER=ccache \ ++ -DCMAKE_CXX_COMPILER_LAUNCHER=ccache \ ++ -DWERROR=ON \ ++ -DCMAKE_TOOLCHAIN_FILE=../cmake/mingw-w64-toolchain.cmake \ ++ ${{ matrix.flags }} + cmake --build . + - name: Pack cache + run: cd ~ && tar cJf ccache.tar.xz .cache/ccache +@@ -261,7 +291,10 @@ jobs: + run: | + mkdir build + cd build +- /opt/devkitpro/portlibs/switch/bin/aarch64-none-elf-cmake -GNinja -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DWERROR=ON .. ++ /opt/devkitpro/portlibs/switch/bin/aarch64-none-elf-cmake .. -GNinja \ ++ -DCMAKE_C_COMPILER_LAUNCHER=ccache \ ++ -DCMAKE_CXX_COMPILER_LAUNCHER=ccache \ ++ -DWERROR=ON + /opt/devkitpro/portlibs/switch/bin/aarch64-none-elf-cmake --build . + - name: Pack cache + run: cd ~ && tar cJf ccache.tar.xz .cache/ccache +diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml +index f0e66f129..433e95838 100644 +--- a/.github/workflows/publish.yml ++++ b/.github/workflows/publish.yml +@@ -190,7 +190,7 @@ jobs: + keyStorePassword: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }} + alias: remcpe + env: +- BUILD_TOOLS_VERSION: 33.0.1 ++ BUILD_TOOLS_VERSION: 36.1.0 + - name: Rename APK + run: mv ${{ steps.sign_apk.outputs.signedReleaseFile }} ReMCPE.apk + - uses: alexellis/upload-assets@0.4.1 +diff --git a/build-wasm.sh b/build-wasm.sh +index 638906693..355bc8f56 100755 +--- a/build-wasm.sh ++++ b/build-wasm.sh +@@ -1,4 +1,4 @@ +-#!/bin/bash ++#!/bin/sh + + set -e + +@@ -19,7 +19,7 @@ git pull + + # Use Emscripten SDK + export EMSDK_QUIET=1 +-source ./emsdk_env.sh ++. ./emsdk_env.sh + + # Create Output Directory + cd ../ +diff --git a/platforms/sdl/sdl2/android/app/build.gradle b/platforms/sdl/sdl2/android/app/build.gradle +index 7d8bb604c..e41118bbf 100644 +--- a/platforms/sdl/sdl2/android/app/build.gradle ++++ b/platforms/sdl/sdl2/android/app/build.gradle +@@ -1,25 +1,31 @@ + apply plugin: 'com.android.application' + ++def useCcache = System.getenv("USE_CCACHE") == "1" ++ + android { +- compileSdk 34 ++ compileSdk 36 + defaultConfig { + applicationId 'io.github.reminecraftpe' + minSdkVersion 21 + //noinspection EditedTargetSdkVersion +- targetSdkVersion 34 ++ targetSdkVersion 36 + versionCode 1 + versionName '1.0' + externalNativeBuild { + cmake { +- arguments '-DANDROID_PLATFORM=android-21', '-DANDROID_STL=c++_static' ++ arguments '-DANDROID_PLATFORM=android-21', '-DANDROID_STL=c++_static', '-DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON' + abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64' ++ if (useCcache) { ++ arguments += '-DCMAKE_C_COMPILER_LAUNCHER=ccache' ++ arguments += '-DCMAKE_CXX_COMPILER_LAUNCHER=ccache' ++ } + } + } + } + buildTypes { + release { + minifyEnabled false +- proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' ++ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + applicationVariants.configureEach { variant -> +@@ -56,11 +62,11 @@ android { + externalNativeBuild { + cmake { + path '../../../../../CMakeLists.txt' +- version '3.22.1' ++ version = '3.22.1' + } + } + lint { +- abortOnError false ++ abortOnError = false + } +- namespace 'io.github.reminecraftpe' ++ namespace = 'io.github.reminecraftpe' + } +diff --git a/platforms/sdl/sdl2/android/build.gradle b/platforms/sdl/sdl2/android/build.gradle +index ab974e859..534feb40a 100644 +--- a/platforms/sdl/sdl2/android/build.gradle ++++ b/platforms/sdl/sdl2/android/build.gradle +@@ -6,7 +6,7 @@ buildscript { + google() + } + dependencies { +- classpath 'com.android.tools.build:gradle:8.1.2' ++ classpath 'com.android.tools.build:gradle:8.13.2' + + // NOTE: Do not place your application dependencies here; they belong + // in the individual module build.gradle files +@@ -20,6 +20,6 @@ allprojects { + } + } + +-task clean(type: Delete) { +- delete rootProject.buildDir ++tasks.register('clean', Delete) { ++ delete rootProject.layout.buildDirectory + } +diff --git a/platforms/sdl/sdl2/android/gradle.properties b/platforms/sdl/sdl2/android/gradle.properties +index 8450cacc3..833814d4b 100644 +--- a/platforms/sdl/sdl2/android/gradle.properties ++++ b/platforms/sdl/sdl2/android/gradle.properties +@@ -9,7 +9,6 @@ + + # Specifies the JVM arguments used for the daemon process. + # The setting is particularly useful for tweaking memory settings. +-android.defaults.buildfeatures.buildconfig=true + android.nonFinalResIds=false + android.nonTransitiveRClass=false + org.gradle.jvmargs=-Xmx1536m +diff --git a/platforms/sdl/sdl2/android/gradle/wrapper/gradle-wrapper.jar b/platforms/sdl/sdl2/android/gradle/wrapper/gradle-wrapper.jar +index 2b338a935b508eb6286ed4b84a91176ee5fb93c5..d64cd4917707c1f8861d8cb53dd15194d4248596 100644 +GIT binary patch +literal 43462 +zcma&NWl&^owk(X(xVyW%ySuwf;qI=D6|RlDJ2cR^yEKh!@I- +zp9QeisK*rlxC>+~7Dk4IxIRsKBHqdR9b3+fyL=ynHmIDe&|>O*VlvO+%z5;9Z$|DJ +zb4dO}-R=MKr^6EKJiOrJdLnCJn>np?~vU-1sSFgPu;pthGwf}bG +z(1db%xwr#x)r+`4AGu$j7~u2MpVs3VpLp|mx&;>`0p0vH6kF+D2CY0fVdQOZ@h;A` +z{infNyvmFUiu*XG}RNMNwXrbec_*a3N=2zJ|Wh5z* +z5rAX$JJR{#zP>KY**>xHTuw?|-Rg|o24V)74HcfVT;WtQHXlE+_4iPE8QE#DUm%x0 +zEKr75ur~W%w#-My3Tj`hH6EuEW+8K-^5P62$7Sc5OK+22qj&Pd1;)1#4tKihi=~8C +zHiQSst0cpri6%OeaR`PY>HH_;CPaRNty%WTm4{wDK8V6gCZlG@U3$~JQZ;HPvDJcT1V{ +z?>H@13MJcCNe#5z+MecYNi@VT5|&UiN1D4ATT+%M+h4c$t;C#UAs3O_q=GxK0}8%8 +z8J(_M9bayxN}69ex4dzM_P3oh@ZGREjVvn%%r7=xjkqxJP4kj}5tlf;QosR=%4L5y +zWhgejO=vao5oX%mOHbhJ8V+SG&K5dABn6!WiKl{|oPkq(9z8l&Mm%(=qGcFzI=eLu +zWc_oCLyf;hVlB@dnwY98?75B20=n$>u3b|NB28H0u-6Rpl((%KWEBOfElVWJx+5yg +z#SGqwza7f}$z;n~g%4HDU{;V{gXIhft*q2=4zSezGK~nBgu9-Q*rZ#2f=Q}i2|qOp +z!!y4p)4o=LVUNhlkp#JL{tfkhXNbB=Ox>M=n6soptJw-IDI|_$is2w}(XY>a=H52d +z3zE$tjPUhWWS+5h=KVH&uqQS=$v3nRs&p$%11b%5qtF}S2#Pc`IiyBIF4%A!;AVoI +zXU8-Rpv!DQNcF~(qQnyyMy=-AN~U>#&X1j5BLDP{?K!%h!;hfJI>$mdLSvktEr*89 +zdJHvby^$xEX0^l9g$xW-d?J;L0#(`UT~zpL&*cEh$L|HPAu=P8`OQZV!-}l`noSp_ +zQ-1$q$R-gDL)?6YaM!=8H=QGW$NT2SeZlb8PKJdc=F-cT@j7Xags+Pr*jPtlHFnf- +zh?q<6;)27IdPc^Wdy-mX%2s84C1xZq9Xms+==F4);O`VUASmu3(RlgE#0+#giLh-& +zcxm3_e}n4{%|X +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 +zF_*M4yi6J&Z4LQj65)S +zXwdM{SwUo%3SbPwFsHgqF@V|6afT|R6?&S;lw=8% +z3}@9B=#JI3@B*#4s!O))~z +zc>2_4Q_#&+5V`GFd?88^;c1i7;Vv_I*qt!_Yx*n=;rj!82rrR2rQ8u5(Ejlo{15P% +zs~!{%XJ>FmJ})H^I9bn^Re&38H{xA!0l3^89k(oU;bZWXM@kn$#aoS&Y4l^-WEn-fH39Jb9lA%s*WsKJQl?n9B7_~P +z-XM&WL7Z!PcoF6_D>V@$CvUIEy=+Z&0kt{szMk=f1|M+r*a43^$$B^MidrT0J;RI` +z(?f!O<8UZkm$_Ny$Hth1J#^4ni+im8M9mr&k|3cIgwvjAgjH +z8`N&h25xV#v*d$qBX5jkI|xOhQn!>IYZK7l5#^P4M&twe9&Ey@@GxYMxBZq2e7?`q +z$~Szs0!g{2fGcp9PZEt|rdQ6bhAgpcLHPz?f-vB?$dc*!9OL?Q8mn7->bFD2Si60* +z!O%y)fCdMSV|lkF9w%x~J*A&srMyYY3{=&$}H +zGQ4VG_?$2X(0|vT0{=;W$~icCI{b6W{B!Q8xdGhF|D{25G_5_+%s(46lhvNLkik~R +z>nr(&C#5wwOzJZQo9m|U<;&Wk!_#q|V>fsmj1g<6%hB{jGoNUPjgJslld>xmODzGjYc?7JSuA?A_QzjDw5AsRgi@Y|Z0{F{!1=!NES-#*f^s4l0Hu +zz468))2IY5dmD9pa*(yT5{EyP^G>@ZWumealS-*WeRcZ}B%gxq{MiJ|RyX-^C1V=0 +z@iKdrGi1jTe8Ya^x7yyH$kBNvM4R~`fbPq$BzHum-3Zo8C6=KW@||>zsA8-Y9uV5V +z#oq-f5L5}V<&wF4@X@<3^C%ptp6+Ce)~hGl`kwj)bsAjmo_GU^r940Z-|`<)oGnh7 +zFF0Tde3>ui?8Yj{sF-Z@)yQd~CGZ*w-6p2U<8}JO-sRsVI5dBji`01W8A&3$?}lxBaC&vn0E$c5tW* +zX>5(zzZ=qn&!J~KdsPl;P@bmA-Pr8T*)eh_+Dv5=Ma|XSle6t(k8qcgNyar{*ReQ8 +zTXwi=8vr>!3Ywr+BhggHDw8ke==NTQVMCK`$69fhzEFB*4+H9LIvdt-#IbhZvpS}} +zO3lz;P?zr0*0$%-Rq_y^k(?I{Mk}h@w}cZpMUp|ucs55bcloL2)($u%mXQw({Wzc~ +z;6nu5MkjP)0C(@%6Q_I_vsWrfhl7Zpoxw#WoE~r&GOSCz;_ro6i(^hM>I$8y>`!wW +z*U^@?B!MMmb89I}2(hcE4zN2G^kwyWCZp5JG>$Ez7zP~D=J^LMjSM)27_0B_X^C(M +z`fFT+%DcKlu?^)FCK>QzSnV%IsXVcUFhFdBP!6~se&xxrIxsvySAWu++IrH;FbcY$ +z2DWTvSBRfLwdhr0nMx+URA$j3i7_*6BWv#DXfym?ZRDcX9C?cY9sD3q)uBDR3uWg= +z(lUIzB)G$Hr!){>E{s4Dew+tb9kvToZp-1&c?y2wn@Z~(VBhqz`cB;{E4(P3N2*nJ +z_>~g@;UF2iG{Kt(<1PyePTKahF8<)pozZ*xH~U-kfoAayCwJViIrnqwqO}7{0pHw$ +zs2Kx?s#vQr7XZ264>5RNKSL8|Ty^=PsIx^}QqOOcfpGUU4tRkUc|kc7-!Ae6!+B{o~7nFpm3|G5^=0#Bnm6`V}oSQlrX(u%OWnC +zoLPy&Q;1Jui&7ST0~#+}I^&?vcE*t47~Xq#YwvA^6^} +z`WkC)$AkNub|t@S!$8CBlwbV~?yp&@9h{D|3z-vJXgzRC5^nYm+PyPcgRzAnEi6Q^gslXYRv4nycsy-SJu?lMps-? +zV`U*#WnFsdPLL)Q$AmD|0`UaC4ND07+&UmOu!eHruzV|OUox<+Jl|Mr@6~C`T@P%s +zW7sgXLF2SSe9Fl^O(I*{9wsFSYb2l%-;&Pi^dpv!{)C3d0AlNY6!4fgmSgj_wQ*7Am7&$z;Jg&wgR-Ih;lUvWS|KTSg!&s_E9_bXBkZvGiC6bFKDWZxsD$*NZ#_8bl +zG1P-#@?OQzED7@jlMJTH@V!6k;W>auvft)}g +zhoV{7$q=*;=l{O>Q4a@ +ziMjf_u*o^PsO)#BjC%0^h>Xp@;5$p{JSYDt)zbb}s{Kbt!T*I@Pk@X0zds6wsefuU +zW$XY%yyRGC94=6mf?x+bbA5CDQ2AgW1T-jVAJbm7K(gp+;v6E0WI#kuACgV$r}6L? +zd|Tj?^%^*N&b>Dd{Wr$FS2qI#Ucs1yd4N+RBUQiSZGujH`#I)mG&VKoDh=KKFl4=G +z&MagXl6*<)$6P}*Tiebpz5L=oMaPrN+caUXRJ`D?=K9!e0f{@D&cZLKN?iNP@X0aF +zE(^pl+;*T5qt?1jRC=5PMgV!XNITRLS_=9{CJExaQj;lt!&pdzpK?8p>%Mb+D +z?yO*uSung=-`QQ@yX@Hyd4@CI^r{2oiu`%^bNkz+Nkk!IunjwNC|WcqvX~k=><-I3 +zDQdbdb|!v+Iz01$w@aMl!R)koD77Xp;eZwzSl-AT +zr@Vu{=xvgfq9akRrrM)}=!=xcs+U1JO}{t(avgz`6RqiiX<|hGG1pmop8k6Q+G_mv +zJv|RfDheUp2L3=^C=4aCBMBn0aRCU(DQwX-W(RkRwmLeuJYF<0urcaf(=7)JPg<3P +zQs!~G)9CT18o!J4{zX{_e}4eS)U-E)0FAt}wEI(c0%HkxgggW;(1E=>J17_hsH^sP +z%lT0LGgbUXHx-K*CI-MCrP66UP0PvGqM$MkeLyqHdbgP|_Cm!7te~b8p+e6sQ_3k| +zVcwTh6d83ltdnR>D^)BYQpDKlLk3g0Hdcgz2}%qUs9~~Rie)A-BV1mS&naYai#xcZ +z(d{8=-LVpTp}2*y)|gR~;qc7fp26}lPcLZ#=JpYcn3AT9(UIdOyg+d(P5T7D&*P}# +zQCYplZO5|7+r19%9e`v^vfSS1sbX1c%=w1;oyruXB%Kl$ACgKQ6=qNWLsc=28xJjg +zwvsI5-%SGU|3p>&zXVl^vVtQT3o-#$UT9LI@Npz~6=4!>mc431VRNN8od&Ul^+G_kHC`G=6WVWM +z%9eWNyy(FTO|A+@x}Ou3CH)oi;t#7rAxdIXfNFwOj_@Y&TGz6P_sqiB`Q6Lxy|Q{`|fgmRG(k+!#b*M+Z9zFce)f-7;?Km5O=LHV9f9_87; +zF7%R2B+$?@sH&&-$@tzaPYkw0;=i|;vWdI|Wl3q_Zu>l;XdIw2FjV=;Mq5t1Q0|f< +zs08j54Bp`3RzqE=2enlkZxmX6OF+@|2<)A^RNQpBd6o@OXl+i)zO%D4iGiQNuXd+zIR{_lb96{lc~bxsBveIw6umhShTX+3@ZJ=YHh@ +zWY3(d0azg;7oHn>H<>?4@*RQbi>SmM=JrHvIG(~BrvI)#W(EAeO6fS+}mxxcc+X~W6&YVl86W9WFSS}Vz-f9vS?XUDBk)3TcF +z8V?$4Q)`uKFq>xT=)Y9mMFVTUk*NIA!0$?RP6Ig0TBmUFrq*Q-Agq~DzxjStQyJ({ +zBeZ;o5qUUKg=4Hypm|}>>L=XKsZ!F$yNTDO)jt4H0gdQ5$f|d&bnVCMMXhNh)~mN +z@_UV6D7MVlsWz+zM+inZZp&P4fj=tm6fX)SG5H>OsQf_I8c~uGCig$GzuwViK54bcgL;VN|FnyQl>Ed7(@>=8$a_UKIz|V6CeVSd2(P +z0Uu>A8A+muM%HLFJQ9UZ5c)BSAv_zH#1f02x?h9C}@pN@6{>UiAp>({Fn(T9Q8B +z^`zB;kJ5b`>%dLm+Ol}ty!3;8f1XDSVX0AUe5P#@I+FQ-`$(a;zNgz)4x5hz$Hfbg +z!Q(z26wHLXko(1`;(BAOg_wShpX0ixfWq3ponndY+u%1gyX)_h=v1zR#V}#q{au6; +z!3K=7fQwnRfg6FXtNQmP>`<;!N137paFS%y?;lb1@BEdbvQHYC{976l`cLqn;b8lp +zIDY>~m{gDj(wfnK!lpW6pli)HyLEiUrNc%eXTil|F2s(AY+LW5hkKb>TQ3|Q4S9rr +zpDs4uK_co6XPsn_z$LeS{K4jFF`2>U`tbgKdyDne`xmR<@6AA+_hPNKCOR-Zqv;xk +zu5!HsBUb^!4uJ7v0RuH-7?l?}b=w5lzzXJ~gZcxRKOovSk@|#V+MuX%Y+=;14i*%{)_gSW9(#4%)AV#3__kac1|qUy!uyP{>?U#5wYNq}y$S9pCc +zFc~4mgSC*G~j0u#qqp9 +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 +zPtI_m%g$`kL_fVUk9J@>EiBH +zOO&jtn~&`hIFMS5S`g8w94R4H40mdNUH4W@@XQk1sr17b{@y|JB*G9z1|CrQjd+GX +z6+KyURG3;!*BQrentw{B2R&@2&`2}n(z-2&X7#r!{yg@Soy}cRD~j +zj9@UBW+N|4HW4AWapy4wfUI- +zZ`gSL6DUlgj*f1hSOGXG0IVH8HxK?o2|3HZ;KW{K+yPAlxtb)NV_2AwJm|E)FRs&& +z=c^e7bvUsztY|+f^k7NXs$o1EUq>cR7C0$UKi6IooHWlK_#?IWDkvywnzg&ThWo^? +z2O_N{5X39#?eV9l)xI(>@!vSB{DLt*oY!K1R8}_?%+0^C{d9a%N4 +zoxHVT1&Lm|uDX%$QrBun5e-F`HJ^T$ +zmzv)p@4ZHd_w9!%Hf9UYNvGCw2TTTbrj9pl+T9%-_-}L(tES>Or-}Z4F*{##n3~L~TuxjirGuIY#H7{%$E${?p{Q01 +zi6T`n;rbK1yIB9jmQNycD~yZq&mbIsFWHo|ZAChSFPQa<(%d8mGw*V3fh|yFoxOOiWJd(qvVb!Z$b88cg->N=qO*4k~6;R==|9ihg&riu#P~s4Oap9O7f%crSr^rljeIfXDEg>wi)&v*a%7zpz<9w +z*r!3q9J|390x`Zk;g$&OeN&ctp)VKRpDSV@kU2Q>jtok($Y-*x8_$2piTxun81@vt +z!Vj?COa0fg2RPXMSIo26T=~0d`{oGP*eV+$!0I<(4azk&Vj3SiG=Q!6mX0p$z7I}; +z9BJUFgT-K9MQQ-0@Z=^7R<{bn2Fm48endsSs`V7_@%8?Bxkqv>BDoVcj?K#dV#uUP +zL1ND~?D-|VGKe3Rw_7-Idpht>H6XRLh*U7epS6byiGvJpr%d}XwfusjH9g;Z98H`x +zyde%%5mhGOiL4wljCaWCk-&uE4_OOccb9c!ZaWt4B(wYl!?vyzl%7n~QepN&eFUrw +zFIOl9c({``6~QD+43*_tzP{f2x41h(?b43^y6=iwyB)2os5hBE!@YUS5?N_tXd=h( +z)WE286Fbd>R4M^P{!G)f;h<3Q>Fipuy+d2q-)!RyTgt;wr$(?9ox3;q+{E*ZQHhOn;lM`cjnu9 +zXa48ks-v(~b*;MAI<>YZH(^NV8vjb34beE<_cwKlJoR;k6lJNSP6v}uiyRD?|0w+X@o1ONrH8a$fCxXpf? +z?$DL0)7|X}Oc%h^zrMKWc-NS9I0Utu@>*j}b@tJ=ixQSJ={4@854wzW@E>VSL+Y{i +z#0b=WpbCZS>kUCO_iQz)LoE>P5LIG-hv9E+oG}DtlIDF>$tJ1aw9^LuhLEHt?BCj& +z(O4I8v1s#HUi5A>nIS-JK{v!7dJx)^Yg%XjNmlkWAq2*cv#tHgz`Y(bETc6CuO1VkN^L-L3j_x<4NqYb5rzrLC-7uOv +z!5e`GZt%B782C5-fGnn*GhDF$%(qP<74Z}3xx+{$4cYKy2ikxI7B2N+2r07DN;|-T->nU&!=Cm#rZt%O_5c&1Z%nlWq3TKAW0w +zQqemZw_ue--2uKQsx+niCUou?HjD`xhEjjQd3%rrBi82crq*~#uA4+>vR<_S{~5ce +z-2EIl?~s +z1=GVL{NxP1N3%=AOaC}j_Fv=ur&THz +zyO!d9kHq|c73kpq`$+t+8Bw7MgeR5~`d7ChYyGCBWSteTB>8WAU(NPYt2Dk`@#+}= +zI4SvLlyk#pBgVigEe`?NG*vl7V6m+<}%FwPV=~PvvA)=#ths==DRTDEYh4V5}Cf$z@#;< +zyWfLY_5sP$gc3LLl2x+Ii)#b2nhNXJ{R~vk`s5U7Nyu^3yFg&D%Txwj6QezMX`V(x +z=C`{76*mNb!qHHs)#GgGZ_7|vkt9izl_&PBrsu@}L`X{95-2jf99K)0=*N)VxBX2q +z((vkpP2RneSIiIUEnGb?VqbMb=Zia+rF~+iqslydE34cSLJ&BJW^3knX@M;t*b=EA +zNvGzv41Ld_T+WT#XjDB840vovUU^FtN_)G}7v)1lPetgpEK9YS^OWFkPoE{ovj^=@ +zO9N$S=G$1ecndT_=5ehth2Lmd1II-PuT~C9`XVePw$y8J#dpZ?Tss<6wtVglm(Ok7 +z3?^oi@pPio6l&!z8JY(pJvG=*pI?GIOu}e^EB6QYk$#FJQ%^AIK$I4epJ+9t?KjqA+bkj&PQ*|vLttme+`9G=L% +ziadyMw_7-M)hS(3E$QGNCu|o23|%O+VN7;Qggp?PB3K-iSeBa2b}V4_wY`G1Jsfz4 +z9|SdB^;|I8E8gWqHKx!vj_@SMY^hLEIbSMCuE?WKq=c2mJK +z8LoG-pnY!uhqFv&L?yEuxo{dpMTsmCn)95xanqBrNPTgXP((H$9N${Ow~Is-FBg%h +z53;|Y5$MUN)9W2HBe2TD`ct^LHI<(xWrw}$qSoei?}s)&w$;&!14w6B6>Yr6Y8b)S +z0r71`WmAvJJ`1h&poLftLUS6Ir +zC$bG9!Im_4Zjse)#K=oJM9mHW1{%l8sz$1o?ltdKlLTxWWPB>Vk22czVt|1%^wnN@*!l)}?EgtvhC>vlHm^t+ogpgHI1_$1ox9e;>0!+b(tBrmXRB`PY1vp-R**8N7 +zGP|QqI$m(Rdu#=(?!(N}G9QhQ%o!aXE=aN{&wtGP8|_qh+7a_j_sU5|J^)vxq;# +zjvzLn%_QPHZZIWu1&mRAj;Sa_97p_lLq_{~j!M9N^1yp3U_SxRqK&JnR%6VI#^E12 +z>CdOVI^_9aPK2eZ4h&^{pQs}xsijXgFYRIxJ~N7&BB9jUR1fm!(xl)mvy|3e6-B3j +zJn#ajL;bFTYJ2+Q)tDjx=3IklO@Q+FFM}6UJr6km7hj7th9n_&JR7fnqC!hTZoM~T +zBeaVFp%)0cbPhejX<8pf5HyRUj2>aXnXBqDJe73~J%P(2C?-RT{c3NjE`)om! +zl$uewSgWkE66$Kb34+QZZvRn`fob~Cl9=cRk@Es}KQm=?E~CE%spXaMO6YmrMl%9Q +zlA3Q$3|L1QJ4?->UjT&CBd!~ru{Ih^in&JXO=|<6J!&qp +zRe*OZ*cj5bHYlz!!~iEKcuE|;U4vN1rk$xq6>bUWD*u(V@8sG^7>kVuo(QL@Ki;yL +zWC!FT(q{E8#on>%1iAS0HMZDJg{Z{^!De(vSIq&;1$+b)oRMwA3nc3mdTSG#3uYO_ +z>+x;7p4I;uHz?ZB>dA-BKl+t-3IB!jBRgdvAbW!aJ(Q{aT>+iz?91`C-xbe)IBoND +z9_Xth{6?(y3rddwY$GD65IT#f3<(0o#`di{sh2gm{dw*#-Vnc3r=4==&PU^hCv$qd +zjw;>i&?L*Wq#TxG$mFIUf>eK+170KG;~+o&1;Tom9}}mKo23KwdEM6UonXgc +z!6N(@k8q@HPw{O8O!lAyi{rZv|DpgfU{py+j(X_cwpKqcalcqKIr0kM^%Br3SdeD> +zHSKV94Yxw;pjzDHo!Q?8^0bb%L|wC;4U^9I#pd5O&eexX+Im{ +z?jKnCcsE|H?{uGMqVie_C~w7GX)kYGWAg%-?8|N_1#W-|4F)3YTDC+QSq1s!DnOML3@d`mG%o2YbYd#jww|jD$gotpa)kntakp#K;+yo-_ZF9qrNZw<%#C +zuPE@#3RocLgPyiBZ+R_-FJ_$xP!RzWm|aN)S+{$LY9vvN+IW~Kf3TsEIvP+B9Mtm! +zpfNNxObWQpLoaO&cJh5>%slZnHl_Q~(-Tfh!DMz(dTWld@LG1VRF`9`DYKhyNv +z2pU|UZ$#_yUx_B_|MxUq^glT}O5Xt(Vm4Mr02><%C)@v;vPb@pT$*yzJ4aPc_FZ3z +z3}PLoMBIM>q_9U2rl^sGhk1VUJ89=*?7|v`{!Z{6bqFMq(mYiA?%KbsI~JwuqVA9$H5vDE+VocjX+G^%bieqx->s;XWlKcuv(s%y%D5Xbc9+ +zc(_2nYS1&^yL*ey664&4`IoOeDIig}y-E~_GS?m;D!xv5-xwz+G`5l6V+}CpeJDi^ +z%4ed$qowm88=iYG+(`ld5Uh&>Dgs4uPHSJ^TngXP_V6fPyl~>2bhi20QB%lSd#yYn +zO05?KT1z@?^-bqO8Cg`;ft>ilejsw@2%RR7;`$Vs;FmO(Yr3Fp`pHGr@P2hC%QcA|X&N2Dn +zYf`MqXdHi%cGR@%y7Rg7?d3?an){s$zA{!H;Ie5exE#c~@NhQUFG8V=SQh%UxUeiV +zd7#UcYqD=lk-}sEwlpu&H^T_V0{#G?lZMxL7ih_&{(g)MWBnCZxtXg +znr#}>U^6!jA%e}@Gj49LWG@*&t0V>Cxc3?oO7LSG%~)Y5}f7vqUUnQ;STjdDU}P9IF9d9<$;=QaXc +zL1^X7>fa^jHBu_}9}J~#-oz3Oq^JmGR#?GO7b9a(=R@fw@}Q{{@`Wy1vIQ#Bw?>@X +z-_RGG@wt|%u`XUc%W{J +z>iSeiz8C3H7@St3mOr_mU+&bL#Uif;+Xw-aZdNYUpdf>Rvu0i0t6k*}vwU`XNO2he +z%miH|1tQ8~ZK!zmL&wa3E;l?!!XzgV#%PMVU!0xrDsNNZUWKlbiOjzH-1Uoxm8E#r`#2Sz;-o&qcqB +zC-O_R{QGuynW14@)7&@yw1U}uP(1cov)twxeLus0s|7ayrtT8c#`&2~Fiu2=R;1_4bCaD=*E@cYI>7YSnt)nQc +zohw5CsK%m?8Ack)qNx`W0_v$5S}nO|(V|RZKBD+btO?JXe|~^Qqur%@eO~<8-L^9d +z=GA3-V14ng9L29~XJ>a5k~xT2152zLhM*@zlp2P5Eu}bywkcqR;ISbas&#T#;HZSf +z2m69qTV(V@EkY(1Dk3`}j)JMo%ZVJ*5eB +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 +zg|^VPf5c6-!FxN{25dvVh#fog=NNpXz +zfB$o+0jbRkHH{!TKhE709f+jI^$3#v1Nmf80w`@7-5$1Iv_`)W^px8P-({xwb;D0y +z7LKDAHgX<84?l!I*Dvi2#D@oAE^J|g$3!)x1Ua;_;<@#l1fD}lqU2_tS^6Ht$1Wl} +zBESo7o^)9-Tjuz$8YQSGhfs{BQV6zW7dA?0b(Dbt=UnQs&4zHfe_sj{RJ4uS-vQpC +zX;Bbsuju4%!o8?&m4UZU@~ZZjeFF6ex2ss5_60_JS_|iNc+R0GIjH1@Z +z=rLT9%B|WWgOrR7IiIwr2=T;Ne?30M!@{%Qf8o`!>=s<2CBpCK_TWc(DX51>e^xh8 +z&@$^b6CgOd7KXQV&Y4%}_#uN*mbanXq(2=Nj`L7H7*k(6F8s6{FOw@(DzU`4-*77{ +zF+dxpv}%mFpYK?>N_2*#Y?oB*qEKB}VoQ@bzm>ptmVS_EC(#}Lxxx730trt0G)#$b +zE=wVvtqOct1%*9}U{q<)2?{+0TzZzP0jgf9*)arV)*e!f`|jgT{7_9iS@e)recI#z +zbzolURQ+TOzE!ymqvBY7+5NnAbWxvMLsLTwEbFqW=CPyCsmJ}P1^V30|D5E|p3BC5 +z)3|qgw@ra7aXb-wsa|l^in~1_fm{7bS9jhVRkYVO#U{qMp +z)Wce+|DJ}4<2gp8r0_xfZpMo#{Hl2MfjLcZdRB9(B(A(f;+4s*FxV{1F|4d`*sRNd +zp4#@sEY|?^FIJ;tmH{@keZ$P(sLh5IdOk@k^0uB^BWr@pk6mHy$qf&~rI>P*a;h0C{%oA*i!VjWn&D~O#MxN&f@1Po# +zKN+ +zrGrkSjcr?^R#nGl<#Q722^wbYcgW@{+6CBS<1@%dPA8HC!~a`jTz<`g_l5N1M@9wn9GOAZ>nqNgq!yOCbZ@1z`U_N`Z>}+1HIZxk*5RDc&rd5{3qjRh8QmT$VyS;jK +z;AF+r6XnnCp=wQYoG|rT2@8&IvKq*IB_WvS%nt%e{MCFm`&W*#LXc|HrD?nVBo=(8*=Aq?u$sDA_sC_RPDUiQ+wnIJET8vx$&fxkW~kP9qXKt +zozR)@xGC!P)CTkjeWvXW5&@2?)qt)jiYWWBU?AUtzAN}{JE1I)dfz~7$;}~BmQF`k +zpn11qmObXwRB8&rnEG*#4Xax3XBkKlw(;tb?Np^i+H8m(Wyz9k{~ogba@laiEk;2! +zV*QV^6g6(QG%vX5Um#^sT&_e`B1pBW5yVth~xUs#0}nv?~C#l?W+9Lsb_5)!71rirGvY +zTIJ$OPOY516Y|_014sNv+Z8cc5t_V=i>lWV=vNu#!58y9Zl&GsMEW#pPYPYGHQ|;vFvd*9eM==$_=vc7xnyz0~ +zY}r??$<`wAO?JQk@?RGvkWVJlq2dk9vB(yV^vm{=NVI8dhsX<)O(#nr9YD?I?(VmQ +z^r7VfUBn<~p3()8yOBjm$#KWx!5hRW)5Jl7wY@ky9lNM^jaT##8QGVsYeaVywmpv>X|Xj7gWE1Ezai&wVLt3p)k4w~yrskT-!PR!kiyQlaxl(( +zXhF%Q9x}1TMt3~u@|#wWm-Vq?ZerK={8@~&@9r5JW}r#45#rWii};t`{5#&3$W)|@ +zbAf2yDNe0q}NEUvq_Quq3cTjcw +z@H_;$hu&xllCI9CFDLuScEMg|x{S7GdV8<&Mq=ezDnRZAyX-8gv97YTm0bg=d)(>N +z+B2FcqvI9>jGtnK%eO%y +zoBPkJTk%y`8TLf4)IXPBn`U|9>O~WL2C~C$z~9|0m*YH<-vg2CD^SX#&)B4ngOSG$ +zV^wmy_iQk>dfN@Pv(ckfy&#ak@MLC7&Q6Ro#!ezM*VEh`+b3Jt%m(^T&p&WJ2Oqvj +zs-4nq0TW6cv~(YI$n0UkfwN}kg3_fp?(ijSV#tR9L0}l2qjc7W?i*q01=St0eZ=4h +zyGQbEw`9OEH>NMuIe)hVwYHsGERWOD;JxEiO7cQv%pFCeR+IyhwQ|y@&^24k+|8fD +zLiOWFNJ2&vu2&`Jv96_z-Cd5RLgmeY3*4rDOQo?Jm`;I_(+ejsPM03!ly!*Cu}Cco +zrQSrEDHNyzT(D5s1rZq!8#?f6@v6dB7a-aWs(Qk>N?UGAo{gytlh$%_IhyL7h?DLXDGx +zgxGEBQoCAWo-$LRvM=F5MTle`M})t3vVv;2j0HZY&G +z22^iGhV@uaJh(XyyY%} +zd4iH_UfdV#T=3n}(Lj^|n;O4|$;xhu*8T3hR1mc_A}fK}jfZ7LX~*n5+`8N2q#rI$ +z@<_2VANlYF$vIH$ +zl<)+*tIWW78IIINA7Rr7i{<;#^yzxoLNkXL)eSs=%|P>$YQIh+ea_3k +z_s7r4%j7%&*NHSl?R4k%1>Z=M9o#zxY!n8sL5>BO-ZP;T3Gut>iLS@U%IBrX6BA3k +z)&@q}V8a{X<5B}K5s(c(LQ=%v1ocr`t$EqqY0EqVjr65usa=0bkf|O#ky{j3)WBR(((L^wmyHRzoWuL2~WTC=`yZ +zn%VX`L=|Ok0v7?s>IHg?yArBcync5rG#^+u)>a%qjES%dRZoIyA8gQ;StH +z1Ao7{<&}6U=5}4v<)1T7t!J_CL%U}CKNs-0xWoTTeqj{5{?Be$L0_tk>M9o8 +zo371}S#30rKZFM{`H_(L`EM9DGp+Mifk&IP|C2Zu_)Ghr4Qtpmkm1osCf@%Z$%t+7 +zYH$Cr)Ro@3-QDeQJ8m+x6%;?YYT;k6Z0E-?kr>x33`H%*ueBD7Zx~3&HtWn0?2Wt} +zTG}*|v?{$ajzt}xPzV%lL1t-URi8*Zn)YljXNGDb>;!905Td|mpa@mHjIH%VIiGx- +zd@MqhpYFu4_?y5N4xiHn3vX&|e6r~Xt> +zZG`aGq|yTNjv;9E+Txuoa@A(9V7g?1_T5FzRI;!=NP1Kqou1z5?%X~Wwb{trRfd>i +z8&y^H)8YnKyA_Fyx>}RNmQIczT?w2J4SNvI{5J&}Wto|8FR(W;Qw#b1G<1%#tmYzQ +zQ2mZA-PAdi%RQOhkHy9Ea#TPSw?WxwL@H@cbkZwIq0B!@ns}niALidmn&W?!Vd4Gj +zO7FiuV4*6Mr^2xlFSvM;Cp_#r8UaqIzHJQg_z^rEJw&OMm_8NGAY2)rKvki|o1bH~ +z$2IbfVeY2L(^*rMRU1lM5Y_sgrDS`Z??nR2lX;zyR=c%UyGb*%TC-Dil?SihkjrQy~TMv6;BMs7P8il`H7DmpVm@rJ;b)hW)BL)GjS154b*xq-NXq2cwE +z^;VP7ua2pxvCmxrnqUYQMH%a%nHmwmI33nJM(>4LznvY*k&C0{8f*%?zggpDgkuz&JBx{9mfb@wegEl2v!=}Sq2Gaty0<)UrOT0{MZtZ~j5y&w +zXlYa_jY)I_+VA-^#mEox#+G>UgvM!Ac8zI<%JRXM_73Q!#i3O|)lOP*qBeJG#BST0 +zqohi)O!|$|2SeJQo(w6w7%*92S})XfnhrH_Z8qe!G5>CglP=nI7JAOW?(Z29;pXJ9 +zR9`KzQ=WEhy*)WH>$;7Cdz|>*i>=##0bB)oU0OR>>N<21e4rMCHDemNi2LD>Nc$;& +zQRFthpWniC1J6@Zh~iJCoLOxN`oCKD5Q4r%ynwgUKPlIEd#?QViIqovY|czyK8>6B +zSP%{2-<;%;1`#0mG^B(8KbtXF;Nf>K#Di72UWE4gQ%(_26Koiad)q$xRL~?pN71ZZ +zujaaCx~jXjygw;rI!WB=xrOJO6HJ!!w}7eiivtCg5K|F6$EXa)=xUC +za^JXSX98W`7g-tm@uo|BKj39Dl;sg5ta;4qjo^pCh~{-HdLl6qI9Ix6f$+qiZ$}s= +zNguKrU;u+T@ko(Vr1>)Q%h$?UKXCY>3se%&;h2osl2D +zE4A9bd7_|^njDd)6cI*FupHpE3){4NQ*$k*cOWZ_?CZ>Z4_fl@n(mMnYK62Q1d@+I +zr&O))G4hMihgBqRIAJkLdk(p(D~X{-oBUA+If@B}j& +zsHbeJ3RzTq96lB7d($h$xTeZ^gP0c{t!Y0c)aQE;$FY2!mACg!GDEMKXFOPI^)nHZ +z`aSPJpvV0|bbrzhWWkuPURlDeN%VT8tndV8?d)eN*i4I@u +zVKl^6{?}A?P)Fsy?3oi#clf}L18t;TjNI2>eI&(ezDK7RyqFxcv%>?oxUlonv(px) +z$vnPzRH`y5A(x!yOIfL0bmgeMQB$H5wenx~!ujQK*nUBW;@Em&6Xv2%s(~H5WcU2R +z;%Nw<$tI)a`Ve!>x+qegJnQsN2N7HaKzrFqM>`6R*gvh%O*-%THt +zrB$Nk;lE;z{s{r^PPm5qz(&lM{sO*g+W{sK+m3M_z=4=&CC>T`{X}1Vg2PEfSj2x_ +zmT*(x;ov%3F?qoEeeM>dUn$a*?SIGyO8m806J1W1o+4HRhc2`9$s6hM#qAm +zChQ87b~GEw{ADfs+5}FJ8+|bIlIv(jT$Ap#hSHoXdd9#w<#cA<1Rkq^*EEkknUd4& +zoIWIY)sAswy6fSERVm&!SO~#iN$OgOX*{9@_BWFyJTvC%S++ilSfCrO(?u=Dc?CXZ +zzCG&0yVR{Z`|ZF0eEApWEo#s9osV>F{uK{QA@BES#&;#KsScf>y +zvs?vIbI>VrT<*!;XmQS=bhq%46-aambZ(8KU-wOO2=en~D}MCToB_u;Yz{)1ySrPZ +z@=$}EvjTdzTWU7c0ZI6L8=yP+YRD_eMMos}b5vY^S*~VZysrkq<`cK3>>v%uy7jgq +z0ilW9KjVDHLv0b<1K_`1IkbTOINs0=m-22c%M~l=^S}%hbli-3?BnNq?b`hx^HX2J +zIe6ECljRL0uBWb`%{EA=%!i^4sMcj+U_TaTZRb+~GOk +z^ZW!nky0n*Wb*r+Q|9H@ml@Z5gU&W`(z4-j!OzC1wOke`TRAYGZVl$PmQ16{3196( +zO*?`--I}Qf(2HIwb2&1FB^!faPA2=sLg(@6P4mN)>Dc3i(B0;@O-y2;lM4akD>@^v +z=u>*|!s&9zem70g7zfw9FXl1bpJW(C#5w#uy5!V?Q(U35A~$dR%LDVnq@}kQm13{} +zd53q3N(s$Eu{R}k2esbftfjfOITCL;jWa$}(mmm}d(&7JZ6d3%IABCapFFYjdEjdK +z&4Edqf$G^MNAtL=uCDRs&Fu@FXRgX{*0<(@c3|PNHa>L%zvxWS={L8%qw`STm+=Rd +zA}FLspESSIpE_^41~#5yI2bJ=9`oc;GIL!JuW&7YetZ?0H}$$%8rW@*J37L-~Rsx!)8($nI4 +zZhcZ2^=Y+p4YPl%j!nFJA|*M^gc(0o$i3nlphe+~-_m}jVkRN{spFs(o0ajW@f3K{ +zDV!#BwL322CET$}Y}^0ixYj2w>&Xh12|R8&yEw|wLDvF!lZ#dOTHM9pK6@Nm-@9Lnng4ZHBgBSrr7KI8YCC9DX5Kg|`HsiwJHg2(7#nS;A{b3tVO?Z% +za{m5b3rFV6EpX;=;n#wltDv1LE*|g5pQ+OY&*6qCJZc5oDS6Z6JD#6F)bWxZSF@q% +z+1WV;m!lRB!n^PC>RgQCI#D1br_o^#iPk>;K2hB~0^<~)?p}LG%kigm@moD#q3PE+ +zA^Qca)(xnqw6x>XFhV6ku9r$E>bWNrVH9fum0?4s?Rn2LG{Vm_+QJHse6xa%nzQ?k +zKug4PW~#Gtb;#5+9!QBgyB@q=sk9=$S{4T>wjFICStOM?__fr+Kei1 +z3j~xPqW;W@YkiUM;HngG!;>@AITg}vAE`M2Pj9Irl4w1fo4w<|Bu!%rh%a(Ai^Zhi +zs92>v5;@Y(Zi#RI*ua*h`d_7;byQSa*v9E{2x$<-_=5Z<7{%)}4XExANcz@rK69T0x3%H<@frW>RA8^swA+^a(FxK| +zFl3LD*ImHN=XDUkrRhp6RY5$rQ{bRgSO*(vEHYV)3Mo6Jy3puiLmU&g82p{qr0F?ohmbz)f2r{X2|T2 +z$4fdQ=>0BeKbiVM!e-lIIs8wVTuC_m7}y4A_%ikI;Wm5$9j(^Y +z(cD%U%k)X>_>9~t8;pGzL6L-fmQO@K; +zo&vQzMlgY95;1BSkngY)e{`n0!NfVgf}2mB3t}D9@*N;FQ{HZ3Pb%BK6;5#-O|WI( +zb6h@qTLU~AbVW#_6?c!?Dj65Now7*pU{h!1+eCV^KCuPAGs28~3k@ueL5+u|Z-7}t +z9|lskE`4B7W8wMs@xJa{#bsCGDFoRSNSnmNYB&U7 +zVGKWe%+kFB6kb)e;TyHfqtU6~fRg)f|>=5(N36)0+C +z`hv65J<$B}WUc!wFAb^QtY31yNleq4dzmG`1wHTj=c*=hay9iD071Hc?oYoUk|M*_ +zU1GihAMBsM@5rUJ(qS?9ZYJ6@{bNqJ`2Mr+5#hKf?doa?F|+^IR!8lq9)wS3tF_9n +zW_?hm)G(M+MYb?V9YoX^_mu5h-LP^TL^!Q9Z7|@sO(rg_4+@=PdI)WL(B7`!K^ND- +z-uIuVDCVEdH_C@c71YGYT^_Scf_dhB8Z2Xy6vGtBSlYud9vggOqv^L~F{BraSE_t} +zIkP+Hp2&nH^-MNEs}^`oMLy11`PQW$T|K(`Bu*(f@)mv1-qY(_YG&J2M2<7k;;RK~ +zL{Fqj9yCz8(S{}@c)S!65aF<=&eLI{hAMErCx&>i7OeDN>okvegO87OaG{Jmi<|}D +zaT@b|0X{d@OIJ7zvT>r+eTzgLq~|Dpu)Z&db-P4z*`M$UL51lf>FLlq6rfG)%doyp +z)3kk_YIM!03eQ8Vu_2fg{+osaEJPtJ-s36R+5_AEG12`NG)IQ#TF9c@$99%0iye+ +zUzZ57=m2)$D(5Nx!n)=5Au&O0BBgwxIBaeI(mro$#&UGCr<;C{UjJVAbVi%|+WP(a +zL$U@TYCxJ=1{Z~}rnW;7UVb7+ZnzgmrogDxhjLGo>c~MiJAWs&&;AGg@%U?Y^0JhL +ze(x6Z74JG6FlOFK(T}SXQfhr}RIFl@QXKnIcXYF)5|V~e-}suHILKT-k|<*~Ij|VF +zC;t@=uj=hot~*!C68G8hTA%8SzOfETOXQ|3FSaIEjvBJp(A)7SWUi5!Eu#yWgY+;n +zlm<$+UDou*V+246_o#V4kMdto8hF%%Lki#zPh}KYXmMf?hrN0;>Mv%`@{0Qn`Ujp) +z=lZe+13>^Q!9zT);H<(#bIeRWz%#*}sgUX9P|9($kexOyKIOc`dLux}c$7It4u|Rl +z6SSkY*V~g_B-hMPo_ak>>z@AVQ(_N)VY2kB3IZ0G(iDUYw+2d7W^~(Jq}KY=JnWS( +z#rzEa&0uNhJ>QE8iiyz;n2H|SV#Og+wEZv=f2%1ELX!SX-(d3tEj$5$1}70Mp<&eI +zCkfbByL7af=qQE@5vDVxx1}FSGt_a1DoE3SDI+G)mBAna)KBG4p8Epxl9QZ4BfdAN +zFnF|Y(umr;gRgG6NLQ$?ZWgllEeeq~z^ZS7L?<(~O&$5|y)Al^iMKy}&W+eMm1W +z7EMU)u^ke(A1#XCV>CZ71}P}0x)4wtHO8#JRG3MA-6g=`ZM!FcICCZ{IEw8Dm2&LQ +z1|r)BUG^0GzI6f946RrBlfB1Vs)~8toZf~7)+G;pv&XiUO(%5bm)pl=p>nV^o*;&T +z;}@oZSibzto$arQgfkp|z4Z($P>dTXE{4O=vY0!)kDO* +zGF8a4wq#VaFpLfK!iELy@?-SeRrdz%F*}hjKcA*y@mj~VD3!it9lhRhX}5YOaR9$} +z3mS%$2Be7{l(+MVx3 +z(4?h;P!jnRmX9J9sYN#7i=iyj_5q7n#X(!cdqI2lnr8T$IfOW<_v`eB!d9xY1P=2q&WtOXY=D9QYteP)De?S4}FK6#6Ma +z=E*V+#s8>L;8aVroK^6iKo=MH{4yEZ_>N-N +z`(|;aOATba1^asjxlILk<4}f~`39dBFlxj>Dw(hMYKPO3EEt1@S`1lxFNM+J@uB7T +zZ8WKjz7HF1-5&2=l=fqF-*@>n5J}jIxdDwpT?oKM3s8Nr`x8JnN-kCE?~aM1H!hAE +z%%w(3kHfGwMnMmNj(SU(w42OrC-euI>Dsjk&jz3ts}WHqmMpzQ3vZrsXrZ|}+MHA7 +z068obeXZTsO*6RS@o3x80E4ok``rV^Y3hr&C1;|ZZ0|*EKO`$lECUYG2gVFtUTw)R +z4Um<0ZzlON`zTdvVdL#KFoMFQX*a5wM0Czp%wTtfK4Sjs)P**RW&?lP$(<}q%r68Z +zS53Y!d@&~ne9O)A^tNrXHhXBkj~$8j%pT1%%mypa9AW5E&s9)rjF4@O3ytH{0z6riz|@< +zB~UPh*wRFg2^7EbQrHf0y?E~dHlkOxof_a?M{LqQ^C!i2dawHTPYUE=X@2(3<=OOxs8qn_(y>pU>u^}3y&df{JarR0@VJn0f+U%UiF=$Wyq +zQvnVHESil@d|8&R<%}uidGh7@u^(%?$#|&J$pvFC-n8&A>utA=n3#)yMkz+qnG3wd +zP7xCnF|$9Dif@N~L)Vde3hW8W!UY0BgT2v(wzp;tlLmyk2%N|0jfG$%<;A&IVrOI< +z!L)o>j>;dFaqA3pL}b-Je(bB@VJ4%!JeX@3x!i{yIeIso^=n?fDX`3bU=eG7sTc%g%ye8$v8P@yKE^XD=NYxTb +zbf!Mk=h|otpqjFaA-vs5YOF-*GwWPc7VbaOW&stlANnCN8iftFMMrUdYNJ_Bnn5Vt +zxfz@Ah|+4&P;reZxp;MmEI7C|FOv8NKUm8njF7Wb6Gi7DeODLl&G~}G4be&*Hi0Qw +z5}77vL0P+7-B%UL@3n1&JPxW^d@vVwp?u#gVcJqY9#@-3X{ok#UfW3<1fb%FT`|)V~ggq +z(3AUoUS-;7)^hCjdT0Kf{i}h)mBg4qhtHHBti=~h^n^OTH5U*XMgDLIR@sre`AaB$ +zg)IGBET_4??m@cx&c~bA80O7B8CHR7(LX7%HThkeC*@vi{-pL%e)yXp!B2InafbDF +zjPXf1mko3h59{lT6EEbxKO1Z5GF71)WwowO6kY|6tjSVSWdQ}NsK2x{>i|MKZK8%Q +zfu&_0D;CO-Jg0#YmyfctyJ!mRJp)e#@O0mYdp|8x;G1%OZQ3Q847YWTyy|%^cpA;m +zze0(5p{tMu^lDkpe?HynyO?a1$_LJl2L&mpeKu%8YvgRNr=%2z${%WThHG=vrWY@4 +zsA`OP#O&)TetZ>s%h!=+CE15lOOls&nvC~$Qz0Ph7tHiP;O$i|eDwpT{cp>+)0-|; +zY$|bB+Gbel>5aRN3>c0x)4U=|X+z+{ +zn*_p*EQoquRL+=+p;=lm`d71&1NqBz&_ph)MXu(Nv6&XE7(RsS)^MGj5Q?Fwude-(sq +zjJ>aOq!7!EN>@(fK7EE#;i_BGvli`5U;r!YA{JRodLBc6-`n8K+Fjgwb%sX;j=qHQ +z7&Tr!)!{HXoO<2BQrV9Sw?JRaLXV8HrsNevvnf>Y-6|{T!pYLl7jp$-nEE +z#X!4G4L#K0qG_4Z;Cj6=;b|Be$hi4JvMH!-voxqx^@8cXp`B??eFBz2lLD8RRaRGh +zn7kUfy!YV~p(R|p7iC1Rdgt$_24i0cd-S8HpG|`@my70g^y`gu%#Tf_L21-k?sRRZHK&at(*ED0P8iw{7?R$9~OF$Ko;Iu5)ur5<->x!m93Eb +zFYpIx60s=Wxxw=`$aS-O&dCO_9?b1yKiPCQmSQb>T)963`*U+Ydj5kI(B(B?HNP8r +z*bfSBpSu)w(Z3j7HQoRjUG(+d=IaE~tv}y14zHHs|0UcN52fT8V_<@2ep_ee{QgZG +zmgp8iv4V{k;~8@I%M3<#B;2R>Ef(Gg_cQM7%}0s*^)SK6!Ym+~P^58*wnwV1BW@eG +z4sZLqsUvBbFsr#8u7S1r4teQ;t)Y@jnn_m5jS$CsW1um!p&PqAcc8!zyiXHVta9QC +zY~wCwCF0U%xiQPD_INKtTb;A|Zf29(mu9NI;E +zc-e>*1%(LSXB`g}kd`#}O;veb<(sk~RWL|f3ljxCnEZDdNSTDV6#Td({6l&y4IjKF +z^}lIUq*ZUqgTPumD)RrCN{M^jhY>E~1pn|KOZ5((%F)G|*ZQ|r4zIbrEiV%42hJV8 +z3xS)=!X1+=olbdGJ=yZil?oXLct8FM{(6ikLL3E%=q#O6(H$p~gQu6T8N!plf!96| +z&Q3=`L~>U0zZh;z(pGR2^S^{#PrPxTRHD1RQOON&f)Siaf`GLj#UOk&(|@0?zm;Sx +ztsGt8=29-MZs5CSf1l1jNFtNt5rFNZxJPvkNu~2}7*9468TWm>nN9TP&^!;J{-h)_ +z7WsHH9|F%I`Pb!>KAS3jQWKfGivTVkMJLO-HUGM_a4UQ_%RgL6WZvrW+Z4ujZn;y@ +zz9$=oO!7qVTaQAA^BhX&ZxS*|5dj803M=k&2%QrXda`-Q#IoZL6E(g+tN!6CA!CP* +zCpWtCujIea)ENl0liwVfj)Nc<9mV%+e@=d`haoZ*`B7+PNjEbXBkv=B+Pi^~L#EO$D$ZqTiD8f<5$eyb54-(=3 +zh)6i8i|jp(@OnRrY5B8t|LFXFQVQ895n*P16cEKTrT*~yLH6Z4e*bZ5otpRDri&+A +zfNbK1D5@O=sm`fN=WzWyse!za5n%^+6dHPGX#8DyIK>?9qyX}2XvBWVqbP%%D)7$= +z=#$WulZlZR<{m#gU7lwqK4WS1Ne$#_P{b17qe$~UOXCl>5b|6WVh;5vVnR<%d+Lnp +z$uEmML38}U4vaW8>shm6CzB(Wei3s#NAWE3)a2)z@i{4jTn;;aQS)O@l{rUM`J@K& +l00vQ5JBs~;vo!vr%%-k{2_Fq1Mn4QF81S)AQ99zk{{c4yR+0b! + +literal 54213 +zcmaI7W3XjgkTrT(b!^+VZQHhOvyN@swr$(CZTqW^?*97Se)qi=aD0)rp{0Dyr3n3s!40Q|jx{^R!d0{?5$!b<$q;xZz%zyNapa2&?V6XpHup!C=N +zhX0SFG{20vh_Ip(jkL&v^yGw;BsI+(v?Mjf^yEx~0^K6x?$P}u^{Dui^c1By6(GcU +zuu<}1p$2&?Dsk~)p}}Z>6UIf_t;3xI;Q!-+n*Zy~K>j|^*1_~2FZI8DApgt9)Is0K +z%J~1+74e_0t`7QkcE%3>uaNcc5Wo>I0D#25{$&3iqWYhq!fwWf&Q7)tG=^6Cj*dyH +zVV;O9@IO^?RPO3fqiD7CVF17a@${~(@kp48o9}Yem=+7e>XMe8VU@@g$h%DD0v?5D +z+Ut$@U9uh{je2vf;M{rAHy=Ddu|8Su9hE8ud5;e#FWa4IFBu0@lbT)kIjFk7YO#M{ +z_UhnpU=OAk&ToalWXHkwGoip`@1`{c+$_;-A@{BrvWGd1n0C?8BkXAcUB}hJ9ifTd +zXmGZt20UMPJ>A`K9d~etf4lL_aN-^=h4i~6pTIuc#?fUTya6@joGghByrRwEp6ns& +zd&Qr~-rb(T@gNSHuKk&*dp$9}97J6mjOctPsOd%;PFee`sqIx2e8rg2HGO8p@5D2N +zJx=u&A7;Ik{?$cw0C8-bXwMvJD{jWVnSq0IeuaU4jg5tdi++wN3k_ZD5gaT^Ec7l@ +zUa~ZunVxelrCFSv!-1zS-V#TvVX@6od@PY3xJ>bsOEg6JdG +z+K!pzxd`b3k$evQeZqTUAhmZe`x3ix`C8_(`>+zEQ}shDw<}~?e3?djT#2A`La?}p +zj0O5dtyyJ+H +zqUrjYQ4e+(l4AV;pwtqZhOgYr#WFAg%Zl0&ZaMV`k(g8ES^@~SDWgW;9h;u?1=8tr +zhoiZ1-y%eL8TN8Sq8K9aFLZim@CgG=D^T~Tb1d}pBShxg|^!mT2n2oXVB%PHm=LAMNN;P01p$$>)?(n+ +zt_o!OLxHg0))2ibNbMM!m!??fAwd5`p_L`rU%9-n#*mCH`H8X7;!s8YR +z7UZrCqE{W1h4i|mwPjfp^5UjtSi?jhvKz5ei8I0GPZvW3W6GIH3k2?0@tX3AV4CQ< +z{qH~842S+LBE*9hht~B2_$?~!>HbXJ(&b;_>F%_5bd|ftS_R))_HLz#Esy9^RdnFu +zBgJN*TpZ%VLaeq_Hqj=~1P{T;OVbKxRz>M$deAaQe4Xw1sB`Oqv5TsUHs%!O)+OCZF->(mdYr{;SrB4(jk^ +zHZpUJJoL;_=KPKz?N{}Y^7pJ`JJ&N>Z +z4bP_VWj74DedTUNKgk1mDPJK;g;5OgKb8A-ZeQTO^LBGyQvwBnMU;rV9wTj}MRDg$ +zt|n+v8Y6kiEZ0i2U!2cO(&cn2?=X_Hr!>#iWxdijtGQ0swst>+l#{rbdxYt6E`)(l +zK-pE2f}6?2XA9U0G`_uiH`uQ_pKQsee6%AMX{ncaa3&0wZcrqdm$>LNm6iVfiptW$ +zyn7m8(>gfzQYFKW{#7zG9^e^<0i0MFp*w9&FtQoL=-)E%@@9Cc +zP9_=$*WH0TVDAK@nl1s3ydV_122<5;>(ebz*twQa8o!2`5X@d4!f}PF8SWX5KQ<#O +zZuQ|zNK9)>s#<)>#vY)+HTMZch!(bdU^n;EbZH+w)yAsxiM?!Iz9LKQaW76UeCfZ& +z&G{g4dIN;I!b>@<2;XBvbcCH!LUaV3T0*)*P6u#2xaYWWJO~LkbIq~$aHvhr*O_SX +zx4chD#{l!&zjREIRfzCw^;)IPsP~fnr3%Vm`Gyh-~&Y^4uB1MIJgVi9;LZdkV +z;tGh}#^1RI=8T+!GDV6U_DZU8wUcFcLli|4uc)bS%8C-A%%PD+f~?Q(%_TW)0`NvR +z)12Xrfts1p=!g*RQ4@C>tJZR5clwKQ*@H@hGwcIRlg6vo5%{FB88gio7O9D-+%d&0 +z88=QAENyEV-ZX`EH9c>0KW}#-rJiyvuVq|ZO+gFPZf$Rv-B=@dX3*w4^SSj5J@fQ! +zyC%Z-xP)EC?5lHyq#n4UCeO7-b*}O80=5`}y2v@X#xL5mk8=;U#Z-IJ>cn`b_W5KE +z0CM~Q;A0JfZpNUVt}7Ba0OqS2fQ$$co!Dhg^Hs9>#H-mGEY9I_uQA^8j`@An8P0IV)J8- +zy=Fx~&V9q*kLhS%Fn}efjT>!tx9S08$5~Sujy`}~Wv58)7+=-SAo(hE0+24Ok0-gn +zDneFf@Xcl`&(3wUx?cyqL?>3gxsW9~sZ05O(GKM5b^N_0gKg|Ym@ZzM=4I}()T{vg +zrx$~)J*tpI0N$#&Ey_zDF~7gE$)>m(Ij*=`;-$>MU(O~yDbLqqCM$i7pAi$c`N7oJ +zdW!qfp6-&3jDMiev3r3X_wb=9q=YLZ;Chc-ij#gUZjQfuvasr__nS{NKdAbBw +zSDX-k4sib4(T^BnR5U0 +zmtS(P^I{9gar_Fr)O1zXpI|rtduN&QQ%za5UiEwLWQlkA@FBy)z5_LB_0?d~u%ATI +z=&Ne#|M)~xcXC8A5=2)8zUGE5S7LS{HSs4~AZ{Ih=Pp=_0Bd!!YT8I+Wj_lwPAzR6 +zpC*s$B>OJ-0{##F`wysfa;fH6{ucyo{567q2SegQwyri-w)#f@34?^A`XKu0pn`uU +z&yJDcJ0WzQ4DLEBAb|Ph9(7t6SR^>lop>^S7xeejx%YRDg-tV;;U^L$S0<$n8I>TczV;H-4&5 +zT?qE8Wh52_lPc7X-{!*wGh_7M8q&5&tUV`2v=T*r7aS{w@Y%`zZVN=wny{91zFK{> +zy6N=={^v>!Ud<^_e*pkszyJV{{QFAf^qtK39UYCW4Xlj+8}zBX>0Xp`1 +zhMan0#!`s*faP1m*3$dQl+6eriIhV!0w|3r7okb@9rbyt9&OS$l-%>}FWw2uahtQU +z51v1z%{yz_lDVNIZ~Qk?p6RR)SvQjzEkEBg7e7FDFh7xdT#JZI0K}&V`w}< +zsKW1!;WMM3YiKeDjtpKpL)OT;q5Bc^M7Ih^x(G+K6Sv6pkIHe~C_^j8-y%pmk^7qT +zUYI-ZC$yq>TV&m&q`E41-pIUic2@0;)u^P>BTZ9H;g-o%pc>2dP@egvoY8w^Y|idJ +zRt_E(&gS|SK2PITHWtqM_B@=9>ik~s!9I#JNX`|p>bZawbmhCZLSqhETMj8t219ao +zMm9drVP#=M?`4Fbnlq?T#3Qvei7dhctcJ-9F&V-EBm^<($!9tWv)Nc$Dsbs!M`bQf +z>y43Vf}fDD##=1L*jB-t&x +zZVPr;U3yaKpab^Ek8cvubvkv@uAB)QAFNLUmK)VdrW;N6pb&;!btC7C%kAPrpa02d1c|Oku&)PqpeJo(Q^Yqz{aX +zwfQ4OM$+(OzPlm>p=%MVdvklYGFuiQ2U +zm(W$DcEay%?jVKdU +zk06WOukkk%?Yl!sQ<+^@&8ktWZZp4pYjDk1B0ok{8I!iEr9&)Mu5JbIVG#cD+jvE*J7r-E?MQ<%4G4G`tU(9*$7eXT%)KXb$%^bJIqk3)f!GHu^U3FG`}z +z5*qT@rr07#9n_Q;%Zbu6S+PO8=!A#unVJ|I80$N>;M!hX8t%flkZ7(vH#uUji6`72 +zAE!j~(RAe?BR`X~F~@HWFwMZQffSth&eD&$r~d_sJD@h9(%Dnha$nohXX#lcZ}yYSa6@i6&aNd=PJiVs`JzkN0P8>}?&4(-C28Wwp_$Kj6H_R5{ch}o{u!yvk +ziY8X1E2udz4&M*NTO<(Z4)&E@l+f*>Cr;!?j7LI;zWPbaU6yHEGOygY0ykb1Yymb? +z7`(tNL=)_iS3RmbhXHd%(5xW%TU7%&wZ9g19U0e&yKy6xfV&deCr8JGX5H&245TR#;AZ$ZrRyRc=NhTka0F +zE(C)7ZH#x0-{o$0q%9GH5}#0vOFzM;Z%~Tg)!v&?_E@674X=&rKhEB$ydk?^=)b`{ +z=tE*|g?_)KjR4hU7IlUa)27F9yu(v@o^Bix!^ZMRT9da_JAGOq1IIjV^Rsm&*xXd@ +zZ$azq>oCbOC@wT+5*{>U{{;FrU*|#MK5>;Us`V+?jJ}=0z1EjeyLgO_2)5yi_w^R> +zaA2*YpF?u14h=xZkfNibNv7JHMHVKk9yD%`O2}?m!e;AZS=qDsDc2Y<=@8t}s_(gq +z@BR>HBZ`nL+!-MU)ZnGJM>J2g(Yp^i1?%b_1v@YT) +zb@oI~8=DSUb|;&&zE|gMCZFzIz4OpGM>1zh7KGPE7AE}ZxTg(w?WW$k1BOB_VQs~{ +zB?zK3pgi1ZwR^Op9q^fM9hP${R+Ba-8%?YjDny#OpWAkYTwRG-$Guen2jy2h^M%;a +z45j9D1Qj{qr?$0;dMq--dyT1Q +zRP=pw0f)|e|K4k{*`lrc&ZGAEvJe=wA%BTPt*GcQ^#}oBlO=PL9noEQGu3Yb!j0sV +znN^-RcL;?V+cxAHj3L~xE3G?cpDpYubLvLQYF44x7?#8#L+?U4SOWr^?7s^3(X~s +z%p~`biV0K9W+<%UiXXq2TLKCW_cS67S^bSVh*b!m_fs<32}rK`F!*53Bj!(siro(! +znHl{eD?PLoXsmvHU_mx2y~^mV-j%!X-hzvq-LT**r9#_X%-3Q+)jMhg^Hp+MwAW@1 +zv|(uAnmuRW9r*#*JC3#6-r=|4itP&tB_Ppzoq@8{YSdI?T@61b%jEHTb#;?e_pt}* +zA)6PevRssjAMI4Z +zDSI3|iA14*Dw%?Ho{H`E7R#DO;Sb%N16^Qr#heT@o2&raa9+KaN6!oP8a6o3n1eXX +zLma;mu>gyBau7dETp8)Hw^L(`XH$cwR`lvp1z$Pe2v#*=T$aZt9*XRd2w`r7M>nbs#qq8*v)eA)1U+rP4NW+42jP+P480(80rJ +zW%u&i%#&A%2cvw59mZ}m1_q0d6=okiJ2eg?d$1%Q7d#IWzB;i0Z6#;eluVel4g%p6 +zXi!lybOy_Upf2!m(%MX`nRRZi#L+DOfhDp*uybli@s6b3X1_GOV@J3o-P6WE54?+! +zdaK)7Dc@_A6U9^t2IFTX6vVcfxdD5$>v>&CVh3GCFMfUQV**; +z!W)R4dkC13NlYnE8pvxGujq$orL9fsq${&dVzzbU^9K?)9Ot-J)Jl<&f`Ei!?>ja< +z&0M)=MU65R;zJx*IIu)LB}ENBLbygQfB1Y!y+Ktyi&ZVB#SW_FR{ax${qq;$E49d6 +z;WEPT=^YWzAo(XI=x4~)M-N-T2U$4UG&ppEkiEoX0iMfV@7fW*2s7aIrT#sH +zF{ZDcPee;m$W!(f*%osjh_3>pJ3v2gbLR42VYe`5Jpq1KtGZuX5F-hDZ$WSk@MDd) +zXXl8E^S*wLR9+Om{42ZKSV(TLcl#e)(TcSZHiV8}Vu7@jGRRqDHwFalcMfLVISgBw +zGy7T>byEB4Nxw5;_oTjX|Jm(X;90~r0s;W200RK9{d)!bN4G~LWoxK!C1mdC# +z>|}0h^IxRDf~F)UKhpQK$<~rng?&@=x@Mz$sO81_zNREU0tkL%5DKmrnN&Q!O#2#i +zf^@`>M4#Mk9&azMG8bd;d?}pQYMSE*jpOP>52`Of=THUvq+S&mtgQ6oB-V^~=c7Ey +zt2Ogzj8YEW&S`iKfr@%(4Z@qxW;vzw?Y$v$=_MQsM%witHuZW~q_6qhjU=`&{M+5O +z9-ilvkj1b&u2T7ZOkmgf|cRsdlxFgQK#UMv&f*VLyvem7U&n9E@eaiR4vUe=EKBt!`1hVgqQ +zmdb~my*pk-J~J*mBn&~mG9#*W7+VC`x6G4EPOMfhT2W0xxkpTyM~^^(M-z~j$O{-0 +zs*p7K$wlKuSSoz}FiK++Ln@TFfT+l1L&5@^MjU~!*Twak08I>c85=^jSPR9q)LQSs2!5vFzQ3~zGU=`whAtT?(orpa$#q_1 +zp!jv7j0T|$>DEFSs#q?$ +zbgvQ43cUA(eB%^&uQTd{CbqrFrJ37byNTctGXJ5#OiHI442Ah7@WuVp+a+ga%Ti{r +zM+1zu{NU)BLTPe4Dj$aL3KQ42oy5HaN9)b#bB;uTK@Ov!R)}#jYM^Fixkc_WIcnP> +zpx%0t3l=kdM{AdE85ag=#h$cTYGc`)2@NV +zKs41wCN9OTw>rK;N~ZCqU!lN;Y2d~X@ETKgc7_%WXV7C( +z?P@%os#ef5?Dv)*nyOhS+91RkL)C>xWra(4ACw6;-#8r77t<62T-<+!R=R&rFhzGqCu8fs+4WbC +zbTT(~6w|l)D`x%|#T2EYsi>)p^vxp9hL1Jg#U!R#*c7O#Kr2SvNP$Fz3`7i8q;rm+ +zNfHw5xIZQiX#4c8p^IgD9$*VI%{IN5LN^-e{UTbnBSUbwJZ@C~yl(03dDYa@v?BBU +z{t?3q*coc;eL7U=PmX&|cQ)WGMVWfnM;K-MmaC^CL!i)+w`&dR2yyIf)?bJ!&rTy& +zM>Zslt3)O4RtZ1hRsv6{mb9O|d032U$+J1!q0mV>^nvisPk6m62%7Hi?AN@iVdd`e +zJ-t8QPcZZ-b%+w>n6d6noj5-!M0UIyoCXHTB&}{TJSSx;ENSfQ_YOY5lxYc6-P;@f +z$8&r=x5<5)?#ax>QoALk=_!#WK;53YDSs_E6E(_))Z7Rp_=JiRUSf4!L;}`&LxZDg +zBX8Aac&-J-Is$QIma!pSyk!b$9kE^U44Dm=tk6;|51p_m3Z^?9wX+mEkeGk&=66^tnGsmq3S9Gxk#5L+Ir*U=-wVlNS7R-XxxJbsK3x_jRBs;X#^)WGNM#ffO3{r!#~XdQAN@vI9@nFWi%Y +zBS6yf2rcWj;Xlyyg!&dT%O;U$rjFkGsokkbO$Q!+q1h!$Tx2Qbt)ZwO8Zj?vOAO-I +zMR?T)z*;-<5#WB+B}y71ofQOrg%Ew6{h6HA-GlwjjS`w-tb>lTy_9sEeYkZIEgxH$GV| +ziVTMZHZ@BG<=T;I{3gggUj$ICBg%D1Tu4}Z>A;|7Wjx*LjVg@bY_=l +z@Wx$?*VudeRP>JmH2~co{%B~lemZ$As_uXcvE3HI#j2|TX4cez4^g*Z9Nd0E!LLvJ +zL}rm^kq}V_v)z@J-_D!V$UPrpI35Kdaw{-%LXTz5$5!0jSy#2RxhizCM!`wcwO*ymdhcAcPrf1x}?5HX)-|T1} +zP^fRGqOoDW?b&(X>4WW=<_TeOg*lJZ-Rxne4(pPj-p6!t)h|v=LmNTZqvbJ4sr3;W +z!r@mNdGHEg(Vu>Q>%W24?5ai{i)$G)@;D(NAJXVQ!nvlc3pydLiF#OfhNs*zkT2N< +zP>P-rH#J7vKkl1q^;uE{;qExD&`HwCQ_1wXCapJl0qT}Ki|BYh=>Btm%c?+oUHnT1 +zu)zL*O9VEKPWo0>MD+g&nzH^<4@j!$KC;gY6DEJ)H0(6Z=0sMhpds_*!2KY=tp!u~ +zFaHejM`P~eqdD{wwo46;)fW{y-7As2-;s1*<3`E9) +zj3iEoA7$a*h}cfzc!8kKI5n;>u20$kp@@hFiq@}Q%qva_fsK&FG=VMTfxtZ<5w}lN +zc+arjs~!<|gp}h>+)E-@meh`aFh_j9;Z+MECq*yeRRBnq__mRYhj0LO=vz`;;JZH9 +zl$on!j}k)L6slvy6juE0cjr#(cxi;$6qqLoq`B1xGOyrVZ4@+j!bxa&CMw@eG@}xDaNGoqU3RputERQN%a}LT*+R +zD+J!EK#PUE%`$B2(dwYPz-d^(xyV!VBID6i;$bNUsc;lqC8pmxkD$TURMab>!;bs! +z&_(A$F={!jt5ky=j>`;3vn3MJA~-{#RVGu2CVKvd4Y_GHy>)%4TSt24Nhu{5_sSFU +zGS%kGn}YWZriRonR2!ojJx)6s+higW^#Rqkpm&>qO5AJ?<749adish}G@qe@74Hb- +z^!?brxA2p+=p1Z=ZxFGLT0@(mi41*-M~&r{Pz*@V-mwjvv?Cs)_XQfwp%o{tn3@Z; +zUYo41BHa-e^y>i_Y)<>0=oh_|XnwDN?sUPkR}vg0wGD}aFXRcD)a>X8H~x{9TWb~j +z2v3a>S0nCFRA(>LorODZbRWF>l)oH1@BAGD4f$YmBGk;vouT^|;%B1##Z%z}^!}YG +zhEMeY>T6N7?p}Scs?#S%&zwDI14nslxxUN@b7%Qpd-P6t&W_)r4!6~MF|CXk1G@7~PhZtFv!GDR3oSZ$wc*(=F+{y~kKy_Xx~hThKCO(Y1(d-A9mNtAC?)`Ob*Vbn5I$P1>!U4FH|i3kNwY^8y*Zp +zko^)3el8ig*1E$S>&>L9)6^&djWlF}RUuT$!HdvY^drp^=bPKrwIdj@AE5WURf0HW +zrdBV|JVhcRh(T{79hwU5wM_n${Lk@qqS`p0)Po4)i~6=dJGGjTs5uCfC=zHJTsJycXbi1`-|>XFE-G~>DxFsB!%!&Oo4z_^waNjSh43rbUlR){LMDdKoA`tvbH?Ed7xh((Y9^o;1DR;7Cj}88`2X-Xj4P+a)Gh^^~D#Wd?^3V*^>j&*W +zYvPTEM#{}!%)j&(^Hcvj<`=NFb^6OD=-Wx_o7*Tl={q?658zjKT~LAhMw&<_6hbit +z{4EBBKR9imC}A#c2GI%*lF4TX#+-*V)a?RNpE%Ayw1wLK0(-lj(w&T&k*w(PzV186 +zE5NB*k6>$;p6Qsf)|19b`1AGoVhW(sC(9tL6ub@^+H~RYq6&F+zLJTqLJcJE +zWhSd;V^ZfPC5bePXn6Wd3$#fYEnV@Yx>kp{d>1hwj|(qOqU-dBl+-T$V-Tn%a5bu$ +zhRQ5EcOv%H)YdC$TKBIhlNqT=`-ull^=5O+V)^)7dGh=8ILR_&!j3+w-;;KYss2Xongwkj(@ay7vH8n`GU6eA=)`gWN^pzz +zX>TUvQj+z@>QSr?{)V9H#sUOvL{7BV?E|)gr}Gf8Z?UlN3TE@^YkN>bvN{k9&o0T< +zV>XuU6Ma?Vo1u9df7dP#43tIk3ZE&B*1?ExS2yScgWwq{MRlO&T?B7$o*wuR=u3H( +z=o9pk{LOI~qSp}G +z)ki?n9BRtwaJDHS#K`3!Q@~($VQhoXz@vf@L-~rsdpqlcMEA|>8J%v*TH7A_+3fBe +zq{Ao6q_Xonq9KY{t5UpJgGiBxeO3wN2L2H>HEWw@t#WnMK0C++x)xoh%E$e+1l_Jv=S +z32+|8nKPuuOv?;bL_j^2_uyMX)(tI0-4bZdL +zGuBZx^QLcf-Z^hMus}KPjW|V`{w{tlKSId;h8#|Mk;{JsH)9MNDXIa6;fu0x7)Zpz +zDS1iR!=66}m5{L~WcMaQefcI|iz#na;lz0Tl=y4Ir_t}g4{O!>vTM;4C{{TSU_S)4 +ziMF!tp7Kbw`ER7~u;4-w#$QR!wp|ISzJtC+wQg9Uz}zlDW(qiiWi&!YKLCLo;1V8> +zc*FFydcjoS`>cT`LI(!VVraMTjg(Pk)pJ +zrmV9EEs31VlOa=HIPSLb!o?C7jDouHni5sU4F5b&$kL~#L0wfC_=4^gm666|b572MjQs +zL~P{DD;&?h)bWtTA14!^&c_M8fm +zw-U4h7Z)$@%7BF3%^Up7iE$ls<4k(hyc~ez3HJA*83=eav!+aVml5l?H&xB4AYDjo +zg6cOjwl#M%os(r$P@|Cq204dQl0s0sUkGVSj`;dkL;?sn(22B1p=?Xaig9AB^pp9t +zD>2xDJ@Cdlp~G=|mEZ=>5Qe +zP5-Y{8kF#1J1>Vc(vvbmQA0m$CzXnr1tF{&Y)elPYy=LE3vNR4QI(icEoq*I6!jDC +z8-y`5i2DirSrB>B42_`H5SyLtc*CCaK;irS{SLhgCz~L)YXX#FN9ngwN+KUXC8Qn7 +zDX^JjhsPf`s}~wm^2-%{6?|Zwae!g-1gh>_{3=z)+OrqEUVC7_reuJ}b-T!f!nYNSkQ4?wR&ktwh5%!f +zXDjUkE&x7Ed9hr-VFc +z58{Bz_B@1^28e3NqS$w);rh0iTJcb>R$~tG{@&1nZW#HrXqQtEg!dQtZ0^|_(m|oG +zNaiEdvbf0@hd`hYpTajdNs15NeNrVDi&!${K7|pqgt(99R|auJXporuZ@eZy_ +zdi-ZZ=2r#C(GC>WaL>gnEbvd*&-~pEh7VE8x9G^v`DFQ`)-M7&Gb$7wRfa`2}YwtJ@ +z>BXDMa!xISdxyLLS#7?nmtL;#<+aeK5^qa$3s@k-^kk$odB!hn*J97%reX${7todQ +zBdZoqIqYl1*;bt9W2Os?!&ZQ3g%atB|IjAvMg$7XHe +zV+Z;PR~IQTkoQdTa&6|+>GgrPHt`K^eQA?Ke3|iaDK#67YRL>hUl!@i>Z+30T1Wg0 +z`%3b4PwDY7nG)0c>MkTX=YV0BGW8q^dN3<1@74C)p4n*^mGU7 +znDz`y@v!&AtG6>NaTiBw+PaIL)OyGJ*h%ULjsx`_mj;#K;lr&-gt7o5%W2PMPqSef +z_taY1$7)HvWsFvb_259 +zIG3P1+z!mjia#+mPb=^CA67;xmE78h%AZ5z#hVfiRa{K23hC;JmGhD~@dKu$mXUx#p&`BuShfqjsi1=S+FiushWf%9hqSukXa~7$sDxgF8-drUqqk|k!yMZ%IcEi(k8*?lx~ +zVzsQn!Ph0AazfTio4!2ZnlT}_ss_(8%qs0MT^;XkLO)5g>+E$POk4Q1R`JSnCEO5= +z`*h!yDTrB|znNib9Ey{HrjX~sYN^w+Ou*4{Ji8$_!w5nEX9%+R-!Zt;s0V%!1l;X;xT$pp{^%?saKshn# +z+st|6o`iZ{rDvyXRbk&W89vfVSDCTSJ}f-lq_pW9oG@N5a_!}K4N&EFOQ+BjyrVdZy|DCX1)xe@ +zb7qU_#6RO1T@OP)Y@`S<>Jw_;hvLAk9v1&HY_XDBQS0Ed^x$o1jm$CzqT9{f`fbuR +ztKq<16NNub9Jrd9vE;pAbLQymi1Y3TH=|QLG*=DX7JRuVS@BqEWoGFkS;x5^`*Fe+ +z!(H|%IX}m;UJa@bNCm4eX*UA>K8TlMA7=>&zMLc`S$or`?kt`k7e#UgR>$Se#WmUk<8At4j36`;2Y?Q&(o^D2?wfDs$`As +z!m=wf?s#dX7ieHHw(InLidG(sf^)T8e!ohlFcuJs|7xiDq#X}rE}(h!jB?ctznaTw +zW{7b@bvFBx6~R=O9lM9tTOssqTb?&ye%ApyQ==amDcr8gtvK=2Nh>^lbcT3W +z5JSFMF|(x|_VR(pq5Hf}V!%Ud?)QsXz&!1ul*VkX$$YTL^jn);z1|;7@g^3mc91KG +zbXq~#a8M>~fBCs>DT-Z@(^Z)&&P0)hQPp`eJKSs1rbKob3-O-vhCj%lsiYg69H_Mp +zWvw#d7yDergTfJ1N062Mw7bR_d5trqj{@5wA +zTD|4jv&M}?w1&>{;RBFrj9B2vwauin+wkC2df0W=S91h@x9_1Uy}@F+f5c?%o}QCm +zPPsi6YzPq|PeHAut}QIw5JhP86#-Yc{GDa@)^Cr2nzclj(6`(FTx44^FEeu+U9o6H +zHMROwpFP; +z$Lzi1G<>&_%n%+s%HGfOf0FC$2I=-9KT!$1I}C%`F{-x28ldWLO_{9)Sg%Uvdb-ue +z_`VdN{ze;U(T8g}K!U*^%JQ5QMSVoP2JA!-z16@PDqs`g7JPN=|6&rkrNJ6$Kr0x= +z_saCl+1o~Kdr`jjPQXc_nbZe;>IUqI{Ec44x=^jF-)X++`U`ds+35vD}J5^;pTVg2M14sFx3$ZcU#rSYmg*K_y +zfGcdsR^!%-FEB3j?xuith}9>uaGOnOR+r!xt>NuMAdmhJh}E6#ra!=5D0Z>-s$-#3 +zf#9iy*yI8X!gwWV>x`4(OxP~f6hpAdeH?1PF7SLpdYNK9VSQAKUh|jaukRJBQZM%b +znnoG!>27>A0b5|5gJF?pF|RGXP(mP2aj&6ZN1x&VR>p>z+0u7yWOF5B@pO9Yvh|4I +z!0(x|tuDcKMAv^Sb#nhb`;d^m!xt`k=LwRR(RBC14ryl! +z{LROHg{4n@%`GO{1MWMj?(cplnmGpaXotQXf}H4OOKMy{7%ae7CJhD%CGzY?B{fMoC|1M02`>PF_5%g_qv +z#PFrk+~^7#M{06?W`Otboufh0QZ|W%z +z>_gw(z%)GgUxtaR)~35-6ah+kbr9@_R($dg2~u!xBG!8SnV=d@@26B&XuP2RWHWwQ+ii^s0Q#nz(1-H|vZXr&Qvw+SL# +zsNkw-T#0O14`ExFBPW4GgBBA5KCMo2*@Z1FOr+T*lPfctK +zWa0+c56Xine#z#v2q3rOO@qXUAN5vMsfeK +zWRtdrv`~ceQlmyO{DsCeQ#B8cq?*R$dZlpu?2!eDF46WnJ18Pvkg<}-%Y@4o_Fz8R_zJx56w@fmD|HIfj +zMQPS0``%e;+qP}H(zb2eS(%l#ZQH7}ZQHiZlW)Jhzumk0>~EZlHO6zXZq_qn#*By= +z|6dpsG4bTO5=-};R;xo%1BSqeBxlta85gs6koVjRvWu2cRmmFDX +z2AWx31wXM!8jb=fiUJmu8Ww06R|7_3Xtye(LD?=!=hySCsbKBe>zRF$e+-tffpQM4 +zbXrd%;S|m6HIHMlN|h*XS9H4-6s^=eWH2J$e(;zt2a8N-GsLY)sJD&9@}PJ>YEvkx0= +zrQDHbKKV1o3d@?!RybN8e~3SR$--%XnacdsP@6ujC|taV)Tz7{E~l!KRYk9g4%ZcWEzdryaimVbE-g>co&qS%7+S8BC4;z$|=VEwBT}0b< +zkfg0j0uvz^@sT-=*~Ks7n8>b^7RhUj3BN{_d7yh%>aqKIMG;VjPz@>CZ<75epBFdM +zfWVF2ocZS3#jW(%Qm?XC032Hc;&PS9D +znaP>obCz|f8xAUW-RRr_C~V3lvd~I&|CC|ueCoNki79CjpNwTRgqVVzFdm55ZdQ2F +z_yNi?y^yV9OeP0hTgc^emwGg^)<|i}$ok;cX_pFjj0a=}0_P^{vjV$cN&7S{eZA13 +z2Gr8sirJmepvBij)9LbCjGCIJ{iovo(`B@jyx6vr8LB{?eca-Ajl&ksT!q$Co0NP! +zX@eYiCA+iW%1_y}#XO`rPJJ8kISuNDGMT`|#hn33qZ<&i+GnLZ3z@AveOk%Dv0FtK +zSC@tYaT+dTYh(NJ%t@U7@>2H&M(M(%WB0_(V%>6Pet*i3h+kE+ir<`prCGibCm3&; +z!r&8x#K-GKJueBAB5J_MsksNri=s2+&B|NnclX5VBOXW}0p8A%TNtnWQQcjvS{^*z +z(iW(Ic6FG@y^2S-wr$J2(5@6;Y&u2LJe50TnVWSFz>clY0sF$)hz*kv7MNDjPU7yY +z0tHtB18<>euXAM45ObqHHB>~G}S&CdXV^H9n3B!Z$d97w+ +zKd1f1+dnPl(fi()8g;{L67lvw3~GQVeZrp@tof?oDK;T$LW1+MWdIZLc5TZWqAyim +z@O?xNu6ilgt&`P7f-@b3ZYE>Wp))bDk$EeLRS+$KM6{2D8$ad#G)clWo_SCkna|*^ +z)ChG1Eof^a)KtB=FPh$_@%Tnr1VP*+b0FAMj5Vnf^%;E +zmBh7-Tgh{|=4uI5R1OsBJnFhV--;@C3I! +z&Y)G91H9Wa$S<)2ygMw)uYj$7wQ5DsvUk)krGq~&IP*DGjnNWaN7)qbj)_|+PVatO +zlA0b;kwv$3JBFW$dZvSBrWIK7?a~u6BP&9?!A}D;%YL82cvSDdN4s_`l|N~=g4R9W +zd44q7wu%bkf^79F@{tttShmrPnU*9LiqKeF#2vNAx5M)6z@sOJ3`A6utB|C~3T5-ZPaaJYO(k(JCe +zKGlLFWv#14b0LnsF;tQM)^md6*bnVkRms`Cl4%)jcSAo|e;D9Q`}`j3X+H;MBZ$|U +zMVm#y)$iGFQZ|;JCSO5Xq^9th-szr4lL_6Xp^3+e3ngNT_m)ht9aw_aO+rp&ZP}d& +z-jhgPupat-jmR~2;^1NavHX(ymN>e1cFMY8J22-Efv3JHL_suzeo<@3d+)qzzQWrd +zk=toq;i>gN;Seu%NrE(ZK%Wani)9!Q(dPYXEY=e$uQYqUFLsepka=8fDcesZE}86x +z*&_a!&*+QCvrJew^>s!tajotWC&@NGL!^t)qB{Eqz19~sbYooH$=#BSz`wSHYivlX +zbsH%x)T-wXu7N(u{n7uVttStVdD^JP9i%|JOU_a_e*oG&natl|uF1F-(}#wKG~_fo +zSb%}rr1PfcsBkT=TLT>JX_a{AqNQgXY@h;-VrpC-`xLIY0r!-*P_UCb&k}q8N`jq! +z(UyZ!95BwnCJ{%+gvJ`jvsf&lT*M?ZN<22l?g%KR@Mm4l+G658n8;GEB@-xZuR|<@ +zoH@wdXv+Q|H^*=KZP6K*EF>YYmg7qvS7!P0b`93h`ux1nnqEK6{LMAvN@S8!cWg5{%OA8< +z?c%wT?9mYNWIrbmZE-2p9jf4}B0&z#y$(6p)kh>zqqopI#;kaHS2*t<7ic%mFywAG +z8JfIe?gv81W>n+ZVstQZ9w~!~s@SB3?YHzVqvf!3&qA!hSkp2@c&i+07L>M`4zJYcSS`;)Io8^1X>X5 +zRA!vYg7)vMIhNSh86Bz$s%Lb>5+qG$mQG0JAf^pkF&-cEtT{FW{|MEeOy9w_BGn&G +zL%)JLC_eiHgjKaF=V`w-$l +zy@tJqS8)m!FC2p0ftp1<*^I(bg4}@}-_r>EY(2DDxj6b5(t)D{Qew19T3|-F2!wBo +zB%M~_H^kC%LzpPKV2-8@%5|AeoriS{IN_c(2XdYzng0puJZJ1gky8w0CchStDR+%n +z(FrCpntx{wu_5^stD7*H^1#`OCnDIdw*YjHUy#DzX2yr(s}4mWu+1FkWa~2g~+YXzQcmz!8y_lUt4jWODJhF@GSY?gR +z5CB`Lq6``kOO13bnl0{Yjv&e=bCG?y)d3i1Nb&1IrY`}#JW6p{(Lp^VMdnbk(5<>~ +z{C+yrIKc2oME~C0J65))5}VS$H#++c3MewOfeu;0;}=(HqQDkX +z17UE}`6Ni(RyFqH1aLZY?5 +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 +z@|KOJlYd^Nezid>wCmCE4DG +z+*DTvk^qlhtD1}$nnMlfCkzri+$wo+3+h^TV)O6vLwx=rgwG^=KLmqVwT#?mg7fyWU`0Nq;In-CQ81)g; +zTB!J2^dTp~fk_tC_*v>55UWVzGY2m8348%BN^S2_RFD)qV^n0w#d_qF~ivNiWM5Yx_&zPoSa)Wc3<1!>rkXrQHx(4Pj`7I+`_lvt2$1yNOw58U?87Fg26?Y6BrBHVT8A%5=l>u+-o!+kPXS7^ +z7vn$yT9prkZ8a*ko<|HuvB^WM>55trDZM2*hisppC|9Vnbo$Ji7^|AhIHzp<0IX{! +z8OvK3D6d7C)C2mmhGquo#n58>E&X!d>bSgMv~rVpS=Sa_74LCX+ItAyWK_fAFUizw +zb@f4hme$*}GYFw&AH|pkM1x_~g(gWtZ&!FFe~zxRJk1wcPI?{TSjAY0)cjgOQy$mZ +zvc6zMVhr#R0E58kYXQyZ3`-wcwR6Ff@}R>aUQX?dZ{ +zr_)c~R)6(UzxUF(P67*yJU2FJQ(()*(fz@;7=THo#B^524@|3ysncciQAXzUBT;=LDT;ptPL8uo7+X_;{CbUYj%zvAQj1*q0r<|jWs;+D5fktH5N0j{Sb +zqV*gKFH(cQ)8ZEc&;jouFQQ;3-75(p2_3Kb`uHk9sk=H-Wm|YZD(PvdHOySu-I?A_}C^uh=Yu*C()oQPI5Fjr%Ckt0>-G=(llFVSdMG!G4a +z6Rd_aQN$?=(+g>~&ew=2{}pnyJ@!2Ix$zP_w2aq~w(d4e}dInH_$nQNe2 +zHwM?9D|ZWbov%*1hbniAhJ(Iy#RhY}?h<>wPD$>FlDn+fxxwtyOZ3E8BEYt$C;3mfDWyWocm(?)3 +zXYTV(KBl|;@bd`dk1YZ14De5m;S}n9231d43M3SUCR7P=fsPiR#1CpY>qSH*Cw(ND +zJMF4UrqJ|KR0T(&%LxE0{p6Uxh7Waw_6eqb?6o;35p*b0c6t0a&D%JPj!5jcnZA5$ +z!T%RC{bwpBWNTw$ZtCoy|KA*)$arg6BmwxLueGB^e_lV|ygb4Sf{dJPCI~oX24!dz +zF)yJiyCkB6sC8|Y8%1+MhMPdVZaCwN4$Yj3wSG3HdZxSVj|;80x2Y*zfWvF@V9Asb +zJ=SpS2H6aC2^>=|^k6>vI*h8tq{H8hf)}j4(rx5tS1U#n6G9 +zuVE*e(1j(%hMd;<;w;59PaRDDKtZ{iN_X8Ex@uM~(de_f=X+*|(RrKmOl!6NBtdSC +zO%pL{&QGOTw#!iuO`h|0?N27_Eoiy@;5F$NKFaWz1AQXY1`Z7Do1Rm-W(KbJ!*sNlM3Z$<|vEzXsb%k%hQ6( +zz8=d&MBCy_g;x+t|CsI&{n}E9Qo@tr6P>)`5l%E~E(sBuyYTQ_gi62qrPR2s{)q{L +z0zIRnYeQSja@wXj@p^b!9?9km<3G#AIc7<2w`e>v=m*TtP4;jAtiuxI!sH1f8I3jUYAP`{=2^4w>c?=P9D&e5=CK23yC|W4V==<(8r9PsEXDc-c +z@EPIprn~l9NV_DbQ8zZ;ufVB}30f(c_!AR$y>{~?q}O6lTcdV3Y{>Zb7)A;Zi~@To +z-@gh(FgxJDzv$n0H6>ySpc(UlTPi`tNAVpCQm=p%;PK-nVk)5Pa)4X%K}SaMqs8wE +z;Kby8r6>dx7>6B6#FSy;;slb!>u13Vi1{rfVj7?oRQ-;>VNlR@GHFZR{G)(Iob&4+ +zQ2(>ouopP3i~C(Ld9RNi_Vyw$9?a# +zjBowWzu_1EdR@rY+WH$HBV}%5ET`}AeI^jg+WocD6u;S3Hl}|c3yF#sGDzRVqB*#x +zghuVrWb!mWRW +zYrxNrN1I%Zmpn(4|2P?bl7wQwhz!;mC%~BWHsb*=< +z+UfQI1+hP+L$@^Ye8y_Rx~4Ch9Ix3prs{WF1~(nW)f=?AG>_72p7SiFQ&=+)Tj&VU +z8!cI>R$TpY3HVC7Vi$C|JzZbf?WEZwPX%|q@D)kc2fom-%-U*5 +zwSoUy<0kI(4N}_HkSFE1 +zWJr`gI;R7A>|tyaHMD_FshL}aAqEvR(newS)tZdZGiR2b@(_#^LrqxJS<38nLaqbF +zDfHmiD;Ae$9xmf}1|O5h*iR0d{B)cXSi#HS9xkqRWArn}mcpm|QTH~Qb&{ZK$GOf@{1Y +z5<&h6rVZExA1Lu(tU;4jUR*oO_?ET$1438xk#6)a$az_)l@4^~xB^$8(b<4xTzW!b +z6QbKNaiYDssRu2F{jjauX@2RML}X0U^f(jzeGzHD?`?8@n`3*e*H83Cc3V@;O;e=H +zXBoyRHTw%%eI+lwGn +zA}{o>V}<)qd4652AA_{V6vxy07RS-1<63rC=Ldk?U>GRM9A;h037NPmLpedDI}9nR +zQi3uyyw`zmZK`n+4_`4?Cz=t- +zIB`X@@e?b!FdHuci)1Ab|9f2FFqOsWq0{Mv{n~`nO6TaceCQck$96!lGj-6yEp`l}!%rU154&bJ{K`}xoWu_34r0BZ +z=EhBOa1CNTh|*9%gg0vPwA3$#nV^HHAkU3@FlWe4lzc0)2fmENei0c?Qbc^v6Va%A +z|2RoKX`1DiXh-=WWt7c+5woe9;821NvvRS4CF0{^7fz`S%mVdc5w<2X*#Ae5r#dZ{Pd{1rfZLwSkQ)|`m{t-l4{^cg+=zm{Q`&(7H&!`JVqmA8aalnj8YORv!_NoQg>y$#dt{*?PC_Bg}Zs<$;YoB +zT5?s2Lvc9OvARj|SMa(E6FL$#d-XL-Hq +zF>gHu+onno=Cn_P3n4>;mzLyx(HH!33^}c+z;To%NhS(<^ybXXd2Hl`n8N*fDtb}^ +zE>32?>Y_MQlt~CtA+ZTy9b+oaQt_41o7^E;*C +zC$^sMvNP-sfHp^~9Hk*?UtQFH4U!JZm!1eIl>uuT^4*Hw@!!S +z9@eF+1%)1P7cywH3x`9~1jTM&rG9Xy9;U0>r96i~iqZo&&&ptv+!`jJR4Gr`^Hr-- +zWacDWsBl~cOmP0VsI`o73@1-`ThCD@J8K6}CL@~f2#U{&x6}dw=ZHs1bUQynaa{o7 +z&al^hP@3?ns9Q?KRLMPgs?BofYo9CzcZ|T)pNm}y>9@#f;?EJ{&Vtgryap6!-AF$6-_sMw?968wD;sUy>LT$n<+b58i8;q1~& +z<-+C|;ULo)jO`vE_7)8zoDMnPwpRUAc=&7i51z(zs`crS!`*&d?*3uw`UL6-&UKvR +zn-u0P|48aEQi0&4&0H#GH_@Bhpf@f^G2IHgw?_O4P!%(dcFRr8rh^mNx~I{sW1c&T +zI0RXHbJMK_qJas>8u?;XiA+^DA{}1*LM@=O#LKe6=71)aY3|7Do>$^5i)!oVzof-~ +zd38oB)h3DWNCx=Zvy1#^Z2zCZx{$u3@wX_z*v8S^>6@Sachvs#N8^ks}s}F=F(X|!68sJAt7zh$u2+lqI0L?I2v35xw?ArC&9!O4m*7N#q +z2DqZel7a$75=!wrnru0m>D{c94}?|j)3NK-QM%; +zjdISUG2L|nlTBMZ(@tpj1NwN>o>;Xt)K+rBb?cdjq2+mKE=}d{O6p#j0H%3mKJckz +z9dj7t_#X5CuGT@F7Ej8_Kw~IVtBKf&1F=FOj!X3%t>WA_bwKJUxYGJu%t&;#BgnV6 +z&r)pQiGvW6qik4OBvI4X0{w0Sez8AIFCvOldG~buK~A!fI0#aWh?QO1&Y0wDuSB|* +zEpJ45qxb8jY%#V8xHkHw>zy);u{|tEU}h=oz!a%%62=BdnxI(>?eAL*x(3;7{WXnc +zL_r%577SJ*(TB?y5jacnt-O7YVPFMdX*xL=VQ0tUi2l56qj_-juvN}^nc`gG)G&CV +zr>fU<`*ud=(x>>cyPPkF*uF6Px!Dln=p@nLIArP73v}>Yt1l7#lTvRtD}EH!2;70h +zvP6AM^eq^5Do3dZ<&RDB;8X|p@!T@5^!8AH5XL(4(p>Y>Y!UMDVk#GZ;ma3)fyC7( +zqR~zM4o6g9ALytNM&;VS!4?ZO5PtWgj!w`kPb44z0o4nPz*}&K?jrO~lpy$q&eqGz +zKB6NOb@?ls+M#sozZ2bmSgWpabkVn!9)CaoHvI74Vvv8Pmj7gM1V#w_#o+k)W!9(x +z<#Ny(VktBwhYb9)2dUqsgvK0D{K1Zv+cy|dQLELC_l^(GWb^F94R9Df7+gp=;MmHh +zY1_IorDj-qO+x$9a)QhpXU&=DD(;(lEQq0ccG|tMkU(G(P*|H-QbCOpF1WCJD=4lVx>vZ9M^x}7CVt8R0~s +zhLzS-@izmy2k<>1@gw^|#&SQiiU(Z`o2ZzOk$mNM703qiJ_Ehxhq_ +zlV1pVrbsmz+^co7ubepUC59z`phz5XUDF2;v~g;5(bu{Wz*NDY^cgH2sd2;aI#Adk +zNzu87y$s=)BCseFxMTLJOpmOi-Fm?tMho-ejG2r+8ZW9(E=|}%;?YZco*ZasN~p@y +z3KC$%VDjkG^CJG+eGI9#{V!ZsW}KvKFF$hN6bP`e7oS{T-g!4LCX(|W +zk$ePI9x?ip5LXg|bucs##FvCBDee1@Px3wFGKOX0J?hJoZ(8NOOOfprT{XaCttLMz +zmb=wqZK5be@CCLD_zDsNq_>Ees-l9fKd{ZHpb1}p}R +z@x7*|e-#e?b4~yEC5)7pmh9t)_nuoEoUbk;n<8X}6seY`5R*p+goN1qbJA)h&Q`aP +z@W~4I3E-2^ES(D+FNl_u>0W>JjSf3{I>YMbnZ$9z$w15?R)ng8$=!k~w(5CLpxEg` +zuUcV05P0;nHikD|iXJCOSTy3d8!zp0xtjZh=M*g{`ieeC|V0PT?Np=rv +z-(|sFk*Sbyz_}yK*!YS@(lX-#p|w?|7BF@(nO+@m=>yd};j-(G`Vv7^zoL}RZ>Hy* +zMk9zslYX&MVSK}ijm1)X;I5Y|%Ol6Zf4YS1Dyxz#q(ol0M +z7hi}}WD`4+5aBQXtEvM}-7_d_ElJhv51da}=j`A3Mm2@%y}MeEE2dYrK5rS`&wJIn +zK45krd}8duYlKN883Q<*6=KcdvLqFR6UEs#GdvI&72;|`gYc|3FYulGNo-GG*M-1v +zO`tVA0rp-4WL)j;_`3vKUt;}BgbvW31x1#Ri2iKYD+cgMk$I!^aWhWN9V#Q`hu$Q* +zq~iF7$O*Se1{PkMh>(w2CJb6r=q408jEM&7k!YhD+=+jz6e*U|i{zE1H5Dt3^A+Up +z3EA4Lj=_kPCV>0Y#CcRW*GpE@a+xB6iBi1}_(PLXI*_MUi;9xPoO=sBL>o~mD^M|t +zJSx;d6fM=UsnK7nRLW9;IgoiFnt)b|3^W45k#?#%4TDye&f=S99KO{-q>QtuP|}d- +zHf_`Fq~SPih|-J}O)62<6br)p{TYm^EaVW>(&8R@xHc3AX{`#?X=TP7F@PP*_hyd$ +zPQj(jB*L{YxDm_HgQp0QSUCPl)~2a=8S%%HE_fMzr#Cw-iv&S8AiMVta&KrsbyKmzP)+zmtC_B^ +zuc5qrpEl@=ETrnJH|Bh($x?T%Z{U-;v}U?|S4nY~0RFN90ApfT)uvgqmp-TQyX~i= +z3I=C^Wjje>gyeH^;rnRogFDt=$P|B4T<$#0D?(d%9i$biK|?YBB%U{Wi>&c<%^onB +zF1rzuesm*0ahf9IK))nGGdcbm;JA(kAGGCZyqcz#;n|RWKFs$25CnoFEq&nX#V_i< +zs?7JjYX$%Zq=S*+n>#gim*u?DuQG0sSk*bD6Y=iqso%@A?M~l7C_$=&d0sStd0sML +zM!`Z~{#>#I0Bnp0b_(k}LLiLB?s1yaK>E!DITIbb39g(&e__(@7K8fqN#_2fm>urW +z1q02BkE0)=|1pbT6qH>f=+6XAx6?u)1^#;nR0P#qU~39Je8%Y>+)&4gZ-98BT|gJ; +zHw3{k5-!`disi`_TCM4R&sk0e{XQVP}ubae4S`OxM+=V2r7J`ZNzk- +z9ZIqptse9fIY@Hmv%|%+!@cbtQ1An`dYiX(hGILx&!rI8o`Z|p}E_8244Hu`G4sZ{mXIf8!V9R +zd>;xn-__*5rkVdWRQzA=SN`Q-_-9nBY-4HjJ=OAm3HUmc#}xj$JmDE3)@S4ghrbC7 +zAs>MU-^nEmAuKFZM%D^z1GzdLy4wD`{nz!J-E~xiN)4h)6SC$ +zi6BT~zjL^Gx%QON>3un||8e!_3Si$}QviAol9PT$pge;dL+^G9b`&zRY}gqpMS0?E(IxL;dJ?V9K5}#8q_*O@zuvN^MVuIr?D*fWia}?Ur)!&WF`kT>=6__v4Dn_+&2wzl +zA{cb`h#W>Y>zo)2*wDMLPx+W@++8+p>l%0{q;yg!^ouY=G=9vDEoOviPG6GZcgA9|atk5w0G*5Di +zAeQINT>H^LHA5@ascQ%p(@@J3&~T31yZVf~kHZ-gLzwO-#q^25_y!#4EyDKZ$NPv< +zd@NOts0UyQ;6p-d^eLf5@j@jp6_RIaPut8XsbeI*v()HGNZ8x?(r)p;rWjuuP{s{sEbJcTt0hYw(lLtz07k56%;R2ii&k^cx? +z1Sr!`?2R*AdGIPCfFYYET;{e9In)re7LQ4QQr_ +z004vEZPx$)b?Lu%&p+$Z>QHV<3ynHdckJ=s0^L{ue{Mp!5yLnDLEmdeVWk9MdhnoN +zH!+#G-y>2fsQ~gNdGnMH^5uDY-m0aQDnG?T^D*F0@K*E}pW +zPr4pcQ^%!XNgwz2&UrkmI~G^ZZmt?#H{YLIkc64TWe;azUwvNQfAZpu993g}&?JA# +z;GON~Dso=v&6b9$?_p;;nQL=moG-5Q>7*_)KbmKx4{;uyD0K(Pyl@Nd#d4zDlyFZT +z`Ek?kGwm~J>=9fhDAs{ +z+%TJs%z1k?4Kg`F(ueOOMoK!D89dsjHXPhS42MC!C_(yD?r +z=G}*9eWW}$87$@)Xfk*b1RHKW3h?3q(o?09kLX@lJr_9?^?3( +zDwR(yyTZSqeIGpxYwKym7s9;F>R +zEECECm?1+*lc{CCwFRVSsdc2LyeD&!eSmobCI=sSfsNSjD2){3zy9 +zGn29JsjEL6d7U5|$1m$HM$Q2Uj*2eT!E2FepfLS0Wv8a-)P>qmblLlJ+M{)nPaYOnj^mbxQEFF@|C`%b$vUb +zObNXZJ`58WvzT+B#&FGUm3B~!pqDzor^AT%RH?6WI(-97kS2Op#RQwikYPD`%!g|l +zLX_SF7U`s5Ol^?tmr^?z!2WPr>!?B@x$AJ;FEX+5kTR9Ea>d6U?85{X#3iV6Sgq>2 +zJKpkx0{%vfMJ|Vd+uv)56NtzF)KQI4A?VNy#u}HgS~p>abZr$2E46G}fLUHP4{F7qHw@g9f9Y7a31^ITPs2=7j;(42 +z&dCoN=FbqqIb|OFV5af^rU-5f!LI2VuEzMeB=sGj0F?TM8#%VQZj$yCa +z<8pc^evk91NemvLsf5_tTbmfUE-i5=Q*iZ11vfis|Hz40lf@Y;J;J(Td~E_%opCn` +z1C{cy+aH(4rZCw-7{mstiI8R$3OvTCryoJ#qeh03nXbg~ee3hZlgCPwrzVc|DB;rS +zT#lr3_i?!bImjUVlb5MLR7Yc@jzRSfax-y8wft)PB)`TL%BazK`ve98Z4r(y1O;s) +zoPMUu_d!HOWAx`KprbpXZCFnWw9e(wOKb3; +zZc;(>5oMRBmIZS^VxMWKdq)nwL3buq&$)H=WFMTtd}40_WQ;}8_}lISh4_a-`twb( +zeK&7WFn64ys0MjD|Ma&#;sP)uku)rX2pdQ=2HhF~ +z3p}Rvzd95Gux6o~W0VZ;H+K)M4Vy?b78_$`msUw1%byXO-|ox1U+z)If^HNOWO{Ul +z^BLLpeBt%KLKEWdc13^3=QFzQ^BUf!o8tlf9KUoc9hj0Z?BpZh!ClZ +zD2~52&*8i#OE16Gs7?(UH8?nWA*L%*rdeC#e^RF@9;dbbHp0)Riz4lylKC?kO*!~k~M&An$pHJzFh9Fa23uW>0 +zRwLM?XZsSV9WtkKm}*7g!owEq3``za-iYKZI|=7hzL6oo8Eb|w-t@{CFY1oTG|sMW +zs0_*#1a-J0Ss(6J?XHEj*U-%|Ob_K@BA +zfur7B$t7SH`ANr7y@tlFgHq7sGl(9lhrOazt)+cX8j* +zKD6vc^pr*L!9!nKw^(JCj&c+#sPMRq(RlhcZ5d{vedeyfMBa~qG1^#ds5WV}=py$EXcGs-?WX7DqQ0B~xsa!`)`zS$Frz)7 +ze9z{D(y(aJi&eU0eUa(gG>)stBb@Mo19jyF*WgOAu~lSaU_nndSU{zjZ<)-&E-6bH +zjc-DOK$*VZd3HvbC_7YKku84x74gnwU1f(h?9O&J1yh1GI$Ie|C{)g%Miteff==U_%EhpNTo*8HY( +z5DgsXjC|EtA09lhr6ma*1^h1tJ+c>Yg%t~^a{Wk5X91;FYAqW60M}u3XjZ{aF~}{d +z&tF;MLp{n_iiSoULbwasr9oj8CK|2MUnS0GN0*OW@I|X7zI<`|mC%}1cV|dO&8XZ9=g@{&CUgFr>qlZ+Ebf0S~-_qn^5EG}iQ +z@M;OFMID2g9&JTEI*r|`dd62)%J7u?VW`9jogN?A5)ylOWU!r1znL&Ah59oYQJ26^ +zA!91FzR5xT5*v9=!n7h5N8RT_)1PpktfQ44e2Zy5xW18nWNV~azak%7pN}|7I5wqk +z9Pctx;c-rmh^bGg)jZ`c!&nThh=@0$2+y_MeRB?%l6qJG8H7>-iCoGOD>5!k{Vh+u +zax%q*;s~R0j5+!9QY+Is#tF~{zbL6{z!6AgNe;AKIuc#4K@0*xYx53a(3}YTWCFdR +zXTrQLeUa0QDHh1cBtkvfl1x77y~t503$7>#~K-w4h}L9rcq +zHd!y^_~gFSeBI08&)4M0Nf4gBl9QEF@p3hm6mZ55@0sp9aJ(94%NKBa=i-rXId+XD +ze8v$R!dTms5|y7HZ-!oGPR^b}`fx{i +zTLcqkfHtz1^@X3SIdCM}!6bb2=vx+hVBy%2gavx>7%*n~!fMe7&1ckD1s=h(DysV! +z_u;~EMwLD2c$bjP#ej!Kx35!!B1RN;17b?7^qzkC@OcRcyPx&Xup->JYO({jAc@n} +zDbpj~0j-2HMX-#^cRSH&NTZLVsY)nC1$!JJ~f6S2}~gwG>0ds0%Hv67E{4cZj#DZ_e(zpMC2wbYA0dB7g?h;+v-T4|xzrL=2$ +zy(|{HYwZHkDiUSJkk7~1mG1wVB@Mz@Zs*%M@h* +zWTtQHOkmmr@Qe%6C(|2t7YJ_nxHHbmZhEl`97hKDaaXY0wVC!$LKe6p@zR^`W%0Qo +zQ?AJ9g=M)jqRUviTfH8kUFf|zZ0U(GE8-dn^UZ*u%vPJQ#c{+zU(Cvu5;`zg{bn$e;>@8ntrS!EYe=AV +z&3jEd7<{{(V3Y-^F(sqao@CJKDt2S~l}5d$x-_ybADxtmTQ+gZi4-q?5oqLo>jId4 +zUp03dr1~Bz{Ubzs-n$9Kb8k{2TjE@_u?k?krm@y6QmC2X}bdUqLSVkEZPcV$D2#X98~FfvQDz>!G8} +z1NHT0$v%GrC5vw;UJ^PCh958Ijdpbi7a=SmlMTJTEBaVeNfQl}-n%iiEWszw7V~u> +z=4_#6JMx_<_2js~S{HP(D9f-1E6`o9hw376o*}LM3?#~R?#5Mah&*L;oaP}f67VuC +z91iU?6UYkBxX~>m!Gl1ujHkExo9w|IOLC$|X;(T&nlKw!MnK>l{hZL~Mxhx0ozVvj +zk!kuAUpNG#Q9jQK9|!b(w>%#Q)|Y;Wl;uloFADN99|U3MWr#B$(5{tih{#p~C2x+p +zca{AWxn7|;@T4mkDix%K;@#qF2lYHjd~#IY|NMF)4gIM@)@J0G_zC}ET$>(VfMNfl +zV5+Q8br>O>$JgQfEsFBt8-9yfMtMaHPU#av^7GnaLWmGQldt_;=Z~et?m?w!VK_n> +z4HdyU=-Er*oNBD|0OOi~E9yMA(Uni1;h1LGeS`01qkR+Ffjvfsp99bQXm%^!nUE1r +z;3Y&YYm@B`(EOCtG-#6J42hd<>^4Zo*VXxcIQ#R33u<} +zd+(zS$d>!JosrkqZ1_9`8yI*Wj&(8ZD?6{DJ;|J%$-^>Eue32ER}Cfa&S36At|YEW +zN}T08D*!p3-J;kU(ELNq$4*^iz_{j{nMA*(Ta%)IUQ+36v#1V5{lGN@} +zJSm|XghV*JeZI71-`-x$mS&_BW)K&!%jLtQ6r-)(HP@`(6nfK-038X-ySnlOzpIkk +zv?Q?>^UA!X_H3eK5KWWA`O9Z_EodEbEl|bJ5xcAysaxMpja1o;U@kM2I$ktHXOV%; +z3lFd=}rY` +zzc`X)DF>6qp5JyV*E}5Cn!9{i`-_Pg;xrACG!>ig3S|R0E0q(P0L+MvXy=Fay6o30 +zrfe_u<1*OoHZwitQM+q~Cy76<+!6#(CU&qx*T9VEN_W^g2?Gw{G}}XAJo-9z=2OHQ +zAf`=(n{uRg>e}>fXxFZg9y3=X_kDTR_#=)UPGyf7njc1;ggS&;C@Vb(d&)mV2Yq9? +zm;=peDqdy2_fMDwXUUz$jKG?Wt;2hj(LHTILk~pMtyI7uh6*yHl-6+yo$7p +z*l-DYY2qD0QWt@fKMig*Jh;aPA~k4CaGsA&>+apO-pXLaG&OHbQ(%TMSvYA)dn}B* +zucmB&ZiedQQVi2-NzIj&^6Id#pd>*vVG%Q6OqHw6i?b|1GYyJccq!PAIodgLrz#%K +zkQMz#AGzyOL3;-Y+pDJ&&0{ROtg2{D6`3evg^KftahQ-8kOv4sZiHOgdk!Ev_AJVPT( +zRvbzflx=NV#b$q;s!FPw_@z#5RZ$~{yao%XwEiO$u5IR(JI1p +z{c)!9TWy$iK7WcaUl=*7KqYOsaIL9tkO2j2bZ_=ZRM_j*Fv|VIA}6nq2_LcdG4$P` +zEedEUyOd>CD!iYJIRpCmPoJLc5MqHilfL_X-2;w;*?-+3JkP}b)6wuRZeN4qUA!m( +zqjM$Z0J&|GX`w}Ceq_OJfAlCc^mnvxcRiBvCZmawNhkT2#g%+u@!JED$czz+__|e6 +z8P7(ahEp3`JX_j;+kwpz2;jqs%uupa{DwiyP&0)HJU?@hjza6r-$wD)4fcs<&Ku$_ +zOD-XXxN_Khcz0wQ${|3h%p~1inp)}AE}H?nPz#RBX{7p+5mK9AycKy-gb))#kyVZ;1Q~=GTVDsp(tV&oBy)V`F49g|D$u2lJ<7+quDZ@c*_E8a)=E-~RsVG* +zn1mwri)(phr-AH{i%%a61i|GqfrLPEMKW=^-}Wh?cy9oUo0l7TRG0PQGqLj>m^hVv +z9It#)Hfh^s1eg(qQ%Ttt2yFL#%tZ?tnXiStQKn)(*;YcbU#!Xwf&`CbuUBIwQ6k6L +z-qG>`wwci4lv}!0FZEHWY<)jd(YL`&gB=oE<+q*Jda-=UN}{!+0UxH$<}5M-{P{Xt +z6{h%&>Ava{*k9eggzKQLxB+f_pP_&2mh@8>{&~9ppI9+rV;fsLIeVQy|M`zY%O1sV +z?Rk)rk%JOPon!hD)Mz+o}jn +zODGiN(DZULF|4ccl$fVm`sk*=nLSqclW5XZgD^CWe!0il^7_lC(@NE{7InsGVxLn-XEPzHr{E +zBlJO-?t}f5v6K~w5ezB7>3Gl{sj;g9C0-&(r1T_Os-19njCSn$zS=|M(T4L)`Ie@A +zA9xK(;`}5UhHz7I=IvXw>OkB?NQ6AYne}Jk?B#ajo0&%wn7WRO=8NMK_py-|shb8_*GUbk3~x=A7}hXl4^=$UB0Xm)%=khT>2iW=5Pz!#iP<* +zCmEj*`opH$4tKlTn>S-Ywp~5j*u&pg=A|Z=rQ~4(-gKeD`8%#Xin^lD8=G%&)CF-| +zf@#L{K@M<;0<_R0L%n*&^@uRLP@9H@AG{!(f=GQbR&POMuBrCauf1p!K1 +z|L$`Wo7RHb0$5}T0C5l0zb>@@nTmQp&CQ=<+P;fc032){>*{F^i1O4?_{h<-Xg-Ia +zx4bO@qbD@(&V}%`6yQ_|_#T~1gXjq}O4@dv87v+p=>A>qaQ}54ikMqc8v9+^k=qJ8 +z)idyi8m^^(#{H`$j{xQv}ncqdB++W6Vh;b9EK{>h)JdrUGv868t9wlUK0voe*OvbUni;9$^nU`VA)%2P;L6ICt}`(~GC8b*bJyqJ{g8&hh`XzRN!og!53 +z*q<1v`}yT4@l2e+Vz{t6#hlB>8Mw!poW1OzRXcT?xR#`uKb#15$x#?xKJ_{W=pYEf;J1O~Q7xT0pbMb+C4YGhUW<%R%EDEQGlpa;pNf^;sDVF$5 +zWG8&p-jQl6ij@6T+gp6>(}5)>12;@NLxJ9RfzlUGY$eo3;f4MQ!77Emh$T{mNw#=>_5k8B +zr=rjeQYoAT3TrVdRTyMbBAF<|599s8YT^=EWBz%0;K}jsjG^ASOwgn}@vPS;BiNQL +zfF!hSoQzl!GY#~#jN8lXSDf2!hK&OU=FTWt5){V0CE%P{bt<~%?sa_)ScoavEwGe +z)kcG7IdC1}cVO@ZkU_yl&v~ZKE~h`OD0DA|L;2&y#A>Z7Ui2743Hpoaa@AzN*zorv +zOZsBWA-=EM`#%_?LWahxk{kP;k+>gQPZt~cL)%)*GQ-F=6DfX|ihhVl~Vrj|85o2jIJ +z{IOEdgzgo7mmEhpx^>1UDeoMv?LgzAmME9Py3R-Tzc`VKa_Z3T!~rdly6t^72*11} +z2q3m&ny6GSm$hc&^*ld346Y3W)7O4SsafJ2(L`d$gu^PgH~}VEw5(JqVpq{$b%H(mOXe{Mm7U#SllNmCbzz(; +zAcX^q>G^od4ES_OQr5PYaQOq|=s7lMG`P8Z0JsLXde0gW-`3NjCRUuJ6?#ef#3_&fNxa^j2UNzZH4 +zb0iM0(Dr7bJUMu;4mY1X{Yn}HJ{nXM7|(!ZPR@3hs(jq~sM}<896@^h&}j@Fc)|j$ +zLAKkVsKqXoXrmu=}_f~!$M`Rnn1Zjo(r^Q@Cva4ST+ +z+%|1>6zb6G2OrdXAEi-JIbTPiH#w^b*QkNQ6+RS_REa=056p`C_re`y^aZQ+*SI3i +zOJQ0CDAVtTOLCO9m?Hzz%4L$4)5R@&=?*0Mn%j3^?}U0^_c!FPawwv<1Qwv&=&krB;jn6cUEL}Aqp}K{cHN`J4^ER*EQiZF75IzjnsyjrK*4yZ$T9t6Un0pmTLgk-%r&yp +z2M4^)zO)KLKH!LTj8)M{M4?XT&oC>8Be^o`V&bVdZZZ6BK8k+Qo=vYV$?Qh_ZNntm +z{L^Y|5eZ3g=_$J#5T3lsO<#sVO>oa|upK7vT`fWdTW=XaKdo{0UhOXIkk(&Gi}b2L +z2EOv)4*Vj(f0`J6=mHt+cH5RRXU~$ce9+RAZC`Rc_c`s;w&nVx>6@jyw9h*NIPAnb +zLh+~xhGn>VV_n4VsrC-3DAG2D$;_l&Xkdm_kdc4{Hxt4*46i!nGKMB|m7_4X2PP%? +zgLe8(z>HyM5vglOuE8?$y7~4I-F!HS_{`IRq>CD~v)(?oFkI2J+M +zkj;m#FxH4u?||l<==O=VWtPD&#ke*%_oUjuLEXU?07^q^zYY8#Zvj=oS +zSBVD=G#1X{Wl$X%H+4_tgOP4o?=WBAMSGP^5oEewK4{#)! +z4x0C<*?#^JMgknotb*IB1!taJBMUo85=WtI(~Gn)muN0eG4CtpYmv3RrSfIlc*)Rl +z$QWO1r3t*|p{Of0YDL&QOT0?NvP%Tb&K6^-6D%qx$;QjX7V?$}7hWRAyQnUNLJ*GD +zzffO^YG;tRd#3?{y21x8)W=5$v?RdP0(NMzsxSOOV>!vlor%8k(DF01s$?or%tJ7o(SNJ+bfaeeIv$oKnWNwgB~=KX$o%HikdN52nRw$V_Shgi1zPn##WE +zWfHV8pnqs3&<*nPBK4uAmuBkF@=<~>O=4b>n?PHkZ8_=1ejv~k&3<*#3BMZ1kX}Gz +zi!IR`PkO*EGkTLYTl=3h-sPtbIx@Ci6nMkHfD3KE54TUI%1?jPSm?3)R?O#X; +z()ZDw&&6g~EsP($NNDEpz2t>j(qwGh*m>Ed{-wI4r+AnuUvrT@d_7sA#9EdDoB!&8 +zG=evt;&TWg+!b0Ee;q!rMJ!>pfdY93pK(K*(!$I^f!jiE>{|85u3;$)?04`lUme2w +zPT)H0u|*ITzd5bbcu}mnpDrXKLrJ2rVOeiKVK(b2B3r$gaF0rxtS%plT`pxY*hmvV +zS6{;L=@l(zOuP}FnLV$a*X8VkAIS`jH3sE4!^*UDqa*wz7J+^+X>4;g|3^dH6j^mc +zpsusFfaRrvuQb9t!(lCP7^D#!otYNOu9p!Wqfwy@F5L#ImjYb`?@n1N_InYVs>9Tp`=myv}2o$p(Yse*N8&q@C +zhV6aSsmO;-AY2;8G`{ufAyhI}mi8qECuAq&z$7|&V@&8mdb$sk<+euFd6yf|kE4f2 +zI_ij_b(B>N&n9M=)5b0x4JH$#EIex*20oStRQwgzUa*bX3FhlNPqTIM7ZE;;Y~QSn +zBtS+4LS!%rlAgS+a6$cxI6fSiH^|_9Ms&%vjdOS6jB*w6k!I%6f|NZF+#jSrqTvGMOW+;nqWK^q`Z6w9)??vx~VLvJ#4iMYWZ6jgjb!xVkw}UtUcpz7k#pSi~r#VDk?w +zbM;cnlhb;$U(AXQZ5?%?NGm(6^WPs1HjZ*{H;$&e +zUOrwNS<(XKTEFjA8A{7G&=kh7#lgfxLer;SRRxl07&!?}q_!=EntOrCpvXQ0Q8{8Q +z*9-9`jy^~#A~g)5j_nku9`p9#=)%P=K%3f7HBP?gonwh=vHG-}N{ymi_)@BcQN2P* +zxe6vfEnve)OJ6GE()C`1ZEJ}|-M}P;?{ji9Ru`uBoXM1Uvb_k6!T{3eB>j&~WvWey +ztl=@4g9w`Ahbi%O`D*sfW8}>44CL4WSt?(ORVz%vQqG7m5B7qjvB>dxn}a5u(E|7; +z$>)moxADI;8C$uH(0z?qQm!?Mtw1v{QeEagg=wK_{dTZ +z1}N8vEJlP#d%q!1!gcuG4<+Ck>8EUPf>VkU=_IWcZ%4C4vW;0?fv+X(b;YrkK(->- +z0A3}EGzWB9IcNql9v!=y&IRA9f;W6HSX?RjTQwIB4*q~ufdUE)KlIwyLB-|Fb?};YHzExDZx4_=xoK#%>TbFrLyNYCp +z_lEqasqbp?W2nzGoR&E?ixfY;bo@fK{%{7NKesWv`T&3Zsaq6K0Y#4BR)n +zWwIYWqwwI?sM}V5AP$)TjbFW_iIp4*omNXvWfDWDH*5( +zf!DppSV4%I&P|aWIy<%JoFga?1D%XuS9Iq#_*vO=(KykXge%VT*{*o0I|jjfFT$!7 +z!m5~w=QtS5e3wJn10VGhltbGBpe2#a>65dg;eq!F0>E(HKYSI_!Fq>4tkUG%z*QzX +zWh+ACkg52|stYZRjvjpiri)%;kI~R>U|b^X^@i@YtGczDdQad~MU;c#F1uSgwd`IW +z$%(oqZ>nYw#U3J#m(y!QLcA{62B8MJiwvs1-lENbpi+i^ +z^A`Q%W%|>*Gfr;Ba^fw@qcjBytZnAv4NM2> +zDH{&ngR%?Db&W}xTH?HDKS_JpVSzP8asRnji{(g&9 +zZ?j5VqJ!p=o(S?vING2D65!RAc*9epkpx!kA=d`^`6v&y5sDilmC_;o6~?KYbg +z?d-^X1&^uJJW3ChSi9zrNY%{cVmD!xhE}0%ff0jdo}e~dhobUM<*%ufASbCT~?pdIIIluw1DmLGsOu&E%-9t~<^KlfLq +z%YZ1CgOD=v(L+#HN%GF8>KpACP+Z_{&8pF*&<_bC#k69SmDC!tOk+`ypIr> +z-A}F>J~B7plp7-VrZ(yy7F(By@69bs`g)3zan78ZWX(dg5^gp&pgC>1#^Q_Yxt&cvc +zS#R|y2t>t$a)Y6(5Ru`6m-7$u;Zz(34&$#Y+6Ph~T~9!QNqcKNKb#CZl<}LIu-PrA +zT(vnLEm$s}_l?r70HK@=wIgi3z%UuF%G4eA3LyEI$PurQY$n?m5}^>_O|#ptl|mSw +z!J{?y*2# +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 +z`wN2^?r#~MJH(0tGE4kzH>8zd +zTu%YIfd!Dn;;%zR67bjGbOS)=)=t~ZjQCF#(dVr_a#2k<01lf-FFc>tMA!90Ux$MR2rW{8~NxN2`$z10Vyx3>aL5Kac=dhyXk3Z*Kg*t^56ygD}TW +z#sw@2`+%oL@E15E!0PxLxIg>l_h`>Eul@k+diPs2k>BTGecmO{^IZMFEz|f1+}~xr +zdJg?OH_;Dhd9B|<|G6^$CR5RK=;x_nen8Xd{_n;5=Ysp&m+Q}f-yJ8v1AHGx-wR+K +z{Zt|WfIoHCcahrXm7k}E`BA&=*UCTbH2?BV_J<+#*Utamy61^Geh_3C{L;E#34Tq} +z@%#>+XF>Qu5NG^P1V8(aKF55n!Tkf%)bgJ&|9mR{N|E~)&Sy%>KR7XM{srf6R+;aQ +z|G8%I4^(j5e}VePL;B;kezN;LZ~Jp`+#f6;_P?Yr5xpOZb8Vf#Twtzf&Wj3iqCK5 +z`6l1OI@RYxyVmKj+`?jl$1Ko?Fs> +zkl26vh2*~r{7<9$|5!twTStDt19kpyOZRuSlK-~#zm}1ots3908$XIH0POGVByT0a +W0pC&I?SueJAtDe^1>pYz0sTMJrJ5N4 + +diff --git a/platforms/sdl/sdl2/android/gradle/wrapper/gradle-wrapper.properties b/platforms/sdl/sdl2/android/gradle/wrapper/gradle-wrapper.properties +index 099f1c8c0..37f853b1c 100644 +--- a/platforms/sdl/sdl2/android/gradle/wrapper/gradle-wrapper.properties ++++ b/platforms/sdl/sdl2/android/gradle/wrapper/gradle-wrapper.properties +@@ -1,6 +1,7 @@ +-#Thu Nov 11 18:20:34 PST 2021 + distributionBase=GRADLE_USER_HOME +-distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip + distributionPath=wrapper/dists +-zipStorePath=wrapper/dists ++distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip ++networkTimeout=10000 ++validateDistributionUrl=true + zipStoreBase=GRADLE_USER_HOME ++zipStorePath=wrapper/dists +diff --git a/platforms/sdl/sdl2/android/gradlew b/platforms/sdl/sdl2/android/gradlew +index 9d82f7891..1aa94a426 100755 +--- a/platforms/sdl/sdl2/android/gradlew ++++ b/platforms/sdl/sdl2/android/gradlew +@@ -1,74 +1,127 @@ +-#!/usr/bin/env bash ++#!/bin/sh ++ ++# ++# Copyright © 2015-2021 the original authors. ++# ++# Licensed under the Apache License, Version 2.0 (the "License"); ++# you may not use this file except in compliance with the License. ++# You may obtain a copy of the License at ++# ++# https://www.apache.org/licenses/LICENSE-2.0 ++# ++# Unless required by applicable law or agreed to in writing, software ++# distributed under the License is distributed on an "AS IS" BASIS, ++# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ++# See the License for the specific language governing permissions and ++# limitations under the License. ++# + + ############################################################################## +-## +-## Gradle start up script for UN*X +-## ++# ++# Gradle start up script for POSIX generated by Gradle. ++# ++# Important for running: ++# ++# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is ++# noncompliant, but you have some other compliant shell such as ksh or ++# bash, then to run this script, type that shell name before the whole ++# command line, like: ++# ++# ksh Gradle ++# ++# Busybox and similar reduced shells will NOT work, because this script ++# requires all of these POSIX shell features: ++# * functions; ++# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», ++# «${var#prefix}», «${var%suffix}», and «$( cmd )»; ++# * compound commands having a testable exit status, especially «case»; ++# * various built-in commands including «command», «set», and «ulimit». ++# ++# Important for patching: ++# ++# (2) This script targets any POSIX shell, so it avoids extensions provided ++# by Bash, Ksh, etc; in particular arrays are avoided. ++# ++# The "traditional" practice of packing multiple parameters into a ++# space-separated string is a well documented source of bugs and security ++# problems, so this is (mostly) avoided, by progressively accumulating ++# options in "$@", and eventually passing that to Java. ++# ++# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, ++# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; ++# see the in-line comments for details. ++# ++# There are tweaks for specific operating systems such as AIX, CygWin, ++# Darwin, MinGW, and NonStop. ++# ++# (3) This script is generated from the Groovy template ++# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt ++# within the Gradle project. ++# ++# You can find Gradle at https://github.com/gradle/gradle/. ++# + ############################################################################## + +-# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +-DEFAULT_JVM_OPTS="" ++# Attempt to set APP_HOME ++ ++# Resolve links: $0 may be a link ++app_path=$0 ++ ++# Need this for daisy-chained symlinks. ++while ++ APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path ++ [ -h "$app_path" ] ++do ++ ls=$( ls -ld "$app_path" ) ++ link=${ls#*' -> '} ++ case $link in #( ++ /*) app_path=$link ;; #( ++ *) app_path=$APP_HOME$link ;; ++ esac ++done + +-APP_NAME="Gradle" +-APP_BASE_NAME=`basename "$0"` ++# This is normally unused ++# shellcheck disable=SC2034 ++APP_BASE_NAME=${0##*/} ++# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) ++APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit + + # Use the maximum available, or set MAX_FD != -1 to use that value. +-MAX_FD="maximum" ++MAX_FD=maximum + +-warn ( ) { ++warn () { + echo "$*" +-} ++} >&2 + +-die ( ) { ++die () { + echo + echo "$*" + echo + exit 1 +-} ++} >&2 + + # OS specific support (must be 'true' or 'false'). + cygwin=false + msys=false + darwin=false +-case "`uname`" in +- CYGWIN* ) +- cygwin=true +- ;; +- Darwin* ) +- darwin=true +- ;; +- MINGW* ) +- msys=true +- ;; ++nonstop=false ++case "$( uname )" in #( ++ CYGWIN* ) cygwin=true ;; #( ++ Darwin* ) darwin=true ;; #( ++ MSYS* | MINGW* ) msys=true ;; #( ++ NONSTOP* ) nonstop=true ;; + esac + +-# Attempt to set APP_HOME +-# Resolve links: $0 may be a link +-PRG="$0" +-# Need this for relative symlinks. +-while [ -h "$PRG" ] ; do +- ls=`ls -ld "$PRG"` +- link=`expr "$ls" : '.*-> \(.*\)$'` +- if expr "$link" : '/.*' > /dev/null; then +- PRG="$link" +- else +- PRG=`dirname "$PRG"`"/$link" +- fi +-done +-SAVED="`pwd`" +-cd "`dirname \"$PRG\"`/" >/dev/null +-APP_HOME="`pwd -P`" +-cd "$SAVED" >/dev/null +- + CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + ++ + # Determine the Java command to use to start the JVM. + if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables +- JAVACMD="$JAVA_HOME/jre/sh/java" ++ JAVACMD=$JAVA_HOME/jre/sh/java + else +- JAVACMD="$JAVA_HOME/bin/java" ++ JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME +@@ -77,84 +130,120 @@ Please set the JAVA_HOME variable in your environment to match the + location of your Java installation." + fi + else +- JAVACMD="java" +- which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. ++ JAVACMD=java ++ if ! command -v java >/dev/null 2>&1 ++ then ++ die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + + Please set the JAVA_HOME variable in your environment to match the + location of your Java installation." ++ fi + fi + + # Increase the maximum file descriptors if we can. +-if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then +- MAX_FD_LIMIT=`ulimit -H -n` +- if [ $? -eq 0 ] ; then +- if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then +- MAX_FD="$MAX_FD_LIMIT" +- fi +- ulimit -n $MAX_FD +- if [ $? -ne 0 ] ; then +- warn "Could not set maximum file descriptor limit: $MAX_FD" +- fi +- else +- warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" +- fi ++if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then ++ case $MAX_FD in #( ++ max*) ++ # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. ++ # shellcheck disable=SC2039,SC3045 ++ MAX_FD=$( ulimit -H -n ) || ++ warn "Could not query maximum file descriptor limit" ++ esac ++ case $MAX_FD in #( ++ '' | soft) :;; #( ++ *) ++ # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. ++ # shellcheck disable=SC2039,SC3045 ++ ulimit -n "$MAX_FD" || ++ warn "Could not set maximum file descriptor limit to $MAX_FD" ++ esac + fi + +-# For Darwin, add options to specify how the application appears in the dock +-if $darwin; then +- GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +-fi ++# Collect all arguments for the java command, stacking in reverse order: ++# * args from the command line ++# * the main class name ++# * -classpath ++# * -D...appname settings ++# * --module-path (only if needed) ++# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. ++ ++# For Cygwin or MSYS, switch paths to Windows format before running java ++if "$cygwin" || "$msys" ; then ++ APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) ++ CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) ++ ++ JAVACMD=$( cygpath --unix "$JAVACMD" ) + +-# For Cygwin, switch paths to Windows format before running java +-if $cygwin ; then +- APP_HOME=`cygpath --path --mixed "$APP_HOME"` +- CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` +- JAVACMD=`cygpath --unix "$JAVACMD"` +- +- # We build the pattern for arguments to be converted via cygpath +- ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` +- SEP="" +- for dir in $ROOTDIRSRAW ; do +- ROOTDIRS="$ROOTDIRS$SEP$dir" +- SEP="|" +- done +- OURCYGPATTERN="(^($ROOTDIRS))" +- # Add a user-defined pattern to the cygpath arguments +- if [ "$GRADLE_CYGPATTERN" != "" ] ; then +- OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" +- fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh +- i=0 +- for arg in "$@" ; do +- CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` +- CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option +- +- if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition +- eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` +- else +- eval `echo args$i`="\"$arg\"" ++ for arg do ++ if ++ case $arg in #( ++ -*) false ;; # don't mess with options #( ++ /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath ++ [ -e "$t" ] ;; #( ++ *) false ;; ++ esac ++ then ++ arg=$( cygpath --path --ignore --mixed "$arg" ) + fi +- i=$((i+1)) ++ # Roll the args list around exactly as many times as the number of ++ # args, so each arg winds up back in the position where it started, but ++ # possibly modified. ++ # ++ # NB: a `for` loop captures its iteration list before it begins, so ++ # changing the positional parameters here affects neither the number of ++ # iterations, nor the values presented in `arg`. ++ shift # remove old arg ++ set -- "$@" "$arg" # push replacement arg + done +- case $i in +- (0) set -- ;; +- (1) set -- "$args0" ;; +- (2) set -- "$args0" "$args1" ;; +- (3) set -- "$args0" "$args1" "$args2" ;; +- (4) set -- "$args0" "$args1" "$args2" "$args3" ;; +- (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; +- (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; +- (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; +- (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; +- (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; +- esac + fi + +-# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +-function splitJvmOpts() { +- JVM_OPTS=("$@") +-} +-eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +-JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +-exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" ++# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. ++DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' ++ ++# Collect all arguments for the java command: ++# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, ++# and any embedded shellness will be escaped. ++# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be ++# treated as '${Hostname}' itself on the command line. ++ ++set -- \ ++ "-Dorg.gradle.appname=$APP_BASE_NAME" \ ++ -classpath "$CLASSPATH" \ ++ org.gradle.wrapper.GradleWrapperMain \ ++ "$@" ++ ++# Stop when "xargs" is not available. ++if ! command -v xargs >/dev/null 2>&1 ++then ++ die "xargs is not available" ++fi ++ ++# Use "xargs" to parse quoted args. ++# ++# With -n1 it outputs one arg per line, with the quotes and backslashes removed. ++# ++# In Bash we could simply go: ++# ++# readarray ARGS < <( xargs -n1 <<<"$var" ) && ++# set -- "${ARGS[@]}" "$@" ++# ++# but POSIX shell has neither arrays nor command substitution, so instead we ++# post-process each arg (as a line of input to sed) to backslash-escape any ++# character that might be a shell metacharacter, then use eval to reverse ++# that process (while maintaining the separation between arguments), and wrap ++# the whole thing up as a single "set" statement. ++# ++# This will of course break if any of these variables contains a newline or ++# an unmatched quote. ++# ++ ++eval "set -- $( ++ printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | ++ xargs -n1 | ++ sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | ++ tr '\n' ' ' ++ )" '"$@"' ++ ++exec "$JAVACMD" "$@" +diff --git a/platforms/sdl/sdl2/android/gradlew.bat b/platforms/sdl/sdl2/android/gradlew.bat +index 8a0b282aa..93e3f59f1 100644 +--- a/platforms/sdl/sdl2/android/gradlew.bat ++++ b/platforms/sdl/sdl2/android/gradlew.bat +@@ -1,4 +1,20 @@ +-@if "%DEBUG%" == "" @echo off ++@rem ++@rem Copyright 2015 the original author or authors. ++@rem ++@rem Licensed under the Apache License, Version 2.0 (the "License"); ++@rem you may not use this file except in compliance with the License. ++@rem You may obtain a copy of the License at ++@rem ++@rem https://www.apache.org/licenses/LICENSE-2.0 ++@rem ++@rem Unless required by applicable law or agreed to in writing, software ++@rem distributed under the License is distributed on an "AS IS" BASIS, ++@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ++@rem See the License for the specific language governing permissions and ++@rem limitations under the License. ++@rem ++ ++@if "%DEBUG%"=="" @echo off + @rem ########################################################################## + @rem + @rem Gradle startup script for Windows +@@ -8,20 +24,24 @@ + @rem Set local scope for the variables with windows NT shell + if "%OS%"=="Windows_NT" setlocal + +-@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +-set DEFAULT_JVM_OPTS= +- + set DIRNAME=%~dp0 +-if "%DIRNAME%" == "" set DIRNAME=. ++if "%DIRNAME%"=="" set DIRNAME=. ++@rem This is normally unused + set APP_BASE_NAME=%~n0 + set APP_HOME=%DIRNAME% + ++@rem Resolve any "." and ".." in APP_HOME to make it shorter. ++for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi ++ ++@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. ++set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" ++ + @rem Find java.exe + if defined JAVA_HOME goto findJavaFromJavaHome + + set JAVA_EXE=java.exe + %JAVA_EXE% -version >NUL 2>&1 +-if "%ERRORLEVEL%" == "0" goto init ++if %ERRORLEVEL% equ 0 goto execute + + echo. + echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +@@ -35,7 +55,7 @@ goto fail + set JAVA_HOME=%JAVA_HOME:"=% + set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +-if exist "%JAVA_EXE%" goto init ++if exist "%JAVA_EXE%" goto execute + + echo. + echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +@@ -45,44 +65,26 @@ echo location of your Java installation. + + goto fail + +-:init +-@rem Get command-line arguments, handling Windowz variants +- +-if not "%OS%" == "Windows_NT" goto win9xME_args +-if "%@eval[2+2]" == "4" goto 4NT_args +- +-:win9xME_args +-@rem Slurp the command line arguments. +-set CMD_LINE_ARGS= +-set _SKIP=2 +- +-:win9xME_args_slurp +-if "x%~1" == "x" goto execute +- +-set CMD_LINE_ARGS=%* +-goto execute +- +-:4NT_args +-@rem Get arguments from the 4NT Shell from JP Software +-set CMD_LINE_ARGS=%$ +- + :execute + @rem Setup the command line + + set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + ++ + @rem Execute Gradle +-"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% ++"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + + :end + @rem End local scope for the variables with windows NT shell +-if "%ERRORLEVEL%"=="0" goto mainEnd ++if %ERRORLEVEL% equ 0 goto mainEnd + + :fail + rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of + rem the _cmd.exe /c_ return code! +-if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +-exit /b 1 ++set EXIT_CODE=%ERRORLEVEL% ++if %EXIT_CODE% equ 0 set EXIT_CODE=1 ++if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% ++exit /b %EXIT_CODE% + + :mainEnd + if "%OS%"=="Windows_NT" endlocal +diff --git a/thirdparty/SDL2/src b/thirdparty/SDL2/src +index 9959e840b..cd6fbfbb0 160000 +--- a/thirdparty/SDL2/src ++++ b/thirdparty/SDL2/src +@@ -1 +1 @@ +-Subproject commit 9959e840b6b74a19a0ca716cc4249b6ca8512b0e ++Subproject commit cd6fbfbb035452a991b9762ecbb888b48ec84c1f + +From 6a2067b28f28ad4a6fa36434ea3746472bce0efd Mon Sep 17 00:00:00 2001 +From: Brent <43089001+BrentDaMage@users.noreply.github.com> +Date: Fri, 20 Feb 2026 17:27:58 -0800 +Subject: [PATCH 6/9] Fixed crash (#497) + +--- + source/client/renderer/Textures.cpp | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/source/client/renderer/Textures.cpp b/source/client/renderer/Textures.cpp +index d1e1a8e86..5fdc17b52 100644 +--- a/source/client/renderer/Textures.cpp ++++ b/source/client/renderer/Textures.cpp +@@ -61,8 +61,11 @@ size_t _mipTagStart(const std::string& path) + constexpr size_t mipSuffixLength = MIP_TAG_SIZE + 2; + + std::string extension = Util::getExtension(path); ++ size_t len = path.length() - extension.length(); ++ if (len <= mipSuffixLength) ++ return 0; + +- return path.length() - mipSuffixLength - extension.length(); ++ return len - mipSuffixLength; + } + + bool _isMipmap(const std::string& path) + +From b3e07ef8737fd13c4662deb7a9e4544df8d4cf1c Mon Sep 17 00:00:00 2001 +From: Un1q32 +Date: Sat, 21 Feb 2026 17:29:20 -0500 +Subject: [PATCH 7/9] XDG base directory spec compliance (#498) + +--- + platforms/ios/build.sh | 6 ++---- + platforms/macos/build.sh | 4 ++-- + platforms/sdl/sdl1/main.cpp | 17 +++++++++++++++-- + platforms/sdl/sdl2/main.cpp | 15 ++++++++++++++- + 4 files changed, 33 insertions(+), 9 deletions(-) + +diff --git a/platforms/ios/build.sh b/platforms/ios/build.sh +index 43bd17764..8e4a951e5 100755 +--- a/platforms/ios/build.sh ++++ b/platforms/ios/build.sh +@@ -85,10 +85,8 @@ else + fi + # ensure we use ccache for the toolchain build + ccache="$(command -v ccache || true)" +-printf '#!/bin/sh\n +- exec %sclang "$@"\n' "$ccache " > bin/remcpe-clang +-printf '#!/bin/sh\n +- exec %sclang++ "$@"\n' "$ccache " > bin/remcpe-clang++ ++printf '#!/bin/sh\nexec %s clang "$@"\n' "$ccache" > bin/remcpe-clang ++printf '#!/bin/sh\nexec %s clang++ "$@"\n' "$ccache" > bin/remcpe-clang++ + chmod +x bin/remcpe-clang bin/remcpe-clang++ + + if [ -n "$outdated_toolchain" ]; then +diff --git a/platforms/macos/build.sh b/platforms/macos/build.sh +index 441fcd2a8..3e31399a6 100755 +--- a/platforms/macos/build.sh ++++ b/platforms/macos/build.sh +@@ -109,8 +109,8 @@ else + fi + # ensure we use ccache for the toolchain build + ccache="$(command -v ccache || true)" +-printf '#!/bin/sh\nexec %sclang "$@"\n' "$ccache " > bin/remcpe-clang +-printf '#!/bin/sh\nexec %sclang++ "$@"\n' "$ccache " > bin/remcpe-clang++ ++printf '#!/bin/sh\nexec %s clang "$@"\n' "$ccache" > bin/remcpe-clang ++printf '#!/bin/sh\nexec %s clang++ "$@"\n' "$ccache" > bin/remcpe-clang++ + chmod +x bin/remcpe-clang bin/remcpe-clang++ + + if [ -n "$outdated_toolchain" ]; then +diff --git a/platforms/sdl/sdl1/main.cpp b/platforms/sdl/sdl1/main.cpp +index 2bc5c4b62..4e08fc1b9 100644 +--- a/platforms/sdl/sdl1/main.cpp ++++ b/platforms/sdl/sdl1/main.cpp +@@ -92,10 +92,23 @@ static std::string getStoragePath() + #ifdef _WIN32 + pathBase = getenv("APPDATA"); + #else +- pathBase = getenv("HOME"); ++ const char *xdg_data = getenv("XDG_DATA_HOME"); ++ if (xdg_data) ++ pathBase = xdg_data; ++ else ++ { ++ xdg_data = getenv("HOME"); ++ if (!xdg_data) ++ { ++ LOG_E("HOME not set"); ++ pathBase = ""; // current working directory ++ } ++ else ++ pathBase = ((std::string)xdg_data + "/.local/share").c_str(); ++ } + #endif + +- if (pathBase == nullptr || pathBase[0] == '\0') ++ if (!pathBase) + pathBase = ""; // just use the current working directory + + std::string path(pathBase); +diff --git a/platforms/sdl/sdl2/main.cpp b/platforms/sdl/sdl2/main.cpp +index fa228b636..3cbaabbe5 100644 +--- a/platforms/sdl/sdl2/main.cpp ++++ b/platforms/sdl/sdl2/main.cpp +@@ -420,7 +420,20 @@ int main(int argc, char *argv[]) + #elif defined(ANDROID) + storagePath = SDL_AndroidGetExternalStoragePath(); + #else +- storagePath = getenv("HOME"); ++ const char *xdg_data = getenv("XDG_DATA_HOME"); ++ if (xdg_data) ++ storagePath = xdg_data; ++ else ++ { ++ xdg_data = getenv("HOME"); ++ if (!xdg_data) ++ { ++ LOG_E("HOME not set"); ++ storagePath = "."; // current working directory ++ } ++ else ++ storagePath = (std::string)xdg_data + "/.local/share"; ++ } + #endif + storagePath += "/.reminecraftpe"; + + +From a31e7ce4d1b5814b075c1d23e27c62755a025cef Mon Sep 17 00:00:00 2001 +From: Brent <43089001+BrentDaMage@users.noreply.github.com> +Date: Sat, 21 Feb 2026 15:34:20 -0800 +Subject: [PATCH 8/9] Fixed ClassicCraftingScreen_Console positioning (#499) + +--- + .../gui/screens/inventory/ClassicCraftingScreen_Console.cpp | 5 ----- + .../gui/screens/inventory/ClassicCraftingScreen_Console.hpp | 1 - + source/network/MinecraftPackets.cpp | 1 + + source/network/Packet.hpp | 6 ++++-- + 4 files changed, 5 insertions(+), 8 deletions(-) + +diff --git a/source/client/gui/screens/inventory/ClassicCraftingScreen_Console.cpp b/source/client/gui/screens/inventory/ClassicCraftingScreen_Console.cpp +index 56619d6bb..f81d568a5 100644 +--- a/source/client/gui/screens/inventory/ClassicCraftingScreen_Console.cpp ++++ b/source/client/gui/screens/inventory/ClassicCraftingScreen_Console.cpp +@@ -6,11 +6,6 @@ ClassicCraftingScreen_Console::ClassicCraftingScreen_Console(Inventory* inventor + ContainerScreen(new CraftingMenu(inventory, tilePos, level)) + { + m_uiTheme = UI_CONSOLE; +-} +- +-void ClassicCraftingScreen_Console::init() +-{ +- ContainerScreen::init(); + m_imageWidth = 428; + m_imageHeight = 450; + } +diff --git a/source/client/gui/screens/inventory/ClassicCraftingScreen_Console.hpp b/source/client/gui/screens/inventory/ClassicCraftingScreen_Console.hpp +index d5e5b5091..e74ce590e 100644 +--- a/source/client/gui/screens/inventory/ClassicCraftingScreen_Console.hpp ++++ b/source/client/gui/screens/inventory/ClassicCraftingScreen_Console.hpp +@@ -7,7 +7,6 @@ class ClassicCraftingScreen_Console : public ContainerScreen + public: + ClassicCraftingScreen_Console(Inventory* inventory, const TilePos& tilePos, Level* level); + +- void init() override; + void renderBackground() override; + + protected: +diff --git a/source/network/MinecraftPackets.cpp b/source/network/MinecraftPackets.cpp +index 297124cfb..0fe7ddaa1 100644 +--- a/source/network/MinecraftPackets.cpp ++++ b/source/network/MinecraftPackets.cpp +@@ -58,6 +58,7 @@ Packet* MinecraftPackets::createPacket(MinecraftPacketIds id) + case PACKET_CONTAINER_SET_DATA: return new ContainerSetDataPacket; + case PACKET_CONTAINER_SET_CONTENT: return new ContainerSetContentPacket; + case PACKET_CONTAINER_ACK: return new ContainerAckPacket; ++ case PACKET_CHAT: return nullptr; + + case PACKET_LEVEL_DATA: return new LevelDataPacket; + } +diff --git a/source/network/Packet.hpp b/source/network/Packet.hpp +index 4825bee6f..12fe4688d 100644 +--- a/source/network/Packet.hpp ++++ b/source/network/Packet.hpp +@@ -13,12 +13,13 @@ + #include "BitStream.h" + #include "MessageIdentifiers.h" + +-#define NETWORK_PROTOCOL_VERSION_MIN 6 // the packet IDs changed completely between 2, 3, 4, 5, and 6 ++#define NETWORK_PROTOCOL_VERSION_MIN 6 // the packet IDs changed completely between 2 thru 6 + //#define NETWORK_PROTOCOL_VERSION 2 // 0.1.0 (actual client crashes with unrecognized tiles) + //#define NETWORK_PROTOCOL_VERSION 3 // 0.2.0 (actual client crashes with unrecognized entities) + //#define NETWORK_PROTOCOL_VERSION 4 // 0.3.0 + //#define NETWORK_PROTOCOL_VERSION 5 // 0.3.2 +-#define NETWORK_PROTOCOL_VERSION 6 // 0.3.3 ++#define NETWORK_PROTOCOL_VERSION 6 // 0.3.3 ++//#define NETWORK_PROTOCOL_VERSION 7 // 0.4.0 + + class NetEventCallback; + class Level; +@@ -262,6 +263,7 @@ enum MinecraftPacketIds + PACKET_CONTAINER_SET_DATA, + PACKET_CONTAINER_SET_CONTENT, + PACKET_CONTAINER_ACK, // Unused in PE ++ PACKET_CHAT, + + PACKET_LEVEL_DATA = 200 + + +From 7bdeeec7510261bc50d5c60a4ce07e087efede44 Mon Sep 17 00:00:00 2001 +From: Un1q32 +Date: Sat, 21 Feb 2026 18:50:11 -0500 +Subject: [PATCH 9/9] Enabled Auto-Jump by Default for Touchscreens (#500) + +--- + source/client/options/Options.cpp | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/source/client/options/Options.cpp b/source/client/options/Options.cpp +index 4e65f5d71..4aea60bbc 100644 +--- a/source/client/options/Options.cpp ++++ b/source/client/options/Options.cpp +@@ -80,7 +80,7 @@ Options::Options(Minecraft* mc, const std::string& folderPath) : + , m_flightHax("misc_flycheat", "options.flightHax", false) + , m_playerName("mp_username", "options.username", "Steve") + , m_serverVisibleDefault("mp_server_visible_default", "options.serverVisibleDefault", true) +- , m_autoJump("ctrl_autojump", "options.autoJump", false) ++ , m_autoJump("ctrl_autojump", "options.autoJump", mc->platform()->isTouchscreen()) + , m_debugText("info_debugtext", "options.debugText", false) + , m_blockOutlines("gfx_blockoutlines", "options.blockOutlines", false) + , m_fancyGrass("gfx_fancygrass", "options.fancyGrass", true) diff --git a/478.patch.txt b/478.patch.txt new file mode 100644 index 000000000..2b014e0d9 --- /dev/null +++ b/478.patch.txt @@ -0,0 +1,10726 @@ +From 8c687e0cc188c5abba28105c742e43919e29b472 Mon Sep 17 00:00:00 2001 +From: hellosammu +Date: Sun, 15 Feb 2026 09:33:45 -0500 +Subject: [PATCH 01/22] clean branch + +--- + source/CMakeLists.txt | 16 +- + source/client/app/NinecraftApp.cpp | 4 +- + .../gui/screens/inventory/FurnaceScreen.cpp | 57 +++++ + .../gui/screens/inventory/FurnaceScreen.hpp | 20 ++ + source/client/player/LocalPlayer.cpp | 5 +- + source/client/player/LocalPlayer.hpp | 2 +- + source/client/renderer/Chunk.cpp | 49 +++- + source/client/renderer/Chunk.hpp | 5 +- + source/client/renderer/LevelRenderer.cpp | 19 +- + source/client/renderer/LevelRenderer.hpp | 1 + + source/client/sound/SoundRepository.cpp | 2 +- + source/client/sound/sound_list.h | 12 + + source/common/Mth.cpp | 4 +- + source/world/Facing.cpp | 19 ++ + source/world/Facing.hpp | 4 +- + source/world/entity/Player.cpp | 5 + + source/world/entity/Player.hpp | 3 +- + source/world/inventory/FurnaceMenu.cpp | 100 ++++++++ + source/world/inventory/FurnaceMenu.hpp | 24 ++ + source/world/inventory/FurnaceResultSlot.cpp | 19 ++ + source/world/inventory/FurnaceResultSlot.hpp | 16 ++ + source/world/inventory/SimpleContainer.cpp | 6 +- + source/world/inventory/SimpleContainer.hpp | 31 +-- + source/world/item/CoalItem.cpp | 12 + + source/world/item/CoalItem.hpp | 12 + + source/world/item/Item.cpp | 17 +- + source/world/item/crafting/FurnaceRecipes.cpp | 2 + + source/world/item/crafting/Recipes.cpp | 26 +-- + source/world/level/Level.cpp | 74 ++++++ + source/world/level/Level.hpp | 8 + + source/world/level/Region.cpp | 5 + + source/world/level/Region.hpp | 1 + + .../level/levelgen/chunk/ChunkTilePos.hpp | 9 +- + .../world/level/levelgen/chunk/LevelChunk.cpp | 69 ++++++ + .../world/level/levelgen/chunk/LevelChunk.hpp | 7 + + .../storage/ExternalFileLevelStorage.cpp | 30 +++ + source/world/level/storage/LevelSource.hpp | 3 + + source/world/particle/BubbleParticle.cpp | 10 +- + source/world/particle/ExplodeParticle.cpp | 10 +- + source/world/particle/FlameParticle.cpp | 14 +- + source/world/particle/LavaParticle.cpp | 16 +- + source/world/particle/NoteParticle.cpp | 47 ++++ + source/world/particle/Particle.cpp | 30 +-- + source/world/particle/Particle.hpp | 22 +- + source/world/particle/RedDustParticle.cpp | 14 +- + source/world/particle/SmokeParticle.cpp | 14 +- + source/world/particle/TerrainParticle.cpp | 18 +- + source/world/phys/Vec3.hpp | 7 + + source/world/tile/ChestTile.cpp | 217 ++++++++++++++++++ + source/world/tile/ChestTile.hpp | 20 ++ + source/world/tile/CropsTile.cpp | 2 +- + source/world/tile/FurnaceTile.cpp | 194 ++++++++++++++++ + source/world/tile/FurnaceTile.hpp | 33 +++ + source/world/tile/MusicTile.cpp | 98 ++++++++ + source/world/tile/MusicTile.hpp | 17 ++ + source/world/tile/Tile.cpp | 55 ++++- + source/world/tile/Tile.hpp | 9 +- + source/world/tile/entity/ChestTileEntity.cpp | 31 +++ + source/world/tile/entity/ChestTileEntity.hpp | 17 ++ + .../world/tile/entity/FurnaceTileEntity.cpp | 163 +++++++++++++ + .../world/tile/entity/FurnaceTileEntity.hpp | 33 +++ + source/world/tile/entity/MusicTileEntity.cpp | 46 ++++ + source/world/tile/entity/MusicTileEntity.hpp | 21 ++ + source/world/tile/entity/TileEntity.cpp | 106 +++++++++ + source/world/tile/entity/TileEntity.hpp | 50 ++++ + source/world/tile/entity/TileEntityType.cpp | 51 ++++ + source/world/tile/entity/TileEntityType.hpp | 50 ++++ + 67 files changed, 1978 insertions(+), 135 deletions(-) + create mode 100644 source/client/gui/screens/inventory/FurnaceScreen.cpp + create mode 100644 source/client/gui/screens/inventory/FurnaceScreen.hpp + create mode 100644 source/world/Facing.cpp + create mode 100644 source/world/inventory/FurnaceMenu.cpp + create mode 100644 source/world/inventory/FurnaceMenu.hpp + create mode 100644 source/world/inventory/FurnaceResultSlot.cpp + create mode 100644 source/world/inventory/FurnaceResultSlot.hpp + create mode 100644 source/world/item/CoalItem.cpp + create mode 100644 source/world/item/CoalItem.hpp + create mode 100644 source/world/particle/NoteParticle.cpp + create mode 100644 source/world/tile/ChestTile.cpp + create mode 100644 source/world/tile/ChestTile.hpp + create mode 100644 source/world/tile/FurnaceTile.cpp + create mode 100644 source/world/tile/FurnaceTile.hpp + create mode 100644 source/world/tile/MusicTile.cpp + create mode 100644 source/world/tile/MusicTile.hpp + create mode 100644 source/world/tile/entity/ChestTileEntity.cpp + create mode 100644 source/world/tile/entity/ChestTileEntity.hpp + create mode 100644 source/world/tile/entity/FurnaceTileEntity.cpp + create mode 100644 source/world/tile/entity/FurnaceTileEntity.hpp + create mode 100644 source/world/tile/entity/MusicTileEntity.cpp + create mode 100644 source/world/tile/entity/MusicTileEntity.hpp + create mode 100644 source/world/tile/entity/TileEntity.cpp + create mode 100644 source/world/tile/entity/TileEntity.hpp + create mode 100644 source/world/tile/entity/TileEntityType.cpp + create mode 100644 source/world/tile/entity/TileEntityType.hpp + +diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt +index 36493fd02..afe085a83 100644 +--- a/source/CMakeLists.txt ++++ b/source/CMakeLists.txt +@@ -124,8 +124,9 @@ add_library(reminecraftpe-core STATIC + client/gui/screens/inventory/ContainerScreen.cpp + client/gui/screens/inventory/InventoryScreen.cpp + client/gui/screens/inventory/CraftingScreen.cpp +- client/gui/screens/inventory/ClassicCraftingScreen_Console.cpp ++ client/gui/screens/inventory/FurnaceScreen.cpp + client/gui/screens/inventory/ChestScreen.cpp ++ client/gui/screens/inventory/ClassicCraftingScreen_Console.cpp + client/gui/components/ScrolledSelectionList.cpp + client/gui/components/AvailableGamesList.cpp + client/gui/components/RolledSelectionList.cpp +@@ -350,6 +351,7 @@ add_library(reminecraftpe-core STATIC + world/item/TilePlanterItem.cpp + world/item/CameraItem.cpp + world/item/TileItem.cpp ++ world/item/CoalItem.cpp + world/item/Inventory.cpp + world/item/crafting/Recipes.cpp + world/item/crafting/FurnaceRecipes.cpp +@@ -360,10 +362,12 @@ add_library(reminecraftpe-core STATIC + world/inventory/ContainerMenu.cpp + world/inventory/InventoryMenu.cpp + world/inventory/CraftingMenu.cpp ++ world/inventory/FurnaceMenu.cpp + world/inventory/ChestMenu.cpp + world/inventory/Slot.cpp + world/inventory/ResultSlot.cpp + world/inventory/ArmorSlot.cpp ++ world/inventory/FurnaceResultSlot.cpp + world/inventory/CraftingContainer.cpp + world/inventory/ResultContainer.cpp + world/inventory/SimpleContainer.cpp +@@ -386,6 +390,7 @@ add_library(reminecraftpe-core STATIC + world/item/SlabItem.cpp + world/particle/RedDustParticle.cpp + world/particle/TerrainParticle.cpp ++ world/particle/NoteParticle.cpp + world/particle/BubbleParticle.cpp + world/particle/ExplodeParticle.cpp + world/particle/ParticleEngine.cpp +@@ -421,6 +426,9 @@ add_library(reminecraftpe-core STATIC + world/tile/OreTile.cpp + world/tile/StairTile.cpp + world/tile/SandStoneTile.cpp ++ world/tile/ChestTile.cpp ++ world/tile/FurnaceTile.cpp ++ world/tile/MusicTile.cpp + world/tile/FireTile.cpp + world/tile/StoneSlabTile.cpp + world/tile/LiquidTile.cpp +@@ -442,6 +450,12 @@ add_library(reminecraftpe-core STATIC + world/tile/Web.cpp + world/tile/FenceTile.cpp + world/tile/CraftingTableTile.cpp ++ world/tile/entity/TileEntity.cpp ++ world/tile/entity/TileEntityType.cpp ++ world/tile/entity/ChestTileEntity.cpp ++ world/tile/entity/FurnaceTileEntity.cpp ++ world/tile/entity/MusicTileEntity.cpp ++ world/Facing.cpp + renderer/GL/GL.cpp + renderer/Attribute.cpp + renderer/ConstantBufferMetaData.cpp +diff --git a/source/client/app/NinecraftApp.cpp b/source/client/app/NinecraftApp.cpp +index a591064f5..b805ddcd1 100644 +--- a/source/client/app/NinecraftApp.cpp ++++ b/source/client/app/NinecraftApp.cpp +@@ -10,6 +10,7 @@ + #include "world/item/Item.hpp" + #include "world/entity/MobCategory.hpp" + #include "world/entity/MobFactory.hpp" ++#include "world/tile/entity/TileEntityType.hpp" + #include "client/player/input/GameControllerHandler.hpp" + #include "client/player/input/Multitouch.hpp" + #include "client/gui/screens/StartMenuScreen.hpp" +@@ -182,7 +183,7 @@ void NinecraftApp::_initAll() + Tile::initTiles(); + Item::initItems(); + Biome::initBiomes(); +- //TileEntity::initTileEntities(); ++ TileEntityType::InitTileEntities(); + } + + _initOptions(); +@@ -340,6 +341,7 @@ void NinecraftApp::onGraphicsReset() + + void NinecraftApp::teardown() + { ++ TileEntityType::TeardownTileEntities(); + teardownRenderer(); + Resource::teardownLoaders(); + // Stop our SoundSystem before we nuke our sound buffers and cause it to implode +diff --git a/source/client/gui/screens/inventory/FurnaceScreen.cpp b/source/client/gui/screens/inventory/FurnaceScreen.cpp +new file mode 100644 +index 000000000..b28b6d538 +--- /dev/null ++++ b/source/client/gui/screens/inventory/FurnaceScreen.cpp +@@ -0,0 +1,57 @@ ++#include "FurnaceScreen.hpp" ++#include "world/inventory/FurnaceMenu.hpp" ++#include "renderer/ShaderConstants.hpp" ++ ++FurnaceScreen::FurnaceScreen(Inventory* inventory, FurnaceTileEntity* container) ++ : ContainerScreen(new FurnaceMenu(inventory, container)), m_inventory(inventory), m_furnace(container) ++{ ++ m_uiTheme = UI_JAVA; ++} ++ ++void FurnaceScreen::tick() ++{ ++ ContainerScreen::tick(); ++} ++ ++void FurnaceScreen::_renderLabels() ++{ ++ m_pFont->draw(m_furnace->getName(), 66, 6, 0x404040); ++ m_pFont->draw(m_inventory->getName(), 8, m_imageHeight - 96 + 2, 0x404040); ++} ++ ++void FurnaceScreen::_renderBg(float a) ++{ ++ currentShaderColor = Color::WHITE; ++ ++ m_pMinecraft->m_pTextures->loadAndBindTexture("gui/furnace.png"); ++ ++ blit(m_leftPos, m_topPos, 0, 0, m_imageWidth, m_imageHeight, 0, 0); ++ ++ int p; ++ if (m_furnace->isLit()) ++ { ++ p = m_furnace->getLitProgress(12); ++ blit(m_leftPos + 56, m_topPos + 36 + 12 - p, 176, 12 - p, 14, p + 2, 0, 0); ++ } ++ ++ p = m_furnace->getBurnProgress(24); ++ blit(m_leftPos + 79, m_topPos + 34, 176, 14, p + 1, 16, 0, 0); ++} ++ ++SlotDisplay FurnaceScreen::_createSlotDisplay(const Slot& slot) ++{ ++ constexpr int slotSize = 18; ++ switch (slot.m_group) ++ { ++ case Slot::INPUT: ++ return SlotDisplay(56, 17 + (slot.m_slot % 2) * (slotSize * 2)); ++ case Slot::OUTPUT: ++ return SlotDisplay(116, 35); ++ case Slot::INVENTORY: ++ return SlotDisplay(8 + (slot.m_slot % 9) * slotSize, 84 + ((slot.m_slot / 9) - 1) * slotSize, slotSize); ++ case Slot::HOTBAR: ++ return SlotDisplay(8 + (slot.m_slot % 9) * slotSize, 142, slotSize); ++ default: ++ return SlotDisplay(); ++ } ++} +\ No newline at end of file +diff --git a/source/client/gui/screens/inventory/FurnaceScreen.hpp b/source/client/gui/screens/inventory/FurnaceScreen.hpp +new file mode 100644 +index 000000000..5b84e36d3 +--- /dev/null ++++ b/source/client/gui/screens/inventory/FurnaceScreen.hpp +@@ -0,0 +1,20 @@ ++#pragma once ++ ++#include "ContainerScreen.hpp" ++#include "world/tile/entity/FurnaceTileEntity.hpp" ++ ++class FurnaceScreen : public ContainerScreen ++{ ++public: ++ FurnaceScreen(Inventory* inventory, FurnaceTileEntity* container); ++ void tick() override; ++ ++protected: ++ void _renderLabels() override; ++ void _renderBg(float a) override; ++ SlotDisplay _createSlotDisplay(const Slot&) override; ++ ++private: ++ Inventory* m_inventory; ++ FurnaceTileEntity* m_furnace; ++}; +diff --git a/source/client/player/LocalPlayer.cpp b/source/client/player/LocalPlayer.cpp +index ec62ed15b..8db2a1dcb 100644 +--- a/source/client/player/LocalPlayer.cpp ++++ b/source/client/player/LocalPlayer.cpp +@@ -13,6 +13,7 @@ + #include "network/packets/PlayerEquipmentPacket.hpp" + #include "client/gui/screens/inventory/CraftingScreen.hpp" + #include "client/gui/screens/inventory/ChestScreen.hpp" ++#include "client/gui/screens/inventory/FurnaceScreen.hpp" + + int dword_250ADC, dword_250AE0; + +@@ -134,11 +135,11 @@ void LocalPlayer::startCrafting(const TilePos& pos) + m_pMinecraft->getScreenChooser()->pushCraftingScreen(this, pos); + } + +-/*void LocalPlayer::openFurnace(FurnaceTileEntity* furnace) ++void LocalPlayer::openFurnace(FurnaceTileEntity* furnace) + { + // PE 0.3.2 doesn't let you cook in creative mode + m_pMinecraft->setScreen(new FurnaceScreen(m_pInventory, furnace)); +-}*/ ++} + + void LocalPlayer::openContainer(Container* container) + { +diff --git a/source/client/player/LocalPlayer.hpp b/source/client/player/LocalPlayer.hpp +index 1eecb1288..076c5abe1 100644 +--- a/source/client/player/LocalPlayer.hpp ++++ b/source/client/player/LocalPlayer.hpp +@@ -39,7 +39,7 @@ class LocalPlayer : public Player + void setPlayerGameType(GameType gameType) override; + void swing() override; + void startCrafting(const TilePos&) override; +- //void openFurnace(FurnaceTileEntity* furnace) override; ++ void openFurnace(FurnaceTileEntity* furnace) override; + void openContainer(Container* container) override; + void closeContainer() override; + //void openTrap(DispenserTileEntity* tileEntity) override; +diff --git a/source/client/renderer/Chunk.cpp b/source/client/renderer/Chunk.cpp +index 86ece3261..ec7d6db5e 100644 +--- a/source/client/renderer/Chunk.cpp ++++ b/source/client/renderer/Chunk.cpp +@@ -133,6 +133,9 @@ void Chunk::rebuild() + + LevelChunk::touchedSky = false; + ++ std::set tmpSet(m_renderableTileEntities.begin(), m_renderableTileEntities.end()); ++ m_renderableTileEntities.clear(); ++ + for (int i = Tile::RENDER_LAYERS_MIN; i <= Tile::RENDER_LAYERS_MAX; i++) + { + m_empty[i] = true; +@@ -169,6 +172,16 @@ void Chunk::rebuild() + t.setOffset(-m_pos); + } + ++ if (!layer && Tile::isEntityTile[tile]) ++ { ++ /* ++ // @TODO: ADD TILE ENTITY RENDER DISPATCHER ++ TileEntity* et = region.getTileEntity(tp); ++ if (TileEntityRenderDispatcher::getInstance()->hasRenderer(et)) ++ m_renderableTileEntities.push_back(et); ++ */ ++ } ++ + Tile* pTile = Tile::tiles[tile]; + + if (layer == pTile->getRenderLayer()) +@@ -201,11 +214,45 @@ void Chunk::rebuild() + break; + } + ++ std::set newSet(m_renderableTileEntities.begin(), m_renderableTileEntities.end()); ++ std::vector toAdd, toRemove; ++ ++ std::set_difference( ++ newSet.begin(), newSet.end(), ++ tmpSet.begin(), tmpSet.end(), ++ std::back_inserter(toAdd) ++ ); ++ ++ std::set_difference( ++ tmpSet.begin(), tmpSet.end(), ++ newSet.begin(), newSet.end(), ++ std::back_inserter(toRemove) ++ ); ++ ++ // Add ++ for (std::vector::iterator it = toAdd.begin(); it != toAdd.end(); ++it) ++ { ++ m_globalRenderableTileEntities.push_back(*it); ++ } ++ ++ // Remove ++ for (std::vector::iterator it = toRemove.begin(); it != toRemove.end(); ++it) ++ { ++ std::vector::iterator f = ++ std::find(m_globalRenderableTileEntities.begin(), ++ m_globalRenderableTileEntities.end(), ++ *it); ++ ++ if (f != m_globalRenderableTileEntities.end()) ++ m_globalRenderableTileEntities.erase(f); ++ } ++ + field_54 = LevelChunk::touchedSky; + m_bCompiled = true; + } + +-Chunk::Chunk(Level* level, const TilePos& pos, int size, int lists) ++Chunk::Chunk(Level* level, std::vector& renderableTileEntities, const TilePos& pos, int size, int lists) ++ : m_globalRenderableTileEntities(renderableTileEntities) + { + m_bOcclusionVisible = true; + m_bOcclusionQuerying = false; +diff --git a/source/client/renderer/Chunk.hpp b/source/client/renderer/Chunk.hpp +index 5f575c0d8..957ef0446 100644 +--- a/source/client/renderer/Chunk.hpp ++++ b/source/client/renderer/Chunk.hpp +@@ -15,11 +15,12 @@ + + class Level; + class Entity; ++class TileEntity; + + class Chunk + { + public: +- Chunk(Level*, const TilePos& pos, int, int); ++ Chunk(Level*, std::vector& renderableTileEntities, const TilePos& pos, int, int); + + public: + float distanceToSqr(const Entity& entity) const; +@@ -42,6 +43,8 @@ class Chunk + + public: + Level* m_pLevel; ++ std::vector& m_globalRenderableTileEntities; ++ std::vector m_renderableTileEntities; + TilePos m_pos; + TilePos m_posS; + bool m_empty[Tile::RENDER_LAYERS_COUNT]; +diff --git a/source/client/renderer/LevelRenderer.cpp b/source/client/renderer/LevelRenderer.cpp +index c7202fe54..98a311c52 100644 +--- a/source/client/renderer/LevelRenderer.cpp ++++ b/source/client/renderer/LevelRenderer.cpp +@@ -617,7 +617,7 @@ void LevelRenderer::allChanged() + m_zMinChunk = 0; + + m_dirtyChunks.clear(); +- //m_renderableTileEntities.clear(); ++ m_renderableTileEntities.clear(); + + m_xMaxChunk = m_xChunks; + m_yMaxChunk = m_yChunks; +@@ -638,7 +638,7 @@ void LevelRenderer::allChanged() + { + int index = (cp.z * m_yChunks + cp.y) * m_xChunks + cp.x; + +- Chunk* pChunk = new Chunk(m_pLevel, cp * 16, 16, id + m_chunkLists); ++ Chunk* pChunk = new Chunk(m_pLevel, m_renderableTileEntities, cp * 16, 16, id + m_chunkLists); + + if (m_bOcclusionCheck) + pChunk->m_occlusionId = 0; // m_occlusionCheckIds.get(count) +@@ -1421,6 +1421,11 @@ void LevelRenderer::addParticle(const std::string& name, const Vec3& pos, const + pe->add(new SmokeParticle(m_pLevel, pos, dir, 1.0f)); + return; + } ++ if (name == "note") ++ { ++ pe->add(new NoteParticle(m_pLevel, pos, dir)); ++ return; ++ } + if (name == "explode") + { + pe->add(new ExplodeParticle(m_pLevel, pos, dir)); +@@ -1599,6 +1604,16 @@ void LevelRenderer::renderEntities(Vec3 pos, Culler* culler, float f) + EntityRenderDispatcher::getInstance()->render(*entity, f); + } + } ++ ++ /* ++ // @TODO: TileEntityRenderDispatcher ++ for (std::vector::const_iterator it = m_renderableTileEntities.begin(); ++ it != m_renderableTileEntities.end(); ++it) ++ { ++ TileEntity* tileEntity = *it; ++ TileEntityRenderDispatcher::getInstance()->render(tileEntity, f); ++ } ++ */ + } + + void LevelRenderer::renderShadow(const Entity& entity, const Vec3& pos, float r, float pow, float a) +diff --git a/source/client/renderer/LevelRenderer.hpp b/source/client/renderer/LevelRenderer.hpp +index 1e2fbbc60..18947ff8c 100644 +--- a/source/client/renderer/LevelRenderer.hpp ++++ b/source/client/renderer/LevelRenderer.hpp +@@ -223,4 +223,5 @@ class LevelRenderer : public LevelListener, public AppPlatformListener + mce::Mesh m_darkMesh; + //... + Textures* m_pTextures; ++ std::vector m_renderableTileEntities; + }; +diff --git a/source/client/sound/SoundRepository.cpp b/source/client/sound/SoundRepository.cpp +index 32a24a2dd..17342dc52 100644 +--- a/source/client/sound/SoundRepository.cpp ++++ b/source/client/sound/SoundRepository.cpp +@@ -33,7 +33,7 @@ bool SoundRepository::get(const std::string& name, SoundDesc& sd) + std::map >::iterator iter = m_repo.find(name); + if (iter == m_repo.end()) + { +- LOG_E("Couldn't find a sound with id: %s", name.c_str()); ++ LOG_W("Couldn't find a sound with id: %s", name.c_str()); + return false; + } + +diff --git a/source/client/sound/sound_list.h b/source/client/sound/sound_list.h +index e20026c34..ee6906caf 100644 +--- a/source/client/sound/sound_list.h ++++ b/source/client/sound/sound_list.h +@@ -48,6 +48,12 @@ SOUND(ui, press) + SOUND(ui, scroll) + + SOUND(fire, fire) ++SOUND(fire, ignite) ++SOUND_NUM(fire, fire_crackle, 1) ++SOUND_NUM(fire, fire_crackle, 2) ++SOUND_NUM(fire, fire_crackle, 3) ++SOUND_NUM(fire, fire_crackle, 4) ++SOUND_NUM(fire, fire_crackle, 5) + + SOUND_NUM(damage, fallbig, 1) + SOUND_NUM(damage, fallbig, 2) +@@ -103,3 +109,9 @@ SOUND_NUM(mob, zombie, 3) + SOUND_NUM(mob, zombiehurt, 1) + SOUND_NUM(mob, zombiehurt, 2) + SOUND(mob, zombiedeath) ++ ++SOUND(note, harp) ++SOUND(note, bd) ++SOUND(note, hat) ++SOUND(note, snare) ++SOUND(note, bassattack) +\ No newline at end of file +diff --git a/source/common/Mth.cpp b/source/common/Mth.cpp +index d3a20ddb0..5126313ab 100644 +--- a/source/common/Mth.cpp ++++ b/source/common/Mth.cpp +@@ -48,13 +48,13 @@ float Mth::invSqrt(float number) + // they just stole it from Quake. + + float x2, y; +- const float threehalfs = 1.5F; ++ const float threehalfs = 1.5f; + union { + float f; + int32_t i; + } un; + +- x2 = number * 0.5F; ++ x2 = number * 0.5f; + un.f = number; // evil floating point bit level hacking + un.i = 0x5f3759df - ( un.i >> 1 ); // what the fuck? + y = un.f; +diff --git a/source/world/Facing.cpp b/source/world/Facing.cpp +new file mode 100644 +index 000000000..0893ffc57 +--- /dev/null ++++ b/source/world/Facing.cpp +@@ -0,0 +1,19 @@ ++#include "Facing.hpp" ++ ++const Facing::Name Facing::OPPOSITE[6] = ++{ ++ Facing::UP, // DOWN -> UP ++ Facing::DOWN, // UP -> DOWN ++ Facing::SOUTH, // NORTH -> SOUTH ++ Facing::NORTH, // SOUTH -> NORTH ++ Facing::EAST, // WEST -> EAST ++ Facing::WEST // EAST -> WEST ++}; ++ ++const Facing::Name Facing::HORIZONTAL[4] = ++{ ++ Facing::NORTH, ++ Facing::SOUTH, ++ Facing::EAST, ++ Facing::WEST ++}; +\ No newline at end of file +diff --git a/source/world/Facing.hpp b/source/world/Facing.hpp +index 740f1ce37..2875da6d5 100644 +--- a/source/world/Facing.hpp ++++ b/source/world/Facing.hpp +@@ -12,4 +12,6 @@ class Facing + WEST, // -X + EAST // +X + }; +-}; ++ static const Name OPPOSITE[6]; ++ static const Name HORIZONTAL[4]; ++}; +\ No newline at end of file +diff --git a/source/world/entity/Player.cpp b/source/world/entity/Player.cpp +index 444cb5254..d7e0cff66 100644 +--- a/source/world/entity/Player.cpp ++++ b/source/world/entity/Player.cpp +@@ -540,6 +540,11 @@ void Player::startCrafting(const TilePos& pos) + + } + ++void Player::openFurnace(FurnaceTileEntity* tileEntity) ++{ ++ ++} ++ + void Player::startStonecutting(const TilePos& pos) + { + +diff --git a/source/world/entity/Player.hpp b/source/world/entity/Player.hpp +index 2d88c21ed..1a25f7228 100644 +--- a/source/world/entity/Player.hpp ++++ b/source/world/entity/Player.hpp +@@ -16,6 +16,7 @@ + #include "world/inventory/InventoryMenu.hpp" + + class Inventory; // in case we're included from Inventory.hpp ++class FurnaceTileEntity; + + class Player : public Mob + { +@@ -72,7 +73,7 @@ class Player : public Mob + virtual void startStonecutting(const TilePos& pos); + virtual void startDestroying(); + virtual void stopDestroying(); +- //virtual void openFurnace(FurnaceTileEntity* tileEntity); ++ virtual void openFurnace(FurnaceTileEntity* tileEntity); + virtual void openContainer(Container* container) {} + virtual void closeContainer() {} + //virtual void openTrap(DispenserTileEntity* tileEntity); +diff --git a/source/world/inventory/FurnaceMenu.cpp b/source/world/inventory/FurnaceMenu.cpp +new file mode 100644 +index 000000000..e8f6678ac +--- /dev/null ++++ b/source/world/inventory/FurnaceMenu.cpp +@@ -0,0 +1,100 @@ ++#include "FurnaceMenu.hpp" ++#include "Slot.hpp" ++#include "FurnaceResultSlot.hpp" ++#include "world/ContainerListener.hpp" ++ ++FurnaceMenu::FurnaceMenu(Inventory* inventory, FurnaceTileEntity* furnace) ++ : ContainerMenu(Container::FURNACE), m_furnace(furnace), m_lastCookTime(0), m_lastBurnTime(0), m_lastLitDuration(0) ++{ ++ addSlot(new Slot(m_furnace, 0, Slot::INPUT)); ++ addSlot(new Slot(m_furnace, 1, Slot::INPUT)); ++ addSlot(new FurnaceResultSlot(inventory->m_pPlayer, m_furnace, 2)); ++ ++ for (int y = 0; y < 3; ++y) ++ { ++ for (int x = 0; x < 9; ++x) ++ addSlot(new Slot(inventory, x + (y + 1) * 9, Slot::INVENTORY)); ++ } ++ ++ for (int i = 0; i < 9; ++i) ++ { ++ addSlot(new Slot(inventory, i, Slot::HOTBAR)); ++ } ++} ++ ++bool FurnaceMenu::stillValid(Player* player) const ++{ ++ return m_furnace->stillValid(player); ++} ++ ++void FurnaceMenu::addSlotListener(ContainerListener* listener) ++{ ++ ContainerMenu::addSlotListener(listener); ++ listener->setContainerData(this, 0, m_furnace->m_tickCount); ++ listener->setContainerData(this, 1, m_furnace->m_litTime); ++ listener->setContainerData(this, 2, m_furnace->m_litDuration); ++} ++ ++void FurnaceMenu::broadcastChanges() ++{ ++ ContainerMenu::broadcastChanges(); ++ ++ for (std::vector::iterator it = m_listeners.begin(); it != m_listeners.end(); ++it) ++ { ++ ContainerListener* listener = *it; ++ ++ if (m_lastCookTime != m_furnace->m_tickCount) ++ listener->setContainerData(this, 0, m_furnace->m_tickCount); ++ ++ if (m_lastBurnTime != m_furnace->m_litTime) ++ listener->setContainerData(this, 1, m_furnace->m_litTime); ++ ++ if (m_lastLitDuration != m_furnace->m_litDuration) ++ listener->setContainerData(this, 2, m_furnace->m_litDuration); ++ } ++ ++ m_lastCookTime = m_furnace->m_tickCount; ++ m_lastBurnTime = m_furnace->m_litTime; ++ m_lastLitDuration = m_furnace->m_litDuration; ++} ++ ++void FurnaceMenu::setData(int index, int value) ++{ ++ if (index == 0) ++ m_furnace->m_tickCount = value; ++ else if (index == 1) ++ m_furnace->m_litTime = value; ++ else if (index == 2) ++ m_furnace->m_litDuration = value; ++} ++ ++ItemStack FurnaceMenu::quickMoveStack(int index) ++{ ++ ItemStack item; ++ Slot* slot = getSlot(index); ++ if (slot && slot->hasItem()) ++ { ++ ItemStack& slotItem = slot->getItem(); ++ item = slotItem.copy(); ++ if (index == 2) ++ moveItemStackTo(slotItem, 3, 39, true); ++ else if (index >= 3 && index < 30) ++ moveItemStackTo(slotItem, 30, 39, false); ++ else if (index >= 30 && index < 39) ++ moveItemStackTo(slotItem, 3, 30, false); ++ else ++ moveItemStackTo(slotItem, 3, 39, false); ++ ++ if (slotItem.m_count == 0) ++ slot->set(ItemStack::EMPTY); ++ else ++ slot->setChanged(); ++ ++ if (slotItem.m_count == item.m_count) ++ return ItemStack::EMPTY; ++ ++ slot->onTake(slotItem); ++ } ++ ++ return item; ++} +diff --git a/source/world/inventory/FurnaceMenu.hpp b/source/world/inventory/FurnaceMenu.hpp +new file mode 100644 +index 000000000..42025cf41 +--- /dev/null ++++ b/source/world/inventory/FurnaceMenu.hpp +@@ -0,0 +1,24 @@ ++#pragma once ++ ++#include "ContainerMenu.hpp" ++#include "world/entity/Player.hpp" ++#include ++ ++class FurnaceMenu : public ContainerMenu ++{ ++public: ++ FurnaceMenu(Inventory* inventory, FurnaceTileEntity* container); ++ ++public: ++ bool stillValid(Player* player) const override; ++ void addSlotListener(ContainerListener* listener) override; ++ void broadcastChanges() override; ++ void setData(int, int) override; ++ ItemStack quickMoveStack(int index) override; ++ ++private: ++ FurnaceTileEntity* m_furnace; ++ int m_lastCookTime; ++ int m_lastBurnTime; ++ int m_lastLitDuration; ++}; +diff --git a/source/world/inventory/FurnaceResultSlot.cpp b/source/world/inventory/FurnaceResultSlot.cpp +new file mode 100644 +index 000000000..84d628de0 +--- /dev/null ++++ b/source/world/inventory/FurnaceResultSlot.cpp +@@ -0,0 +1,19 @@ ++#include "FurnaceResultSlot.hpp" ++#include "../item/Item.hpp" ++#include "world/entity/Player.hpp" ++ ++FurnaceResultSlot::FurnaceResultSlot(Player* player, Container* container, int slotIndex) ++ : Slot(container, slotIndex, OUTPUT), m_pPlayer(player) ++{ ++} ++ ++bool FurnaceResultSlot::mayPlace(const ItemStack&) const ++{ ++ return false; ++} ++ ++void FurnaceResultSlot::onTake(ItemStack& inst) ++{ ++ inst.onCraftedBy(m_pPlayer, m_pPlayer->m_pLevel); ++ Slot::onTake(inst); ++} +diff --git a/source/world/inventory/FurnaceResultSlot.hpp b/source/world/inventory/FurnaceResultSlot.hpp +new file mode 100644 +index 000000000..4c1e277a1 +--- /dev/null ++++ b/source/world/inventory/FurnaceResultSlot.hpp +@@ -0,0 +1,16 @@ ++#pragma once ++ ++#include "Slot.hpp" ++#include "source/world/Container.hpp" ++ ++class FurnaceResultSlot : public Slot ++{ ++public: ++ FurnaceResultSlot(Player* player, Container* container, int slotIdx); ++ ++ bool mayPlace(const ItemStack&) const override; ++ void onTake(ItemStack&) override; ++ ++private: ++ Player* m_pPlayer; ++}; +\ No newline at end of file +diff --git a/source/world/inventory/SimpleContainer.cpp b/source/world/inventory/SimpleContainer.cpp +index 6799cd433..74b8c4c5d 100644 +--- a/source/world/inventory/SimpleContainer.cpp ++++ b/source/world/inventory/SimpleContainer.cpp +@@ -64,7 +64,7 @@ bool SimpleContainer::stillValid(Player* player) const + return true; + } + +-void SimpleContainer::load(CompoundTag& tag) ++void SimpleContainer::load(const CompoundTag& tag) + { + clear(); + const ListTag* list = tag.getList("Items"); +@@ -78,14 +78,14 @@ void SimpleContainer::load(CompoundTag& tag) + { + uint8_t slot = itemTag->getInt8("Slot") & 255; + ItemStack item = ItemStack::fromTag(*itemTag); +- if (itemTag->isEmpty() && slot >= 0 && slot < m_items.size()) ++ if (!itemTag->isEmpty() && slot >= 0 && slot < m_items.size()) + m_items[slot] = item; + } + } + + } + +-void SimpleContainer::save(CompoundTag& tag) ++void SimpleContainer::save(CompoundTag& tag) const + { + ListTag* list = new ListTag; + +diff --git a/source/world/inventory/SimpleContainer.hpp b/source/world/inventory/SimpleContainer.hpp +index 2e676e564..a371d2532 100644 +--- a/source/world/inventory/SimpleContainer.hpp ++++ b/source/world/inventory/SimpleContainer.hpp +@@ -9,28 +9,21 @@ class SimpleContainer : public Container + public: + SimpleContainer(int size, const std::string& name); + +- virtual uint16_t getContainerSize() const override; +- +- virtual ItemStack& getItem(int index) override; +- +- virtual ItemStack removeItem(int index, int count) override; +- +- virtual void setItem(int index, const ItemStack& item) override; +- +- virtual std::string getName() const override; +- +- virtual void setChanged() override; +- +- virtual bool stillValid(Player* player) const override; ++public: ++ uint16_t getContainerSize() const override; ++ ItemStack& getItem(int index) override; ++ ItemStack removeItem(int index, int count) override; ++ void setItem(int index, const ItemStack& item) override; ++ std::string getName() const override; ++ void setChanged() override; ++ bool stillValid(Player* player) const override; + ++public: + virtual void clear(); +- +- virtual void load(CompoundTag& tag); +- virtual void save(CompoundTag& tag); ++ virtual void load(const CompoundTag& tag); ++ virtual void save(CompoundTag& tag) const; + + private: + std::vector m_items; + std::string m_name; +-}; +- +- ++}; +\ No newline at end of file +diff --git a/source/world/item/CoalItem.cpp b/source/world/item/CoalItem.cpp +new file mode 100644 +index 000000000..4d3f6669e +--- /dev/null ++++ b/source/world/item/CoalItem.cpp +@@ -0,0 +1,12 @@ ++#include "CoalItem.hpp" ++ ++CoalItem::CoalItem(int id) : Item(id) ++{ ++ m_maxDamage = 0; ++ m_bStackedByData = true; ++} ++ ++std::string CoalItem::getDescriptionId(ItemStack* inst) const ++{ ++ return (inst->getAuxValue() == 1) ? "item.charcoal" : "item.coal"; ++} +\ No newline at end of file +diff --git a/source/world/item/CoalItem.hpp b/source/world/item/CoalItem.hpp +new file mode 100644 +index 000000000..fc1474401 +--- /dev/null ++++ b/source/world/item/CoalItem.hpp +@@ -0,0 +1,12 @@ ++#pragma once ++ ++#include "Item.hpp" ++ ++class CoalItem : public Item ++{ ++public: ++ CoalItem(int id); ++ ++ std::string getDescriptionId(ItemStack* inst) const override; ++ ++}; +\ No newline at end of file +diff --git a/source/world/item/Item.cpp b/source/world/item/Item.cpp +index 88fe95939..7377fca9f 100644 +--- a/source/world/item/Item.cpp ++++ b/source/world/item/Item.cpp +@@ -11,19 +11,20 @@ + #include "common/Logger.hpp" + #include "common/Util.hpp" + ++#include "ArmorItem.hpp" ++#include "BowItem.hpp" + #include "CameraItem.hpp" ++#include "CoalItem.hpp" + #include "DoorItem.hpp" +-#include "TileItem.hpp" +-#include "TilePlanterItem.hpp" +-#include "RocketItem.hpp" +-#include "ToolItem.hpp" +-#include "BowItem.hpp" + #include "DyePowderItem.hpp" +-#include "WeaponItem.hpp" + #include "FoodItem.hpp" +-#include "ArmorItem.hpp" + #include "HoeItem.hpp" ++#include "RocketItem.hpp" + #include "SeedItem.hpp" ++#include "TileItem.hpp" ++#include "ToolItem.hpp" ++#include "TilePlanterItem.hpp" ++#include "WeaponItem.hpp" + + #define ITEM(x) ((x) - 256) + +@@ -339,7 +340,7 @@ void Item::initItems() + ->setIcon(5, 2) + ->setDescriptionId("arrow"); + +- Item::coal = NEW_ITEM(ITEM_COAL) ++ Item::coal = NEW_X_ITEMN(CoalItem, ITEM_COAL) + ->setIcon(7, 0) + ->setDescriptionId("coal"); + +diff --git a/source/world/item/crafting/FurnaceRecipes.cpp b/source/world/item/crafting/FurnaceRecipes.cpp +index 0c170f30d..36f99a069 100644 +--- a/source/world/item/crafting/FurnaceRecipes.cpp ++++ b/source/world/item/crafting/FurnaceRecipes.cpp +@@ -1,6 +1,8 @@ + #include "FurnaceRecipes.hpp" + #include "common/Logger.hpp" + ++FurnaceRecipes* FurnaceRecipes::instance; ++ + FurnaceRecipes::FurnaceRecipes() + { + addFurnaceRecipe(Tile::ironOre, ItemStack(Item::ironIngot)); +diff --git a/source/world/item/crafting/Recipes.cpp b/source/world/item/crafting/Recipes.cpp +index 05f0e32c0..fe375b83b 100644 +--- a/source/world/item/crafting/Recipes.cpp ++++ b/source/world/item/crafting/Recipes.cpp +@@ -120,14 +120,14 @@ Recipes::Recipes() + addOre(ItemStack(Item::dye_powder, 1, 4), Tile::lapisBlock); + + // StructureRecipes +- //add(ShapedRecipeBuilder("###", +- // "# #", +- // "###", ItemStack(Tile::chest)) +- // .add('#', Tile::wood)); +- //add(ShapedRecipeBuilder("###", +- // "# #", +- // "###", ItemStack(Tile::furnace)) +- // .add('#', Tile::stoneBrick)); ++ add(ShapedRecipeBuilder("###", ++ "# #", ++ "###", ItemStack(Tile::chest)) ++ .add('#', Tile::wood)); ++ add(ShapedRecipeBuilder("###", ++ "# #", ++ "###", ItemStack(Tile::furnace)) ++ .add('#', Tile::stoneBrick)); + + add(ShapedRecipeBuilder("##", + "##", ItemStack(Tile::craftingTable)) +@@ -169,11 +169,11 @@ Recipes::Recipes() + // .add('#', Tile::wood) + // .add('X', Item::emerald)); + +- //add(ShapedRecipeBuilder("###", +- // "#X#", +- // "###", ItemStack(Tile::musicBlock, 1)) +- // .add('#', Tile::wood) +- // .add('X', Item::redStone)); ++ add(ShapedRecipeBuilder("###", ++ "#X#", ++ "###", ItemStack(Tile::musicBlock, 1)) ++ .add('#', Tile::wood) ++ .add('X', Item::redStone)); + + add(ShapedRecipeBuilder("###", + "XXX", +diff --git a/source/world/level/Level.cpp b/source/world/level/Level.cpp +index e91a3c8f2..efec2255d 100644 +--- a/source/world/level/Level.cpp ++++ b/source/world/level/Level.cpp +@@ -18,6 +18,7 @@ + #include "network/packets/ExplodePacket.hpp" + #include "world/level/levelgen/chunk/ChunkCache.hpp" + #include "world/entity/MobSpawner.hpp" ++#include "world/tile/entity/TileEntity.hpp" + + #include "Explosion.hpp" + #include "Region.hpp" +@@ -38,6 +39,8 @@ Level::Level(LevelStorage* pStor, const std::string& name, const LevelSettings& + m_bCalculatingInitialSpawn = false; + m_pChunkSource = nullptr; + m_pLevelStorage = pStor; ++ m_tileEntityList = TileEntityVector(); ++ m_bUpdatingTileEntities = false; + m_randValue = 42184323; + m_addend = 1013904223; + m_bUpdateLights = true; +@@ -258,6 +261,54 @@ int Level::getRawBrightness(const TilePos& pos, bool b) const + return pChunk->getRawBrightness(pos, m_skyDarken); + } + ++TileEntity* Level::getTileEntity(const TilePos& pos) const ++{ ++ LevelChunk* pChunk = getChunk(pos); ++ return pChunk ? pChunk->getTileEntity(pos) : nullptr; ++} ++ ++const TileEntityVector* Level::getAllTileEntities() const ++{ ++ return &m_tileEntityList; ++} ++ ++void Level::setTileEntity(const TilePos& pos, TileEntity* tileEntity) ++{ ++ ++ if (tileEntity->isRemoved()) ++ return; ++ ++ if (m_bUpdatingTileEntities) ++ { ++ tileEntity->m_pos = pos; ++ m_pendingTileEntities.push_back(tileEntity); ++ return; ++ } ++ ++ m_tileEntityList.push_back(tileEntity); ++ LevelChunk* pChunk = getChunk(pos); ++ if (pChunk) ++ pChunk->setTileEntity(pos, tileEntity); ++} ++ ++void Level::removeTileEntity(const TilePos& pos) ++{ ++ TileEntity* old = getTileEntity(pos); ++ ++ if (old != nullptr && m_bUpdatingTileEntities) ++ { ++ old->setRemoved(); ++ return; ++ } ++ ++ if (old) ++ Util::remove(m_tileEntityList, old); ++ ++ LevelChunk* pChunk = getChunk(pos); ++ if (pChunk) ++ pChunk->removeTileEntity(pos); ++} ++ + void Level::swap(const TilePos& pos1, const TilePos& pos2) + { + TileID tile1 = getTile(pos1); +@@ -1706,6 +1757,29 @@ void Level::tickEntities() + delete pEnt; + } + } ++ ++ m_bUpdatingTileEntities = true; ++ for (size_t i = 0; i < m_tileEntityList.size(); i++) ++ { ++ TileEntity* tileEnt = m_tileEntityList[i]; ++ ++ if (!tileEnt->isRemoved()) ++ { ++ tileEnt->tick(); ++ } ++ else ++ { ++ LevelChunk* ch = getChunk(tileEnt->m_pos); ++ if (ch) ++ ch->removeTileEntity(tileEnt->m_pos); ++ ++ m_entities.erase(m_entities.begin() + i); ++ i--; ++ ++ delete tileEnt; ++ } ++ } ++ m_bUpdatingTileEntities = false; + } + + HitResult Level::clip(Vec3 v1, Vec3 v2, bool flag) const +diff --git a/source/world/level/Level.hpp b/source/world/level/Level.hpp +index 993830cf6..95ae707ea 100644 +--- a/source/world/level/Level.hpp ++++ b/source/world/level/Level.hpp +@@ -38,6 +38,7 @@ class Packet; + class MobSpawner; + + typedef std::vector EntityVector; ++typedef std::vector TileEntityVector; + typedef std::vector AABBVector; + + struct Brightness +@@ -72,6 +73,10 @@ class Level : public LevelSource + LevelChunk* getChunkAt(const TilePos& pos) const; + int getRawBrightness(const TilePos& pos) const; + int getRawBrightness(const TilePos& pos, bool b) const; ++ TileEntity* getTileEntity(const TilePos& pos) const override; ++ const TileEntityVector* getAllTileEntities() const; ++ void setTileEntity(const TilePos& pos, TileEntity* tileEntity); ++ void removeTileEntity(const TilePos& pos); + int getBrightness(const LightLayer&, const TilePos& pos) const; + void setBrightness(const LightLayer&, const TilePos& pos, int brightness); + int getSeaLevel() const { return 63; } +@@ -203,6 +208,7 @@ class Level : public LevelSource + + private: + LevelData* m_pLevelData; ++ bool m_bUpdatingTileEntities; + + protected: + int m_randValue; +@@ -237,5 +243,7 @@ class Level : public LevelSource + MobSpawner* m_pMobSpawner; + + std::map m_entityCountsByCategory; ++ TileEntityVector m_tileEntityList; ++ TileEntityVector m_pendingTileEntities; + }; + +diff --git a/source/world/level/Region.cpp b/source/world/level/Region.cpp +index 9a7109ce6..8d55fcd48 100644 +--- a/source/world/level/Region.cpp ++++ b/source/world/level/Region.cpp +@@ -111,6 +111,11 @@ BiomeSource* Region::getBiomeSource() const + return m_pLevel->getBiomeSource(); + } + ++TileEntity* Region::getTileEntity(const TilePos& pos) const ++{ ++ return m_pLevel->getTileEntity(pos); ++} ++ + Region::~Region() + { + delete[] field_C; +diff --git a/source/world/level/Region.hpp b/source/world/level/Region.hpp +index ca0101edb..e0eeea90a 100644 +--- a/source/world/level/Region.hpp ++++ b/source/world/level/Region.hpp +@@ -21,6 +21,7 @@ class Region : public LevelSource + Material* getMaterial(const TilePos& pos) const override; + bool isSolidTile(const TilePos& pos) const override; + BiomeSource* getBiomeSource() const override; ++ TileEntity* getTileEntity(const TilePos& pos) const override; + + virtual ~Region(); + Region(const Level* level, const TilePos& min, const TilePos& max); +diff --git a/source/world/level/levelgen/chunk/ChunkTilePos.hpp b/source/world/level/levelgen/chunk/ChunkTilePos.hpp +index 923a430f3..258f9010d 100644 +--- a/source/world/level/levelgen/chunk/ChunkTilePos.hpp ++++ b/source/world/level/levelgen/chunk/ChunkTilePos.hpp +@@ -19,4 +19,11 @@ struct ChunkTilePos + { + return ChunkTilePos(x + other.x, y + other.y, z + other.z); + } +-}; ++ ++ bool operator<(const ChunkTilePos& other) const ++ { ++ if (x != other.x) return x < other.x; ++ if (y != other.y) return y < other.y; ++ return z < other.z; ++ } ++}; +\ No newline at end of file +diff --git a/source/world/level/levelgen/chunk/LevelChunk.cpp b/source/world/level/levelgen/chunk/LevelChunk.cpp +index e0de339b4..598a4ef8a 100644 +--- a/source/world/level/levelgen/chunk/LevelChunk.cpp ++++ b/source/world/level/levelgen/chunk/LevelChunk.cpp +@@ -8,6 +8,7 @@ + + #include "common/Logger.hpp" + #include "world/level/Level.hpp" ++#include "world/tile/entity/TileEntity.hpp" + #include "world/phys/AABB.hpp" + + bool LevelChunk::touchedSky = false; +@@ -683,6 +684,74 @@ bool LevelChunk::setTileAndData(const ChunkTilePos& pos, TileID tile, TileData d + return true; + } + ++TileEntity* LevelChunk::getTileEntity(const ChunkTilePos& pos) ++{ ++ std::map::iterator it = m_tileEntities.find(pos); ++ if (it == m_tileEntities.end()) ++ { ++ int tileId = getTile(pos); ++ if (tileId <= TILE_AIR || !Tile::isEntityTile[tileId]) ++ return nullptr; ++ ++ TilePos tilePos(m_chunkPos, pos.y); ++ tilePos += TilePos(pos.x, 0, pos.z); ++ ++ Tile* pTile = Tile::tiles[tileId]; ++ pTile->onPlace(m_pLevel, tilePos); ++ ++ // do a recheck to see if a tile entity was actually added. ++ it = m_tileEntities.find(pos); ++ return (it == m_tileEntities.end()) ? nullptr : it->second; ++ } ++ ++ if (!it->second || it->second->isRemoved()) ++ { ++ m_tileEntities.erase(it); ++ return nullptr; ++ } ++ ++ return it->second; ++} ++ ++void LevelChunk::addTileEntity(TileEntity* tileEntity) ++{ ++ setTileEntity(tileEntity->m_pos, tileEntity); ++ if (m_bLoaded) ++ m_pLevel->m_tileEntityList.push_back(tileEntity); ++} ++ ++void LevelChunk::setTileEntity(const ChunkTilePos& pos, TileEntity* tileEntity) ++{ ++ tileEntity->m_pLevel = m_pLevel; ++ TileID tile = getTile(pos); ++ TilePos tilePos(m_chunkPos, pos.y); ++ tilePos.x += pos.x; ++ tilePos.z += pos.z; ++ tileEntity->m_pos = tilePos; ++ if (tile > 0 && Tile::isEntityTile[tile]) ++ { ++ tileEntity->clearRemoved(); ++ m_tileEntities[pos] = tileEntity; ++ return; ++ } ++ ++ LOG_I("Attempted to place a tile entity where there was no entity tile! %d", tile); ++} ++ ++void LevelChunk::removeTileEntity(const ChunkTilePos& pos) ++{ ++ if (!m_bLoaded) ++ return; ++ ++ std::map::iterator it = m_tileEntities.find(pos); ++ if (it != m_tileEntities.end()) ++ { ++ if (it->second) ++ it->second->setRemoved(); ++ m_tileEntities.erase(it); ++ } ++} ++ + TileData LevelChunk::getData(const ChunkTilePos& pos) + { + CheckPosition(pos); +diff --git a/source/world/level/levelgen/chunk/LevelChunk.hpp b/source/world/level/levelgen/chunk/LevelChunk.hpp +index f25faf097..d103eb18a 100644 +--- a/source/world/level/levelgen/chunk/LevelChunk.hpp ++++ b/source/world/level/levelgen/chunk/LevelChunk.hpp +@@ -11,6 +11,7 @@ + #include + #include + #include ++#include + #include "common/Random.hpp" + #include "common/Utils.hpp" + #include "client/renderer/LightLayer.hpp" +@@ -21,6 +22,7 @@ + class Level; + class AABB; + class Entity; ++class TileEntity; + + class LevelChunk + { +@@ -69,6 +71,10 @@ class LevelChunk + virtual void setBlocks(uint8_t* pData, int y); + virtual int getBlocksAndData(uint8_t* pData, int, int, int, int, int, int, int); + virtual int setBlocksAndData(uint8_t* pData, int, int, int, int, int, int, int); ++ virtual TileEntity* getTileEntity(const ChunkTilePos& pos); ++ virtual void addTileEntity(TileEntity* tileEntity); ++ virtual void setTileEntity(const ChunkTilePos& pos, TileEntity* tileEntity); ++ virtual void removeTileEntity(const ChunkTilePos& pos); + virtual Random getRandom(int32_t l); + virtual void recalcHeight(const ChunkTilePos& pos); + virtual bool isEmpty(); +@@ -96,4 +102,5 @@ class LevelChunk + int field_23C; + TileID* m_pBlockData; + std::vector m_entities[128 / 16]; ++ std::map m_tileEntities; + }; +diff --git a/source/world/level/storage/ExternalFileLevelStorage.cpp b/source/world/level/storage/ExternalFileLevelStorage.cpp +index 18b1e297c..681d04db0 100644 +--- a/source/world/level/storage/ExternalFileLevelStorage.cpp ++++ b/source/world/level/storage/ExternalFileLevelStorage.cpp +@@ -16,6 +16,7 @@ + #include "network/RakIO.hpp" + #include "world/entity/EntityFactory.hpp" + #include "world/level/Level.hpp" ++#include "world/tile/entity/TileEntity.hpp" + #include "thirdparty/raknet/GetTime.h" + + #ifndef DEMO +@@ -335,6 +336,22 @@ void ExternalFileLevelStorage::loadEntities(Level* level, LevelChunk* chunk) + level->addEntity(entity); + } + } ++ ++ const ListTag* tileEntitiesTag = tag->getList("TileEntities"); ++ if (tileEntitiesTag) ++ { ++ const std::vector& tileEntities = tileEntitiesTag->rawView(); ++ for (std::vector::const_iterator it = tileEntities.begin(); it != tileEntities.end(); it++) ++ { ++ const Tag* betterTag = *it; ++ if (!betterTag || betterTag->getId() != Tag::TAG_TYPE_COMPOUND) ++ continue; ++ ++ TileEntity* tileEntity = TileEntity::LoadTileEntity(*(CompoundTag*)betterTag); ++ if (tileEntity) ++ level->setTileEntity(tileEntity->m_pos, tileEntity); ++ } ++ } + } + + tag->deleteChildren(); +@@ -395,8 +412,21 @@ void ExternalFileLevelStorage::saveEntities(Level* level, LevelChunk* chunk) + entitiesTag->add(tag); + } + ++ ListTag* tileEntitiesTag = new ListTag(); ++ ++ const TileEntityVector* tileEntities = level->getAllTileEntities(); ++ for (TileEntityVector::const_iterator it = tileEntities->begin(); it != tileEntities->end(); it++) ++ { ++ const TileEntity* tileEntity = *it; ++ CompoundTag* tag = new CompoundTag(); ++ ++ tileEntity->save(*tag); ++ tileEntitiesTag->add(tag); ++ } ++ + CompoundTag tag = CompoundTag(); + tag.put("Entities", entitiesTag); ++ tag.put("TileEntities", tileEntitiesTag); + RakNet::BitStream bs; + RakDataOutput dos = RakDataOutput(bs); + NbtIo::write(tag, dos); +diff --git a/source/world/level/storage/LevelSource.hpp b/source/world/level/storage/LevelSource.hpp +index 97cc026b2..7cdbc8d2b 100644 +--- a/source/world/level/storage/LevelSource.hpp ++++ b/source/world/level/storage/LevelSource.hpp +@@ -12,6 +12,8 @@ + #include "world/level/Material.hpp" + #include "world/level/levelgen/biome/BiomeSource.hpp" + ++class TileEntity; ++ + class LevelSource + { + public: +@@ -22,5 +24,6 @@ class LevelSource + virtual Material* getMaterial(const TilePos& pos) const = 0; + virtual bool isSolidTile(const TilePos& pos) const = 0; + virtual BiomeSource* getBiomeSource() const = 0; ++ virtual TileEntity* getTileEntity(const TilePos& pos) const = 0; + }; + +diff --git a/source/world/particle/BubbleParticle.cpp b/source/world/particle/BubbleParticle.cpp +index 234917b45..96642b64a 100644 +--- a/source/world/particle/BubbleParticle.cpp ++++ b/source/world/particle/BubbleParticle.cpp +@@ -13,14 +13,14 @@ BubbleParticle::BubbleParticle(Level* level, const Vec3& pos, const Vec3& dir) : + Particle(level, pos, dir) + { + m_rCol = m_gCol = m_bCol = 1.0f; +- field_DC = PTI_BUBBLE; ++ m_tex = PTI_BUBBLE; + setSize(0.02f, 0.02f); + +- field_F0 *= 0.2f + 0.6f * sharedRandom.nextFloat(); ++ m_size *= 0.2f + 0.6f * sharedRandom.nextFloat(); + m_vel.x = dir.x * 0.2f + 0.02f * (2.0f * Mth::random() - 1.0f); + m_vel.y = dir.y * 0.2f + 0.02f * (2.0f * Mth::random() - 1.0f); + m_vel.z = dir.z * 0.2f + 0.02f * (2.0f * Mth::random() - 1.0f); +- field_EC = int(8.0f / (Mth::random() * 0.8f + 0.2f)); ++ m_lifetime = int(8.0f / (Mth::random() * 0.8f + 0.2f)); + } + + void BubbleParticle::tick() +@@ -35,7 +35,7 @@ void BubbleParticle::tick() + if (m_pLevel->getMaterial(m_pos) != Material::water) + remove(); + +- field_EC--; +- if (field_EC <= 0) ++ m_lifetime--; ++ if (m_lifetime <= 0) + remove(); + } +diff --git a/source/world/particle/ExplodeParticle.cpp b/source/world/particle/ExplodeParticle.cpp +index e5149e29a..c13d15f45 100644 +--- a/source/world/particle/ExplodeParticle.cpp ++++ b/source/world/particle/ExplodeParticle.cpp +@@ -18,20 +18,20 @@ ExplodeParticle::ExplodeParticle(Level* level, const Vec3& pos, const Vec3& dir) + m_vel.z = dir.z + 0.05f * (2.0f * Mth::random() - 1.0f); + + m_rCol = m_gCol = m_bCol = 0.7f + 0.3f * sharedRandom.nextFloat(); +- field_F0 = 1.0f + 6.0f * sharedRandom.nextFloat() * sharedRandom.nextFloat(); +- field_EC = int(16.0f / (0.2f + 0.8f * sharedRandom.nextFloat())) + 2; ++ m_size = 1.0f + 6.0f * sharedRandom.nextFloat() * sharedRandom.nextFloat(); ++ m_lifetime = int(16.0f / (0.2f + 0.8f * sharedRandom.nextFloat())) + 2; + } + + void ExplodeParticle::tick() + { + m_oPos = m_pos; + +- field_E8++; +- if (field_E8 > field_EC) ++ m_timer++; ++ if (m_timer > m_lifetime) + remove(); + + m_vel.y += 0.004f; +- field_DC = -8 * field_E8 / field_EC + 7; ++ m_tex = -8 * m_timer / m_lifetime + 7; + + move(m_vel); + +diff --git a/source/world/particle/FlameParticle.cpp b/source/world/particle/FlameParticle.cpp +index 39a63d158..5e17d316e 100644 +--- a/source/world/particle/FlameParticle.cpp ++++ b/source/world/particle/FlameParticle.cpp +@@ -23,10 +23,10 @@ FlameParticle::FlameParticle(Level* level, const Vec3& pos, const Vec3& dir) : + sharedRandom.genrand_int32(); + sharedRandom.genrand_int32(); + +- field_104 = field_F0; ++ field_104 = m_size; + m_rCol = m_gCol = m_bCol = 1.0f; +- field_EC = int(8.0f / (0.2f + 0.8f * Mth::random())) + 4; +- field_DC = PTI_FLAME; ++ m_lifetime = int(8.0f / (0.2f + 0.8f * Mth::random())) + 4; ++ m_tex = PTI_FLAME; + } + + float FlameParticle::getBrightness(float unused) const +@@ -38,8 +38,8 @@ void FlameParticle::tick() + { + m_oPos = m_pos; + +- field_E8++; +- if (field_E8 > field_EC) ++ m_timer++; ++ if (m_timer > m_lifetime) + remove(); + + move(m_vel); +@@ -55,7 +55,7 @@ void FlameParticle::tick() + + void FlameParticle::render(Tesselator& t, float f, float a, float b, float c, float d, float e) + { +- float mult = float(field_E8 + f) / float(field_EC); +- field_F0 = field_104 * (1.0f - 0.5f * mult * mult); ++ float mult = float(m_timer + f) / float(m_lifetime); ++ m_size = field_104 * (1.0f - 0.5f * mult * mult); + Particle::render(t, f, a, b, c, d, e); + } +diff --git a/source/world/particle/LavaParticle.cpp b/source/world/particle/LavaParticle.cpp +index d76ad60bd..413eedd87 100644 +--- a/source/world/particle/LavaParticle.cpp ++++ b/source/world/particle/LavaParticle.cpp +@@ -17,9 +17,9 @@ LavaParticle::LavaParticle(Level* level, const Vec3& pos) : + m_vel *= 0.8f; + m_vel.y = sharedRandom.nextFloat() * 0.4f + 0.05f; + m_rCol = m_gCol = m_bCol = 1.0f; +- field_104 = field_F0 = field_F0 * (0.2f + 2 * sharedRandom.nextFloat()); +- field_DC = PTI_LAVA; +- field_EC = int(16.0f / (0.2f + 0.8f * Mth::random())); ++ field_104 = m_size = m_size * (0.2f + 2 * sharedRandom.nextFloat()); ++ m_tex = PTI_LAVA; ++ m_lifetime = int(16.0f / (0.2f + 0.8f * Mth::random())); + } + + float LavaParticle::getBrightness(float unused) const +@@ -31,11 +31,11 @@ void LavaParticle::tick() + { + m_oPos = m_pos; + +- field_E8++; +- if (field_E8 > field_EC) ++ m_timer++; ++ if (m_timer > m_lifetime) + remove(); + +- float a = float(field_E8) / float(field_EC); ++ float a = float(m_timer) / float(m_lifetime); + float b = sharedRandom.nextFloat(); + if (a < b) + { +@@ -55,7 +55,7 @@ void LavaParticle::tick() + + void LavaParticle::render(Tesselator& t, float f, float a, float b, float c, float d, float e) + { +- float mult = float(field_E8 + f) / float(field_EC); +- field_F0 = field_104 * (1.0f - mult * mult); ++ float mult = float(m_timer + f) / float(m_lifetime); ++ m_size = field_104 * (1.0f - mult * mult); + Particle::render(t, f, a, b, c, d, e); + } +diff --git a/source/world/particle/NoteParticle.cpp b/source/world/particle/NoteParticle.cpp +new file mode 100644 +index 000000000..149dee931 +--- /dev/null ++++ b/source/world/particle/NoteParticle.cpp +@@ -0,0 +1,47 @@ ++#include "Particle.hpp" ++ ++NoteParticle::NoteParticle(Level* level, const Vec3& pos, const Vec3& dir, float scale) : ++ Particle(level, pos, Vec3::ZERO) ++{ ++ m_vel.x *= 0.01f; ++ m_vel.y *= 0.01f; ++ m_vel.z *= 0.01f; ++ m_vel.y += 0.2f; ++ m_rCol = Mth::sin((dir.x + 0.0f) * M_PI * 2.0f) * 0.65f + 0.35f; ++ m_gCol = Mth::sin((dir.x + (1.0f / 3.0f)) * M_PI * 2.0f) * 0.65f + 0.35f; ++ m_bCol = Mth::sin((dir.x + (2.0f / 3.0f)) * M_PI * 2.0f) * 0.65f + 0.35f; ++ m_size *= 0.75f * scale; ++ m_oSize = m_size; ++ m_lifetime = 6; ++ m_bNoPhysics = false; ++ m_tex = PTI_NOTE; ++} ++ ++void NoteParticle::tick() ++{ ++ m_oPos = m_pos; ++ ++ m_timer++; ++ if (m_timer > m_lifetime) ++ remove(); ++ ++ move(m_vel); ++ if (m_pos.y == m_oPos.y) ++ { ++ m_vel *= Vec3(1.1f, 1.0f, 1.1f); ++ } ++ ++ m_vel *= 0.66f; ++ ++ if (m_bOnGround) ++ { ++ m_vel *= Vec3(0.7f, 1.0f, 0.7f); ++ } ++} ++ ++void NoteParticle::render(Tesselator& t, float f, float a, float b, float c, float d, float e) ++{ ++ float mult = float(m_timer + f) / float(m_lifetime) * 32.0f; ++ m_size = m_oSize * Mth::clamp(mult, 0.0f, 1.0f); ++ Particle::render(t, f, a, b, c, d, e); ++} +diff --git a/source/world/particle/Particle.cpp b/source/world/particle/Particle.cpp +index 8b276c1db..e29863500 100644 +--- a/source/world/particle/Particle.cpp ++++ b/source/world/particle/Particle.cpp +@@ -12,12 +12,12 @@ float Particle::xOff, Particle::yOff, Particle::zOff; + + void Particle::_init() + { +- field_DC = 0; ++ m_tex = 0; + field_E0 = 0.0f; + field_E4 = 0.0f; +- field_E8 = 0; +- field_EC = 0; +- field_F0 = 0.0f; ++ m_timer = 0; ++ m_lifetime = 0; ++ m_size = 0.0f; + field_F4 = 0.0f; + m_rCol = 1.0f; + m_gCol = 1.0f; +@@ -45,8 +45,8 @@ Particle::Particle(Level* level, const Vec3& pos, const Vec3& dir) : Entity(leve + + field_E0 = 3.0f * sharedRandom.nextFloat(); + field_E4 = 3.0f * sharedRandom.nextFloat(); +- field_F0 = 2.0f * (0.5f + 0.5f * sharedRandom.nextFloat()); +- field_EC = int(4.0f / (0.1f + 0.9f * sharedRandom.nextFloat())); ++ m_size = 2.0f * (0.5f + 0.5f * sharedRandom.nextFloat()); ++ m_lifetime = int(4.0f / (0.1f + 0.9f * sharedRandom.nextFloat())); + } + + int Particle::getParticleTexture() +@@ -57,7 +57,7 @@ int Particle::getParticleTexture() + Particle* Particle::scale(float f) + { + setSize(0.2f * f, 0.2f * f); +- field_F0 *= f; ++ m_size *= f; + return this; + } + +@@ -73,7 +73,7 @@ void Particle::render(Tesselator& t, float f, float a4, float a5, float a6, floa + { + constexpr float C_MAGIC_1 = 0.062438f; // @BUG: Slightly bigger than 1/16.0f + +- int texture = field_DC; ++ int texture = m_tex; + int texX = texture % 16; + if (texture < 0) + texture += 15; +@@ -86,11 +86,11 @@ void Particle::render(Tesselator& t, float f, float a4, float a5, float a6, floa + float posZ = Mth::Lerp(m_oPos.z, m_pos.z, f) - zOff; + float fBright = m_bIsUnlit ? 1.0f : getBrightness(f); + +- float sizeX = a4 * field_F0 * 0.1f; +- float sizeY = a5 * field_F0 * 0.1f; +- float sizeZ = a6 * field_F0 * 0.1f; +- float siz2X = a7 * field_F0 * 0.1f; +- float siz2Z = a8 * field_F0 * 0.1f; ++ float sizeX = a4 * m_size * 0.1f; ++ float sizeY = a5 * m_size * 0.1f; ++ float sizeZ = a6 * m_size * 0.1f; ++ float siz2X = a7 * m_size * 0.1f; ++ float siz2Z = a8 * m_size * 0.1f; + + t.color(m_rCol * fBright, m_gCol * fBright, m_bCol * fBright); + t.vertexUV(posX - sizeX - siz2X, posY - sizeY, posZ - sizeZ - siz2Z, texU_1 + C_MAGIC_1, texV_1 + C_MAGIC_1); +@@ -102,8 +102,8 @@ void Particle::render(Tesselator& t, float f, float a4, float a5, float a6, floa + void Particle::tick() + { + m_oPos = m_pos; +- field_E8++; +- if (field_E8 >= field_EC) ++ m_timer++; ++ if (m_timer >= m_lifetime) + remove(); + + m_vel.y -= field_F4 * 0.04f; +diff --git a/source/world/particle/Particle.hpp b/source/world/particle/Particle.hpp +index 85d4814bc..9d58ab074 100644 +--- a/source/world/particle/Particle.hpp ++++ b/source/world/particle/Particle.hpp +@@ -23,7 +23,8 @@ enum eParticleTextureIndex + { + PTI_BUBBLE = 32, + PTI_FLAME = 48, +- PTI_LAVA ++ PTI_LAVA, ++ PTI_NOTE = 64 + }; + + class Particle : public Entity +@@ -45,12 +46,12 @@ class Particle : public Entity + Particle* setPower(float); + + public: +- int field_DC; ++ int m_tex; + float field_E0; + float field_E4; +- int field_E8; +- int field_EC; +- float field_F0; ++ int m_timer; ++ int m_lifetime; ++ float m_size; + float field_F4; + float m_rCol; + float m_gCol; +@@ -138,3 +139,14 @@ class LavaParticle : public Particle + public: + float field_104; + }; ++ ++class NoteParticle : public Particle ++{ ++public: ++ NoteParticle(Level*, const Vec3& pos, const Vec3& dir, float scale = 2.0f); ++ void tick() override; ++ void render(Tesselator&, float, float, float, float, float, float) override; ++ ++public: ++ float m_oSize; ++}; +\ No newline at end of file +diff --git a/source/world/particle/RedDustParticle.cpp b/source/world/particle/RedDustParticle.cpp +index bae587260..b7edf53a4 100644 +--- a/source/world/particle/RedDustParticle.cpp ++++ b/source/world/particle/RedDustParticle.cpp +@@ -20,21 +20,21 @@ RedDustParticle::RedDustParticle(Level* level, const Vec3& pos, const Vec3& dir) + m_gCol = f * dir.y * (Mth::random() * 0.2f + 0.8f); + m_bCol = f * dir.z * (Mth::random() * 0.2f + 0.8f); + +- field_104 = field_F0 = field_F0 * 0.75f; ++ field_104 = m_size = m_size * 0.75f; + + m_bNoPhysics = false; +- field_EC = int(8.0f / (0.2f + 0.8f * Mth::random())); ++ m_lifetime = int(8.0f / (0.2f + 0.8f * Mth::random())); + } + + void RedDustParticle::render(Tesselator& t, float f, float a, float b, float c, float d, float e) + { +- float mult = 32.0f * (float(field_E8 + f) / float(field_EC)); ++ float mult = 32.0f * (float(m_timer + f) / float(m_lifetime)); + if (mult < 0.0f) + mult = 0.0f; + if (mult > 1.0f) + mult = 1.0f; + +- field_F0 = field_104 * mult; ++ m_size = field_104 * mult; + Particle::render(t, f, a, b, c, d, e); + } + +@@ -42,12 +42,12 @@ void RedDustParticle::tick() + { + m_oPos = m_pos; + +- field_E8++; +- if (field_E8 > field_EC) ++ m_timer++; ++ if (m_timer > m_lifetime) + remove(); + + m_vel.y += 0.004f; +- field_DC = -8 * field_E8 / field_EC + 7; ++ m_tex = -8 * m_timer / m_lifetime + 7; + + move(m_vel); + +diff --git a/source/world/particle/SmokeParticle.cpp b/source/world/particle/SmokeParticle.cpp +index 2c781a660..812e291ee 100644 +--- a/source/world/particle/SmokeParticle.cpp ++++ b/source/world/particle/SmokeParticle.cpp +@@ -17,21 +17,21 @@ SmokeParticle::SmokeParticle(Level* level, const Vec3& pos, const Vec3& dir, flo + + m_bCol = m_gCol = m_rCol = Mth::random() * 0.5f; + +- field_104 = field_F0 = field_F0 * 0.75f * a9; ++ field_104 = m_size = m_size * 0.75f * a9; + + m_bNoPhysics = false; +- field_EC = int((a9 * 8.0f) / (0.2f + 0.8f * Mth::random())); ++ m_lifetime = int((a9 * 8.0f) / (0.2f + 0.8f * Mth::random())); + } + + void SmokeParticle::render(Tesselator& t, float f, float a, float b, float c, float d, float e) + { +- float mult = 32.0f * (float(field_E8 + f) / float(field_EC)); ++ float mult = 32.0f * (float(m_timer + f) / float(m_lifetime)); + if (mult < 0.0f) + mult = 0.0f; + if (mult > 1.0f) + mult = 1.0f; + +- field_F0 = field_104 * mult; ++ m_size = field_104 * mult; + Particle::render(t, f, a, b, c, d, e); + } + +@@ -39,12 +39,12 @@ void SmokeParticle::tick() + { + m_oPos = m_pos; + +- field_E8++; +- if (field_E8 > field_EC) ++ m_timer++; ++ if (m_timer > m_lifetime) + remove(); + + m_vel.y += 0.004f; +- field_DC = -8 * field_E8 / field_EC + 7; ++ m_tex = -8 * m_timer / m_lifetime + 7; + + move(m_vel); + +diff --git a/source/world/particle/TerrainParticle.cpp b/source/world/particle/TerrainParticle.cpp +index 3235e2b95..cf6b02f60 100644 +--- a/source/world/particle/TerrainParticle.cpp ++++ b/source/world/particle/TerrainParticle.cpp +@@ -12,10 +12,10 @@ + void TerrainParticle::_init(Tile* tile) + { + m_pTile = tile; +- field_DC = tile->m_TextureFrame; ++ m_tex = tile->m_TextureFrame; + field_F4 = tile->field_28; + m_rCol = m_gCol = m_bCol = 0.6f; +- field_F0 *= 0.5f; ++ m_size *= 0.5f; + } + + TerrainParticle::TerrainParticle(Level* level, const Vec3& pos, Tile* tile) : +@@ -36,7 +36,7 @@ TerrainParticle* TerrainParticle::init(const TilePos& tilePos, Facing::Name face + face = Facing::DOWN; + #endif + +- field_DC = m_pTile->getTexture(m_pLevel, tilePos, face); ++ m_tex = m_pTile->getTexture(m_pLevel, tilePos, face); + + if (m_pTile == Tile::grass && face != Facing::UP) + return this; +@@ -58,7 +58,7 @@ void TerrainParticle::render(Tesselator& t, float f, float a4, float a5, float a + { + constexpr float C_MAGIC_1 = 0.015609f; // @BUG: Slightly bigger than 1/64.0f + +- int texture = field_DC; ++ int texture = m_tex; + int texX = texture % 16; + if (texture < 0) + texture += 15; +@@ -71,11 +71,11 @@ void TerrainParticle::render(Tesselator& t, float f, float a4, float a5, float a + float posZ = Mth::Lerp(m_oPos.z, m_pos.z, f) - zOff; + float fBright = getBrightness(f); + +- float sizeX = a4 * field_F0 * 0.1f; +- float sizeY = a5 * field_F0 * 0.1f; +- float sizeZ = a6 * field_F0 * 0.1f; +- float siz2X = a7 * field_F0 * 0.1f; +- float siz2Z = a8 * field_F0 * 0.1f; ++ float sizeX = a4 * m_size * 0.1f; ++ float sizeY = a5 * m_size * 0.1f; ++ float sizeZ = a6 * m_size * 0.1f; ++ float siz2X = a7 * m_size * 0.1f; ++ float siz2Z = a8 * m_size * 0.1f; + + t.color(m_rCol * fBright, m_gCol * fBright, m_bCol * fBright); + t.vertexUV(posX - sizeX - siz2X, posY - sizeY, posZ - sizeZ - siz2Z, texU_1 + C_MAGIC_1, texV_1 + C_MAGIC_1); +diff --git a/source/world/phys/Vec3.hpp b/source/world/phys/Vec3.hpp +index a3725336e..efb4333ae 100644 +--- a/source/world/phys/Vec3.hpp ++++ b/source/world/phys/Vec3.hpp +@@ -123,6 +123,13 @@ class Vec3 + (*this) += -b; + } + ++ void operator*=(const Vec3& b) ++ { ++ x *= b.x; ++ y *= b.y; ++ z *= b.z; ++ } ++ + void operator+=(float f) + { + x += f; +diff --git a/source/world/tile/ChestTile.cpp b/source/world/tile/ChestTile.cpp +new file mode 100644 +index 000000000..327bd0bf6 +--- /dev/null ++++ b/source/world/tile/ChestTile.cpp +@@ -0,0 +1,217 @@ ++#include "ChestTile.hpp" ++#include "world/level/Level.hpp" ++#include "world/CompoundContainer.hpp" ++#include "world/tile/entity/ChestTileEntity.hpp" ++ ++ChestTile::ChestTile(int id, int texture) : Tile(id, texture, Material::wood) ++{ ++ setTicking(true); ++} ++ ++int ChestTile::getTexture(const LevelSource* level, const TilePos& pos, Facing::Name face) const ++{ ++ if (face == Facing::UP || face == Facing::DOWN) { ++ return m_TextureFrame - 1; ++ } ++ ++ int id_north = level->getTile(pos.north()); ++ int id_south = level->getTile(pos.south()); ++ int id_west = level->getTile(pos.west()); ++ int id_east = level->getTile(pos.east()); ++ ++ bool isDoubleNS = (id_north == m_ID || id_south == m_ID); ++ bool isDoubleWE = (id_west == m_ID || id_east == m_ID); ++ ++ if (!isDoubleNS && !isDoubleWE) { ++ Facing::Name front = Facing::SOUTH; ++ ++ if (Tile::solid[id_north] && !Tile::solid[id_south]) ++ front = Facing::SOUTH; ++ else if (Tile::solid[id_south] && !Tile::solid[id_north]) ++ front = Facing::NORTH; ++ else if (Tile::solid[id_west] && !Tile::solid[id_east]) ++ front = Facing::EAST; ++ else if (Tile::solid[id_east] && !Tile::solid[id_west]) ++ front = Facing::WEST; ++ ++ return (face == front) ? m_TextureFrame + 1 : m_TextureFrame; ++ } ++ ++ bool left = false; ++ Facing::Name front = Facing::SOUTH; ++ ++ if (isDoubleWE) { ++ left = id_west == m_ID; ++ TilePos side = left ? pos.west() : pos.east(); ++ ++ int id_behind = level->getTile(side.north()); ++ int id_infront = level->getTile(side.south()); ++ ++ if ((Tile::solid[id_north] || Tile::solid[id_behind]) && !Tile::solid[id_south] && !Tile::solid[id_infront]) ++ front = Facing::SOUTH; ++ else if ((Tile::solid[id_south] || Tile::solid[id_infront]) && !Tile::solid[id_north] && !Tile::solid[id_behind]) ++ front = Facing::NORTH; ++ ++ if (front == Facing::SOUTH) left = !left; ++ if (face == front) return m_TextureFrame + (left ? 15 : 16); ++ if (face == Facing::OPPOSITE[front]) return m_TextureFrame + (left ? 32 : 31); ++ return m_TextureFrame; ++ } ++ ++ if (isDoubleNS) ++ { ++ front = Facing::EAST; ++ left = id_north == m_ID; ++ TilePos side = left ? pos.north() : pos.south(); ++ ++ int id_behind = level->getTile(side.west()); ++ int id_infront = level->getTile(side.east()); ++ ++ if ((Tile::solid[id_west] || Tile::solid[id_behind]) && !Tile::solid[id_east] && !Tile::solid[id_infront]) { ++ front = Facing::EAST; ++ } ++ else if ((Tile::solid[id_east] || Tile::solid[id_infront]) && !Tile::solid[id_west] && !Tile::solid[id_behind]) { ++ front = Facing::WEST; ++ left = !left; ++ } ++ ++ if (face == front) ++ return m_TextureFrame + (left ? 15 : 16); ++ if (face == Facing::OPPOSITE[front]) ++ return m_TextureFrame + (left ? 32 : 31); ++ return m_TextureFrame; ++ } ++ ++ return m_TextureFrame; ++} ++ ++int ChestTile::getTexture(Facing::Name face) const ++{ ++ switch (face) { ++ case Facing::UP: ++ case Facing::DOWN: ++ return m_TextureFrame - 1; ++ case Facing::SOUTH: ++ return m_TextureFrame + 1; ++ default: ++ return m_TextureFrame; ++ } ++} ++ ++bool ChestTile::mayPlace(const Level* level, const TilePos& pos) const ++{ ++ return !hasNeighbors(level, pos, 1); ++} ++ ++bool ChestTile::hasNeighbors(const Level* level, const TilePos& pos, int count) const ++{ ++ int neighbors = 0; ++ for (int i = 0; i < 4; ++i) ++ { ++ Facing::Name f = static_cast(Facing::HORIZONTAL[i]); ++ TilePos relative = pos.relative(f); ++ if (level->getTile(relative) == m_ID) ++ { ++ neighbors++; ++ if (neighbors > count) return true; ++ ++ if (hasNeighbors(level, relative, 0)) return true; ++ } ++ } ++ ++ return false; ++} ++ ++void ChestTile::onRemove(Level* level, const TilePos& pos){ ++ ChestTileEntity* ent = static_cast(level->getTileEntity(pos)); ++ ++ if (!ent) ++ { ++ Tile::onRemove(level, pos); ++ } ++ ++ for (int slot = 0; slot < ent->getContainerSize(); ++slot) ++ { ++ ItemStack& item = ent->getItem(slot); ++ if (!item.isEmpty()) ++ { ++ TilePos offset( ++ (m_chestRandom.nextFloat() * 0.8f) + 0.1f, ++ (m_chestRandom.nextFloat() * 0.8f) + 0.1f, ++ (m_chestRandom.nextFloat() * 0.8f) + 0.1f ++ ); ++ ++ while (item.m_count > 0) ++ { ++ int toRemove = std::min(m_chestRandom.nextInt(21) + 10, static_cast(item.m_count)); ++ item.m_count -= toRemove; ++ ItemEntity* itemEnt = new ItemEntity(level, pos + offset, ItemStack(item.getItem()->m_itemID, toRemove, item.getAuxValue())); ++ float spread = 0.05f; ++ itemEnt->m_vel.x = (double)((float)m_chestRandom.nextGaussian() * spread); ++ itemEnt->m_vel.y = (double)((float)m_chestRandom.nextGaussian() * spread + 0.2f); ++ itemEnt->m_vel.z = (double)((float)m_chestRandom.nextGaussian() * spread); ++ level->addEntity(itemEnt); ++ } ++ } ++ } ++ ++ Tile::onRemove(level, pos); ++} ++ ++bool ChestTile::use(Level* level, const TilePos& pos, Player* player) ++{ ++ if (level->m_bIsClientSide) ++ return true; ++ ++ if (level->isSolidTile(pos.above())) ++ return true; ++ ++ if (level->getTile(pos.west()) == m_ID && level->isSolidTile(pos + TilePos(-1, 1, 0))) ++ return true; ++ ++ if (level->getTile(pos.east()) == m_ID && level->isSolidTile(pos + TilePos(1, 1, 0))) ++ return true; ++ ++ if (level->getTile(pos.north()) == m_ID && level->isSolidTile(pos + TilePos(0, 1, -1))) ++ return true; ++ ++ if (level->getTile(pos.south()) == m_ID && level->isSolidTile(pos + TilePos(0, 1, 1))) ++ return true; ++ ++ TileEntity* tileEnt = level->getTileEntity(pos); ++ if (!tileEnt) ++ return false; ++ ++ ChestTileEntity* chest = static_cast(tileEnt); ++ Container* container = static_cast(chest); ++ for (int rel = Facing::NORTH; rel <= Facing::EAST; rel++) ++ { ++ TilePos relPos = pos.relative(static_cast(rel)); ++ if (level->getTile(pos.relative(static_cast(rel))) == m_ID) ++ { ++ TileEntity* nearbyTileEntity = level->getTileEntity(relPos); ++ if (!nearbyTileEntity) ++ continue; ++ ++ Container* nearbyContainer = static_cast(static_cast(nearbyTileEntity)); ++ if (rel % 2 == 0) ++ container = new CompoundContainer("Large chest", nearbyContainer, container); ++ else ++ container = new CompoundContainer("Large chest", container, nearbyContainer); ++ break; ++ } ++ } ++ ++ player->openContainer(container); ++ return true; ++} ++ ++bool ChestTile::hasTileEntity() const ++{ ++ return true; ++} ++ ++TileEntity* ChestTile::newTileEntity() ++{ ++ return new ChestTileEntity(); ++} +diff --git a/source/world/tile/ChestTile.hpp b/source/world/tile/ChestTile.hpp +new file mode 100644 +index 000000000..051081631 +--- /dev/null ++++ b/source/world/tile/ChestTile.hpp +@@ -0,0 +1,20 @@ ++#pragma once ++ ++#include "Tile.hpp" ++ ++class ChestTile : public Tile ++{ ++public: ++ ChestTile(int id, int texture); ++public: ++ int getTexture(const LevelSource*, const TilePos& pos, Facing::Name face) const override; ++ int getTexture(Facing::Name face) const override; ++ bool mayPlace(const Level* level, const TilePos& pos) const override; ++ bool hasNeighbors(const Level* level, const TilePos& pos, int count) const; ++ void onRemove(Level* level, const TilePos& pos) override; ++ bool use(Level* level, const TilePos& pos, Player* var5) override; ++ bool hasTileEntity() const override; ++ TileEntity* newTileEntity() override; ++public: ++ Random m_chestRandom; ++}; +\ No newline at end of file +diff --git a/source/world/tile/CropsTile.cpp b/source/world/tile/CropsTile.cpp +index cb361a031..9cd432ede 100644 +--- a/source/world/tile/CropsTile.cpp ++++ b/source/world/tile/CropsTile.cpp +@@ -85,7 +85,7 @@ float CropsTile::getGrowthRate(Level* level, const TilePos& pos) + + if (diag || (hor && vert)) + { +- rate /= 2.0F; ++ rate /= 2.0f; + } + + return rate; +diff --git a/source/world/tile/FurnaceTile.cpp b/source/world/tile/FurnaceTile.cpp +new file mode 100644 +index 000000000..e5ea21f65 +--- /dev/null ++++ b/source/world/tile/FurnaceTile.cpp +@@ -0,0 +1,194 @@ ++#include "FurnaceTile.hpp" ++#include "world/level/Level.hpp" ++#include "entity/FurnaceTileEntity.hpp" ++ ++bool FurnaceTile::keepInventory = false; ++ ++FurnaceTile::FurnaceTile(int id, bool active) : Tile(id, TEXTURE_FURNACE_SIDE, Material::stone) ++{ ++ setTicking(true); ++ m_active = active; ++} ++ ++int FurnaceTile::getTexture(const LevelSource* level, const TilePos& pos, Facing::Name face) const ++{ ++ switch (face) ++ { ++ case Facing::UP: ++ case Facing::DOWN: ++ return m_TextureFrame + 17; ++ default: ++ { ++ int meta = level->getData(pos); ++ return face != meta ? m_TextureFrame : (m_active ? m_TextureFrame + 16 : m_TextureFrame - 1); ++ } ++ } ++} ++ ++void FurnaceTile::animateTick(Level* level, const TilePos& pos, Random* random) { ++ if (!m_active) ++ return; ++ ++ int data = level->getData(pos); ++ Vec3 particlePos(pos.x + 0.5f, pos.y + 0.0f + random->nextFloat() * 6.0f / 16.0f, pos.z + 0.5f); ++ const float outward = 0.52f; ++ float randomOffset = random->nextFloat() * 0.6f - 0.3f; ++ ++ if (random->nextFloat() < 0.1f) ++ level->playSound(Vec3(particlePos.x + 0.5f, particlePos.y + 0.5f, particlePos.z + 0.5f), "fire.fire_crackle", 1.0f, 1.0f); ++ ++ if (data == 4 || data == 5) ++ { ++ particlePos.x += data == 4 ? -outward : outward; ++ particlePos.z += randomOffset; ++ } ++ else if (data == 2 || data == 3) ++ { ++ particlePos.x += randomOffset; ++ particlePos.z += data == 2 ? -outward : outward; ++ } ++ ++ level->addParticle("smoke", particlePos); ++ level->addParticle("flame", particlePos); ++} ++ ++int FurnaceTile::getTexture(Facing::Name face) const ++{ ++ switch (face) { ++ case Facing::UP: ++ case Facing::DOWN: ++ return m_TextureFrame + 17; ++ case Facing::SOUTH: ++ return (m_active) ? m_TextureFrame + 16 : m_TextureFrame - 1; ++ default: ++ return m_TextureFrame; ++ } ++} ++ ++void FurnaceTile::onPlace(Level* level, const TilePos& pos) ++{ ++ Tile::onPlace(level, pos); ++ RecalculateLookDirection(level, pos); ++} ++ ++bool FurnaceTile::use(Level* level, const TilePos& pos, Player* player) ++{ ++ if (player->isSneaking() && player->getSelectedItem()) ++ return false; ++ ++ if (level->m_bIsClientSide) ++ return true; ++ ++ player->openFurnace(static_cast(level->getTileEntity(pos))); ++ return true; ++} ++ ++void FurnaceTile::setPlacedBy(Level* level, const TilePos& pos, Mob* mob) ++{ ++ int rot = Mth::floor(0.5f + (mob->m_rot.y * 4.0f / 360.0f)) & 3; ++ int data = 4; ++ ++ switch (rot) ++ { ++ case 0: ++ data = 2; ++ break; ++ case 1: ++ data = 5; ++ break; ++ case 2: ++ data = 3; ++ break; ++ } ++ ++ level->setData(pos, data); ++} ++ ++void FurnaceTile::onRemove(Level* level, const TilePos& pos) ++{ ++ if (keepInventory) ++ return; ++ ++ TileEntity* tileEnt = level->getTileEntity(pos); ++ ++ if (!tileEnt) ++ { ++ return; // this has to be wrapped in a guard or the compiler screams, thanks -Wmisleading-indentation ++ } ++ ++ FurnaceTileEntity* furnace = static_cast(tileEnt); ++ for (int i = 0; i < furnace->getContainerSize(); ++i) ++ { ++ ItemStack& item = furnace->getItem(i); ++ if (item.isEmpty()) ++ continue; ++ ++ TilePos splitSource( ++ m_random.nextFloat() * 0.8f + 0.1f, ++ m_random.nextFloat() * 0.8f + 0.1f, ++ m_random.nextFloat() * 0.8f + 0.1f ++ ); ++ ++ while (item.m_count > 0) ++ { ++ int splitCount = std::min(m_random.nextInt(21) + 10, static_cast(item.m_count)); ++ item.m_count -= splitCount; ++ ItemEntity* itemEnt = new ItemEntity(level, splitSource + pos, ItemStack(item.getId(), splitCount, item.getAuxValue())); ++ ++ float deviation = 0.05f; ++ itemEnt->m_vel.x = (double)((float)m_random.nextGaussian() * deviation); ++ itemEnt->m_vel.y = (double)((float)m_random.nextGaussian() * deviation + 0.2f); ++ itemEnt->m_vel.z = (double)((float)m_random.nextGaussian() * deviation); ++ level->addEntity(itemEnt); ++ } ++ } ++} ++ ++void FurnaceTile::SetLit(bool lit, Level* level, const TilePos& pos) ++{ ++ TileEntity* tileEntity = level->getTileEntity(pos); ++ int data = level->getData(pos); ++ ++ keepInventory = true; ++ level->setTile(pos, (lit) ? Tile::furnaceLit->m_ID : Tile::furnace->m_ID); ++ keepInventory = false; ++ ++ level->setData(pos, data); ++ tileEntity->clearRemoved(); ++ level->setTileEntity(pos, tileEntity); ++} ++ ++bool FurnaceTile::hasTileEntity() const ++{ ++ return true; ++} ++ ++TileEntity* FurnaceTile::newTileEntity() ++{ ++ return new FurnaceTileEntity(); ++} ++ ++int FurnaceTile::getResource(TileData, Random*) const ++{ ++ return Tile::furnace->m_ID; ++} ++ ++void FurnaceTile::RecalculateLookDirection(Level* level, const TilePos& pos) ++{ ++ TileID n = level->getTile(pos.north()); ++ TileID s = level->getTile(pos.south()); ++ TileID w = level->getTile(pos.west()); ++ TileID e = level->getTile(pos.east()); ++ uint8_t lookData = 3; ++ ++ if (Tile::solid[e] && !Tile::solid[w]) ++ lookData = 4; ++ else if (Tile::solid[w] && !Tile::solid[e]) ++ lookData = 5; ++ else if (Tile::solid[s] && !Tile::solid[n]) ++ lookData = 2; ++ else if (Tile::solid[n] && !Tile::solid[s]) ++ lookData = 3; ++ ++ level->setData(pos, lookData); ++} +diff --git a/source/world/tile/FurnaceTile.hpp b/source/world/tile/FurnaceTile.hpp +new file mode 100644 +index 000000000..bb0ac9359 +--- /dev/null ++++ b/source/world/tile/FurnaceTile.hpp +@@ -0,0 +1,33 @@ ++#pragma once ++ ++#include "Tile.hpp" ++ ++class FurnaceTile : public Tile ++{ ++public: ++ FurnaceTile(int id, bool active); ++ ++public: ++ int getTexture(const LevelSource*, const TilePos& pos, Facing::Name face) const override; ++ void animateTick(Level* level, const TilePos& pos, Random* random) override; ++ int getTexture(Facing::Name face) const override; ++ void onPlace(Level* level, const TilePos& pos) override; ++ bool use(Level* level, const TilePos& pos, Player* player) override; ++ void setPlacedBy(Level*, const TilePos& pos, Mob*) override; ++ void onRemove(Level* level, const TilePos& pos) override; ++ bool hasTileEntity() const override; ++ TileEntity* newTileEntity() override; ++ int getResource(TileData, Random*) const override; ++ ++public: ++ static void SetLit(bool lit, Level* level, const TilePos& pos); ++ static void RecalculateLookDirection(Level* level, const TilePos& pos); ++ ++private: ++ Random m_random; ++ static bool keepInventory; ++ ++public: ++ bool m_active; ++}; ++ +diff --git a/source/world/tile/MusicTile.cpp b/source/world/tile/MusicTile.cpp +new file mode 100644 +index 000000000..7842f458e +--- /dev/null ++++ b/source/world/tile/MusicTile.cpp +@@ -0,0 +1,98 @@ ++#include "MusicTile.hpp" ++#include "world/level/Level.hpp" ++#include "entity/MusicTileEntity.hpp" ++ ++MusicTile::MusicTile(TileID id, int texture) : Tile(id, texture, Material::wood) ++{ ++} ++ ++void MusicTile::neighborChanged(Level* level, const TilePos& pos, TileID tile) ++{ ++ if (tile <= TILE_AIR || !Tile::tiles[tile]->isSignalSource()) ++ return; ++ ++ TileEntity* tileEnt = level->getTileEntity(pos); ++ if (!tileEnt) ++ return; ++ ++ bool signalProvided = level->hasDirectSignal(pos); ++ MusicTileEntity* musEnt = static_cast(tileEnt); ++ if (musEnt->m_bOn == signalProvided) ++ return; ++ ++ if (signalProvided) ++ musEnt->play(level, pos); ++ ++ musEnt->m_bOn = signalProvided; ++} ++ ++bool MusicTile::use(Level* level, const TilePos& pos, Player* player) ++{ ++ if (player->isSneaking() && player->getSelectedItem()) ++ return false; ++ ++ if (level->m_bIsClientSide) ++ return true; ++ ++ TileEntity* tileEnt = level->getTileEntity(pos); ++ if (!tileEnt) ++ return false; ++ ++ MusicTileEntity* musEnt = static_cast(tileEnt); ++ ++ musEnt->tune(); ++ musEnt->play(level, pos); ++ return true; ++} ++ ++void MusicTile::attack(Level* level, const TilePos& pos, Player* player) ++{ ++ if (level->m_bIsClientSide) ++ return; ++ ++ TileEntity* tileEnt = level->getTileEntity(pos); ++ if (!tileEnt) ++ return; ++ ++ (static_cast(tileEnt))->play(level, pos); ++} ++ ++bool MusicTile::hasTileEntity() const ++{ ++ return true; ++} ++ ++TileEntity* MusicTile::newTileEntity() ++{ ++ return new MusicTileEntity(); ++} ++ ++void MusicTile::triggerEvent(Level* level, const TileEvent& event) ++{ ++ int instrument = event.b0; ++ int note = event.b1; ++ ++ ++ std::string soundType; ++ switch (instrument) ++ { ++ default: ++ soundType = "harp"; ++ break; ++ case 1: ++ soundType = "bd"; ++ break; ++ case 2: ++ soundType = "snare"; ++ break; ++ case 3: ++ soundType = "hat"; ++ break; ++ case 4: ++ soundType = "bassattack"; ++ break; ++ } ++ ++ level->playSound(event.pos + 0.5f, "note." + soundType, 3.0f, std::pow(2.0f, (note - 12) / 12.0f)); ++ level->addParticle("note", Vec3(event.pos.x + 0.5f, event.pos.y + 1.2f, event.pos.z + 0.5f), Vec3(note / 24.0f, 0, 0)); ++} +diff --git a/source/world/tile/MusicTile.hpp b/source/world/tile/MusicTile.hpp +new file mode 100644 +index 000000000..fcddfba82 +--- /dev/null ++++ b/source/world/tile/MusicTile.hpp +@@ -0,0 +1,17 @@ ++#pragma once ++ ++#include "Tile.hpp" ++ ++class MusicTile : public Tile ++{ ++public: ++ MusicTile(TileID id, int textureID); ++ ++public: ++ void neighborChanged(Level*, const TilePos& pos, TileID tile) override; ++ bool use(Level*, const TilePos&, Player*) override; ++ void attack(Level*, const TilePos&, Player*) override; ++ bool hasTileEntity() const override; ++ void triggerEvent(Level*, const TileEvent&) override; ++ TileEntity* newTileEntity() override; ++}; +\ No newline at end of file +diff --git a/source/world/tile/Tile.cpp b/source/world/tile/Tile.cpp +index 7c5646745..4aea4c4b7 100644 +--- a/source/world/tile/Tile.cpp ++++ b/source/world/tile/Tile.cpp +@@ -55,12 +55,12 @@ + #include "RocketLauncherTile.hpp" + //#include "RedStoneDustTile.hpp" + #include "CraftingTableTile.hpp" +-//#include "FurnaceTile.hpp" ++#include "FurnaceTile.hpp" + #include "TallGrass.hpp" + #include "DeadBush.hpp" + //#include "Fern.hpp" + #include "CactusTile.hpp" +-//#include "ChestTile.hpp" ++#include "ChestTile.hpp" + #include "PumpkinTile.hpp" + #include "SoulSandTile.hpp" + #include "GlowstoneTile.hpp" +@@ -79,7 +79,7 @@ + //#include "RedstoneTorchTile.hpp" + //#include "CakeTile.hpp" + //#include "DispenserTile.hpp" +-//#include "MusicTile.hpp" ++#include "MusicTile.hpp" + //#include "RecordPlayerTile.hpp" + //#include "TrapDoorTile.hpp" + //#include "PortalTile.hpp" +@@ -205,7 +205,7 @@ Tile* Tile::init() + solid[m_ID] = isSolidRender(); + lightBlock[m_ID] = isSolidRender() ? 255 : 0; + translucent[m_ID] = m_pMaterial->blocksLight(); +- isEntityTile[m_ID] = 0; ++ isEntityTile[m_ID] = hasTileEntity(); + + m_toolMask = Tool::NONE; + m_requiredToolLevel = 0; +@@ -263,6 +263,11 @@ bool Tile::mayPick(TileData data, bool y) const + return mayPick(); + } + ++bool Tile::hasTileEntity() const ++{ ++ return false; ++} ++ + int Tile::getResource(TileData data, Random* pRandom) const + { + return m_ID; +@@ -278,6 +283,11 @@ int Tile::getSpawnResourcesAuxValue(int x) const + return 0; + } + ++TileEntity* Tile::newTileEntity() ++{ ++ return nullptr; ++} ++ + Tile* Tile::setToolTypes(unsigned int toolMask) + { + m_toolMask |= toolMask; +@@ -786,6 +796,31 @@ void Tile::initTiles() + ->setSoundType(Tile::SOUND_GRASS) + ->setDescriptionId("crops"); + ++ Tile::musicBlock = (new MusicTile(TILE_NOTE_BLOCK, TEXTURE_JUKEBOX_SIDE)) ++ ->init() ++ ->setSoundType(Tile::SOUND_WOOD) ++ ->setDestroyTime(0.8f) ++ ->setDescriptionId("musicBlock"); ++ ++ Tile::furnace = (new FurnaceTile(TILE_FURNACE, false)) ++ ->init() ++ ->setDestroyTime(3.5f) ++ ->setSoundType(Tile::SOUND_STONE) ++ ->setDescriptionId("furnace"); ++ ++ Tile::furnaceLit = (new FurnaceTile(TILE_FURNACE_LIT, true)) ++ ->init() ++ ->setLightEmission(14.0f / 16.0f) ++ ->setDestroyTime(3.5f) ++ ->setSoundType(Tile::SOUND_STONE) ++ ->setDescriptionId("furnace"); ++ ++ Tile::chest = (new ChestTile(TILE_CHEST, TEXTURE_CHEST_ONE_SIDE)) ++ ->init() ++ ->setDestroyTime(2.5f) ++ ->setSoundType(Tile::SOUND_WOOD) ++ ->setDescriptionId("chest"); ++ + // Great + Item::items[Tile::cloth->m_ID] = (new ClothItem(Tile::cloth->m_ID - C_MAX_TILES)) + ->setDescriptionId("cloth"); +@@ -963,12 +998,14 @@ void Tile::neighborChanged(Level* pLevel, const TilePos& pos, TileID tile) + + void Tile::onPlace(Level* pLevel, const TilePos& pos) + { +- ++ if (hasTileEntity()) ++ pLevel->setTileEntity(pos, newTileEntity()); + } + + void Tile::onRemove(Level* pLevel, const TilePos& pos) + { +- ++ if (hasTileEntity()) ++ pLevel->removeTileEntity(pos); + } + + bool Tile::containsX(const Vec3& v) +@@ -1279,4 +1316,8 @@ Tile + *Tile::web, + *Tile::fence, + *Tile::craftingTable, +- *Tile::crops; ++ *Tile::crops, ++ *Tile::musicBlock, ++ *Tile::furnace, ++ *Tile::furnaceLit, ++ *Tile::chest; +\ No newline at end of file +diff --git a/source/world/tile/Tile.hpp b/source/world/tile/Tile.hpp +index aad6af4c2..2a9bc0ad3 100644 +--- a/source/world/tile/Tile.hpp ++++ b/source/world/tile/Tile.hpp +@@ -29,6 +29,7 @@ class Entity; + class Mob; + class Player; + class LiquidTile; ++class TileEntity; + + class Tile + { +@@ -76,6 +77,7 @@ class Tile + virtual bool isSolidRender() const; + virtual bool mayPick() const; + virtual bool mayPick(TileData, bool) const; ++ virtual bool hasTileEntity() const; + virtual bool mayPlace(const Level*, const TilePos& pos) const; + virtual int getTickDelay() const; + virtual void tick(Level*, const TilePos& pos, Random*); +@@ -121,6 +123,7 @@ class Tile + virtual Tile* setDestroyTime(float); + virtual Tile* setTicking(bool); + virtual int getSpawnResourcesAuxValue(int) const; ++ virtual TileEntity* newTileEntity(); + Tile* setToolTypes(unsigned int toolMask); + Tile* setToolLevel(int toolLevel); + Tile* setToolTypesAndLevel(unsigned int toolMask, int toolLevel = 0); +@@ -241,7 +244,11 @@ class Tile + * web, + * fence, + * craftingTable, +- * crops; ++ * crops, ++ * furnace, ++ * furnaceLit, ++ * musicBlock, ++ * chest; + + public: + int m_TextureFrame; +diff --git a/source/world/tile/entity/ChestTileEntity.cpp b/source/world/tile/entity/ChestTileEntity.cpp +new file mode 100644 +index 000000000..f72f78a4c +--- /dev/null ++++ b/source/world/tile/entity/ChestTileEntity.cpp +@@ -0,0 +1,31 @@ ++#include "ChestTileEntity.hpp" ++ ++ChestTileEntity::ChestTileEntity() : SimpleContainer(27, "Chest") ++{ ++ m_pType = TileEntityType::chest; ++} ++ ++void ChestTileEntity::load(const CompoundTag& tag) ++{ ++ TileEntity::load(tag); ++ SimpleContainer::load(tag); ++} ++ ++void ChestTileEntity::save(CompoundTag& tag) const ++{ ++ TileEntity::save(tag); ++ SimpleContainer::save(tag); ++} ++ ++bool ChestTileEntity::stillValid(Player* player) const ++{ ++ if (m_pLevel->getTileEntity(m_pos) != this) ++ return false; ++ ++ return player->distanceToSqr(m_pos + 0.5f) <= 64.0f; ++} ++ ++void ChestTileEntity::setChanged() ++{ ++ TileEntity::setChanged(); ++} +diff --git a/source/world/tile/entity/ChestTileEntity.hpp b/source/world/tile/entity/ChestTileEntity.hpp +new file mode 100644 +index 000000000..38137f631 +--- /dev/null ++++ b/source/world/tile/entity/ChestTileEntity.hpp +@@ -0,0 +1,17 @@ ++#pragma once ++ ++#include "TileEntity.hpp" ++#include "world/level/Level.hpp" ++#include "world/inventory/SimpleContainer.hpp" ++ ++class ChestTileEntity : public TileEntity, public SimpleContainer { ++public: ++ ChestTileEntity(); ++ ++ void load(const CompoundTag& tag) override; ++ void save(CompoundTag& tag) const override; ++ ++ bool stillValid(Player* player) const override; ++ ++ virtual void setChanged() override; ++}; +diff --git a/source/world/tile/entity/FurnaceTileEntity.cpp b/source/world/tile/entity/FurnaceTileEntity.cpp +new file mode 100644 +index 000000000..1134d2736 +--- /dev/null ++++ b/source/world/tile/entity/FurnaceTileEntity.cpp +@@ -0,0 +1,163 @@ ++#include "FurnaceTileEntity.hpp" ++#include "world/item/crafting/FurnaceRecipes.hpp" ++#include "world/tile/FurnaceTile.hpp" ++ ++#define C_BURN_TIME (200) ++ ++FurnaceTileEntity::FurnaceTileEntity() ++ : SimpleContainer(3, "gui.furnace"), m_litTime(0), m_litDuration(0), m_tickCount(0) ++{ ++ m_pType = TileEntityType::furnace; ++} ++ ++bool FurnaceTileEntity::_canBurn() ++{ ++ if (getItem(0).isEmpty()) ++ return false; ++ ++ const ItemStack& result = FurnaceRecipes::singleton().getItemFor(this); ++ ++ // Not a furnace recipe ++ if (result.isEmpty()) ++ return false; ++ ++ // Nothing has started burning yet ++ if (getItem(2).isEmpty()) ++ return true; ++ ++ // Potential result/current result mismatch ++ if (getItem(2).getItem()->m_itemID != result.getItem()->m_itemID) ++ return false; ++ ++ if (getItem(2).m_count < getMaxStackSize() && getItem(2).m_count < getItem(2).getMaxStackSize()) ++ return true; ++ ++ // Return if result slot is full ++ return getItem(2).m_count < result.getMaxStackSize(); ++} ++ ++void FurnaceTileEntity::_burn() ++{ ++ if (!_canBurn()) ++ return; ++ ++ const ItemStack& result = FurnaceRecipes::singleton().getItemFor(this); ++ ++ // Either set the item in the result slot to the item, or add what's already there ++ if (getItem(2).isEmpty()) ++ setItem(2, result); ++ else if (getItem(2).getItem()->m_itemID == result.getItem()->m_itemID) ++ ++getItem(2).m_count; ++ ++ // Decrement burning item ++ if (getItem(0).m_count > 0) ++ --getItem(0).m_count; ++ ++ // No more burning item ++ if (getItem(0).m_count <= 0) ++ setItem(0, ItemStack::EMPTY); ++} ++ ++void FurnaceTileEntity::tick() ++{ ++ if (m_pLevel->m_bIsClientSide) ++ return; ++ ++ bool wasBurning = m_litTime > 0; ++ bool changed = false; ++ ++ if (m_litTime > 0) ++ --m_litTime; ++ ++ if (m_litTime == 0 && _canBurn()) ++ { ++ m_litDuration = m_litTime = FurnaceRecipes::singleton().getBurnDuration(getItem(1)); ++ if (m_litTime > 0) ++ { ++ changed = true; ++ if (!getItem(1).isEmpty()) ++ { ++ --getItem(1).m_count; ++ if (!getItem(1).m_count) ++ setItem(1, ItemStack::EMPTY); ++ } ++ } ++ } ++ ++ if (isLit() && _canBurn()) ++ { ++ ++m_tickCount; ++ if (m_tickCount == C_BURN_TIME) ++ { ++ m_tickCount = 0; ++ _burn(); ++ changed = true; ++ } ++ } ++ else ++ m_tickCount = 0; ++ ++ bool isBurning = m_litTime > 0; ++ if (isBurning != wasBurning) ++ { ++ changed = true; ++ FurnaceTile::SetLit(isBurning, m_pLevel, m_pos); ++ } ++ ++ if (changed) ++ setChanged(); ++} ++ ++bool FurnaceTileEntity::stillValid(Player* player) const ++{ ++ if (m_pLevel->getTileEntity(m_pos) != this) ++ return false; ++ ++ return player->distanceToSqr(m_pos + 0.5f) <= 64.0; ++} ++ ++void FurnaceTileEntity::setChanged() ++{ ++ TileEntity::setChanged(); ++} ++ ++void FurnaceTileEntity::load(const CompoundTag& tag) ++{ ++ TileEntity::load(tag); ++ SimpleContainer::load(tag); ++ ++ m_litTime = tag.getInt16("BurnTime"); ++ m_tickCount = tag.getInt16("CookTime"); ++ m_litDuration = FurnaceRecipes::singleton().getBurnDuration(getItem(1)); ++} ++ ++void FurnaceTileEntity::save(CompoundTag& tag) const ++{ ++ TileEntity::save(tag); ++ tag.putInt16("BurnTime", m_litTime); ++ tag.putInt16("CookTime", m_tickCount); ++ SimpleContainer::save(tag); ++} ++ ++std::string FurnaceTileEntity::getName() const ++{ ++ return "Furnace"; ++} ++ ++int FurnaceTileEntity::getBurnProgress(int height) ++{ ++ return m_tickCount * height / C_BURN_TIME; ++} ++ ++int FurnaceTileEntity::getLitProgress(int height) ++{ ++ if (m_litDuration == 0) ++ m_litDuration = C_BURN_TIME; ++ ++ return m_litTime * height / m_litDuration; ++} ++ ++bool FurnaceTileEntity::isLit() ++{ ++ return m_litTime > 0; ++} +\ No newline at end of file +diff --git a/source/world/tile/entity/FurnaceTileEntity.hpp b/source/world/tile/entity/FurnaceTileEntity.hpp +new file mode 100644 +index 000000000..55108282f +--- /dev/null ++++ b/source/world/tile/entity/FurnaceTileEntity.hpp +@@ -0,0 +1,33 @@ ++#pragma once ++ ++#include "TileEntity.hpp" ++#include "world/inventory/SimpleContainer.hpp" ++#include "world/level/Level.hpp" ++ ++class FurnaceTileEntity : public SimpleContainer, public TileEntity ++{ ++public: ++ FurnaceTileEntity(); ++ ++private: ++ bool _canBurn(); ++ void _burn(); ++ ++public: ++ void tick() override; ++ bool stillValid(Player* player) const override; ++ void setChanged() override; ++ void load(const CompoundTag& tag) override; ++ void save(CompoundTag& tag) const override; ++ std::string getName() const override; ++ ++public: ++ int getBurnProgress(int height); ++ int getLitProgress(int height); ++ bool isLit(); ++ ++public: ++ int m_litTime; ++ int m_litDuration; ++ int m_tickCount; ++}; +diff --git a/source/world/tile/entity/MusicTileEntity.cpp b/source/world/tile/entity/MusicTileEntity.cpp +new file mode 100644 +index 000000000..d38630bd7 +--- /dev/null ++++ b/source/world/tile/entity/MusicTileEntity.cpp +@@ -0,0 +1,46 @@ ++#include "MusicTileEntity.hpp" ++#include "world/level/Level.hpp" ++ ++MusicTileEntity::MusicTileEntity() : TileEntity(), m_note(0), m_bOn(false) ++{ ++ m_pType = TileEntityType::noteblock; ++} ++ ++void MusicTileEntity::load(const CompoundTag& tag) ++{ ++ TileEntity::load(tag); ++ m_note = static_cast(Mth::clamp(tag.getInt8("note"), 0, 24)); ++} ++ ++void MusicTileEntity::save(CompoundTag& tag) const ++{ ++ TileEntity::save(tag); ++ tag.putInt8("note", m_note); ++} ++ ++void MusicTileEntity::tune() ++{ ++ m_note = (m_note + 1) % 25; ++ setChanged(); ++} ++ ++void MusicTileEntity::play(Level* pLevel, const TilePos& pos) ++{ ++ // noteblocks only play if the block above them is air ++ if (pLevel->getMaterial(TilePos(pos.x, pos.y + 1, pos.z)) != Material::air) ++ return; ++ ++ int instrument = 0; ++ Material* below = pLevel->getMaterial(TilePos(pos.x, pos.y - 1, pos.z)); ++ ++ if (below == Material::stone) ++ instrument = 1; ++ else if (below == Material::sand) ++ instrument = 2; ++ else if (below == Material::glass) ++ instrument = 3; ++ else if (below == Material::wood) ++ instrument = 4; ++ ++ pLevel->tileEvent(TileEvent(pos, instrument, m_note)); ++} +\ No newline at end of file +diff --git a/source/world/tile/entity/MusicTileEntity.hpp b/source/world/tile/entity/MusicTileEntity.hpp +new file mode 100644 +index 000000000..9a8a1a60a +--- /dev/null ++++ b/source/world/tile/entity/MusicTileEntity.hpp +@@ -0,0 +1,21 @@ ++#pragma once ++ ++#include "TileEntity.hpp" ++ ++class MusicTileEntity : public TileEntity ++{ ++public: ++ MusicTileEntity(); ++ ++public: ++ void load(const CompoundTag& tag) override; ++ void save(CompoundTag& tag) const override; ++ ++public: ++ void tune(); ++ void play(Level* pLevel, const TilePos& pos); ++ ++public: ++ uint8_t m_note; ++ bool m_bOn; ++}; +\ No newline at end of file +diff --git a/source/world/tile/entity/TileEntity.cpp b/source/world/tile/entity/TileEntity.cpp +new file mode 100644 +index 000000000..9cd7b79ad +--- /dev/null ++++ b/source/world/tile/entity/TileEntity.cpp +@@ -0,0 +1,106 @@ ++#include "TileEntity.hpp" ++#include "common/Logger.hpp" ++#include "world/level/Level.hpp" ++ ++TileEntity::TileEntity() : m_bRemove(false), m_pLevel(nullptr) ++{ ++} ++ ++TileEntity::~TileEntity() ++{ ++} ++ ++TileEntity* TileEntity::LoadTileEntity(const CompoundTag& tag) ++{ ++ if (!tag.contains("id")) ++ { ++ LOG_W("Skipping TileEntity with no id!"); ++ return nullptr; ++ } ++ ++ std::string id = tag.getString("id"); ++ const TileEntityType* type = TileEntityType::GetType(id); ++ ++ if (!type) ++ { ++ LOG_I("Skipping TileEntity with id %s", id.c_str()); ++ return nullptr; ++ } ++ ++ TileEntity* newEnt = type->newTileEntity(); ++ newEnt->load(tag); ++ return newEnt; ++} ++ ++void TileEntity::load(const CompoundTag& tag) ++{ ++ m_pos.x = tag.getInt32("x"); ++ m_pos.y = tag.getInt32("y"); ++ m_pos.z = tag.getInt32("z"); ++} ++ ++void TileEntity::save(CompoundTag& tag) const ++{ ++ tag.putString("id", getId()); ++ tag.putInt32("x", m_pos.x); ++ tag.putInt32("y", m_pos.y); ++ tag.putInt32("z", m_pos.z); ++} ++ ++void TileEntity::tick() ++{ ++} ++ ++Packet* TileEntity::getUpdatePacket() ++{ ++ return nullptr; ++} ++ ++bool TileEntity::isRemoved() const ++{ ++ return m_bRemove; ++} ++ ++void TileEntity::setRemoved() ++{ ++ m_bRemove = true; ++} ++ ++void TileEntity::clearRemoved() ++{ ++ m_bRemove = false; ++} ++ ++int TileEntity::getData() const ++{ ++ return m_pLevel->getData(m_pos); ++} ++ ++void TileEntity::setData(int data) ++{ ++ m_pLevel->setData(m_pos, data); ++} ++ ++void TileEntity::setChanged() ++{ ++ // @TODO: tileEntityChanged ++ /* ++ if (m_pLevel) ++ m_pLevel->tileEntityChanged(m_pos, this); ++ */ ++} ++ ++float TileEntity::distanceToSqr(const Vec3& vec) const ++{ ++ return (m_pos + 0.5f).distanceToSqr(vec); ++} ++ ++Tile* TileEntity::getTile() const ++{ ++ return Tile::tiles[m_pLevel->getTile(m_pos)]; ++} ++ ++std::string TileEntity::getId() const ++{ ++ return (m_pType) ? m_pType->getName() : "Unknown"; ++} +\ No newline at end of file +diff --git a/source/world/tile/entity/TileEntity.hpp b/source/world/tile/entity/TileEntity.hpp +new file mode 100644 +index 000000000..2a3666fc7 +--- /dev/null ++++ b/source/world/tile/entity/TileEntity.hpp +@@ -0,0 +1,50 @@ ++#pragma once ++ ++#include "TileEntityType.hpp" ++#include "world/level/TilePos.hpp" ++#include "world/phys/Vec3.hpp" ++#include "nbt/CompoundTag.hpp" ++ ++class Tile; ++class Level; ++class Packet; ++ ++class TileEntity ++{ ++public: ++ TileEntity(); ++ virtual ~TileEntity(); ++ ++protected: ++ virtual std::string getId() const; ++ ++public: ++ virtual void load(const CompoundTag& tag); ++ virtual void save(CompoundTag& tag) const; ++ virtual void tick(); ++ virtual Packet* getUpdatePacket(); ++ const TileEntityType* getType() const; ++ virtual int getData() const; ++ virtual Tile* getTile() const; ++ ++public: ++ static TileEntity* LoadTileEntity(const CompoundTag& tag); ++ ++public: ++ float distanceToSqr(const Vec3& vec) const; ++ bool isRemoved() const; ++ void setRemoved(); ++ void clearRemoved(); ++ ++ void setData(int data); ++ void setChanged(); ++ ++protected: ++ const TileEntityType* m_pType; ++ bool m_bRemove; ++ ++public: ++ Level* m_pLevel; ++ TilePos m_pos; ++}; ++ +diff --git a/source/world/tile/entity/TileEntityType.cpp b/source/world/tile/entity/TileEntityType.cpp +new file mode 100644 +index 000000000..8ee14b727 +--- /dev/null ++++ b/source/world/tile/entity/TileEntityType.cpp +@@ -0,0 +1,51 @@ ++#include "TileEntityType.hpp" ++#include "FurnaceTileEntity.hpp" ++#include "ChestTileEntity.hpp" ++#include "MusicTileEntity.hpp" ++// #include "MobSpawnerTileEntity.hpp" ++// #include "DispenserTileEntity.hpp" ++// #include "SignTileEntity.hpp" ++// #include "RecordPlayerTileEntity.hpp" ++// #include "PistonMovingTileEntity.hpp" ++ ++std::map TileEntityType::_types; ++ ++TileEntityType* TileEntityType::furnace; ++TileEntityType* TileEntityType::chest; ++TileEntityType* TileEntityType::noteblock; ++ ++TileEntityType::TileEntityType(const std::string& name, CreateFunction func) : _name(name), _function(func) ++{ ++} ++ ++const std::string& TileEntityType::getName() const ++{ ++ return _name; ++} ++ ++TileEntity* TileEntityType::newTileEntity() const ++{ ++ return _function(); ++} ++ ++void TileEntityType::InitTileEntities() ++{ ++ furnace = RegisterTileEntity("Furnace"); ++ chest = RegisterTileEntity("Chest"); ++ noteblock = RegisterTileEntity("Music"); ++} ++ ++const TileEntityType* TileEntityType::GetType(const std::string& name) ++{ ++ return _types[name]; ++} ++ ++void TileEntityType::TeardownTileEntities() ++{ ++ // delete all heap allocated tile entity types (furnace, chest, etc.) ++ for (std::map::const_iterator it = _types.begin(); it != _types.end(); ++it) ++ { ++ SAFE_DELETE(it->second); ++ } ++ _types.clear(); ++} +\ No newline at end of file +diff --git a/source/world/tile/entity/TileEntityType.hpp b/source/world/tile/entity/TileEntityType.hpp +new file mode 100644 +index 000000000..3a981cf6c +--- /dev/null ++++ b/source/world/tile/entity/TileEntityType.hpp +@@ -0,0 +1,50 @@ ++#pragma once ++ ++#include ++#include ++#include ++ ++class Level; ++class TileEntity; ++ ++class TileEntityType ++{ ++public: ++ typedef TileEntity* (*CreateFunction)(); ++ ++public: ++ TileEntityType(const std::string& name, CreateFunction func); ++ ++public: ++ const std::string& getName() const; ++ TileEntity* newTileEntity() const; ++ ++public: ++ static void InitTileEntities(); ++ static const TileEntityType* GetType(const std::string& name); ++ static void TeardownTileEntities(); ++ ++private: ++ static std::map _types; ++ ++private: ++ std::string _name; ++ CreateFunction _function; ++ ++public: ++ static TileEntityType* furnace; ++ static TileEntityType* chest; ++ static TileEntityType* noteblock; ++ ++ template ++ static TileEntity* CreateType() { return new T(); } ++ ++public: ++ template ++ static TileEntityType* RegisterTileEntity(const std::string& name) ++ { ++ TileEntityType* type = new TileEntityType(name, &TileEntityType::CreateType); ++ _types[type->_name] = type; ++ return type; ++ } ++}; +\ No newline at end of file + +From 3054cbd410c68aa6f1ea5ce8c885caf3657b998b Mon Sep 17 00:00:00 2001 +From: hellosammu +Date: Sun, 15 Feb 2026 17:59:03 -0500 +Subject: [PATCH 02/22] furnace rot fix, useTouchscreen/isTouchscreen + +--- + source/client/app/Minecraft.cpp | 21 ++++++----- + source/client/app/Minecraft.hpp | 6 ++-- + source/client/app/NinecraftApp.cpp | 2 +- + source/client/gui/Gui.cpp | 17 +++++---- + source/client/gui/Gui.hpp | 1 + + source/client/gui/Screen.cpp | 2 +- + source/client/gui/components/OptionList.cpp | 3 +- + source/client/gui/components/TextBox.cpp | 19 ++++++---- + .../gui/screens/inventory/ContainerScreen.cpp | 6 ++-- + source/client/model/models/SpiderModel.cpp | 6 ++-- + source/client/options/Options.cpp | 16 +++++++-- + source/client/options/Options.hpp | 14 ++++++-- + source/client/player/input/Keyboard.cpp | 3 +- + source/client/renderer/Chunk.hpp | 2 ++ + source/client/renderer/Font.cpp | 6 ++-- + source/client/renderer/GameRenderer.cpp | 2 +- + source/client/renderer/LevelRenderer.cpp | 3 +- + source/client/renderer/LogoRenderer.cpp | 6 ++-- + source/client/renderer/TileRenderer.cpp | 6 ++-- + .../renderer/entity/HumanoidMobRenderer.cpp | 6 ++++ + .../renderer/entity/HumanoidMobRenderer.hpp | 1 + + .../client/renderer/entity/ItemRenderer.cpp | 6 ++-- + .../client/renderer/entity/SheepRenderer.cpp | 2 +- + source/client/sound/SoundData.cpp | 6 ++-- + source/client/sound/SoundEngine.hpp | 1 + + source/common/Logger.cpp | 5 ++- + source/common/Random.cpp | 9 +++-- + source/network/packets/AddMobPacket.hpp | 2 ++ + .../network/packets/SetEntityDataPacket.hpp | 2 ++ + source/renderer/GL/GL.cpp | 24 ++++++++----- + source/world/entity/Creeper.cpp | 3 +- + source/world/entity/MobSpawner.hpp | 2 +- + source/world/entity/Player.cpp | 12 ++++--- + source/world/entity/Player.hpp | 2 +- + source/world/inventory/ContainerMenu.cpp | 12 ++++--- + source/world/item/BowItem.cpp | 3 +- + source/world/item/ItemStack.hpp | 1 + + .../levelgen/chunk/RandomLevelSource.cpp | 6 ++-- + .../level/levelgen/chunk/TestChunkSource.cpp | 6 ++-- + source/world/level/path/BinaryHeap.cpp | 30 ++++++++-------- + source/world/level/path/BinaryHeap.hpp | 12 ++++--- + source/world/tile/ChestTile.cpp | 35 +++++++++++-------- + source/world/tile/ChestTile.hpp | 2 ++ + source/world/tile/CraftingTableTile.cpp | 3 +- + source/world/tile/FurnaceTile.cpp | 23 ++++++------ + source/world/tile/MusicTile.cpp | 2 +- + source/world/tile/PumpkinTile.cpp | 6 ++-- + 47 files changed, 231 insertions(+), 134 deletions(-) + +diff --git a/source/client/app/Minecraft.cpp b/source/client/app/Minecraft.cpp +index cd3f8986a..749a6a493 100644 +--- a/source/client/app/Minecraft.cpp ++++ b/source/client/app/Minecraft.cpp +@@ -173,12 +173,12 @@ GameMode* Minecraft::_createGameMode(GameType gameType, Level& level) + } + } + +-void Minecraft::_reloadInput() ++void Minecraft::reloadInput() + { + if (m_pInputHolder) + delete m_pInputHolder; + +- if (isTouchscreen()) ++ if (useTouchscreen()) + { + m_pInputHolder = new TouchInputHolder(this, getOptions()); + } +@@ -206,7 +206,7 @@ void Minecraft::_reloadInput() + m_pLocalPlayer->m_pMoveInput = m_pInputHolder->getMoveInput(); + } + +- getOptions()->field_19 = !isTouchscreen(); ++ getOptions()->m_bUseMouseToBreak = !useTouchscreen(); + } + + int Minecraft::getLicenseId() +@@ -246,7 +246,7 @@ void Minecraft::grabMouse() + // This will call grabMouse again, so why are we calling it here? + //setScreen(nullptr); + +- if (useController() || isTouchscreen()) ++ if (useController() || useTouchscreen()) + return; // don't actually try to grab the mouse + + platform()->setMouseGrabbed(true); +@@ -254,7 +254,7 @@ void Minecraft::grabMouse() + + void Minecraft::recenterMouse() + { +- if (useController() || isTouchscreen()) ++ if (useController() || useTouchscreen()) + return; + + platform()->recenterMouse(); +@@ -364,9 +364,14 @@ bool Minecraft::isTouchscreen() const + return m_bIsTouchscreen; + } + ++bool Minecraft::useTouchscreen() const ++{ ++ return isTouchscreen() && !getOptions()->m_bUseController.get(); ++} ++ + bool Minecraft::useSplitControls() const + { +- return !m_bIsTouchscreen || getOptions()->m_splitControls.get(); ++ return !useTouchscreen() || getOptions()->m_splitControls.get(); + } + + bool Minecraft::useController() const +@@ -625,7 +630,7 @@ void Minecraft::tickInput() + #endif + } + +- if (getOptions()->field_19) ++ if (!getOptions()->m_bUseMouseToBreak) + continue; + + // @TODO: Replace with KeyboardBuildInput +@@ -672,7 +677,7 @@ void Minecraft::tickMouse() + * This is exactly what Minecraft Java does too + **/ + +- if (useController() || isTouchscreen()) ++ if (useController() || useTouchscreen()) + return; // don't actually try to recenter the mouse + + if (platform()->getRecenterMouseEveryTick()) // just for SDL1 +diff --git a/source/client/app/Minecraft.hpp b/source/client/app/Minecraft.hpp +index edd594852..6a112aaad 100644 +--- a/source/client/app/Minecraft.hpp ++++ b/source/client/app/Minecraft.hpp +@@ -44,9 +44,6 @@ class Minecraft : public App + void _resetPlayer(Player* player); + GameMode* _createGameMode(GameType gameType, Level& level); + +-protected: +- void _reloadInput(); +- + public: + int getLicenseId(); + void setScreen(Screen * pScreen); +@@ -79,11 +76,13 @@ class Minecraft : public App + void handlePointerPressedButtonRelease(); + void handleKeyboardClosed(); + void resetInput(); ++ void reloadInput(); + void sendMessage(const std::string& message); + void respawnPlayer(); + void freeResources(bool bCopyMap); + std::string getVersionString(const std::string& str = Util::EMPTY_STRING) const; + bool isTouchscreen() const; ++ bool useTouchscreen() const; + bool useSplitControls() const; + bool useController() const; + +@@ -118,6 +117,7 @@ class Minecraft : public App + private: + // Value provided by the OS + static float _renderScaleMultiplier; ++ + public: + static float getRenderScaleMultiplier() { return _renderScaleMultiplier; } + static void setRenderScaleMultiplier(float value) { _renderScaleMultiplier = value; } +diff --git a/source/client/app/NinecraftApp.cpp b/source/client/app/NinecraftApp.cpp +index b805ddcd1..0a16d0d3d 100644 +--- a/source/client/app/NinecraftApp.cpp ++++ b/source/client/app/NinecraftApp.cpp +@@ -105,7 +105,7 @@ void NinecraftApp::_initInput() + m_bIsTouchscreen = platform()->isTouchscreen(); + getOptions()->m_bUseController.set(platform()->hasGamepad()); + getOptions()->loadControls(); +- _reloadInput(); ++ reloadInput(); + } + + void NinecraftApp::_updateStats() +diff --git a/source/client/gui/Gui.cpp b/source/client/gui/Gui.cpp +index d8e94fcdf..5e4784d4f 100644 +--- a/source/client/gui/Gui.cpp ++++ b/source/client/gui/Gui.cpp +@@ -259,8 +259,6 @@ void Gui::renderSlot(int slot, int x, int y, float f) + + ItemRenderer::singleton().renderGuiItem(m_pMinecraft->m_pFont, m_pMinecraft->m_pTextures, item, x, y, true); + } +- +- //ItemRenderer::renderGuiItemDecorations(m_pMinecraft->m_pFont, m_pMinecraft->m_pTextures, item, x, y); + } + + void Gui::renderSlotOverlay(int slot, int x, int y, float f) +@@ -311,7 +309,8 @@ void Gui::handleClick(int clickID, int mouseX, int mouseY) + if (slot == -1) + return; + +- if (m_pMinecraft->isTouchscreen() && slot == getNumSlots() - 1) ++ // Final slot on touch opens inventory ++ if (m_pMinecraft->useTouchscreen() && slot == getNumSlots() - 1) + { + if (m_pMinecraft->m_pGameMode->isSurvivalType()) + m_pMinecraft->setScreen(new InventoryScreen(m_pMinecraft->m_pLocalPlayer)); +@@ -360,7 +359,7 @@ void Gui::handleKeyPressed(int keyCode) + if (slotL || slotR) + { + int maxItems = getNumSlots() - 1; +- if (m_pMinecraft->isTouchscreen()) ++ if (m_pMinecraft->useTouchscreen()) + maxItems--; + SlotID* slot = &m_pMinecraft->m_pLocalPlayer->m_pInventory->m_selectedSlot; + +@@ -672,7 +671,7 @@ void Gui::renderToolBar(float f, float alpha) + + textures->loadAndBindTexture(C_BLOCKS_NAME); + +- int diff = mc->isTouchscreen(); ++ int diff = mc->useTouchscreen(); + + int slotX = -hotbarWidth / 2 + 3; + for (int i = 0; i < nSlots - diff; i++) +@@ -694,8 +693,8 @@ void Gui::renderToolBar(float f, float alpha) + + field_A3C = false; + +- // blit the "more items" button +- if (mc->isTouchscreen()) ++ // blit the "more items" button if using touch ++ if (mc->useTouchscreen()) + { + textures->loadAndBindTexture(C_TERRAIN_NAME); + blit(hotbarWidth / 2 - 19, -19, 208, 208, 16, 16, 0, 0); +@@ -704,7 +703,7 @@ void Gui::renderToolBar(float f, float alpha) + + int Gui::getNumSlots() + { +- if (m_pMinecraft->isTouchscreen()) ++ if (m_pMinecraft->useTouchscreen()) + return 6; + + return 9; +@@ -712,7 +711,7 @@ int Gui::getNumSlots() + + int Gui::getNumUsableSlots() + { +- return getNumSlots() - m_pMinecraft->isTouchscreen(); ++ return getNumSlots() - m_pMinecraft->useTouchscreen(); + } + + RectangleArea Gui::getRectangleArea(bool b) +diff --git a/source/client/gui/Gui.hpp b/source/client/gui/Gui.hpp +index 9957d2f65..156f13788 100644 +--- a/source/client/gui/Gui.hpp ++++ b/source/client/gui/Gui.hpp +@@ -42,6 +42,7 @@ class Gui : public GuiComponent + + private: + static bool _isVignetteAvailable; ++ + public: + static bool isVignetteAvailable() { return _isVignetteAvailable; } + static void setIsVignetteAvailable(bool value) { _isVignetteAvailable = value; } +diff --git a/source/client/gui/Screen.cpp b/source/client/gui/Screen.cpp +index 230a85921..7aec2e65e 100644 +--- a/source/client/gui/Screen.cpp ++++ b/source/client/gui/Screen.cpp +@@ -393,7 +393,7 @@ void Screen::pointerPressed(const MenuPointer& pointer, MouseButtonType btn) // + { + m_pClickedElement = element; + +- if (!m_pMinecraft->isTouchscreen()) ++ if (!m_pMinecraft->useTouchscreen()) + { + if (_useController()) + m_pMinecraft->m_pSoundEngine->playUI(C_SOUND_UI_PRESS); +diff --git a/source/client/gui/components/OptionList.cpp b/source/client/gui/components/OptionList.cpp +index 062f32f5e..26e7afa1f 100644 +--- a/source/client/gui/components/OptionList.cpp ++++ b/source/client/gui/components/OptionList.cpp +@@ -173,7 +173,8 @@ void OptionList::initControlsMenu() + + if (!m_pMinecraft->isTouchscreen()) + m_items[idxSplit]->setEnabled(false); +- m_items[idxController]->setEnabled(false); ++ if (!m_pMinecraft->m_pPlatform->hasGamepad()) ++ m_items[idxController]->setEnabled(false); + } + + void OptionList::initVideoMenu() +diff --git a/source/client/gui/components/TextBox.cpp b/source/client/gui/components/TextBox.cpp +index f18371498..36d64344d 100644 +--- a/source/client/gui/components/TextBox.cpp ++++ b/source/client/gui/components/TextBox.cpp +@@ -149,7 +149,8 @@ bool TextBox::pointerPressed(Minecraft* pMinecraft, const MenuPointer& pointer) + + #ifndef HANDLE_CHARS_SEPARATELY + +-char TextBox::guessCharFromKey(int key) { ++char TextBox::guessCharFromKey(int key) ++{ + bool bShiftPressed = m_pParent->m_pMinecraft->platform()->shiftPressed(); + char chr = '\0'; + if (key >= AKEYCODE_A && key <= AKEYCODE_Z) +@@ -218,13 +219,15 @@ void TextBox::handleButtonPress(Minecraft* pMinecraft, int key) + + #ifndef HANDLE_CHARS_SEPARATELY + char guess = guessCharFromKey(key); +- if (guess != '\0') { ++ if (guess != '\0') ++ { + handleTextChar(guess); + return; + } + #endif + +- switch (key) { ++ switch (key) ++ { + case AKEYCODE_DEL: + { + // handled elsewhere, do not dupe +@@ -298,7 +301,8 @@ void TextBox::handleTextChar(Minecraft* pMinecraft, int k) + if (!hasFocus()) + return; + +- switch (k) { ++ switch (k) ++ { + case '\b': // BACKSPACE + case '\x7f': // DELETE + { +@@ -546,10 +550,13 @@ void TextBox::recalculateScroll() + } + else + { +- if (m_scrollPos == int(m_text.length())) { ++ if (m_scrollPos == int(m_text.length())) ++ { + LOG_W("Text Box Is Too Small"); + break; +- } else { ++ } ++ else ++ { + m_scrollPos++; + } + } +diff --git a/source/client/gui/screens/inventory/ContainerScreen.cpp b/source/client/gui/screens/inventory/ContainerScreen.cpp +index 991e74357..98d0b447d 100644 +--- a/source/client/gui/screens/inventory/ContainerScreen.cpp ++++ b/source/client/gui/screens/inventory/ContainerScreen.cpp +@@ -234,14 +234,14 @@ void ContainerScreen::render(float partialTicks) + void ContainerScreen::pointerPressed(const MenuPointer& pointer, MouseButtonType button) + { + Screen::pointerPressed(pointer, button); +- if (m_pMinecraft->isTouchscreen()) return; ++ if (m_pMinecraft->useTouchscreen()) return; + slotClicked(pointer, button); + } + + void ContainerScreen::pointerReleased(const MenuPointer& pointer, MouseButtonType button) + { + Screen::pointerReleased(pointer, button); +- if (m_pMinecraft->isTouchscreen() && m_timeSlotDragged < 5) ++ if (m_pMinecraft->useTouchscreen() && m_timeSlotDragged < 5) + slotClicked(pointer, button); + m_timeSlotDragged = 0; + } +@@ -253,7 +253,7 @@ void ContainerScreen::handlePointerPressed(bool isPressed) + m_timeSlotDragged++; + else m_timeSlotDragged = 0; + +- if (m_pMinecraft->isTouchscreen() && m_timeSlotDragged % 5 == 0) ++ if (m_pMinecraft->useTouchscreen() && m_timeSlotDragged % 5 == 0) + { + slotClicked(m_menuPointer, MOUSE_BUTTON_RIGHT); + } +diff --git a/source/client/model/models/SpiderModel.cpp b/source/client/model/models/SpiderModel.cpp +index a3293ecb4..677798049 100644 +--- a/source/client/model/models/SpiderModel.cpp ++++ b/source/client/model/models/SpiderModel.cpp +@@ -57,7 +57,8 @@ SpiderModel::~SpiderModel() + { + } + +-void SpiderModel::render(float time, float r, float bob, float yRot, float xRot, float scale) { ++void SpiderModel::render(float time, float r, float bob, float yRot, float xRot, float scale) ++{ + setupAnim(time, r, bob, yRot, xRot, scale); + m_head.render(scale); + +@@ -74,7 +75,8 @@ void SpiderModel::render(float time, float r, float bob, float yRot, float xRot, + m_leg7.render(scale); + } + +-void SpiderModel::setupAnim(float time, float r, float bob, float yRot, float xRot, float scale) { ++void SpiderModel::setupAnim(float time, float r, float bob, float yRot, float xRot, float scale) ++{ + m_head.m_rot.y = yRot / 57.295776f; + m_head.m_rot.x = xRot / 57.295776f; + +diff --git a/source/client/options/Options.cpp b/source/client/options/Options.cpp +index 6cbc4bead..546f53a58 100644 +--- a/source/client/options/Options.cpp ++++ b/source/client/options/Options.cpp +@@ -41,11 +41,11 @@ void Options::_initDefaultValues() + field_16 = 0; + field_240 = 1; + field_1C = "Default"; +- field_19 = 1; ++ m_bUseMouseToBreak = true; + #ifdef ORIGINAL_CODE + m_viewDistance.set(2); + m_thirdPerson.set(0); +- field_19 = 0; ++ m_bUseMouseToBreak = false; + #endif + + // Force this on until we get a proper UI +@@ -59,7 +59,7 @@ static UITheme GetDefaultUiTheme(Minecraft* mc) + #if MC_PLATFORM_XBOX360 + return UI_CONSOLE; + #else +- return mc->isTouchscreen() ? UI_POCKET : UI_JAVA; ++ return (mc->useTouchscreen() || mc->isTouchscreen()) ? UI_POCKET : UI_JAVA; + #endif + } + +@@ -126,6 +126,7 @@ Options::Options(Minecraft* mc, const std::string& folderPath) : + add(m_playerName); + add(m_debugText); + add(m_lang); ++ add(m_bUseController); + add(m_uiTheme); + add(m_logoType); + add(m_hudSize); +@@ -803,3 +804,12 @@ void UIThemeOption::apply() + m_pMinecraft->getOptions()->m_logoType.apply(); + } + } ++ ++void ControllerOption::apply() ++{ ++ // @TODO: This works but ultimately needs to be a multi-select (KBM, controller, touch) instead of a single option. ++ // Either that or figure out an automatic way to switch between them based on input like how Minecraft Bedrock does ++ // For now, I just wanted to be able to switch to controller input on mobile devices. ++ if (m_pMinecraft && m_pMinecraft->m_pInputHolder) ++ m_pMinecraft->reloadInput(); ++} +\ No newline at end of file +diff --git a/source/client/options/Options.hpp b/source/client/options/Options.hpp +index 99bacdf1a..1fe458dae 100644 +--- a/source/client/options/Options.hpp ++++ b/source/client/options/Options.hpp +@@ -214,6 +214,14 @@ class GraphicsOption : public BoolOption + void apply() override; + }; + ++class ControllerOption : public BoolOption ++{ ++public: ++ ControllerOption(const std::string& key, const std::string& name, bool initial = true) : BoolOption(key, name, initial) {} ++ ++ void apply() override; ++}; ++ + class FancyGraphicsOption : public GraphicsOption + { + public: +@@ -329,6 +337,7 @@ class Options + { + public: + struct KeyBind; ++ + private: + static bool _hasResourcePack(const ResourcePack& pack, ResourcePackStack& packs); + static void _tryAddResourcePack(const std::string& name, ResourcePackStack& packs); +@@ -358,6 +367,7 @@ class Options + void _initDefaultValues(); + void _load(); + AsyncTask _saveAsync(); ++ + public: + Options(Minecraft*, const std::string& folderPath = ""); + +@@ -399,7 +409,7 @@ class Options + uint8_t field_16; + FancyGraphicsOption m_fancyGraphics; + AOOption m_ambientOcclusion; +- uint8_t field_19; // use Mouse as input for breaking ++ bool m_bUseMouseToBreak; + std::string field_1C; + ValuesOption m_difficulty; + BoolOption m_hideGui; +@@ -419,7 +429,7 @@ class Options + GraphicsOption m_fancyGrass; + GraphicsOption m_biomeColors; + BoolOption m_splitControls; +- BoolOption m_bUseController; ++ ControllerOption m_bUseController; + BoolOption m_dynamicHand; + BoolOption m_menuPanorama; + GuiScaleOption m_guiScale; +diff --git a/source/client/player/input/Keyboard.cpp b/source/client/player/input/Keyboard.cpp +index 562003650..0f82cc3e8 100644 +--- a/source/client/player/input/Keyboard.cpp ++++ b/source/client/player/input/Keyboard.cpp +@@ -18,9 +18,8 @@ Keyboard::KeyState Keyboard::_states[KEYBOARD_STATES_SIZE]; + void Keyboard::feed(KeyState state, int key) + { + // Prevent Crashes +- if (key >= KEYBOARD_STATES_SIZE || key < 0) { ++ if (key >= KEYBOARD_STATES_SIZE || key < 0) + return; +- } + + _inputs.push_back(KeyboardAction(key, state)); + +diff --git a/source/client/renderer/Chunk.hpp b/source/client/renderer/Chunk.hpp +index 957ef0446..aea9643c1 100644 +--- a/source/client/renderer/Chunk.hpp ++++ b/source/client/renderer/Chunk.hpp +@@ -59,8 +59,10 @@ class Chunk + bool field_54; + RenderChunk m_renderChunks[Tile::RENDER_LAYERS_COUNT]; + Tesselator* m_pTesselator; ++ + private: + int m_lists; ++ + public: + bool m_bCompiled; + bool m_bDirty; +diff --git a/source/client/renderer/Font.cpp b/source/client/renderer/Font.cpp +index 6ec81196f..e66f98b6e 100644 +--- a/source/client/renderer/Font.cpp ++++ b/source/client/renderer/Font.cpp +@@ -138,7 +138,8 @@ void Font::drawOutlinedString(const std::string& str, int x, int y, const Color& + for (int yi = 0; yi < 3; ++yi) + { + int t1 = translations[yi]; +- if (t != 0 || t1 != 0) { ++ if (t != 0 || t1 != 0) ++ { + MatrixStack::Ref matrix = MatrixStack::World.push(); + matrix->translate(Vec3(t, t1, 0)); + drawScalable(str, x, y, outlineColor, scale, false); +@@ -290,7 +291,8 @@ std::vector Font::split(const std::string& text, int maxWidth) + std::istringstream iss(paragraph); + std::string word; + +- while (iss >> word) { ++ while (iss >> word) ++ { + std::string testLine = currentLine.empty() ? word : currentLine + " " + word; + + if (width(testLine) <= maxWidth) +diff --git a/source/client/renderer/GameRenderer.cpp b/source/client/renderer/GameRenderer.cpp +index d71fd3d4e..c265d6b5e 100644 +--- a/source/client/renderer/GameRenderer.cpp ++++ b/source/client/renderer/GameRenderer.cpp +@@ -643,7 +643,7 @@ void GameRenderer::render(const Timer& timer) + int mouseY = -9999; + bool bMouseData = false; + +- if (m_pMinecraft->isTouchscreen()) ++ if (m_pMinecraft->useTouchscreen()) + { + int pointerId = Multitouch::getFirstActivePointerIdExThisUpdate(); + if (pointerId >= 0) +diff --git a/source/client/renderer/LevelRenderer.cpp b/source/client/renderer/LevelRenderer.cpp +index 98a311c52..f9c47bc95 100644 +--- a/source/client/renderer/LevelRenderer.cpp ++++ b/source/client/renderer/LevelRenderer.cpp +@@ -1163,7 +1163,8 @@ bool LevelRenderer::updateDirtyChunks(const Entity& camera, bool b) + if (--j <= 0) + continue; + +- for (int k = j; --k != 0;) { ++ for (int k = j; --k != 0;) ++ { + pChunks[k - 1] = pChunks[k]; + } + +diff --git a/source/client/renderer/LogoRenderer.cpp b/source/client/renderer/LogoRenderer.cpp +index c5dac8dc3..e9cdbbfeb 100644 +--- a/source/client/renderer/LogoRenderer.cpp ++++ b/source/client/renderer/LogoRenderer.cpp +@@ -414,8 +414,10 @@ Tile* TitleTile::getRandomTile(Tile* except1, Tile* except2) + for (;;) + { + id = _random.nextInt(256); +- for (int i = 0; i < _tileBlockListSize; i++) { +- if (_tileBlockList[i] == id) { ++ for (int i = 0; i < _tileBlockListSize; i++) ++ { ++ if (_tileBlockList[i] == id) ++ { + // N.B. Air does not have a tile + id = TILE_AIR; + break; +diff --git a/source/client/renderer/TileRenderer.cpp b/source/client/renderer/TileRenderer.cpp +index 575742689..ed07fc37b 100644 +--- a/source/client/renderer/TileRenderer.cpp ++++ b/source/client/renderer/TileRenderer.cpp +@@ -1168,7 +1168,8 @@ bool TileRenderer::tesselateFenceInWorld(Tile* tile, const TilePos& pos) + bool connectsHorizontally = tileWest || tileEast; + bool connectsVertically = tileNorth || tileSouth; + +- if (!connectsHorizontally && !connectsVertically) { ++ if (!connectsHorizontally && !connectsVertically) ++ { + connectsHorizontally = true; + } + +@@ -2793,7 +2794,8 @@ void TileRenderer::renderTile(const FullTile& tile, const mce::MaterialPtr& mate + float v6 = v5 * 2.0f; + for (int i = 0; i < 4; i++) + { +- switch (i) { ++ switch (i) ++ { + case 0: tileType->setShape(0.5f - v6, 0.0f, 0.0f, 0.5f + v6, 1.0f, v6 * 2.0f); break; + case 1: tileType->setShape(0.5f - v6, 0.0f, 1.0f - (v6 * 2.0f), 0.5f + v6, 1.0f, 1.0f); break; + case 2: tileType->setShape(0.5f - v5, 1.0f - v5 * 3.0f, -v5 * 2.0f, 0.5f + v5, 1.0f - v5, 1.0f + v5 * 2.0f); break; +diff --git a/source/client/renderer/entity/HumanoidMobRenderer.cpp b/source/client/renderer/entity/HumanoidMobRenderer.cpp +index c448e50c7..31f1d6c26 100644 +--- a/source/client/renderer/entity/HumanoidMobRenderer.cpp ++++ b/source/client/renderer/entity/HumanoidMobRenderer.cpp +@@ -203,3 +203,9 @@ void HumanoidMobRenderer::renderHand(const Entity& entity, float a) + } + #endif + } ++ ++void HumanoidMobRenderer::scale(const Mob &mob, Matrix &matrix, float a) ++{ ++ // players are actually 15/16ths the size than any other bipedal mob ++ matrix.scale((mob.isPlayer()) ? (15.0f / 16.0f) : 1.0f); ++} +diff --git a/source/client/renderer/entity/HumanoidMobRenderer.hpp b/source/client/renderer/entity/HumanoidMobRenderer.hpp +index bcb072c89..62b900098 100644 +--- a/source/client/renderer/entity/HumanoidMobRenderer.hpp ++++ b/source/client/renderer/entity/HumanoidMobRenderer.hpp +@@ -21,6 +21,7 @@ class HumanoidMobRenderer : public MobRenderer + virtual void onGraphicsReset() override; + + void renderHand(const Entity& entity, float a); ++ void scale(const Mob& mob, Matrix& matrix, float a); + + public: + HumanoidModel* m_pHumanoidModel; +diff --git a/source/client/renderer/entity/ItemRenderer.cpp b/source/client/renderer/entity/ItemRenderer.cpp +index aa3d9973f..e55794ba5 100644 +--- a/source/client/renderer/entity/ItemRenderer.cpp ++++ b/source/client/renderer/entity/ItemRenderer.cpp +@@ -192,7 +192,8 @@ void ItemRenderer::renderGuiItemOverlay(Font* font, Textures* textures, ItemStac + return; + + // Draw damage amount +- if (item.isDamaged()) { ++ if (item.isDamaged()) ++ { + int duraWidth = ceilf(13.0f - static_cast(item.getDamageValue()) * 13.0f / static_cast(item.getMaxDamage())); + int duraPercent = ceilf(255.0f - static_cast(item.getDamageValue()) * 255.0f / static_cast(item.getMaxDamage())); + +@@ -207,7 +208,8 @@ void ItemRenderer::renderGuiItemOverlay(Font* font, Textures* textures, ItemStac + blitRect(t, x + 2, y + 13, duraWidth, 1, duraColor); + } + +- if (item.m_count <= 1) { ++ if (item.m_count <= 1) ++ { + return; + } + +diff --git a/source/client/renderer/entity/SheepRenderer.cpp b/source/client/renderer/entity/SheepRenderer.cpp +index 3e864a296..ed2ef91b8 100644 +--- a/source/client/renderer/entity/SheepRenderer.cpp ++++ b/source/client/renderer/entity/SheepRenderer.cpp +@@ -15,7 +15,7 @@ int SheepRenderer::prepareArmor(const Mob& mob, int layer, float a) + const Sheep& sheep = (const Sheep&)mob; + if (layer == 0 && !sheep.isSheared()) + { +- bindTexture("/mob/sheep_fur.png"); ++ bindTexture("mob/sheep_fur.png"); + float brightness = sheep.getBrightness(a); + int color = sheep.getColor(); + currentShaderColor = Sheep::COLOR[color]; +diff --git a/source/client/sound/SoundData.cpp b/source/client/sound/SoundData.cpp +index 44c6bb1ed..23ab30a8c 100644 +--- a/source/client/sound/SoundData.cpp ++++ b/source/client/sound/SoundData.cpp +@@ -81,11 +81,13 @@ bool SoundDesc::_load(const char* category, const char *name) + } + location.path = "sound/" + std::string(name) + ".pcm"; + ret = _loadPcm(location); +- if (!ret) { ++ if (!ret) ++ { + m_codecType = AudioCodec::NONE; + LOG_W("Failed to load sound \"%s\"!", name); + return false; +- } else ++ } ++ else + return true; + } + +diff --git a/source/client/sound/SoundEngine.hpp b/source/client/sound/SoundEngine.hpp +index 620a108e0..e01b33f4e 100644 +--- a/source/client/sound/SoundEngine.hpp ++++ b/source/client/sound/SoundEngine.hpp +@@ -43,6 +43,7 @@ class SoundEngine + + public: + SoundSystem* m_pSoundSystem; ++ + private: + SoundRepository m_sounds; + SoundPathRepository m_songs; +diff --git a/source/common/Logger.cpp b/source/common/Logger.cpp +index a98c4559f..d0115bc6e 100644 +--- a/source/common/Logger.cpp ++++ b/source/common/Logger.cpp +@@ -15,11 +15,10 @@ Logger* Logger::singleton() + void Logger::setSingleton(Logger* logger) + { + // Stick with the first output handle we get +- if (!m_singleton) { ++ if (!m_singleton) + m_singleton = logger; +- } else { ++ else + m_singleton->print(LOG_ERR, "Logging already setup!"); +- } + } + + Logger::~Logger() +diff --git a/source/common/Random.cpp b/source/common/Random.cpp +index 66c9b30f8..8ea1c3763 100644 +--- a/source/common/Random.cpp ++++ b/source/common/Random.cpp +@@ -35,7 +35,8 @@ void Random::setSeed(int32_t seed) + void Random::init_genrand(uint32_t s) + { + mt[0] = s & 0xffffffffUL; +- for (mti = 1; mti < N; mti++) { ++ for (mti = 1; mti < N; mti++) ++ { + mt[mti] = + (1812433253UL * (mt[mti - 1] ^ (mt[mti - 1] >> 30)) + mti); + /* See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier. */ +@@ -65,11 +66,13 @@ uint32_t Random::genrand_int32() + if (mti == N+1) /* if init_genrand() has not been called, */ + init_genrand(5489UL); /* a default initial seed is used */ + +- for (kk=0;kk> 1) ^ mag01[y & 0x1UL]; + } +- for (;kk> 1) ^ mag01[y & 0x1UL]; + } +diff --git a/source/network/packets/AddMobPacket.hpp b/source/network/packets/AddMobPacket.hpp +index 0fc9d397e..8d311e986 100644 +--- a/source/network/packets/AddMobPacket.hpp ++++ b/source/network/packets/AddMobPacket.hpp +@@ -17,11 +17,13 @@ class AddMobPacket : public Packet + void write(RakNet::BitStream&) override; + void read(RakNet::BitStream&) override; + const SynchedEntityData::ItemsArray& getUnpackedData() const { return m_unpack; } ++ + public: + int32_t m_entityId; + int32_t m_entityTypeId; + Vec3 m_pos; + Vec2 m_rot; ++ + private: + SynchedEntityData m_entityData; + SynchedEntityData::ItemsArray m_unpack; +diff --git a/source/network/packets/SetEntityDataPacket.hpp b/source/network/packets/SetEntityDataPacket.hpp +index 917e42e2b..cd2b18e4a 100644 +--- a/source/network/packets/SetEntityDataPacket.hpp ++++ b/source/network/packets/SetEntityDataPacket.hpp +@@ -17,9 +17,11 @@ class SetEntityDataPacket : public Packet + void write(RakNet::BitStream&) override; + void read(RakNet::BitStream&) override; + const SynchedEntityData::ItemsArray& getUnpackedData() const { return m_packedItems; } ++ + public: + int32_t m_entityId; + bool m_bIsIncoming; ++ + private: + SynchedEntityData::ItemsArray m_packedItems; + }; +diff --git a/source/renderer/GL/GL.cpp b/source/renderer/GL/GL.cpp +index 230895f46..12ffd16ad 100644 +--- a/source/renderer/GL/GL.cpp ++++ b/source/renderer/GL/GL.cpp +@@ -157,25 +157,29 @@ int glhInvertMatrixf2(float* m, float* out) + r2[3] -= m2 * s; + r3[3] -= m3 * s; + s = r0[4]; +- if (s != 0.0f) { ++ if (s != 0.0f) ++ { + r1[4] -= m1 * s; + r2[4] -= m2 * s; + r3[4] -= m3 * s; + } + s = r0[5]; +- if (s != 0.0f) { ++ if (s != 0.0f) ++ { + r1[5] -= m1 * s; + r2[5] -= m2 * s; + r3[5] -= m3 * s; + } + s = r0[6]; +- if (s != 0.0f) { ++ if (s != 0.0f) ++ { + r1[6] -= m1 * s; + r2[6] -= m2 * s; + r3[6] -= m3 * s; + } + s = r0[7]; +- if (s != 0.0f) { ++ if (s != 0.0f) ++ { + r1[7] -= m1 * s; + r2[7] -= m2 * s; + r3[7] -= m3 * s; +@@ -195,22 +199,26 @@ int glhInvertMatrixf2(float* m, float* out) + r2[3] -= m2 * r1[3]; + r3[3] -= m3 * r1[3]; + s = r1[4]; +- if (0.0f != s) { ++ if (0.0f != s) ++ { + r2[4] -= m2 * s; + r3[4] -= m3 * s; + } + s = r1[5]; +- if (0.0f != s) { ++ if (0.0f != s) ++ { + r2[5] -= m2 * s; + r3[5] -= m3 * s; + } + s = r1[6]; +- if (0.0f != s) { ++ if (0.0f != s) ++ { + r2[6] -= m2 * s; + r3[6] -= m3 * s; + } + s = r1[7]; +- if (0.0f != s) { ++ if (0.0f != s) ++ { + r2[7] -= m2 * s; + r3[7] -= m3 * s; + } +diff --git a/source/world/entity/Creeper.cpp b/source/world/entity/Creeper.cpp +index 5046ff694..e55485e5b 100644 +--- a/source/world/entity/Creeper.cpp ++++ b/source/world/entity/Creeper.cpp +@@ -76,7 +76,8 @@ void Creeper::checkHurtTarget(Entity* pEnt, float f) + { + setSwellDir(-1); + m_swell--; +- if (m_swell < 0) { ++ if (m_swell < 0) ++ { + m_swell = 0; + } + } +diff --git a/source/world/entity/MobSpawner.hpp b/source/world/entity/MobSpawner.hpp +index fd29acaf3..3d7316255 100644 +--- a/source/world/entity/MobSpawner.hpp ++++ b/source/world/entity/MobSpawner.hpp +@@ -24,7 +24,7 @@ class MobSpawner { + public: + TilePos getRandomPosWithin(Level& level, int chunkX, int chunkZ); + void tick(Level& level, bool allowHostile, bool allowFriendly); +-private: + ++private: + std::set chunksToPoll; + }; +\ No newline at end of file +diff --git a/source/world/entity/Player.cpp b/source/world/entity/Player.cpp +index d7e0cff66..34c37381b 100644 +--- a/source/world/entity/Player.cpp ++++ b/source/world/entity/Player.cpp +@@ -318,7 +318,8 @@ void Player::readAdditionalSaveData(const CompoundTag& tag) + m_dimension = tag.getInt32("Dimension"); + //m_sleepTimer = tag.getInt32("SleepTimer"); + +- if (tag.contains("SpawnX") && tag.contains("SpawnY") && tag.contains("SpawnZ")) { ++ if (tag.contains("SpawnX") && tag.contains("SpawnY") && tag.contains("SpawnZ")) ++ { + setRespawnPos(TilePos( static_cast(tag.getInt32("SpawnX")), + static_cast(tag.getInt32("SpawnY")), + static_cast(tag.getInt32("SpawnZ")))); +@@ -383,7 +384,8 @@ void Player::attack(Entity* pEnt) + if (!item.isEmpty() && isMob) + { + item.hurtEnemy((Mob*)pEnt, this); +- if (item.m_count <= 0) { ++ if (item.m_count <= 0) ++ { + item.snap(this); + removeSelectedItem(); + } +@@ -575,9 +577,11 @@ void Player::interact(Entity* pEnt) + return; + + ItemStack& item = getSelectedItem(); +- if (!item.isEmpty()) { ++ if (!item.isEmpty()) ++ { + item.interactEnemy(static_cast(pEnt)); +- if (item.m_count <= 0) { ++ if (item.m_count <= 0) ++ { + item.snap(this); + removeSelectedItem(); + } +diff --git a/source/world/entity/Player.hpp b/source/world/entity/Player.hpp +index 1a25f7228..ed63daee9 100644 +--- a/source/world/entity/Player.hpp ++++ b/source/world/entity/Player.hpp +@@ -45,7 +45,7 @@ class Player : public Mob + public: + void reset() override; + void remove() override; +- float getHeadHeight() const override { return 0.12f; /*@HUH: what ?*/ } ++ float getHeadHeight() const override { return 0.12f; } + int getMaxHealth() const override { return 20; } + bool isShootable() const override { return true; } + bool isPlayer() const override { return true; } +diff --git a/source/world/inventory/ContainerMenu.cpp b/source/world/inventory/ContainerMenu.cpp +index 8c083c908..7a549e7b0 100644 +--- a/source/world/inventory/ContainerMenu.cpp ++++ b/source/world/inventory/ContainerMenu.cpp +@@ -338,17 +338,21 @@ void ContainerMenu::setAll(const std::vector& items) + } + } + +-void ContainerMenu::setData(int id, int value) { ++void ContainerMenu::setData(int id, int value) ++{ + } + +-uint16_t ContainerMenu::backup(Inventory*) { ++uint16_t ContainerMenu::backup(Inventory*) ++{ + return ++m_changeUid; + } + +-void ContainerMenu::deleteBackup(uint16_t) { ++void ContainerMenu::deleteBackup(uint16_t) ++{ + } + +-void ContainerMenu::rollbackToBackup(uint16_t) { ++void ContainerMenu::rollbackToBackup(uint16_t) ++{ + } + + bool ContainerMenu::isSynched(Player* player) const +diff --git a/source/world/item/BowItem.cpp b/source/world/item/BowItem.cpp +index 53ebdce5d..5b7ef84f1 100644 +--- a/source/world/item/BowItem.cpp ++++ b/source/world/item/BowItem.cpp +@@ -13,9 +13,8 @@ ItemStack* BowItem::use(ItemStack* inst, Level* level, Mob* user) const + if (!user->isPlayer() || static_cast(user)->isCreative() || static_cast(user)->m_pInventory->removeResource(Item::arrow->m_itemID)) + { + level->playSound(user, "random.bow", 1.0f, 1.0f / (level->m_random.nextFloat() * 0.4f + 0.8f)); +- if (!level->m_bIsClientSide) { ++ if (!level->m_bIsClientSide) + level->addEntity(new Arrow(level, user)); +- } + } + + return inst; +diff --git a/source/world/item/ItemStack.hpp b/source/world/item/ItemStack.hpp +index 53db80153..39586fe5a 100644 +--- a/source/world/item/ItemStack.hpp ++++ b/source/world/item/ItemStack.hpp +@@ -125,6 +125,7 @@ class ItemStack + public: + int16_t m_count; + int m_popTime; ++ + private: + int16_t m_auxValue; + CompoundTag* m_userData; +diff --git a/source/world/level/levelgen/chunk/RandomLevelSource.cpp b/source/world/level/levelgen/chunk/RandomLevelSource.cpp +index 1908668f1..222a48533 100644 +--- a/source/world/level/levelgen/chunk/RandomLevelSource.cpp ++++ b/source/world/level/levelgen/chunk/RandomLevelSource.cpp +@@ -320,11 +320,13 @@ void RandomLevelSource::buildSurfaces(const ChunkPos& pos, TileID* tiles, Biome* + { + byte1 = pBiome->field_20; + byte2 = pBiome->field_21; +- if (flag1) { ++ if (flag1) ++ { + byte1 = 0; + byte2 = Tile::gravel->m_ID; + } +- if (flag) { ++ if (flag) ++ { + byte1 = Tile::sand->m_ID; + byte2 = Tile::sand->m_ID; + } +diff --git a/source/world/level/levelgen/chunk/TestChunkSource.cpp b/source/world/level/levelgen/chunk/TestChunkSource.cpp +index d0f5bde69..48b72e22f 100644 +--- a/source/world/level/levelgen/chunk/TestChunkSource.cpp ++++ b/source/world/level/levelgen/chunk/TestChunkSource.cpp +@@ -59,7 +59,8 @@ LevelChunk* TestChunkSource::generateChunk(const ChunkPos& pos) + *p = TILE_BEDROCK; + //else if (j == 0 || k == 0) + // *p = TILE_AIR; +- else if (i == 65) { ++ else if (i == 65) ++ { + /*if (rand() % 10 == 0) + *p = TILE_ROSE; + else if (rand() % 10 == 0) +@@ -69,7 +70,8 @@ LevelChunk* TestChunkSource::generateChunk(const ChunkPos& pos) + } + else if (i > 64) + *p = TILE_AIR; +- else if (i == 64) { ++ else if (i == 64) ++ { + //if ((j + k) % 2 == 0) + *p = TILE_GRASS; + } +diff --git a/source/world/level/path/BinaryHeap.cpp b/source/world/level/path/BinaryHeap.cpp +index 5eed80453..07faf7fd7 100644 +--- a/source/world/level/path/BinaryHeap.cpp ++++ b/source/world/level/path/BinaryHeap.cpp +@@ -39,12 +39,12 @@ void BinaryHeap::inlined0(int num) + Node* var2 = m_items[num]; + + int var4; +- for (float var3 = var2->field_C; num > 0; num = var4) { ++ for (float var3 = var2->field_C; num > 0; num = var4) ++ { + var4 = (num - 1) >> 1; + Node* var5 = m_items[var4]; +- if (var3 >= var5->field_C) { ++ if (var3 >= var5->field_C) + break; +- } + + m_items[num] = var5; + var5->field_0 = num; +@@ -59,39 +59,41 @@ void BinaryHeap::downHeap(int num) + Node* var2 = m_items[num]; + float var3 = var2->field_C; + +- while (true) { ++ while (true) ++ { + int var4 = 1 + (num << 1); + int var5 = var4 + 1; +- if (var4 >= m_count) { ++ if (var4 >= m_count) + break; +- } + + Node* var6 = m_items[var4]; + float var7 = var6->field_C; + Node* var8; + float var9; +- if (var5 >= m_count) { ++ if (var5 >= m_count) ++ { + var8 = nullptr; + var9 = std::numeric_limits::infinity(); + } +- else { ++ else ++ { + var8 = m_items[var5]; + var9 = var8->field_C; + } + +- if (var7 < var9) { +- if (var7 >= var3) { ++ if (var7 < var9) ++ { ++ if (var7 >= var3) + break; +- } + + m_items[num] = var6; + var6->field_0 = num; + num = var4; + } +- else { +- if (var9 >= var3) { ++ else ++ { ++ if (var9 >= var3) + break; +- } + + m_items[num] = var8; + var8->field_0 = num; +diff --git a/source/world/level/path/BinaryHeap.hpp b/source/world/level/path/BinaryHeap.hpp +index e0142b685..57fb67525 100644 +--- a/source/world/level/path/BinaryHeap.hpp ++++ b/source/world/level/path/BinaryHeap.hpp +@@ -29,7 +29,8 @@ class BinaryHeap + void inlined0(int i); + void downHeap(int i); + +- Node* removeTop() { ++ Node* removeTop() ++ { + Node* pNode = m_items[0]; + m_items[0] = m_items[--m_count]; + m_items[m_count] = 0; +@@ -41,15 +42,18 @@ class BinaryHeap + return pNode; + } + +- void clear() { ++ void clear() ++ { + m_count = 0; + } + +- int size() const { ++ int size() const ++ { + return m_count; + } + +- void setDistance(Node* pNode, float distance) { ++ void setDistance(Node* pNode, float distance) ++ { + float oldDistance = pNode->field_C; + pNode->field_C = distance; + +diff --git a/source/world/tile/ChestTile.cpp b/source/world/tile/ChestTile.cpp +index 327bd0bf6..ca8f42ad9 100644 +--- a/source/world/tile/ChestTile.cpp ++++ b/source/world/tile/ChestTile.cpp +@@ -10,9 +10,8 @@ ChestTile::ChestTile(int id, int texture) : Tile(id, texture, Material::wood) + + int ChestTile::getTexture(const LevelSource* level, const TilePos& pos, Facing::Name face) const + { +- if (face == Facing::UP || face == Facing::DOWN) { ++ if (face == Facing::UP || face == Facing::DOWN) + return m_TextureFrame - 1; +- } + + int id_north = level->getTile(pos.north()); + int id_south = level->getTile(pos.south()); +@@ -22,7 +21,8 @@ int ChestTile::getTexture(const LevelSource* level, const TilePos& pos, Facing:: + bool isDoubleNS = (id_north == m_ID || id_south == m_ID); + bool isDoubleWE = (id_west == m_ID || id_east == m_ID); + +- if (!isDoubleNS && !isDoubleWE) { ++ if (!isDoubleNS && !isDoubleWE) ++ { + Facing::Name front = Facing::SOUTH; + + if (Tile::solid[id_north] && !Tile::solid[id_south]) +@@ -40,7 +40,8 @@ int ChestTile::getTexture(const LevelSource* level, const TilePos& pos, Facing:: + bool left = false; + Facing::Name front = Facing::SOUTH; + +- if (isDoubleWE) { ++ if (isDoubleWE) ++ { + left = id_west == m_ID; + TilePos side = left ? pos.west() : pos.east(); + +@@ -52,9 +53,12 @@ int ChestTile::getTexture(const LevelSource* level, const TilePos& pos, Facing:: + else if ((Tile::solid[id_south] || Tile::solid[id_infront]) && !Tile::solid[id_north] && !Tile::solid[id_behind]) + front = Facing::NORTH; + +- if (front == Facing::SOUTH) left = !left; +- if (face == front) return m_TextureFrame + (left ? 15 : 16); +- if (face == Facing::OPPOSITE[front]) return m_TextureFrame + (left ? 32 : 31); ++ if (front == Facing::SOUTH) ++ left = !left; ++ if (face == front) ++ return m_TextureFrame + (left ? 15 : 16); ++ if (face == Facing::OPPOSITE[front]) ++ return m_TextureFrame + (left ? 32 : 31); + return m_TextureFrame; + } + +@@ -67,10 +71,10 @@ int ChestTile::getTexture(const LevelSource* level, const TilePos& pos, Facing:: + int id_behind = level->getTile(side.west()); + int id_infront = level->getTile(side.east()); + +- if ((Tile::solid[id_west] || Tile::solid[id_behind]) && !Tile::solid[id_east] && !Tile::solid[id_infront]) { ++ if ((Tile::solid[id_west] || Tile::solid[id_behind]) && !Tile::solid[id_east] && !Tile::solid[id_infront]) + front = Facing::EAST; +- } +- else if ((Tile::solid[id_east] || Tile::solid[id_infront]) && !Tile::solid[id_west] && !Tile::solid[id_behind]) { ++ else if ((Tile::solid[id_east] || Tile::solid[id_infront]) && !Tile::solid[id_west] && !Tile::solid[id_behind]) ++ { + front = Facing::WEST; + left = !left; + } +@@ -87,7 +91,8 @@ int ChestTile::getTexture(const LevelSource* level, const TilePos& pos, Facing:: + + int ChestTile::getTexture(Facing::Name face) const + { +- switch (face) { ++ switch (face) ++ { + case Facing::UP: + case Facing::DOWN: + return m_TextureFrame - 1; +@@ -122,13 +127,12 @@ bool ChestTile::hasNeighbors(const Level* level, const TilePos& pos, int count) + return false; + } + +-void ChestTile::onRemove(Level* level, const TilePos& pos){ ++void ChestTile::onRemove(Level* level, const TilePos& pos) ++{ + ChestTileEntity* ent = static_cast(level->getTileEntity(pos)); + + if (!ent) +- { + Tile::onRemove(level, pos); +- } + + for (int slot = 0; slot < ent->getContainerSize(); ++slot) + { +@@ -160,6 +164,9 @@ void ChestTile::onRemove(Level* level, const TilePos& pos){ + + bool ChestTile::use(Level* level, const TilePos& pos, Player* player) + { ++ if (player->isSneaking() && !player->getSelectedItem().isEmpty()) ++ return false; ++ + if (level->m_bIsClientSide) + return true; + +diff --git a/source/world/tile/ChestTile.hpp b/source/world/tile/ChestTile.hpp +index 051081631..8ade330a0 100644 +--- a/source/world/tile/ChestTile.hpp ++++ b/source/world/tile/ChestTile.hpp +@@ -6,6 +6,7 @@ class ChestTile : public Tile + { + public: + ChestTile(int id, int texture); ++ + public: + int getTexture(const LevelSource*, const TilePos& pos, Facing::Name face) const override; + int getTexture(Facing::Name face) const override; +@@ -15,6 +16,7 @@ class ChestTile : public Tile + bool use(Level* level, const TilePos& pos, Player* var5) override; + bool hasTileEntity() const override; + TileEntity* newTileEntity() override; ++ + public: + Random m_chestRandom; + }; +\ No newline at end of file +diff --git a/source/world/tile/CraftingTableTile.cpp b/source/world/tile/CraftingTableTile.cpp +index f6cd821ab..dc5c26e95 100644 +--- a/source/world/tile/CraftingTableTile.cpp ++++ b/source/world/tile/CraftingTableTile.cpp +@@ -24,7 +24,8 @@ bool CraftingTableTile::use(Level* level, const TilePos& pos, Player* player) + + int CraftingTableTile::getTexture(Facing::Name face) const + { +- switch (face) { ++ switch (face) ++ { + case Facing::UP: return m_TextureFrame - 16; + case Facing::DOWN: return Tile::wood->getTexture(face); + case Facing::NORTH: case Facing::SOUTH: return m_TextureFrame + 1; +diff --git a/source/world/tile/FurnaceTile.cpp b/source/world/tile/FurnaceTile.cpp +index e5ea21f65..85ff6a4cb 100644 +--- a/source/world/tile/FurnaceTile.cpp ++++ b/source/world/tile/FurnaceTile.cpp +@@ -25,7 +25,8 @@ int FurnaceTile::getTexture(const LevelSource* level, const TilePos& pos, Facing + } + } + +-void FurnaceTile::animateTick(Level* level, const TilePos& pos, Random* random) { ++void FurnaceTile::animateTick(Level* level, const TilePos& pos, Random* random) ++{ + if (!m_active) + return; + +@@ -54,7 +55,8 @@ void FurnaceTile::animateTick(Level* level, const TilePos& pos, Random* random) + + int FurnaceTile::getTexture(Facing::Name face) const + { +- switch (face) { ++ switch (face) ++ { + case Facing::UP: + case Facing::DOWN: + return m_TextureFrame + 17; +@@ -73,7 +75,7 @@ void FurnaceTile::onPlace(Level* level, const TilePos& pos) + + bool FurnaceTile::use(Level* level, const TilePos& pos, Player* player) + { +- if (player->isSneaking() && player->getSelectedItem()) ++ if (player->isSneaking() && !player->getSelectedItem().isEmpty()) + return false; + + if (level->m_bIsClientSide) +@@ -85,20 +87,15 @@ bool FurnaceTile::use(Level* level, const TilePos& pos, Player* player) + + void FurnaceTile::setPlacedBy(Level* level, const TilePos& pos, Mob* mob) + { +- int rot = Mth::floor(0.5f + (mob->m_rot.y * 4.0f / 360.0f)) & 3; ++ int rot = Mth::floor(0.5f + (mob->m_rot.x * 4.0f / 360.0f)) & 3; + int data = 4; + + switch (rot) + { +- case 0: +- data = 2; +- break; +- case 1: +- data = 5; +- break; +- case 2: +- data = 3; +- break; ++ case 0: data = 2; break; ++ case 1: data = 5; break; ++ case 2: data = 3; break; ++ case 3: data = 4; break; + } + + level->setData(pos, data); +diff --git a/source/world/tile/MusicTile.cpp b/source/world/tile/MusicTile.cpp +index 7842f458e..5d50c6a1d 100644 +--- a/source/world/tile/MusicTile.cpp ++++ b/source/world/tile/MusicTile.cpp +@@ -28,7 +28,7 @@ void MusicTile::neighborChanged(Level* level, const TilePos& pos, TileID tile) + + bool MusicTile::use(Level* level, const TilePos& pos, Player* player) + { +- if (player->isSneaking() && player->getSelectedItem()) ++ if (player->isSneaking() && !player->getSelectedItem().isEmpty()) + return false; + + if (level->m_bIsClientSide) +diff --git a/source/world/tile/PumpkinTile.cpp b/source/world/tile/PumpkinTile.cpp +index c9481e442..cab8de26f 100644 +--- a/source/world/tile/PumpkinTile.cpp ++++ b/source/world/tile/PumpkinTile.cpp +@@ -7,7 +7,8 @@ PumpkinTile::PumpkinTile(TileID id, bool lantern) : Tile(id, TEXTURE_PUMPKIN_TOP + + int PumpkinTile::getTexture(Facing::Name face, TileData data) const + { +- switch (face) { ++ switch (face) ++ { + case Facing::UP: case Facing::DOWN: return m_TextureFrame; + default: + return (face == 2 && data == 2) || (face == 5 && data == 3) || (face == 3 && data == 0) || (face == 4 && data == 1) ? m_TextureFrame + (m_bLantern ? 18 : 17) : m_TextureFrame + 16; +@@ -16,7 +17,8 @@ int PumpkinTile::getTexture(Facing::Name face, TileData data) const + + int PumpkinTile::getTexture(Facing::Name face) const + { +- switch (face) { ++ switch (face) ++ { + case Facing::UP: case Facing::DOWN: return m_TextureFrame; + case Facing::SOUTH: return m_TextureFrame + 17; + default: return m_TextureFrame + 16; + +From 26e7f2e3746fc5b0114e446c3601b708dbcfde0f Mon Sep 17 00:00:00 2001 +From: hellosammu +Date: Mon, 16 Feb 2026 11:05:15 -0500 +Subject: [PATCH 03/22] Virtual EntityTile class; fix save/load of entity tiles + (far safer) + +--- + source/CMakeLists.txt | 1 + + source/world/level/Level.cpp | 21 +++++++++----- + .../world/level/levelgen/chunk/LevelChunk.cpp | 28 +++++++++++-------- + source/world/tile/ChestTile.cpp | 6 ++-- + source/world/tile/ChestTile.hpp | 4 +-- + source/world/tile/EntityTile.cpp | 23 +++++++++++++++ + source/world/tile/EntityTile.hpp | 17 +++++++++++ + source/world/tile/FurnaceTile.cpp | 10 +++++-- + source/world/tile/FurnaceTile.hpp | 4 +-- + source/world/tile/MusicTile.cpp | 2 +- + source/world/tile/MusicTile.hpp | 4 +-- + source/world/tile/Tile.cpp | 9 ------ + source/world/tile/Tile.hpp | 1 - + 13 files changed, 89 insertions(+), 41 deletions(-) + create mode 100644 source/world/tile/EntityTile.cpp + create mode 100644 source/world/tile/EntityTile.hpp + +diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt +index afe085a83..a91b654c5 100644 +--- a/source/CMakeLists.txt ++++ b/source/CMakeLists.txt +@@ -426,6 +426,7 @@ add_library(reminecraftpe-core STATIC + world/tile/OreTile.cpp + world/tile/StairTile.cpp + world/tile/SandStoneTile.cpp ++ world/tile/EntityTile.cpp + world/tile/ChestTile.cpp + world/tile/FurnaceTile.cpp + world/tile/MusicTile.cpp +diff --git a/source/world/level/Level.cpp b/source/world/level/Level.cpp +index efec2255d..7460bbb08 100644 +--- a/source/world/level/Level.cpp ++++ b/source/world/level/Level.cpp +@@ -293,20 +293,27 @@ void Level::setTileEntity(const TilePos& pos, TileEntity* tileEntity) + + void Level::removeTileEntity(const TilePos& pos) + { +- TileEntity* old = getTileEntity(pos); ++ TileEntity* tileEntity = getTileEntity(pos); + +- if (old != nullptr && m_bUpdatingTileEntities) ++ if (tileEntity == nullptr) + { +- old->setRemoved(); ++ LOG_W("Tried to remove a tile entity at %d, %d, %d, but there was no tile entity there!", pos.x, pos.y, pos.z); + return; + } + +- if (old) +- Util::remove(m_tileEntityList, old); ++ // During a tile entity update, just mark it for potential removal ++ if (m_bUpdatingTileEntities) ++ { ++ tileEntity->setRemoved(); ++ return; ++ } + +- LevelChunk* pChunk = getChunk(pos); +- if (pChunk) ++ Util::remove(m_tileEntityList, tileEntity); ++ ++ if (LevelChunk* pChunk = getChunk(pos)) ++ { + pChunk->removeTileEntity(pos); ++ } + } + + void Level::swap(const TilePos& pos1, const TilePos& pos2) +diff --git a/source/world/level/levelgen/chunk/LevelChunk.cpp b/source/world/level/levelgen/chunk/LevelChunk.cpp +index 598a4ef8a..357a805c1 100644 +--- a/source/world/level/levelgen/chunk/LevelChunk.cpp ++++ b/source/world/level/levelgen/chunk/LevelChunk.cpp +@@ -589,7 +589,7 @@ bool LevelChunk::setTile(const ChunkTilePos& pos, TileID tile) + tilePos.x += pos.x; + tilePos.z += pos.z; + m_pBlockData[index] = tile; +- if (oldTile) ++ if (oldTile && Tile::tiles[oldTile]) + { + Tile::tiles[oldTile]->onRemove(m_pLevel, tilePos); + } +@@ -722,20 +722,24 @@ void LevelChunk::addTileEntity(TileEntity* tileEntity) + + void LevelChunk::setTileEntity(const ChunkTilePos& pos, TileEntity* tileEntity) + { +- tileEntity->m_pLevel = m_pLevel; +- TileID tile = getTile(pos); + TilePos tilePos(m_chunkPos, pos.y); +- tilePos.x += pos.x; +- tilePos.z += pos.z; +- tileEntity->m_pos = tilePos; +- if (tile > 0 && Tile::isEntityTile[tile]) +- { +- tileEntity->clearRemoved(); +- m_tileEntities[pos] = tileEntity; +- return; ++ ++ if (tileEntity) ++ { ++ tileEntity->m_pLevel = m_pLevel; ++ TileID tile = getTile(pos); ++ tilePos.x += pos.x; ++ tilePos.z += pos.z; ++ tileEntity->m_pos = tilePos; ++ if (tile > 0 && Tile::isEntityTile[tile]) ++ { ++ tileEntity->clearRemoved(); ++ m_tileEntities[pos] = tileEntity; ++ return; ++ } + } + +- LOG_I("Attempted to place a tile entity where there was no entity tile! %d", tile); ++ LOG_W("Attempted to place a tile entity at %d, %d, %d where there was no entity tile!", tilePos.x, tilePos.y, tilePos.z); + } + + void LevelChunk::removeTileEntity(const ChunkTilePos& pos) +diff --git a/source/world/tile/ChestTile.cpp b/source/world/tile/ChestTile.cpp +index ca8f42ad9..e7aad4afa 100644 +--- a/source/world/tile/ChestTile.cpp ++++ b/source/world/tile/ChestTile.cpp +@@ -3,7 +3,7 @@ + #include "world/CompoundContainer.hpp" + #include "world/tile/entity/ChestTileEntity.hpp" + +-ChestTile::ChestTile(int id, int texture) : Tile(id, texture, Material::wood) ++ChestTile::ChestTile(int id, int texture) : EntityTile(id, texture, Material::wood) + { + setTicking(true); + } +@@ -132,7 +132,7 @@ void ChestTile::onRemove(Level* level, const TilePos& pos) + ChestTileEntity* ent = static_cast(level->getTileEntity(pos)); + + if (!ent) +- Tile::onRemove(level, pos); ++ EntityTile::onRemove(level, pos); + + for (int slot = 0; slot < ent->getContainerSize(); ++slot) + { +@@ -159,7 +159,7 @@ void ChestTile::onRemove(Level* level, const TilePos& pos) + } + } + +- Tile::onRemove(level, pos); ++ EntityTile::onRemove(level, pos); + } + + bool ChestTile::use(Level* level, const TilePos& pos, Player* player) +diff --git a/source/world/tile/ChestTile.hpp b/source/world/tile/ChestTile.hpp +index 8ade330a0..e154d454e 100644 +--- a/source/world/tile/ChestTile.hpp ++++ b/source/world/tile/ChestTile.hpp +@@ -1,8 +1,8 @@ + #pragma once + +-#include "Tile.hpp" ++#include "EntityTile.hpp" + +-class ChestTile : public Tile ++class ChestTile : public EntityTile + { + public: + ChestTile(int id, int texture); +diff --git a/source/world/tile/EntityTile.cpp b/source/world/tile/EntityTile.cpp +new file mode 100644 +index 000000000..ed5e75f84 +--- /dev/null ++++ b/source/world/tile/EntityTile.cpp +@@ -0,0 +1,23 @@ ++#include "EntityTile.hpp" ++#include "world/level/Level.hpp" ++ ++EntityTile::EntityTile(TileID id, int textureID, Material *material) : Tile(id, textureID, material) ++{ ++} ++ ++bool EntityTile::hasTileEntity() const ++{ ++ return true; ++} ++ ++void EntityTile::onPlace(Level *level, const TilePos &pos) ++{ ++ Tile::onPlace(level, pos); ++ level->setTileEntity(pos, newTileEntity()); ++} ++ ++void EntityTile::onRemove(Level *level, const TilePos &pos) ++{ ++ Tile::onRemove(level, pos); ++ level->removeTileEntity(pos); ++} +\ No newline at end of file +diff --git a/source/world/tile/EntityTile.hpp b/source/world/tile/EntityTile.hpp +new file mode 100644 +index 000000000..d79c2cc1a +--- /dev/null ++++ b/source/world/tile/EntityTile.hpp +@@ -0,0 +1,17 @@ ++#pragma once ++ ++#include "Tile.hpp" ++ ++class EntityTile : public Tile ++{ ++public: ++ EntityTile(TileID id, int textureID, Material* material); ++ ++public: ++ virtual TileEntity* newTileEntity() = 0; ++ ++public: ++ bool hasTileEntity() const override; ++ void onPlace(Level* level, const TilePos& pos) override; ++ void onRemove(Level* level, const TilePos& pos) override; ++}; +\ No newline at end of file +diff --git a/source/world/tile/FurnaceTile.cpp b/source/world/tile/FurnaceTile.cpp +index 85ff6a4cb..ccf3e1c28 100644 +--- a/source/world/tile/FurnaceTile.cpp ++++ b/source/world/tile/FurnaceTile.cpp +@@ -4,7 +4,7 @@ + + bool FurnaceTile::keepInventory = false; + +-FurnaceTile::FurnaceTile(int id, bool active) : Tile(id, TEXTURE_FURNACE_SIDE, Material::stone) ++FurnaceTile::FurnaceTile(int id, bool active) : EntityTile(id, TEXTURE_FURNACE_SIDE, Material::stone) + { + setTicking(true); + m_active = active; +@@ -69,7 +69,7 @@ int FurnaceTile::getTexture(Facing::Name face) const + + void FurnaceTile::onPlace(Level* level, const TilePos& pos) + { +- Tile::onPlace(level, pos); ++ EntityTile::onPlace(level, pos); + RecalculateLookDirection(level, pos); + } + +@@ -110,6 +110,7 @@ void FurnaceTile::onRemove(Level* level, const TilePos& pos) + + if (!tileEnt) + { ++ EntityTile::onRemove(level, pos); + return; // this has to be wrapped in a guard or the compiler screams, thanks -Wmisleading-indentation + } + +@@ -139,11 +140,16 @@ void FurnaceTile::onRemove(Level* level, const TilePos& pos) + level->addEntity(itemEnt); + } + } ++ ++ EntityTile::onRemove(level, pos); + } + + void FurnaceTile::SetLit(bool lit, Level* level, const TilePos& pos) + { + TileEntity* tileEntity = level->getTileEntity(pos); ++ if (!tileEntity) ++ return; ++ + int data = level->getData(pos); + + keepInventory = true; +diff --git a/source/world/tile/FurnaceTile.hpp b/source/world/tile/FurnaceTile.hpp +index bb0ac9359..0a79e01aa 100644 +--- a/source/world/tile/FurnaceTile.hpp ++++ b/source/world/tile/FurnaceTile.hpp +@@ -1,8 +1,8 @@ + #pragma once + +-#include "Tile.hpp" ++#include "EntityTile.hpp" + +-class FurnaceTile : public Tile ++class FurnaceTile : public EntityTile + { + public: + FurnaceTile(int id, bool active); +diff --git a/source/world/tile/MusicTile.cpp b/source/world/tile/MusicTile.cpp +index 5d50c6a1d..372077028 100644 +--- a/source/world/tile/MusicTile.cpp ++++ b/source/world/tile/MusicTile.cpp +@@ -2,7 +2,7 @@ + #include "world/level/Level.hpp" + #include "entity/MusicTileEntity.hpp" + +-MusicTile::MusicTile(TileID id, int texture) : Tile(id, texture, Material::wood) ++MusicTile::MusicTile(TileID id, int texture) : EntityTile(id, texture, Material::wood) + { + } + +diff --git a/source/world/tile/MusicTile.hpp b/source/world/tile/MusicTile.hpp +index fcddfba82..7383a0694 100644 +--- a/source/world/tile/MusicTile.hpp ++++ b/source/world/tile/MusicTile.hpp +@@ -1,8 +1,8 @@ + #pragma once + +-#include "Tile.hpp" ++#include "EntityTile.hpp" + +-class MusicTile : public Tile ++class MusicTile : public EntityTile + { + public: + MusicTile(TileID id, int textureID); +diff --git a/source/world/tile/Tile.cpp b/source/world/tile/Tile.cpp +index 4aea4c4b7..14878f2d2 100644 +--- a/source/world/tile/Tile.cpp ++++ b/source/world/tile/Tile.cpp +@@ -283,11 +283,6 @@ int Tile::getSpawnResourcesAuxValue(int x) const + return 0; + } + +-TileEntity* Tile::newTileEntity() +-{ +- return nullptr; +-} +- + Tile* Tile::setToolTypes(unsigned int toolMask) + { + m_toolMask |= toolMask; +@@ -998,14 +993,10 @@ void Tile::neighborChanged(Level* pLevel, const TilePos& pos, TileID tile) + + void Tile::onPlace(Level* pLevel, const TilePos& pos) + { +- if (hasTileEntity()) +- pLevel->setTileEntity(pos, newTileEntity()); + } + + void Tile::onRemove(Level* pLevel, const TilePos& pos) + { +- if (hasTileEntity()) +- pLevel->removeTileEntity(pos); + } + + bool Tile::containsX(const Vec3& v) +diff --git a/source/world/tile/Tile.hpp b/source/world/tile/Tile.hpp +index 2a9bc0ad3..61388b225 100644 +--- a/source/world/tile/Tile.hpp ++++ b/source/world/tile/Tile.hpp +@@ -123,7 +123,6 @@ class Tile + virtual Tile* setDestroyTime(float); + virtual Tile* setTicking(bool); + virtual int getSpawnResourcesAuxValue(int) const; +- virtual TileEntity* newTileEntity(); + Tile* setToolTypes(unsigned int toolMask); + Tile* setToolLevel(int toolLevel); + Tile* setToolTypesAndLevel(unsigned int toolMask, int toolLevel = 0); + +From f64f96a392f9124127306d3274585aa59c11ad3a Mon Sep 17 00:00:00 2001 +From: hellosammu +Date: Mon, 16 Feb 2026 12:18:45 -0500 +Subject: [PATCH 04/22] base client riding logic + +--- + source/client/renderer/entity/MobRenderer.cpp | 2 +- + source/world/entity/Entity.cpp | 139 ++++++++++++++++++ + source/world/entity/Entity.hpp | 19 ++- + source/world/entity/Mob.cpp | 20 ++- + source/world/entity/Mob.hpp | 6 +- + source/world/entity/Pig.cpp | 31 ++++ + source/world/entity/Pig.hpp | 2 +- + source/world/entity/Player.cpp | 4 +- + source/world/entity/Player.hpp | 3 +- + source/world/entity/Spider.hpp | 2 +- + source/world/level/Level.cpp | 56 ++++++- + source/world/tile/FurnaceTile.cpp | 2 + + 12 files changed, 267 insertions(+), 19 deletions(-) + +diff --git a/source/client/renderer/entity/MobRenderer.cpp b/source/client/renderer/entity/MobRenderer.cpp +index 13d7ba76e..c3af5f015 100644 +--- a/source/client/renderer/entity/MobRenderer.cpp ++++ b/source/client/renderer/entity/MobRenderer.cpp +@@ -90,7 +90,7 @@ void MobRenderer::render(const Entity& entity, const Vec3& pos, float rot, float + MatrixStack::Ref matrix = MatrixStack::World.push(); + + m_pModel->m_attackTime = getAttackAnim(mob, a); +- m_pModel->m_bRiding = false; ++ m_pModel->m_bRiding = mob.isRiding(); + m_pModel->m_bIsBaby = mob.isBaby(); + + if (m_pArmorModel != nullptr) +diff --git a/source/world/entity/Entity.cpp b/source/world/entity/Entity.cpp +index 49d7112a0..f7edc64a2 100644 +--- a/source/world/entity/Entity.cpp ++++ b/source/world/entity/Entity.cpp +@@ -28,6 +28,9 @@ void Entity::_init() + field_28 = 0; + field_30 = 1.0f; + m_dimensionId = DIMENSION_OVERWORLD; ++ m_riderId = 0; ++ m_ridingId = 0; ++ m_bRiding = false; + m_bBlocksBuilding = false; + m_pLevel = nullptr; + m_tintColor = Color::WHITE; +@@ -469,6 +472,14 @@ void Entity::tick() + void Entity::baseTick() + { + //@TODO: untangle the gotos ++ if (const Entity* riding = getRiding()) ++ { ++ if ((!riding && m_riderId > 0) || riding->m_bRemoved) ++ { ++ m_riderId = 0; ++ setSharedFlag(C_ENTITY_FLAG_RIDING, false); ++ } ++ } + + field_90 = m_walkDist; + m_oPos = m_pos; +@@ -937,6 +948,46 @@ AABB* Entity::getCollideAgainstBox(Entity* ent) const + return nullptr; + } + ++void Entity::rideTick() ++{ ++ Entity* riding = getRiding(); ++ if (!riding || riding->m_bRemoved) ++ { ++ m_riderId = 0; ++ setSharedFlag(C_ENTITY_FLAG_RIDING, false); ++ } ++ ++ // we don't move ++ m_vel = Vec3::ZERO; ++ ++ tick(); ++ ++ riding->positionRider(); ++ m_rideRot.x += riding->m_rot.x - riding->m_oRot.x; ++ m_rideRot.y += riding->m_rot.y - riding->m_oRot.y; ++ while (m_rideRot.y >= 180.0f) ++ m_rideRot.y -= 360.0f; ++ while (m_rideRot.y < -180.0f) ++ m_rideRot.y += 360.0f; ++ while (m_rideRot.x >= 180.0f) ++ m_rideRot.x -= 360.0f; ++ while (m_rideRot.x < -180.0f) ++ m_rideRot.x += 360.0f; ++ ++ float rotX = m_rideRot.x * 0.5f; ++ float rotY = m_rideRot.y * 0.5f; ++ ++ float lookLimiter = 10.0f; ++ rotX = Mth::clamp(rotX, -lookLimiter, lookLimiter); ++ rotY = Mth::clamp(rotY, -lookLimiter, lookLimiter); ++ ++ m_rideRot.x -= rotX; ++ m_rideRot.y -= rotY; ++ ++ m_rot.x += rotX; ++ m_rot.y += rotY; ++} ++ + void Entity::handleInsidePortal() + { + } +@@ -946,6 +997,94 @@ void Entity::handleEntityEvent(EventType::ID eventId) + LOG_W("Unknown EntityEvent ID: %d, EntityType: %s", eventId, getDescriptor().getEntityType().getName().c_str()); + } + ++void Entity::positionRider() ++{ ++ Entity* rider = getRider(); ++ if (!rider) ++ return; ++ ++ rider->setPos(Vec3(m_pos.x, m_pos.y + getRideHeight() + rider->getRidingHeight(), m_pos.z)); ++} ++ ++void Entity::ride(Entity* newRiding) ++{ ++ m_rideRot = Vec2::ZERO; ++ Entity* oldRiding = getRiding(); ++ ++ // Dismount current ride if nullptr is fed in ++ if (newRiding == nullptr) ++ { ++ if (oldRiding) ++ { ++ moveTo(oldRiding->m_pos); ++ setRot(oldRiding->m_rot); ++ oldRiding->m_riderId = 0; // Let them know you dismounted them ++ } ++ ++ // Let yourself know you aren't riding anything ++ m_ridingId = 0; ++ setSharedFlag(C_ENTITY_FLAG_RIDING, false); ++ ++ return; ++ } ++ ++ // Dismount if the same entity is fed in ++ if (oldRiding && oldRiding == newRiding) ++ { ++ oldRiding->m_riderId = 0; ++ ++ m_ridingId = 0; ++ setSharedFlag(C_ENTITY_FLAG_RIDING, false); ++ ++ moveTo(oldRiding->m_pos); ++ setRot(oldRiding->m_rot); ++ return; ++ } ++ ++ // if (this.riding != null) this.riding.rider = null; ++ if (oldRiding) ++ { ++ oldRiding->m_riderId = 0; ++ } ++ ++ // if (newRiding.rider != null) newRiding.rider.riding = null; ++ // i hate this name but it's literally what it is ++ if (Entity* newRidesOldRider = newRiding->getRider()) ++ { ++ newRidesOldRider->m_ridingId = 0; ++ newRidesOldRider->setSharedFlag(C_ENTITY_FLAG_RIDING, false); ++ } ++ ++ // Tell yourself that you're riding the new ride ++ m_ridingId = newRiding->m_EntityID; ++ setSharedFlag(C_ENTITY_FLAG_RIDING, true); ++ ++ // Tell the new ride that it's being ridden by you ++ newRiding->m_riderId = m_EntityID; ++} ++ ++Entity* Entity::getRiding() const ++{ ++ if (m_ridingId <= 0) ++ return nullptr; ++ ++ if (Entity* riding = m_pLevel->getEntity(m_ridingId)) ++ return riding; ++ ++ return nullptr; ++} ++ ++Entity* Entity::getRider() const ++{ ++ if (m_riderId <= 0) ++ return nullptr; ++ ++ if (Entity* rider = m_pLevel->getEntity(m_riderId)) ++ return rider; ++ ++ return nullptr; ++} ++ + /*void Entity::thunderHit(LightningBolt* bolt) + { + burn(5); +diff --git a/source/world/entity/Entity.hpp b/source/world/entity/Entity.hpp +index 07a0baf01..e104b01de 100644 +--- a/source/world/entity/Entity.hpp ++++ b/source/world/entity/Entity.hpp +@@ -112,11 +112,9 @@ class Entity + Entity(Level*); + virtual ~Entity(); + +-protected: ++public: + virtual bool getSharedFlag(SharedFlag flag) const; + virtual void setSharedFlag(SharedFlag flag, bool value); +- +-public: + virtual void reset(); + virtual void setLevel(Level*); + virtual void removed(); +@@ -165,7 +163,7 @@ class Entity + virtual bool isPushable() const { return false; } + virtual bool isShootable() const { return false; } + virtual bool isOnFire() const { return m_fireTicks > 0 || getSharedFlag(C_ENTITY_FLAG_ONFIRE); } +- virtual bool isRiding() const { return /*m_pRiding != nullptr ||*/ getSharedFlag(C_ENTITY_FLAG_RIDING); } ++ virtual bool isRiding() const { return getRiding() || getSharedFlag(C_ENTITY_FLAG_RIDING); } + virtual bool isSneaking() const { return getSharedFlag(C_ENTITY_FLAG_SNEAKING); } + virtual void setSneaking(bool value) { setSharedFlag(C_ENTITY_FLAG_SNEAKING, value); } + virtual bool isAlive() const { return m_bRemoved; } +@@ -196,9 +194,18 @@ class Entity + virtual RenderType queryEntityRenderer() const; + virtual const AABB* getCollideBox() const; + virtual AABB* getCollideAgainstBox(Entity* ent) const; ++ virtual void rideTick(); + virtual void handleInsidePortal(); + virtual void handleEntityEvent(EventType::ID eventId); + //virtual void thunderHit(LightningBolt*); ++ virtual void positionRider(); ++ virtual void ride(Entity*); ++ virtual float getRideHeight() const { return m_bbHeight * 0.75f; } ++ virtual float getRidingHeight() const { return m_heightOffset; } ++ Entity* getRiding() const; ++ Entity* getRider() const; ++ // void setRiding(Entity* newRiding); ++ // void setRider(Entity* newRider); + void load(const CompoundTag& tag); + bool save(CompoundTag& tag) const; + void saveWithoutId(CompoundTag& tag) const; +@@ -240,12 +247,16 @@ class Entity + float field_30; + //TileSource* m_pTileSource; + DimensionId m_dimensionId; ++ Entity::ID m_ridingId; ++ Entity::ID m_riderId; ++ bool m_bRiding; + bool m_bBlocksBuilding; + Level* m_pLevel; + Vec3 m_oPos; // "o" in Java or "xo" "yo" "zo" + Vec3 m_vel; + Vec2 m_rot; + Vec2 m_oRot; // "RotO" in Java or "xRotO" "yRotO" ++ Vec2 m_rideRot; + Color m_tintColor; + AABB m_hitbox; + bool m_bOnGround; +diff --git a/source/world/entity/Mob.cpp b/source/world/entity/Mob.cpp +index a336c5654..a3967ce12 100644 +--- a/source/world/entity/Mob.cpp ++++ b/source/world/entity/Mob.cpp +@@ -56,7 +56,7 @@ void Mob::_init() + m_lPos = Vec3::ZERO; + m_lRot = Vec2::ZERO; + m_lastHurt = 0; +- m_pEntLookedAt = nullptr; ++ m_entLookedAtId = 0; + m_bSwinging = false; + m_swingTime = 0; + m_ambientSoundTime = 0; +@@ -786,6 +786,13 @@ void Mob::lookAt(Entity* pEnt, float a3, float a4) + -rotlerp(m_rot.y, x2 * 180.0f / float(M_PI), a3))); + } + ++Entity *Mob::getLookingAt() const ++{ ++ if (m_entLookedAtId == 0) ++ return nullptr; ++ return m_pLevel->getEntity(m_entLookedAtId); ++} ++ + bool Mob::canSpawn() + { + return m_pLevel->getCubes(this, m_hitbox)->empty(); +@@ -832,7 +839,7 @@ void Mob::updateAi() + Entity* nearestPlayer = m_pLevel->getNearestPlayer(*this, 8.0f); + if (nearestPlayer) + { +- m_pEntLookedAt = nearestPlayer; ++ m_entLookedAtId = nearestPlayer->m_EntityID; + + field_120 = m_random.nextInt(20) + 10; + } +@@ -843,17 +850,18 @@ void Mob::updateAi() + } + + // @TODO: we get a crash here when a Player leaves +- if (m_pEntLookedAt) ++ if (m_entLookedAtId > 0) + { +- lookAt(m_pEntLookedAt, 10.0f, getMaxHeadXRot()); ++ Entity* pEnt = m_pLevel->getEntity(m_entLookedAtId); ++ lookAt(pEnt, 10.0f, getMaxHeadXRot()); + + // gaze timer + field_120--; + + // if the entity was removed, or we're too far away, or our gaze timer is up +- if (field_120 < 0 || m_pEntLookedAt->m_bRemoved || m_pEntLookedAt->distanceToSqr(this) > 64.0f) ++ if (field_120 < 0 || pEnt->m_bRemoved || pEnt->distanceToSqr(this) > 64.0f) + // stop staring +- m_pEntLookedAt = nullptr; ++ m_entLookedAtId = 0; + } + else + { +diff --git a/source/world/entity/Mob.hpp b/source/world/entity/Mob.hpp +index 2f3909617..b98687817 100644 +--- a/source/world/entity/Mob.hpp ++++ b/source/world/entity/Mob.hpp +@@ -63,9 +63,9 @@ class Mob : public Entity + virtual void updateWalkAnim(); + virtual void aiStep(); + virtual void lookAt(Entity* pEnt, float, float); +- virtual bool isLookingAtAnEntity() { return m_pEntLookedAt != nullptr; } ++ virtual bool isLookingAtAnEntity() { return m_entLookedAtId > 0; } + virtual bool isSlowedByLiquids() const { return true; } +- virtual Entity* getLookingAt() const { return m_pEntLookedAt; } ++ virtual Entity* getLookingAt() const; + virtual void beforeRemove() {} + virtual bool canSpawn(); + virtual float getAttackAnim(float f) const; +@@ -145,7 +145,7 @@ class Mob : public Entity + Vec3 m_lPos; + Vec2 m_lRot; + int m_lastHurt; +- Entity* m_pEntLookedAt; ++ Entity::ID m_entLookedAtId; + + float v020_field_104; + +diff --git a/source/world/entity/Pig.cpp b/source/world/entity/Pig.cpp +index 153163f49..0f160d6d0 100644 +--- a/source/world/entity/Pig.cpp ++++ b/source/world/entity/Pig.cpp +@@ -6,6 +6,8 @@ + SPDX-License-Identifier: BSD-1-Clause + ********************************************************************/ + #include "Pig.hpp" ++#include "Player.hpp" ++#include "world/level/Level.hpp" + + Pig::Pig(Level* pLevel) : Animal(pLevel) + { +@@ -14,6 +16,7 @@ Pig::Pig(Level* pLevel) : Animal(pLevel) + m_texture = "mob/pig.png"; + setSize(0.9f, 0.9f); + // some dataitem stuff ++ setSaddle(true); + } + int Pig::getDeathLoot() const + { +@@ -23,6 +26,34 @@ int Pig::getDeathLoot() const + return Item::porkChop_raw->m_itemID; + } + ++bool Pig::interact(Player* pPlayer) ++{ ++ return false; ++ // @TODO: add saddles ++ /* ++ if (m_pLevel->m_bIsClientSide) ++ { ++ return false; ++ } ++ ++ if (!m_bSaddled) ++ { ++ return false; ++ } ++ ++ Entity* rider = getRider(); ++ ++ // already being ridden by someone else ++ if (rider && rider != pPlayer) ++ { ++ return false; ++ } ++ ++ pPlayer->ride(this); ++ return true; ++ */ ++} ++ + void Pig::setSaddle(bool b) + { + // @TODO: this +diff --git a/source/world/entity/Pig.hpp b/source/world/entity/Pig.hpp +index fb1d9ae77..0e93310d9 100644 +--- a/source/world/entity/Pig.hpp ++++ b/source/world/entity/Pig.hpp +@@ -19,7 +19,7 @@ class Pig : public Animal + std::string getHurtSound() const override { return "mob.pig"; } + int getDeathLoot() const override; + int getMaxHealth() const override { return 10; } +- bool interact(Player*) override { return false; } ++ bool interact(Player*) override; + + bool hasSaddle() const { return false; } + void setSaddle(bool b); +diff --git a/source/world/entity/Player.cpp b/source/world/entity/Player.cpp +index 34c37381b..a22fb7e90 100644 +--- a/source/world/entity/Player.cpp ++++ b/source/world/entity/Player.cpp +@@ -472,7 +472,9 @@ void Player::respawn() + + void Player::rideTick() + { +- ++ Mob::rideTick(); ++ m_oBob = m_bob; ++ m_bob = 0.0f; + } + + void Player::setDefaultHeadHeight() +diff --git a/source/world/entity/Player.hpp b/source/world/entity/Player.hpp +index ed63daee9..9e982f7dd 100644 +--- a/source/world/entity/Player.hpp ++++ b/source/world/entity/Player.hpp +@@ -94,7 +94,8 @@ class Player : public Mob + Dimension* getDimension() const; + void prepareCustomTextures(); + void respawn(); +- void rideTick(); ++ void rideTick() override; ++ float getRidingHeight() const override { return m_heightOffset - 0.5f; } + void setDefaultHeadHeight(); + void setRespawnPos(const TilePos& pos); + inline const Abilities& getAbilities() const { return m_abilities; } +diff --git a/source/world/entity/Spider.hpp b/source/world/entity/Spider.hpp +index e176275ca..1b2662fb5 100644 +--- a/source/world/entity/Spider.hpp ++++ b/source/world/entity/Spider.hpp +@@ -11,7 +11,7 @@ class Spider : public Monster + std::string getDeathSound() const override { return "mob.spiderdeath"; } + std::string getHurtSound() const override { return "mob.spider"; } + int getDeathLoot() const override { return ITEM_STRING; } +- float getRideHeight() const { return m_bbHeight * 0.75f - 0.5f; } ++ float getRideHeight() const override { return m_bbHeight * 0.75f - 0.5f; } + + Entity* findAttackTarget() override; + void checkHurtTarget(Entity* ent, float var2) override; +diff --git a/source/world/level/Level.cpp b/source/world/level/Level.cpp +index 7460bbb08..c5b170c09 100644 +--- a/source/world/level/Level.cpp ++++ b/source/world/level/Level.cpp +@@ -1253,6 +1253,17 @@ void Level::removeAllPendingEntityRemovals() + for (EntityVector::iterator it = m_pendingEntityRemovals.begin(); it != m_pendingEntityRemovals.end(); it++) + { + Entity* ent = *it; ++ if (Entity* riding = ent->getRiding()) ++ { ++ if (riding->m_bRemoved || riding->getRider() != ent) ++ { ++ riding->m_riderId = 0; ++ ent->m_ridingId = 0; ++ ent->setSharedFlag(C_ENTITY_FLAG_RIDING, false); ++ } ++ else ++ continue; ++ } + ent->removed(); + + LevelChunk* chunk = getChunk(ent->m_chunkPos); +@@ -1273,6 +1284,14 @@ void Level::removeEntities(const EntityVector& vec) + + bool Level::removeEntity(Entity* pEnt) + { ++ // kick off rider before disappearing ++ if (Entity* rider = pEnt->getRider()) ++ rider->ride(nullptr); ++ ++ // kick self off mount before disappearing ++ if (Entity* mount = pEnt->getRiding()) ++ mount->ride(nullptr); ++ + pEnt->remove(); + + if (pEnt->isPlayer()) +@@ -1678,7 +1697,12 @@ void Level::tick(Entity* pEnt, bool shouldTick) + pEnt->m_oRot = pEnt->m_rot; + + if (pEnt->m_bInAChunk) +- pEnt->tick(); ++ { ++ if (pEnt->getRiding()) ++ pEnt->rideTick(); ++ else ++ pEnt->tick(); ++ } + } + else + { +@@ -1712,6 +1736,24 @@ void Level::tick(Entity* pEnt, bool shouldTick) + getChunk(cp)->updateEntity(pEnt); + } + } ++ if (shouldTick && pEnt->m_bInAChunk) ++ { ++ Entity* rider = pEnt->getRider(); ++ if (rider) ++ { ++ if (rider->m_bRemoved || rider->getRiding() != pEnt) ++ { ++ rider->m_riderId = 0; ++ ++ pEnt->m_ridingId = 0; ++ pEnt->setSharedFlag(C_ENTITY_FLAG_RIDING, false); ++ } ++ else ++ { ++ tick(rider); ++ } ++ } ++ } + } + + void Level::tick(Entity* pEnt) +@@ -1748,6 +1790,18 @@ void Level::tickEntities() + { + Entity* pEnt = m_entities[i]; + ++ if (Entity* riding = pEnt->getRiding()) ++ { ++ if (riding->m_bRemoved || riding->getRider() != pEnt) ++ { ++ riding->m_riderId = 0; ++ pEnt->m_ridingId = 0; ++ pEnt->setSharedFlag(C_ENTITY_FLAG_RIDING, false); ++ } ++ else ++ continue; ++ } ++ + if (!pEnt->m_bRemoved) + { + tick(pEnt); +diff --git a/source/world/tile/FurnaceTile.cpp b/source/world/tile/FurnaceTile.cpp +index ccf3e1c28..e17ea782d 100644 +--- a/source/world/tile/FurnaceTile.cpp ++++ b/source/world/tile/FurnaceTile.cpp +@@ -148,7 +148,9 @@ void FurnaceTile::SetLit(bool lit, Level* level, const TilePos& pos) + { + TileEntity* tileEntity = level->getTileEntity(pos); + if (!tileEntity) ++ { + return; ++ } + + int data = level->getData(pos); + + +From bd18e03f60f3f899b7fb18ea0a96b1ee697fb97c Mon Sep 17 00:00:00 2001 +From: hellosammu +Date: Mon, 16 Feb 2026 19:30:15 -0500 +Subject: [PATCH 05/22] naming convention adjustment to match other classes + better + +--- + source/client/app/NinecraftApp.cpp | 4 +- + source/world/tile/entity/TileEntity.cpp | 2 +- + source/world/tile/entity/TileEntityType.cpp | 40 ++++++++-------- + source/world/tile/entity/TileEntityType.hpp | 52 ++++++++++++--------- + 4 files changed, 53 insertions(+), 45 deletions(-) + +diff --git a/source/client/app/NinecraftApp.cpp b/source/client/app/NinecraftApp.cpp +index 0a16d0d3d..1b336d72c 100644 +--- a/source/client/app/NinecraftApp.cpp ++++ b/source/client/app/NinecraftApp.cpp +@@ -180,10 +180,10 @@ void NinecraftApp::_initAll() + EntityTypeDescriptor::initDescriptors(); // custom + MobCategory::initMobCategories(); + MobFactory::initMobLists(); ++ TileEntityFactory::InitTileEntities(); + Tile::initTiles(); + Item::initItems(); + Biome::initBiomes(); +- TileEntityType::InitTileEntities(); + } + + _initOptions(); +@@ -341,7 +341,7 @@ void NinecraftApp::onGraphicsReset() + + void NinecraftApp::teardown() + { +- TileEntityType::TeardownTileEntities(); ++ TileEntityFactory::TeardownTileEntities(); + teardownRenderer(); + Resource::teardownLoaders(); + // Stop our SoundSystem before we nuke our sound buffers and cause it to implode +diff --git a/source/world/tile/entity/TileEntity.cpp b/source/world/tile/entity/TileEntity.cpp +index 9cd7b79ad..a5b11be86 100644 +--- a/source/world/tile/entity/TileEntity.cpp ++++ b/source/world/tile/entity/TileEntity.cpp +@@ -19,7 +19,7 @@ TileEntity* TileEntity::LoadTileEntity(const CompoundTag& tag) + } + + std::string id = tag.getString("id"); +- const TileEntityType* type = TileEntityType::GetType(id); ++ const TileEntityType* type = TileEntityFactory::GetType(id); + + if (!type) + { +diff --git a/source/world/tile/entity/TileEntityType.cpp b/source/world/tile/entity/TileEntityType.cpp +index 8ee14b727..1391e41ac 100644 +--- a/source/world/tile/entity/TileEntityType.cpp ++++ b/source/world/tile/entity/TileEntityType.cpp +@@ -8,44 +8,44 @@ + // #include "RecordPlayerTileEntity.hpp" + // #include "PistonMovingTileEntity.hpp" + +-std::map TileEntityType::_types; +- + TileEntityType* TileEntityType::furnace; + TileEntityType* TileEntityType::chest; + TileEntityType* TileEntityType::noteblock; + +-TileEntityType::TileEntityType(const std::string& name, CreateFunction func) : _name(name), _function(func) ++std::map TileEntityFactory::_types; ++ ++void TileEntityFactory::InitTileEntities() + { ++ TileEntityType::furnace = RegisterTileEntity("Furnace"); ++ TileEntityType::chest = RegisterTileEntity("Chest"); ++ TileEntityType::noteblock = RegisterTileEntity("Music"); + } + +-const std::string& TileEntityType::getName() const ++void TileEntityFactory::TeardownTileEntities() + { +- return _name; ++ // delete all heap allocated tile entity types (furnace, chest, etc.) ++ for (std::map::const_iterator it = _types.begin(); it != _types.end(); ++it) ++ { ++ SAFE_DELETE(it->second); ++ } ++ _types.clear(); + } + +-TileEntity* TileEntityType::newTileEntity() const ++const TileEntityType* TileEntityFactory::GetType(const std::string& name) + { +- return _function(); ++ return _types[name]; + } + +-void TileEntityType::InitTileEntities() ++TileEntityType::TileEntityType(const std::string& name, CreateFunction func) : _name(name), _function(func) + { +- furnace = RegisterTileEntity("Furnace"); +- chest = RegisterTileEntity("Chest"); +- noteblock = RegisterTileEntity("Music"); + } + +-const TileEntityType* TileEntityType::GetType(const std::string& name) ++const std::string& TileEntityType::getName() const + { +- return _types[name]; ++ return _name; + } + +-void TileEntityType::TeardownTileEntities() ++TileEntity* TileEntityType::newTileEntity() const + { +- // delete all heap allocated tile entity types (furnace, chest, etc.) +- for (std::map::const_iterator it = _types.begin(); it != _types.end(); ++it) +- { +- SAFE_DELETE(it->second); +- } +- _types.clear(); ++ return _function(); + } +\ No newline at end of file +diff --git a/source/world/tile/entity/TileEntityType.hpp b/source/world/tile/entity/TileEntityType.hpp +index 3a981cf6c..0baa6b857 100644 +--- a/source/world/tile/entity/TileEntityType.hpp ++++ b/source/world/tile/entity/TileEntityType.hpp +@@ -6,10 +6,32 @@ + + class Level; + class TileEntity; ++class TileEntityType; ++ ++class TileEntityFactory ++{ ++private: ++ static std::map _types; ++ ++public: ++ static void InitTileEntities(); ++ static void TeardownTileEntities(); ++ static const TileEntityType* GetType(const std::string& name); ++ ++public: ++ template ++ static TileEntityType* RegisterTileEntity(const std::string& name); ++}; + + class TileEntityType + { + public: ++ static TileEntityType* furnace; ++ static TileEntityType* chest; ++ static TileEntityType* noteblock; ++ ++public: ++ friend class TileEntityFactory; + typedef TileEntity* (*CreateFunction)(); + + public: +@@ -19,32 +41,18 @@ class TileEntityType + const std::string& getName() const; + TileEntity* newTileEntity() const; + +-public: +- static void InitTileEntities(); +- static const TileEntityType* GetType(const std::string& name); +- static void TeardownTileEntities(); +- +-private: +- static std::map _types; +- + private: + std::string _name; + CreateFunction _function; + +-public: +- static TileEntityType* furnace; +- static TileEntityType* chest; +- static TileEntityType* noteblock; +- + template + static TileEntity* CreateType() { return new T(); } ++}; + +-public: +- template +- static TileEntityType* RegisterTileEntity(const std::string& name) +- { +- TileEntityType* type = new TileEntityType(name, &TileEntityType::CreateType); +- _types[type->_name] = type; +- return type; +- } +-}; +\ No newline at end of file ++template ++TileEntityType* TileEntityFactory::RegisterTileEntity(const std::string& name) ++{ ++ TileEntityType* type = new TileEntityType(name, &TileEntityType::CreateType); ++ _types[type->_name] = type; ++ return type; ++} +\ No newline at end of file + +From ecdaaea97942f004e67b06d95b8a88594d702ae5 Mon Sep 17 00:00:00 2001 +From: hellosammu +Date: Mon, 16 Feb 2026 20:52:43 -0500 +Subject: [PATCH 06/22] Entities should be hashmaps. Can open furnace now + +--- + source/client/gui/ScreenChooser.cpp | 6 ++ + source/client/gui/ScreenChooser.hpp | 2 + + .../client/multiplayer/MultiPlayerLevel.cpp | 2 +- + .../network/ClientSideNetworkHandler.cpp | 3 +- + source/client/player/LocalPlayer.cpp | 2 +- + source/client/renderer/LevelRenderer.cpp | 8 +- + source/server/ServerPlayer.cpp | 20 ++++- + source/server/ServerPlayer.hpp | 1 + + source/server/ServerSideNetworkHandler.cpp | 4 +- + source/world/entity/Entity.hpp | 2 - + source/world/entity/Pig.cpp | 7 +- + source/world/entity/Player.cpp | 3 - + source/world/level/Level.cpp | 84 ++++++++++++------- + source/world/level/Level.hpp | 6 +- + .../storage/ExternalFileLevelStorage.cpp | 6 +- + 15 files changed, 104 insertions(+), 52 deletions(-) + +diff --git a/source/client/gui/ScreenChooser.cpp b/source/client/gui/ScreenChooser.cpp +index d2620083f..2ce71d3a7 100644 +--- a/source/client/gui/ScreenChooser.cpp ++++ b/source/client/gui/ScreenChooser.cpp +@@ -5,6 +5,7 @@ + #include "screens/PauseScreen.hpp" + #include "screens/inventory/CraftingScreen.hpp" + #include "screens/inventory/ChestScreen.hpp" ++#include "screens/inventory/FurnaceScreen.hpp" + #include "screens/OptionsScreen.hpp" + #include "screens/OptionsScreen_Console.hpp" + #include "screens/CreateWorldScreen.hpp" +@@ -53,6 +54,11 @@ void ScreenChooser::pushCraftingScreen(Player* player, const TilePos& pos) + m_pMinecraft->setScreen(new CraftingScreen(player->m_pInventory, pos, player->m_pLevel)); + } + ++void ScreenChooser::pushFurnaceScreen(Player* player, FurnaceTileEntity* furnace) ++{ ++ m_pMinecraft->setScreen(new FurnaceScreen(player->m_pInventory, furnace)); ++} ++ + void ScreenChooser::pushChestScreen(Player* player, Container* container) + { + m_pMinecraft->setScreen(new ChestScreen(player->m_pInventory, container)); +diff --git a/source/client/gui/ScreenChooser.hpp b/source/client/gui/ScreenChooser.hpp +index c052f6233..e78800ade 100644 +--- a/source/client/gui/ScreenChooser.hpp ++++ b/source/client/gui/ScreenChooser.hpp +@@ -7,6 +7,7 @@ class Player; + class Minecraft; + class Container; + class Screen; ++class FurnaceTileEntity; + + //@NOTE: This is just based on MCPE, not really a decompilation, make it accurate if necessary + class ScreenChooser +@@ -22,6 +23,7 @@ class ScreenChooser + virtual void pushOptionsScreen(Screen*); + virtual void pushProgressScreen(); + virtual void pushCraftingScreen(Player*, const TilePos&); // originally pushWorkbenchScreen ++ virtual void pushFurnaceScreen(Player*, FurnaceTileEntity*); + virtual void pushChestScreen(Player*, Container*); + virtual void pushCreditsScreen(Screen*); + +diff --git a/source/client/multiplayer/MultiPlayerLevel.cpp b/source/client/multiplayer/MultiPlayerLevel.cpp +index 1890674d5..66c6bcfa4 100644 +--- a/source/client/multiplayer/MultiPlayerLevel.cpp ++++ b/source/client/multiplayer/MultiPlayerLevel.cpp +@@ -13,7 +13,7 @@ void MultiPlayerLevel::tick() + for (size_t i = 0; i < 10 && i < m_reEntries.size(); i++) + { + Entity* pEntity = m_reEntries[i]; +- if (std::find(m_entities.begin(), m_entities.end(), pEntity) != m_entities.end()) ++ if (m_entitiesById.find(pEntity->hashCode()) == m_entitiesById.end()) + { + addEntity(pEntity); + } +diff --git a/source/client/network/ClientSideNetworkHandler.cpp b/source/client/network/ClientSideNetworkHandler.cpp +index 7b5fd9559..56f55f0df 100644 +--- a/source/client/network/ClientSideNetworkHandler.cpp ++++ b/source/client/network/ClientSideNetworkHandler.cpp +@@ -17,6 +17,7 @@ + #include "world/entity/MobFactory.hpp" + #include "world/level/Explosion.hpp" + #include "world/inventory/SimpleContainer.hpp" ++#include "world/tile/entity/FurnaceTileEntity.hpp" + + // This lets you make the client shut up and not log events in the debug console. + //#define VERBOSE_CLIENT +@@ -683,7 +684,7 @@ void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& guid, ContainerO + pLocalPlayer->openContainer(new SimpleContainer(packet->m_size, packet->m_title.C_String())); + break; + case Container::FURNACE: +- //pLocalPlayer->openFurnace(new FurnaceTileEntity()); ++ pLocalPlayer->openFurnace(new FurnaceTileEntity); + break; + case Container::DISPENSER: + //pLocalPlayer->openTrap(new DispenserTileEntity()); +diff --git a/source/client/player/LocalPlayer.cpp b/source/client/player/LocalPlayer.cpp +index 8db2a1dcb..6ca19d378 100644 +--- a/source/client/player/LocalPlayer.cpp ++++ b/source/client/player/LocalPlayer.cpp +@@ -138,7 +138,7 @@ void LocalPlayer::startCrafting(const TilePos& pos) + void LocalPlayer::openFurnace(FurnaceTileEntity* furnace) + { + // PE 0.3.2 doesn't let you cook in creative mode +- m_pMinecraft->setScreen(new FurnaceScreen(m_pInventory, furnace)); ++ m_pMinecraft->getScreenChooser()->pushFurnaceScreen(this, furnace); + } + + void LocalPlayer::openContainer(Container* container) +diff --git a/source/client/renderer/LevelRenderer.cpp b/source/client/renderer/LevelRenderer.cpp +index f9c47bc95..e30b75164 100644 +--- a/source/client/renderer/LevelRenderer.cpp ++++ b/source/client/renderer/LevelRenderer.cpp +@@ -1584,12 +1584,12 @@ void LevelRenderer::renderEntities(Vec3 pos, Culler* culler, float f) + + EntityRenderDispatcher::off = camera->m_posPrev + (camera->m_pos - camera->m_posPrev) * f; + +- const EntityVector* pVec = m_pLevel->getAllEntities(); ++ const EntityMap* pVec = m_pLevel->getAllEntities(); + m_totalEntities = int(pVec->size()); + +- for (int i = 0; i < m_totalEntities; i++) +- { +- const Entity* entity = (*pVec)[i]; ++ for (EntityMap::const_iterator it = pVec->begin(); it != pVec->end(); ++it) ++ { ++ const Entity* entity = it->second; + if (!entity->shouldRender(pos)) + continue; + +diff --git a/source/server/ServerPlayer.cpp b/source/server/ServerPlayer.cpp +index c48af8dd7..21ed28339 100644 +--- a/source/server/ServerPlayer.cpp ++++ b/source/server/ServerPlayer.cpp +@@ -9,8 +9,10 @@ + #include "network/packets/ContainerSetContentPacket.hpp" + #include "network/RakNetInstance.hpp" + #include "world/inventory/CraftingMenu.hpp" ++#include "world/inventory/FurnaceMenu.hpp" + #include "world/inventory/ChestMenu.hpp" + #include "world/level/Level.hpp" ++#include "world/tile/entity/FurnaceTileEntity.hpp" + + ServerPlayer::ServerPlayer(Level* pLevel, GameType playerGameType) + : Player(pLevel, playerGameType) +@@ -58,7 +60,7 @@ void ServerPlayer::openContainer(Container* container) + #if NETWORK_PROTOCOL_VERSION >= 5 + m_pLevel->m_pRakNetInstance->send( + new ContainerOpenPacket( +- m_pContainerMenu->m_containerId, Container::CONTAINER, ++ m_containerId, Container::CONTAINER, + container->getName(), container->getContainerSize() + ) + ); +@@ -76,6 +78,22 @@ void ServerPlayer::closeContainer() + doCloseContainer(); + } + ++void ServerPlayer::openFurnace(FurnaceTileEntity* furnace) ++{ ++ _nextContainerCounter(); ++ ++#if NETWORK_PROTOCOL_VERSION >= 5 ++ m_pLevel->m_pRakNetInstance->send( ++ new ContainerOpenPacket( ++ m_containerId, Container::FURNACE, ++ furnace->getName(), furnace->getContainerSize() ++ ) ++ ); ++#endif ++ ++ setContainerMenu(new FurnaceMenu(m_pInventory, furnace)); ++} ++ + void ServerPlayer::take(Entity* pEnt, int count) + { + m_pLevel->m_pRakNetInstance->send(new TakeItemEntityPacket(pEnt->m_EntityID, m_EntityID)); +diff --git a/source/server/ServerPlayer.hpp b/source/server/ServerPlayer.hpp +index ddc2b7d53..11152a123 100644 +--- a/source/server/ServerPlayer.hpp ++++ b/source/server/ServerPlayer.hpp +@@ -14,6 +14,7 @@ class ServerPlayer : public Player, public ContainerListener + void startCrafting(const TilePos& pos) override; + void openContainer(Container* container) override; + void closeContainer() override; ++ void openFurnace(FurnaceTileEntity* tileEntity); + void take(Entity* pEnt, int count) override; + + void refreshContainer(ContainerMenu* menu, const std::vector& items) override; +diff --git a/source/server/ServerSideNetworkHandler.cpp b/source/server/ServerSideNetworkHandler.cpp +index 840207710..3cb71fd4e 100644 +--- a/source/server/ServerSideNetworkHandler.cpp ++++ b/source/server/ServerSideNetworkHandler.cpp +@@ -218,9 +218,9 @@ void ServerSideNetworkHandler::handle(const RakNet::RakNetGUID& guid, ReadyPacke + + #if NETWORK_PROTOCOL_VERSION >= 3 + // send the connecting player info about all entities in the world +- for (size_t i = 0; i < m_pLevel->m_entities.size(); i++) ++ for (EntityMap::iterator it = m_pLevel->m_entities.begin(); it != m_pLevel->m_entities.end(); ++it) + { +- Entity* entity = m_pLevel->m_entities[i]; ++ Entity* entity = it->second; + if (canReplicateEntity(entity)) + { + AddMobPacket packet(*((Mob*)entity)); +diff --git a/source/world/entity/Entity.hpp b/source/world/entity/Entity.hpp +index e104b01de..0869cc71b 100644 +--- a/source/world/entity/Entity.hpp ++++ b/source/world/entity/Entity.hpp +@@ -204,8 +204,6 @@ class Entity + virtual float getRidingHeight() const { return m_heightOffset; } + Entity* getRiding() const; + Entity* getRider() const; +- // void setRiding(Entity* newRiding); +- // void setRider(Entity* newRider); + void load(const CompoundTag& tag); + bool save(CompoundTag& tag) const; + void saveWithoutId(CompoundTag& tag) const; +diff --git a/source/world/entity/Pig.cpp b/source/world/entity/Pig.cpp +index 0f160d6d0..7ba77895e 100644 +--- a/source/world/entity/Pig.cpp ++++ b/source/world/entity/Pig.cpp +@@ -20,10 +20,9 @@ Pig::Pig(Level* pLevel) : Animal(pLevel) + } + int Pig::getDeathLoot() const + { +- if (isOnFire()) +- return Item::porkChop_cooked->m_itemID; +- else +- return Item::porkChop_raw->m_itemID; ++ return (isOnFire()) ? ++ Item::porkChop_cooked->m_itemID : ++ Item::porkChop_raw->m_itemID; + } + + bool Pig::interact(Player* pPlayer) +diff --git a/source/world/entity/Player.cpp b/source/world/entity/Player.cpp +index a22fb7e90..9d0b19685 100644 +--- a/source/world/entity/Player.cpp ++++ b/source/world/entity/Player.cpp +@@ -541,17 +541,14 @@ void Player::drop(const ItemStack& item, bool randomly) + + void Player::startCrafting(const TilePos& pos) + { +- + } + + void Player::openFurnace(FurnaceTileEntity* tileEntity) + { +- + } + + void Player::startStonecutting(const TilePos& pos) + { +- + } + + void Player::startDestroying() +diff --git a/source/world/level/Level.cpp b/source/world/level/Level.cpp +index c5b170c09..0c1efd729 100644 +--- a/source/world/level/Level.cpp ++++ b/source/world/level/Level.cpp +@@ -80,10 +80,9 @@ Level::~Level() + SAFE_DELETE(m_pPathFinder); + SAFE_DELETE(m_pMobSpawner); + +- const size_t size = m_entities.size(); +- for (size_t i = 0; i < size; i++) ++ for (EntityMap::iterator it = m_entities.begin(); it != m_entities.end(); it++) + { +- Entity* pEnt = m_entities.at(i); ++ Entity* pEnt = it->second; + + //you better HOPE this is freed by Minecraft! (or a NetworkHandler) + //Really should have used shared pointers and stuff. +@@ -358,22 +357,18 @@ Material* Level::getMaterial(const TilePos& pos) const + + Entity* Level::getEntity(Entity::ID id) const + { +- // @TODO: wtf? no map?? +- + // prioritize players first. + for (std::vector::const_iterator it = m_players.begin(); it != m_players.end(); it++) + { + Player* pEnt = *it; +- if (pEnt->m_EntityID == id) +- return pEnt; +- } +- for (std::vector::const_iterator it = m_entities.begin(); it != m_entities.end(); it++) +- { +- Entity* pEnt = *it; +- if (pEnt->m_EntityID == id) ++ if (pEnt->hashCode() == id) + return pEnt; + } + ++ EntityMap::const_iterator it = m_entities.find(id); ++ if (it != m_entities.end()) ++ return it->second; ++ + return nullptr; + } + +@@ -386,7 +381,7 @@ unsigned int Level::getEntityCount(const EntityCategories& category) const + return it->second; + } + +-const EntityVector* Level::getAllEntities() const ++const EntityMap* Level::getAllEntities() const + { + return &m_entities; + } +@@ -1248,7 +1243,15 @@ void Level::validateSpawn() + + void Level::removeAllPendingEntityRemovals() + { +- Util::removeAll(m_entities, m_pendingEntityRemovals); ++ for (EntityVector::iterator it = m_pendingEntityRemovals.begin(); it != m_pendingEntityRemovals.end(); it++) ++ { ++ Entity* ent = *it; ++ if (m_entities.find(ent->hashCode()) != m_entities.end()) ++ { ++ m_entities.erase(ent->hashCode()); ++ } ++ ++ } + + for (EntityVector::iterator it = m_pendingEntityRemovals.begin(); it != m_pendingEntityRemovals.end(); it++) + { +@@ -1326,7 +1329,7 @@ bool Level::addEntity(Entity* pEnt) + m_players.push_back((Player*)pEnt); + } + +- m_entities.push_back(pEnt); ++ m_entities.insert(std::make_pair(pEnt->hashCode(), pEnt)); + + entityAdded(pEnt); + +@@ -1392,9 +1395,9 @@ void Level::sendEntityData() + return; + + // Inlined on 0.2.1, god bless PerfTimer +- for (EntityVector::iterator it = m_entities.begin(); it != m_entities.end(); it++) ++ for (EntityMap::iterator it = m_entities.begin(); it != m_entities.end(); it++) + { +- Entity* ent = *it; ++ Entity* ent = it->second; + SynchedEntityData& data = ent->getEntityData(); + if (data.isDirty()) + m_pRakNetInstance->send(new SetEntityDataPacket(ent->m_EntityID, data)); +@@ -1786,9 +1789,9 @@ void Level::tickEntities() + // inlined in the original + removeAllPendingEntityRemovals(); + +- for (size_t i = 0; i < m_entities.size(); i++) ++ for (EntityMap::iterator it = m_entities.begin(); it != m_entities.end();) + { +- Entity* pEnt = m_entities[i]; ++ Entity* pEnt = it->second; + + if (Entity* riding = pEnt->getRiding()) + { +@@ -1799,24 +1802,36 @@ void Level::tickEntities() + pEnt->setSharedFlag(C_ENTITY_FLAG_RIDING, false); + } + else ++ { ++ ++it; + continue; ++ } + } + + if (!pEnt->m_bRemoved) + { + tick(pEnt); ++ ++it; ++ ++ continue; + } +- else if (!pEnt->isPlayer() || pEnt->m_bForceRemove) ++ ++ if (!pEnt->isPlayer() || pEnt->m_bForceRemove) + { + if (pEnt->m_bInAChunk && hasChunk(pEnt->m_chunkPos)) + getChunk(pEnt->m_chunkPos)->removeEntity(pEnt); + +- m_entities.erase(m_entities.begin() + i); +- i--; ++ EntityMap::iterator itErase = it; ++ it++; ++ m_entities.erase(itErase); + + entityRemoved(pEnt); + delete pEnt; ++ ++ continue; + } ++ ++ ++it; + } + + m_bUpdatingTileEntities = true; +@@ -1834,7 +1849,7 @@ void Level::tickEntities() + if (ch) + ch->removeTileEntity(tileEnt->m_pos); + +- m_entities.erase(m_entities.begin() + i); ++ m_tileEntityList.erase(m_tileEntityList.begin() + i); + i--; + + delete tileEnt; +@@ -2076,11 +2091,24 @@ void Level::explode(Entity* entity, const Vec3& pos, float power, bool bIsFiery) + + void Level::addEntities(const EntityVector& entities) + { +- m_entities.insert(m_entities.end(), entities.begin(), entities.end()); +- +- for (EntityVector::iterator it = m_entities.begin(); it != m_entities.end(); it++) ++ for (EntityVector::const_iterator it = entities.begin(); it != entities.end(); it++) + { + Entity* pEnt = *it; ++ EntityMap::iterator result = m_entities.find(pEnt->hashCode()); ++ ++ if (result != m_entities.end()) ++ { ++ if (result->second == pEnt) ++ { ++ LOG_W("Entity %d already exists. Skipping...", pEnt->hashCode()); ++ continue; ++ } ++ ++ removeEntity(result->second); ++ continue; ++ } ++ ++ m_entities.insert(std::make_pair(pEnt->hashCode(), pEnt)); + entityAdded(pEnt); + } + } +@@ -2097,9 +2125,9 @@ void Level::ensureAdded(Entity* entity) + for (cp.z = chunkPos.z - 2; cp.z <= chunkPos.z + 2; cp.z++) + getChunk(cp); + +- EntityVector::iterator result = std::find(m_entities.begin(), m_entities.end(), entity); ++ EntityMap::iterator result = m_entities.find(entity->hashCode()); + if (result == m_entities.end()) +- m_entities.push_back(entity); ++ m_entities.insert(std::make_pair(entity->hashCode(), entity)); + } + + bool Level::extinguishFire(Player* player, const TilePos& pos, Facing::Name face) +diff --git a/source/world/level/Level.hpp b/source/world/level/Level.hpp +index 95ae707ea..9caf0153e 100644 +--- a/source/world/level/Level.hpp ++++ b/source/world/level/Level.hpp +@@ -14,6 +14,7 @@ + #define _USE_MATH_DEFINES + #endif + #include ++#include + + #include "client/renderer/LightUpdate.hpp" + #include "world/tile/Tile.hpp" +@@ -38,6 +39,7 @@ class Packet; + class MobSpawner; + + typedef std::vector EntityVector; ++typedef std::map EntityMap; + typedef std::vector TileEntityVector; + typedef std::vector AABBVector; + +@@ -183,7 +185,7 @@ class Level : public LevelSource + HitResult clip(Vec3 a, Vec3 b, bool c) const; + Entity* getEntity(Entity::ID id) const; + unsigned int getEntityCount(const EntityCategories&) const; +- const EntityVector* getAllEntities() const; ++ const EntityMap* getAllEntities() const; + EntityVector getEntities(Entity* pAvoid, const AABB&) const; + BiomeSource* getBiomeSource() const override; + LevelStorage* getLevelStorage() const { return m_pLevelStorage; } +@@ -219,7 +221,7 @@ class Level : public LevelSource + bool m_bInstantTicking; + bool m_bIsClientSide; // if the level is controlled externally by a server. + bool m_bPostProcessing; +- EntityVector m_entities; ++ EntityMap m_entities; + std::vector m_players; + int m_skyDarken; + uint8_t field_30; +diff --git a/source/world/level/storage/ExternalFileLevelStorage.cpp b/source/world/level/storage/ExternalFileLevelStorage.cpp +index 681d04db0..31e8fcb35 100644 +--- a/source/world/level/storage/ExternalFileLevelStorage.cpp ++++ b/source/world/level/storage/ExternalFileLevelStorage.cpp +@@ -400,10 +400,10 @@ void ExternalFileLevelStorage::saveEntities(Level* level, LevelChunk* chunk) + //getTimeS(); + ListTag* entitiesTag = new ListTag(); + +- const EntityVector* entities = level->getAllEntities(); +- for (EntityVector::const_iterator it = entities->begin(); it != entities->end(); it++) ++ const EntityMap* entities = level->getAllEntities(); ++ for (EntityMap::const_iterator it = entities->begin(); it != entities->end(); it++) + { +- const Entity* entity = *it; ++ const Entity* entity = it->second; + CompoundTag* tag = new CompoundTag(); + + if (!entity->save(*tag)) + +From d0699c4839197b656c468e8191fcb0589f975ef4 Mon Sep 17 00:00:00 2001 +From: hellosammu +Date: Tue, 17 Feb 2026 08:22:55 -0500 +Subject: [PATCH 07/22] bounds check fix + +--- + source/world/inventory/ContainerMenu.cpp | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/source/world/inventory/ContainerMenu.cpp b/source/world/inventory/ContainerMenu.cpp +index 7a549e7b0..f618561e3 100644 +--- a/source/world/inventory/ContainerMenu.cpp ++++ b/source/world/inventory/ContainerMenu.cpp +@@ -332,7 +332,8 @@ void ContainerMenu::setItem(int index, ItemStack item) + + void ContainerMenu::setAll(const std::vector& items) + { +- for (size_t i = 0; i < items.size(); ++i) ++ size_t n = std::min(items.size(), m_slots.size()); ++ for (size_t i = 0; i < n; ++i) + { + m_slots[i]->set(items[i]); + } + +From 22170d2907952956801216a66f102eacb0c294b8 Mon Sep 17 00:00:00 2001 +From: hellosammu +Date: Tue, 17 Feb 2026 13:44:35 -0500 +Subject: [PATCH 08/22] vsync, top snow fix + +--- + platforms/android/AppPlatform_android.cpp | 9 ++++++ + platforms/android/AppPlatform_android.hpp | 1 + + platforms/sdl/base/AppPlatform_sdl.cpp | 7 +++++ + platforms/sdl/base/AppPlatform_sdl.hpp | 1 + + platforms/sdl/sdl2/main.cpp | 14 +++------ + platforms/windows/AppPlatform_win32.cpp | 7 +++++ + platforms/windows/AppPlatform_win32.hpp | 1 + + platforms/windows/main.cpp | 2 ++ + platforms/xdk360/AppPlatform_xdk360.cpp | 6 ++++ + platforms/xdk360/AppPlatform_xdk360.hpp | 1 + + platforms/xdk360/main.cpp | 2 ++ + source/client/app/AppPlatform.cpp | 4 +++ + source/client/app/AppPlatform.hpp | 2 ++ + source/client/options/Options.cpp | 12 +++++-- + source/client/options/Options.hpp | 12 ++++++- + source/client/renderer/LevelRenderer.cpp | 4 +-- + source/client/renderer/TileRenderer.cpp | 38 +++++++++++------------ + source/world/tile/TopSnowTile.cpp | 18 ++++++++++- + source/world/tile/TopSnowTile.hpp | 1 + + 19 files changed, 107 insertions(+), 35 deletions(-) + +diff --git a/platforms/android/AppPlatform_android.cpp b/platforms/android/AppPlatform_android.cpp +index ceb1f9872..a6cedc81b 100644 +--- a/platforms/android/AppPlatform_android.cpp ++++ b/platforms/android/AppPlatform_android.cpp +@@ -141,6 +141,15 @@ void AppPlatform_android::setScreenSize(int width, int height) + m_ScreenHeight = height; + } + ++void AppPlatform_android::setVSyncEnabled(bool enabled) ++{ ++ EGLDisplay display = eglGetCurrentDisplay(); ++ if (display == EGL_NO_DISPLAY) ++ return; ++ ++ glSwapInterval(display, enabled ? 1 : 0); ++} ++ + void AppPlatform_android::initAndroidApp(android_app* ptr) + { + m_app = ptr; +diff --git a/platforms/android/AppPlatform_android.hpp b/platforms/android/AppPlatform_android.hpp +index ce7f219a4..fecbbc46c 100644 +--- a/platforms/android/AppPlatform_android.hpp ++++ b/platforms/android/AppPlatform_android.hpp +@@ -55,6 +55,7 @@ class AppPlatform_android : public AppPlatform + void setExternalStoragePath(const std::string& path); + + AssetFile readAssetFile(const std::string&, bool) const override; ++ void setVSyncEnabled(bool enabled) override; + + private: + void changeKeyboardVisibility(bool bShown); +diff --git a/platforms/sdl/base/AppPlatform_sdl.cpp b/platforms/sdl/base/AppPlatform_sdl.cpp +index 30de0ac72..aae613798 100644 +--- a/platforms/sdl/base/AppPlatform_sdl.cpp ++++ b/platforms/sdl/base/AppPlatform_sdl.cpp +@@ -149,6 +149,13 @@ void AppPlatform_sdl::_setDefaultIcon() + _setIcon(data); + } + ++void AppPlatform_sdl::setVSyncEnabled(bool enabled) ++{ ++#if MCE_GFX_API_OGL ++ SDL_GL_SetSwapInterval(enabled ? 1 : 0); ++#endif ++} ++ + void AppPlatform_sdl::initSoundSystem() + { + if (m_pSoundSystem) +diff --git a/platforms/sdl/base/AppPlatform_sdl.hpp b/platforms/sdl/base/AppPlatform_sdl.hpp +index c93d4f4b2..d72a97b5b 100644 +--- a/platforms/sdl/base/AppPlatform_sdl.hpp ++++ b/platforms/sdl/base/AppPlatform_sdl.hpp +@@ -38,6 +38,7 @@ class AppPlatform_sdl : public AppPlatform + int getUserInputStatus() override; + void saveScreenshot(const std::string& fileName, int width, int height) override; + SoundSystem* getSoundSystem() const override { return m_pSoundSystem; } ++ void setVSyncEnabled(bool enabled) override; + + // Also add these to allow proper turning within the game. + void setMouseGrabbed(bool b) override; +diff --git a/platforms/sdl/sdl2/main.cpp b/platforms/sdl/sdl2/main.cpp +index fa228b636..0fc0713a4 100644 +--- a/platforms/sdl/sdl2/main.cpp ++++ b/platforms/sdl/sdl2/main.cpp +@@ -63,16 +63,11 @@ static void initGraphics() + exit(EXIT_FAILURE); + } + +- // Enable V-Sync +- // Not setting this explicitly results in undefined behavior +- if (SDL_GL_SetSwapInterval(-1) == -1) // Try adaptive ++ // Vsync is controlled through the AppPlatform, ++ // default to no vsync here, let platform set it when needed ++ if (SDL_GL_SetSwapInterval(0) == -1) + { +- LOG_W("Adaptive V-Sync is not supported on this platform. Falling back to standard V-Sync..."); +- // fallback to standard +- if (SDL_GL_SetSwapInterval(1) == -1) +- { +- LOG_W("Setting the swap interval for V-Sync is not supported on this platform!"); +- } ++ LOG_W("Setting the swap interval is not supported on this platform!"); + } + + if (!mce::Platform::OGL::InitBindings()) +@@ -430,6 +425,7 @@ int main(int argc, char *argv[]) + // Start MCPE + g_pAppPlatform = new UsedAppPlatform(storagePath, window); + g_pAppPlatform->m_externalStorageDir = storagePath; ++ g_pAppPlatform->setVSyncEnabled(true); + g_pApp = new NinecraftApp; + g_pApp->m_pPlatform = g_pAppPlatform; + g_pApp->init(); +diff --git a/platforms/windows/AppPlatform_win32.cpp b/platforms/windows/AppPlatform_win32.cpp +index f54c64812..674b5bf3b 100644 +--- a/platforms/windows/AppPlatform_win32.cpp ++++ b/platforms/windows/AppPlatform_win32.cpp +@@ -484,6 +484,13 @@ bool AppPlatform_win32::initGraphics(int width, int height) + return true; + } + ++void AppPlatform_win32::setVSyncEnabled(bool enabled) ++{ ++#if MCE_GFX_API_OGL ++ xglSwapIntervalEXT(enabled ? 1 : 0); ++#endif ++} ++ + void AppPlatform_win32::createWindowSizeDependentResources(const Vec2& logicalSize, const Vec2& compositionScale) + { + #if MCE_GFX_API_D3D9 +diff --git a/platforms/windows/AppPlatform_win32.hpp b/platforms/windows/AppPlatform_win32.hpp +index 1dc3bb7e0..d047243e9 100644 +--- a/platforms/windows/AppPlatform_win32.hpp ++++ b/platforms/windows/AppPlatform_win32.hpp +@@ -82,6 +82,7 @@ class AppPlatform_win32 : public AppPlatform + bool initGraphics(int width, int height); + void createWindowSizeDependentResources(const Vec2& logicalSize, const Vec2& compositionScale); + void swapBuffers(); ++ void setVSyncEnabled(bool enabled) override; + + static MouseButtonType GetMouseButtonType(UINT iMsg); + static bool GetMouseButtonState(UINT iMsg, WPARAM wParam); +diff --git a/platforms/windows/main.cpp b/platforms/windows/main.cpp +index 00f703ea1..b614ca405 100644 +--- a/platforms/windows/main.cpp ++++ b/platforms/windows/main.cpp +@@ -152,6 +152,8 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine + if (!g_AppPlatform.initGraphics(Minecraft::width, Minecraft::height)) + goto _cleanup; + ++ g_AppPlatform.setVSyncEnabled(true); ++ + g_pApp = new NinecraftApp; + g_pApp->m_pPlatform = &g_AppPlatform; + +diff --git a/platforms/xdk360/AppPlatform_xdk360.cpp b/platforms/xdk360/AppPlatform_xdk360.cpp +index 0cf1313db..33fea93f4 100644 +--- a/platforms/xdk360/AppPlatform_xdk360.cpp ++++ b/platforms/xdk360/AppPlatform_xdk360.cpp +@@ -319,6 +319,12 @@ const std::string& AppPlatform_xdk360::getKeyboardText() const + return m_keyboardText; + } + ++void AppPlatform_xdk360::setVSyncEnabled(bool enabled) ++{ ++ // XDK360 V-Sync control would go here ++ // For Xbox 360, this is typically handled by the D3D device settings ++} ++ + bool AppPlatform_xdk360::initGraphics(unsigned int width, unsigned int height) + { + m_bHasGraphics = true; +diff --git a/platforms/xdk360/AppPlatform_xdk360.hpp b/platforms/xdk360/AppPlatform_xdk360.hpp +index d1409aaff..d2934aa71 100644 +--- a/platforms/xdk360/AppPlatform_xdk360.hpp ++++ b/platforms/xdk360/AppPlatform_xdk360.hpp +@@ -52,6 +52,7 @@ class AppPlatform_xdk360 : public AppPlatform + bool initGraphics(unsigned int width, unsigned int height); + void createWindowSizeDependentResources(unsigned int width, unsigned int height); + void swapBuffers(); ++ void setVSyncEnabled(bool enabled) override; + + private: + int m_ScreenWidth; +diff --git a/platforms/xdk360/main.cpp b/platforms/xdk360/main.cpp +index 2af6b639a..06d09f0b7 100644 +--- a/platforms/xdk360/main.cpp ++++ b/platforms/xdk360/main.cpp +@@ -45,6 +45,8 @@ void __cdecl main() + if (!g_AppPlatform.initGraphics(Minecraft::width, Minecraft::height)) + goto _cleanup; + ++ g_AppPlatform.setVSyncEnabled(true); ++ + g_pApp = new NinecraftApp; + g_pApp->m_pPlatform = &g_AppPlatform; + g_AppPlatform.m_externalStorageDir = "savedrive:"; +diff --git a/source/client/app/AppPlatform.cpp b/source/client/app/AppPlatform.cpp +index ef28f0f4b..74713c1ca 100644 +--- a/source/client/app/AppPlatform.cpp ++++ b/source/client/app/AppPlatform.cpp +@@ -320,6 +320,10 @@ std::string AppPlatform::getExternalStoragePath(const std::string& path) const + return m_externalStorageDir + C_HOME_PATH + path; + } + ++void AppPlatform::setVSyncEnabled(bool enabled) ++{ ++} ++ + bool AppPlatform::hasAssetFile(const std::string& path) const + { + return isRegularFile(path.c_str()); +diff --git a/source/client/app/AppPlatform.hpp b/source/client/app/AppPlatform.hpp +index 9cf1b6cc6..26d74bbd6 100644 +--- a/source/client/app/AppPlatform.hpp ++++ b/source/client/app/AppPlatform.hpp +@@ -107,6 +107,8 @@ class AppPlatform + virtual void vibrate(int milliSeconds); + virtual bool getRecenterMouseEveryTick(); + virtual std::string getClipboardText(); ++ // Graphics settings ++ virtual void setVSyncEnabled(bool enabled); + + void _fireLowMemory(); + void _fireAppSuspended(); +diff --git a/source/client/options/Options.cpp b/source/client/options/Options.cpp +index 546f53a58..c6d24cb34 100644 +--- a/source/client/options/Options.cpp ++++ b/source/client/options/Options.cpp +@@ -98,7 +98,7 @@ Options::Options(Minecraft* mc, const std::string& folderPath) : + //, m_limitFramerate("gfx_fpslimit", "options.framerateLimit", 0, ValuesBuilder().add(performance.max").add("performance.balanced").add("performance.powersaver")) + //, m_bMipmaps("gfx_mipmaps", "options.mipmaps") + //, m_moreWorldOptions("misc_moreworldoptions", "options.moreWorldOptions", true) +- //, m_vSync("enableVsync", "options.enableVsync") ++ , m_vSync("enableVsync", "options.enableVsync") + { + add(m_musicVolume); + add(m_masterVolume); +@@ -127,10 +127,11 @@ Options::Options(Minecraft* mc, const std::string& folderPath) : + add(m_debugText); + add(m_lang); + add(m_bUseController); ++ add(m_hudSize); + add(m_uiTheme); + add(m_logoType); +- add(m_hudSize); + add(m_classicCrafting); ++ add(m_vSync); + _initDefaultValues(); + if (folderPath.empty()) return; + m_filePath = folderPath + "/options.txt"; +@@ -812,4 +813,9 @@ void ControllerOption::apply() + // For now, I just wanted to be able to switch to controller input on mobile devices. + if (m_pMinecraft && m_pMinecraft->m_pInputHolder) + m_pMinecraft->reloadInput(); +-} +\ No newline at end of file ++} ++ ++void VsyncOption::apply() ++{ ++ m_pMinecraft->platform()->setVSyncEnabled(get()); ++} +diff --git a/source/client/options/Options.hpp b/source/client/options/Options.hpp +index 1fe458dae..72cce0703 100644 +--- a/source/client/options/Options.hpp ++++ b/source/client/options/Options.hpp +@@ -222,6 +222,14 @@ class ControllerOption : public BoolOption + void apply() override; + }; + ++class VsyncOption : public BoolOption ++{ ++public: ++ VsyncOption(const std::string& key, const std::string& name, bool initial = true) : BoolOption(key, name, initial) {} ++ ++ void apply() override; ++}; ++ + class FancyGraphicsOption : public GraphicsOption + { + public: +@@ -326,7 +334,7 @@ class UIThemeOption : public ValuesOption + class HUDSizeOption : public MinMaxOption + { + public: +- HUDSizeOption(const std::string& key, const std::string& name, int initial) : MinMaxOption(key, name, initial, HUD_SIZE_1, HUD_SIZE_3 + 1) ++ HUDSizeOption(const std::string& key, const std::string& name, int initial) : MinMaxOption(key, name, initial, HUD_SIZE_1, HUD_SIZE_MAX + 1) + { + } + +@@ -438,6 +446,7 @@ class Options + LogoTypeOption m_logoType; + HUDSizeOption m_hudSize; + BoolOption m_classicCrafting; ++ VsyncOption m_vSync; + ResourcePackStack m_resourcePacks; + }; + +@@ -483,6 +492,7 @@ class Options + OPTION(m_viewBobbing); \ + OPTION(m_anaglyphs); \ + OPTION(m_blockOutlines); \ ++ OPTION(m_vSync); \ + OPTION(m_fancyGrass); \ + OPTION(m_biomeColors); \ + OPTION(m_dynamicHand); \ +diff --git a/source/client/renderer/LevelRenderer.cpp b/source/client/renderer/LevelRenderer.cpp +index e30b75164..ef388e89f 100644 +--- a/source/client/renderer/LevelRenderer.cpp ++++ b/source/client/renderer/LevelRenderer.cpp +@@ -1137,7 +1137,7 @@ void LevelRenderer::tick() + typedef std::vector ChunkVector; + typedef ChunkVector::iterator ChunkVectorIterator; + +-bool LevelRenderer::updateDirtyChunks(const Entity& camera, bool b) ++bool LevelRenderer::updateDirtyChunks(const Entity& camera, bool force) + { + constexpr int C_MAX = 3; + DirtyChunkSorter dcs(camera); +@@ -1148,7 +1148,7 @@ bool LevelRenderer::updateDirtyChunks(const Entity& camera, bool b) + for (size_t i = 0; i < pendingChunkSize; i++) + { + Chunk* pChunk = m_dirtyChunks[i]; +- if (!b) ++ if (!force) + { + if (pChunk->distanceToSqr(camera) > 1024.0f) + { +diff --git a/source/client/renderer/TileRenderer.cpp b/source/client/renderer/TileRenderer.cpp +index ed07fc37b..026c42d68 100644 +--- a/source/client/renderer/TileRenderer.cpp ++++ b/source/client/renderer/TileRenderer.cpp +@@ -204,7 +204,7 @@ void TileRenderer::renderEast(Tile* tile, const Vec3& pos, int texture) + texV_d = C_RATIO * (texY + aabb.max.y * 16.0f - 0.01f); + } + +- Tesselator& t = Tesselator::instance; ++ Tesselator& t = m_tessellator; + + if (m_ambientOcclusion) + { +@@ -281,7 +281,7 @@ void TileRenderer::renderWest(Tile* tile, const Vec3& pos, int texture) + texV_d = C_RATIO * (texY + aabb.max.y * 16.0f - 0.01f); + } + +- Tesselator& t = Tesselator::instance; ++ Tesselator& t = m_tessellator; + + if (m_ambientOcclusion) + { +@@ -358,7 +358,7 @@ void TileRenderer::renderSouth(Tile* tile, const Vec3& pos, int texture) + texV_d = C_RATIO * (texY + aabb.max.y * 16.0f - 0.01f); + } + +- Tesselator& t = Tesselator::instance; ++ Tesselator& t = m_tessellator; + + if (m_ambientOcclusion) + { +@@ -435,7 +435,7 @@ void TileRenderer::renderNorth(Tile* tile, const Vec3& pos, int texture) + texV_d = C_RATIO * (texY + aabb.max.y * 16.0f - 0.01f); + } + +- Tesselator& t = Tesselator::instance; ++ Tesselator& t = m_tessellator; + + if (m_ambientOcclusion) + { +@@ -506,7 +506,7 @@ void TileRenderer::renderFaceDown(Tile* tile, const Vec3& pos, int texture) + texV_2 = C_RATIO * (texY + 15.99f); + } + +- Tesselator& t = Tesselator::instance; ++ Tesselator& t = m_tessellator; + + if (m_ambientOcclusion) + { +@@ -577,7 +577,7 @@ void TileRenderer::renderFaceUp(Tile* tile, const Vec3& pos, int texture) + texV_2 = C_RATIO * (texY + 15.99f); + } + +- Tesselator& t = Tesselator::instance; ++ Tesselator& t = m_tessellator; + + if (m_ambientOcclusion) + { +@@ -641,7 +641,7 @@ void TileRenderer::tesselateCrossTexture(const FullTile& tile, const Vec3& pos, + float x1 = cenX - 0.45f, x2 = cenX + 0.45f; + float z1 = cenZ - 0.45f, z2 = cenZ + 0.45f; + +- Tesselator& t = Tesselator::instance; ++ Tesselator& t = m_tessellator; + // face 1 + t.vertexUV(x1, newY + 1, z1, texU_l, texV_u); + t.vertexUV(x1, newY + 0, z1, texU_l, texV_d); +@@ -693,7 +693,7 @@ void TileRenderer::tesselateRowTexture(Tile* tile, int data, const Vec3& pos) + float x0 = pos.x + 0.25f, x1 = pos.x + 0.75f; + float z0 = pos.z, z1 = pos.z + 1.0f; + +- Tesselator& t = Tesselator::instance; ++ Tesselator& t = m_tessellator; + t.vertexUV(x0, pos.y + 1.0, z0, u0, v0); + t.vertexUV(x0, pos.y + 0.0, z0, u0, v1); + t.vertexUV(x0, pos.y + 0.0, z1, u1, v1); +@@ -739,7 +739,7 @@ bool TileRenderer::tesselateBlockInWorld(Tile* tile, const TilePos& pos, float r + if (tile == Tile::grass) + r = g = b = 1.0f; + +- Tesselator& t = Tesselator::instance; ++ Tesselator& t = m_tessellator; + + float fLightHere = tile->getBrightness(m_pTileSource, pos); + bool bDrewAnything = false; +@@ -871,7 +871,7 @@ bool TileRenderer::tesselateBlockInWorld(Tile* tile, const TilePos& pos) + + bool TileRenderer::tesselateCrossInWorld(Tile* tile, const TilePos& pos) + { +- Tesselator& t = Tesselator::instance; ++ Tesselator& t = m_tessellator; + + float bright = tile->getBrightness(m_pTileSource, pos); + int color = getTileColor(tile, pos); +@@ -888,7 +888,7 @@ bool TileRenderer::tesselateCrossInWorld(Tile* tile, const TilePos& pos) + + bool TileRenderer::tesselateRowInWorld(Tile* tile, const TilePos& pos) + { +- Tesselator& t = Tesselator::instance; ++ Tesselator& t = m_tessellator; + + Color color = getTileColor(tile, pos); + color.a = 1.0f; +@@ -906,7 +906,7 @@ bool TileRenderer::tesselateWaterInWorld(Tile* tile1, const TilePos& pos) + LiquidTile* tile = (LiquidTile*)tile1; + bool bRenderFaceDown, bRenderFaceUp, bRenderSides[4]; + +- Tesselator& t = Tesselator::instance; ++ Tesselator& t = m_tessellator; + + bRenderFaceDown = tile->shouldRenderFace(m_pTileSource, pos.above(), Facing::UP); + bRenderFaceUp = tile->shouldRenderFace(m_pTileSource, pos.below(), Facing::DOWN); +@@ -1217,7 +1217,7 @@ bool TileRenderer::tesselateFenceInWorld(Tile* tile, const TilePos& pos) + + bool TileRenderer::tesselateDoorInWorld(Tile* tile, const TilePos& pos) + { +- Tesselator& t = Tesselator::instance; ++ Tesselator& t = m_tessellator; + float fBrightHere = tile->getBrightness(m_pTileSource, pos), fBright; + int texture; + +@@ -1298,7 +1298,7 @@ void TileRenderer::tesselateTorch(Tile* tile, const Vec3& pos, float a, float b) + float x2 = x1 + (float)(a * C_TOP_SKEW_RATIO); + float z2 = z1 + (float)(b * C_TOP_SKEW_RATIO); + +- Tesselator& t = Tesselator::instance; ++ Tesselator& t = m_tessellator; + + // Top side (flame) + float x_1 = x2 - C_ONE_PIXEL; +@@ -1371,7 +1371,7 @@ bool TileRenderer::tesselateTorchInWorld(Tile* tile, const TilePos& pos) + if (Tile::lightEmission[tile->m_ID] > 0) + bright = 1.0f; + +- Tesselator& t = Tesselator::instance; ++ Tesselator& t = m_tessellator; + t.color(bright, bright, bright); + + switch (data) +@@ -1400,7 +1400,7 @@ bool TileRenderer::tesselateLadderInWorld(Tile* tile, const TilePos& pos) + { + constexpr float C_RATIO = 1.0f / 256.0f; + +- Tesselator& t = Tesselator::instance; ++ Tesselator& t = m_tessellator; + + int texture = tile->getTexture(Facing::DOWN); + +@@ -1453,7 +1453,7 @@ bool TileRenderer::tesselateFireInWorld(Tile* tile, const TilePos& pos) + { + constexpr float C_RATIO = 1.0f / 256.0f; + +- Tesselator& t = Tesselator::instance; ++ Tesselator& t = m_tessellator; + + int texture = tile->getTexture(Facing::DOWN); + float bright = tile->getBrightness(m_pTileSource, pos); +@@ -2691,7 +2691,7 @@ bool TileRenderer::tesselateBlockInWorldWithAmbienceOcclusion(Tile* a2, const Ti + + void TileRenderer::renderTile(const FullTile& tile, const mce::MaterialPtr& material, float bright, bool preshade) + { +- Tesselator& t = Tesselator::instance; ++ Tesselator& t = m_tessellator; + Tile* tileType = tile.getType(); + + #ifndef ENH_SHADE_HELD_TILES +@@ -2926,7 +2926,7 @@ bool TileRenderer::tesselateBlockInWorldWithAmbienceOcclusionV2(Tile* tile, cons + if (tile == Tile::grass) + r = g = b = 1.0f; + +- //Tesselator& t = Tesselator::instance; ++ //Tesselator& t = m_tessellator; + + //float fLightHere = tile->getBrightness(m_pTileSource, pos); + +diff --git a/source/world/tile/TopSnowTile.cpp b/source/world/tile/TopSnowTile.cpp +index b974b308b..9bec5bc70 100644 +--- a/source/world/tile/TopSnowTile.cpp ++++ b/source/world/tile/TopSnowTile.cpp +@@ -32,7 +32,7 @@ bool TopSnowTile::isSolidRender() const + + int TopSnowTile::getResource(TileData data, Random* random) const + { +- return 0; ++ return Item::snowBall->m_itemID; + } + + int TopSnowTile::getResourceCount(Random* random) const +@@ -84,3 +84,19 @@ void TopSnowTile::tick(Level* level, const TilePos& pos, Random* random) + level->setTile(pos, TILE_AIR); + } + } ++ ++void TopSnowTile::playerDestroy(Level* level, Player* player, const TilePos& pos, TileData data) ++{ ++ float dispersion = 0.7f; ++ ++ Vec3 offset ( ++ (level->m_random.nextFloat() * dispersion) + (1.0f - dispersion) * 0.5f, ++ (level->m_random.nextFloat() * dispersion) + (1.0f - dispersion) * 0.5f, ++ (level->m_random.nextFloat() * dispersion) + (1.0f - dispersion) * 0.5f ++ ); ++ ++ ItemEntity* pItemEntity = new ItemEntity(level, Vec3(pos) + offset, ItemStack(getResource(data, &level->m_random), 1, 0)); ++ pItemEntity->m_throwTime = 10; ++ level->addEntity(pItemEntity); ++ level->setTile(pos, TILE_AIR); ++} +diff --git a/source/world/tile/TopSnowTile.hpp b/source/world/tile/TopSnowTile.hpp +index 82af8e659..cafd89ea2 100644 +--- a/source/world/tile/TopSnowTile.hpp ++++ b/source/world/tile/TopSnowTile.hpp +@@ -24,6 +24,7 @@ class TopSnowTile : public Tile + void neighborChanged(Level*, const TilePos& pos, TileID tile) override; + bool shouldRenderFace(const LevelSource*, const TilePos& pos, Facing::Name face) const override; + void tick(Level*, const TilePos& pos, Random*) override; ++ virtual void playerDestroy(Level*, Player*, const TilePos& pos, TileData data); + + bool checkCanSurvive(Level*, const TilePos& pos); + }; + +From 837b7412c8900d3c6cb3c1151bfa81efb0a360d9 Mon Sep 17 00:00:00 2001 +From: hellosammu +Date: Tue, 17 Feb 2026 13:58:15 -0500 +Subject: [PATCH 09/22] drop sand + +--- + platforms/xdk360/AppPlatform_xdk360.cpp | 4 ++-- + source/client/options/Options.hpp | 2 +- + source/world/entity/FallingTile.cpp | 5 ++++- + 3 files changed, 7 insertions(+), 4 deletions(-) + +diff --git a/platforms/xdk360/AppPlatform_xdk360.cpp b/platforms/xdk360/AppPlatform_xdk360.cpp +index 33fea93f4..f9fe54f86 100644 +--- a/platforms/xdk360/AppPlatform_xdk360.cpp ++++ b/platforms/xdk360/AppPlatform_xdk360.cpp +@@ -321,8 +321,8 @@ const std::string& AppPlatform_xdk360::getKeyboardText() const + + void AppPlatform_xdk360::setVSyncEnabled(bool enabled) + { +- // XDK360 V-Sync control would go here +- // For Xbox 360, this is typically handled by the D3D device settings ++ // You have to reset/recreate the D3D device to change the vsync setting ++ // @TODO: Someone with a windows machine or xbox please do and test this. + } + + bool AppPlatform_xdk360::initGraphics(unsigned int width, unsigned int height) +diff --git a/source/client/options/Options.hpp b/source/client/options/Options.hpp +index 72cce0703..b15eb695b 100644 +--- a/source/client/options/Options.hpp ++++ b/source/client/options/Options.hpp +@@ -334,7 +334,7 @@ class UIThemeOption : public ValuesOption + class HUDSizeOption : public MinMaxOption + { + public: +- HUDSizeOption(const std::string& key, const std::string& name, int initial) : MinMaxOption(key, name, initial, HUD_SIZE_1, HUD_SIZE_MAX + 1) ++ HUDSizeOption(const std::string& key, const std::string& name, int initial) : MinMaxOption(key, name, initial, HUD_SIZE_1, HUD_SIZE_3 + 1) + { + } + +diff --git a/source/world/entity/FallingTile.cpp b/source/world/entity/FallingTile.cpp +index 8fb89b6a9..23c028573 100644 +--- a/source/world/entity/FallingTile.cpp ++++ b/source/world/entity/FallingTile.cpp +@@ -66,7 +66,10 @@ void FallingTile::tick() + if (!m_bOnGround) + { + if (field_E0 > 100 && !m_pLevel->m_bIsClientSide) ++ { ++ spawnAtLocation(m_id, 1); + remove(); ++ } + + return; + } +@@ -81,7 +84,7 @@ void FallingTile::tick() + } + else + { +- // @TODO: spawn resources? ++ spawnAtLocation(m_id, 1); + } + } + + +From da33efab2a2597efa36c6dd445b51f8c488d4e21 Mon Sep 17 00:00:00 2001 +From: hellosammu +Date: Tue, 17 Feb 2026 19:43:33 -0500 +Subject: [PATCH 10/22] containers shouldnt look stupid + +--- + platforms/android/AppPlatform_android.cpp | 5 +++++ + platforms/android/AppPlatform_android.hpp | 1 + + platforms/sdl/base/AppPlatform_sdl.cpp | 9 +++++++++ + platforms/sdl/base/AppPlatform_sdl.hpp | 1 + + platforms/windows/AppPlatform_win32.cpp | 9 +++++++++ + platforms/windows/AppPlatform_win32.hpp | 1 + + platforms/xdk360/AppPlatform_xdk360.cpp | 5 +++++ + platforms/xdk360/AppPlatform_xdk360.hpp | 1 + + source/client/app/AppPlatform.cpp | 5 +++++ + source/client/app/AppPlatform.hpp | 1 + + source/client/gui/screens/inventory/ChestScreen.cpp | 7 +++---- + source/client/options/Options.cpp | 5 ++++- + source/world/inventory/ChestMenu.cpp | 6 ++++-- + 13 files changed, 49 insertions(+), 7 deletions(-) + +diff --git a/platforms/android/AppPlatform_android.cpp b/platforms/android/AppPlatform_android.cpp +index a6cedc81b..a2aacf87c 100644 +--- a/platforms/android/AppPlatform_android.cpp ++++ b/platforms/android/AppPlatform_android.cpp +@@ -150,6 +150,11 @@ void AppPlatform_android::setVSyncEnabled(bool enabled) + glSwapInterval(display, enabled ? 1 : 0); + } + ++bool AppPlatform_android::isVsyncSwitchable() const ++{ ++ return eglGetCurrentDisplay() != EGL_NO_DISPLAY; ++} ++ + void AppPlatform_android::initAndroidApp(android_app* ptr) + { + m_app = ptr; +diff --git a/platforms/android/AppPlatform_android.hpp b/platforms/android/AppPlatform_android.hpp +index fecbbc46c..b18433f5c 100644 +--- a/platforms/android/AppPlatform_android.hpp ++++ b/platforms/android/AppPlatform_android.hpp +@@ -56,6 +56,7 @@ class AppPlatform_android : public AppPlatform + + AssetFile readAssetFile(const std::string&, bool) const override; + void setVSyncEnabled(bool enabled) override; ++ bool isVsyncSwitchable() const override; + + private: + void changeKeyboardVisibility(bool bShown); +diff --git a/platforms/sdl/base/AppPlatform_sdl.cpp b/platforms/sdl/base/AppPlatform_sdl.cpp +index aae613798..1d21c36c8 100644 +--- a/platforms/sdl/base/AppPlatform_sdl.cpp ++++ b/platforms/sdl/base/AppPlatform_sdl.cpp +@@ -156,6 +156,15 @@ void AppPlatform_sdl::setVSyncEnabled(bool enabled) + #endif + } + ++bool AppPlatform_sdl::isVsyncSwitchable() const ++{ ++#if MCE_GFX_API_OGL ++ return true; ++#else ++ return false; ++#endif ++} ++ + void AppPlatform_sdl::initSoundSystem() + { + if (m_pSoundSystem) +diff --git a/platforms/sdl/base/AppPlatform_sdl.hpp b/platforms/sdl/base/AppPlatform_sdl.hpp +index d72a97b5b..976668744 100644 +--- a/platforms/sdl/base/AppPlatform_sdl.hpp ++++ b/platforms/sdl/base/AppPlatform_sdl.hpp +@@ -39,6 +39,7 @@ class AppPlatform_sdl : public AppPlatform + void saveScreenshot(const std::string& fileName, int width, int height) override; + SoundSystem* getSoundSystem() const override { return m_pSoundSystem; } + void setVSyncEnabled(bool enabled) override; ++ bool isVsyncSwitchable() const override; + + // Also add these to allow proper turning within the game. + void setMouseGrabbed(bool b) override; +diff --git a/platforms/windows/AppPlatform_win32.cpp b/platforms/windows/AppPlatform_win32.cpp +index 674b5bf3b..b177fef9a 100644 +--- a/platforms/windows/AppPlatform_win32.cpp ++++ b/platforms/windows/AppPlatform_win32.cpp +@@ -491,6 +491,15 @@ void AppPlatform_win32::setVSyncEnabled(bool enabled) + #endif + } + ++bool AppPlatform_win32::isVsyncSwitchable() const ++{ ++#if MCE_GFX_API_OGL ++ return true; ++#else ++ return false; ++#endif ++} ++ + void AppPlatform_win32::createWindowSizeDependentResources(const Vec2& logicalSize, const Vec2& compositionScale) + { + #if MCE_GFX_API_D3D9 +diff --git a/platforms/windows/AppPlatform_win32.hpp b/platforms/windows/AppPlatform_win32.hpp +index d047243e9..bac0f7eda 100644 +--- a/platforms/windows/AppPlatform_win32.hpp ++++ b/platforms/windows/AppPlatform_win32.hpp +@@ -83,6 +83,7 @@ class AppPlatform_win32 : public AppPlatform + void createWindowSizeDependentResources(const Vec2& logicalSize, const Vec2& compositionScale); + void swapBuffers(); + void setVSyncEnabled(bool enabled) override; ++ bool isVsyncSwitchable() const override; + + static MouseButtonType GetMouseButtonType(UINT iMsg); + static bool GetMouseButtonState(UINT iMsg, WPARAM wParam); +diff --git a/platforms/xdk360/AppPlatform_xdk360.cpp b/platforms/xdk360/AppPlatform_xdk360.cpp +index f9fe54f86..8b4ec16a6 100644 +--- a/platforms/xdk360/AppPlatform_xdk360.cpp ++++ b/platforms/xdk360/AppPlatform_xdk360.cpp +@@ -325,6 +325,11 @@ void AppPlatform_xdk360::setVSyncEnabled(bool enabled) + // @TODO: Someone with a windows machine or xbox please do and test this. + } + ++bool AppPlatform_xdk360::isVsyncSwitchable() const ++{ ++ return false; ++} ++ + bool AppPlatform_xdk360::initGraphics(unsigned int width, unsigned int height) + { + m_bHasGraphics = true; +diff --git a/platforms/xdk360/AppPlatform_xdk360.hpp b/platforms/xdk360/AppPlatform_xdk360.hpp +index d2934aa71..d1fb3b859 100644 +--- a/platforms/xdk360/AppPlatform_xdk360.hpp ++++ b/platforms/xdk360/AppPlatform_xdk360.hpp +@@ -53,6 +53,7 @@ class AppPlatform_xdk360 : public AppPlatform + void createWindowSizeDependentResources(unsigned int width, unsigned int height); + void swapBuffers(); + void setVSyncEnabled(bool enabled) override; ++ bool isVsyncSwitchable() const override; + + private: + int m_ScreenWidth; +diff --git a/source/client/app/AppPlatform.cpp b/source/client/app/AppPlatform.cpp +index 74713c1ca..7eba59212 100644 +--- a/source/client/app/AppPlatform.cpp ++++ b/source/client/app/AppPlatform.cpp +@@ -324,6 +324,11 @@ void AppPlatform::setVSyncEnabled(bool enabled) + { + } + ++bool AppPlatform::isVsyncSwitchable() const ++{ ++ return false; ++} ++ + bool AppPlatform::hasAssetFile(const std::string& path) const + { + return isRegularFile(path.c_str()); +diff --git a/source/client/app/AppPlatform.hpp b/source/client/app/AppPlatform.hpp +index 26d74bbd6..b7852bd21 100644 +--- a/source/client/app/AppPlatform.hpp ++++ b/source/client/app/AppPlatform.hpp +@@ -109,6 +109,7 @@ class AppPlatform + virtual std::string getClipboardText(); + // Graphics settings + virtual void setVSyncEnabled(bool enabled); ++ virtual bool isVsyncSwitchable() const; + + void _fireLowMemory(); + void _fireAppSuspended(); +diff --git a/source/client/gui/screens/inventory/ChestScreen.cpp b/source/client/gui/screens/inventory/ChestScreen.cpp +index 312618638..2df77fff9 100644 +--- a/source/client/gui/screens/inventory/ChestScreen.cpp ++++ b/source/client/gui/screens/inventory/ChestScreen.cpp +@@ -31,15 +31,14 @@ SlotDisplay ChestScreen::_createSlotDisplay(const Slot& slot) + { + constexpr int slotSize = 18; + int rows = m_pContainer->getContainerSize() / 9; +- int verticalOffset = (rows - 4) * slotSize; + switch (slot.m_group) + { + case Slot::CONTAINER: +- return SlotDisplay(8 + (slot.m_slot % 9) * slotSize, 18 + verticalOffset + ((slot.m_slot / 9) - 1) * slotSize, slotSize); ++ return SlotDisplay(8 + (slot.m_slot % 9) * slotSize, 18 + (slot.m_slot / 9) * slotSize, slotSize); + case Slot::INVENTORY: +- return SlotDisplay(8 + (slot.m_slot % 9) * slotSize, 103 + verticalOffset + ((slot.m_slot / 9) - 1) * slotSize, slotSize); ++ return SlotDisplay(8 + (slot.m_slot % 9) * slotSize, (rows * 18) + 13 + (slot.m_slot / 9) * slotSize, slotSize); + case Slot::HOTBAR: +- return SlotDisplay(8 + (slot.m_slot % 9) * slotSize, 142, slotSize); ++ return SlotDisplay(8 + (slot.m_slot % 9) * slotSize, 89 + rows * slotSize, slotSize); + default: + return SlotDisplay(); + } +diff --git a/source/client/options/Options.cpp b/source/client/options/Options.cpp +index c6d24cb34..f99e36c2f 100644 +--- a/source/client/options/Options.cpp ++++ b/source/client/options/Options.cpp +@@ -98,7 +98,7 @@ Options::Options(Minecraft* mc, const std::string& folderPath) : + //, m_limitFramerate("gfx_fpslimit", "options.framerateLimit", 0, ValuesBuilder().add(performance.max").add("performance.balanced").add("performance.powersaver")) + //, m_bMipmaps("gfx_mipmaps", "options.mipmaps") + //, m_moreWorldOptions("misc_moreworldoptions", "options.moreWorldOptions", true) +- , m_vSync("enableVsync", "options.enableVsync") ++ , m_vSync("enableVsync", "options.enableVsync", true) + { + add(m_musicVolume); + add(m_masterVolume); +@@ -676,6 +676,9 @@ void Options::initResourceDependentOptions() + + if (!Screen::isMenuPanoramaAvailable()) + m_menuPanorama.set(false); ++ ++ if (!m_pMinecraft->platform()->isVsyncSwitchable()) ++ m_vSync.set(false); + } + + const std::string& OptionEntry::getDisplayName() const +diff --git a/source/world/inventory/ChestMenu.cpp b/source/world/inventory/ChestMenu.cpp +index 95a78894f..7766dd63c 100644 +--- a/source/world/inventory/ChestMenu.cpp ++++ b/source/world/inventory/ChestMenu.cpp +@@ -7,16 +7,18 @@ ChestMenu::ChestMenu(Container* inventory, Container* container) + { + int rows = m_pContainer->getContainerSize() / 9; + ++ // Chest slots + for (int row = 0; row < rows; ++row) + { + for (int col = 0; col < 9; ++col) +- addSlot(new Slot(m_pContainer, col + row * 9)); ++ addSlot(new Slot(m_pContainer, col + row * 9, Slot::CONTAINER)); + } + ++ // Inventory slots + for (int row = 0; row < 3; ++row) + { + for (int col = 0; col < 9; ++col) +- addSlot(new Slot(inventory, col + row * 9 + 9)); ++ addSlot(new Slot(inventory, col + row * 9 + 9, Slot::INVENTORY)); + } + + for (int col = 0; col < 9; ++col) + +From fa895e26b5439b840badc5477853909e003ee321 Mon Sep 17 00:00:00 2001 +From: hellosammu +Date: Tue, 17 Feb 2026 20:15:46 -0500 +Subject: [PATCH 11/22] naming conventions, not done yet + +--- + source/client/renderer/Chunk.cpp | 30 ++++++++++----------- + source/client/renderer/Chunk.hpp | 6 ++--- + source/client/renderer/LevelRenderer.cpp | 2 +- + source/client/renderer/LevelRenderer.hpp | 2 +- + source/world/entity/FallingTile.cpp | 4 +-- + source/world/tile/entity/TileEntityType.cpp | 12 ++++----- + source/world/tile/entity/TileEntityType.hpp | 10 +++---- + 7 files changed, 33 insertions(+), 33 deletions(-) + +diff --git a/source/client/renderer/Chunk.cpp b/source/client/renderer/Chunk.cpp +index ec7d6db5e..aed0ab5c7 100644 +--- a/source/client/renderer/Chunk.cpp ++++ b/source/client/renderer/Chunk.cpp +@@ -133,8 +133,8 @@ void Chunk::rebuild() + + LevelChunk::touchedSky = false; + +- std::set tmpSet(m_renderableTileEntities.begin(), m_renderableTileEntities.end()); +- m_renderableTileEntities.clear(); ++ std::set tmpSet(m_tileEntities.begin(), m_tileEntities.end()); ++ m_tileEntities.clear(); + + for (int i = Tile::RENDER_LAYERS_MIN; i <= Tile::RENDER_LAYERS_MAX; i++) + { +@@ -178,7 +178,7 @@ void Chunk::rebuild() + // @TODO: ADD TILE ENTITY RENDER DISPATCHER + TileEntity* et = region.getTileEntity(tp); + if (TileEntityRenderDispatcher::getInstance()->hasRenderer(et)) +- m_renderableTileEntities.push_back(et); ++ m_tileEntities.push_back(et); + */ + } + +@@ -214,8 +214,8 @@ void Chunk::rebuild() + break; + } + +- std::set newSet(m_renderableTileEntities.begin(), m_renderableTileEntities.end()); +- std::vector toAdd, toRemove; ++ std::set newSet(m_tileEntities.begin(), m_tileEntities.end()); ++ TileEntityVector toAdd, toRemove; + + std::set_difference( + newSet.begin(), newSet.end(), +@@ -230,29 +230,29 @@ void Chunk::rebuild() + ); + + // Add +- for (std::vector::iterator it = toAdd.begin(); it != toAdd.end(); ++it) ++ for (TileEntityVector::iterator it = toAdd.begin(); it != toAdd.end(); ++it) + { +- m_globalRenderableTileEntities.push_back(*it); ++ m_globalTileEntities.push_back(*it); + } + + // Remove +- for (std::vector::iterator it = toRemove.begin(); it != toRemove.end(); ++it) ++ for (TileEntityVector::iterator it = toRemove.begin(); it != toRemove.end(); ++it) + { +- std::vector::iterator f = +- std::find(m_globalRenderableTileEntities.begin(), +- m_globalRenderableTileEntities.end(), ++ TileEntityVector::iterator f = ++ std::find(m_globalTileEntities.begin(), ++ m_globalTileEntities.end(), + *it); + +- if (f != m_globalRenderableTileEntities.end()) +- m_globalRenderableTileEntities.erase(f); ++ if (f != m_globalTileEntities.end()) ++ m_globalTileEntities.erase(f); + } + + field_54 = LevelChunk::touchedSky; + m_bCompiled = true; + } + +-Chunk::Chunk(Level* level, std::vector& renderableTileEntities, const TilePos& pos, int size, int lists) +- : m_globalRenderableTileEntities(renderableTileEntities) ++Chunk::Chunk(Level* level, TileEntityVector& tileEntities, const TilePos& pos, int size, int lists) ++ : m_globalTileEntities(tileEntities) + { + m_bOcclusionVisible = true; + m_bOcclusionQuerying = false; +diff --git a/source/client/renderer/Chunk.hpp b/source/client/renderer/Chunk.hpp +index aea9643c1..58d6d4793 100644 +--- a/source/client/renderer/Chunk.hpp ++++ b/source/client/renderer/Chunk.hpp +@@ -20,7 +20,7 @@ class TileEntity; + class Chunk + { + public: +- Chunk(Level*, std::vector& renderableTileEntities, const TilePos& pos, int, int); ++ Chunk(Level*, std::vector& tileEntities, const TilePos& pos, int, int); + + public: + float distanceToSqr(const Entity& entity) const; +@@ -43,8 +43,8 @@ class Chunk + + public: + Level* m_pLevel; +- std::vector& m_globalRenderableTileEntities; +- std::vector m_renderableTileEntities; ++ std::vector& m_globalTileEntities; ++ std::vector m_tileEntities; + TilePos m_pos; + TilePos m_posS; + bool m_empty[Tile::RENDER_LAYERS_COUNT]; +diff --git a/source/client/renderer/LevelRenderer.cpp b/source/client/renderer/LevelRenderer.cpp +index ef388e89f..a8dc66049 100644 +--- a/source/client/renderer/LevelRenderer.cpp ++++ b/source/client/renderer/LevelRenderer.cpp +@@ -1608,7 +1608,7 @@ void LevelRenderer::renderEntities(Vec3 pos, Culler* culler, float f) + + /* + // @TODO: TileEntityRenderDispatcher +- for (std::vector::const_iterator it = m_renderableTileEntities.begin(); ++ for (TileEntityVector::const_iterator it = m_renderableTileEntities.begin(); + it != m_renderableTileEntities.end(); ++it) + { + TileEntity* tileEntity = *it; +diff --git a/source/client/renderer/LevelRenderer.hpp b/source/client/renderer/LevelRenderer.hpp +index 18947ff8c..63520dc24 100644 +--- a/source/client/renderer/LevelRenderer.hpp ++++ b/source/client/renderer/LevelRenderer.hpp +@@ -223,5 +223,5 @@ class LevelRenderer : public LevelListener, public AppPlatformListener + mce::Mesh m_darkMesh; + //... + Textures* m_pTextures; +- std::vector m_renderableTileEntities; ++ TileEntityVector m_renderableTileEntities; + }; +diff --git a/source/world/entity/FallingTile.cpp b/source/world/entity/FallingTile.cpp +index 23c028573..aa249e52d 100644 +--- a/source/world/entity/FallingTile.cpp ++++ b/source/world/entity/FallingTile.cpp +@@ -80,11 +80,11 @@ void FallingTile::tick() + remove(); + if (m_pLevel->mayPlace(m_id, tilePos, true)) + { +- m_pLevel->setTile(tilePos, m_id); ++ m_pLevel->setTile(tilePos, static_cast(m_id)); + } + else + { +- spawnAtLocation(m_id, 1); ++ spawnAtLocation(static_cast(m_id), 1); + } + } + +diff --git a/source/world/tile/entity/TileEntityType.cpp b/source/world/tile/entity/TileEntityType.cpp +index 1391e41ac..18589f526 100644 +--- a/source/world/tile/entity/TileEntityType.cpp ++++ b/source/world/tile/entity/TileEntityType.cpp +@@ -14,14 +14,14 @@ TileEntityType* TileEntityType::noteblock; + + std::map TileEntityFactory::_types; + +-void TileEntityFactory::InitTileEntities() ++void TileEntityFactory::initTileEntities() + { +- TileEntityType::furnace = RegisterTileEntity("Furnace"); +- TileEntityType::chest = RegisterTileEntity("Chest"); +- TileEntityType::noteblock = RegisterTileEntity("Music"); ++ TileEntityType::furnace = registerTileEntity("Furnace"); ++ TileEntityType::chest = registerTileEntity("Chest"); ++ TileEntityType::noteblock = registerTileEntity("Music"); + } + +-void TileEntityFactory::TeardownTileEntities() ++void TileEntityFactory::teardownTileEntities() + { + // delete all heap allocated tile entity types (furnace, chest, etc.) + for (std::map::const_iterator it = _types.begin(); it != _types.end(); ++it) +@@ -31,7 +31,7 @@ void TileEntityFactory::TeardownTileEntities() + _types.clear(); + } + +-const TileEntityType* TileEntityFactory::GetType(const std::string& name) ++const TileEntityType* TileEntityFactory::getType(const std::string& name) + { + return _types[name]; + } +diff --git a/source/world/tile/entity/TileEntityType.hpp b/source/world/tile/entity/TileEntityType.hpp +index 0baa6b857..d1ad2b6ff 100644 +--- a/source/world/tile/entity/TileEntityType.hpp ++++ b/source/world/tile/entity/TileEntityType.hpp +@@ -14,13 +14,13 @@ class TileEntityFactory + static std::map _types; + + public: +- static void InitTileEntities(); +- static void TeardownTileEntities(); +- static const TileEntityType* GetType(const std::string& name); ++ static void initTileEntities(); ++ static void teardownTileEntities(); ++ static const TileEntityType* getType(const std::string& name); + + public: + template +- static TileEntityType* RegisterTileEntity(const std::string& name); ++ static TileEntityType* registerTileEntity(const std::string& name); + }; + + class TileEntityType +@@ -50,7 +50,7 @@ class TileEntityType + }; + + template +-TileEntityType* TileEntityFactory::RegisterTileEntity(const std::string& name) ++TileEntityType* TileEntityFactory::registerTileEntity(const std::string& name) + { + TileEntityType* type = new TileEntityType(name, &TileEntityType::CreateType); + _types[type->_name] = type; + +From 732374d48ab2f66399efb8839019ec531052fff8 Mon Sep 17 00:00:00 2001 +From: hellosammu +Date: Tue, 17 Feb 2026 20:17:21 -0500 +Subject: [PATCH 12/22] wrong way of doing this but i have to merge + +--- + source/world/entity/FallingTile.cpp | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/source/world/entity/FallingTile.cpp b/source/world/entity/FallingTile.cpp +index aa249e52d..dd9edfdc8 100644 +--- a/source/world/entity/FallingTile.cpp ++++ b/source/world/entity/FallingTile.cpp +@@ -80,11 +80,11 @@ void FallingTile::tick() + remove(); + if (m_pLevel->mayPlace(m_id, tilePos, true)) + { +- m_pLevel->setTile(tilePos, static_cast(m_id)); ++ m_pLevel->setTile(tilePos, getTile()); + } + else + { +- spawnAtLocation(static_cast(m_id), 1); ++ spawnAtLocation(getTile()); + } + } + + +From 67a9b26d4ca7a09d6e14013cd7ad61aaa093ebc8 Mon Sep 17 00:00:00 2001 +From: hellosammu +Date: Tue, 17 Feb 2026 20:19:38 -0500 +Subject: [PATCH 13/22] syntax bitch + +--- + source/client/app/NinecraftApp.cpp | 4 ++-- + source/world/entity/FallingTile.cpp | 4 ++-- + source/world/tile/entity/TileEntity.cpp | 2 +- + 3 files changed, 5 insertions(+), 5 deletions(-) + +diff --git a/source/client/app/NinecraftApp.cpp b/source/client/app/NinecraftApp.cpp +index 1b336d72c..4994bbfe2 100644 +--- a/source/client/app/NinecraftApp.cpp ++++ b/source/client/app/NinecraftApp.cpp +@@ -180,7 +180,7 @@ void NinecraftApp::_initAll() + EntityTypeDescriptor::initDescriptors(); // custom + MobCategory::initMobCategories(); + MobFactory::initMobLists(); +- TileEntityFactory::InitTileEntities(); ++ TileEntityFactory::initTileEntities(); + Tile::initTiles(); + Item::initItems(); + Biome::initBiomes(); +@@ -341,7 +341,7 @@ void NinecraftApp::onGraphicsReset() + + void NinecraftApp::teardown() + { +- TileEntityFactory::TeardownTileEntities(); ++ TileEntityFactory::teardownTileEntities(); + teardownRenderer(); + Resource::teardownLoaders(); + // Stop our SoundSystem before we nuke our sound buffers and cause it to implode +diff --git a/source/world/entity/FallingTile.cpp b/source/world/entity/FallingTile.cpp +index 5b23d84ac..838b63a0a 100644 +--- a/source/world/entity/FallingTile.cpp ++++ b/source/world/entity/FallingTile.cpp +@@ -79,7 +79,7 @@ void FallingTile::tick() + { + if (field_E0 > 100 && !m_pLevel->m_bIsClientSide) + { +- spawnAtLocation(m_id, 1); ++ spawnAtLocation(getTile(), 1); + remove(); + } + +@@ -96,7 +96,7 @@ void FallingTile::tick() + } + else + { +- spawnAtLocation(getTile()); ++ spawnAtLocation(getTile(), 1); + } + } + +diff --git a/source/world/tile/entity/TileEntity.cpp b/source/world/tile/entity/TileEntity.cpp +index a5b11be86..8cf5078c1 100644 +--- a/source/world/tile/entity/TileEntity.cpp ++++ b/source/world/tile/entity/TileEntity.cpp +@@ -19,7 +19,7 @@ TileEntity* TileEntity::LoadTileEntity(const CompoundTag& tag) + } + + std::string id = tag.getString("id"); +- const TileEntityType* type = TileEntityFactory::GetType(id); ++ const TileEntityType* type = TileEntityFactory::getType(id); + + if (!type) + { + +From 7506a6cf273ac381f307509974696229ce39b3cc Mon Sep 17 00:00:00 2001 +From: hellosammu +Date: Tue, 17 Feb 2026 20:24:46 -0500 +Subject: [PATCH 14/22] sorry SDL1 no vsync yet + +--- + platforms/sdl/base/AppPlatform_sdl.cpp | 16 ---------------- + platforms/sdl/base/AppPlatform_sdl.hpp | 2 -- + 2 files changed, 18 deletions(-) + +diff --git a/platforms/sdl/base/AppPlatform_sdl.cpp b/platforms/sdl/base/AppPlatform_sdl.cpp +index 1d21c36c8..30de0ac72 100644 +--- a/platforms/sdl/base/AppPlatform_sdl.cpp ++++ b/platforms/sdl/base/AppPlatform_sdl.cpp +@@ -149,22 +149,6 @@ void AppPlatform_sdl::_setDefaultIcon() + _setIcon(data); + } + +-void AppPlatform_sdl::setVSyncEnabled(bool enabled) +-{ +-#if MCE_GFX_API_OGL +- SDL_GL_SetSwapInterval(enabled ? 1 : 0); +-#endif +-} +- +-bool AppPlatform_sdl::isVsyncSwitchable() const +-{ +-#if MCE_GFX_API_OGL +- return true; +-#else +- return false; +-#endif +-} +- + void AppPlatform_sdl::initSoundSystem() + { + if (m_pSoundSystem) +diff --git a/platforms/sdl/base/AppPlatform_sdl.hpp b/platforms/sdl/base/AppPlatform_sdl.hpp +index 976668744..c93d4f4b2 100644 +--- a/platforms/sdl/base/AppPlatform_sdl.hpp ++++ b/platforms/sdl/base/AppPlatform_sdl.hpp +@@ -38,8 +38,6 @@ class AppPlatform_sdl : public AppPlatform + int getUserInputStatus() override; + void saveScreenshot(const std::string& fileName, int width, int height) override; + SoundSystem* getSoundSystem() const override { return m_pSoundSystem; } +- void setVSyncEnabled(bool enabled) override; +- bool isVsyncSwitchable() const override; + + // Also add these to allow proper turning within the game. + void setMouseGrabbed(bool b) override; + +From b39ae301d75c329e3949a1d521860206d3d67ea3 Mon Sep 17 00:00:00 2001 +From: hellosammu +Date: Tue, 17 Feb 2026 20:26:19 -0500 +Subject: [PATCH 15/22] android + +--- + platforms/android/AppPlatform_android.cpp | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/platforms/android/AppPlatform_android.cpp b/platforms/android/AppPlatform_android.cpp +index a2aacf87c..f3547ef58 100644 +--- a/platforms/android/AppPlatform_android.cpp ++++ b/platforms/android/AppPlatform_android.cpp +@@ -147,7 +147,7 @@ void AppPlatform_android::setVSyncEnabled(bool enabled) + if (display == EGL_NO_DISPLAY) + return; + +- glSwapInterval(display, enabled ? 1 : 0); ++ eglSwapInterval(display, enabled ? 1 : 0); + } + + bool AppPlatform_android::isVsyncSwitchable() const + +From 4d117c3db6398f09f1835674dac8e64cfdfa03f6 Mon Sep 17 00:00:00 2001 +From: Brent Da Mage +Date: Tue, 17 Feb 2026 19:30:35 -0600 +Subject: [PATCH 16/22] Updated Visual Studio Projects + +--- + .../windows/projects/Client/Client.vcxproj | 2 + + .../projects/Client/Client.vcxproj.filters | 6 ++ + .../windows/projects/World/World.vcxproj | 26 ++++++ + .../projects/World/World.vcxproj.filters | 93 +++++++++++++++++-- + source/server/ServerPlayer.cpp | 10 ++ + 5 files changed, 128 insertions(+), 9 deletions(-) + +diff --git a/platforms/windows/projects/Client/Client.vcxproj b/platforms/windows/projects/Client/Client.vcxproj +index c046d534d..8da4fa551 100644 +--- a/platforms/windows/projects/Client/Client.vcxproj ++++ b/platforms/windows/projects/Client/Client.vcxproj +@@ -258,6 +258,7 @@ + + + ++ + + + +@@ -434,6 +435,7 @@ + + + ++ + + + +diff --git a/platforms/windows/projects/Client/Client.vcxproj.filters b/platforms/windows/projects/Client/Client.vcxproj.filters +index 55cd0f467..35b451268 100644 +--- a/platforms/windows/projects/Client/Client.vcxproj.filters ++++ b/platforms/windows/projects/Client/Client.vcxproj.filters +@@ -677,6 +677,9 @@ + + Header Files\GUI\Screens\Inventory + ++ ++ Header Files\GUI\Screens\Inventory ++ + + + +@@ -1204,5 +1207,8 @@ + + Source Files\GUI\Screens\Inventory + ++ ++ Source Files\GUI\Screens\Inventory ++ + + +\ No newline at end of file +diff --git a/platforms/windows/projects/World/World.vcxproj b/platforms/windows/projects/World/World.vcxproj +index 8a8312eca..7247155f5 100644 +--- a/platforms/windows/projects/World/World.vcxproj ++++ b/platforms/windows/projects/World/World.vcxproj +@@ -228,6 +228,20 @@ + + + ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ + + + +@@ -397,6 +411,18 @@ + + + ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ + + + +diff --git a/platforms/windows/projects/World/World.vcxproj.filters b/platforms/windows/projects/World/World.vcxproj.filters +index 18a19e701..6c9bbd804 100644 +--- a/platforms/windows/projects/World/World.vcxproj.filters ++++ b/platforms/windows/projects/World/World.vcxproj.filters +@@ -105,6 +105,12 @@ + + {3a97a5e5-77e6-43eb-9890-3fa79b287cd1} + ++ ++ {53363441-c079-4adf-a246-481be4b68199} ++ ++ ++ {9dca9e2a-e126-4598-a852-105be172d198} ++ + + + +@@ -659,6 +665,48 @@ + + Source Files\Item + ++ ++ Source Files\Inventory ++ ++ ++ Source Files\Inventory ++ ++ ++ Source Files\Item ++ ++ ++ Source Files\Particle ++ ++ ++ Source Files ++ ++ ++ Source Files\Tile ++ ++ ++ Source Files\Tile ++ ++ ++ Source Files\Tile ++ ++ ++ Source Files\Tile ++ ++ ++ Source Files\Tile\Entity ++ ++ ++ Source Files\Tile\Entity ++ ++ ++ Source Files\Tile\Entity ++ ++ ++ Source Files\Tile\Entity ++ ++ ++ Source Files\Tile\Entity ++ + + + +@@ -1126,15 +1174,6 @@ + + Header Files\Item + +- +- Header Files\Item +- +- +- Header Files\Item +- +- +- Header Files\Item +- + + Header Files\Item + +@@ -1171,5 +1210,41 @@ + + Header Files\Item + ++ ++ Header Files\Inventory ++ ++ ++ Header Files\Inventory ++ ++ ++ Header Files\Item ++ ++ ++ Header Files\Tile\Entity ++ ++ ++ Header Files\Tile\Entity ++ ++ ++ Header Files\Tile\Entity ++ ++ ++ Header Files\Tile\Entity ++ ++ ++ Header Files\Tile\Entity ++ ++ ++ Header Files\Tile ++ ++ ++ Header Files\Tile ++ ++ ++ Header Files\Tile ++ ++ ++ Header Files\Tile ++ + + +\ No newline at end of file +diff --git a/source/server/ServerPlayer.cpp b/source/server/ServerPlayer.cpp +index 21ed28339..8a0aad136 100644 +--- a/source/server/ServerPlayer.cpp ++++ b/source/server/ServerPlayer.cpp +@@ -1,4 +1,5 @@ + #include "ServerPlayer.hpp" ++#include "common/Logger.hpp" + #include "network/packets/SetHealthPacket.hpp" + #include "network/packets/TakeItemEntityPacket.hpp" + #include "network/packets/SendInventoryPacket.hpp" +@@ -55,6 +56,8 @@ void ServerPlayer::startCrafting(const TilePos& pos) + + void ServerPlayer::openContainer(Container* container) + { ++ LOG_I("Client is opening a container"); ++ + _nextContainerCounter(); + + #if NETWORK_PROTOCOL_VERSION >= 5 +@@ -71,6 +74,8 @@ void ServerPlayer::openContainer(Container* container) + + void ServerPlayer::closeContainer() + { ++ LOG_I("Client is closing a container"); ++ + #if NETWORK_PROTOCOL_VERSION >= 5 + m_pLevel->m_pRakNetInstance->send(new ContainerClosePacket(m_pContainerMenu->m_containerId)); + #endif +@@ -80,6 +85,8 @@ void ServerPlayer::closeContainer() + + void ServerPlayer::openFurnace(FurnaceTileEntity* furnace) + { ++ LOG_I("Client is opening a furnace"); ++ + _nextContainerCounter(); + + #if NETWORK_PROTOCOL_VERSION >= 5 +@@ -131,6 +138,9 @@ void ServerPlayer::doCloseContainer() + { + if (m_pContainerMenu) + m_pContainerMenu->removed(this); ++ else ++ LOG_W("Container is missing @ doCloseContainer!"); ++ + setContainerMenu(nullptr); // m_pInventoryMenu on Java, nullptr on Pocket + } + + +From d2bdb19429320f21a2ac526564b4a7d244d01a87 Mon Sep 17 00:00:00 2001 +From: Sam +Date: Tue, 17 Feb 2026 20:47:38 -0500 +Subject: [PATCH 17/22] tim apple + +--- + .../Minecraft.xcodeproj/project.pbxproj | 220 ++++++++++++++---- + 1 file changed, 170 insertions(+), 50 deletions(-) + +diff --git a/platforms/macos/projects/Minecraft/Minecraft.xcodeproj/project.pbxproj b/platforms/macos/projects/Minecraft/Minecraft.xcodeproj/project.pbxproj +index 7a625e3e8..dcc3c8727 100644 +--- a/platforms/macos/projects/Minecraft/Minecraft.xcodeproj/project.pbxproj ++++ b/platforms/macos/projects/Minecraft/Minecraft.xcodeproj/project.pbxproj +@@ -194,6 +194,34 @@ + 8426107D2AE989730065905F /* UpdateBlockPacket.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 840DD64C2AC810620006A435 /* UpdateBlockPacket.cpp */; }; + 8426107E2AE989730065905F /* RakNetInstance.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 840DD64E2AC810620006A435 /* RakNetInstance.cpp */; }; + 842610882AE98A4C0065905F /* libRakNet.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 84FFBD7E2ACA2876005A8CCF /* libRakNet.a */; }; ++ 84358AFE2F4550B30077EA44 /* TileEntityType.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84358AFC2F4550B30077EA44 /* TileEntityType.cpp */; }; ++ 84358AFF2F4550B30077EA44 /* ChestTileEntity.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84358AF42F4550B30077EA44 /* ChestTileEntity.cpp */; }; ++ 84358B002F4550B30077EA44 /* FurnaceTileEntity.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84358AF62F4550B30077EA44 /* FurnaceTileEntity.cpp */; }; ++ 84358B012F4550B30077EA44 /* TileEntity.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84358AFA2F4550B30077EA44 /* TileEntity.cpp */; }; ++ 84358B022F4550B30077EA44 /* MusicTileEntity.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84358AF82F4550B30077EA44 /* MusicTileEntity.cpp */; }; ++ 84358B032F4550B30077EA44 /* TileEntity.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84358AF92F4550B30077EA44 /* TileEntity.hpp */; }; ++ 84358B042F4550B30077EA44 /* FurnaceTileEntity.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84358AF52F4550B30077EA44 /* FurnaceTileEntity.hpp */; }; ++ 84358B052F4550B30077EA44 /* ChestTileEntity.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84358AF32F4550B30077EA44 /* ChestTileEntity.hpp */; }; ++ 84358B062F4550B30077EA44 /* MusicTileEntity.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84358AF72F4550B30077EA44 /* MusicTileEntity.hpp */; }; ++ 84358B072F4550B30077EA44 /* TileEntityType.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84358AFB2F4550B30077EA44 /* TileEntityType.hpp */; }; ++ 84358B102F4550CC0077EA44 /* FurnaceTile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84358B0D2F4550CC0077EA44 /* FurnaceTile.cpp */; }; ++ 84358B112F4550CC0077EA44 /* EntityTile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84358B0B2F4550CC0077EA44 /* EntityTile.cpp */; }; ++ 84358B122F4550CC0077EA44 /* ChestTile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84358B092F4550CC0077EA44 /* ChestTile.cpp */; }; ++ 84358B132F4550CC0077EA44 /* MusicTile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84358B0F2F4550CC0077EA44 /* MusicTile.cpp */; }; ++ 84358B142F4550CC0077EA44 /* ChestTile.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84358B082F4550CC0077EA44 /* ChestTile.hpp */; }; ++ 84358B152F4550CC0077EA44 /* FurnaceTile.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84358B0C2F4550CC0077EA44 /* FurnaceTile.hpp */; }; ++ 84358B162F4550CC0077EA44 /* EntityTile.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84358B0A2F4550CC0077EA44 /* EntityTile.hpp */; }; ++ 84358B172F4550CC0077EA44 /* MusicTile.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84358B0E2F4550CC0077EA44 /* MusicTile.hpp */; }; ++ 84358B192F45511C0077EA44 /* NoteParticle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84358B182F45511C0077EA44 /* NoteParticle.cpp */; }; ++ 84358B1B2F4551290077EA44 /* Facing.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84358B1A2F4551290077EA44 /* Facing.cpp */; }; ++ 84358B1E2F4551500077EA44 /* CoalItem.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84358B1D2F4551500077EA44 /* CoalItem.cpp */; }; ++ 84358B1F2F4551500077EA44 /* CoalItem.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84358B1C2F4551500077EA44 /* CoalItem.hpp */; }; ++ 84358B242F4551730077EA44 /* FurnaceResultSlot.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84358B232F4551730077EA44 /* FurnaceResultSlot.cpp */; }; ++ 84358B252F4551730077EA44 /* FurnaceMenu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84358B212F4551730077EA44 /* FurnaceMenu.cpp */; }; ++ 84358B262F4551730077EA44 /* FurnaceMenu.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84358B202F4551730077EA44 /* FurnaceMenu.hpp */; }; ++ 84358B272F4551730077EA44 /* FurnaceResultSlot.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84358B222F4551730077EA44 /* FurnaceResultSlot.hpp */; }; ++ 84358B2A2F45519B0077EA44 /* FurnaceScreen.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84358B282F45519B0077EA44 /* FurnaceScreen.hpp */; }; ++ 84358B2B2F45519B0077EA44 /* FurnaceScreen.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84358B292F45519B0077EA44 /* FurnaceScreen.cpp */; }; + 8435BB192DCD47F400D38282 /* SoundStreamOAL.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8435BB182DCD47F400D38282 /* SoundStreamOAL.cpp */; }; + 8435BB1A2DCD47F400D38282 /* SoundStreamOAL.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8435BB182DCD47F400D38282 /* SoundStreamOAL.cpp */; }; + 8441F98F2DB4E911005977BD /* SoundSystemOAL.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8441F9862DB4E911005977BD /* SoundSystemOAL.cpp */; }; +@@ -2234,6 +2262,34 @@ + 84336BA52B1EB57E00097DB0 /* Settings_iOS_Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Settings_iOS_Debug.xcconfig; path = ../Configuration/Settings_iOS_Debug.xcconfig; sourceTree = ""; }; + 84336BA82B1EB88500097DB0 /* Settings_macOS.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Settings_macOS.xcconfig; path = ../Configuration/Settings_macOS.xcconfig; sourceTree = ""; }; + 84336BA92B1EB9C200097DB0 /* Settings_macOS_Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Settings_macOS_Debug.xcconfig; path = ../Configuration/Settings_macOS_Debug.xcconfig; sourceTree = ""; }; ++ 84358AF32F4550B30077EA44 /* ChestTileEntity.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ChestTileEntity.hpp; sourceTree = ""; }; ++ 84358AF42F4550B30077EA44 /* ChestTileEntity.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ChestTileEntity.cpp; sourceTree = ""; }; ++ 84358AF52F4550B30077EA44 /* FurnaceTileEntity.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = FurnaceTileEntity.hpp; sourceTree = ""; }; ++ 84358AF62F4550B30077EA44 /* FurnaceTileEntity.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FurnaceTileEntity.cpp; sourceTree = ""; }; ++ 84358AF72F4550B30077EA44 /* MusicTileEntity.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = MusicTileEntity.hpp; sourceTree = ""; }; ++ 84358AF82F4550B30077EA44 /* MusicTileEntity.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = MusicTileEntity.cpp; sourceTree = ""; }; ++ 84358AF92F4550B30077EA44 /* TileEntity.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = TileEntity.hpp; sourceTree = ""; }; ++ 84358AFA2F4550B30077EA44 /* TileEntity.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = TileEntity.cpp; sourceTree = ""; }; ++ 84358AFB2F4550B30077EA44 /* TileEntityType.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = TileEntityType.hpp; sourceTree = ""; }; ++ 84358AFC2F4550B30077EA44 /* TileEntityType.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = TileEntityType.cpp; sourceTree = ""; }; ++ 84358B082F4550CC0077EA44 /* ChestTile.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ChestTile.hpp; sourceTree = ""; }; ++ 84358B092F4550CC0077EA44 /* ChestTile.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ChestTile.cpp; sourceTree = ""; }; ++ 84358B0A2F4550CC0077EA44 /* EntityTile.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = EntityTile.hpp; sourceTree = ""; }; ++ 84358B0B2F4550CC0077EA44 /* EntityTile.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = EntityTile.cpp; sourceTree = ""; }; ++ 84358B0C2F4550CC0077EA44 /* FurnaceTile.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = FurnaceTile.hpp; sourceTree = ""; }; ++ 84358B0D2F4550CC0077EA44 /* FurnaceTile.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FurnaceTile.cpp; sourceTree = ""; }; ++ 84358B0E2F4550CC0077EA44 /* MusicTile.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = MusicTile.hpp; sourceTree = ""; }; ++ 84358B0F2F4550CC0077EA44 /* MusicTile.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = MusicTile.cpp; sourceTree = ""; }; ++ 84358B182F45511C0077EA44 /* NoteParticle.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = NoteParticle.cpp; sourceTree = ""; }; ++ 84358B1A2F4551290077EA44 /* Facing.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Facing.cpp; sourceTree = ""; }; ++ 84358B1C2F4551500077EA44 /* CoalItem.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = CoalItem.hpp; sourceTree = ""; }; ++ 84358B1D2F4551500077EA44 /* CoalItem.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = CoalItem.cpp; sourceTree = ""; }; ++ 84358B202F4551730077EA44 /* FurnaceMenu.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = FurnaceMenu.hpp; sourceTree = ""; }; ++ 84358B212F4551730077EA44 /* FurnaceMenu.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FurnaceMenu.cpp; sourceTree = ""; }; ++ 84358B222F4551730077EA44 /* FurnaceResultSlot.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = FurnaceResultSlot.hpp; sourceTree = ""; }; ++ 84358B232F4551730077EA44 /* FurnaceResultSlot.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FurnaceResultSlot.cpp; sourceTree = ""; }; ++ 84358B282F45519B0077EA44 /* FurnaceScreen.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = FurnaceScreen.hpp; sourceTree = ""; }; ++ 84358B292F45519B0077EA44 /* FurnaceScreen.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FurnaceScreen.cpp; sourceTree = ""; }; + 8435BB172DCD47F400D38282 /* SoundStreamOAL.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = SoundStreamOAL.hpp; sourceTree = ""; }; + 8435BB182DCD47F400D38282 /* SoundStreamOAL.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = SoundStreamOAL.cpp; sourceTree = ""; }; + 8441F9852DB4E911005977BD /* CustomSoundSystem.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = CustomSoundSystem.hpp; sourceTree = ""; }; +@@ -3756,6 +3812,7 @@ + 84E7BF2D2F286A20002D3936 /* ContainerListener.cpp */, + 84E7BF2E2F286A20002D3936 /* ContainerListener.hpp */, + 840DD6572AC810620006A435 /* entity */, ++ 84358B1A2F4551290077EA44 /* Facing.cpp */, + 8477B3A82C4DC3B3004E1AC5 /* Facing.hpp */, + 840DD6682AC810620006A435 /* gamemode */, + 84E7BF092F286A08002D3936 /* inventory */, +@@ -3852,6 +3909,8 @@ + children = ( + 84B9D8962F3BE0A900FD67C4 /* ArmorItem.hpp */, + 84B9D8972F3BE0A900FD67C4 /* ArmorItem.cpp */, ++ 84358B1C2F4551500077EA44 /* CoalItem.hpp */, ++ 84358B1D2F4551500077EA44 /* CoalItem.cpp */, + 84B9D8982F3BE0A900FD67C4 /* HoeItem.hpp */, + 84B9D8992F3BE0A900FD67C4 /* HoeItem.cpp */, + 84B9D89A2F3BE0A900FD67C4 /* SeedItem.hpp */, +@@ -4042,6 +4101,7 @@ + 840DD6CE2AC810620006A435 /* particle */ = { + isa = PBXGroup; + children = ( ++ 84358B182F45511C0077EA44 /* NoteParticle.cpp */, + 840DD6CF2AC810620006A435 /* BubbleParticle.cpp */, + 840DD6D02AC810620006A435 /* ExplodeParticle.cpp */, + 8470AF3B2BE9B6FA00BCA54E /* FireworkParticle.hpp */, +@@ -4077,104 +4137,113 @@ + 840DD6E12AC810620006A435 /* tile */ = { + isa = PBXGroup; + children = ( +- 84B9D8A22F3BE0B200FD67C4 /* CropsTile.hpp */, +- 84B9D8A32F3BE0B200FD67C4 /* CropsTile.cpp */, +- 840DD6E22AC810620006A435 /* BookshelfTile.cpp */, ++ 84358AFD2F4550B30077EA44 /* entity */, + 840DD6E32AC810620006A435 /* BookshelfTile.hpp */, +- 840DD6E42AC810620006A435 /* Bush.cpp */, ++ 840DD6E22AC810620006A435 /* BookshelfTile.cpp */, + 840DD6E52AC810620006A435 /* Bush.hpp */, +- 84E1C9BF2E7FDC26007D2F5D /* CactusTile.cpp */, ++ 840DD6E42AC810620006A435 /* Bush.cpp */, + 84E1C9C02E7FDC26007D2F5D /* CactusTile.hpp */, +- 840DD6E62AC810620006A435 /* ClayTile.cpp */, ++ 84E1C9BF2E7FDC26007D2F5D /* CactusTile.cpp */, ++ 84358B082F4550CC0077EA44 /* ChestTile.hpp */, ++ 84358B092F4550CC0077EA44 /* ChestTile.cpp */, + 840DD6E72AC810620006A435 /* ClayTile.hpp */, +- 840DD6E82AC810620006A435 /* ClothTile.cpp */, ++ 840DD6E62AC810620006A435 /* ClayTile.cpp */, + 840DD6E92AC810620006A435 /* ClothTile.hpp */, +- 84DED1B52F30970A004001C5 /* CraftingTableTile.cpp */, ++ 840DD6E82AC810620006A435 /* ClothTile.cpp */, + 84DED1B62F30970A004001C5 /* CraftingTableTile.hpp */, +- 84E1C9C12E7FDC26007D2F5D /* DeadBush.cpp */, ++ 84DED1B52F30970A004001C5 /* CraftingTableTile.cpp */, ++ 84B9D8A22F3BE0B200FD67C4 /* CropsTile.hpp */, ++ 84B9D8A32F3BE0B200FD67C4 /* CropsTile.cpp */, + 84E1C9C22E7FDC26007D2F5D /* DeadBush.hpp */, +- 840DD6EA2AC810620006A435 /* DirtTile.cpp */, ++ 84E1C9C12E7FDC26007D2F5D /* DeadBush.cpp */, + 840DD6EB2AC810620006A435 /* DirtTile.hpp */, +- 840DD6EC2AC810620006A435 /* DoorTile.cpp */, ++ 840DD6EA2AC810620006A435 /* DirtTile.cpp */, + 840DD6ED2AC810620006A435 /* DoorTile.hpp */, +- 840DD6EE2AC810620006A435 /* FarmTile.cpp */, ++ 840DD6EC2AC810620006A435 /* DoorTile.cpp */, ++ 84358B0A2F4550CC0077EA44 /* EntityTile.hpp */, ++ 84358B0B2F4550CC0077EA44 /* EntityTile.cpp */, + 840DD6EF2AC810620006A435 /* FarmTile.hpp */, +- 84E1C9C32E7FDC26007D2F5D /* FenceTile.cpp */, ++ 840DD6EE2AC810620006A435 /* FarmTile.cpp */, + 84E1C9C42E7FDC26007D2F5D /* FenceTile.hpp */, +- 840DD6F02AC810620006A435 /* FireTile.cpp */, ++ 84E1C9C32E7FDC26007D2F5D /* FenceTile.cpp */, + 840DD6F12AC810620006A435 /* FireTile.hpp */, +- 840DD6F22AC810620006A435 /* GlassTile.cpp */, ++ 840DD6F02AC810620006A435 /* FireTile.cpp */, ++ 84358B0C2F4550CC0077EA44 /* FurnaceTile.hpp */, ++ 84358B0D2F4550CC0077EA44 /* FurnaceTile.cpp */, + 840DD6F32AC810620006A435 /* GlassTile.hpp */, +- 84E1C9C52E7FDC26007D2F5D /* GlowstoneTile.cpp */, ++ 840DD6F22AC810620006A435 /* GlassTile.cpp */, + 84E1C9C62E7FDC26007D2F5D /* GlowstoneTile.hpp */, +- 840DD6F42AC810620006A435 /* GrassTile.cpp */, ++ 84E1C9C52E7FDC26007D2F5D /* GlowstoneTile.cpp */, + 840DD6F52AC810620006A435 /* GrassTile.hpp */, +- 840DD6F62AC810620006A435 /* GravelTile.cpp */, ++ 840DD6F42AC810620006A435 /* GrassTile.cpp */, + 840DD6F72AC810620006A435 /* GravelTile.hpp */, +- 840DD6F82AC810620006A435 /* HalfTransparentTile.cpp */, ++ 840DD6F62AC810620006A435 /* GravelTile.cpp */, + 840DD6F92AC810620006A435 /* HalfTransparentTile.hpp */, +- 840DD6FA2AC810620006A435 /* IceTile.cpp */, ++ 840DD6F82AC810620006A435 /* HalfTransparentTile.cpp */, + 840DD6FB2AC810620006A435 /* IceTile.hpp */, +- 840DD6FC2AC810620006A435 /* InvisibleTile.cpp */, ++ 840DD6FA2AC810620006A435 /* IceTile.cpp */, + 840DD6FD2AC810620006A435 /* InvisibleTile.hpp */, +- 840DD6FE2AC810620006A435 /* LadderTile.cpp */, ++ 840DD6FC2AC810620006A435 /* InvisibleTile.cpp */, + 840DD6FF2AC810620006A435 /* LadderTile.hpp */, +- 840DD7002AC810620006A435 /* LeafTile.cpp */, ++ 840DD6FE2AC810620006A435 /* LadderTile.cpp */, + 840DD7012AC810620006A435 /* LeafTile.hpp */, +- 840DD7022AC810620006A435 /* LiquidTile.cpp */, ++ 840DD7002AC810620006A435 /* LeafTile.cpp */, + 840DD7032AC810620006A435 /* LiquidTile.hpp */, +- 840DD7042AC810620006A435 /* LiquidTileDynamic.cpp */, ++ 840DD7022AC810620006A435 /* LiquidTile.cpp */, + 840DD7052AC810620006A435 /* LiquidTileDynamic.hpp */, +- 840DD7062AC810620006A435 /* LiquidTileStatic.cpp */, ++ 840DD7042AC810620006A435 /* LiquidTileDynamic.cpp */, + 840DD7072AC810620006A435 /* LiquidTileStatic.hpp */, +- 840DD7082AC810620006A435 /* MetalTile.cpp */, ++ 840DD7062AC810620006A435 /* LiquidTileStatic.cpp */, + 840DD7092AC810620006A435 /* MetalTile.hpp */, +- 840DD70A2AC810620006A435 /* ObsidianTile.cpp */, ++ 840DD7082AC810620006A435 /* MetalTile.cpp */, ++ 84358B0E2F4550CC0077EA44 /* MusicTile.hpp */, ++ 84358B0F2F4550CC0077EA44 /* MusicTile.cpp */, + 840DD70B2AC810620006A435 /* ObsidianTile.hpp */, +- 840DD70C2AC810620006A435 /* OreTile.cpp */, ++ 840DD70A2AC810620006A435 /* ObsidianTile.cpp */, + 840DD70D2AC810620006A435 /* OreTile.hpp */, +- 84E1C9C72E7FDC26007D2F5D /* PumpkinTile.cpp */, ++ 840DD70C2AC810620006A435 /* OreTile.cpp */, + 84E1C9C82E7FDC26007D2F5D /* PumpkinTile.hpp */, +- 840DD70E2AC810620006A435 /* RedStoneOreTile.cpp */, ++ 84E1C9C72E7FDC26007D2F5D /* PumpkinTile.cpp */, + 840DD70F2AC810620006A435 /* RedStoneOreTile.hpp */, +- 840DD7102AC810620006A435 /* ReedTile.cpp */, ++ 840DD70E2AC810620006A435 /* RedStoneOreTile.cpp */, + 840DD7112AC810620006A435 /* ReedTile.hpp */, +- 84E78C852B58B66B00D515EF /* RocketLauncherTile.cpp */, ++ 840DD7102AC810620006A435 /* ReedTile.cpp */, + 84E78C862B58B66B00D515EF /* RocketLauncherTile.hpp */, +- 840DD7122AC810620006A435 /* SandStoneTile.cpp */, ++ 84E78C852B58B66B00D515EF /* RocketLauncherTile.cpp */, + 840DD7132AC810620006A435 /* SandStoneTile.hpp */, +- 840DD7142AC810620006A435 /* SandTile.cpp */, ++ 840DD7122AC810620006A435 /* SandStoneTile.cpp */, + 840DD7152AC810620006A435 /* SandTile.hpp */, +- 840DD7162AC810620006A435 /* Sapling.cpp */, ++ 840DD7142AC810620006A435 /* SandTile.cpp */, + 840DD7172AC810620006A435 /* Sapling.hpp */, +- 84E1C9C92E7FDC26007D2F5D /* SoulSandTile.cpp */, ++ 840DD7162AC810620006A435 /* Sapling.cpp */, + 84E1C9CA2E7FDC26007D2F5D /* SoulSandTile.hpp */, +- 840DD7182AC810620006A435 /* SpongeTile.cpp */, ++ 84E1C9C92E7FDC26007D2F5D /* SoulSandTile.cpp */, + 840DD7192AC810620006A435 /* SpongeTile.hpp */, +- 840DD71A2AC810620006A435 /* StairTile.cpp */, ++ 840DD7182AC810620006A435 /* SpongeTile.cpp */, + 840DD71B2AC810620006A435 /* StairTile.hpp */, +- 840DD71C2AC810620006A435 /* StoneSlabTile.cpp */, ++ 840DD71A2AC810620006A435 /* StairTile.cpp */, + 840DD71D2AC810620006A435 /* StoneSlabTile.hpp */, +- 840DD71E2AC810620006A435 /* StoneTile.cpp */, ++ 840DD71C2AC810620006A435 /* StoneSlabTile.cpp */, + 840DD71F2AC810620006A435 /* StoneTile.hpp */, +- 84E1C9CB2E7FDC26007D2F5D /* TallGrass.cpp */, ++ 840DD71E2AC810620006A435 /* StoneTile.cpp */, + 84E1C9CC2E7FDC26007D2F5D /* TallGrass.hpp */, +- 840DD7202AC810620006A435 /* Tile.cpp */, ++ 84E1C9CB2E7FDC26007D2F5D /* TallGrass.cpp */, + 840DD7212AC810620006A435 /* Tile.hpp */, +- 840DD7222AC810620006A435 /* TntTile.cpp */, ++ 840DD7202AC810620006A435 /* Tile.cpp */, + 840DD7232AC810620006A435 /* TntTile.hpp */, +- 840DD7242AC810620006A435 /* TopSnowTile.cpp */, ++ 840DD7222AC810620006A435 /* TntTile.cpp */, + 840DD7252AC810620006A435 /* TopSnowTile.hpp */, +- 840DD7262AC810620006A435 /* TorchTile.cpp */, ++ 840DD7242AC810620006A435 /* TopSnowTile.cpp */, + 840DD7272AC810620006A435 /* TorchTile.hpp */, +- 840DD7282AC810620006A435 /* TransparentTile.cpp */, ++ 840DD7262AC810620006A435 /* TorchTile.cpp */, + 840DD7292AC810620006A435 /* TransparentTile.hpp */, +- 840DD72A2AC810620006A435 /* TreeTile.cpp */, ++ 840DD7282AC810620006A435 /* TransparentTile.cpp */, + 840DD72B2AC810620006A435 /* TreeTile.hpp */, +- 84E1C9CD2E7FDC26007D2F5D /* Web.cpp */, ++ 840DD72A2AC810620006A435 /* TreeTile.cpp */, + 84E1C9CE2E7FDC26007D2F5D /* Web.hpp */, +- 840DD72C2AC810620006A435 /* WireTile.cpp */, ++ 84E1C9CD2E7FDC26007D2F5D /* Web.cpp */, + 840DD72D2AC810620006A435 /* WireTile.hpp */, ++ 840DD72C2AC810620006A435 /* WireTile.cpp */, + ); + path = tile; + sourceTree = ""; +@@ -4566,6 +4635,23 @@ + name = macOS; + sourceTree = ""; + }; ++ 84358AFD2F4550B30077EA44 /* entity */ = { ++ isa = PBXGroup; ++ children = ( ++ 84358AF32F4550B30077EA44 /* ChestTileEntity.hpp */, ++ 84358AF42F4550B30077EA44 /* ChestTileEntity.cpp */, ++ 84358AF52F4550B30077EA44 /* FurnaceTileEntity.hpp */, ++ 84358AF62F4550B30077EA44 /* FurnaceTileEntity.cpp */, ++ 84358AF72F4550B30077EA44 /* MusicTileEntity.hpp */, ++ 84358AF82F4550B30077EA44 /* MusicTileEntity.cpp */, ++ 84358AF92F4550B30077EA44 /* TileEntity.hpp */, ++ 84358AFA2F4550B30077EA44 /* TileEntity.cpp */, ++ 84358AFB2F4550B30077EA44 /* TileEntityType.hpp */, ++ 84358AFC2F4550B30077EA44 /* TileEntityType.cpp */, ++ ); ++ path = entity; ++ sourceTree = ""; ++ }; + 8441F9872DB4E911005977BD /* openal */ = { + isa = PBXGroup; + children = ( +@@ -4949,6 +5035,8 @@ + 84E022E82F2A0211003C8FFE /* inventory */ = { + isa = PBXGroup; + children = ( ++ 84358B282F45519B0077EA44 /* FurnaceScreen.hpp */, ++ 84358B292F45519B0077EA44 /* FurnaceScreen.cpp */, + 84E022E92F2A0211003C8FFE /* ChestScreen.cpp */, + 84E022EA2F2A0211003C8FFE /* ChestScreen.hpp */, + 844337192F405E1C00EB3115 /* ClassicCraftingScreen_Console.cpp */, +@@ -5006,6 +5094,10 @@ + 84E7BF0F2F286A08002D3936 /* CraftingContainer.hpp */, + 84DED19A2F3095FD004001C5 /* CraftingMenu.cpp */, + 84DED19B2F3095FD004001C5 /* CraftingMenu.hpp */, ++ 84358B202F4551730077EA44 /* FurnaceMenu.hpp */, ++ 84358B212F4551730077EA44 /* FurnaceMenu.cpp */, ++ 84358B222F4551730077EA44 /* FurnaceResultSlot.hpp */, ++ 84358B232F4551730077EA44 /* FurnaceResultSlot.cpp */, + 84E7BF102F286A08002D3936 /* InventoryMenu.cpp */, + 84E7BF112F286A08002D3936 /* InventoryMenu.hpp */, + 84E7BF122F286A08002D3936 /* ResultContainer.cpp */, +@@ -5806,6 +5898,7 @@ + 84A2FF182DB61D440090CE3E /* SoundPathRepository.hpp in Headers */, + 84EB09FB2EE2CCA8008FB007 /* TextureData.hpp in Headers */, + 84B1E0302E04FD4500ED000A /* ArrowRenderer.hpp in Headers */, ++ 84358B2A2F45519B0077EA44 /* FurnaceScreen.hpp in Headers */, + 84AA8C2B2B32F3F3003F5B82 /* LightUpdate.hpp in Headers */, + 84AA8C2D2B32F3F3003F5B82 /* PatchManager.hpp in Headers */, + 84AA8C2F2B32F3F3003F5B82 /* RenderChunk.hpp in Headers */, +@@ -5845,6 +5938,10 @@ + 84E1C9D42E7FDC26007D2F5D /* FenceTile.hpp in Headers */, + 84BF637B2AF186C8008A9995 /* ItemEntity.hpp in Headers */, + 84BF637D2AF186C8008A9995 /* Mob.hpp in Headers */, ++ 84358B142F4550CC0077EA44 /* ChestTile.hpp in Headers */, ++ 84358B152F4550CC0077EA44 /* FurnaceTile.hpp in Headers */, ++ 84358B162F4550CC0077EA44 /* EntityTile.hpp in Headers */, ++ 84358B172F4550CC0077EA44 /* MusicTile.hpp in Headers */, + 84BF637E2AF186C8008A9995 /* Player.hpp in Headers */, + 8477B3A92C4DC3B3004E1AC5 /* Facing.hpp in Headers */, + 84BF637F2AF186C8008A9995 /* PrimedTnt.hpp in Headers */, +@@ -5915,6 +6012,8 @@ + 84BF63A92AF186C8008A9995 /* RegionFile.hpp in Headers */, + 84BF63AA2AF186C8008A9995 /* TickNextTickData.hpp in Headers */, + 84BF63AB2AF186C8008A9995 /* Particle.hpp in Headers */, ++ 84358B262F4551730077EA44 /* FurnaceMenu.hpp in Headers */, ++ 84358B272F4551730077EA44 /* FurnaceResultSlot.hpp in Headers */, + 84E7BF232F286A08002D3936 /* ResultContainer.hpp in Headers */, + 84E1C9E62E7FDC72007D2F5D /* AuxTileItem.hpp in Headers */, + 84E7BF272F286A08002D3936 /* SimpleContainer.hpp in Headers */, +@@ -6001,6 +6100,7 @@ + 84E78C842B58B5FB00D515EF /* RocketItem.hpp in Headers */, + 84E78C882B58B66B00D515EF /* RocketLauncherTile.hpp in Headers */, + 8477B3B72C4DC414004E1AC5 /* EmptyLevelChunk.hpp in Headers */, ++ 84358B1F2F4551500077EA44 /* CoalItem.hpp in Headers */, + 84E1C9E82E7FDC72007D2F5D /* ClothItem.hpp in Headers */, + 8470AF2B2BE9B60A00BCA54E /* EntityType.hpp in Headers */, + 8445E7A42D769329008DC834 /* EntityTypeDescriptor.hpp in Headers */, +@@ -6009,6 +6109,11 @@ + 84B9D8A12F3BE0A900FD67C4 /* SeedItem.hpp in Headers */, + 8470AF2D2BE9B60A00BCA54E /* MobFactory.hpp in Headers */, + 8470AF392BE9B6D100BCA54E /* GameType.hpp in Headers */, ++ 84358B032F4550B30077EA44 /* TileEntity.hpp in Headers */, ++ 84358B042F4550B30077EA44 /* FurnaceTileEntity.hpp in Headers */, ++ 84358B052F4550B30077EA44 /* ChestTileEntity.hpp in Headers */, ++ 84358B062F4550B30077EA44 /* MusicTileEntity.hpp in Headers */, ++ 84358B072F4550B30077EA44 /* TileEntityType.hpp in Headers */, + 8470AF3D2BE9B6FA00BCA54E /* FireworkParticle.hpp in Headers */, + 84E7BF302F286A20002D3936 /* CompoundContainer.hpp in Headers */, + ); +@@ -6785,6 +6890,7 @@ + 84B8AEFD2AF1896F008DE93D /* InvalidLicenseScreen.cpp in Sources */, + 84B8AEFE2AF1896F008DE93D /* JoinGameScreen.cpp in Sources */, + 844336FB2F405DCA00EB3115 /* VerticalLayout.cpp in Sources */, ++ 84358B2B2F45519B0077EA44 /* FurnaceScreen.cpp in Sources */, + 84B8AEFF2AF1896F008DE93D /* OptionsScreen.cpp in Sources */, + 84B8AF002AF1896F008DE93D /* PauseScreen.cpp in Sources */, + 84B8AF012AF1896F008DE93D /* ProgressScreen.cpp in Sources */, +@@ -6976,6 +7082,11 @@ + 84BF63162AF18631008A9995 /* SurvivalMode.cpp in Sources */, + 84BF63172AF18631008A9995 /* CameraItem.cpp in Sources */, + 84DED1AF2F309611004001C5 /* ShapedRecipe.cpp in Sources */, ++ 84358AFE2F4550B30077EA44 /* TileEntityType.cpp in Sources */, ++ 84358AFF2F4550B30077EA44 /* ChestTileEntity.cpp in Sources */, ++ 84358B002F4550B30077EA44 /* FurnaceTileEntity.cpp in Sources */, ++ 84358B012F4550B30077EA44 /* TileEntity.cpp in Sources */, ++ 84358B022F4550B30077EA44 /* MusicTileEntity.cpp in Sources */, + 84E1C9E52E7FDC72007D2F5D /* AuxTileItem.cpp in Sources */, + 84E1C9D52E7FDC26007D2F5D /* GlowstoneTile.cpp in Sources */, + 84BF63182AF18631008A9995 /* DoorItem.cpp in Sources */, +@@ -6983,6 +7094,7 @@ + 84B9D8A42F3BE0B200FD67C4 /* CropsTile.cpp in Sources */, + 8445E7A22D769329008DC834 /* EntityType.cpp in Sources */, + 84BF631A2AF18631008A9995 /* Item.cpp in Sources */, ++ 84358B1B2F4551290077EA44 /* Facing.cpp in Sources */, + 84BF631C2AF18631008A9995 /* TileItem.cpp in Sources */, + 84E1C9F02E7FDC89007D2F5D /* VegetationFeature.cpp in Sources */, + 84BF631D2AF18631008A9995 /* TilePlanterItem.cpp in Sources */, +@@ -6995,8 +7107,11 @@ + 84BF63222AF18631008A9995 /* BiomeSource.cpp in Sources */, + 84BF63232AF18631008A9995 /* ChunkCache.cpp in Sources */, + 84E1C9CF2E7FDC26007D2F5D /* CactusTile.cpp in Sources */, ++ 84358B242F4551730077EA44 /* FurnaceResultSlot.cpp in Sources */, ++ 84358B252F4551730077EA44 /* FurnaceMenu.cpp in Sources */, + 84E7BF1A2F286A08002D3936 /* ArmorSlot.cpp in Sources */, + 84BF63242AF18631008A9995 /* ChunkSource.cpp in Sources */, ++ 84358B192F45511C0077EA44 /* NoteParticle.cpp in Sources */, + 84BF63252AF18631008A9995 /* LevelChunk.cpp in Sources */, + 84BF63262AF18631008A9995 /* PerformanceTestChunkSource.cpp in Sources */, + 84EE51312E8DCC9000D3DCA2 /* DataLayer.cpp in Sources */, +@@ -7026,6 +7141,10 @@ + 84B9D89C2F3BE0A900FD67C4 /* ArmorItem.cpp in Sources */, + 84B9D89D2F3BE0A900FD67C4 /* HoeItem.cpp in Sources */, + 84B9D89E2F3BE0A900FD67C4 /* SeedItem.cpp in Sources */, ++ 84358B102F4550CC0077EA44 /* FurnaceTile.cpp in Sources */, ++ 84358B112F4550CC0077EA44 /* EntityTile.cpp in Sources */, ++ 84358B122F4550CC0077EA44 /* ChestTile.cpp in Sources */, ++ 84358B132F4550CC0077EA44 /* MusicTile.cpp in Sources */, + 84BF63382AF18631008A9995 /* LevelListener.cpp in Sources */, + 84E7BF2F2F286A20002D3936 /* CompoundContainer.cpp in Sources */, + 84BF63392AF18631008A9995 /* Material.cpp in Sources */, +@@ -7097,6 +7216,7 @@ + 84BF63662AF18631008A9995 /* MetalTile.cpp in Sources */, + 84BF63672AF18631008A9995 /* ObsidianTile.cpp in Sources */, + 84CCBC932E61880600E251AF /* EntityFactory.cpp in Sources */, ++ 84358B1E2F4551500077EA44 /* CoalItem.cpp in Sources */, + 84BF63682AF18631008A9995 /* OreTile.cpp in Sources */, + 84B1E0392E04FD7900ED000A /* Arrow.cpp in Sources */, + 84BF63692AF18631008A9995 /* RedStoneOreTile.cpp in Sources */, + +From da4f8640e5a7cc22e72d8186ddaf0df8add4428a Mon Sep 17 00:00:00 2001 +From: Sam <31021951+samdotjpg@users.noreply.github.com> +Date: Tue, 17 Feb 2026 20:53:59 -0500 +Subject: [PATCH 18/22] add to lang file + +--- + game/assets/lang/en_US.lang | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/game/assets/lang/en_US.lang b/game/assets/lang/en_US.lang +index d85d3c0b3..1002d7955 100644 +--- a/game/assets/lang/en_US.lang ++++ b/game/assets/lang/en_US.lang +@@ -104,6 +104,7 @@ options.guiScale.small=Small + options.guiScale.normal=Normal + options.guiScale.large=Large + options.advancedOpengl=Advanced OpenGL ++options.enableVsync=Enable Vsync + + performance.max=Max FPS + performance.balanced=Balanced + +From d46806d9cf49edd66fd97cd83ad806874be0d36a Mon Sep 17 00:00:00 2001 +From: Sam +Date: Tue, 17 Feb 2026 22:18:30 -0500 +Subject: [PATCH 19/22] Make riding / rider code not suck really badly + +--- + source/world/entity/Entity.cpp | 60 +++++++++++++++++++--------------- + source/world/entity/Entity.hpp | 8 +++-- + source/world/entity/Mob.cpp | 2 +- + source/world/level/Level.cpp | 17 ++++------ + 4 files changed, 48 insertions(+), 39 deletions(-) + +diff --git a/source/world/entity/Entity.cpp b/source/world/entity/Entity.cpp +index b70616ba1..8e8ba64a9 100644 +--- a/source/world/entity/Entity.cpp ++++ b/source/world/entity/Entity.cpp +@@ -28,8 +28,8 @@ void Entity::_init() + field_28 = 0; + field_30 = 1.0f; + m_dimensionId = DIMENSION_OVERWORLD; +- m_riderId = 0; +- m_ridingId = 0; ++ _riderId = 0; ++ _ridingId = 0; + m_bRiding = false; + m_bBlocksBuilding = false; + m_pLevel = nullptr; +@@ -474,10 +474,10 @@ void Entity::baseTick() + //@TODO: untangle the gotos + if (const Entity* riding = getRiding()) + { +- if ((!riding && m_riderId > 0) || riding->m_bRemoved) ++ // if you were riding an entity and they no longer exist, stop ++ if ((!riding && _ridingId > 0) || riding->m_bRemoved) + { +- m_riderId = 0; +- setSharedFlag(C_ENTITY_FLAG_RIDING, false); ++ setRiding(nullptr); + } + } + +@@ -756,6 +756,9 @@ void Entity::playerTouch(Player* player) + + void Entity::push(Entity* bud) + { ++ if (bud == getRider() || bud == getRiding()) ++ return; ++ + float diffX = bud->m_pos.x - m_pos.x; + float diffZ = bud->m_pos.z - m_pos.z; + float maxDiff = Mth::absMax(diffX, diffZ); +@@ -963,8 +966,8 @@ void Entity::rideTick() + Entity* riding = getRiding(); + if (!riding || riding->m_bRemoved) + { +- m_riderId = 0; +- setSharedFlag(C_ENTITY_FLAG_RIDING, false); ++ setRiding(nullptr); ++ return; + } + + // we don't move +@@ -1028,12 +1031,11 @@ void Entity::ride(Entity* newRiding) + { + moveTo(oldRiding->m_pos); + setRot(oldRiding->m_rot); +- oldRiding->m_riderId = 0; // Let them know you dismounted them ++ oldRiding->setRider(nullptr); + } + + // Let yourself know you aren't riding anything +- m_ridingId = 0; +- setSharedFlag(C_ENTITY_FLAG_RIDING, false); ++ setRiding(nullptr); + + return; + } +@@ -1041,10 +1043,9 @@ void Entity::ride(Entity* newRiding) + // Dismount if the same entity is fed in + if (oldRiding && oldRiding == newRiding) + { +- oldRiding->m_riderId = 0; ++ oldRiding->setRider(nullptr); + +- m_ridingId = 0; +- setSharedFlag(C_ENTITY_FLAG_RIDING, false); ++ setRiding(nullptr); + + moveTo(oldRiding->m_pos); + setRot(oldRiding->m_rot); +@@ -1054,31 +1055,27 @@ void Entity::ride(Entity* newRiding) + // if (this.riding != null) this.riding.rider = null; + if (oldRiding) + { +- oldRiding->m_riderId = 0; ++ oldRiding->setRider(nullptr); + } + + // if (newRiding.rider != null) newRiding.rider.riding = null; + // i hate this name but it's literally what it is + if (Entity* newRidesOldRider = newRiding->getRider()) + { +- newRidesOldRider->m_ridingId = 0; +- newRidesOldRider->setSharedFlag(C_ENTITY_FLAG_RIDING, false); ++ setRiding(nullptr); ++ newRidesOldRider->setRider(nullptr); + } + +- // Tell yourself that you're riding the new ride +- m_ridingId = newRiding->m_EntityID; +- setSharedFlag(C_ENTITY_FLAG_RIDING, true); +- +- // Tell the new ride that it's being ridden by you +- newRiding->m_riderId = m_EntityID; ++ setRiding(newRiding); ++ newRiding->setRider(this); + } + + Entity* Entity::getRiding() const + { +- if (m_ridingId <= 0) ++ if (_ridingId <= 0) + return nullptr; + +- if (Entity* riding = m_pLevel->getEntity(m_ridingId)) ++ if (Entity* riding = m_pLevel->getEntity(_ridingId)) + return riding; + + return nullptr; +@@ -1086,15 +1083,26 @@ Entity* Entity::getRiding() const + + Entity* Entity::getRider() const + { +- if (m_riderId <= 0) ++ if (_riderId <= 0) + return nullptr; + +- if (Entity* rider = m_pLevel->getEntity(m_riderId)) ++ if (Entity* rider = m_pLevel->getEntity(_riderId)) + return rider; + + return nullptr; + } + ++void Entity::setRider(Entity* rider) ++{ ++ _riderId = (rider) ? rider->m_EntityID : 0; ++} ++ ++void Entity::setRiding(Entity* riding) ++{ ++ _ridingId = (riding) ? riding->m_EntityID : 0; ++ setSharedFlag(C_ENTITY_FLAG_RIDING, riding); ++} ++ + /*void Entity::thunderHit(LightningBolt* bolt) + { + burn(5); +diff --git a/source/world/entity/Entity.hpp b/source/world/entity/Entity.hpp +index e3b53d6a1..9fec6801f 100644 +--- a/source/world/entity/Entity.hpp ++++ b/source/world/entity/Entity.hpp +@@ -207,6 +207,8 @@ class Entity + virtual float getRidingHeight() const { return m_heightOffset; } + Entity* getRiding() const; + Entity* getRider() const; ++ void setRiding(Entity* ent); ++ void setRider(Entity* ent); + void load(const CompoundTag& tag); + bool save(CompoundTag& tag) const; + void saveWithoutId(CompoundTag& tag) const; +@@ -231,6 +233,10 @@ class Entity + (m_pos.z - pos.z) * (m_pos.z - pos.z); + } + ++private: ++ Entity::ID _ridingId; ++ Entity::ID _riderId; ++ + protected: + SynchedEntityData m_entityData; + bool m_bMakeStepSound; +@@ -248,8 +254,6 @@ class Entity + float field_30; + //TileSource* m_pTileSource; + DimensionId m_dimensionId; +- Entity::ID m_ridingId; +- Entity::ID m_riderId; + bool m_bRiding; + bool m_bBlocksBuilding; + Level* m_pLevel; +diff --git a/source/world/entity/Mob.cpp b/source/world/entity/Mob.cpp +index 6dede3f9b..459ef5571 100644 +--- a/source/world/entity/Mob.cpp ++++ b/source/world/entity/Mob.cpp +@@ -758,7 +758,7 @@ void Mob::aiStep() + { + Entity* pEnt = *it; + if (pEnt->isPushable()) +- pEnt->push(this); ++ pEnt->push(this); + } + } + +diff --git a/source/world/level/Level.cpp b/source/world/level/Level.cpp +index 0c1efd729..08d495c18 100644 +--- a/source/world/level/Level.cpp ++++ b/source/world/level/Level.cpp +@@ -1260,9 +1260,8 @@ void Level::removeAllPendingEntityRemovals() + { + if (riding->m_bRemoved || riding->getRider() != ent) + { +- riding->m_riderId = 0; +- ent->m_ridingId = 0; +- ent->setSharedFlag(C_ENTITY_FLAG_RIDING, false); ++ riding->setRider(nullptr); ++ ent->setRiding(nullptr); + } + else + continue; +@@ -1742,14 +1741,13 @@ void Level::tick(Entity* pEnt, bool shouldTick) + if (shouldTick && pEnt->m_bInAChunk) + { + Entity* rider = pEnt->getRider(); ++ // someone is riding this entity + if (rider) + { + if (rider->m_bRemoved || rider->getRiding() != pEnt) + { +- rider->m_riderId = 0; +- +- pEnt->m_ridingId = 0; +- pEnt->setSharedFlag(C_ENTITY_FLAG_RIDING, false); ++ rider->setRiding(nullptr); ++ pEnt->setRider(nullptr); + } + else + { +@@ -1797,9 +1795,8 @@ void Level::tickEntities() + { + if (riding->m_bRemoved || riding->getRider() != pEnt) + { +- riding->m_riderId = 0; +- pEnt->m_ridingId = 0; +- pEnt->setSharedFlag(C_ENTITY_FLAG_RIDING, false); ++ riding->setRider(nullptr); ++ pEnt->setRiding(nullptr); + } + else + { + +From 6bea080d8b33da01a06726233cb517584222a2f7 Mon Sep 17 00:00:00 2001 +From: Brent Da Mage +Date: Wed, 18 Feb 2026 02:23:38 -0600 +Subject: [PATCH 20/22] Fixed container listening and networking + +--- + .../windows/projects/World/World.vcxproj | 14 ++-- + .../projects/World/World.vcxproj.filters | 42 +++++++---- + .../multiplayer/MultiplayerLocalPlayer.cpp | 12 ++- + .../multiplayer/MultiplayerLocalPlayer.hpp | 3 +- + .../network/ClientSideNetworkHandler.cpp | 6 ++ + source/client/player/LocalPlayer.cpp | 10 +++ + source/network/Packet.hpp | 1 + + source/network/PacketUtil.cpp | 5 ++ + .../network/packets/ContainerOpenPacket.hpp | 2 +- + .../packets/ContainerSetContentPacket.cpp | 2 +- + source/server/ServerPlayer.cpp | 6 ++ + source/server/ServerPlayer.hpp | 3 +- + source/world/Container.hpp | 37 ---------- + source/world/entity/Player.cpp | 26 +++++-- + source/world/entity/Player.hpp | 5 +- + source/world/inventory/ArmorSlot.hpp | 2 +- + source/world/inventory/ChestMenu.hpp | 2 +- + .../{ => inventory}/CompoundContainer.cpp | 6 +- + .../{ => inventory}/CompoundContainer.hpp | 2 +- + source/world/inventory/Container.hpp | 55 ++++++++++++++ + .../ContainerContentChangeListener.cpp | 2 + + .../ContainerContentChangeListener.hpp | 12 +++ + .../{ => inventory}/ContainerListener.cpp | 2 +- + .../{ => inventory}/ContainerListener.hpp | 0 + source/world/inventory/ContainerMenu.cpp | 74 ++++++++++++++----- + source/world/inventory/ContainerMenu.hpp | 22 ++++-- + .../inventory/ContainerSizeChangeListener.cpp | 1 + + .../inventory/ContainerSizeChangeListener.hpp | 12 +++ + source/world/inventory/CraftingContainer.cpp | 2 +- + source/world/inventory/CraftingContainer.hpp | 4 +- + source/world/inventory/CraftingMenu.cpp | 3 + + source/world/inventory/FurnaceMenu.cpp | 4 +- + source/world/inventory/FurnaceResultSlot.hpp | 2 +- + source/world/inventory/InventoryMenu.cpp | 3 + + source/world/inventory/ResultContainer.cpp | 2 +- + source/world/inventory/ResultContainer.hpp | 4 +- + source/world/inventory/ResultSlot.hpp | 2 +- + source/world/inventory/SimpleContainer.cpp | 51 ++++++++++--- + source/world/inventory/SimpleContainer.hpp | 15 +++- + source/world/inventory/Slot.hpp | 8 +- + source/world/item/Inventory.hpp | 6 +- + source/world/item/crafting/Recipe.hpp | 2 +- + source/world/tile/ChestTile.cpp | 2 +- + source/world/tile/entity/ChestTileEntity.cpp | 2 +- + source/world/tile/entity/ChestTileEntity.hpp | 2 +- + .../world/tile/entity/FurnaceTileEntity.cpp | 3 +- + .../world/tile/entity/FurnaceTileEntity.hpp | 2 +- + 47 files changed, 346 insertions(+), 139 deletions(-) + delete mode 100644 source/world/Container.hpp + rename source/world/{ => inventory}/CompoundContainer.cpp (90%) + rename source/world/{ => inventory}/CompoundContainer.hpp (92%) + create mode 100644 source/world/inventory/Container.hpp + create mode 100644 source/world/inventory/ContainerContentChangeListener.cpp + create mode 100644 source/world/inventory/ContainerContentChangeListener.hpp + rename source/world/{ => inventory}/ContainerListener.cpp (81%) + rename source/world/{ => inventory}/ContainerListener.hpp (100%) + create mode 100644 source/world/inventory/ContainerSizeChangeListener.cpp + create mode 100644 source/world/inventory/ContainerSizeChangeListener.hpp + +diff --git a/platforms/windows/projects/World/World.vcxproj b/platforms/windows/projects/World/World.vcxproj +index 7247155f5..7b610602c 100644 +--- a/platforms/windows/projects/World/World.vcxproj ++++ b/platforms/windows/projects/World/World.vcxproj +@@ -202,7 +202,7 @@ + + + +- ++ + + + +@@ -210,7 +210,7 @@ + + + +- ++ + + + +@@ -242,6 +242,8 @@ + + + ++ ++ + + + +@@ -385,8 +387,8 @@ + + + +- +- ++ ++ + + + +@@ -394,7 +396,7 @@ + + + +- ++ + + + +@@ -423,6 +425,8 @@ + + + ++ ++ + + + +diff --git a/platforms/windows/projects/World/World.vcxproj.filters b/platforms/windows/projects/World/World.vcxproj.filters +index 6c9bbd804..85317d225 100644 +--- a/platforms/windows/projects/World/World.vcxproj.filters ++++ b/platforms/windows/projects/World/World.vcxproj.filters +@@ -587,9 +587,6 @@ + + Source Files\Level\LevelGen\Chunk + +- +- Source Files +- + + Source Files\Inventory + +@@ -614,9 +611,6 @@ + + Source Files\Inventory + +- +- Source Files +- + + Source Files\Inventory + +@@ -707,6 +701,18 @@ + + Source Files\Tile\Entity + ++ ++ Source Files\Inventory ++ ++ ++ Source Files\Inventory ++ ++ ++ Source Files\Inventory ++ ++ ++ Source Files\Inventory ++ + + + +@@ -1132,12 +1138,6 @@ + + Header Files\Level + +- +- Header Files +- +- +- Header Files +- + + Header Files\Inventory + +@@ -1159,9 +1159,6 @@ + + Header Files\Inventory + +- +- Header Files +- + + Header Files\Inventory + +@@ -1246,5 +1243,20 @@ + + Header Files\Tile + ++ ++ Header Files\Inventory ++ ++ ++ Header Files\Inventory ++ ++ ++ Header Files\Inventory ++ ++ ++ Header Files\Inventory ++ ++ ++ Header Files\Inventory ++ + + +\ No newline at end of file +diff --git a/source/client/multiplayer/MultiplayerLocalPlayer.cpp b/source/client/multiplayer/MultiplayerLocalPlayer.cpp +index 85742cbbf..36750209d 100644 +--- a/source/client/multiplayer/MultiplayerLocalPlayer.cpp ++++ b/source/client/multiplayer/MultiplayerLocalPlayer.cpp +@@ -15,6 +15,14 @@ void MultiplayerLocalPlayer::reallyDrop(ItemEntity* itemEntity) + { + } + ++void MultiplayerLocalPlayer::_handleOpenedContainerMenu() ++{ ++ if (m_pContainerMenu) ++ m_pContainerMenu->addSlotListener(this); ++ else ++ LOG_W("Tried to add MultiplayerLocalPlayer as ContainerListener for NULL container!"); ++} ++ + bool MultiplayerLocalPlayer::hurt(Entity* pAttacker, int damage) + { + // Java returns false +@@ -135,5 +143,7 @@ void MultiplayerLocalPlayer::refreshContainer(ContainerMenu* menu, const std::ve + + void MultiplayerLocalPlayer::slotChanged(ContainerMenu* menu, int index, ItemStack& item, bool isResultSlot) + { +- // @TODO: Replicate ContainerSetSlotPacket ++#if NETWORK_PROTOCOL_VERSION >= 5 ++ m_pMinecraft->m_pRakNetInstance->send(new ContainerSetSlotPacket(m_pContainerMenu->m_containerId, index, item)); ++#endif + } +diff --git a/source/client/multiplayer/MultiplayerLocalPlayer.hpp b/source/client/multiplayer/MultiplayerLocalPlayer.hpp +index d1a7e2bec..6ab1a580b 100644 +--- a/source/client/multiplayer/MultiplayerLocalPlayer.hpp ++++ b/source/client/multiplayer/MultiplayerLocalPlayer.hpp +@@ -1,7 +1,7 @@ + #pragma once + + #include "client/player/LocalPlayer.hpp" +-#include "world/ContainerListener.hpp" ++#include "world/inventory/ContainerListener.hpp" + + class MultiplayerLocalPlayer : public LocalPlayer, public ContainerListener + { +@@ -10,6 +10,7 @@ class MultiplayerLocalPlayer : public LocalPlayer, public ContainerListener + + protected: + void reallyDrop(ItemEntity* itemEntity) override; ++ void _handleOpenedContainerMenu() override; + + public: + bool hurt(Entity*, int) override; +diff --git a/source/client/network/ClientSideNetworkHandler.cpp b/source/client/network/ClientSideNetworkHandler.cpp +index a1392ef6e..5079976d5 100644 +--- a/source/client/network/ClientSideNetworkHandler.cpp ++++ b/source/client/network/ClientSideNetworkHandler.cpp +@@ -777,7 +777,9 @@ void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& guid, ContainerS + if (pContainerMenu->m_containerId != packet->m_containerId) + return; + ++ pContainerMenu->m_bBroadcastChanges = false; + pContainerMenu->setItem(packet->m_slot, packet->m_item); ++ pContainerMenu->m_bBroadcastChanges = true; + } + + void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& guid, ContainerSetDataPacket* packet) +@@ -798,7 +800,9 @@ void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& guid, ContainerS + if (pContainerMenu->m_containerId != packet->m_containerId) + return; + ++ pContainerMenu->m_bBroadcastChanges = false; + pContainerMenu->setData(packet->m_slot, packet->m_value); ++ pContainerMenu->m_bBroadcastChanges = true; + } + + void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& guid, ContainerSetContentPacket* packet) +@@ -819,7 +823,9 @@ void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& guid, ContainerS + if (pContainerMenu->m_containerId != packet->m_containerId) + return; + ++ pContainerMenu->m_bBroadcastChanges = false; + pContainerMenu->setAll(packet->m_items); ++ pContainerMenu->m_bBroadcastChanges = true; + } + + void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& guid, LevelDataPacket* packet) +diff --git a/source/client/player/LocalPlayer.cpp b/source/client/player/LocalPlayer.cpp +index 6ca19d378..787d65806 100644 +--- a/source/client/player/LocalPlayer.cpp ++++ b/source/client/player/LocalPlayer.cpp +@@ -133,18 +133,24 @@ void LocalPlayer::swing() + void LocalPlayer::startCrafting(const TilePos& pos) + { + m_pMinecraft->getScreenChooser()->pushCraftingScreen(this, pos); ++ ++ Player::startCrafting(pos); + } + + void LocalPlayer::openFurnace(FurnaceTileEntity* furnace) + { + // PE 0.3.2 doesn't let you cook in creative mode + m_pMinecraft->getScreenChooser()->pushFurnaceScreen(this, furnace); ++ ++ Player::openFurnace(furnace); + } + + void LocalPlayer::openContainer(Container* container) + { + // PE 0.3.2 doesn't let you open chests in creative mode + m_pMinecraft->getScreenChooser()->pushChestScreen(this, container); ++ ++ Player::openContainer(container); + } + + void LocalPlayer::closeContainer() +@@ -157,11 +163,15 @@ void LocalPlayer::closeContainer() + /*void LocalPlayer::openTrap(DispenserTileEntity* tileEntity) + { + m_pMinecraft->setScreen(new TrapScreen(m_pInventory, tileEntity)); ++ ++ Player::openTrap(tileEntity); + }*/ + + /*void LocalPlayer::openTextEdit(SignTileEntity* tileEntity) + { + m_pMinecraft->setScreen(new TextEditScreen(tileEntity)); ++ ++ Player::openTextEdit(tileEntity); + }*/ + + void LocalPlayer::reset() +diff --git a/source/network/Packet.hpp b/source/network/Packet.hpp +index 4825bee6f..4d7a538d5 100644 +--- a/source/network/Packet.hpp ++++ b/source/network/Packet.hpp +@@ -19,6 +19,7 @@ + //#define NETWORK_PROTOCOL_VERSION 4 // 0.3.0 + //#define NETWORK_PROTOCOL_VERSION 5 // 0.3.2 + #define NETWORK_PROTOCOL_VERSION 6 // 0.3.3 ++//#define NETWORK_PROTOCOL_VERSION 29 // 0.12.1 + + class NetEventCallback; + class Level; +diff --git a/source/network/PacketUtil.cpp b/source/network/PacketUtil.cpp +index f6000cb85..8be6f9bbe 100644 +--- a/source/network/PacketUtil.cpp ++++ b/source/network/PacketUtil.cpp +@@ -1,4 +1,5 @@ + #include "PacketUtil.hpp" ++#include "Packet.hpp" + #include "nbt/NbtIo.hpp" + + char PacketUtil::Rot_degreesToChar(float degrees) +@@ -146,12 +147,14 @@ void PacketUtil::WriteItemStack(const ItemStack& item, RakNet::BitStream& bs, bo + int16_t itemId = item.getId(); + int8_t count = item.m_count; + int16_t auxValue = item.getAuxValue(); ++#if NETWORK_PROTOCOL_VERSION >= 29 + if (itemId <= 0) + { + itemId = -1; + bs.Write(itemId); + return; + } ++#endif + + bs.Write(itemId); + bs.Write(count); +@@ -166,8 +169,10 @@ ItemStack PacketUtil::ReadItemStack(RakNet::BitStream& bs, bool doUserData) + if (!bs.Read(itemId)) + return ItemStack(); + ++#if NETWORK_PROTOCOL_VERSION >= 29 + if (itemId == ItemStack::EMPTY.getId()) + return ItemStack(); ++#endif + + uint8_t count; + int16_t auxValue; +diff --git a/source/network/packets/ContainerOpenPacket.hpp b/source/network/packets/ContainerOpenPacket.hpp +index 11fc81f6c..648d6569b 100644 +--- a/source/network/packets/ContainerOpenPacket.hpp ++++ b/source/network/packets/ContainerOpenPacket.hpp +@@ -2,7 +2,7 @@ + + #include + #include "../Packet.hpp" +-#include "world/Container.hpp" ++#include "world/inventory/Container.hpp" + + class ContainerOpenPacket : public Packet + { +diff --git a/source/network/packets/ContainerSetContentPacket.cpp b/source/network/packets/ContainerSetContentPacket.cpp +index 02b5b07b5..cc1552065 100644 +--- a/source/network/packets/ContainerSetContentPacket.cpp ++++ b/source/network/packets/ContainerSetContentPacket.cpp +@@ -30,7 +30,7 @@ void ContainerSetContentPacket::read(RakNet::BitStream& bs) + bs.Read(m_containerId); + int16_t size = 0; + bs.Read(size); +- m_items.resize(size); ++ m_items.reserve(size); + + for (uint16_t i = 0; i < size; i++) + { +diff --git a/source/server/ServerPlayer.cpp b/source/server/ServerPlayer.cpp +index 8a0aad136..a2a5d7cc5 100644 +--- a/source/server/ServerPlayer.cpp ++++ b/source/server/ServerPlayer.cpp +@@ -24,6 +24,11 @@ ServerPlayer::ServerPlayer(Level* pLevel, GameType playerGameType) + m_pInventoryMenu->addSlotListener(this); + } + ++ServerPlayer::~ServerPlayer() ++{ ++ doCloseContainer(); ++} ++ + void ServerPlayer::_nextContainerCounter() + { + m_containerId++; +@@ -156,5 +161,6 @@ void ServerPlayer::setContainerMenu(ContainerMenu* menu) + { + m_pContainerMenu->m_containerId = m_containerId; + m_pContainerMenu->addSlotListener(this); ++ refreshContainer(m_pContainerMenu, m_pContainerMenu->cloneItems()); + } + } +\ No newline at end of file +diff --git a/source/server/ServerPlayer.hpp b/source/server/ServerPlayer.hpp +index 11152a123..874a0f1cc 100644 +--- a/source/server/ServerPlayer.hpp ++++ b/source/server/ServerPlayer.hpp +@@ -1,10 +1,11 @@ + #include "world/entity/Player.hpp" +-#include "world/ContainerListener.hpp" ++#include "world/inventory/ContainerListener.hpp" + + class ServerPlayer : public Player, public ContainerListener + { + public: + ServerPlayer(Level* pLevel, GameType playerGameType); ++ ~ServerPlayer(); + + protected: + void _nextContainerCounter(); +diff --git a/source/world/Container.hpp b/source/world/Container.hpp +deleted file mode 100644 +index 8c18f3a6c..000000000 +--- a/source/world/Container.hpp ++++ /dev/null +@@ -1,37 +0,0 @@ +-#pragma once +- +-#include "world/item/ItemStack.hpp" +- +-#define C_MAX_CONTAINER_STACK_SIZE (64) +- +-class Container +-{ +-public: +- enum Type +- { +- CONTAINER, +- CRAFTING, +- FURNACE, +- DISPENSER +- }; +- +-public: +- virtual uint16_t getContainerSize() const = 0; +- virtual ItemStack& getItem(int index) = 0; +- virtual ItemStack* tryGetItem(int index) +- { +- if (index >= 0 && index < getContainerSize()) +- return &getItem(index); +- else +- return nullptr; +- } +- virtual ItemStack removeItem(int index, int count) = 0; +- virtual void setItem(int index, const ItemStack& item) = 0; +- virtual std::string getName() const = 0; +- virtual int getMaxStackSize() +- { +- return C_MAX_CONTAINER_STACK_SIZE; +- } +- virtual void setChanged() = 0; +- virtual bool stillValid(Player* player) const = 0; +-}; +\ No newline at end of file +diff --git a/source/world/entity/Player.cpp b/source/world/entity/Player.cpp +index 95193e007..2ce2c9012 100644 +--- a/source/world/entity/Player.cpp ++++ b/source/world/entity/Player.cpp +@@ -63,8 +63,8 @@ Player::Player(Level* pLevel, GameType playerGameType) : Mob(pLevel) + + Player::~Player() + { +- delete m_pInventory; + delete m_pInventoryMenu; ++ delete m_pInventory; + } + + void Player::reallyDrop(ItemEntity* pEnt) +@@ -72,6 +72,10 @@ void Player::reallyDrop(ItemEntity* pEnt) + m_pLevel->addEntity(pEnt); + } + ++void Player::_handleOpenedContainerMenu() ++{ ++} ++ + void Player::reset() + { + Mob::reset(); +@@ -563,14 +567,12 @@ void Player::drop(const ItemStack& item, bool randomly) + + void Player::startCrafting(const TilePos& pos) + { +-} +- +-void Player::openFurnace(FurnaceTileEntity* tileEntity) +-{ ++ _handleOpenedContainerMenu(); + } + + void Player::startStonecutting(const TilePos& pos) + { ++ _handleOpenedContainerMenu(); + } + + void Player::startDestroying() +@@ -583,6 +585,20 @@ void Player::stopDestroying() + m_destroyingBlock = false; + } + ++void Player::openFurnace(FurnaceTileEntity* tileEntity) ++{ ++ _handleOpenedContainerMenu(); ++} ++ ++void Player::openContainer(Container* container) ++{ ++ _handleOpenedContainerMenu(); ++} ++ ++void Player::closeContainer() ++{ ++} ++ + void Player::touch(Entity* pEnt) + { + pEnt->playerTouch(this); +diff --git a/source/world/entity/Player.hpp b/source/world/entity/Player.hpp +index 3ba5345a2..c2c5dcdd2 100644 +--- a/source/world/entity/Player.hpp ++++ b/source/world/entity/Player.hpp +@@ -41,6 +41,7 @@ class Player : public Mob + + protected: + virtual void reallyDrop(ItemEntity* pEnt); ++ virtual void _handleOpenedContainerMenu(); + + public: + void reset() override; +@@ -74,8 +75,8 @@ class Player : public Mob + virtual void startDestroying(); + virtual void stopDestroying(); + virtual void openFurnace(FurnaceTileEntity* tileEntity); +- virtual void openContainer(Container* container) {} +- virtual void closeContainer() {} ++ virtual void openContainer(Container* container); ++ virtual void closeContainer(); + //virtual void openTrap(DispenserTileEntity* tileEntity); + //virtual void openTextEdit(SignTileEntity* tileEntity); + virtual bool isLocalPlayer() const { return false; } +diff --git a/source/world/inventory/ArmorSlot.hpp b/source/world/inventory/ArmorSlot.hpp +index 2b679e8d9..0e54f8ab5 100644 +--- a/source/world/inventory/ArmorSlot.hpp ++++ b/source/world/inventory/ArmorSlot.hpp +@@ -1,7 +1,7 @@ + #pragma once + + #include "Slot.hpp" +-#include "world/Container.hpp" ++#include "Container.hpp" + + class ArmorSlot : public Slot + { +diff --git a/source/world/inventory/ChestMenu.hpp b/source/world/inventory/ChestMenu.hpp +index ea787bea3..04eb68da5 100644 +--- a/source/world/inventory/ChestMenu.hpp ++++ b/source/world/inventory/ChestMenu.hpp +@@ -1,7 +1,7 @@ + #pragma once + + #include "ContainerMenu.hpp" +-#include "world/Container.hpp" ++#include "Container.hpp" + #include "world/entity/Player.hpp" + + class ChestMenu : public ContainerMenu +diff --git a/source/world/CompoundContainer.cpp b/source/world/inventory/CompoundContainer.cpp +similarity index 90% +rename from source/world/CompoundContainer.cpp +rename to source/world/inventory/CompoundContainer.cpp +index 80e339b90..ea4d4dd1c 100644 +--- a/source/world/CompoundContainer.cpp ++++ b/source/world/inventory/CompoundContainer.cpp +@@ -44,10 +44,10 @@ int CompoundContainer::getMaxStackSize() + return m_pLeftContainer->getMaxStackSize(); + } + +-void CompoundContainer::setChanged() ++void CompoundContainer::setContainerChanged(SlotID slot) + { +- m_pLeftContainer->setChanged(); +- m_pRightContainer->setChanged(); ++ m_pLeftContainer->setContainerChanged(slot); ++ m_pRightContainer->setContainerChanged(slot); + } + + bool CompoundContainer::stillValid(Player* player) const +diff --git a/source/world/CompoundContainer.hpp b/source/world/inventory/CompoundContainer.hpp +similarity index 92% +rename from source/world/CompoundContainer.hpp +rename to source/world/inventory/CompoundContainer.hpp +index 5a4884591..1c3fd0fea 100644 +--- a/source/world/CompoundContainer.hpp ++++ b/source/world/inventory/CompoundContainer.hpp +@@ -25,7 +25,7 @@ class CompoundContainer : public Container + + int getMaxStackSize() override; + +- void setChanged() override; ++ void setContainerChanged(SlotID slot) override; + + bool stillValid(Player* player) const override; + }; +diff --git a/source/world/inventory/Container.hpp b/source/world/inventory/Container.hpp +new file mode 100644 +index 000000000..6e6956689 +--- /dev/null ++++ b/source/world/inventory/Container.hpp +@@ -0,0 +1,55 @@ ++#pragma once ++ ++#include ++ ++#include "world/item/ItemStack.hpp" ++ ++#define C_MAX_CONTAINER_STACK_SIZE (64) ++ ++class ContainerContentChangeListener; ++class ContainerSizeChangeListener; ++ ++class Container ++{ ++protected: ++ typedef std::set ContentChangeListeners; ++ typedef std::set SizeChangeListeners; ++ ++public: ++ typedef uint16_t Size; ++ ++public: ++ enum Type ++ { ++ CONTAINER, ++ CRAFTING, ++ FURNACE, ++ DISPENSER ++ }; ++ ++public: ++ virtual Size getContainerSize() const = 0; ++ virtual ItemStack& getItem(int index) = 0; ++ virtual ItemStack* tryGetItem(int index) ++ { ++ if (index >= 0 && index < getContainerSize()) ++ return &getItem(index); ++ else ++ return nullptr; ++ } ++ virtual ItemStack removeItem(int index, int count) = 0; ++ virtual void setItem(int index, const ItemStack& item) = 0; ++ virtual std::string getName() const = 0; ++ virtual int getMaxStackSize() ++ { ++ return C_MAX_CONTAINER_STACK_SIZE; ++ } ++ // Was called setChanged in Java ++ virtual void setContainerChanged(SlotID slot) = 0; ++ virtual bool stillValid(Player* player) const = 0; ++ ++ virtual void addContentChangeListener(ContainerContentChangeListener* listener) {} ++ virtual void addSizeChangeListener(ContainerSizeChangeListener* listener) {} ++ virtual void removeContentChangeListener(ContainerContentChangeListener* listener) {} ++ virtual void removeSizeChangeListener(ContainerSizeChangeListener* listener) {} ++}; +\ No newline at end of file +diff --git a/source/world/inventory/ContainerContentChangeListener.cpp b/source/world/inventory/ContainerContentChangeListener.cpp +new file mode 100644 +index 000000000..bb72498aa +--- /dev/null ++++ b/source/world/inventory/ContainerContentChangeListener.cpp +@@ -0,0 +1,2 @@ ++#include "ContainerContentChangeListener.hpp" ++ +diff --git a/source/world/inventory/ContainerContentChangeListener.hpp b/source/world/inventory/ContainerContentChangeListener.hpp +new file mode 100644 +index 000000000..675f1b221 +--- /dev/null ++++ b/source/world/inventory/ContainerContentChangeListener.hpp +@@ -0,0 +1,12 @@ ++#pragma once ++#include ++#include "Container.hpp" ++ ++class ContainerContentChangeListener ++{ ++public: ++ virtual ~ContainerContentChangeListener() {} ++ ++public: ++ virtual void containerContentChanged(SlotID slot) = 0; ++}; +diff --git a/source/world/ContainerListener.cpp b/source/world/inventory/ContainerListener.cpp +similarity index 81% +rename from source/world/ContainerListener.cpp +rename to source/world/inventory/ContainerListener.cpp +index ddfc041fa..d0377bd7c 100644 +--- a/source/world/ContainerListener.cpp ++++ b/source/world/inventory/ContainerListener.cpp +@@ -7,5 +7,5 @@ ContainerListener::~ContainerListener() + + void ContainerListener::refreshContainerItems(ContainerMenu* menu) + { +- refreshContainer(menu, menu->copyItems()); ++ refreshContainer(menu, menu->cloneItems()); + } +diff --git a/source/world/ContainerListener.hpp b/source/world/inventory/ContainerListener.hpp +similarity index 100% +rename from source/world/ContainerListener.hpp +rename to source/world/inventory/ContainerListener.hpp +diff --git a/source/world/inventory/ContainerMenu.cpp b/source/world/inventory/ContainerMenu.cpp +index f618561e3..7e52498df 100644 +--- a/source/world/inventory/ContainerMenu.cpp ++++ b/source/world/inventory/ContainerMenu.cpp +@@ -1,14 +1,15 @@ + #include "ContainerMenu.hpp" +-#include "Slot.hpp" + #include "world/item/ItemStack.hpp" + #include "world/item/Inventory.hpp" +-#include "world/Container.hpp" +-#include "world/ContainerListener.hpp" ++#include "Slot.hpp" ++#include "Container.hpp" ++#include "ContainerListener.hpp" + + ContainerMenu::ContainerMenu(Container::Type containerType) + : m_changeUid(0) + , m_containerId(0) + , m_containerType(containerType) ++ , m_bBroadcastChanges(true) + { + } + +@@ -18,8 +19,24 @@ ContainerMenu::~ContainerMenu() + /*for (std::vector::iterator it = m_listeners.begin(); it != m_listeners.end(); ++it) + delete (*it);*/ + ++ _clearSlots(); ++} ++ ++void ContainerMenu::_clearSlots() ++{ + for (std::vector::iterator it = m_slots.begin(); it != m_slots.end(); ++it) +- delete (*it); ++ { ++ Slot* slot = *it; ++ ++ // @HACK: I don't like this ++ Container* pContainer = slot->m_pContainer; ++ if (pContainer) ++ pContainer->removeContentChangeListener(this); ++ ++ delete slot; ++ } ++ ++ m_slots.clear(); + } + + void ContainerMenu::addSlot(Slot* slot) +@@ -27,35 +44,45 @@ void ContainerMenu::addSlot(Slot* slot) + slot->m_index = m_slots.size(); + m_slots.push_back(slot); + m_lastSlots.push_back(ItemStack::EMPTY); ++ ++ // @HACK: holy hack ++ Container* pContainer = slot->m_pContainer; ++ if (pContainer) ++ pContainer->addContentChangeListener(this); + } + + void ContainerMenu::addSlotListener(ContainerListener* listener) + { +- m_listeners.push_back(listener); ++ m_listeners.insert(listener); + + // Not done on PE +- /*std::vector snapshot = copyItems(); ++ /*std::vector snapshot = cloneItems(); + listener->refreshContainer(this, snapshot); + broadcastChanges();*/ + } + + void ContainerMenu::sendData(int id, int value) + { +- for (std::vector::iterator it = m_listeners.begin(); it != m_listeners.end(); ++it) ++ for (ContainerListeners::iterator it = m_listeners.begin(); it != m_listeners.end(); ++it) + (*it)->setContainerData(this, id, value); + } + ++void ContainerMenu::broadcastChanges(SlotID slot) ++{ ++ ItemStack& current = m_slots[slot]->getItem(); ++ if (m_lastSlots[slot] != current) ++ { ++ m_lastSlots[slot] = ItemStack(current); ++ for (ContainerListeners::iterator it = m_listeners.begin(); it != m_listeners.end(); ++it) ++ (*it)->slotChanged(this, slot, m_lastSlots[slot], isResultSlot()); ++ } ++} ++ + void ContainerMenu::broadcastChanges() + { + for (size_t i = 0; i < m_slots.size(); ++i) + { +- ItemStack& current = m_slots[i]->getItem(); +- if (m_lastSlots[i] != current) +- { +- m_lastSlots[i] = ItemStack(current); +- for (std::vector::iterator it = m_listeners.begin(); it != m_listeners.end(); ++it) +- (*it)->slotChanged(this, i, m_lastSlots[i], isResultSlot()); +- } ++ broadcastChanges(i); + } + } + +@@ -74,12 +101,15 @@ void ContainerMenu::slotsChanged(Container*) + broadcastChanges(); + } + +-std::vector ContainerMenu::copyItems() ++std::vector ContainerMenu::cloneItems() + { + std::vector content; + + for (std::vector::iterator it = m_slots.begin(); it != m_slots.end(); ++it) +- content.push_back((*it)->getItem()); ++ { ++ const ItemStack& item = (*it)->getItem(); ++ content.push_back(item); ++ } + + return content; + } +@@ -358,13 +388,19 @@ void ContainerMenu::rollbackToBackup(uint16_t) + + bool ContainerMenu::isSynched(Player* player) const + { +- return unsynchedPlayers.find(player) == unsynchedPlayers.end(); ++ return m_unsynchedPlayers.find(player) == m_unsynchedPlayers.end(); + } + + void ContainerMenu::setSynched(Player* player, bool isSynched) + { + if (isSynched) +- unsynchedPlayers.erase(player); ++ m_unsynchedPlayers.erase(player); + else +- unsynchedPlayers.insert(player); ++ m_unsynchedPlayers.insert(player); ++} ++ ++void ContainerMenu::containerContentChanged(SlotID slot) ++{ ++ if (m_bBroadcastChanges) ++ broadcastChanges(slot); + } +diff --git a/source/world/inventory/ContainerMenu.hpp b/source/world/inventory/ContainerMenu.hpp +index f7b299806..481d199be 100644 +--- a/source/world/inventory/ContainerMenu.hpp ++++ b/source/world/inventory/ContainerMenu.hpp +@@ -3,30 +3,38 @@ + #include + #include + #include "world/item/ItemStack.hpp" +-#include "world/Container.hpp" + #include "client/player/input/MouseDevice.hpp" ++#include "Container.hpp" ++#include "ContainerContentChangeListener.hpp" + + class Player; + class Inventory; + class Slot; + class ContainerListener; + +-class ContainerMenu ++class ContainerMenu : public ContainerContentChangeListener + { ++protected: ++ typedef std::set ContainerListeners; ++ + public: + ContainerMenu(Container::Type containerType); + virtual ~ContainerMenu(); + ++protected: ++ void _clearSlots(); ++ + public: + void addSlot(Slot* slot); + virtual void addSlotListener(ContainerListener* listener); + void sendData(int id, int value); ++ virtual void broadcastChanges(SlotID slot); + virtual void broadcastChanges(); + virtual void removed(Player* player); + virtual void slotsChanged(Container* container); + + // Called getItems in PE and Java +- std::vector copyItems(); ++ std::vector cloneItems(); + Slot* getSlotFor(Container* container, int index); + Slot* getSlot(int index); + virtual ItemStack clicked(int slotIndex, MouseButtonType mouseButton, bool quickMove, Player* player); +@@ -50,14 +58,18 @@ class ContainerMenu + //Unused + virtual bool isPauseScreen() const { return false; } + ++public: ++ void containerContentChanged(SlotID slot) override; ++ + protected: + std::vector m_lastSlots; + uint16_t m_changeUid; +- std::vector m_listeners; +- std::set unsynchedPlayers; ++ ContainerListeners m_listeners; ++ std::set m_unsynchedPlayers; + + public: + int m_containerId; + Container::Type m_containerType; + std::vector m_slots; ++ bool m_bBroadcastChanges; + }; +diff --git a/source/world/inventory/ContainerSizeChangeListener.cpp b/source/world/inventory/ContainerSizeChangeListener.cpp +new file mode 100644 +index 000000000..671ca58bd +--- /dev/null ++++ b/source/world/inventory/ContainerSizeChangeListener.cpp +@@ -0,0 +1 @@ ++#include "ContainerSizeChangeListener.hpp" +diff --git a/source/world/inventory/ContainerSizeChangeListener.hpp b/source/world/inventory/ContainerSizeChangeListener.hpp +new file mode 100644 +index 000000000..f39a00780 +--- /dev/null ++++ b/source/world/inventory/ContainerSizeChangeListener.hpp +@@ -0,0 +1,12 @@ ++#pragma once ++#include ++#include "Container.hpp" ++ ++class ContainerSizeChangeListener ++{ ++public: ++ virtual ~ContainerSizeChangeListener() {} ++ ++public: ++ virtual void containerSizeChanged(Container::Size size) = 0; ++}; +diff --git a/source/world/inventory/CraftingContainer.cpp b/source/world/inventory/CraftingContainer.cpp +index 548c6db7b..edfc5a04c 100644 +--- a/source/world/inventory/CraftingContainer.cpp ++++ b/source/world/inventory/CraftingContainer.cpp +@@ -73,7 +73,7 @@ void CraftingContainer::setItem(int index, const ItemStack& item) + } + } + +-void CraftingContainer::setChanged() ++void CraftingContainer::setContainerChanged(SlotID slot) + { + } + +diff --git a/source/world/inventory/CraftingContainer.hpp b/source/world/inventory/CraftingContainer.hpp +index 5fecc7d91..f450e2c9f 100644 +--- a/source/world/inventory/CraftingContainer.hpp ++++ b/source/world/inventory/CraftingContainer.hpp +@@ -1,8 +1,8 @@ + #pragma once + + #include +-#include "world/Container.hpp" + #include "world/item/ItemStack.hpp" ++#include "Container.hpp" + #include "ContainerMenu.hpp" + + class ContainerMenu; +@@ -25,7 +25,7 @@ class CraftingContainer : public Container + ItemStack removeItem(int index, int amount) override; + void setItem(int index, const ItemStack& item) override; + +- void setChanged() override; ++ void setContainerChanged(SlotID slot) override; + bool stillValid(Player* player) const override; + + private: +diff --git a/source/world/inventory/CraftingMenu.cpp b/source/world/inventory/CraftingMenu.cpp +index aeb9d016b..728aad397 100644 +--- a/source/world/inventory/CraftingMenu.cpp ++++ b/source/world/inventory/CraftingMenu.cpp +@@ -36,6 +36,9 @@ CraftingMenu::CraftingMenu(Inventory* inventory, const TilePos& tilePos, Level* + + CraftingMenu::~CraftingMenu() + { ++ _clearSlots(); ++ ++ // clearSlots must be called before these are deleted + delete m_pCraftSlots; + delete m_pResultSlots; + } +diff --git a/source/world/inventory/FurnaceMenu.cpp b/source/world/inventory/FurnaceMenu.cpp +index e8f6678ac..b3c3cb2f3 100644 +--- a/source/world/inventory/FurnaceMenu.cpp ++++ b/source/world/inventory/FurnaceMenu.cpp +@@ -1,7 +1,7 @@ + #include "FurnaceMenu.hpp" + #include "Slot.hpp" + #include "FurnaceResultSlot.hpp" +-#include "world/ContainerListener.hpp" ++#include "ContainerListener.hpp" + + FurnaceMenu::FurnaceMenu(Inventory* inventory, FurnaceTileEntity* furnace) + : ContainerMenu(Container::FURNACE), m_furnace(furnace), m_lastCookTime(0), m_lastBurnTime(0), m_lastLitDuration(0) +@@ -39,7 +39,7 @@ void FurnaceMenu::broadcastChanges() + { + ContainerMenu::broadcastChanges(); + +- for (std::vector::iterator it = m_listeners.begin(); it != m_listeners.end(); ++it) ++ for (ContainerListeners::iterator it = m_listeners.begin(); it != m_listeners.end(); ++it) + { + ContainerListener* listener = *it; + +diff --git a/source/world/inventory/FurnaceResultSlot.hpp b/source/world/inventory/FurnaceResultSlot.hpp +index 4c1e277a1..912b17000 100644 +--- a/source/world/inventory/FurnaceResultSlot.hpp ++++ b/source/world/inventory/FurnaceResultSlot.hpp +@@ -1,7 +1,7 @@ + #pragma once + + #include "Slot.hpp" +-#include "source/world/Container.hpp" ++#include "Container.hpp" + + class FurnaceResultSlot : public Slot + { +diff --git a/source/world/inventory/InventoryMenu.cpp b/source/world/inventory/InventoryMenu.cpp +index 6c5d2a76f..1e5b2c37e 100644 +--- a/source/world/inventory/InventoryMenu.cpp ++++ b/source/world/inventory/InventoryMenu.cpp +@@ -42,6 +42,9 @@ InventoryMenu::InventoryMenu(Inventory* inventory, bool active) + + InventoryMenu::~InventoryMenu() + { ++ _clearSlots(); ++ ++ // clearSlots must be called before these are deleted + delete m_pCraftSlots; + delete m_pResultSlots; + } +diff --git a/source/world/inventory/ResultContainer.cpp b/source/world/inventory/ResultContainer.cpp +index 3f6763d53..1cf3be2d6 100644 +--- a/source/world/inventory/ResultContainer.cpp ++++ b/source/world/inventory/ResultContainer.cpp +@@ -40,7 +40,7 @@ void ResultContainer::setItem(int index, const ItemStack& item) + m_item = item; + } + +-void ResultContainer::setChanged() ++void ResultContainer::setContainerChanged(SlotID slot) + { + } + +diff --git a/source/world/inventory/ResultContainer.hpp b/source/world/inventory/ResultContainer.hpp +index a3d95dc65..14228c1fb 100644 +--- a/source/world/inventory/ResultContainer.hpp ++++ b/source/world/inventory/ResultContainer.hpp +@@ -1,7 +1,7 @@ + #pragma once + +-#include "world/Container.hpp" + #include "world/item/ItemStack.hpp" ++#include "Container.hpp" + + class Player; + +@@ -19,7 +19,7 @@ class ResultContainer : public Container + ItemStack removeItem(int index, int amount) override; + void setItem(int index, const ItemStack& item) override; + +- void setChanged() override; ++ void setContainerChanged(SlotID slot) override; + bool stillValid(Player* player) const override; + + private: +diff --git a/source/world/inventory/ResultSlot.hpp b/source/world/inventory/ResultSlot.hpp +index 92dd109e6..7da681e83 100644 +--- a/source/world/inventory/ResultSlot.hpp ++++ b/source/world/inventory/ResultSlot.hpp +@@ -1,7 +1,7 @@ + #pragma once + + #include "Slot.hpp" +-#include "world/Container.hpp" ++#include "Container.hpp" + + class ResultSlot : public Slot + { +diff --git a/source/world/inventory/SimpleContainer.cpp b/source/world/inventory/SimpleContainer.cpp +index 74b8c4c5d..62d57425c 100644 +--- a/source/world/inventory/SimpleContainer.cpp ++++ b/source/world/inventory/SimpleContainer.cpp +@@ -1,8 +1,10 @@ + #include "SimpleContainer.hpp" ++#include "ContainerContentChangeListener.hpp" ++#include "ContainerSizeChangeListener.hpp" + +-SimpleContainer::SimpleContainer(int size, const std::string& name) : +- m_items(size), +- m_name(name) ++SimpleContainer::SimpleContainer(int size, const std::string& name) ++ : m_items(size) ++ , m_name(name) + { + } + +@@ -25,7 +27,7 @@ ItemStack SimpleContainer::removeItem(int index, int count) + { + result = m_items[index]; + m_items[index] = ItemStack::EMPTY; +- setChanged(); ++ setContainerChanged(index); + return result; + } + else +@@ -34,7 +36,7 @@ ItemStack SimpleContainer::removeItem(int index, int count) + if (!m_items[index].m_count) + m_items[index] = ItemStack::EMPTY; + +- setChanged(); ++ setContainerChanged(index); + return result; + } + } +@@ -47,7 +49,7 @@ void SimpleContainer::setItem(int index, const ItemStack& item) + if (!item.isEmpty() && item.m_count > getMaxStackSize()) + m_items[index].m_count = getMaxStackSize(); + +- setChanged(); ++ setContainerChanged(index); + } + + std::string SimpleContainer::getName() const +@@ -55,8 +57,13 @@ std::string SimpleContainer::getName() const + return m_name; + } + +-void SimpleContainer::setChanged() ++void SimpleContainer::setContainerChanged(SlotID slot) + { ++ for (ContentChangeListeners::iterator it = m_contentChangeListeners.begin(); it != m_contentChangeListeners.end(); it++) ++ { ++ ContainerContentChangeListener* pListener = *it; ++ pListener->containerContentChanged(slot); ++ } + } + + bool SimpleContainer::stillValid(Player* player) const +@@ -64,6 +71,31 @@ bool SimpleContainer::stillValid(Player* player) const + return true; + } + ++void SimpleContainer::addContentChangeListener(ContainerContentChangeListener* listener) ++{ ++ m_contentChangeListeners.insert(listener); ++} ++ ++void SimpleContainer::addSizeChangeListener(ContainerSizeChangeListener* listener) ++{ ++ m_sizeChangeListeners.insert(listener); ++} ++ ++void SimpleContainer::removeContentChangeListener(ContainerContentChangeListener* listener) ++{ ++ m_contentChangeListeners.erase(listener); ++} ++ ++void SimpleContainer::removeSizeChangeListener(ContainerSizeChangeListener* listener) ++{ ++ m_sizeChangeListeners.erase(listener); ++} ++ ++void SimpleContainer::clear() ++{ ++ std::fill(m_items.begin(), m_items.end(), ItemStack::EMPTY); ++} ++ + void SimpleContainer::load(const CompoundTag& tag) + { + clear(); +@@ -102,8 +134,3 @@ void SimpleContainer::save(CompoundTag& tag) const + + tag.put("Items", list); + } +- +-void SimpleContainer::clear() +-{ +- std::fill(m_items.begin(), m_items.end(), ItemStack::EMPTY); +-} +\ No newline at end of file +diff --git a/source/world/inventory/SimpleContainer.hpp b/source/world/inventory/SimpleContainer.hpp +index a371d2532..2df4756a4 100644 +--- a/source/world/inventory/SimpleContainer.hpp ++++ b/source/world/inventory/SimpleContainer.hpp +@@ -1,9 +1,10 @@ + #pragma once + +-#include "world/Container.hpp" +-#include "nbt/CompoundTag.hpp" + #include + ++#include "Container.hpp" ++#include "nbt/CompoundTag.hpp" ++ + class SimpleContainer : public Container + { + public: +@@ -15,15 +16,21 @@ class SimpleContainer : public Container + ItemStack removeItem(int index, int count) override; + void setItem(int index, const ItemStack& item) override; + std::string getName() const override; +- void setChanged() override; ++ void setContainerChanged(SlotID slot) override; + bool stillValid(Player* player) const override; ++ void addContentChangeListener(ContainerContentChangeListener* listener) override; ++ void addSizeChangeListener(ContainerSizeChangeListener* listener) override; ++ void removeContentChangeListener(ContainerContentChangeListener* listener) override; ++ void removeSizeChangeListener(ContainerSizeChangeListener* listener) override; + + public: + virtual void clear(); + virtual void load(const CompoundTag& tag); + virtual void save(CompoundTag& tag) const; + +-private: ++protected: ++ ContentChangeListeners m_contentChangeListeners; ++ SizeChangeListeners m_sizeChangeListeners; + std::vector m_items; + std::string m_name; + }; +\ No newline at end of file +diff --git a/source/world/inventory/Slot.hpp b/source/world/inventory/Slot.hpp +index 12a033823..f4286639d 100644 +--- a/source/world/inventory/Slot.hpp ++++ b/source/world/inventory/Slot.hpp +@@ -1,6 +1,6 @@ + #pragma once + +-#include "world/Container.hpp" ++#include "Container.hpp" + + class ItemStack; + +@@ -32,7 +32,7 @@ class Slot + + virtual void set(const ItemStack& item); + +- virtual void setChanged() { m_pContainer->setChanged(); } ++ virtual void setChanged() { m_pContainer->setContainerChanged(m_slot); } + + virtual int getMaxStackSize() const { return m_pContainer->getMaxStackSize(); } + +@@ -42,7 +42,7 @@ class Slot + + public: + Container* m_pContainer; +- int m_slot; +- int m_index; ++ int m_slot; // the position in the attached container ++ int m_index; // the position in the ContainerMenu::m_slots vector + Group m_group; + }; +\ No newline at end of file +diff --git a/source/world/item/Inventory.hpp b/source/world/item/Inventory.hpp +index 568a1e54a..876d06183 100644 +--- a/source/world/item/Inventory.hpp ++++ b/source/world/item/Inventory.hpp +@@ -1,8 +1,8 @@ + #pragma once + + #include +-#include "world/Container.hpp" + #include "GameMods.hpp" ++#include "world/inventory/Container.hpp" + #include "world/item/ItemStack.hpp" + #include "world/entity/Player.hpp" + #include "world/gamemode/GameType.hpp" +@@ -80,9 +80,9 @@ class Inventory : public Container + return "Inventory"; + } + +- void setChanged() override { } ++ void setContainerChanged(SlotID slot) override { } + +- bool stillValid(Player* player) const override { return true; } ++ bool stillValid(Player* player) const override { return true; } + + private: + GameType _getGameMode() const; +diff --git a/source/world/item/crafting/Recipe.hpp b/source/world/item/crafting/Recipe.hpp +index 6bb7a40dd..169d83a17 100644 +--- a/source/world/item/crafting/Recipe.hpp ++++ b/source/world/item/crafting/Recipe.hpp +@@ -1,6 +1,6 @@ + #pragma once + +-#include "world/Container.hpp" ++#include "world/inventory/Container.hpp" + + class Recipe + { +diff --git a/source/world/tile/ChestTile.cpp b/source/world/tile/ChestTile.cpp +index e7aad4afa..6db79d413 100644 +--- a/source/world/tile/ChestTile.cpp ++++ b/source/world/tile/ChestTile.cpp +@@ -1,6 +1,6 @@ + #include "ChestTile.hpp" + #include "world/level/Level.hpp" +-#include "world/CompoundContainer.hpp" ++#include "world/inventory/CompoundContainer.hpp" + #include "world/tile/entity/ChestTileEntity.hpp" + + ChestTile::ChestTile(int id, int texture) : EntityTile(id, texture, Material::wood) +diff --git a/source/world/tile/entity/ChestTileEntity.cpp b/source/world/tile/entity/ChestTileEntity.cpp +index f72f78a4c..323cfa7eb 100644 +--- a/source/world/tile/entity/ChestTileEntity.cpp ++++ b/source/world/tile/entity/ChestTileEntity.cpp +@@ -25,7 +25,7 @@ bool ChestTileEntity::stillValid(Player* player) const + return player->distanceToSqr(m_pos + 0.5f) <= 64.0f; + } + +-void ChestTileEntity::setChanged() ++void ChestTileEntity::setContainerChanged(SlotID slot) + { + TileEntity::setChanged(); + } +diff --git a/source/world/tile/entity/ChestTileEntity.hpp b/source/world/tile/entity/ChestTileEntity.hpp +index 38137f631..0fb177245 100644 +--- a/source/world/tile/entity/ChestTileEntity.hpp ++++ b/source/world/tile/entity/ChestTileEntity.hpp +@@ -13,5 +13,5 @@ class ChestTileEntity : public TileEntity, public SimpleContainer { + + bool stillValid(Player* player) const override; + +- virtual void setChanged() override; ++ void setContainerChanged(SlotID slot) override; + }; +diff --git a/source/world/tile/entity/FurnaceTileEntity.cpp b/source/world/tile/entity/FurnaceTileEntity.cpp +index 1134d2736..28e7ead28 100644 +--- a/source/world/tile/entity/FurnaceTileEntity.cpp ++++ b/source/world/tile/entity/FurnaceTileEntity.cpp +@@ -116,8 +116,9 @@ bool FurnaceTileEntity::stillValid(Player* player) const + return player->distanceToSqr(m_pos + 0.5f) <= 64.0; + } + +-void FurnaceTileEntity::setChanged() ++void FurnaceTileEntity::setContainerChanged(SlotID slot) + { ++ SimpleContainer::setContainerChanged(slot); + TileEntity::setChanged(); + } + +diff --git a/source/world/tile/entity/FurnaceTileEntity.hpp b/source/world/tile/entity/FurnaceTileEntity.hpp +index 55108282f..0a50b691e 100644 +--- a/source/world/tile/entity/FurnaceTileEntity.hpp ++++ b/source/world/tile/entity/FurnaceTileEntity.hpp +@@ -16,7 +16,7 @@ class FurnaceTileEntity : public SimpleContainer, public TileEntity + public: + void tick() override; + bool stillValid(Player* player) const override; +- void setChanged() override; ++ void setContainerChanged(SlotID slot) override; + void load(const CompoundTag& tag) override; + void save(CompoundTag& tag) const override; + std::string getName() const override; + +From 02783ae6b725a90e35b4da109644cc57d0c013d7 Mon Sep 17 00:00:00 2001 +From: Sam +Date: Wed, 18 Feb 2026 08:49:39 -0500 +Subject: [PATCH 21/22] CMakeLists & Xcode + +--- + .../Minecraft.xcodeproj/project.pbxproj | 56 ++++++++++++------- + source/CMakeLists.txt | 6 +- + 2 files changed, 40 insertions(+), 22 deletions(-) + +diff --git a/platforms/macos/projects/Minecraft/Minecraft.xcodeproj/project.pbxproj b/platforms/macos/projects/Minecraft/Minecraft.xcodeproj/project.pbxproj +index dcc3c8727..7600da5f0 100644 +--- a/platforms/macos/projects/Minecraft/Minecraft.xcodeproj/project.pbxproj ++++ b/platforms/macos/projects/Minecraft/Minecraft.xcodeproj/project.pbxproj +@@ -989,11 +989,6 @@ + 84E7BF272F286A08002D3936 /* SimpleContainer.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84E7BF172F286A08002D3936 /* SimpleContainer.hpp */; }; + 84E7BF282F286A08002D3936 /* Slot.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84E7BF182F286A08002D3936 /* Slot.cpp */; }; + 84E7BF292F286A08002D3936 /* Slot.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84E7BF192F286A08002D3936 /* Slot.hpp */; }; +- 84E7BF2F2F286A20002D3936 /* CompoundContainer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84E7BF2A2F286A20002D3936 /* CompoundContainer.cpp */; }; +- 84E7BF302F286A20002D3936 /* CompoundContainer.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84E7BF2B2F286A20002D3936 /* CompoundContainer.hpp */; }; +- 84E7BF312F286A20002D3936 /* Container.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84E7BF2C2F286A20002D3936 /* Container.hpp */; }; +- 84E7BF322F286A20002D3936 /* ContainerListener.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84E7BF2D2F286A20002D3936 /* ContainerListener.cpp */; }; +- 84E7BF332F286A20002D3936 /* ContainerListener.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84E7BF2E2F286A20002D3936 /* ContainerListener.hpp */; }; + 84E7BF362F286A6A002D3936 /* ItemStack.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84E7BF342F286A6A002D3936 /* ItemStack.cpp */; }; + 84E7BF372F286A6A002D3936 /* ItemStack.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84E7BF352F286A6A002D3936 /* ItemStack.hpp */; }; + 84E8DCE52E84ACBC00B30789 /* libRenderer.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 8406FD292AF1814500B09C1D /* libRenderer.a */; }; +@@ -1287,6 +1282,15 @@ + 84EEED642F0A01B700F741B6 /* ViewportOrigin.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84EEED622F0A01B700F741B6 /* ViewportOrigin.hpp */; }; + 84EEED682F0A02F300F741B6 /* ErrorHandlerOGL.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84EEED662F0A02F300F741B6 /* ErrorHandlerOGL.cpp */; }; + 84EEED692F0A02F300F741B6 /* ErrorHandlerOGL.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84EEED672F0A02F300F741B6 /* ErrorHandlerOGL.hpp */; }; ++ 84F0DA762F45FAE100906960 /* ContainerContentChangeListener.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84F0DA702F45FAE100906960 /* ContainerContentChangeListener.hpp */; }; ++ 84F0DA772F45FAE100906960 /* ContainerListener.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84F0DA722F45FAE100906960 /* ContainerListener.hpp */; }; ++ 84F0DA782F45FAE100906960 /* Container.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84F0DA6F2F45FAE100906960 /* Container.hpp */; }; ++ 84F0DA792F45FAE100906960 /* CompoundContainer.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84F0DA6D2F45FAE100906960 /* CompoundContainer.hpp */; }; ++ 84F0DA7A2F45FAE100906960 /* ContainerSizeChangeListener.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84F0DA742F45FAE100906960 /* ContainerSizeChangeListener.hpp */; }; ++ 84F0DA7B2F45FAE100906960 /* ContainerContentChangeListener.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84F0DA712F45FAE100906960 /* ContainerContentChangeListener.cpp */; }; ++ 84F0DA7C2F45FAE100906960 /* CompoundContainer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84F0DA6E2F45FAE100906960 /* CompoundContainer.cpp */; }; ++ 84F0DA7D2F45FAE100906960 /* ContainerListener.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84F0DA732F45FAE100906960 /* ContainerListener.cpp */; }; ++ 84F0DA7E2F45FAE100906960 /* ContainerSizeChangeListener.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84F0DA752F45FAE100906960 /* ContainerSizeChangeListener.cpp */; }; + 84F4880E2EEA4F5C00309B1E /* FixedPipelineStateNull.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84F487F02EEA4F5C00309B1E /* FixedPipelineStateNull.cpp */; }; + 84F4880F2EEA4F5C00309B1E /* FixedPipelineStateNull.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84F487F12EEA4F5C00309B1E /* FixedPipelineStateNull.hpp */; }; + 84F488102EEA4F5C00309B1E /* FogStateNull.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84F487F22EEA4F5C00309B1E /* FogStateNull.cpp */; }; +@@ -2766,11 +2770,6 @@ + 84E7BF172F286A08002D3936 /* SimpleContainer.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = SimpleContainer.hpp; sourceTree = ""; }; + 84E7BF182F286A08002D3936 /* Slot.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Slot.cpp; sourceTree = ""; }; + 84E7BF192F286A08002D3936 /* Slot.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Slot.hpp; sourceTree = ""; }; +- 84E7BF2A2F286A20002D3936 /* CompoundContainer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CompoundContainer.cpp; sourceTree = ""; }; +- 84E7BF2B2F286A20002D3936 /* CompoundContainer.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = CompoundContainer.hpp; sourceTree = ""; }; +- 84E7BF2C2F286A20002D3936 /* Container.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Container.hpp; sourceTree = ""; }; +- 84E7BF2D2F286A20002D3936 /* ContainerListener.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ContainerListener.cpp; sourceTree = ""; }; +- 84E7BF2E2F286A20002D3936 /* ContainerListener.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ContainerListener.hpp; sourceTree = ""; }; + 84E7BF342F286A6A002D3936 /* ItemStack.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ItemStack.cpp; sourceTree = ""; }; + 84E7BF352F286A6A002D3936 /* ItemStack.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ItemStack.hpp; sourceTree = ""; }; + 84EAE8DD2AF1EAA1000894E8 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; +@@ -3069,6 +3068,15 @@ + 84EEED622F0A01B700F741B6 /* ViewportOrigin.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ViewportOrigin.hpp; sourceTree = ""; }; + 84EEED662F0A02F300F741B6 /* ErrorHandlerOGL.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ErrorHandlerOGL.cpp; sourceTree = ""; }; + 84EEED672F0A02F300F741B6 /* ErrorHandlerOGL.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ErrorHandlerOGL.hpp; sourceTree = ""; }; ++ 84F0DA6D2F45FAE100906960 /* CompoundContainer.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = CompoundContainer.hpp; sourceTree = ""; }; ++ 84F0DA6E2F45FAE100906960 /* CompoundContainer.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = CompoundContainer.cpp; sourceTree = ""; }; ++ 84F0DA6F2F45FAE100906960 /* Container.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Container.hpp; sourceTree = ""; }; ++ 84F0DA702F45FAE100906960 /* ContainerContentChangeListener.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ContainerContentChangeListener.hpp; sourceTree = ""; }; ++ 84F0DA712F45FAE100906960 /* ContainerContentChangeListener.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ContainerContentChangeListener.cpp; sourceTree = ""; }; ++ 84F0DA722F45FAE100906960 /* ContainerListener.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ContainerListener.hpp; sourceTree = ""; }; ++ 84F0DA732F45FAE100906960 /* ContainerListener.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ContainerListener.cpp; sourceTree = ""; }; ++ 84F0DA742F45FAE100906960 /* ContainerSizeChangeListener.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ContainerSizeChangeListener.hpp; sourceTree = ""; }; ++ 84F0DA752F45FAE100906960 /* ContainerSizeChangeListener.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ContainerSizeChangeListener.cpp; sourceTree = ""; }; + 84F487F02EEA4F5C00309B1E /* FixedPipelineStateNull.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FixedPipelineStateNull.cpp; sourceTree = ""; }; + 84F487F12EEA4F5C00309B1E /* FixedPipelineStateNull.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = FixedPipelineStateNull.hpp; sourceTree = ""; }; + 84F487F22EEA4F5C00309B1E /* FogStateNull.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FogStateNull.cpp; sourceTree = ""; }; +@@ -3806,11 +3814,6 @@ + 840DD6562AC810620006A435 /* world */ = { + isa = PBXGroup; + children = ( +- 84E7BF2A2F286A20002D3936 /* CompoundContainer.cpp */, +- 84E7BF2B2F286A20002D3936 /* CompoundContainer.hpp */, +- 84E7BF2C2F286A20002D3936 /* Container.hpp */, +- 84E7BF2D2F286A20002D3936 /* ContainerListener.cpp */, +- 84E7BF2E2F286A20002D3936 /* ContainerListener.hpp */, + 840DD6572AC810620006A435 /* entity */, + 84358B1A2F4551290077EA44 /* Facing.cpp */, + 8477B3A82C4DC3B3004E1AC5 /* Facing.hpp */, +@@ -5083,6 +5086,15 @@ + 84E7BF092F286A08002D3936 /* inventory */ = { + isa = PBXGroup; + children = ( ++ 84F0DA6D2F45FAE100906960 /* CompoundContainer.hpp */, ++ 84F0DA6E2F45FAE100906960 /* CompoundContainer.cpp */, ++ 84F0DA6F2F45FAE100906960 /* Container.hpp */, ++ 84F0DA702F45FAE100906960 /* ContainerContentChangeListener.hpp */, ++ 84F0DA712F45FAE100906960 /* ContainerContentChangeListener.cpp */, ++ 84F0DA722F45FAE100906960 /* ContainerListener.hpp */, ++ 84F0DA732F45FAE100906960 /* ContainerListener.cpp */, ++ 84F0DA742F45FAE100906960 /* ContainerSizeChangeListener.hpp */, ++ 84F0DA752F45FAE100906960 /* ContainerSizeChangeListener.cpp */, + 84E7BF0A2F286A08002D3936 /* ArmorSlot.cpp */, + 84E7BF0B2F286A08002D3936 /* ArmorSlot.hpp */, + 84E022FE2F2A0245003C8FFE /* ChestMenu.cpp */, +@@ -5955,7 +5967,6 @@ + 84BF63862AF186C8008A9995 /* Inventory.hpp in Headers */, + 8477B3B52C4DC414004E1AC5 /* ChunkTilePos.hpp in Headers */, + 84BF63872AF186C8008A9995 /* Item.hpp in Headers */, +- 84E7BF332F286A20002D3936 /* ContainerListener.hpp in Headers */, + 84BF63892AF186C8008A9995 /* TileItem.hpp in Headers */, + 84BF638A2AF186C8008A9995 /* TilePlanterItem.hpp in Headers */, + 84E7BF292F286A08002D3936 /* Slot.hpp in Headers */, +@@ -5969,6 +5980,11 @@ + 84BF638F2AF186C8008A9995 /* BiomeSource.hpp in Headers */, + 84BF63902AF186C8008A9995 /* ChunkCache.hpp in Headers */, + 84BF63912AF186C8008A9995 /* ChunkSource.hpp in Headers */, ++ 84F0DA762F45FAE100906960 /* ContainerContentChangeListener.hpp in Headers */, ++ 84F0DA772F45FAE100906960 /* ContainerListener.hpp in Headers */, ++ 84F0DA782F45FAE100906960 /* Container.hpp in Headers */, ++ 84F0DA792F45FAE100906960 /* CompoundContainer.hpp in Headers */, ++ 84F0DA7A2F45FAE100906960 /* ContainerSizeChangeListener.hpp in Headers */, + 84BF63922AF186C8008A9995 /* LevelChunk.hpp in Headers */, + 84BF63932AF186C8008A9995 /* PerformanceTestChunkSource.hpp in Headers */, + 84E023012F2A0245003C8FFE /* ChestMenu.hpp in Headers */, +@@ -6074,7 +6090,6 @@ + 84BF63D22AF186C9008A9995 /* TorchTile.hpp in Headers */, + 84BF63D32AF186C9008A9995 /* TransparentTile.hpp in Headers */, + 84BF63D42AF186C9008A9995 /* TreeTile.hpp in Headers */, +- 84E7BF312F286A20002D3936 /* Container.hpp in Headers */, + 84BF63D52AF186C9008A9995 /* WireTile.hpp in Headers */, + 84AA8B512B32F39A003F5B82 /* BinaryHeap.hpp in Headers */, + 84AA8B532B32F39A003F5B82 /* Node.hpp in Headers */, +@@ -6115,7 +6130,6 @@ + 84358B062F4550B30077EA44 /* MusicTileEntity.hpp in Headers */, + 84358B072F4550B30077EA44 /* TileEntityType.hpp in Headers */, + 8470AF3D2BE9B6FA00BCA54E /* FireworkParticle.hpp in Headers */, +- 84E7BF302F286A20002D3936 /* CompoundContainer.hpp in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +@@ -7067,7 +7081,6 @@ + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( +- 84E7BF322F286A20002D3936 /* ContainerListener.cpp in Sources */, + 84BF630C2AF18631008A9995 /* Entity.cpp in Sources */, + 84BF630D2AF18631008A9995 /* FallingTile.cpp in Sources */, + 84BF630E2AF18631008A9995 /* ItemEntity.cpp in Sources */, +@@ -7146,7 +7159,6 @@ + 84358B122F4550CC0077EA44 /* ChestTile.cpp in Sources */, + 84358B132F4550CC0077EA44 /* MusicTile.cpp in Sources */, + 84BF63382AF18631008A9995 /* LevelListener.cpp in Sources */, +- 84E7BF2F2F286A20002D3936 /* CompoundContainer.cpp in Sources */, + 84BF63392AF18631008A9995 /* Material.cpp in Sources */, + 84BF633A2AF18631008A9995 /* Region.cpp in Sources */, + 84BF633B2AF18631008A9995 /* ChunkStorage.cpp in Sources */, +@@ -7260,6 +7272,10 @@ + 84E78C7B2B58B3E000D515EF /* Rocket.cpp in Sources */, + 84E78C832B58B5FB00D515EF /* RocketItem.cpp in Sources */, + 845BBCB62F2EA81700C7232C /* ToolItem.cpp in Sources */, ++ 84F0DA7B2F45FAE100906960 /* ContainerContentChangeListener.cpp in Sources */, ++ 84F0DA7C2F45FAE100906960 /* CompoundContainer.cpp in Sources */, ++ 84F0DA7D2F45FAE100906960 /* ContainerListener.cpp in Sources */, ++ 84F0DA7E2F45FAE100906960 /* ContainerSizeChangeListener.cpp in Sources */, + 84E78C872B58B66B00D515EF /* RocketLauncherTile.cpp in Sources */, + 8470AF2C2BE9B60A00BCA54E /* MobFactory.cpp in Sources */, + 84E1C9E92E7FDC72007D2F5D /* SlabItem.cpp in Sources */, +diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt +index 853b0897c..057f3ac44 100644 +--- a/source/CMakeLists.txt ++++ b/source/CMakeLists.txt +@@ -360,7 +360,10 @@ add_library(reminecraftpe-core STATIC + world/item/crafting/SingleInputRecipe.cpp + world/item/crafting/ShapedRecipe.cpp + world/item/crafting/ShapelessRecipe.cpp +- world/ContainerListener.cpp ++ world/inventory/ContainerListener.cpp ++ world/inventory/CompoundContainer.cpp ++ world/inventory/ContainerContentChangeListener.cpp ++ world/inventory/ContainerSizeChangeListener.cpp + world/inventory/ContainerMenu.cpp + world/inventory/InventoryMenu.cpp + world/inventory/CraftingMenu.cpp +@@ -373,7 +376,6 @@ add_library(reminecraftpe-core STATIC + world/inventory/CraftingContainer.cpp + world/inventory/ResultContainer.cpp + world/inventory/SimpleContainer.cpp +- world/CompoundContainer.cpp + world/item/DoorItem.cpp + world/item/ItemStack.cpp + world/item/RocketItem.cpp + +From d80620fdbc5bfe4a76c2c89dfa94d7ac1e7a26df Mon Sep 17 00:00:00 2001 +From: hellosammu +Date: Thu, 19 Feb 2026 10:17:18 -0500 +Subject: [PATCH 22/22] todo fix putting item stack in chests not relaying + count to clients when placing back (have to go to work waaaah) + +--- + .../multiplayer/MultiplayerLocalPlayer.cpp | 2 +- + .../multiplayer/MultiplayerLocalPlayer.hpp | 2 +- + source/server/ServerPlayer.cpp | 10 ++-- + source/server/ServerPlayer.hpp | 2 +- + source/server/ServerSideNetworkHandler.cpp | 1 + + source/world/inventory/CompoundContainer.cpp | 56 ++++++++++++++++++- + source/world/inventory/CompoundContainer.hpp | 11 ++++ + .../ContainerContentChangeListener.hpp | 2 +- + source/world/inventory/ContainerListener.hpp | 3 +- + source/world/inventory/ContainerMenu.cpp | 52 ++++++++++++----- + source/world/inventory/ContainerMenu.hpp | 2 +- + source/world/inventory/SimpleContainer.cpp | 2 +- + source/world/item/Inventory.cpp | 20 +++++++ + source/world/item/Inventory.hpp | 8 ++- + source/world/tile/entity/ChestTileEntity.cpp | 1 + + 15 files changed, 144 insertions(+), 30 deletions(-) + +diff --git a/source/client/multiplayer/MultiplayerLocalPlayer.cpp b/source/client/multiplayer/MultiplayerLocalPlayer.cpp +index 36750209d..772734bf5 100644 +--- a/source/client/multiplayer/MultiplayerLocalPlayer.cpp ++++ b/source/client/multiplayer/MultiplayerLocalPlayer.cpp +@@ -141,7 +141,7 @@ void MultiplayerLocalPlayer::refreshContainer(ContainerMenu* menu, const std::ve + { + } + +-void MultiplayerLocalPlayer::slotChanged(ContainerMenu* menu, int index, ItemStack& item, bool isResultSlot) ++void MultiplayerLocalPlayer::slotChanged(ContainerMenu* menu, int index, Slot* slot, ItemStack& item, bool isResultSlot) + { + #if NETWORK_PROTOCOL_VERSION >= 5 + m_pMinecraft->m_pRakNetInstance->send(new ContainerSetSlotPacket(m_pContainerMenu->m_containerId, index, item)); +diff --git a/source/client/multiplayer/MultiplayerLocalPlayer.hpp b/source/client/multiplayer/MultiplayerLocalPlayer.hpp +index 6ab1a580b..fffd41ec5 100644 +--- a/source/client/multiplayer/MultiplayerLocalPlayer.hpp ++++ b/source/client/multiplayer/MultiplayerLocalPlayer.hpp +@@ -22,7 +22,7 @@ class MultiplayerLocalPlayer : public LocalPlayer, public ContainerListener + void closeContainer() override; + + void refreshContainer(ContainerMenu* menu, const std::vector& items) override; +- void slotChanged(ContainerMenu* menu, int index, ItemStack& item, bool isResultSlot) override; ++ void slotChanged(ContainerMenu* menu, int index, Slot* slot, ItemStack& item, bool isResultSlot) override; + + private: + bool m_flashOnSetHealth; +diff --git a/source/server/ServerPlayer.cpp b/source/server/ServerPlayer.cpp +index a2a5d7cc5..d5bc21ebc 100644 +--- a/source/server/ServerPlayer.cpp ++++ b/source/server/ServerPlayer.cpp +@@ -14,6 +14,7 @@ + #include "world/inventory/ChestMenu.hpp" + #include "world/level/Level.hpp" + #include "world/tile/entity/FurnaceTileEntity.hpp" ++#include "world/inventory/Slot.hpp" + + ServerPlayer::ServerPlayer(Level* pLevel, GameType playerGameType) + : Player(pLevel, playerGameType) +@@ -122,13 +123,13 @@ void ServerPlayer::refreshContainer(ContainerMenu* menu, const std::vector= 5 +- if (!isResultSlot) +- { ++ // @TODO: See my gripes in ContainerMenu::slotChanged ++ // But ultimately this is a bandaid for the fact that the client has authority over the inventory in PE ++ if (!isResultSlot && slot->m_group != Slot::INVENTORY && slot->m_group != Slot::HOTBAR) + m_pLevel->m_pRakNetInstance->send(new ContainerSetSlotPacket(menu->m_containerId, index, item)); +- } + #endif + } + +@@ -162,5 +163,6 @@ void ServerPlayer::setContainerMenu(ContainerMenu* menu) + m_pContainerMenu->m_containerId = m_containerId; + m_pContainerMenu->addSlotListener(this); + refreshContainer(m_pContainerMenu, m_pContainerMenu->cloneItems()); ++ m_pContainerMenu->broadcastChanges(); + } + } +\ No newline at end of file +diff --git a/source/server/ServerPlayer.hpp b/source/server/ServerPlayer.hpp +index 874a0f1cc..2f08d5191 100644 +--- a/source/server/ServerPlayer.hpp ++++ b/source/server/ServerPlayer.hpp +@@ -19,7 +19,7 @@ class ServerPlayer : public Player, public ContainerListener + void take(Entity* pEnt, int count) override; + + void refreshContainer(ContainerMenu* menu, const std::vector& items) override; +- void slotChanged(ContainerMenu* menu, int index, ItemStack& item, bool isResultSlot) override; ++ void slotChanged(ContainerMenu* menu, int index, Slot* slot, ItemStack& item, bool isResultSlot) override; + void setContainerData(ContainerMenu* menu, int id, int value) override; + + void doCloseContainer(); +diff --git a/source/server/ServerSideNetworkHandler.cpp b/source/server/ServerSideNetworkHandler.cpp +index b88fc72d0..0651c8037 100644 +--- a/source/server/ServerSideNetworkHandler.cpp ++++ b/source/server/ServerSideNetworkHandler.cpp +@@ -640,6 +640,7 @@ void ServerSideNetworkHandler::handle(const RakNet::RakNetGUID& guid, ContainerS + switch (pContainerMenu->m_containerType) + { + case Container::FURNACE: ++ case Container::CONTAINER: + pContainerMenu->setItem(packet->m_slot, packet->m_item); + break; + default: +diff --git a/source/world/inventory/CompoundContainer.cpp b/source/world/inventory/CompoundContainer.cpp +index ea4d4dd1c..21adf1e2d 100644 +--- a/source/world/inventory/CompoundContainer.cpp ++++ b/source/world/inventory/CompoundContainer.cpp +@@ -1,8 +1,45 @@ + #include "CompoundContainer.hpp" + ++class CompoundContainer::ChildListener : public ContainerContentChangeListener ++{ ++public: ++ ChildListener(CompoundContainer* owner, int offset) ++ : m_pOwner(owner), m_offset(offset) ++ { ++ } ++ ++ void containerContentChanged(Container* container, SlotID slot) override ++ { ++ if (m_pOwner) ++ m_pOwner->setContainerChanged(slot + m_offset); ++ } ++ ++private: ++ CompoundContainer* m_pOwner; ++ int m_offset; ++}; ++ + CompoundContainer::CompoundContainer(const std::string& name, Container* c1, Container* c2) : +- m_name(name), m_pLeftContainer(c1), m_pRightContainer(c2) ++ m_name(name), m_pLeftContainer(c1), m_pRightContainer(c2), m_pLeftListener(nullptr), m_pRightListener(nullptr) + { ++ m_pLeftListener = new ChildListener(this, 0); ++ m_pRightListener = new ChildListener(this, m_pLeftContainer->getContainerSize()); ++ ++ if (m_pLeftContainer) ++ m_pLeftContainer->addContentChangeListener(m_pLeftListener); ++ if (m_pRightContainer) ++ m_pRightContainer->addContentChangeListener(m_pRightListener); ++} ++ ++CompoundContainer::~CompoundContainer() ++{ ++ if (m_pLeftContainer && m_pLeftListener) ++ m_pLeftContainer->removeContentChangeListener(m_pLeftListener); ++ if (m_pRightContainer && m_pRightListener) ++ m_pRightContainer->removeContentChangeListener(m_pRightListener); ++ ++ delete m_pLeftListener; ++ delete m_pRightListener; + } + + uint16_t CompoundContainer::getContainerSize() const +@@ -46,8 +83,11 @@ int CompoundContainer::getMaxStackSize() + + void CompoundContainer::setContainerChanged(SlotID slot) + { +- m_pLeftContainer->setContainerChanged(slot); +- m_pRightContainer->setContainerChanged(slot); ++ for (ContentChangeListeners::iterator it = m_contentChangeListeners.begin(); it != m_contentChangeListeners.end(); ++it) ++ { ++ ContainerContentChangeListener* listener = *it; ++ listener->containerContentChanged(this, slot); ++ } + } + + bool CompoundContainer::stillValid(Player* player) const +@@ -55,3 +95,13 @@ bool CompoundContainer::stillValid(Player* player) const + return m_pLeftContainer->stillValid(player) && m_pRightContainer->stillValid(player); + } + ++void CompoundContainer::addContentChangeListener(ContainerContentChangeListener* listener) ++{ ++ m_contentChangeListeners.insert(listener); ++} ++ ++void CompoundContainer::removeContentChangeListener(ContainerContentChangeListener* listener) ++{ ++ m_contentChangeListeners.erase(listener); ++} ++ +diff --git a/source/world/inventory/CompoundContainer.hpp b/source/world/inventory/CompoundContainer.hpp +index 1c3fd0fea..a429f1ceb 100644 +--- a/source/world/inventory/CompoundContainer.hpp ++++ b/source/world/inventory/CompoundContainer.hpp +@@ -1,17 +1,25 @@ + #pragma once + ++#include + #include + #include "Container.hpp" ++#include "ContainerContentChangeListener.hpp" + + class CompoundContainer : public Container + { + private: ++ class ChildListener; ++ + std::string m_name; + Container* m_pLeftContainer; + Container* m_pRightContainer; ++ ContentChangeListeners m_contentChangeListeners; ++ ChildListener* m_pLeftListener; ++ ChildListener* m_pRightListener; + + public: + CompoundContainer(const std::string& name, Container* c1, Container* c2); ++ ~CompoundContainer() override; + + uint16_t getContainerSize() const override; + +@@ -28,4 +36,7 @@ class CompoundContainer : public Container + void setContainerChanged(SlotID slot) override; + + bool stillValid(Player* player) const override; ++ ++ void addContentChangeListener(ContainerContentChangeListener* listener) override; ++ void removeContentChangeListener(ContainerContentChangeListener* listener) override; + }; +diff --git a/source/world/inventory/ContainerContentChangeListener.hpp b/source/world/inventory/ContainerContentChangeListener.hpp +index 675f1b221..ed0f7139f 100644 +--- a/source/world/inventory/ContainerContentChangeListener.hpp ++++ b/source/world/inventory/ContainerContentChangeListener.hpp +@@ -8,5 +8,5 @@ class ContainerContentChangeListener + virtual ~ContainerContentChangeListener() {} + + public: +- virtual void containerContentChanged(SlotID slot) = 0; ++ virtual void containerContentChanged(Container* container, SlotID slot) = 0; + }; +diff --git a/source/world/inventory/ContainerListener.hpp b/source/world/inventory/ContainerListener.hpp +index a159ae5db..79a63bb7e 100644 +--- a/source/world/inventory/ContainerListener.hpp ++++ b/source/world/inventory/ContainerListener.hpp +@@ -4,6 +4,7 @@ + + class ContainerMenu; + class ItemStack; ++class Slot; + + class ContainerListener + { +@@ -12,6 +13,6 @@ class ContainerListener + + virtual void refreshContainer(ContainerMenu* menu, const std::vector& items) {} + virtual void refreshContainerItems(ContainerMenu* menu); +- virtual void slotChanged(ContainerMenu* menu, int index, ItemStack& item, bool isResultSlot) {} ++ virtual void slotChanged(ContainerMenu* menu, int index, Slot* slot, ItemStack& item, bool isResultSlot) {} + virtual void setContainerData(ContainerMenu* menu, int id, int value) {} + }; +\ No newline at end of file +diff --git a/source/world/inventory/ContainerMenu.cpp b/source/world/inventory/ContainerMenu.cpp +index 7e52498df..936cb1d3e 100644 +--- a/source/world/inventory/ContainerMenu.cpp ++++ b/source/world/inventory/ContainerMenu.cpp +@@ -54,11 +54,6 @@ void ContainerMenu::addSlot(Slot* slot) + void ContainerMenu::addSlotListener(ContainerListener* listener) + { + m_listeners.insert(listener); +- +- // Not done on PE +- /*std::vector snapshot = cloneItems(); +- listener->refreshContainer(this, snapshot); +- broadcastChanges();*/ + } + + void ContainerMenu::sendData(int id, int value) +@@ -74,7 +69,7 @@ void ContainerMenu::broadcastChanges(SlotID slot) + { + m_lastSlots[slot] = ItemStack(current); + for (ContainerListeners::iterator it = m_listeners.begin(); it != m_listeners.end(); ++it) +- (*it)->slotChanged(this, slot, m_lastSlots[slot], isResultSlot()); ++ (*it)->slotChanged(this, slot, m_slots[slot], m_lastSlots[slot], isResultSlot()); + } + } + +@@ -101,12 +96,36 @@ void ContainerMenu::slotsChanged(Container*) + broadcastChanges(); + } + ++void ContainerMenu::containerContentChanged(Container* container, SlotID containerSlot) ++{ ++ // containerSlot is an index within a specific container, but m_slots contains ++ // slots from multiple containers. We need to find which slot in m_slots corresponds ++ // to this container slot by matching both the container and the slot index. ++ for (size_t i = 0; i < m_slots.size(); ++i) ++ { ++ Slot* slot = m_slots[i]; ++ if (slot->m_pContainer == container && slot->m_slot == containerSlot) ++ { ++ if (m_bBroadcastChanges) ++ broadcastChanges(i); ++ return; ++ } ++ } ++} ++ + std::vector ContainerMenu::cloneItems() + { + std::vector content; + + for (std::vector::iterator it = m_slots.begin(); it != m_slots.end(); ++it) + { ++ Slot* slot = *it; ++ // @TODO: I really do not like this. ++ // Firstly, inventories shouldn't be owned by the client ++ // Secondly, we shouldn't be checking types directly like this ++ // Ultimately this HAS to have two different storages, one for the inventory and one for the container ++ if (slot->m_group == Slot::INVENTORY || slot->m_group == Slot::HOTBAR) ++ continue; + const ItemStack& item = (*it)->getItem(); + content.push_back(item); + } +@@ -247,8 +266,6 @@ ItemStack ContainerMenu::clicked(int slotIndex, MouseButtonType mouseButton, boo + if (!slot) + return result; + +- slot->setChanged(); +- + ItemStack& slotItem = slot->getItem(); + + if (!slotItem.isEmpty()) +@@ -289,6 +306,7 @@ ItemStack ContainerMenu::clicked(int slotIndex, MouseButtonType mouseButton, boo + if (carried.m_count <= slot->getMaxStackSize()) + { + std::swap(carried, slotItem); ++ slot->setChanged(); + } + } + else if (slotItem.getId() == carried.getId()) +@@ -309,6 +327,7 @@ ItemStack ContainerMenu::clicked(int slotIndex, MouseButtonType mouseButton, boo + inv->setCarried(ItemStack::EMPTY); + + slotItem.m_count += count; ++ slot->setChanged(); + break; + } + case MOUSE_BUTTON_RIGHT: +@@ -325,6 +344,7 @@ ItemStack ContainerMenu::clicked(int slotIndex, MouseButtonType mouseButton, boo + inv->setCarried(ItemStack::EMPTY); + + slotItem.m_count += count; ++ slot->setChanged(); + break; + } + default: +@@ -358,6 +378,10 @@ ItemStack ContainerMenu::clicked(int slotIndex, MouseButtonType mouseButton, boo + void ContainerMenu::setItem(int index, ItemStack item) + { + m_slots[index]->set(item); ++ if (!m_bBroadcastChanges && index >= 0 && index < (int)m_lastSlots.size()) ++ { ++ m_lastSlots[index] = ItemStack(m_slots[index]->getItem()); ++ } + } + + void ContainerMenu::setAll(const std::vector& items) +@@ -366,6 +390,10 @@ void ContainerMenu::setAll(const std::vector& items) + for (size_t i = 0; i < n; ++i) + { + m_slots[i]->set(items[i]); ++ if (!m_bBroadcastChanges) ++ { ++ m_lastSlots[i] = ItemStack(m_slots[i]->getItem()); ++ } + } + } + +@@ -397,10 +425,4 @@ void ContainerMenu::setSynched(Player* player, bool isSynched) + m_unsynchedPlayers.erase(player); + else + m_unsynchedPlayers.insert(player); +-} +- +-void ContainerMenu::containerContentChanged(SlotID slot) +-{ +- if (m_bBroadcastChanges) +- broadcastChanges(slot); +-} ++} +\ No newline at end of file +diff --git a/source/world/inventory/ContainerMenu.hpp b/source/world/inventory/ContainerMenu.hpp +index 481d199be..45d1aa360 100644 +--- a/source/world/inventory/ContainerMenu.hpp ++++ b/source/world/inventory/ContainerMenu.hpp +@@ -59,7 +59,7 @@ class ContainerMenu : public ContainerContentChangeListener + virtual bool isPauseScreen() const { return false; } + + public: +- void containerContentChanged(SlotID slot) override; ++ void containerContentChanged(Container* container, SlotID slot) override; + + protected: + std::vector m_lastSlots; +diff --git a/source/world/inventory/SimpleContainer.cpp b/source/world/inventory/SimpleContainer.cpp +index 62d57425c..d6ce69a77 100644 +--- a/source/world/inventory/SimpleContainer.cpp ++++ b/source/world/inventory/SimpleContainer.cpp +@@ -62,7 +62,7 @@ void SimpleContainer::setContainerChanged(SlotID slot) + for (ContentChangeListeners::iterator it = m_contentChangeListeners.begin(); it != m_contentChangeListeners.end(); it++) + { + ContainerContentChangeListener* pListener = *it; +- pListener->containerContentChanged(slot); ++ pListener->containerContentChanged(this, slot); + } + } + +diff --git a/source/world/item/Inventory.cpp b/source/world/item/Inventory.cpp +index e0471397b..a14c88e24 100644 +--- a/source/world/item/Inventory.cpp ++++ b/source/world/item/Inventory.cpp +@@ -4,6 +4,7 @@ + #include "common/Logger.hpp" + #include "nbt/CompoundTag.hpp" + #include "network/Packet.hpp" ++#include "world/inventory/ContainerContentChangeListener.hpp" + + #include "Item.hpp" + +@@ -627,3 +628,22 @@ GameType Inventory::_getGameMode() const + { + return m_pPlayer->getPlayerGameType(); + } ++ ++void Inventory::setContainerChanged(SlotID slot) ++{ ++ for (ContentChangeListeners::iterator it = m_contentChangeListeners.begin(); it != m_contentChangeListeners.end(); ++it) ++ { ++ ContainerContentChangeListener* listener = *it; ++ listener->containerContentChanged(this, slot); ++ } ++} ++ ++void Inventory::addContentChangeListener(ContainerContentChangeListener* listener) ++{ ++ m_contentChangeListeners.insert(listener); ++} ++ ++void Inventory::removeContentChangeListener(ContainerContentChangeListener* listener) ++{ ++ m_contentChangeListeners.erase(listener); ++} +diff --git a/source/world/item/Inventory.hpp b/source/world/item/Inventory.hpp +index 876d06183..09a5c7882 100644 +--- a/source/world/item/Inventory.hpp ++++ b/source/world/item/Inventory.hpp +@@ -1,5 +1,6 @@ + #pragma once + ++#include + #include + #include "GameMods.hpp" + #include "world/inventory/Container.hpp" +@@ -19,6 +20,8 @@ class Player; // in case we're included from Player.hpp + + class Inventory : public Container + { ++private: ++ typedef std::set ContentChangeListeners; + public: + Inventory(Player*); + virtual ~Inventory(); +@@ -80,7 +83,9 @@ class Inventory : public Container + return "Inventory"; + } + +- void setContainerChanged(SlotID slot) override { } ++ void setContainerChanged(SlotID slot) override; ++ void addContentChangeListener(ContainerContentChangeListener* listener) override; ++ void removeContentChangeListener(ContainerContentChangeListener* listener) override; + + bool stillValid(Player* player) const override { return true; } + +@@ -100,4 +105,5 @@ class Inventory : public Container + + std::vector m_items; + std::vector m_armor; ++ ContentChangeListeners m_contentChangeListeners; + }; +diff --git a/source/world/tile/entity/ChestTileEntity.cpp b/source/world/tile/entity/ChestTileEntity.cpp +index 323cfa7eb..e18c18130 100644 +--- a/source/world/tile/entity/ChestTileEntity.cpp ++++ b/source/world/tile/entity/ChestTileEntity.cpp +@@ -27,5 +27,6 @@ bool ChestTileEntity::stillValid(Player* player) const + + void ChestTileEntity::setContainerChanged(SlotID slot) + { ++ SimpleContainer::setContainerChanged(slot); + TileEntity::setChanged(); + } diff --git a/source/client/gui/Gui.cpp b/source/client/gui/Gui.cpp index 5e4784d4f..1f316fc1f 100644 --- a/source/client/gui/Gui.cpp +++ b/source/client/gui/Gui.cpp @@ -669,6 +669,19 @@ void Gui::renderToolBar(float f, float alpha) // selection mark blit(-1 - hotbarWidth / 2 + 20 * inventory->m_selectedSlot, -23, 0, 22, 24, 22, 0, 0); + // chat and pause button for mobile devices + if (mc->isTouchscreen()) + { + textures->loadAndBindTexture("gui/gui2.png"); + + currentShaderColor.a = 0.5f; + + blit(-19, -GuiHeight + 1, 200, 82, 18, 18, 0, 0); // chat + blit(0, -GuiHeight + 1, 200, 64, 18, 18, 0, 0); // pause + + currentShaderColor.a = alpha; + } + textures->loadAndBindTexture(C_BLOCKS_NAME); int diff = mc->useTouchscreen(); From 5f488b585b3597e488831b8d6031691a87a6c8cf Mon Sep 17 00:00:00 2001 From: Un1q32 Date: Wed, 18 Feb 2026 18:58:15 -0500 Subject: [PATCH 23/63] Fixes in macOS artifact build (#491) --- platforms/macos/build.sh | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/platforms/macos/build.sh b/platforms/macos/build.sh index aac05da6d..441fcd2a8 100755 --- a/platforms/macos/build.sh +++ b/platforms/macos/build.sh @@ -199,6 +199,7 @@ for target in $targets; do arch="${target%%-*}" case $arch in (i386) + target_cflags="$cflags -march=pentium-m" export REMCPE_SDK="$old_sdk" set -- -DCMAKE_EXE_LINKER_FLAGS='-framework IOKit -framework Carbon -framework AudioUnit -undefined dynamic_lookup' platform='sdl1' @@ -213,9 +214,9 @@ for target in $targets; do tar -xzf ../sdl1src.tar.gz cd "SDL-1.2-$sdl1_commit" if [ -n "$DEBUG" ]; then - opt='-O2' - else opt='-O0' + else + opt='-O2' fi ./configure \ --host="$arch-apple-darwin" \ @@ -235,9 +236,10 @@ for target in $targets; do printf '%s' "$sdl1ver" > sdl/sdl1ver rm -rf "SDL-1.2-$sdl1_commit" fi - cflags="$cflags -I$PWD/sdl/include" + target_cflags="$target_cflags -I$PWD/sdl/include" ;; (arm64*|x86_64*) + target_cflags="$cflags" case $arch in (arm64*) export REMCPE_SDK="$arm64_sdk" @@ -269,8 +271,8 @@ for target in $targets; do -DCMAKE_CXX_COMPILER="$platformdir/macos-c++" \ -DCMAKE_FIND_ROOT_PATH="$REMCPE_SDK/usr;$PWD/sdl" \ -DCMAKE_SYSROOT="$REMCPE_SDK" \ - -DCMAKE_C_FLAGS="$cflags" \ - -DCMAKE_CXX_FLAGS="$cflags" \ + -DCMAKE_C_FLAGS="$target_cflags" \ + -DCMAKE_CXX_FLAGS="$target_cflags" \ -DWERROR="${WERROR:-OFF}" \ "$@" make -j"$ncpus" From d80ceebd567048d0e742070e3300f3ab50554c79 Mon Sep 17 00:00:00 2001 From: Brent <43089001+BrentDaMage@users.noreply.github.com> Date: Wed, 18 Feb 2026 21:56:37 -0800 Subject: [PATCH 24/63] Xbox 360 / Console Bug Fixes (#492) * Added 720p fallback resolution for Direct3D 9 device initialization. This fixes crashes that would occur when launching the game in 1080p on the Xbox 360. * Added a hack to fix screens being shrunk infinitely when using Java screen scaling with the Console UI theme. * Removed gameTips.animalFollow from tips, since the localization string was missing --- game/assets/lang/en_us.json | 2 +- game/assets/texts/tips.json | 3 --- platforms/xdk360/main.cpp | 12 +++++++++-- source/client/app/Minecraft.cpp | 10 ++++++--- .../renderer/hal/d3d9/RenderContextD3D9.cpp | 21 ++++++++++++++++--- 5 files changed, 36 insertions(+), 12 deletions(-) diff --git a/game/assets/lang/en_us.json b/game/assets/lang/en_us.json index f4499f2d2..337c2ace6 100644 --- a/game/assets/lang/en_us.json +++ b/game/assets/lang/en_us.json @@ -28,7 +28,7 @@ "loadingTip.junkboysFace": "No-one at Mojang has ever seen junkboy's face.", "loadingTip.largeChest": "Placing two chests side by side will make one large chest.", "loadingTip.lavaSmelt": "A single bucket of lava can be used in a furnace to smelt 100 blocks.", - "loadingTip.legacyInfo": "You'll get the latest info on this game from minecraft.net!", + "loadingTip.legacyInfo": "You'll get the latest info on this game from our Discord!", "loadingTip.lightMelt": "Blocks that can be used as a light source will melt snow and ice. This includes torches, glowstone, and Jack O'Lanterns.", "loadingTip.minecart": "Get to destinations faster with a minecart and rail!", "loadingTip.mineconCalifornia": "Minecon 2016 was in Anaheim, California, USA!", diff --git a/game/assets/texts/tips.json b/game/assets/texts/tips.json index 2117623b4..341869de0 100644 --- a/game/assets/texts/tips.json +++ b/game/assets/texts/tips.json @@ -191,9 +191,6 @@ { "translate": "loadingTip.updateInfo" }, - { - "translate": "loadingTip.animalFollow" - }, { "translate": "loadingTip.mineconLondon" }, diff --git a/platforms/xdk360/main.cpp b/platforms/xdk360/main.cpp index 06d09f0b7..4353d31c7 100644 --- a/platforms/xdk360/main.cpp +++ b/platforms/xdk360/main.cpp @@ -12,8 +12,16 @@ void _setSize() { XVIDEO_MODE VideoMode; XGetVideoMode(&VideoMode); - Minecraft::width = Mth::Max(VideoMode.dwDisplayWidth, 1280); - Minecraft::height = Mth::Max(VideoMode.dwDisplayHeight, 720); + Minecraft::width = Mth::Max(VideoMode.dwDisplayWidth, 640); + Minecraft::height = Mth::Max(VideoMode.dwDisplayHeight, 480); + + // Hardcoded 1080p check to avoid failed D3D device creation attempt + if (Minecraft::width == 1920 && Minecraft::height == 1080) + { + // too big, D3D9 Device creation will fail + Minecraft::width = 1280; + Minecraft::height = 720; + } } void _onKeyboardClosed() diff --git a/source/client/app/Minecraft.cpp b/source/client/app/Minecraft.cpp index 239110803..ab0768fc3 100644 --- a/source/client/app/Minecraft.cpp +++ b/source/client/app/Minecraft.cpp @@ -1064,11 +1064,15 @@ float Minecraft::getBestScaleForThisScreenSize(int width, int height) #define USE_JAVA_SCREEN_SCALING #endif #ifdef USE_JAVA_SCREEN_SCALING - int scale; - for (scale = 1; width / (scale + 1) >= 320 && height / (scale + 1) >= 240; ++scale) + // @HACK: the scaling code for Java/Pocket Screens when using the Console theme is pretty broken + if (m_pOptions->getUiTheme() != UI_CONSOLE) { + int scale; + for (scale = 1; width / (scale + 1) >= 320 && height / (scale + 1) >= 240; ++scale) + { + } + return scale; } - return scale; #endif if (height > 1800) diff --git a/source/renderer/hal/d3d9/RenderContextD3D9.cpp b/source/renderer/hal/d3d9/RenderContextD3D9.cpp index 67a496aba..3c69065f8 100644 --- a/source/renderer/hal/d3d9/RenderContextD3D9.cpp +++ b/source/renderer/hal/d3d9/RenderContextD3D9.cpp @@ -167,12 +167,12 @@ void RenderContextD3D9::createWindowSizeDependentResources(HWND hWnd, unsigned i { d3dpp.BackBufferWidth = width; d3dpp.BackBufferHeight = height; - d3dpp.BackBufferFormat = D3DFMT_X8R8G8B8; + d3dpp.BackBufferFormat = D3DFMT_A8R8G8B8; d3dpp.BackBufferCount = 1; d3dpp.EnableAutoDepthStencil = TRUE; d3dpp.AutoDepthStencilFormat = D3DFMT_D24S8; d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD; - d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_ONE; + d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_ONE; // basically V-Sync if (hWnd) { d3dpp.hDeviceWindow = hWnd; @@ -181,8 +181,23 @@ void RenderContextD3D9::createWindowSizeDependentResources(HWND hWnd, unsigned i } DWORD flags = D3DCREATE_HARDWARE_VERTEXPROCESSING; + LOG_I("Creating IDirect3DDevice9 with %dx%d size...", width, height); HRESULT hResult = m_pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, NULL, flags, &d3dpp, *m_d3dDevice); - ErrorHandlerD3D9::checkForErrors(hResult); + + if (hResult == E_OUTOFMEMORY) + { + width = 1280; + height = 720; + d3dpp.BackBufferWidth = width; + d3dpp.BackBufferHeight = height; + LOG_I("Failed to create IDirect3DDevice9! Falling back to (%dx%d)...", width, height); + HRESULT hResult = m_pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, NULL, flags, &d3dpp, *m_d3dDevice); + ErrorHandlerD3D9::checkForErrors(hResult); + } + else + { + ErrorHandlerD3D9::checkForErrors(hResult); + } m_d3dDevice->SetRenderState(D3DRS_SEPARATEALPHABLENDENABLE, TRUE); m_d3dDevice->SetRenderState(D3DRS_TWOSIDEDSTENCILMODE, TRUE); From 8cb59b94019c11a233dfd20c3a892836033f39a8 Mon Sep 17 00:00:00 2001 From: hellosammu Date: Thu, 19 Feb 2026 10:17:18 -0500 Subject: [PATCH 25/63] todo fix putting item stack in chests not relaying count to clients when placing back (have to go to work waaaah) --- .../multiplayer/MultiplayerLocalPlayer.cpp | 2 +- .../multiplayer/MultiplayerLocalPlayer.hpp | 2 +- source/server/ServerPlayer.cpp | 10 ++-- source/server/ServerPlayer.hpp | 2 +- source/server/ServerSideNetworkHandler.cpp | 1 + source/world/inventory/CompoundContainer.cpp | 56 ++++++++++++++++++- source/world/inventory/CompoundContainer.hpp | 11 ++++ .../ContainerContentChangeListener.hpp | 2 +- source/world/inventory/ContainerListener.hpp | 3 +- source/world/inventory/ContainerMenu.cpp | 52 ++++++++++++----- source/world/inventory/ContainerMenu.hpp | 2 +- source/world/inventory/SimpleContainer.cpp | 2 +- source/world/item/Inventory.cpp | 20 +++++++ source/world/item/Inventory.hpp | 8 ++- source/world/tile/entity/ChestTileEntity.cpp | 1 + 15 files changed, 144 insertions(+), 30 deletions(-) diff --git a/source/client/multiplayer/MultiplayerLocalPlayer.cpp b/source/client/multiplayer/MultiplayerLocalPlayer.cpp index 36750209d..772734bf5 100644 --- a/source/client/multiplayer/MultiplayerLocalPlayer.cpp +++ b/source/client/multiplayer/MultiplayerLocalPlayer.cpp @@ -141,7 +141,7 @@ void MultiplayerLocalPlayer::refreshContainer(ContainerMenu* menu, const std::ve { } -void MultiplayerLocalPlayer::slotChanged(ContainerMenu* menu, int index, ItemStack& item, bool isResultSlot) +void MultiplayerLocalPlayer::slotChanged(ContainerMenu* menu, int index, Slot* slot, ItemStack& item, bool isResultSlot) { #if NETWORK_PROTOCOL_VERSION >= 5 m_pMinecraft->m_pRakNetInstance->send(new ContainerSetSlotPacket(m_pContainerMenu->m_containerId, index, item)); diff --git a/source/client/multiplayer/MultiplayerLocalPlayer.hpp b/source/client/multiplayer/MultiplayerLocalPlayer.hpp index 6ab1a580b..fffd41ec5 100644 --- a/source/client/multiplayer/MultiplayerLocalPlayer.hpp +++ b/source/client/multiplayer/MultiplayerLocalPlayer.hpp @@ -22,7 +22,7 @@ class MultiplayerLocalPlayer : public LocalPlayer, public ContainerListener void closeContainer() override; void refreshContainer(ContainerMenu* menu, const std::vector& items) override; - void slotChanged(ContainerMenu* menu, int index, ItemStack& item, bool isResultSlot) override; + void slotChanged(ContainerMenu* menu, int index, Slot* slot, ItemStack& item, bool isResultSlot) override; private: bool m_flashOnSetHealth; diff --git a/source/server/ServerPlayer.cpp b/source/server/ServerPlayer.cpp index a2a5d7cc5..d5bc21ebc 100644 --- a/source/server/ServerPlayer.cpp +++ b/source/server/ServerPlayer.cpp @@ -14,6 +14,7 @@ #include "world/inventory/ChestMenu.hpp" #include "world/level/Level.hpp" #include "world/tile/entity/FurnaceTileEntity.hpp" +#include "world/inventory/Slot.hpp" ServerPlayer::ServerPlayer(Level* pLevel, GameType playerGameType) : Player(pLevel, playerGameType) @@ -122,13 +123,13 @@ void ServerPlayer::refreshContainer(ContainerMenu* menu, const std::vector= 5 - if (!isResultSlot) - { + // @TODO: See my gripes in ContainerMenu::slotChanged + // But ultimately this is a bandaid for the fact that the client has authority over the inventory in PE + if (!isResultSlot && slot->m_group != Slot::INVENTORY && slot->m_group != Slot::HOTBAR) m_pLevel->m_pRakNetInstance->send(new ContainerSetSlotPacket(menu->m_containerId, index, item)); - } #endif } @@ -162,5 +163,6 @@ void ServerPlayer::setContainerMenu(ContainerMenu* menu) m_pContainerMenu->m_containerId = m_containerId; m_pContainerMenu->addSlotListener(this); refreshContainer(m_pContainerMenu, m_pContainerMenu->cloneItems()); + m_pContainerMenu->broadcastChanges(); } } \ No newline at end of file diff --git a/source/server/ServerPlayer.hpp b/source/server/ServerPlayer.hpp index 874a0f1cc..2f08d5191 100644 --- a/source/server/ServerPlayer.hpp +++ b/source/server/ServerPlayer.hpp @@ -19,7 +19,7 @@ class ServerPlayer : public Player, public ContainerListener void take(Entity* pEnt, int count) override; void refreshContainer(ContainerMenu* menu, const std::vector& items) override; - void slotChanged(ContainerMenu* menu, int index, ItemStack& item, bool isResultSlot) override; + void slotChanged(ContainerMenu* menu, int index, Slot* slot, ItemStack& item, bool isResultSlot) override; void setContainerData(ContainerMenu* menu, int id, int value) override; void doCloseContainer(); diff --git a/source/server/ServerSideNetworkHandler.cpp b/source/server/ServerSideNetworkHandler.cpp index b88fc72d0..0651c8037 100644 --- a/source/server/ServerSideNetworkHandler.cpp +++ b/source/server/ServerSideNetworkHandler.cpp @@ -640,6 +640,7 @@ void ServerSideNetworkHandler::handle(const RakNet::RakNetGUID& guid, ContainerS switch (pContainerMenu->m_containerType) { case Container::FURNACE: + case Container::CONTAINER: pContainerMenu->setItem(packet->m_slot, packet->m_item); break; default: diff --git a/source/world/inventory/CompoundContainer.cpp b/source/world/inventory/CompoundContainer.cpp index ea4d4dd1c..21adf1e2d 100644 --- a/source/world/inventory/CompoundContainer.cpp +++ b/source/world/inventory/CompoundContainer.cpp @@ -1,8 +1,45 @@ #include "CompoundContainer.hpp" +class CompoundContainer::ChildListener : public ContainerContentChangeListener +{ +public: + ChildListener(CompoundContainer* owner, int offset) + : m_pOwner(owner), m_offset(offset) + { + } + + void containerContentChanged(Container* container, SlotID slot) override + { + if (m_pOwner) + m_pOwner->setContainerChanged(slot + m_offset); + } + +private: + CompoundContainer* m_pOwner; + int m_offset; +}; + CompoundContainer::CompoundContainer(const std::string& name, Container* c1, Container* c2) : - m_name(name), m_pLeftContainer(c1), m_pRightContainer(c2) + m_name(name), m_pLeftContainer(c1), m_pRightContainer(c2), m_pLeftListener(nullptr), m_pRightListener(nullptr) { + m_pLeftListener = new ChildListener(this, 0); + m_pRightListener = new ChildListener(this, m_pLeftContainer->getContainerSize()); + + if (m_pLeftContainer) + m_pLeftContainer->addContentChangeListener(m_pLeftListener); + if (m_pRightContainer) + m_pRightContainer->addContentChangeListener(m_pRightListener); +} + +CompoundContainer::~CompoundContainer() +{ + if (m_pLeftContainer && m_pLeftListener) + m_pLeftContainer->removeContentChangeListener(m_pLeftListener); + if (m_pRightContainer && m_pRightListener) + m_pRightContainer->removeContentChangeListener(m_pRightListener); + + delete m_pLeftListener; + delete m_pRightListener; } uint16_t CompoundContainer::getContainerSize() const @@ -46,8 +83,11 @@ int CompoundContainer::getMaxStackSize() void CompoundContainer::setContainerChanged(SlotID slot) { - m_pLeftContainer->setContainerChanged(slot); - m_pRightContainer->setContainerChanged(slot); + for (ContentChangeListeners::iterator it = m_contentChangeListeners.begin(); it != m_contentChangeListeners.end(); ++it) + { + ContainerContentChangeListener* listener = *it; + listener->containerContentChanged(this, slot); + } } bool CompoundContainer::stillValid(Player* player) const @@ -55,3 +95,13 @@ bool CompoundContainer::stillValid(Player* player) const return m_pLeftContainer->stillValid(player) && m_pRightContainer->stillValid(player); } +void CompoundContainer::addContentChangeListener(ContainerContentChangeListener* listener) +{ + m_contentChangeListeners.insert(listener); +} + +void CompoundContainer::removeContentChangeListener(ContainerContentChangeListener* listener) +{ + m_contentChangeListeners.erase(listener); +} + diff --git a/source/world/inventory/CompoundContainer.hpp b/source/world/inventory/CompoundContainer.hpp index 1c3fd0fea..a429f1ceb 100644 --- a/source/world/inventory/CompoundContainer.hpp +++ b/source/world/inventory/CompoundContainer.hpp @@ -1,17 +1,25 @@ #pragma once +#include #include #include "Container.hpp" +#include "ContainerContentChangeListener.hpp" class CompoundContainer : public Container { private: + class ChildListener; + std::string m_name; Container* m_pLeftContainer; Container* m_pRightContainer; + ContentChangeListeners m_contentChangeListeners; + ChildListener* m_pLeftListener; + ChildListener* m_pRightListener; public: CompoundContainer(const std::string& name, Container* c1, Container* c2); + ~CompoundContainer() override; uint16_t getContainerSize() const override; @@ -28,4 +36,7 @@ class CompoundContainer : public Container void setContainerChanged(SlotID slot) override; bool stillValid(Player* player) const override; + + void addContentChangeListener(ContainerContentChangeListener* listener) override; + void removeContentChangeListener(ContainerContentChangeListener* listener) override; }; diff --git a/source/world/inventory/ContainerContentChangeListener.hpp b/source/world/inventory/ContainerContentChangeListener.hpp index 675f1b221..ed0f7139f 100644 --- a/source/world/inventory/ContainerContentChangeListener.hpp +++ b/source/world/inventory/ContainerContentChangeListener.hpp @@ -8,5 +8,5 @@ class ContainerContentChangeListener virtual ~ContainerContentChangeListener() {} public: - virtual void containerContentChanged(SlotID slot) = 0; + virtual void containerContentChanged(Container* container, SlotID slot) = 0; }; diff --git a/source/world/inventory/ContainerListener.hpp b/source/world/inventory/ContainerListener.hpp index a159ae5db..79a63bb7e 100644 --- a/source/world/inventory/ContainerListener.hpp +++ b/source/world/inventory/ContainerListener.hpp @@ -4,6 +4,7 @@ class ContainerMenu; class ItemStack; +class Slot; class ContainerListener { @@ -12,6 +13,6 @@ class ContainerListener virtual void refreshContainer(ContainerMenu* menu, const std::vector& items) {} virtual void refreshContainerItems(ContainerMenu* menu); - virtual void slotChanged(ContainerMenu* menu, int index, ItemStack& item, bool isResultSlot) {} + virtual void slotChanged(ContainerMenu* menu, int index, Slot* slot, ItemStack& item, bool isResultSlot) {} virtual void setContainerData(ContainerMenu* menu, int id, int value) {} }; \ No newline at end of file diff --git a/source/world/inventory/ContainerMenu.cpp b/source/world/inventory/ContainerMenu.cpp index 7e52498df..936cb1d3e 100644 --- a/source/world/inventory/ContainerMenu.cpp +++ b/source/world/inventory/ContainerMenu.cpp @@ -54,11 +54,6 @@ void ContainerMenu::addSlot(Slot* slot) void ContainerMenu::addSlotListener(ContainerListener* listener) { m_listeners.insert(listener); - - // Not done on PE - /*std::vector snapshot = cloneItems(); - listener->refreshContainer(this, snapshot); - broadcastChanges();*/ } void ContainerMenu::sendData(int id, int value) @@ -74,7 +69,7 @@ void ContainerMenu::broadcastChanges(SlotID slot) { m_lastSlots[slot] = ItemStack(current); for (ContainerListeners::iterator it = m_listeners.begin(); it != m_listeners.end(); ++it) - (*it)->slotChanged(this, slot, m_lastSlots[slot], isResultSlot()); + (*it)->slotChanged(this, slot, m_slots[slot], m_lastSlots[slot], isResultSlot()); } } @@ -101,12 +96,36 @@ void ContainerMenu::slotsChanged(Container*) broadcastChanges(); } +void ContainerMenu::containerContentChanged(Container* container, SlotID containerSlot) +{ + // containerSlot is an index within a specific container, but m_slots contains + // slots from multiple containers. We need to find which slot in m_slots corresponds + // to this container slot by matching both the container and the slot index. + for (size_t i = 0; i < m_slots.size(); ++i) + { + Slot* slot = m_slots[i]; + if (slot->m_pContainer == container && slot->m_slot == containerSlot) + { + if (m_bBroadcastChanges) + broadcastChanges(i); + return; + } + } +} + std::vector ContainerMenu::cloneItems() { std::vector content; for (std::vector::iterator it = m_slots.begin(); it != m_slots.end(); ++it) { + Slot* slot = *it; + // @TODO: I really do not like this. + // Firstly, inventories shouldn't be owned by the client + // Secondly, we shouldn't be checking types directly like this + // Ultimately this HAS to have two different storages, one for the inventory and one for the container + if (slot->m_group == Slot::INVENTORY || slot->m_group == Slot::HOTBAR) + continue; const ItemStack& item = (*it)->getItem(); content.push_back(item); } @@ -247,8 +266,6 @@ ItemStack ContainerMenu::clicked(int slotIndex, MouseButtonType mouseButton, boo if (!slot) return result; - slot->setChanged(); - ItemStack& slotItem = slot->getItem(); if (!slotItem.isEmpty()) @@ -289,6 +306,7 @@ ItemStack ContainerMenu::clicked(int slotIndex, MouseButtonType mouseButton, boo if (carried.m_count <= slot->getMaxStackSize()) { std::swap(carried, slotItem); + slot->setChanged(); } } else if (slotItem.getId() == carried.getId()) @@ -309,6 +327,7 @@ ItemStack ContainerMenu::clicked(int slotIndex, MouseButtonType mouseButton, boo inv->setCarried(ItemStack::EMPTY); slotItem.m_count += count; + slot->setChanged(); break; } case MOUSE_BUTTON_RIGHT: @@ -325,6 +344,7 @@ ItemStack ContainerMenu::clicked(int slotIndex, MouseButtonType mouseButton, boo inv->setCarried(ItemStack::EMPTY); slotItem.m_count += count; + slot->setChanged(); break; } default: @@ -358,6 +378,10 @@ ItemStack ContainerMenu::clicked(int slotIndex, MouseButtonType mouseButton, boo void ContainerMenu::setItem(int index, ItemStack item) { m_slots[index]->set(item); + if (!m_bBroadcastChanges && index >= 0 && index < (int)m_lastSlots.size()) + { + m_lastSlots[index] = ItemStack(m_slots[index]->getItem()); + } } void ContainerMenu::setAll(const std::vector& items) @@ -366,6 +390,10 @@ void ContainerMenu::setAll(const std::vector& items) for (size_t i = 0; i < n; ++i) { m_slots[i]->set(items[i]); + if (!m_bBroadcastChanges) + { + m_lastSlots[i] = ItemStack(m_slots[i]->getItem()); + } } } @@ -397,10 +425,4 @@ void ContainerMenu::setSynched(Player* player, bool isSynched) m_unsynchedPlayers.erase(player); else m_unsynchedPlayers.insert(player); -} - -void ContainerMenu::containerContentChanged(SlotID slot) -{ - if (m_bBroadcastChanges) - broadcastChanges(slot); -} +} \ No newline at end of file diff --git a/source/world/inventory/ContainerMenu.hpp b/source/world/inventory/ContainerMenu.hpp index 481d199be..45d1aa360 100644 --- a/source/world/inventory/ContainerMenu.hpp +++ b/source/world/inventory/ContainerMenu.hpp @@ -59,7 +59,7 @@ class ContainerMenu : public ContainerContentChangeListener virtual bool isPauseScreen() const { return false; } public: - void containerContentChanged(SlotID slot) override; + void containerContentChanged(Container* container, SlotID slot) override; protected: std::vector m_lastSlots; diff --git a/source/world/inventory/SimpleContainer.cpp b/source/world/inventory/SimpleContainer.cpp index 62d57425c..d6ce69a77 100644 --- a/source/world/inventory/SimpleContainer.cpp +++ b/source/world/inventory/SimpleContainer.cpp @@ -62,7 +62,7 @@ void SimpleContainer::setContainerChanged(SlotID slot) for (ContentChangeListeners::iterator it = m_contentChangeListeners.begin(); it != m_contentChangeListeners.end(); it++) { ContainerContentChangeListener* pListener = *it; - pListener->containerContentChanged(slot); + pListener->containerContentChanged(this, slot); } } diff --git a/source/world/item/Inventory.cpp b/source/world/item/Inventory.cpp index e0471397b..a14c88e24 100644 --- a/source/world/item/Inventory.cpp +++ b/source/world/item/Inventory.cpp @@ -4,6 +4,7 @@ #include "common/Logger.hpp" #include "nbt/CompoundTag.hpp" #include "network/Packet.hpp" +#include "world/inventory/ContainerContentChangeListener.hpp" #include "Item.hpp" @@ -627,3 +628,22 @@ GameType Inventory::_getGameMode() const { return m_pPlayer->getPlayerGameType(); } + +void Inventory::setContainerChanged(SlotID slot) +{ + for (ContentChangeListeners::iterator it = m_contentChangeListeners.begin(); it != m_contentChangeListeners.end(); ++it) + { + ContainerContentChangeListener* listener = *it; + listener->containerContentChanged(this, slot); + } +} + +void Inventory::addContentChangeListener(ContainerContentChangeListener* listener) +{ + m_contentChangeListeners.insert(listener); +} + +void Inventory::removeContentChangeListener(ContainerContentChangeListener* listener) +{ + m_contentChangeListeners.erase(listener); +} diff --git a/source/world/item/Inventory.hpp b/source/world/item/Inventory.hpp index 876d06183..09a5c7882 100644 --- a/source/world/item/Inventory.hpp +++ b/source/world/item/Inventory.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include #include "GameMods.hpp" #include "world/inventory/Container.hpp" @@ -19,6 +20,8 @@ class Player; // in case we're included from Player.hpp class Inventory : public Container { +private: + typedef std::set ContentChangeListeners; public: Inventory(Player*); virtual ~Inventory(); @@ -80,7 +83,9 @@ class Inventory : public Container return "Inventory"; } - void setContainerChanged(SlotID slot) override { } + void setContainerChanged(SlotID slot) override; + void addContentChangeListener(ContainerContentChangeListener* listener) override; + void removeContentChangeListener(ContainerContentChangeListener* listener) override; bool stillValid(Player* player) const override { return true; } @@ -100,4 +105,5 @@ class Inventory : public Container std::vector m_items; std::vector m_armor; + ContentChangeListeners m_contentChangeListeners; }; diff --git a/source/world/tile/entity/ChestTileEntity.cpp b/source/world/tile/entity/ChestTileEntity.cpp index 323cfa7eb..e18c18130 100644 --- a/source/world/tile/entity/ChestTileEntity.cpp +++ b/source/world/tile/entity/ChestTileEntity.cpp @@ -27,5 +27,6 @@ bool ChestTileEntity::stillValid(Player* player) const void ChestTileEntity::setContainerChanged(SlotID slot) { + SimpleContainer::setContainerChanged(slot); TileEntity::setChanged(); } From 183dcb15c5692c069f64d901e9be0b5b910c1b67 Mon Sep 17 00:00:00 2001 From: Brent <43089001+BrentDaMage@users.noreply.github.com> Date: Thu, 19 Feb 2026 20:23:31 -0800 Subject: [PATCH 26/63] Fixed Xbox 360 Hanging on Startup (#493) Fixed device storage selection UI not rendering, which would result in the game hanging when loading the options. This would only affect devices with more than one storage device attached. --- platforms/xdk360/AppPlatform_xdk360.cpp | 28 +++++++++++++++---- .../renderer/hal/base/RenderContextBase.cpp | 8 ++++++ .../renderer/hal/base/RenderContextBase.hpp | 2 ++ .../renderer/hal/d3d9/RenderContextD3D9.cpp | 21 ++++++++++++-- .../renderer/hal/d3d9/RenderContextD3D9.hpp | 2 ++ 5 files changed, 54 insertions(+), 7 deletions(-) diff --git a/platforms/xdk360/AppPlatform_xdk360.cpp b/platforms/xdk360/AppPlatform_xdk360.cpp index 8b4ec16a6..5ffba8025 100644 --- a/platforms/xdk360/AppPlatform_xdk360.cpp +++ b/platforms/xdk360/AppPlatform_xdk360.cpp @@ -51,8 +51,17 @@ const XCONTENTDEVICEID& AppPlatform_xdk360::_getSaveDeviceId(LocalPlayerID playe if (deviceId != C_SAVEDEVICE_ID_NONE) return deviceId; + /*mce::RenderContext& renderContext = mce::RenderContextImmediate::get(); + // This is required in order for the system UI to render in a blocking context + renderContext.suspend();*/ + + // Create event for asynchronous writing + HANDLE hEventComplete = NULL; //CreateEvent(NULL, FALSE, FALSE, NULL); + ULARGE_INTEGER iBytesRequested = { C_SAVEDEVICE_MINIMUM_FREE_BYTES }; XOVERLAPPED xov = {0}; + if (hEventComplete) + xov.hEvent = hEventComplete; deviceId = C_SAVEDEVICE_ID_PENDING; DWORD result = XShowDeviceSelectorUI( @@ -63,7 +72,10 @@ const XCONTENTDEVICEID& AppPlatform_xdk360::_getSaveDeviceId(LocalPlayerID playe &xov ); - if (result != ERROR_IO_PENDING) + if (result != ERROR_IO_PENDING + // Only block if we could get an event handle + || (hEventComplete && XGetOverlappedResult(&xov, NULL, TRUE) != ERROR_SUCCESS) + ) { LOG_W("Failed to open save device for player %d", playerId); deviceId = C_SAVEDEVICE_ID_ERROR; @@ -72,9 +84,13 @@ const XCONTENTDEVICEID& AppPlatform_xdk360::_getSaveDeviceId(LocalPlayerID playe // Wait for the save-device handle while (XHasOverlappedIoCompleted(&xov) == FALSE) { + swapBuffers(); + // This only ends up getting run if the above event handle couldn't be created and waited on sleepMs(20); } + //renderContext.resume(); + return deviceId; } @@ -154,7 +170,8 @@ void AppPlatform_xdk360::beginProfileDataRead(LocalPlayerID playerId) XCONTENT_DATA contentData; _getXContentData(contentData, playerId); - XUID xuid; + // Fuck this check, it just fails sometimes for no apparent reason + /*XUID xuid; BOOL playerIsCreator; XContentGetCreator(playerId, &contentData, &playerIsCreator, &xuid, NULL); @@ -162,7 +179,7 @@ void AppPlatform_xdk360::beginProfileDataRead(LocalPlayerID playerId) { LOG_E("Current player is not the creator of, and therefore cannot write to, the requested profile data!"); throw std::bad_cast(); - } + }*/ // Mount the device to the "savedrive" drive name DWORD result = XContentCreate(playerId, "savedrive", &contentData, XCONTENTFLAG_OPENEXISTING, NULL, NULL, NULL); @@ -187,8 +204,9 @@ void AppPlatform_xdk360::endProfileDataRead(LocalPlayerID playerId) { if (m_currentSavingPlayerId == -1) { - LOG_E("Tried to end profile data read for player %d, but no one is saving any data!", playerId); - throw std::bad_cast(); + LOG_W("Tried to end profile data read for player %d, but no one is saving any data!", playerId); + //throw std::bad_cast(); + return; } XContentClose("savedrive", NULL); diff --git a/source/renderer/hal/base/RenderContextBase.cpp b/source/renderer/hal/base/RenderContextBase.cpp index 165041986..7bcdfcf1a 100644 --- a/source/renderer/hal/base/RenderContextBase.cpp +++ b/source/renderer/hal/base/RenderContextBase.cpp @@ -104,6 +104,14 @@ void RenderContextBase::endRender() { } +void RenderContextBase::suspend() +{ +} + +void RenderContextBase::resume() +{ +} + void RenderContextBase::swapBuffers() { } diff --git a/source/renderer/hal/base/RenderContextBase.hpp b/source/renderer/hal/base/RenderContextBase.hpp index a949fa134..5b869f843 100644 --- a/source/renderer/hal/base/RenderContextBase.hpp +++ b/source/renderer/hal/base/RenderContextBase.hpp @@ -50,6 +50,8 @@ namespace mce void setRenderTarget(); void beginRender(); void endRender(); + void suspend(); + void resume(); void swapBuffers(); void lostContext(); diff --git a/source/renderer/hal/d3d9/RenderContextD3D9.cpp b/source/renderer/hal/d3d9/RenderContextD3D9.cpp index 3c69065f8..329005ee7 100644 --- a/source/renderer/hal/d3d9/RenderContextD3D9.cpp +++ b/source/renderer/hal/d3d9/RenderContextD3D9.cpp @@ -1,5 +1,6 @@ #include #include "RenderContextD3D9.hpp" +#include "compat/PlatformDefinitions.h" #include "common/Logger.hpp" #include "renderer/hal/d3d9/helpers/ErrorHandlerD3D9.hpp" @@ -117,7 +118,8 @@ void RenderContextD3D9::beginRender() #if MCE_GFX_D3D9_SHADER_CONSTANT_BUFFERS m_d3dDevice->GpuOwn(D3DTAG_VERTEXSHADERCONSTANTS); m_d3dDevice->GpuOwn(D3DTAG_PIXELSHADERCONSTANTS); -#else +#endif +#if !MC_PLATFORM_XBOX360 m_d3dDevice->BeginScene(); #endif } @@ -126,11 +128,26 @@ void RenderContextD3D9::endRender() { #if MCE_GFX_D3D9_SHADER_CONSTANT_BUFFERS m_d3dDevice->GpuDisownAll(); -#else +#endif +#if !MC_PLATFORM_XBOX360 m_d3dDevice->EndScene(); #endif } +void RenderContextD3D9::suspend() +{ +#if MC_PLATFORM_XBOX360 + m_d3dDevice->Suspend(); +#endif +} + +void RenderContextD3D9::resume() +{ +#if MC_PLATFORM_XBOX360 + m_d3dDevice->Resume(); +#endif +} + void RenderContextD3D9::swapBuffers() { HRESULT hr = m_d3dDevice->Present(NULL, NULL, NULL, NULL); diff --git a/source/renderer/hal/d3d9/RenderContextD3D9.hpp b/source/renderer/hal/d3d9/RenderContextD3D9.hpp index d337803c5..d5c303fdc 100644 --- a/source/renderer/hal/d3d9/RenderContextD3D9.hpp +++ b/source/renderer/hal/d3d9/RenderContextD3D9.hpp @@ -66,6 +66,8 @@ namespace mce void clearContextState(); void beginRender(); void endRender(); + void suspend(); + void resume(); void swapBuffers(); bool supports8BitIndices() const; From be42c427e4cb1e9794a7e2ac057d53ddefac8ff6 Mon Sep 17 00:00:00 2001 From: Vimdo Date: Thu, 19 Feb 2026 23:46:31 -0500 Subject: [PATCH 27/63] Mipmap Icons for Android (#488) --- game/assets/{ => app/icons}/icon.ico | Bin game/assets/app/icons/icon.jpg | Bin 0 -> 24462 bytes game/assets/{ => app/icons}/icon.png | Bin game/assets/app/icons/mipmap/icon_144x144.png | Bin 0 -> 12364 bytes game/assets/app/icons/mipmap/icon_192x192.png | Bin 0 -> 20170 bytes game/assets/app/icons/mipmap/icon_48x48.png | Bin 0 -> 2007 bytes game/assets/app/icons/mipmap/icon_64x64.png | Bin 0 -> 3071 bytes game/assets/app/icons/mipmap/icon_72x72.png | Bin 0 -> 3808 bytes game/assets/app/icons/mipmap/icon_96x96.png | Bin 0 -> 6294 bytes game/assets/icon.jpg | Bin 24427 -> 0 bytes game/assets/icon_64x64.png | Bin 7689 -> 0 bytes platforms/android/project/app/build.gradle | 25 ++++++ .../project/app/src/main/AndroidManifest.xml | 1 + platforms/ios/build-ipa.sh | 2 +- .../Minecraft.xcodeproj/project.pbxproj | 2 +- platforms/sdl/base/AppPlatform_sdl.cpp | 2 +- platforms/sdl/sdl2/CMakeLists.txt | 2 +- platforms/sdl/sdl2/android/app/build.gradle | 22 ++++++ .../android/app/src/main/AndroidManifest.xml | 3 +- .../drawable-v24/ic_launcher_foreground.xml | 31 -------- .../res/drawable/ic_launcher_background.xml | 74 ------------------ .../res/mipmap-anydpi-v26/ic_launcher.xml | 5 -- .../mipmap-anydpi-v26/ic_launcher_round.xml | 5 -- .../src/main/res/mipmap-hdpi/ic_launcher.webp | Bin 2682 -> 0 bytes .../res/mipmap-hdpi/ic_launcher_round.webp | Bin 4342 -> 0 bytes .../src/main/res/mipmap-mdpi/ic_launcher.webp | Bin 2172 -> 0 bytes .../res/mipmap-mdpi/ic_launcher_round.webp | Bin 2924 -> 0 bytes .../main/res/mipmap-xhdpi/ic_launcher.webp | Bin 4010 -> 0 bytes .../res/mipmap-xhdpi/ic_launcher_round.webp | Bin 6554 -> 0 bytes .../main/res/mipmap-xxhdpi/ic_launcher.webp | Bin 5382 -> 0 bytes .../res/mipmap-xxhdpi/ic_launcher_round.webp | Bin 9466 -> 0 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.webp | Bin 7624 -> 0 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.webp | Bin 12522 -> 0 bytes platforms/windows/MinecraftClient.Win32.rc | 4 +- .../MinecraftClient.Win32.vcxproj | 2 +- .../MinecraftClient.Win32.vcxproj.filters | 2 +- platforms/xdk360/ReMinecraftPE.xlast | Bin 16354 -> 16422 bytes 37 files changed, 57 insertions(+), 125 deletions(-) rename game/assets/{ => app/icons}/icon.ico (100%) create mode 100644 game/assets/app/icons/icon.jpg rename game/assets/{ => app/icons}/icon.png (100%) create mode 100644 game/assets/app/icons/mipmap/icon_144x144.png create mode 100644 game/assets/app/icons/mipmap/icon_192x192.png create mode 100644 game/assets/app/icons/mipmap/icon_48x48.png create mode 100644 game/assets/app/icons/mipmap/icon_64x64.png create mode 100644 game/assets/app/icons/mipmap/icon_72x72.png create mode 100644 game/assets/app/icons/mipmap/icon_96x96.png delete mode 100644 game/assets/icon.jpg delete mode 100644 game/assets/icon_64x64.png delete mode 100644 platforms/sdl/sdl2/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml delete mode 100644 platforms/sdl/sdl2/android/app/src/main/res/drawable/ic_launcher_background.xml delete mode 100644 platforms/sdl/sdl2/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml delete mode 100644 platforms/sdl/sdl2/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml delete mode 100644 platforms/sdl/sdl2/android/app/src/main/res/mipmap-hdpi/ic_launcher.webp delete mode 100644 platforms/sdl/sdl2/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp delete mode 100644 platforms/sdl/sdl2/android/app/src/main/res/mipmap-mdpi/ic_launcher.webp delete mode 100644 platforms/sdl/sdl2/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp delete mode 100644 platforms/sdl/sdl2/android/app/src/main/res/mipmap-xhdpi/ic_launcher.webp delete mode 100644 platforms/sdl/sdl2/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp delete mode 100644 platforms/sdl/sdl2/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp delete mode 100644 platforms/sdl/sdl2/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp delete mode 100644 platforms/sdl/sdl2/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp delete mode 100644 platforms/sdl/sdl2/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp diff --git a/game/assets/icon.ico b/game/assets/app/icons/icon.ico similarity index 100% rename from game/assets/icon.ico rename to game/assets/app/icons/icon.ico diff --git a/game/assets/app/icons/icon.jpg b/game/assets/app/icons/icon.jpg new file mode 100644 index 0000000000000000000000000000000000000000..16ca8e5db82360d6db6f6a2611232d8908405499 GIT binary patch literal 24462 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 T#Gn-*&`^R%+kch>KwtmaZ6gae literal 0 HcmV?d00001 diff --git a/game/assets/icon.png b/game/assets/app/icons/icon.png similarity index 100% rename from game/assets/icon.png rename to game/assets/app/icons/icon.png diff --git a/game/assets/app/icons/mipmap/icon_144x144.png b/game/assets/app/icons/mipmap/icon_144x144.png new file mode 100644 index 0000000000000000000000000000000000000000..8fcd3675380646b4e395947a0e1ac786e264bd5f GIT binary patch literal 12364 zcmV-SFtg8zP)u00GX)I=0|^5?EFY(V zdZHr$oCN?lIyxpLBrzu-t&WMHF#(9O`gs6BL`M8z z07Z+3b6QpWngAOm5QdG7St$-qRaK83188n;m-zKNetIBENOBnqNt>5SYb|ss7h~De zqi6tCIT{R_7NT?jyb=Hw zH8Zza1WvK5h)NA)NEw-2IY$=@&pZQ*=Hba;1IkYXMg{_|wzir>I(y*TTowv)qZuZHM9{16%;MH#FMcouMBSCvKraO z7RKO)CV6g>O|tHxVYai&`YmP|@?f$Y7zP4E=HHy}T>0gtTGlVi($Tr+o_njaGUa*O zHhl8dxwdhwcX{LU;MwnwozI4w0^!Dbf=$baYzyI{+TnM~hS*BIu<{-)& zj*8nTZ(xdI_V_r<1i0lo2N1R(tD(%dqQ%1*+%&;8c?gY6)Ah@r0U zVN4Xk&nL$vv2iXhOJ^@og|;nHky@v416GPu;mL`{!P zJP=5g2y3cF_l&$d^aO=WC!l8_Ncl8@eqcsXYRawguU zB1%#-w|kP*M49JRAT%a5!H zV=6LqlN2u2Z1cS2@+2WkJU-ks98o30;mvD8Q#shF8emkt_kCCO{lIQDDw~YrdZ0C4 zsmsrU&&$-7AZm4A0c}SKl@_ttV=4=-fWna>E)^*4BqW!Tcn6OogY=eST%xwL@=YZf zOVa@A2Kb2`vhD;AniMn%N^a38RXEd%e?cHv4SB<=RN|K==#>E@MyO|%6~ZdO_)!Tt zt&-lMI_}5YDQ3{6w2CpeWJkDY1sV5ac6A*YHguKnaDuoh2au&S+2%g9LQ@FA%92^y za(1S;NvKE)Cz>Z@P+62xw&)c|Q#VYB8nJ|G!GSiE-5x`#`bH10q~=a!#lAz?gQ=hU-DhNVU&3p2S z{OT3HUV0wDLEMHIK-Z75cH$&i2wKUf9HpYWle-Kb+!rGn*5aZ!L{yjw1O{KW zhGB7q_nc6CQZD#5-O5a8$m@jn?c~6 z4LHgyxbp6#YFd-?{1Q|Tb-mq=wVAYQRA|TYX%)+8AyD0`G)=hTfp8rs?eXjsOOK?ZU(*Z)0p6a@uPiu(6{yCvSM%_@Cfr=}$ zXNwWB>bqC)bR1j4fe5y7GgX|3uL%*3qKs`UJy2Gg#?&hXXtpp!3SB00z;idVK0~^8 zio2_)^Lks)=kuqn*@=uYZQaA*D~!p33?AOsvL+{{$tfikZJNSlXTZt2X0g{cq2(1-5Q_lH9_ zblqWqA(HF>#OSf?pfb;GVi*XJY%MqQR}&S?I9uUaBH=8XWwXU%gaKhoDElCFLy)pt zl}Pn{KAjpEgEVK~B{^j-CCP&DoKAn2gZ{LDjL)DhfCK=AtIR-25|shf>nd1j!SL1Qf~tEny`O@~KDObocB)QN0XIg&y{NIc2~9j)I#C3o zsMZPHVe)h&G>%2Gng&A_GX25IY2Hc)7eNa<5j^g1pfMb96PHNmU+G zl#$^C(Z$Ltod7MfHb1ZZL~m$g%CtyZX!Lwc#0r=QQ~%~?S9 z>-GM8-S5wWbv4!%VR+$_+Um8*5CgW=ytElP{3ujG7pgBOna{+HR>6PEG9AJaKd( z!gPg_>+!tbK@rOyY$H5vb}wYwL2fkkuY)R^IyL!?dICYCT&u;S@2x0tW;ofTrdo~~ zT7J=xoKb)t3=e>`X4Ec!`fAbBktln(fb4hs<7sy~?T))6J1?JiAjFNVA&OQyj)$lQ z)65EfXxNzVRyM^Yw@9bi#AxmxOP8(_v3e;kLTT{Q%fy>x;~%w$7^LpR-J>UpGosKbqLhlVzty`c;otEVi{%D z44P#9+AZ%#L?TERKMB%Tf~I56ZT82VaGb{Fbb5Y1{r25ozyJNeKmK-FpXJYPy*oXh zpz}Hnjd~)R0FiJ(a|9-jnl2KFCwt%RC?YErD)yB7uXqGW3<*^I1fsDcnvXT){$`5j zy$E_16s^ki>4 zefXMZ3;XvBMgC8S#>kD3o-{^G_S30~hZz%)cH#y|9~qy2`SzRdfB50sZ~pVIe_p54 z^L6+9@%bosfY|v1(=4m8)*sMMYGzY#_qx_=K3I-tBcT7YWqq%0V`_g`oO8Y#Wn+;(Vk#_xq*KR^3na4qW$i#DPCmJ*PE5qpdoi2F z*%W}1Q5t=;@Pp1_2N@%Sx=vuo7{=`p5#wLzZEuFWBC9jZ0V$7$RV*lJEqHmP0L)gM zM_5u+Kqm20Wll&{q@-T!0Mg-$I2~nK99_htH2LTwdc8vwbsbFM({$Pp5$=+}c!LCf zWrg2l4@)70nw3568q3;ys337k_^wrzt-BA$z*I#ppnP8<)o=7J(=1BjXg-@p>BTFb zC4>l(AOHxS{1zEVOcSRSO#H}MpvwR$v#$dx6H2*M(wOA6R8sj<@PwQcd4;C7Ys%H? z?z5Vg(R?~fv)Oby&9XkoNNCP3%9e-Q7@7> znZ?m4nU1E>CApx54?vKiz+!v3h4Z)D?hisp;2I-b5Q(lKLq9(%2#G2di-Lq;xQrY% zGltxts8K|Q?b3s&E`#7&DC>IFZ}fVUM+SMeH5kz;!%~5FcR?@4w*-Mn(deredW~5h2|q|4RIk2QM2=C z2IWYuX5gc%{}3yRAHf44UjQ*c4MfNZY#kr;2A(oByyYsWKI4H+R*)i>A=?OZeKoX> zEyOi{IQD%*l@J#4l}wMP(a@+SiHbLbg) z-%C)?Wksd0d@b1M_FVIo%U0%bVM2(pySSY`?v{FsB0_12+i?mhQ zb$$PRG>wx8TA!p@`jNLctBtDA1<1>)N_#OguI_gjC5O&lh$Ubqml5Lai=rJx!dVLc zGD?P;L}=9L%C)p=CSi3RC-L+GOdX@wvaf+_w#d;iXSVHtyBa=#c^)Z^rd$b+OSv>? z-ua})Ld;9dR#7j}veGh4*D*17!L>kX_sj4)6W9Q>NeeOm80o5p17 zl*Rp3hJa3|?u)#l8=HNT#3rVW7J%hfbp#M4iF=sZ?&J<+rU24q=`6K_Au3$QBj7Y3@VA;M%XB18sPr~)X}TZ-(5MDC`kYY$J1tB-O|ln_ zrcffN$T$7EjJ75baeU%9&aA=LLYX{PqmYnrO=%gS;sVMyt6Wn2EeQ)4c9OTlF5_e2 zb`7?8aD0`)=1*Zij_1*c11Af~Hvh z22PQ93{N$j5%5`Lxniu@|8P9C5W(v#KF*G>j$y2m=v)nuH8QzRTfoVKpQgJGM3rQm z*V0KS>bgvs=&J_weSl~o3#!&U`aD!lAmbTSU#z~%eDIMSSXlH~0;Z0x((_uM!kO=n zx)eeoM1aAi*rSfkz*C<@nbaOo=qV)y6Xk%ui;pBsxqVhLB18@fQk}ML0Cz~p6=qZ{OU6Q~fb_=R57ybkf=MQ-X%|o>LTKhpg zj-&W7kbmCmSv$WSszKW6rOr2ovm_HAc085Bew%}m$A=>QwE(<4j z4>AHnf@~vivm8(*U(3@XzZ2Rn?T>}X)4F1vn#?7DY@D~4ic(YO|e-oi%#k{XpFX%mylgEwXcrvzV z1d4yzpNz{tO(yrscPIP5pa2Sm@_2F(+6R-#=MNt~Powz5#plnTFFsVQ@!sBKZ!$Ru ztO?fM-rl&*x48P&fjT*G{(W|Ia&&ZZ`mFWr1ZO8FNA;h*1t8qB^7xM}uC*tJZ{_Td zhxh&M!S(@bzVhqp>W+PHu)0^=U+o^017xkdGuU+8?bY^byS?!tPTK!#Z{VxxK3MIp zcDvo(;O-`m-PH%JNmthD_}&9`ve`L$_w-*_PfyQIzdZdf&YzxDj6$9=&|>B7)*9Et z`-cy4*jn3q`1tnbaA&+yS-DwR?+*7$~wb=XM4Cg9Co`OM(=TK zciR9o>27wr!{M&e+QnP#;qZaIzs<9D_n`G)*v4<-%N2l&{^csSU-ckj+U z$G&@Ke|yj#-?h(9U%os#!pqso>6e#h&z?Q|yP<|fHMv$gNY>-~e0j{YNm1_)Ha2jj zAOBCcwST3NC1JD?--54*2#Vgs2tI;}6GI+-P#}p-(k+IjbDAbC9OM9k67dm5qVcWk zYm!a&xa*i1lZmr4b7tp1+^Qy%{b}#Pvby_r*Vol`t1h=cI4{T183NBpq$G~_dXtpO zU}wEKeJ$-x5=*z`a^i&dW-KO)MUQ9MNzO|*s{s&chR1FYC2#bv@iHQFypfRg-9I*u zP;71Ne%{>1gX%)XFHB*{>@VZ*XWx&%-gvd~eEj`-GgLMbqa{g_{XQuw2$G+Y+31iy z%1V+DXuC#fBsND(0v)*g$K}=jn^S>3yt=x)(vyrwa9@i_QKKQpN{mw$qy`2~Wo%Vu zHH=L$Ca|Kxa=!K9EyT}qGk27(yGfy=ps#5c-JHN4aRG zR`N)GlfXsAK7o&xX;?nm+{Sujs*&~vNE;0SB)vQR=aUnf-(UOj>^DOE9^OOC>IRiM z=fS}7(6zlXXr6!>vWV-IdK0don z$Sp+!K>9{7?r-A&ix#w7jWiuqH`2UJU|)<}qxl$dfN>9d^5)^h4$Xgj^W?=l@DR`P z-W(w+LyTh)9DS!5p?PIBq^Qo?fW+nO#{R8~&4XVzR|s{!qG-F7&0ilG&Irk?=i{%T zZQScuw2k!#6eW7-oT!b-xmw1<=Ijh?svI6Vrq{}H)E{8^B%}jcmgQ4OjXNxTHu31; zJ2PuQCMPW$UW2`4CCownMjY-)PEi;;rRt^p>c8~0xC0k#$=0bs0=@HNd zy662b;28+)S$JN=4ADu)7>qXJ2hEIv^1*P^Lne^C91nm*p3e~N&cut4J3PNX@$~Qe z(3$tXA{h=MKgn|$lBB&ERSbjQjNgQe4H=nZ9~kxPkcqa4#b`Hgfwn>{4LEyLDIQYk z)iCI1*HOGeaZiDfLA$^qXo2tX2nZZ>4@TDP zIwT;>LNL^zaE#caycAFqOw>7#kR&k_0S}vjs#fXG$Se(ZIW~8C_LK19(LO6gp@#f> zkT5hL6$QZJ6FdRp7d$bNkvut4_;O5OnDKtfqT9Oo{X>7Jy@8xVCi;N&W97U~{T0x< zl7+@G3*i`p%JF)=S7PN1M7*A|$gJciIT}Qa7W`DD1>BaQSrL372~auy^V6eKK@fPz zqatAhItc>YDN2Av#Pr~oY1ynSWU8pKK-3eUDAm^P=g;SsX1l(2W#!k;O7++x>OxTK zUqkdLA+)|xC~42%zk<>Qg#(5lG4YTR^#TYYd3lk-nJ9`sR$|cu>6= z!tf9;AY||rGEuaXW|+}K z&wQ(@3Qe!>G}3{S07CC zs1DHJ1EN8N>z-zu?);yIwq42uNfEAZ`&{2PySpux-=404v~+7Nhv&YN)(3Q{qy2()QM>sD;VH6=~eHLV?`I-Rbjq^7jJwZ65Yy|hKw)iW?~`+AqI zwCj3LS6A27&i>BH(}{if_ipF%?%mt%r8lnM(A^lg-c?aC&{fgeQc+P~)KSsiUaG5T z*Xde{%R4$+OG}%q)kWpyCB?;XlS%{gR@LYz0N`5>TUt7F*xI4pXm9W8 z=^5x5xPALReD2?U`L)HT16`?Ok;zsm-nIF z?iuLnx&fdn6dQ1>t-6Yqj@FKr`tsuHqT=G>qM{k<+ zl}{%SKth3l7YAi8j%qr-7f&SOclVd(g2BjKcy)PxIXtxx{IZvr-rJjwC(`kBJ`sm* z(})}QDahd5mv`@{pygBfY(A7n1x=+=S+E_NoJ2roO=vO{(r7^Rb=GCEZwR_(CnvM{ z1Yk<16Y&J#OeBVxM8e59-JqnMPKWP@;PUe7YGie0COCh*8a`fFh&*#Lez((Y%MK0> zyPZth?e@Vr@##c59gnBMYj8aT5z2zmNo4F#!rf={usNw#TMY=vU{VMc3ZuGDA(}R7 z70yK!zHN6f!zhPsNbRuM98R~-?Q^=_qV04pyto(+hnK?P<;dJ}aDFBdoDY9F^B=n1 z!^1WkbObnuFp(U#G0tJ9JMD0!)3$tDGx$PNHL38NwAw0wWip}tI;1b7hfJpKDg>o5 zb$0^@WPB;yH7)YY91ODF;BxL^5d;|V`NUg{@AT)@@ZxH4X7PA+e(BG-h46B4CVV_M zGj;qT!!WjCTbtAEbowwmVT!^DuG={Xesc_J8x;bW2KkbWja3T4{#q5XdHd=hu3ZL& zTc%M6i+$aVNUv8|-I}U4WZ{VnvTRqkH3R>fS{ffZ4Z zkajdggrYpS#26fgl0ZowHv44EmfC~J1SW%(oSEv#7>H2&gxh&Q{~eqF>EtqxpQczWVU$ zK)6A)UV~f)?C@Zp%XhejXLoOl%VoE=C=5!~I9<=tFo#Kl=#+Y(^le7?gK z(}yqjUtqhxb91yB^(K=(sF}{H!F&kIx3}PEFd2;ogVAs>9pBEz)oj!sRtr!#{dRxy z8yA8h@~`mmimSq2^It35c8!XxmxYLSpUbj6F8LxLo|1J&KYsUgk1IZRcjZ3=dt7Dn zSFYLE-+A$uucxb7zXu>N-yhFWdKL==sk)sG2XlPP#=SX8QLi@~&Sw3=WH7BJUyhEi zUmhO3fJ?6q_PLl~f9K`?v*+9VC&ivD`{G}LxFn2x>^=R_Y5ag1zV+no9{*W^v8(Zx ztNWjzDB$bA=>0aAUOmMD2jfYI;WF|= zj$0USg3y+B4jy@la{6of`sTxZ)rVY1x2uQyovFDcuN9)Xw(`FC&EYD z?+pL|4dqEhK~#VeKaO8nmgPAvE$kz0H(Tva=Y4N9t9p~sbbWVsdUASpbU#}lVB^v4 zr22gH=Ir>sn*0|;^=D`{dbmG1{We87`>Sd`nBJX!de`lAx*W>VqD;$Jta)A#B^=HI z<#|daphIgP4+k*3IL|x`dmsdlqKPY;98G}*kMr(7eqQ%i)7AQ)|G7Fle*fFovy-FY zWHjurKHr}n;~wbj^v!)WTTDUJw}+#vBOE*W{JB4$4yQ9nd~ttsjz_26YPHjpsB##} zat_|y#1ahvgDO?7QX1kH_n(KV0Aa49LGPmsiJc-rRk`&^8}HbiL_y=e*P66CJnOz{E*J zif9O;9GnPe9R8gQr}C05(h_jeX3NesgrHW79JM;#bHc*ALs!qwyYDUzFE6p*J@4|> zeRsLMI=jAJ-dqUc?!_;kuFsB!)AjoP<5z$to~|IW^APwAlNpK1sS`qyDO2D853n?a zo`Uo+R^X@2&lVr-hnsfGB9t$1S-cj29b&{5peheJ1u#EFq`s1z_jYZF?2@u(sn8zWHYsF z3a>&$+7=L&P@r!M7|cBRw2~++2}PQhA|;9`W5|dxFHk2KJI+~4Vj!|qxWfs?98VD3 zw2l=*IiaQuEek|jCc3E&Isw^J0;Or0q(H?n_|+7C*ePsexkN5UN${MU36=v~k>fa( zPlTd2%Dmtg!lOH!-WQnJm&Ce(MZg6i~vjg%oF+WcU$hL3`E zNINto!5|@7=EPE(0vd}wQ9A`@VP?{VEiwWFH24wQ3vDvw|A!L@h|>nWu%-xC^iYak zgu-%z)B+I+r!0gFRF*rak{Rn#T#_FQV6Ba#6GZ*4FkZk-*NutbZ8XY))0P)a` z!i_8Lcfk=3$gm4M$__JL91*G~Pytw=W+alKjyErm>QQm%e6uHk&SNV{dI@!uA_ht? zliVWLJb=cI3KFA&$b>YYSiA%E$ApC4(;~j&j+Z7APbl(;)eGd#|8cnXVu56lNh-)8 zTudE$7O;d9lo70`l`4}OQwko>fq8h&8A+H0Dh_aWFJ=^9QKVF45;Btrl`15UY7=LA zMzCR`1g#=iT9heX>_y6pg%eL?X`PT^E91cBS$Gmg%Pe?x#LEy2gkflaj=wg<$_J{5 zSix5Ui1OP-5JoDAXiQP=z?I0?n24AdktYEbYGW@+B&4KVJlHT7XPflNM2Xp>G)W~; zIy&S92>>L%4wERt3%i)X8Raof=vRdrxX2IoV>BfcaEbW~9AK;y1{GZ>%caPSMkEQ? zIr8IiYD!Sz5IT@Z#((BGLgpJoM2fx#9=5(~d=ixCNP!>^Oo6^6^rOfaVu>Tc4c9pd z#YQ0skPH!L<@_}VU%)$m1xifFnHPAMh(1a|2_eMI1SQ}ku?Z0e4$LGfG*RgL*olB6&p%v>s{=0;w!*R2Yf$@iZ#(eNC50 zQDSJDha*WB#+qh9nr(pS1!R^P6k6h#+EA);d}vww3Iw@Y(_7qzXcsi05P-di_XEb@ zPD#5!kbpt7M&G^*c!sKj4J2X&&t#$==`iBeAef#L2N0TsKhDL0vGzeI1t%nug-BUA zm=EEO%yqR1Vbjza0p$#KjL{?~1bsR*b)5Hk|7L_iVDNbW1i=qKqy-8_@(()R65aLh}*6?9?4+-ZJQs{!h z0El7A1rh`#3?tzP%0dHO$0-$1h6EJ46qB!E4n`S1A90ox0T!TU5a!~xf5(I5|snF5Rw zaWKyGM7Dt$COMnHhGtOVixR;_6r6J0T3z#=5pNi-F*@i#*X!D?fw9nS80ZFi_v=Zb z-8#h&1rj2hLq0@kA0o;=1L?v8)Gvq)tW$|D6ToM5_@gVz3G>9T;?gUj$G9etPEap! zU*>|5ddRRC1|cD2EkrfI9bMNU48m_vU}(gx`MTDChe-ZJ*XWLBND)w^SIB%|$-+4o z&;K=P_A1irKoGSAVo@Nm;6Q=F!6X8|#(n>Hy62Q<;hSKXA4}a`T~)0euVLqnR`6YV z>BY68WW2TWwWGjRIlA29b2L{$KIL3nFY^jM^v(Ti8GY6Ya5nu6-P_p1hINJa;X2SP z?E0M6gDtczBXq~>;Z(}Thq3$ zAPt*4wZZE=Qhwcwd`ekm+7sf-Bfe<#&T9Ob{d8csZZcq?Wha@^O6h8!dcN)jnaDwb zl;Te=@{?GXg~0mTg*=}hXH+Q~(khBF_j`vW390;k5*w`2QYzY*Qd*SD*jNY z*Ky@MO_ap|hZlkqtS38Kce-z|92Ce-Eo7N!r22DEhabFPK!A-e2=@u(&VVDo8o84S zDmD)`ta4tojRK1tt5kB@S-9*SiA5mTbaf%9ItFOU>p0nH<_3_^OB)2Uvo2LK{D5xZ zR;MS7GsqBEbNQJ=<2&e8BR0r4!D{=d0?~3O#7*p9V=F-D>}$%js@q#%>gn_grt=ic z+#S2T65P`Ov*6vVh>NZ4Vy?dkDBa}48s4^kJZwom_KjNkcn_#Vm|2HL1hI=d1Vk5I zS1__22nWKnG?%C5-nr-G$pv>HHr3MqzxJA{cYYSTrW)wZMAlkl%;3^v$zP;#l`TW9 ze(VL^{4rFi-0gX4Sl!~58fmfO0`zn3c3&mUwYv&Y&n7Qnqa6^loX(=@4Ll&aCD)WQ zcB+lxqK@>~6JI!x)+X6k+P&Koa@jmaZeS6lKGKPm#8Fs!NCV&X6b5mX{v{4Tz|tAf zY&KpPYjTaK72PT^GNERf>@1lL5L)BLl0FB@+AQp`)oz((JkRrH1I!o+@dlmTvcz5` znAtr~zaobwaLsG=kSZ%nm>lu)MnVU-Ure#c5cL3yYP82W`sar-@dgkw5D|^Kq|){S zg3wZmB;RMJ%JB>sQyjgJoC88utfhm_k)p6;vQW2X1t65)O`n>P(9i7*nEF5812x!i zzEB@Byh^pSZ10DF6aqyoA10snzwU=_@x438BoUUw>4awC8@-}&6K(F27#s+A8EgT3 zhN)OO#Fs$kJ2wk5DZzFk*BV0%8sg(PcCc68KAnisLf4fOS;*$ zp}Od>d%R611hX!W_Hr(C5Kpe$oJX3|uVm)DX21%y>0V<0HDh?VN8Xgr61qB5h6b%m#4h{|u z6b=d;92^NA2?z)YBP1jrA0G}a4h07V8WIc-Fb^#s77#KJ0003eCnhl?7$G4cDk>^3 zFE2PJ8yy`T3kL)gJroi*5)(QT7eW;gG!YyZ5;Zk71Plb5c5pu}AQ%)7m;wL>5Cs)S z7a|)LqZk38r>CNNa-V;BJ~}xlAQ~+t9-t=zn|pPl1^}ajel;s6F-aVea%`V9157g? zh6Dkoh=ZR(0bfc*rGI$%2mliq3qe3X`fC9AEC7vaV^csosf~#98~`&s7jITlr?0O! zR3V^rYI*vo009s#PqD%p33TJ$1QhriI0$f zgoK0S;4Wie{j&gBx3Y&HyV(0i%@x!fOPK1>y4m03ZNKL_t(|+B}%eZtFS{gcV@mgM=VJBM?mx zLU@=ExQijQ(8AX%VQ_^5t(?v5^ALmJyv01lyy$$@O0HvDKenar>Z+V8Q?4O~*mEtZ*SxMJ_82+Ww{_x^TBf~(P$#}r zLX064KCHawR8s4ozyJHcIM=-Sr_YZqfBgPvxy4dzOMV=NUpZ&rQg6Ag*|~A_vI>$H6-yZcahw+=#ulLfq{1oI0%4?A^W88hiBNM*i3@&y{{GzJTv}@Wj$! z=Pgv?A1!hV{cHE;emuVpo^Lki8eh(hsCr68VCVuqrR@8bdg&xs2&MFt3AF@DRrWoW z(mDVOVF0TWyI}|Uy^>Fuc+N*1rVS_D^nYT`z3i$eq|MhVKAQf;=jX=B~o6lQz zjeKhHt~vJ=xMiY!UN;K!{P_IKkw9q~ER-(=;N^vKUV+7z4y&kasjc&QuDt?wZxz~b z$Cb!aCBW2$GJ7S4sq%~y$HdR!6lTJo;~pt;%b)-GOo`N{NI7cr&To-&mP(a;<`6J_ z&BRoJBgfhAI?2L{^NOkeIC4E2J3w0H(o06t!`X{fZ#h@iVyWe^i3cGBX5dY z)!-oVD8e|1?=j9Cg)oaY1V0th2-D81h(YCJqkbVQJ3x5fK^l7Y%wkbf2nlc^gyOE{K&m5vP~k&P1PYxd z2#^%?I#1I~9O1shHqeJav$Bf?T4@7?2)Bw|OU)7QfTBJ0zUgEEJN_I}E2=$Vh~48i z_l-Kn{pTN#A7`>cF@r~%t|4Uav-#00iR6@$T!u%n=OKW|-BGz@nWLbeg6(LMPafnVSQ>G?_1tq#!3Z(Tn$=I6-DgK+n zfBtoW{LUmlkOoA7KxE)JL+l+j2ne7HfBMRIVs~0Q&4@M42C81N@k}I-uhB0)-37Gy^q>UAITPf_kC`MDP-Ua{|QsjxOrN zQJhqyWsdVSFVl)%rGsd8lvGNQ3mJ*)?FmM5)!6YH?w)~L4wyF5>n37ze6v!~XLx>n zy;zzcPKS_)yP!ug(oSmH_wq1=T;!@rzDc8vMn(gr zu*{VNVrZrXl*O4=le9z@xzlay3+_sT2D*;l%N0wd`sjBhMocwGokDxTIb}g??mAP{ z52z#V3|G{pA$WrRSrWj8!EbaHJwYb53j~)Dp*op3^tXf3(A*m->ANkDZWvAIKA8p( zvr}EoTDirUp6ECA#R6rmanbaNh!kk0U9rge6GxnxLp1&3G5h-redhW74(_|E5O;bP zGVXOPx<}TqpmrOhnZ5ZXf!;h(i@GUP>rkVeJ|>8p?8uaoRVuO`)I2!HwUm+6;Ey^q zbuPAmu?cL79<65;y(4L(3@IVes<{WeHTo>`Jj<6tyx&QOBXxlp-KvG~{ix`5{btVH z#oCLAqCjsB4Z?jCPO@c7UzvT4K;!LbYqW5WSe#raoBfDLBLfBXkSdx)EjhG98fwT} zK!OUu9^!J?@b_6$TMYM@j>!$_P8?x8bI>j(vsg!fU}RfM8x0>(R`C~!(SjOnKuGEe zx|5uMj_Pjq@i+eZYt5`x>B>f(RY0Hk*RIyr7)7|p6VWRjFskb9TTvaPUY;~DU|vaJ zvWa22OQm=}&Qs&^t`4?br*KtsldEc+pc(h-jH~9^K&CP47}$*8-jpZFW%IsRCD+4- z#-k6oLn|U1k5TPFwXjKv2KKa7*W1~fp-BN`D}FVP2r?a?w$pJ$;7tH7oTfiDs=+8? zRa$q<^IhIKE1T=04aT(Z#Z0Ik2CjQ@6KL6vendP?${_NFVJG!^8jEX3fmEHh;Wqx= zmo5C;Z4mLkA;A4T%Fn8qA@0P3w*+-$&HP}F^DOcpZJ4IP zeO~TYN`aga@wTFa*4qKN?xk*!3}M)fqT3UyuBtWTBBadjV3Ej&OZ9O(bY;+BlU7hDGuxFL>}Z8HEXW--xPA=g@>(ee*XMv85|Wj z<_yzE57q^%=4x0k$2vcppQLbW?nIwhfvrFkY^B05q|pb5fhb4A1^+v{lcA3#-9;@< z&ge#xM#U+cX=r4Fd+I^P3L2TZ8USe&q#4v;niR%f&|ikjWf1s={y&uijZADhBy?i} zRhv3lmu6Mbi1_?)Iz2pWOng~Ox1?p7o-7F_H7;AVXlR4w*Leo2q87(!Y`^wkXw8wth4(D0De8_`|~YHlz;bMx&Y1+r~;! zL~l0Q5h9Mcp1v}8;qT$$tc5+ylu6qvrmG5(N$xJ*;QbWfFEuXmV1Jnw)AMp&_F%=r z$FMYfPGi+n=cy@5wFazjL%70(6+$c2`a~kDfL~0Kg0fXl@sCnSm+h(^>>}$-!;?mnKJx8 zn^x1CID!T33kOF?_|Om`WEPAStVKx1fcs=5j06Y3LP#viB|l+uj7ELPA)3QpqMfM! z!5msMtG(bdcoGbnD5ywNp#b=I43i$g00`sp&Yt=7Hv z1}xP9TL1vO5VUp6z{6do>zYj`L}zp!J~IbiLG#|H*1>lScHFk>=+h2yTx#gTZf-4gWtj^g9ER zJ;O&8k#E$)C?ZxAi%FvyQV)(trd3`TH3x5y-%|mPCPgmCkNSb`;HIpXcWqbX4a1=Y zT4jI^fwuCKQ@zI@0wA-&1h?tuP^9^N{sVw0K1LPWm+F3QS7!jk6@R&t>APS~}$v`#=R1*Cqml9i^dd>iA z00s{P>*czK`(v-SC6^Aiefkwp=$7*i;I;o@&-GiR)~OVV7_QeI(Kj>!07v*EfTqL` z01OQnZY~!eY`1Vj6VBo$MbJe3*JAf^b-+~)>8VKosG*_4v=aJMc?8EQ zOZP6U1SH#lyA&?wSfZ_MnkiJonZ{Wi}SaF`20R7-KfFSIcui`OZ09v%w%12!=JD-^Y+}2A5&ssX-9v^}q z3nUqAiH<)XP){Q*v9{=3sY4Pok} z02Q>H_9eQSIO`BD+)`3Walb69Zhm>l1bE@ChUkAd%)mn4(Dyx0Fd79V5QRUN0tHvA zh30P*A?k6G@Q!C44{^+B=SjlKO7tPI;PWIsF0Fkz5kR18#=mAapt9KotZi*LX!mDp zP%u>Rs6S!Hake3i+Gk0_$&UFx!VAelVORmMeS34{FgKQjq3gSz?{~Fr=dA3G?(<4K zkaTpO87bmTlR5swi#iW{lFWf%&U?_%(#VutIwhw9#(74QuX)zOyho_efXgMipWnuz z*3(|2M1zN627Nb=d*g4QgpUN?e}n}*5IV$qA2J74(FUUNBgI0Uo${g>nee4Ojt2!#A?g0PiIo9u zi0&MneH|4P-!%wsqww8ILK8wICt^-D7SLi*g*;y<=mieGo8a+S{^#c-aL&(1o^}LH z)5#%>Up|5-2U%uK}5V;%*)+xe_Vb&{*tbahiP@mV=%~*#cIJ~ zJX0Q%G*5Cm(8 z;Hw;{kQ!>`C<<&Y$Jscsx>3lBmF{({7B8>wUv}UA*!{Wy)Z;eag6t=8!jIG=={6rs zw3!&`Ld)GmqYE+!y)nJF3msO?Mk1c|D%-$Hs9x7W$PGEk!ccu|EnxAx+DEum8S$^s z7Esm!L8@sM6T{+%V+9zi)oTCv<*|JE`uO{|Z!c@I7bIVi&V_BB*z5@WZnNH6jqYh} z*c&nx6AS?acl&aDt-`MWaJOPtyU{03XDF#1-srUI1=fS?9nrxY-*5LPm$7-#qDV#w^cz3*xYR1AbO6*WAY z1SY5f;InP#2tbF1+|bI%`w@d@DX?Vzsh)zM)~MrDAXpzlv3fc`zPw#FZy(p|>GXKn zoG$;`W@`Dp0Puf^(@5;13jzHi=TPR(cnWU1pJ>-b{*e0veXBC4B`P)Xpv@3KI8N;} ziGa@2YKigRQ2aF;R#WoE&`Ib7${>g%(9wSX#A8=pUav1No73*Syk3!wX~u40e(LF+ z($Tqom;PfQqUvc{w5KzG;(ovS3PJF#hIp1qLsZlN@OP(jW0fVSYbi8h1!+|o5ty1> zt;rje#)u+`RU$x<)EKU(-DdMxo_3e7Z=37s<8hiMDU0~9A!Q*5w()B49Tb%4HCX|` z^YhnDd3pKxKs#LC*2oRw-~V&3;Vo6m6u8`tse-07$aJKzxG|)mI;B7P<_a-%mQCLJDp$f?$kN-#xyRWqEyhE?-ZZ*XPS- zttLltQFkQuC@=u+?V;78dYv9x{i3OBXDlJEqRjk7fxcX4GyMMme#C-f=zBM_kC&dP z$hTeqG`At6Tys%IN*%K#&>Jb@{OR)kx&eU8^Xq1Jyq zcWr79Yx|}mZ^@gv}h?8-wCO`)A?SZl!f1e)aGe@wE{H#iF7XOb; z>w9qaxj7pRtGs1+`7_|_{KD10oqen9g?3A^` zkcXPBV<_9Dg|elOE#x2UJ?DO(tVyFxG?vFdzWd!@=bS6`09aXgGY8ZF+F(4uBV@v& z1P!Shp`oywIa-F?Ak84AiP+AcY-AGoRshhS&R~v!*Cen;yWXJ zwd@xX46)B{KTZRK4ooNO#n50PM?7IkRVyThtN{iZg`t!Kj5l1sYqVpDpiE^5G_Oq8 zuSSRgQtVf4Q7xOxV&0V1)o!=kNe-~~yM91WBnBko6gn|A0zu{00L3dRJmsqWr8%*X zHiAwZLJ|)m8Zem*Ze;UN>7_?80Qu~6<=#<(qqK5LT|&H{(TbV!GjYJ%?WUAA1~6B) zbyc^m7yuNC9ZyrdNbI4sI!wtZ8GQzqTFdD^?O6gfgKX?Jn-NFqfrpWnVq*I8iD# z@f0`sw&+M%b~<*13Wt)8GDlCdx7r_Bo8<7>qG@Y_xy?!*70cZj=|9q#Bvg7bWnwV^ z5jE2s5Epo80T@Z-kkQZqT%X&nkg|gkdk@6%M2cwC!*MjA!_h{hfFXv^7>)r-i59G_ zAk#R~&3$(Jty zrsZ=;5Oq_6sOtlR4olMyo*(sTjpZ|4AL>k?i&dmf6ecqx-d&Wt)n!>Pq?HMn1E@G7 z8x|krngP#W&e#VegiyicZ&UIc&(&!J^4A$!DG`guHeyYC7bujF{YMD(j|ea*{DfQD zfa+Ob+%Qd5n9dSo!i#qobz3azdeH#WEf(dfd~$ebVra(oOCLz#zJMhtOU7E>B!r~6 zPWh*)gMde*L{?zv^?`jOW9yCVwV^hVGEBn1J+HK!vS)yq9Zb853Rad}v}Jv@+!D;~ zDrp79SE_;?FkcT35)Ru?hA{I%mLk_sXq=q$259yUO6Fh~dL5$$7Bcv(mjf{11}zv` zq928QmBQ(4u*jd`sy3wBm}x+k|7CY2e4sAN?bX$?tTxT!0S5>ekD!4CwkV4kE=ekt zH|YUml5Bl3)+-ySU+<_wYBpEbAAQDvo)KtzQ4}hb?5wZ`I7aeC$a40fLmD=>^h~-~ zId2;S=1L*;DcOV2O@Y(_^Hngp&`+`&j)~;cbdIQ{4fp}^5^5T%L>lU#siOn{&y!do zblkU~W0>UmT{5T>j4TmY#+qwEGNoIN;NquxE?{mY4M4zL+ceEJiZEH3fT$col{=m? z+?c!INZgJz=e&vt-K7xJ-!|Wl^OP6IcIiDt8uu8Q(JI1yEcsUP+DB4nN$+si8!4eJ zz?_t!YO`7HuAO{J40kY_LZ@@2T;^y2S*~oApOb1uKwgL|)c}OQErDGf?U-1seLxD) zO*<46?FqihEPW~7>z5{`n9rz^9)5Idy%Y#XFej`)0O@LVog+i2jUt9zkW%(A)F03c zWv39C*jK2O^yGz7&eD}!GOlo*YOsCyQ~))76O#T@c(lNG!KSw;>@!3%w` z4{X!X7uWN0RToWTmAP`ho=(|<1rkY&kduK2NYj8jWG8g_%%Ew)30z8W8u_+;ssVN| zKeP;ZL@-218h2LoV##O~innnJ9m|e}@)$IMsez!*5#5dp1Lh^boOobU6^o*&)`>=t zbO1FVFev&USF%dUTk$Qcnb<%C8Zzg^w>bH-WqVyh!$=k%%YXcv7&E%muwk~3IC32& z9-%q_R85CEQe&vL$&OtY)oNaB8-lrcAz0`lF&Ib;AREq;L|elcYm?8RM#=rxBuJ+& zWhhQ*9Cd+bbd{(xAeIn{;}H+>#3b%a=pDmOO^OG$A*jTGQfSz+???z_31PQ$J+IqU zEd{7qZtG=Tw43h%*&tp&m$r)zY)1<-eFsv4Hv?o0|0&pl%dhe6y<$Ena~Sqek9t4? zXVe{&iS>kr6Mh+kl|$&*%FYGB?kRC%k0dE)+N6HAE?3JfB=%;vZCA~jV4>gsYMU`~o@ zS(j@w0Xd{Lmn5n3&%~%jlGE^|t6Bnh6jyHXF`*^Jahx=bqhZ$v%yD!5JYpi*$oLQa zo(7oF6DEj)Aq=t!APLQ&7N!1Kd(~X++M1aohk>l<~F6b)|6(Qe@p|l*p15!Yov-VOj zTs1G7%Ss+Bn_{hiIk2}tI1G<$@K3#tt8wV&WB_PuD@(ezA&Ae%ki`25Uv;cy+ z-L4dJ-)^^S>^50+;OJ5vx0|v_=^?82LgQDAlwg5xsd+cw!s%ui`ZRE?8KzNVj*Mtj zmV-%Vw1+|_t2#8~7*~I>-ffC{xxFfCfz#3jmbJLsu6l9 ziK3_{YDgF#-4Za@2phg4m}>~;Fo8;W!x|=I7!vYMu*{5^Gwp^-F;aG6xQQVX>~D3z z*$MlAv~R#edrZ2gl{;YTGs5C5WeCBn)WujveTwJq93B1Y=;+Q7n#9q4?~d=|{({dL;Mw~TQ-3mY zrz+{?c{my!{rcYhfywIl(U-UH$8q@d7f)r`x9{ElIcgtU&ZB!z@B3bG_ilPo)Z2N{ zZVQ6B%lUG(XXUs603ZNKL_t*deK1Y4(-)^NUYwquoE*9S>4O(v$(0<^(hi;+oSd9K zIJtWwn=G%8*E~JB`)KF|$vbmoj-O}xFnJenL|{XR$RUm&e*1?v&v?A~?)~`R`-|t3 z*T=`NpSvR>yU`!dk6#}=eEYle$1lD6ug;Hu?@F#oG!4F#i{EKaV>xKN~RXY3TYjbh@+Wqt2{`&X-{P5wwKYaMp zzux^rFFxR6WChZAz?c`vHu8Ra_U73eEc?grzCAP-Z=U^+YU}%HTgk$Xw97+KcW)l3 zS9hz_H=znBRW)q+k*CrrNGzTJvtvy!?pQ*yEjhx%xso4Jp-O3#1)&J#3W`MdEg}JL zP-*|c{U(9lUFQLxu{}96^L^hr#~IF>>4GopSYZG<^ZuLk?4(Wt&y%X~4o19`&FE6k zc<0+9E#{PGQ!Xg8`M1S?_L*%{k5QM-d8>BOZf`sq%eC|7 z#&h@(1!Xdz#BZ8u7M=&k?U_uF;D+Hb^GYRXrnQW z21y%X*l0B3DoK_bI2UQ&;*ExMNVTCaG?G_PjFQuE8+9X>G#YoKzuey5KHpwH=r=<4 z`W9BDR5X*YaZw?&ZrG@j*DyrR3Ru|a5Jww%V(E`zRIGX`bhAZv_xF!lN4uE&R_g{B zI%-{ZN!BcrO7@2g4N`xrO!9i_+i6pK-kd(K!RS1yYT^pUgiPlx$#CZSDl;ya&_Hrw za-niiBCGB)UN?+HG~&_Q+n;V1 zU0v2d+OTw=WHe!tgKVku(#Ic4r81W!r|~MNOeq)}jpoJ-;@FbUlXB$J_;!15c5iR{ z>z6*I{@&mqB`ft<{Iliw+}kKWQ|M=^QjMt{Su2sxr871@Rx)(jLLAknc5D=i@Ace2 zoOSp2Tdh+~g4XvN#Bg0G7ytifdQ*!iMCZJD*PgG=r2_+UI)`<9Nhyn>`Dhb72>wB- zmju?VO=&tuB9F^-x-JtxFfkfVUtqm#Dt*7dw|#j3>1zT<_O`cwJ>0`gD4VgQF9WBZ zH!=~%?#B|{#ih53;-SP>rCbL3SbnP@+&RtDG-O-Ve zr#-xbk^m8^c>bvR&1tOBV-#pMCkW<4xGV}t6uMj$#%U-^&GZYEQDrSY@1b+3ajB z*PuQ_LeCfSMFMc~u#u=KVwfZO1)>@*Q%jZ%L%(V*xo#LOS*1*7-W6`>hW-Ni87Jw& zo98=7;lAx8xNmp{IS!!4)!dYiwwzhTvuS2J!efq6_1%z94Bn9HEs91}Nw*PY<}xM* zaM{{_uhYC@_6#Jr?CqA2e2qN*a%K$o-B|g7=k4||3bbpQ408j)vL>ZRBlYf4QlTV% znydJnCsncv5X1}RG9f={2tZfzWC?Ss$n6Wv`{&PC0p4FSk%2W4Opka0*h!F?lz zD#R!v3#qhO)vCtKra4l|4&QT|4oF}pxsjZ8wT}>RU9K1?A%-Mqb%`-})30A{4)+Z~ zqN_FSxsC$xyn0bXOiKq2Tf}j>o2r}^RD2TGm8v)n`A0_g!DWa6SU}>0V7dt7iuHnv zaHFy}d+_PYzd!0Y&_DcyqH+ghstX2|S0coQB3Xshr=+0JQaq8dy_S?s`gXd)o zfb&U`RbXoY(HC71Q=vmdGDHR8kS9^MvPxmKR?0bH8=m!NRkc0l9Kot}ic{!j1>5=dI953F7fQeC3wXXlAlxwjBZZESCR8Jt_(*e=Z^Zswpi6I{>1QDbw9OWUQTUmubNKVfnkrF#~M z9ZtP^^X|ry$KSOyYVViZH!vy?2}%8mwv9ZOu#6?NJ6GC4%a+m8?lG(Nf7}=S z&0T6-rZv&bAZ2h=OJ_AnYg@>slRi#BkH6kOPC^+^QvDNhP!PKd+gnJJZ{phef_4hn zl)XO9=+^9<*_+KSXusmS0i)*co~w?**I1GlN)Qdd-WnR649J7LHcZ%E>8wj{*TbP%vk7)f zyG#bGj2p3%H5<%&gWhB`8Vs&9T9Y+QPETfW^(M(JvD_q2wY5h9c+cH({hb&*ktRUT`oO*%golm6*j^V8F}r>AswyJLQS+CQoD zpB&VVYPE0o@9!TS)M^I@wS(7Fw{H2P`U(I1bkwe!n!i0|GTHt9$*9o)?L>{nsKf7{ za^Syza#E)^nC&{R*RGocWz1&1-e}SrHTP+Pntm3B6=FcvZZ?C9T}Ie%0`{Jm07n=m zK%(~Oq~EV|z+%cjH9a*oO=KQ)zJ2@d>RptTJ1gu1_Qwb8+REA*TVz@G$K(C`FORnN zx3;$S_TC|aNsE&;51#- z;78aIrin9an1bfH=S@#2E4RjRw!(#1t}x4XKs3JR}+ikb#& zkw}9q8lW3l;zD}xzIQI1|9pDd{P3aKJilmOeBjH+XJ^GTltzP9ccT^qrYPVXg`m4Lus>2(N+KtA7-B>6XGy@jGxS?7tpz761 z0sgu~*1?c2m{6^DC6)*jpfO99MW~PyqegwH2%1UK_8Rk5U6o})Zq-G(!e9Qb?%pUoDwTEaf7 zHIz!FW>S@uCsxQ*5O3*rcH^6%Fcau@`%E$(NajF;crw7`C@K$sR5D3Xpxs+uxF`#) zmLe)5$H_cjZ`Zl9EOVSFKceWJJm{UoOa}^AD5jLp!#{k-k-+?9t}pciedwe>mq}GB zh24}dld1T^7ON)}OBFmm=!#UV5QOkZ=9kI;GmzOffUv=7@7B^7JWpQvRl!eRo%bgr5i#cy#c@hqXPC*+$X!Jym zM!*STt=w*>$Cm-6Gl*TB^~6#$n2S&7Jx-122d^>wn6mnOvk{V(9Py0cZ_c=L*17Hu zAr3MGy%Uckup_y|#Ay;b=q#GPW=Ala@%-CURcK$y5H$)f3oV{2H#w!H4tku@Q|baI z$m*c2$Uhy`HZzv>0AZGpI6~!-e*iCNvP31pCD^DtltLz;dp3pOS>!B$>P{ch?VQet zCeQ=;sbGz13EUA4x)_Vtka2@pIAnf;*^w9o6h#;jh-!)x=nfJ)us7~4CTm}x^GaJ3 zAY6F9CAU=>!{(w5v4o2A&=oC?>$M@Y1cm?b5sa9*@hO99;}wt`#bO-aAp8KDWHcd{ z4k;3E4a&;wdslNlNq>&OdBO3V{Uz3&GfkMT0oSW|2FJw2F+!jd@-Kq=@7! zn$RxjBpq1n(_7!3%k_(ToiEqb3tlKgtk-#tD~swCbO>A$9q1Mju7@(Gs2An3s`S*; z*7>93{Nk6Jg!_S=nZy7h(d7;hmgB zVF*P$1ja%rX6OL~HY2~#iAnz8cPCuObRf>GW1{xrqIC4rX-iS-f}r+#Jzl8ygl3Zy zFFWTgkrVlL83g)3=If%uD{xi!pa&GS#n+X#A~xSQ;$L9kLk#6e(mjp7$G)Xuw7}61 z$&@h^Sic$)BWq?m72<_exlM!}+2;y&AWHr8}Os zd(-X_66zdDsk3Nzdso-L?~a@CN~z& z->u&JYW4mr{#yUh)4%LL{nxs|B5$MndjH|GonH~S!^Hqo?s#wm1<{%Po3FfFduT79(2c7M@P%$Y;p7Z#ecFm%ig{Jte;c9 zprfAuh(Lbu$bNGF7|#9SyPtk~hza=-?*Rhu>OQL9`~dls&_JwIDr=Rs^~%axrGg6F zEBkov?|WW(-A5C^^410J{p){ze)k?#^uf;A?R2km)0;x-z3y$ZH96YDOpDBUGlb58 z<7m*no=i~34Vi`}%i#j$#L;9t93V=1?b(1E42H9-%j50ka(PnA^DIdc^cC+ufBwhM zFL>*%=Rfj(2ag}$TgBhkDDPL+R_>{uzW3_Ylc!HmSAUIfN^8|}r5cv2YFEOdf(=}V z;@m|pi1>)3sGgu`&+`0Y^JYG8cRE+?0h0R7<@WB`Xk+_&Jo_>qboRQP@oa&tw7fjt z+}OP84ZbW`1@&fM5Yt_7b9;HYSnOen9(LNF=EK3>vbk|`a?(iCyjH7cS;9^OFv8EM zAousnY@^oURj-0J2!{IpI^W>$6(U-@zfvlrgDQtX5O_479fjG9l01%+dV&uzEKl=1 zO&g8cWIF70#=Z9Zs(X31+2V<3{zm5TdOSqweAJoG=hv6JBmVMd^X&K8__ui*2#&_> zMSHwB+}*|MUzRNKAgoDmI7C37{rBJfey!H8;ULfwsK|hVIA{&p0-vU-%Yk#Di*j|{ z^DFnO>uW3PYbBUYji40_;B;)p&d5bclA`5e3j@>hT7$;p+5dR4J?TwvCfA$I!{gI8 z8yn{rCns-OXB!iQ0Vdz&<>A>ztJT_k^QP6@KD@jcEM_+o5OZ}2q?=8w-ah^1wucM~ z279yiUUxnloE`u{qtO5lDH^AGT$i3;245W{0WlDd5U^GALTF~adS3wW5MX*9?#9+? z71e`fG@k≤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 M07*qoM6N<$g3=yxMgRZ+ literal 0 HcmV?d00001 diff --git a/game/assets/icon.jpg b/game/assets/icon.jpg deleted file mode 100644 index 10f3fe3296d033d79f7aaf2ff33c0499d230c572..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 24427 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 diff --git a/platforms/android/project/app/build.gradle b/platforms/android/project/app/build.gradle index 3929a7673..d5f75a6f6 100644 --- a/platforms/android/project/app/build.gradle +++ b/platforms/android/project/app/build.gradle @@ -11,6 +11,31 @@ android { from '../../../../game/assets' into 'src/main/assets' } + sourceSets.main { + // Another hack to automatically get our icons + assets.srcDir '../../../../game/assets/app' + def iconDir = file('../../../../game/assets/app/icons/mipmap') + def resDir = file('src/main/res') + def densityMap = [ + 'icon_48x48.png' : 'mipmap-mdpi', + 'icon_72x72.png' : 'mipmap-hdpi', + 'icon_96x96.png' : 'mipmap-xhdpi', + 'icon_144x144.png': 'mipmap-xxhdpi', + 'icon_192x192.png': 'mipmap-xxxhdpi' + ] + + densityMap.each { fileName, folderName -> + def sourceFile = new File(iconDir, fileName) + def targetDir = new File(resDir, folderName) + if (sourceFile.exists()) { + targetDir.mkdirs() + // Copies and renames it to just "icon.png" so AndroidManifest.xml is happy + new File(targetDir, 'icon.png').bytes = sourceFile.bytes + } else { + println "Warning: Could not find ${fileName} in ${iconDir}" + } + } + } applicationVariants.all { variant -> // Ensure the custom task is executed before building the APK diff --git a/platforms/android/project/app/src/main/AndroidManifest.xml b/platforms/android/project/app/src/main/AndroidManifest.xml index 513a5bc65..b14d45171 100644 --- a/platforms/android/project/app/src/main/AndroidManifest.xml +++ b/platforms/android/project/app/src/main/AndroidManifest.xml @@ -9,6 +9,7 @@ android:hasCode="false" android:theme="@android:style/Theme.NoTitleBar" android:label="@string/app_name" + android:icon="@mipmap/icon" tools:targetApi="21"> diff --git a/platforms/ios/build-ipa.sh b/platforms/ios/build-ipa.sh index 2ca216901..b38987894 100755 --- a/platforms/ios/build-ipa.sh +++ b/platforms/ios/build-ipa.sh @@ -37,7 +37,7 @@ cp -a \ "$platformdir/precompiled"/* \ "$assetdir" \ "$apppath" || true -mv "$apppath/assets/icon.png" "$apppath/assets/app/launch"/* "$apppath" +mv "$apppath/assets/app/icons/icon.png" "$apppath/assets/app/launch"/* "$apppath" rm -rf "$apppath/assets/app" "$apppath/assets/.gitignore" cd "$ipadir" rm -f "../$ipaname" diff --git a/platforms/macos/projects/Minecraft/Minecraft.xcodeproj/project.pbxproj b/platforms/macos/projects/Minecraft/Minecraft.xcodeproj/project.pbxproj index 7600da5f0..974fe9f2f 100644 --- a/platforms/macos/projects/Minecraft/Minecraft.xcodeproj/project.pbxproj +++ b/platforms/macos/projects/Minecraft/Minecraft.xcodeproj/project.pbxproj @@ -2419,7 +2419,7 @@ 849371362EE51E6700081C5A /* Default-Landscape~ipad.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "Default-Landscape~ipad.png"; path = "../../game/assets/app/launch/Default-Landscape~ipad.png"; sourceTree = ""; }; 849371372EE51E6700081C5A /* Default.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = Default.png; path = ../../game/assets/app/launch/Default.png; sourceTree = ""; }; 849371382EE51E6700081C5A /* Default@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "Default@2x.png"; path = "../../game/assets/app/launch/Default@2x.png"; sourceTree = ""; }; - 8493713D2EE51F2A00081C5A /* icon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = icon.png; path = ../../game/assets/icon.png; sourceTree = ""; }; + 8493713D2EE51F2A00081C5A /* icon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = icon.png; path = ../../game/assets/app/icons/icon.png; sourceTree = ""; }; 849488342C9284DA006DB706 /* Lighting.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Lighting.cpp; sourceTree = ""; }; 849488352C9284DA006DB706 /* Lighting.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Lighting.hpp; sourceTree = ""; }; 8495E4872AF0905B00A06901 /* AppPlatform_iOS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppPlatform_iOS.h; sourceTree = ""; }; diff --git a/platforms/sdl/base/AppPlatform_sdl.cpp b/platforms/sdl/base/AppPlatform_sdl.cpp index 30de0ac72..ecacb41fb 100644 --- a/platforms/sdl/base/AppPlatform_sdl.cpp +++ b/platforms/sdl/base/AppPlatform_sdl.cpp @@ -145,7 +145,7 @@ void AppPlatform_sdl::_setIcon(ImageData& image) void AppPlatform_sdl::_setDefaultIcon() { ImageData data; - loadImage(data, "assets/icon.png"); + loadImage(data, "assets/app/icons/icon.png"); _setIcon(data); } diff --git a/platforms/sdl/sdl2/CMakeLists.txt b/platforms/sdl/sdl2/CMakeLists.txt index 6c067ae3b..f575bacfa 100644 --- a/platforms/sdl/sdl2/CMakeLists.txt +++ b/platforms/sdl/sdl2/CMakeLists.txt @@ -96,6 +96,6 @@ if(NINTENDO_SWITCH) nx_create_nro(reminecraftpe NACP reminecraftpe.nacp - ICON "../../../game/assets/icon.jpg" + ICON "../../../game/assets/app/icons/icon.jpg" ) endif() diff --git a/platforms/sdl/sdl2/android/app/build.gradle b/platforms/sdl/sdl2/android/app/build.gradle index b16082fc3..7d8bb604c 100644 --- a/platforms/sdl/sdl2/android/app/build.gradle +++ b/platforms/sdl/sdl2/android/app/build.gradle @@ -30,6 +30,28 @@ android { sourceSets.main { java.srcDir '../../../../../thirdparty/SDL2/src/android-project/app/src/main/java' assets.srcDir '../../../../../game' + // Automatically get our icons + def iconDir = file('../../../../../game/assets/app/icons/mipmap') + def resDir = file('src/main/res') + def densityMap = [ + 'icon_48x48.png': 'mipmap-mdpi', + 'icon_72x72.png': 'mipmap-hdpi', + 'icon_96x96.png': 'mipmap-xhdpi', + 'icon_144x144.png': 'mipmap-xxhdpi', + 'icon_192x192.png': 'mipmap-xxxhdpi' + ] + + densityMap.each { fileName, folderName -> + def sourceFile = new File(iconDir, fileName) + def targetDir = new File(resDir, folderName) + if (sourceFile.exists()) { + targetDir.mkdirs() + // Copies and renames it to just "icon.png" so AndroidManifest.xml is happy + new File(targetDir, 'icon.png').bytes = sourceFile.bytes + } else { + println "Warning: Could not find ${fileName} in ${iconDir}" + } + } } externalNativeBuild { cmake { diff --git a/platforms/sdl/sdl2/android/app/src/main/AndroidManifest.xml b/platforms/sdl/sdl2/android/app/src/main/AndroidManifest.xml index f580f1b9e..85550509d 100644 --- a/platforms/sdl/sdl2/android/app/src/main/AndroidManifest.xml +++ b/platforms/sdl/sdl2/android/app/src/main/AndroidManifest.xml @@ -22,8 +22,7 @@ diff --git a/platforms/sdl/sdl2/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/platforms/sdl/sdl2/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml deleted file mode 100644 index da1dcd94e..000000000 --- a/platforms/sdl/sdl2/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/platforms/sdl/sdl2/android/app/src/main/res/drawable/ic_launcher_background.xml b/platforms/sdl/sdl2/android/app/src/main/res/drawable/ic_launcher_background.xml deleted file mode 100644 index ca3826a46..000000000 --- a/platforms/sdl/sdl2/android/app/src/main/res/drawable/ic_launcher_background.xml +++ /dev/null @@ -1,74 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/platforms/sdl/sdl2/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/platforms/sdl/sdl2/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml deleted file mode 100644 index bbd3e0212..000000000 --- a/platforms/sdl/sdl2/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/platforms/sdl/sdl2/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/platforms/sdl/sdl2/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml deleted file mode 100644 index bbd3e0212..000000000 --- a/platforms/sdl/sdl2/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/platforms/sdl/sdl2/android/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/platforms/sdl/sdl2/android/app/src/main/res/mipmap-hdpi/ic_launcher.webp deleted file mode 100644 index 3896fbcd7d24a1eaa3f8f706a0ba817fa3b5c429..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2682 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 diff --git a/platforms/sdl/sdl2/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/platforms/sdl/sdl2/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp deleted file mode 100644 index 2ad8e1bbf6cc4118601a0935a9ec93dbfca30f97..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4342 zcmVZJ3xp>>UgdF#$3Y6;XtC zltL>!RNhkj|6io#{s(bE*4=S;XJzJb_m$c5pWpD`XRm`pa(8!^=9R<=-~gK4BHa$4 z$vPLHT61R;(UYEm8#s-|4ICO-(;sUj&wc}lG?z$QE{|c7NLuvAo#=(kT_bmQ zcXxMpcXxMphu7VvY435Vu4ZP%nkgm0FN9xK_|k=T;D?oX=9CR=rD&H zXx!c1-64WZB6oKua$9%DAtalKaNXT~u{oQ%;g$S%uVBydjW*j_-<>;nv1r{j(s)m; z%eJ-cjHItGt#6`^M&FO5rYo+f7!+qJtg&s|wr$%s4q(}~owm7C&tTdE($N>dR)vnh zCWMX*42A>&!canJL6}K{#_r@A1wDwcsrtP8^Nb6SO6(%UiCPq)#^gk5`PFNWx|?G# zc9zo(0D+Q>rYIXuA??vk8oz0fF{Q*92p9ZuY5@{HP^f6VCL2(WiMF+;%;6~_F4Q5KGD#pR)`Cxo& zaLayt8i3pO)U(e+kRY1tCecF*3709fP@Jn>F~*j`81E{QTwrQhT+6-&KA+JatrB|& zfDXsEQb?7qT}4x9_2Y@F5KMBRO`%o0VhXA1KzfKadDYzB0Xk8Hw3NScVS+IW( zR^?q1iWsuT+9Ul zj1S=%&x0@kQ%KrR6vXwCv0O!=!N355vJWoN0+b|-m9^49+GR?IGDZ+sBw1lZEK}td zjxJ6hI%S{L-}dXb(dvJ}9B*SOe&pGDJ`c4u3=rzskf??=l8`~oxy%cM`o8sM6*v3@ z129MV>(z-~LF_s6L%_+l5>ynIZlyR^k2fpw@hnlYv_Eaz!T=@|=?w#GBzj%5AHxzU z2$B}R?9V$&k(?yVQSPf11Rkb;6BrM8`p9Z(No_4h35d@n1GK?-FJ63#r?~)V*=}vI-6y z?Jo#oddWs+W9QvOHF`$3#