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/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 diff --git a/platforms/android/AppPlatform_android.cpp b/platforms/android/AppPlatform_android.cpp index 7e23bd8a8..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; 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/sdl/sdl2/main.cpp b/platforms/sdl/sdl2/main.cpp index dc6aca02e..328067137 100644 --- a/platforms/sdl/sdl2/main.cpp +++ b/platforms/sdl/sdl2/main.cpp @@ -62,16 +62,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()) @@ -440,6 +435,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 642b54473..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()); @@ -460,6 +445,22 @@ 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 +} + +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 1dc3bb7e0..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. @@ -82,6 +81,8 @@ 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; + bool isVSyncSwitchable() const 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 e68bd2aff..b7b090e81 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 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 32d2ab85e..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; diff --git a/projects/visual-studio/Client/Client.vcxproj b/projects/visual-studio/Client/Client.vcxproj index 9befd92b4..7cbac77c6 100644 --- a/projects/visual-studio/Client/Client.vcxproj +++ b/projects/visual-studio/Client/Client.vcxproj @@ -258,6 +258,7 @@ + @@ -434,6 +435,7 @@ + diff --git a/projects/visual-studio/Client/Client.vcxproj.filters b/projects/visual-studio/Client/Client.vcxproj.filters index 55cd0f467..35b451268 100644 --- a/projects/visual-studio/Client/Client.vcxproj.filters +++ b/projects/visual-studio/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/projects/visual-studio/World/World.vcxproj b/projects/visual-studio/World/World.vcxproj index dd97f7b76..f2e980d1e 100644 --- a/projects/visual-studio/World/World.vcxproj +++ b/projects/visual-studio/World/World.vcxproj @@ -203,7 +203,7 @@ - + @@ -211,7 +211,7 @@ - + @@ -230,6 +230,21 @@ + + + + + + + + + + + + + + + @@ -373,8 +388,8 @@ - - + + @@ -382,7 +397,7 @@ - + @@ -400,6 +415,19 @@ + + + + + + + + + + + + + diff --git a/projects/visual-studio/World/World.vcxproj.filters b/projects/visual-studio/World/World.vcxproj.filters index f923baca5..6d71fb082 100644 --- a/projects/visual-studio/World/World.vcxproj.filters +++ b/projects/visual-studio/World/World.vcxproj.filters @@ -105,6 +105,12 @@ {3a97a5e5-77e6-43eb-9890-3fa79b287cd1} + + {53363441-c079-4adf-a246-481be4b68199} + + + {9dca9e2a-e126-4598-a852-105be172d198} + @@ -584,9 +590,6 @@ Source Files\Level\LevelGen\Chunk - - Source Files - Source Files\Inventory @@ -611,9 +614,6 @@ Source Files\Inventory - - Source Files - Source Files\Inventory @@ -662,9 +662,60 @@ 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 + + + Source Files\Inventory + + + Source Files\Inventory + + + Source Files\Inventory + + + Source Files\Inventory + @@ -1090,12 +1141,6 @@ Header Files\Level - - Header Files - - - Header Files - Header Files\Inventory @@ -1117,9 +1162,6 @@ Header Files\Inventory - - Header Files - Header Files\Inventory @@ -1132,15 +1174,6 @@ Header Files\Item - - Header Files\Item - - - Header Files\Item - - - Header Files\Item - Header Files\Item @@ -1180,5 +1213,53 @@ Header Files\Item + + Header Files\Inventory + + + Header Files\Inventory + + + 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 + + + Header Files\Inventory + + + Header Files\Inventory + + + Header Files\Inventory + + + Header Files\Inventory + + + Header Files\Inventory + 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/CMakeLists.txt b/source/CMakeLists.txt index 87a01d349..4299d1c30 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -126,6 +126,7 @@ add_library(nbcraft-core STATIC 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/components/ScrolledSelectionList.cpp client/gui/components/AvailableGamesList.cpp @@ -354,24 +355,29 @@ add_library(nbcraft-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 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 + 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 - world/CompoundContainer.cpp world/item/DoorItem.cpp world/item/ItemStack.cpp world/item/RocketItem.cpp @@ -391,6 +397,7 @@ add_library(nbcraft-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 @@ -426,6 +433,10 @@ add_library(nbcraft-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 world/tile/FireTile.cpp world/tile/StoneSlabTile.cpp world/tile/LiquidTile.cpp @@ -447,6 +458,12 @@ add_library(nbcraft-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/AppPlatform.cpp b/source/client/app/AppPlatform.cpp index 31626e691..6c3f10e31 100644 --- a/source/client/app/AppPlatform.cpp +++ b/source/client/app/AppPlatform.cpp @@ -322,6 +322,15 @@ std::string AppPlatform::getExternalStoragePath(const std::string& path) const return m_externalStorageDir + C_HOME_PATH + path; } +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 7eb44d4c1..8a68246f7 100644 --- a/source/client/app/AppPlatform.hpp +++ b/source/client/app/AppPlatform.hpp @@ -109,6 +109,9 @@ class AppPlatform virtual void vibrate(int milliSeconds); virtual bool getRecenterMouseEveryTick(); virtual std::string getClipboardText(); + // Graphics settings + virtual void setVSyncEnabled(bool enabled); + virtual bool isVSyncSwitchable() const; void _fireLowMemory(); void _fireAppSuspended(); diff --git a/source/client/app/Minecraft.cpp b/source/client/app/Minecraft.cpp index 2ee8c5b2a..66336ede2 100644 --- a/source/client/app/Minecraft.cpp +++ b/source/client/app/Minecraft.cpp @@ -185,12 +185,12 @@ void Minecraft::_initGameModes(Level& level) } } -void Minecraft::_reloadInput() +void Minecraft::reloadInput() { if (m_pInputHolder) delete m_pInputHolder; - if (isTouchscreen()) + if (useTouchscreen()) { m_pInputHolder = new TouchInputHolder(this, getOptions()); } @@ -258,7 +258,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); @@ -266,7 +266,7 @@ void Minecraft::grabMouse() void Minecraft::recenterMouse() { - if (useController() || isTouchscreen()) + if (useController() || useTouchscreen()) return; platform()->recenterMouse(); @@ -376,9 +376,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 @@ -616,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)) { @@ -694,7 +689,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 7d1871942..7d5b9e5d7 100644 --- a/source/client/app/Minecraft.hpp +++ b/source/client/app/Minecraft.hpp @@ -45,9 +45,6 @@ class Minecraft : public App GameMode* _createGameMode(GameType gameType, Level& level); void _initGameModes(Level& level); -protected: - void _reloadInput(); - public: int getLicenseId(); void setScreen(Screen * pScreen); @@ -80,11 +77,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; @@ -122,6 +121,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 f7f8294fe..45c7bc339 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" @@ -104,7 +105,7 @@ void NinecraftApp::_initInput() m_bIsTouchscreen = platform()->isTouchscreen(); getOptions()->m_bUseController.set(platform()->hasGamepad()); getOptions()->loadControls(); - _reloadInput(); + reloadInput(); } void NinecraftApp::_updateStats() @@ -179,10 +180,10 @@ void NinecraftApp::_initAll() EntityTypeDescriptor::initDescriptors(); // custom MobCategory::initMobCategories(); MobFactory::initMobLists(); + TileEntityFactory::initTileEntities(); Tile::initTiles(); Item::initItems(); Biome::initBiomes(); - //TileEntity::initTileEntities(); } _initOptions(); @@ -340,6 +341,7 @@ void NinecraftApp::onGraphicsReset() void NinecraftApp::teardown() { + TileEntityFactory::teardownTileEntities(); teardownRenderer(); Resource::teardownLoaders(); // Stop our SoundSystem before we nuke our sound buffers and cause it to implode diff --git a/source/client/gui/Gui.cpp b/source/client/gui/Gui.cpp index c7f92ea19..f3b5ed054 100644 --- a/source/client/gui/Gui.cpp +++ b/source/client/gui/Gui.cpp @@ -261,7 +261,7 @@ 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); + //ItemRenderer::renderGuiItemDecorations(m_pMinecraft->m_pFont, m_pMinecraft->m_pTextures, item, x, y); } void Gui::renderSlotOverlay(int slot, int x, int y, float f) @@ -335,7 +335,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->getLocalPlayerGameMode()->isSurvivalType()) m_pMinecraft->setScreen(new InventoryScreen(m_pMinecraft->m_pLocalPlayer)); @@ -348,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::StackID stackId = m_pMinecraft->m_pLocalPlayer->m_pInventory->m_selectedStackId; int maxItems = getNumUsableSlots() - 1; if (down) { - if (slot++ == maxItems) - slot = 0; + if (stackId++ == maxItems) + stackId = 0; } else { - if (slot-- == 0) - slot = maxItems; + if (stackId-- == 0) + stackId = maxItems; } - m_pMinecraft->m_pLocalPlayer->m_pInventory->selectSlot(slot); + m_pMinecraft->m_pLocalPlayer->m_pInventory->selectSlot(stackId); } void Gui::handleKeyPressed(int keyCode) @@ -384,23 +385,23 @@ 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; + Container::StackID* stackId = &m_pMinecraft->m_pLocalPlayer->m_pInventory->m_selectedStackId; if (slotR) { - if (*slot < maxItems) - (*slot)++; + if (*stackId < maxItems) + (*stackId)++; else - *slot = 0; + *stackId = 0; } else if (slotL) { - if (*slot > 0) - (*slot)--; + if (*stackId > 0) + (*stackId)--; else - *slot = maxItems; + *stackId = maxItems; } return; } @@ -689,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()) @@ -706,7 +707,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++) @@ -728,8 +729,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); @@ -747,7 +748,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 5c029da2b..4dd2f1922 100644 --- a/source/client/gui/Screen.cpp +++ b/source/client/gui/Screen.cpp @@ -399,7 +399,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/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/gui/components/OptionList.cpp b/source/client/gui/components/OptionList.cpp index 062f32f5e..d85ca3378 100644 --- a/source/client/gui/components/OptionList.cpp +++ b/source/client/gui/components/OptionList.cpp @@ -173,14 +173,15 @@ 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() { Options* pOptions = m_pMinecraft->getOptions(); int currentIndex = -1; - int idxPano = -1; + int idxPano = -1, idxVSync = -1; OPTIONS_LIST_VIDEO_GRAPHICS; OPTIONS_LIST_VIDEO_EXPERIMENTAL; @@ -189,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/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/components/TextBox.cpp b/source/client/gui/components/TextBox.cpp index 895de827f..711ca9c76 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/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/OptionsScreen_Console.cpp b/source/client/gui/screens/OptionsScreen_Console.cpp index 934283210..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; @@ -83,9 +83,10 @@ ControlsPanelScreen::ControlsPanelScreen(Screen* parent, Minecraft* mc) : PanelS OPTIONS_LIST_CONTROLS_FEEDBACK; OPTIONS_LIST_CONTROLS_EXPERIMENTAL; - if (!mc->isTouchscreen()) - m_layout.m_elements[idxSplit]->setEnabled(false); m_layout.m_elements[idxController]->setEnabled(false); + + if (!mc.isTouchscreen()) + m_layout.m_elements[idxSplit]->setEnabled(false); } void ControlsPanelScreen::removed() @@ -93,10 +94,14 @@ 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; + int idxPano = -1, idxVSync = -1; + + Options& options = *mc.getOptions(); OPTIONS_LIST_GAMEPLAY_GAME; OPTIONS_LIST_GAMEPLAY_AUDIO; @@ -108,6 +113,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/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 312618638..5fc0d08f4 100644 --- a/source/client/gui/screens/inventory/ChestScreen.cpp +++ b/source/client/gui/screens/inventory/ChestScreen.cpp @@ -6,41 +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); + m_pFont->drawScalable(m_pInventory->getName(), 28, m_imageHeight - 234 + 8, Color::TEXT_GREY); + } + 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; - 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); - case Slot::INVENTORY: - return SlotDisplay(8 + (slot.m_slot % 9) * slotSize, 103 + verticalOffset + ((slot.m_slot / 9) - 1) * slotSize, slotSize); - case Slot::HOTBAR: - return SlotDisplay(8 + (slot.m_slot % 9) * slotSize, 142, 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, 226 + 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/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 46f241ead..0ce0919c9 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; @@ -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 + 12; - int ty = m_menuPointer.y - m_topPos - 12; + int tx = m_menuPointer.x - m_leftPos; + int ty = m_menuPointer.y - m_topPos; if (m_uiTheme == UI_CONSOLE) { - blitNineSlice(*m_pMinecraft->m_pTextures, ScreenRenderer::POINTER_TEXT_PANEL_SLICES, tx - 6, ty - 6, w * 2 + 12, 28, 8); + 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->drawScalable(name, tx, ty, -1); + 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); } @@ -235,14 +239,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; } @@ -254,7 +258,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); } @@ -266,18 +270,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 +321,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 +373,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 new file mode 100644 index 000000000..b28a8a18d --- /dev/null +++ b/source/client/gui/screens/inventory/FurnaceScreen.cpp @@ -0,0 +1,131 @@ +#include "FurnaceScreen.hpp" +#include "world/inventory/FurnaceMenu.hpp" +#include "renderer/ShaderConstants.hpp" + +FurnaceScreen::FurnaceScreen(Inventory* inventory, FurnaceTileEntity* container) + : ContainerScreen(new FurnaceMenu(inventory, container)) + , m_pInventory(inventory) + , m_pFurnace(container) +{ +} + +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() +{ + ContainerScreen::tick(); +} + +void FurnaceScreen::_renderLabels() +{ + 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", 40, 68, 0x404040); + m_pFont->drawScalable("Fuel", 102, 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) +{ + 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); + + blitSprite(*m_pMinecraft->m_pTextures, "gui/console/Graphics/Flame_Off.png", m_leftPos + 147, m_topPos + 96, 48, 48); + + 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); + } + + 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 + { + 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_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) +{ + 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 + { + 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(); + } + } +} + +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 new file mode 100644 index 000000000..337ea6a9c --- /dev/null +++ b/source/client/gui/screens/inventory/FurnaceScreen.hpp @@ -0,0 +1,26 @@ +#pragma once + +#include "ContainerScreen.hpp" +#include "world/tile/entity/FurnaceTileEntity.hpp" + +class FurnaceScreen : public ContainerScreen +{ +public: + FurnaceScreen(Inventory* inventory, FurnaceTileEntity* container); + void init() override; + +public: + void tick() override; + +protected: + void _renderLabels() override; + 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/gui/screens/inventory/InventoryScreen.cpp b/source/client/gui/screens/inventory/InventoryScreen.cpp index 680aefcfc..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() @@ -135,16 +137,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 +159,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/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/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/multiplayer/MultiplayerLocalPlayer.cpp b/source/client/multiplayer/MultiplayerLocalPlayer.cpp index d4b004840..526fd0ee5 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 @@ -70,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) @@ -92,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)); } @@ -133,7 +140,9 @@ void MultiplayerLocalPlayer::refreshContainer(ContainerMenu* menu, const std::ve { } -void MultiplayerLocalPlayer::slotChanged(ContainerMenu* menu, int index, ItemStack& item, bool isResultSlot) +void MultiplayerLocalPlayer::slotChanged(ContainerMenu* menu, Container::SlotID slotId, Slot* slot, ItemStack& item, bool isResultSlot) { - // @TODO: Replicate ContainerSetSlotPacket +#if NETWORK_PROTOCOL_VERSION >= 5 + 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 d1a7e2bec..d635ff4a9 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; @@ -21,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, 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 68f47668d..4e0b7f409 100644 --- a/source/client/network/ClientSideNetworkHandler.cpp +++ b/source/client/network/ClientSideNetworkHandler.cpp @@ -19,6 +19,7 @@ #include "world/entity/PrimedTnt.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 @@ -600,7 +601,12 @@ void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& rakGuid, PlayerE 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(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) @@ -729,7 +735,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()); @@ -776,7 +782,9 @@ void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& guid, ContainerS if (pContainerMenu->m_containerId != packet->m_containerId) return; - pContainerMenu->setItem(packet->m_slot, packet->m_item); + pContainerMenu->m_bBroadcastChanges = false; + pContainerMenu->setItem(packet->m_slotId, packet->m_item); + pContainerMenu->m_bBroadcastChanges = true; } void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& guid, ContainerSetDataPacket* packet) @@ -797,7 +805,9 @@ void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& guid, ContainerS if (pContainerMenu->m_containerId != packet->m_containerId) return; - pContainerMenu->setData(packet->m_slot, packet->m_value); + pContainerMenu->m_bBroadcastChanges = false; + pContainerMenu->setData(packet->m_id, packet->m_value); + pContainerMenu->m_bBroadcastChanges = true; } void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& guid, ContainerSetContentPacket* packet) @@ -818,7 +828,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/options/Options.cpp b/source/client/options/Options.cpp index 726edd3e2..6c454aae0 100644 --- a/source/client/options/Options.cpp +++ b/source/client/options/Options.cpp @@ -45,7 +45,7 @@ void Options::_initDefaultValues() #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 @@ -99,7 +99,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); @@ -128,10 +128,12 @@ Options::Options(Minecraft* mc, const std::string& folderPath) : add(m_playerName); 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"; @@ -672,6 +674,9 @@ void Options::initResourceDependentOptions() if (!Screen::isMenuPanoramaAvailable()) m_menuPanorama.set(false); + + if (!m_pMinecraft->platform()->isVSyncSwitchable()) + m_vSync.set(true); } const std::string& OptionEntry::getDisplayName() const @@ -757,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(); @@ -795,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()) diff --git a/source/client/options/Options.hpp b/source/client/options/Options.hpp index abeaa3590..9f99e457f 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: @@ -335,6 +351,7 @@ class Options { public: struct KeyBind; + private: static bool _hasResourcePack(const ResourcePack& pack, ResourcePackStack& packs); static void _tryAddResourcePack(const std::string& name, ResourcePackStack& packs); @@ -364,6 +381,7 @@ class Options void _initDefaultValues(); void _load(); AsyncTask _saveAsync(); + public: Options(Minecraft*, const std::string& folderPath = ""); @@ -425,7 +443,7 @@ class Options GraphicsOption m_fancyGrass; GraphicsOption m_biomeColors; BoolOption m_splitControls; - BoolOption m_bUseController; + ControllerOption m_bUseController; BoolOption m_dynamicHand; BoolOption m_menuPanorama; StringOption m_lang; @@ -433,6 +451,7 @@ class Options LogoTypeOption m_logoType; HUDSizeOption m_hudSize; BoolOption m_classicCrafting; + VsyncOption m_vSync; ResourcePackStack m_resourcePacks; }; @@ -479,6 +498,7 @@ class Options OPTION(m_viewBobbing); \ OPTION(m_anaglyphs); \ OPTION(m_blockOutlines); \ + OPTION(m_vSync); idxVSync = currentIndex; \ OPTION(m_fancyGrass); \ OPTION(m_biomeColors); \ OPTION(m_dynamicHand); \ diff --git a/source/client/player/LocalPlayer.cpp b/source/client/player/LocalPlayer.cpp index 9016b192c..2915304fb 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; @@ -22,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; @@ -42,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; @@ -59,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); @@ -132,18 +138,24 @@ void LocalPlayer::swing() void LocalPlayer::startCrafting(const TilePos& pos) { m_pMinecraft->getScreenChooser()->pushCraftingScreen(this, pos); + + Player::startCrafting(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)); -}*/ + 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() @@ -156,11 +168,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() @@ -247,6 +263,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 @@ -329,9 +348,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 1eecb1288..c5dbdacff 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; @@ -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/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.cpp b/source/client/renderer/Chunk.cpp index 86ece3261..73d7dfaaa 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_tileEntities.begin(), m_tileEntities.end()); + m_tileEntities.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_tileEntities.push_back(et); + */ + } + Tile* pTile = Tile::tiles[tile]; if (layer == pTile->getRenderLayer()) @@ -201,11 +214,47 @@ 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; + + 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 (TileEntityVector::iterator it = toAdd.begin(); it != toAdd.end(); ++it) + { + m_globalTileEntities.push_back(*it); + } + + // Remove + for (TileEntityVector::iterator it = toRemove.begin(); it != toRemove.end(); ++it) + { + TileEntityVector::iterator f = + std::find(m_globalTileEntities.begin(), + m_globalTileEntities.end(), + *it); + + if (f != m_globalTileEntities.end()) + m_globalTileEntities.erase(f); + } + field_54 = LevelChunk::touchedSky; m_bCompiled = true; } -Chunk::Chunk(Level* level, const TilePos& pos, int size, int lists) +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 5f575c0d8..58d6d4793 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& tileEntities, 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_globalTileEntities; + std::vector m_tileEntities; TilePos m_pos; TilePos m_posS; bool m_empty[Tile::RENDER_LAYERS_COUNT]; @@ -56,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 14450fe84..e6f3634a6 100644 --- a/source/client/renderer/GameRenderer.cpp +++ b/source/client/renderer/GameRenderer.cpp @@ -647,7 +647,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/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/client/renderer/LevelRenderer.cpp b/source/client/renderer/LevelRenderer.cpp index d85278176..047c061a9 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) @@ -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(); @@ -1137,7 +1141,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 +1152,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) { @@ -1163,7 +1167,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]; } @@ -1421,6 +1426,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)); @@ -1578,12 +1588,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; @@ -1599,6 +1609,16 @@ void LevelRenderer::renderEntities(Vec3 pos, Culler* culler, float f) EntityRenderDispatcher::getInstance()->render(*entity, f); } } + + /* + // @TODO: TileEntityRenderDispatcher + for (TileEntityVector::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..63520dc24 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; + TileEntityVector m_renderableTileEntities; }; diff --git a/source/client/renderer/LogoRenderer.cpp b/source/client/renderer/LogoRenderer.cpp index 9a45e8a66..f6124f1fe 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/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..7d9446f7b 100644 --- a/source/client/renderer/Textures.cpp +++ b/source/client/renderer/Textures.cpp @@ -165,7 +165,10 @@ 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); 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); diff --git a/source/client/renderer/TileRenderer.cpp b/source/client/renderer/TileRenderer.cpp index 309961f04..77fe6d352 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); @@ -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; } @@ -1216,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; @@ -1297,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; @@ -1370,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) @@ -1399,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); @@ -1452,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); @@ -2690,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 @@ -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; @@ -2924,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/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/MobRenderer.cpp b/source/client/renderer/entity/MobRenderer.cpp index 8e589c112..5039db894 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/client/sound/SoundData.cpp b/source/client/sound/SoundData.cpp index e3351f418..9b325c3a9 100644 --- a/source/client/sound/SoundData.cpp +++ b/source/client/sound/SoundData.cpp @@ -83,11 +83,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 86fa1d0e4..7f068035e 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/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/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/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/common/Random.cpp b/source/common/Random.cpp index 78f7ffe04..342dc6d7b 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/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/Packet.hpp b/source/network/Packet.hpp index bf95f6d98..5d7a295d9 100644 --- a/source/network/Packet.hpp +++ b/source/network/Packet.hpp @@ -20,6 +20,7 @@ //#define NETWORK_PROTOCOL_VERSION 5 // 0.3.2 #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; 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/AddMobPacket.hpp b/source/network/packets/AddMobPacket.hpp index 85c2a7dc4..391ebdd41 100644 --- a/source/network/packets/AddMobPacket.hpp +++ b/source/network/packets/AddMobPacket.hpp @@ -25,6 +25,7 @@ class AddMobPacket : public Packet int32_t m_entityTypeId; Vec3 m_pos; Vec2 m_rot; + private: SynchedEntityData m_entityData; SynchedEntityData::ItemsArray m_unpack; 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/network/packets/ContainerSetDataPacket.cpp b/source/network/packets/ContainerSetDataPacket.cpp index 976a413c3..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 slot, int16_t value) +ContainerSetDataPacket::ContainerSetDataPacket(int8_t containerId, int16_t id, int16_t value) : m_containerId(containerId) - , m_slot(slot) + , 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_slot); + bs.Write(m_id); bs.Write(m_value); } void ContainerSetDataPacket::read(RakNet::BitStream& bs) { bs.Read(m_containerId); - bs.Read(m_slot); + bs.Read(m_id); bs.Read(m_value); } diff --git a/source/network/packets/ContainerSetDataPacket.hpp b/source/network/packets/ContainerSetDataPacket.hpp index e504c7cdd..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_slot = 0; + m_id = 0; m_value = 0; } - ContainerSetDataPacket(int8_t containerId, int16_t slot, 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_slot; + int16_t m_id; 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/network/packets/SendInventoryPacket.cpp b/source/network/packets/SendInventoryPacket.cpp index 2e1888ae4..376255384 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 : EXTRA_NONE) { } @@ -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,14 @@ 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); - // 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_count = Mth::Min(m_count, 512); + 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..df53b6ac8 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 = EXTRA_NONE; } - 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/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/server/ServerPlayer.cpp b/source/server/ServerPlayer.cpp index 873c837f3..c31d4442a 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" @@ -9,8 +10,11 @@ #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" +#include "world/inventory/Slot.hpp" ServerPlayer::ServerPlayer(Level* pLevel, GameType playerGameType) : Player(pLevel, playerGameType) @@ -21,6 +25,11 @@ ServerPlayer::ServerPlayer(Level* pLevel, GameType playerGameType) m_pInventoryMenu->addSlotListener(this); } +ServerPlayer::~ServerPlayer() +{ + doCloseContainer(); +} + void ServerPlayer::_nextContainerCounter() { m_containerId++; @@ -33,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)); @@ -53,12 +65,14 @@ void ServerPlayer::startCrafting(const TilePos& pos) void ServerPlayer::openContainer(Container* container) { + //LOG_I("Client is opening a container"); + _nextContainerCounter(); #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() ) ); @@ -69,6 +83,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 @@ -76,6 +92,24 @@ void ServerPlayer::closeContainer() doCloseContainer(); } +void ServerPlayer::openFurnace(FurnaceTileEntity* furnace) +{ + //LOG_I("Client is opening a 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)); @@ -92,12 +126,19 @@ void ServerPlayer::refreshContainer(ContainerMenu* menu, const std::vector= 5 if (!isResultSlot) { - m_pLevel->m_pRakNetInstance->send(new ContainerSetSlotPacket(menu->m_containerId, index, item)); +#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) + return; +#endif + + m_pLevel->m_pRakNetInstance->send(new ContainerSetSlotPacket(menu->m_containerId, slotId, item)); } #endif } @@ -113,7 +154,10 @@ void ServerPlayer::doCloseContainer() { if (m_pContainerMenu) m_pContainerMenu->removed(this); - setContainerMenu(nullptr); // m_pInventoryMenu on Java, nullptr on Pocket + else + LOG_W("Container is missing @ doCloseContainer!"); + + setContainerMenu(m_pInventoryMenu); // m_pInventoryMenu on Java, nullptr on Pocket } void ServerPlayer::setContainerMenu(ContainerMenu* menu) @@ -121,12 +165,17 @@ 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); + 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 ddc2b7d53..dc385be2b 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(); @@ -14,10 +15,11 @@ 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; - void slotChanged(ContainerMenu* menu, int index, ItemStack& item, bool isResultSlot) override; + void slotChanged(ContainerMenu* menu, Container::SlotID slotId, 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 d031d9d6d..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 @@ -218,9 +237,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)); @@ -396,7 +415,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); } @@ -480,6 +504,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"); @@ -494,6 +535,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; @@ -595,7 +643,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(); } @@ -647,15 +695,26 @@ 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) { case Container::FURNACE: - pContainerMenu->setItem(packet->m_slot, packet->m_item); + case Container::CONTAINER: + pContainerMenu->setItem(packet->m_slotId, packet->m_item); break; default: break; diff --git a/source/world/CompoundContainer.cpp b/source/world/CompoundContainer.cpp deleted file mode 100644 index 80e339b90..000000000 --- a/source/world/CompoundContainer.cpp +++ /dev/null @@ -1,57 +0,0 @@ -#include "CompoundContainer.hpp" - -CompoundContainer::CompoundContainer(const std::string& name, Container* c1, Container* c2) : - m_name(name), m_pLeftContainer(c1), m_pRightContainer(c2) -{ -} - -uint16_t CompoundContainer::getContainerSize() const -{ - return uint16_t(m_pLeftContainer->getContainerSize() + m_pRightContainer->getContainerSize()); -} - -std::string CompoundContainer::getName() const -{ - return m_name; -} - -ItemStack& CompoundContainer::getItem(int index) -{ - if (index >= m_pLeftContainer->getContainerSize()) - return m_pRightContainer->getItem(index - m_pLeftContainer->getContainerSize()); - else - return m_pLeftContainer->getItem(index); -} - -ItemStack CompoundContainer::removeItem(int index, int count) -{ - if (index >= m_pLeftContainer->getContainerSize()) - return m_pRightContainer->removeItem(index - m_pLeftContainer->getContainerSize(), count); - else - return m_pLeftContainer->removeItem(index, count); -} - -void CompoundContainer::setItem(int index, const ItemStack& item) -{ - if (index >= m_pLeftContainer->getContainerSize()) - m_pRightContainer->setItem(index - m_pLeftContainer->getContainerSize(), item); - else - m_pLeftContainer->setItem(index, item); -} - -int CompoundContainer::getMaxStackSize() -{ - return m_pLeftContainer->getMaxStackSize(); -} - -void CompoundContainer::setChanged() -{ - m_pLeftContainer->setChanged(); - m_pRightContainer->setChanged(); -} - -bool CompoundContainer::stillValid(Player* player) const -{ - return m_pLeftContainer->stillValid(player) && m_pRightContainer->stillValid(player); -} - diff --git a/source/world/CompoundContainer.hpp b/source/world/CompoundContainer.hpp deleted file mode 100644 index 5a4884591..000000000 --- a/source/world/CompoundContainer.hpp +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once - -#include -#include "Container.hpp" - -class CompoundContainer : public Container -{ -private: - std::string m_name; - Container* m_pLeftContainer; - Container* m_pRightContainer; - -public: - CompoundContainer(const std::string& name, Container* c1, Container* c2); - - uint16_t getContainerSize() const override; - - std::string getName() const override; - - ItemStack& getItem(int index) override; - - ItemStack removeItem(int index, int count) override; - - void setItem(int index, const ItemStack& item) override; - - int getMaxStackSize() override; - - void setChanged() override; - - bool stillValid(Player* player) const override; -}; 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/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/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/Entity.cpp b/source/world/entity/Entity.cpp index d93b8e395..dfdd6fa73 100644 --- a/source/world/entity/Entity.cpp +++ b/source/world/entity/Entity.cpp @@ -25,6 +25,9 @@ void Entity::_init() m_bInAChunk = false; m_viewScale = 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; @@ -481,6 +484,14 @@ void Entity::tick() void Entity::baseTick() { //@TODO: untangle the gotos + if (const Entity* riding = getRiding()) + { + // if you were riding an entity and they no longer exist, stop + if ((!riding && m_ridingId > 0) || riding->m_bRemoved) + { + setRiding(nullptr); + } + } m_walkDistO = m_walkDist; m_oPos = m_pos; @@ -757,6 +768,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); @@ -959,6 +973,46 @@ AABB* Entity::getCollideAgainstBox(Entity* ent) const return nullptr; } +void Entity::rideTick() +{ + Entity* riding = getRiding(); + if (!riding || riding->m_bRemoved) + { + setRiding(nullptr); + return; + } + + // 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; + + constexpr 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() { } @@ -968,6 +1022,99 @@ 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->setRider(nullptr); + } + + // Let yourself know you aren't riding anything + setRiding(nullptr); + + return; + } + + // Dismount if the same entity is fed in + if (oldRiding && oldRiding == newRiding) + { + oldRiding->setRider(nullptr); + + setRiding(nullptr); + + moveTo(oldRiding->m_pos); + setRot(oldRiding->m_rot); + return; + } + + // if (this.riding != null) this.riding.rider = null; + if (oldRiding) + { + 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()) + { + setRiding(nullptr); + newRidesOldRider->setRider(nullptr); + } + + setRiding(newRiding); + newRiding->setRider(this); +} + +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::setRider(Entity* rider) +{ + m_riderId = (rider) ? rider->m_EntityID : 0; +} + +void Entity::setRiding(Entity* riding) +{ + m_ridingId = (riding) ? riding->m_EntityID : 0; + setSharedFlag(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 ad80a24b8..df739bd14 100644 --- a/source/world/entity/Entity.hpp +++ b/source/world/entity/Entity.hpp @@ -119,7 +119,7 @@ class Entity Entity(Level*); virtual ~Entity(); -protected: +public: virtual bool getSharedFlag(SharedFlag flag) const; virtual void setSharedFlag(SharedFlag flag, bool value); virtual void playStepSound(const TilePos& pos, TileID tileId); @@ -175,7 +175,7 @@ class Entity virtual bool isPushable() const { return false; } virtual bool isShootable() const { return false; } virtual bool isOnFire() const { return m_fireTicks > 0 || getSharedFlag(FLAG_ON_FIRE); } - virtual bool isRiding() const { return /*m_pRiding != nullptr ||*/ getSharedFlag(FLAG_RIDING); } + virtual bool isRiding() const { return getRiding() || getSharedFlag(FLAG_RIDING); } virtual bool isSneaking() const { return getSharedFlag(FLAG_SNEAKING); } virtual void setSneaking(bool value) { setSharedFlag(FLAG_SNEAKING, value); } virtual bool isAlive() const { return m_bRemoved; } @@ -206,9 +206,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* ent); + void setRider(Entity* ent); void load(const CompoundTag& tag); bool save(CompoundTag& tag) const; void saveWithoutId(CompoundTag& tag) const; @@ -233,6 +242,10 @@ class Entity (m_pos.z - pos.z) * (m_pos.z - pos.z); } +private: + Entity::ID m_ridingId; + Entity::ID m_riderId; + protected: SynchedEntityData m_entityData; bool m_bMakeStepSound; @@ -247,12 +260,14 @@ class Entity float m_viewScale; //TileSource* m_pTileSource; DimensionId m_dimensionId; + 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 e754a2fa6..e216f2d9f 100644 --- a/source/world/entity/Mob.cpp +++ b/source/world/entity/Mob.cpp @@ -54,7 +54,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; @@ -760,7 +760,7 @@ void Mob::aiStep() { Entity* pEnt = *it; if (pEnt->isPushable()) - pEnt->push(this); + pEnt->push(this); } } @@ -788,6 +788,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(); @@ -834,7 +841,7 @@ void Mob::updateAi() Entity* nearestPlayer = m_pLevel->getNearestPlayer(*this, 8.0f); if (nearestPlayer) { - m_pEntLookedAt = nearestPlayer; + m_entLookedAtId = nearestPlayer->m_EntityID; m_lookTime = m_random.nextInt(20) + 10; } @@ -845,17 +852,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 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_pEntLookedAt = nullptr; + m_entLookedAtId = 0; } else { diff --git a/source/world/entity/Mob.hpp b/source/world/entity/Mob.hpp index ef91d1366..c697a3010 100644 --- a/source/world/entity/Mob.hpp +++ b/source/world/entity/Mob.hpp @@ -61,9 +61,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; @@ -141,7 +141,7 @@ class Mob : public Entity Vec3 m_lPos; Vec2 m_lRot; int m_lastHurt; - Entity* m_pEntLookedAt; + Entity::ID m_entLookedAtId; bool m_bSwinging; int m_swingTime; 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/Pig.cpp b/source/world/entity/Pig.cpp index 153163f49..9a0caa988 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) { @@ -15,12 +17,40 @@ Pig::Pig(Level* pLevel) : Animal(pLevel) setSize(0.9f, 0.9f); // some dataitem stuff } + 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) +{ + 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) diff --git a/source/world/entity/Pig.hpp b/source/world/entity/Pig.hpp index c617da1a6..d13fe87f7 100644 --- a/source/world/entity/Pig.hpp +++ b/source/world/entity/Pig.hpp @@ -20,7 +20,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 cbeef3ab1..97f052c4f 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(); @@ -169,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 @@ -262,7 +271,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; @@ -320,7 +329,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")))); @@ -385,7 +395,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(); } @@ -492,7 +503,9 @@ void Player::respawn() void Player::rideTick() { - + Mob::rideTick(); + m_oBob = m_bob; + m_bob = 0.0f; } void Player::setDefaultHeadHeight() @@ -512,16 +525,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) { @@ -559,12 +567,12 @@ void Player::drop(const ItemStack& item, bool randomly) void Player::startCrafting(const TilePos& pos) { - + _handleOpenedContainerMenu(); } void Player::startStonecutting(const TilePos& pos) { - + _handleOpenedContainerMenu(); } void Player::startDestroying() @@ -577,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); @@ -592,9 +614,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 c75ff8831..06b3f7b87 100644 --- a/source/world/entity/Player.hpp +++ b/source/world/entity/Player.hpp @@ -18,6 +18,7 @@ #define C_PLAYER_FLAG_USING_ITEM (4) class Inventory; // in case we're included from Inventory.hpp +class FurnaceTileEntity; class Player : public Mob { @@ -40,11 +41,12 @@ class Player : public Mob protected: virtual void reallyDrop(ItemEntity* pEnt); + virtual void _handleOpenedContainerMenu(); 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; } @@ -66,15 +68,15 @@ 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); virtual void startDestroying(); virtual void stopDestroying(); - //virtual void openFurnace(FurnaceTileEntity* tileEntity); - virtual void openContainer(Container* container) {} - virtual void closeContainer() {} + virtual void openFurnace(FurnaceTileEntity* tileEntity); + virtual void openContainer(Container* container); + virtual void closeContainer(); //virtual void openTrap(DispenserTileEntity* tileEntity); //virtual void openTextEdit(SignTileEntity* tileEntity); virtual bool isLocalPlayer() const { return false; } @@ -95,7 +97,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 4ecc268ee..9b81aedbf 100644 --- a/source/world/entity/Spider.hpp +++ b/source/world/entity/Spider.hpp @@ -12,7 +12,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/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/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); } 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.cpp b/source/world/inventory/ChestMenu.cpp index 95a78894f..a0b061981 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) @@ -30,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 ea787bea3..8206e24ba 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 @@ -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 new file mode 100644 index 000000000..58efc6e30 --- /dev/null +++ b/source/world/inventory/CompoundContainer.cpp @@ -0,0 +1,107 @@ +#include "CompoundContainer.hpp" + +class CompoundContainer::ChildListener : public ContainerContentChangeListener +{ +public: + ChildListener(CompoundContainer* owner, int offset) + : m_pOwner(owner), m_offset(offset) + { + } + + void containerContentChanged(Container* container, StackID stackId) override + { + if (m_pOwner) + m_pOwner->setContainerChanged(stackId + 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_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 +{ + return uint16_t(m_pLeftContainer->getContainerSize() + m_pRightContainer->getContainerSize()); +} + +std::string CompoundContainer::getName() const +{ + return m_name; +} + +ItemStack& CompoundContainer::getItem(StackID index) +{ + if (index >= m_pLeftContainer->getContainerSize()) + return m_pRightContainer->getItem(index - m_pLeftContainer->getContainerSize()); + else + return m_pLeftContainer->getItem(index); +} + +ItemStack CompoundContainer::removeItem(StackID index, int count) +{ + if (index >= m_pLeftContainer->getContainerSize()) + return m_pRightContainer->removeItem(index - m_pLeftContainer->getContainerSize(), count); + else + return m_pLeftContainer->removeItem(index, count); +} + +void CompoundContainer::setItem(StackID index, const ItemStack& item) +{ + if (index >= m_pLeftContainer->getContainerSize()) + m_pRightContainer->setItem(index - m_pLeftContainer->getContainerSize(), item); + else + m_pLeftContainer->setItem(index, item); +} + +int CompoundContainer::getMaxStackSize() +{ + return m_pLeftContainer->getMaxStackSize(); +} + +void CompoundContainer::setContainerChanged(StackID stackId) +{ + for (ContentChangeListeners::iterator it = m_contentChangeListeners.begin(); it != m_contentChangeListeners.end(); ++it) + { + ContainerContentChangeListener* listener = *it; + listener->containerContentChanged(this, stackId); + } +} + +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 new file mode 100644 index 000000000..556fde4d2 --- /dev/null +++ b/source/world/inventory/CompoundContainer.hpp @@ -0,0 +1,37 @@ +#pragma once + +#include +#include +#include "Container.hpp" +#include "ContainerContentChangeListener.hpp" + +class CompoundContainer : public Container +{ +private: + class ChildListener; + +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; +}; diff --git a/source/world/inventory/Container.hpp b/source/world/inventory/Container.hpp new file mode 100644 index 000000000..77ac4480f --- /dev/null +++ b/source/world/inventory/Container.hpp @@ -0,0 +1,60 @@ +#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; + typedef int16_t SlotID; + typedef uint16_t StackID; + +public: + enum Type + { + CONTAINER, + CRAFTING, + FURNACE, + DISPENSER + }; + +public: + virtual ~Container() {} + +public: + virtual Size getContainerSize() const = 0; + virtual ItemStack& getItem(StackID stackId) = 0; + virtual ItemStack* tryGetItem(StackID stackId) + { + if (stackId >= 0 && stackId < getContainerSize()) + return &getItem(stackId); + else + return nullptr; + } + 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(StackID stackId) = 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) {} +}; 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..80266d0fd --- /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(Container* container, Container::StackID stackId) = 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 69% rename from source/world/ContainerListener.hpp rename to source/world/inventory/ContainerListener.hpp index a159ae5db..a1b4b34cc 100644 --- a/source/world/ContainerListener.hpp +++ b/source/world/inventory/ContainerListener.hpp @@ -1,9 +1,11 @@ #pragma once #include +#include "Container.hpp" class ContainerMenu; class ItemStack; +class Slot; class ContainerListener { @@ -12,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, ItemStack& item, bool isResultSlot) {} + virtual void slotChanged(ContainerMenu* menu, Container::SlotID slotId, 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 bf97f4140..b2c083130 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,20 +19,41 @@ 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) { - slot->m_index = m_slots.size(); + slot->m_id = 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); // @PARITY: Not done on PE /*std::vector snapshot = copyItems(); @@ -41,21 +63,26 @@ void ContainerMenu::addSlotListener(ContainerListener* listener) 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(Container::SlotID slotId) +{ + ItemStack& current = m_slots[slotId]->getItem(); + if (m_lastSlots[slotId] != current) + { + m_lastSlots[slotId] = ItemStack(current); + for (ContainerListeners::iterator it = m_listeners.begin(); it != m_listeners.end(); ++it) + (*it)->slotChanged(this, slotId, m_slots[slotId], m_lastSlots[slotId], 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,48 +101,72 @@ void ContainerMenu::slotsChanged(Container*) broadcastChanges(); } -std::vector ContainerMenu::copyItems() +void ContainerMenu::containerContentChanged(Container* container, Container::StackID stackId) +{ + // 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) + { + Slot* slot = m_slots[i]; + if (slot->m_pContainer == container && slot->m_stackId == stackId) + { + 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) - content.push_back((*it)->getItem()); + { + 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); + } 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())) { @@ -135,22 +186,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) { @@ -161,14 +212,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; @@ -177,7 +228,7 @@ ItemStack ContainerMenu::clicked(int slotIndex, MouseButtonType mouseButton, boo Inventory* inv = player->m_pInventory; - if (slotIndex == -999) + if (slotId == -999) { if (!inv->getCarried().isEmpty()) { @@ -201,24 +252,22 @@ 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; - slot->setChanged(); - ItemStack& slotItem = slot->getItem(); if (!slotItem.isEmpty()) @@ -259,6 +308,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()) @@ -279,6 +329,7 @@ ItemStack ContainerMenu::clicked(int slotIndex, MouseButtonType mouseButton, boo inv->setCarried(ItemStack::EMPTY); slotItem.m_count += count; + slot->setChanged(); break; } case MOUSE_BUTTON_RIGHT: @@ -295,6 +346,7 @@ ItemStack ContainerMenu::clicked(int slotIndex, MouseButtonType mouseButton, boo inv->setCarried(ItemStack::EMPTY); slotItem.m_count += count; + slot->setChanged(); break; } default: @@ -325,41 +377,54 @@ 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); + m_slots[slotId]->set(item); + if (!m_bBroadcastChanges && slotId >= 0 && slotId < (int)m_lastSlots.size()) + { + m_lastSlots[slotId] = ItemStack(m_slots[slotId]->getItem()); + } } 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]); + if (!m_bBroadcastChanges) + { + m_lastSlots[i] = ItemStack(m_slots[i]->getItem()); + } } } -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 { - 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); } diff --git a/source/world/inventory/ContainerMenu.hpp b/source/world/inventory/ContainerMenu.hpp index f7b299806..ca1a115fc 100644 --- a/source/world/inventory/ContainerMenu.hpp +++ b/source/world/inventory/ContainerMenu.hpp @@ -3,37 +3,45 @@ #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(Container::SlotID slotId); virtual void broadcastChanges(); virtual void removed(Player* player); virtual void slotsChanged(Container* container); // Called getItems in PE and Java - std::vector copyItems(); - 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); - - void setItem(int slotIndex, ItemStack item); + std::vector cloneItems(); + 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(Container::SlotID slotId, ItemStack item); void setAll(const std::vector& items); virtual void setData(int id, int value); @@ -50,14 +58,18 @@ class ContainerMenu //Unused virtual bool isPauseScreen() const { return false; } +public: + void containerContentChanged(Container* container, Container::StackID stackId) 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..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::setChanged() +void CraftingContainer::setContainerChanged(StackID stackId) { } diff --git a/source/world/inventory/CraftingContainer.hpp b/source/world/inventory/CraftingContainer.hpp index 5fecc7d91..ea8c16a4e 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; @@ -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 setChanged() 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 aeb9d016b..cdf3ed95f 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; } @@ -67,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 new file mode 100644 index 000000000..2b5fd2e27 --- /dev/null +++ b/source/world/inventory/FurnaceMenu.cpp @@ -0,0 +1,107 @@ +#include "FurnaceMenu.hpp" +#include "Slot.hpp" +#include "FurnaceResultSlot.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) +{ + 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 (ContainerListeners::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) +{ + switch (index) + { + case 0: + m_furnace->m_tickCount = value; + break; + case 1: + m_furnace->m_litTime = value; + break; + case 2: + m_furnace->m_litDuration = value; + break; + default: break; + } +} + +ItemStack FurnaceMenu::quickMoveStack(Container::SlotID slotId) +{ + ItemStack item; + Slot* slot = getSlot(slotId); + if (slot && slot->hasItem()) + { + ItemStack& slotItem = slot->getItem(); + item = slotItem.copy(); + if (slotId == 2) + moveItemStackTo(slotItem, 3, 39, true); + else if (slotId >= 3 && slotId < 30) + moveItemStackTo(slotItem, 30, 39, false); + else if (slotId >= 30 && slotId < 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..2a1dd1ba0 --- /dev/null +++ b/source/world/inventory/FurnaceMenu.hpp @@ -0,0 +1,24 @@ +#pragma once + +#include "ContainerMenu.hpp" +#include "world/entity/Player.hpp" +#include "world/tile/entity/FurnaceTileEntity.hpp" + +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(Container::SlotID slotId) 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..912b17000 --- /dev/null +++ b/source/world/inventory/FurnaceResultSlot.hpp @@ -0,0 +1,16 @@ +#pragma once + +#include "Slot.hpp" +#include "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/InventoryMenu.cpp b/source/world/inventory/InventoryMenu.cpp index 6c5d2a76f..754b391c7 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; } @@ -70,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 3f6763d53..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::setChanged() +void ResultContainer::setContainerChanged(StackID stackId) { } diff --git a/source/world/inventory/ResultContainer.hpp b/source/world/inventory/ResultContainer.hpp index a3d95dc65..9ff3b9cab 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; @@ -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 setChanged() override; + void setContainerChanged(StackID stackId) 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 6799cd433..2b5c0f07b 100644 --- a/source/world/inventory/SimpleContainer.cpp +++ b/source/world/inventory/SimpleContainer.cpp @@ -1,22 +1,24 @@ #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(Size size, const std::string& name) + : m_items(size) + , m_name(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(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()) { @@ -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,20 +36,20 @@ ItemStack SimpleContainer::removeItem(int index, int count) if (!m_items[index].m_count) m_items[index] = ItemStack::EMPTY; - setChanged(); + setContainerChanged(index); return result; } } 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()) 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(StackID stackId) { + for (ContentChangeListeners::iterator it = m_contentChangeListeners.begin(); it != m_contentChangeListeners.end(); it++) + { + ContainerContentChangeListener* pListener = *it; + pListener->containerContentChanged(this, stackId); + } } bool SimpleContainer::stillValid(Player* player) const @@ -64,7 +71,32 @@ bool SimpleContainer::stillValid(Player* player) const return true; } -void SimpleContainer::load(CompoundTag& tag) +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(); const ListTag* list = tag.getList("Items"); @@ -78,14 +110,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; @@ -102,8 +134,3 @@ void SimpleContainer::save(CompoundTag& tag) 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 2e676e564..cf11cb4ef 100644 --- a/source/world/inventory/SimpleContainer.hpp +++ b/source/world/inventory/SimpleContainer.hpp @@ -1,36 +1,36 @@ #pragma once -#include "world/Container.hpp" -#include "nbt/CompoundTag.hpp" #include +#include "Container.hpp" +#include "nbt/CompoundTag.hpp" + 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; + SimpleContainer(Size size, const std::string& name); - virtual std::string getName() const override; - - virtual void setChanged() override; - - virtual bool stillValid(Player* player) const override; +public: + Size getContainerSize() const 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(StackID stackId) 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; - virtual void load(CompoundTag& tag); - virtual void save(CompoundTag& tag); - -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.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 12a033823..55ef1ae39 100644 --- a/source/world/inventory/Slot.hpp +++ b/source/world/inventory/Slot.hpp @@ -1,6 +1,8 @@ #pragma once -#include "world/Container.hpp" +#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->setChanged(); } + 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; - int m_index; + 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/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/CoalItem.hpp b/source/world/item/CoalItem.hpp index b79c7d360..ce49d6b0c 100644 --- a/source/world/item/CoalItem.hpp +++ b/source/world/item/CoalItem.hpp @@ -9,4 +9,5 @@ class CoalItem : public Item public: std::string getDescriptionId(ItemStack* inst) const override; + }; diff --git a/source/world/item/Inventory.cpp b/source/world/item/Inventory.cpp index 4e64e66fd..68309426d 100644 --- a/source/world/item/Inventory.cpp +++ b/source/world/item/Inventory.cpp @@ -4,13 +4,14 @@ #include "common/Logger.hpp" #include "nbt/CompoundTag.hpp" #include "network/Packet.hpp" +#include "world/inventory/ContainerContentChangeListener.hpp" #include "Item.hpp" 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() @@ -120,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() @@ -246,34 +247,34 @@ 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; } } - else - return ItemStack::EMPTY; + + return ItemStack::EMPTY; } 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; @@ -299,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 (StackID i = 0; i < m_items.size(); i++) { ItemStack& item = m_items[i]; @@ -307,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); } } } @@ -368,14 +369,15 @@ bool Inventory::hasUnlimitedResource(const ItemStack& item) const return true; } -ItemStack& Inventory::getItem(int slotNo) +ItemStack& Inventory::getItem(StackID stackId) { - assert(slotNo >= 0 && slotNo < getContainerSize()); + // stackId < getContainerSize() trips when the RemotePlayer dies + 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) @@ -385,29 +387,29 @@ 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(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; } } -void Inventory::setSelectedItem(ItemStack item) +void Inventory::setSelectedItem(const ItemStack& item) { - setItem(m_selectedSlot, item); + setItem(m_selectedStackId, item); } void Inventory::setCarried(const ItemStack& carried) @@ -425,24 +427,24 @@ 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)) selectSlot(i); else - swapItems(i, m_selectedSlot); + swapItems(i, m_selectedStackId); return; } - if (m_pPlayer->isCreative()) + 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); } } @@ -459,23 +461,23 @@ 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); } } -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(StackID stackId) { - if (slotNo < 0 || slotNo >= C_MAX_HOTBAR_ITEMS) + if (stackId < 0 || stackId >= C_MAX_HOTBAR_ITEMS) return; - m_selectedSlot = slotNo; + m_selectedStackId = stackId; } int Inventory::getAttackDamage(Entity* pEnt) @@ -627,3 +629,22 @@ GameType Inventory::_getGameMode() const { return m_pPlayer->getPlayerGameType(); } + +void Inventory::setContainerChanged(StackID stackId) +{ + for (ContentChangeListeners::iterator it = m_contentChangeListeners.begin(); it != m_contentChangeListeners.end(); ++it) + { + ContainerContentChangeListener* listener = *it; + listener->containerContentChanged(this, stackId); + } +} + +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 568a1e54a..29651111d 100644 --- a/source/world/item/Inventory.hpp +++ b/source/world/item/Inventory.hpp @@ -1,8 +1,9 @@ #pragma once +#include #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" @@ -19,13 +20,15 @@ class Player; // in case we're included from Player.hpp class Inventory : public Container { +private: + typedef std::set ContentChangeListeners; public: Inventory(Player*); virtual ~Inventory(); void prepareCreativeInventory(); void prepareSurvivalInventory(); - uint16_t getContainerSize() const override; + Size getContainerSize() const override; void clear(); void replace(const std::vector& items); @@ -38,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 setSelectedItem(ItemStack item); - ItemStack removeItem(int index, int count) override; + void setItem(StackID stackId, const ItemStack& item) override; + void setSelectedItem(const ItemStack& item); + ItemStack removeItem(StackID stackId, int count) override; bool removeResource(int id); void setCarried(const ItemStack& item); @@ -53,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(StackID stackId); int getAttackDamage(Entity*); @@ -69,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(); } @@ -80,9 +83,11 @@ class Inventory : public Container return "Inventory"; } - void setChanged() override { } + void setContainerChanged(StackID stackId) override; + void addContentChangeListener(ContainerContentChangeListener* listener) override; + void removeContentChangeListener(ContainerContentChangeListener* listener) override; - bool stillValid(Player* player) const override { return true; } + bool stillValid(Player* player) const override { return true; } private: GameType _getGameMode() const; @@ -93,11 +98,12 @@ class Inventory : public Container public: Player* m_pPlayer; - SlotID m_selectedSlot; + StackID m_selectedStackId; private: ItemStack m_carried; std::vector m_items; std::vector m_armor; + ContentChangeListeners m_contentChangeListeners; }; 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/item/ItemStack.hpp b/source/world/item/ItemStack.hpp index b984f3b9e..e51394b21 100644 --- a/source/world/item/ItemStack.hpp +++ b/source/world/item/ItemStack.hpp @@ -126,6 +126,7 @@ class ItemStack public: int16_t m_count; int m_popTime; + private: int16_t m_auxValue; CompoundTag* m_userData; 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/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/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 f4181dfec..e3129f1d7 100644 --- a/source/world/level/Level.cpp +++ b/source/world/level/Level.cpp @@ -19,6 +19,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" @@ -39,6 +40,8 @@ Level::Level(LevelStorage* pStor, const std::string& name, const LevelSettings& m_bCalculatingInitialSpawn = false; m_pChunkSource = nullptr; m_pLevelStorage = pStor; + m_tileEntities = TileEntityVector(); + m_bUpdatingTileEntities = false; m_randValue = 42184323; m_addend = 1013904223; m_bUpdateLights = true; @@ -78,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. @@ -259,6 +261,61 @@ 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_tileEntities; +} + +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_tileEntities.push_back(tileEntity); + LevelChunk* pChunk = getChunk(pos); + if (pChunk) + pChunk->setTileEntity(pos, tileEntity); +} + +void Level::removeTileEntity(const TilePos& pos) +{ + TileEntity* tileEntity = getTileEntity(pos); + + if (tileEntity == nullptr) + { + 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; + } + + // During a tile entity update, just mark it for potential removal + if (m_bUpdatingTileEntities) + { + tileEntity->setRemoved(); + return; + } + + Util::remove(m_tileEntities, tileEntity); + + if (LevelChunk* pChunk = getChunk(pos)) + { + pChunk->removeTileEntity(pos); + } +} + void Level::swap(const TilePos& pos1, const TilePos& pos2) { TileID tile1 = getTile(pos1); @@ -301,22 +358,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; } @@ -329,7 +382,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; } @@ -1191,11 +1244,28 @@ 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++) { Entity* ent = *it; + if (Entity* riding = ent->getRiding()) + { + if (riding->m_bRemoved || riding->getRider() != ent) + { + riding->setRider(nullptr); + ent->setRiding(nullptr); + } + else + continue; + } ent->removed(); LevelChunk* chunk = getChunk(ent->m_chunkPos); @@ -1216,6 +1286,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()) @@ -1250,7 +1328,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); @@ -1329,9 +1407,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)); @@ -1640,7 +1718,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 { @@ -1674,6 +1757,23 @@ void Level::tick(Entity* pEnt, bool shouldTick) getChunk(cp)->updateEntity(pEnt); } } + if (shouldTick && pEnt->m_bInAChunk) + { + Entity* rider = pEnt->getRider(); + // someone is riding this entity + if (rider) + { + if (rider->m_bRemoved || rider->getRiding() != pEnt) + { + rider->setRiding(nullptr); + pEnt->setRider(nullptr); + } + else + { + tick(rider); + } + } + } } void Level::tick(Entity* pEnt) @@ -1706,26 +1806,72 @@ 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()) + { + if (riding->m_bRemoved || riding->getRider() != pEnt) + { + riding->setRider(nullptr); + pEnt->setRiding(nullptr); + } + 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; + for (size_t i = 0; i < m_tileEntities.size(); i++) + { + TileEntity* tileEnt = m_tileEntities[i]; + + if (!tileEnt->isRemoved()) + { + tileEnt->tick(); + } + else + { + LevelChunk* ch = getChunk(tileEnt->m_pos); + if (ch) + ch->removeTileEntity(tileEnt->m_pos); + + m_tileEntities.erase(m_tileEntities.begin() + i); + i--; + + delete tileEnt; + } + } + m_bUpdatingTileEntities = false; } HitResult Level::clip(Vec3 v1, Vec3 v2, bool flag) const @@ -1961,11 +2107,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); } } @@ -1982,9 +2141,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 19a14c8c8..f4e871e9a 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,8 @@ class Packet; class MobSpawner; typedef std::vector EntityVector; +typedef std::map EntityMap; +typedef std::vector TileEntityVector; typedef std::vector AABBVector; struct Brightness @@ -72,6 +75,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; } @@ -179,7 +186,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; } @@ -204,6 +211,7 @@ class Level : public LevelSource private: LevelData* m_pLevelData; + bool m_bUpdatingTileEntities; protected: int m_randValue; @@ -214,7 +222,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; @@ -238,5 +246,7 @@ class Level : public LevelSource MobSpawner* m_pMobSpawner; std::map m_entityCountsByCategory; + TileEntityVector m_tileEntities; + 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 3e36167dd..9f7368dca 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; @@ -588,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); } @@ -683,6 +684,78 @@ 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_tileEntities.push_back(tileEntity); +} + +void LevelChunk::setTileEntity(const ChunkTilePos& pos, TileEntity* tileEntity) +{ + TilePos tilePos(m_chunkPos, pos.y); + + 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_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) +{ + 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/levelgen/chunk/RandomLevelSource.cpp b/source/world/level/levelgen/chunk/RandomLevelSource.cpp index f8d128208..98a1d7877 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.hpp b/source/world/level/path/BinaryHeap.hpp index 523b1a6a6..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() @@ -44,7 +44,7 @@ class BinaryHeap void clear() { m_count = 0; } - + int size() const { return m_count; } diff --git a/source/world/level/storage/ExternalFileLevelStorage.cpp b/source/world/level/storage/ExternalFileLevelStorage.cpp index 18b1e297c..31e8fcb35 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(); @@ -383,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)) @@ -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/NoteParticle.cpp b/source/world/particle/NoteParticle.cpp new file mode 100644 index 000000000..aca991fef --- /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_age++; + if (m_age > 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_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); +} diff --git a/source/world/particle/Particle.hpp b/source/world/particle/Particle.hpp index 0f9996e7d..26cb5d6e9 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 @@ -135,3 +136,14 @@ class LavaParticle : public Particle public: float m_oSize; }; + +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/phys/Vec3.hpp b/source/world/phys/Vec3.hpp index 925f027d9..22e7e08dc 100644 --- a/source/world/phys/Vec3.hpp +++ b/source/world/phys/Vec3.hpp @@ -119,6 +119,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..3c6f36e58 --- /dev/null +++ b/source/world/tile/ChestTile.cpp @@ -0,0 +1,228 @@ +#include "ChestTile.hpp" +#include "world/level/Level.hpp" +#include "world/inventory/CompoundContainer.hpp" +#include "world/tile/entity/ChestTileEntity.hpp" + +ChestTile::ChestTile(int id, int texture) : EntityTile(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; + + 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); + + 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(); + + 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; + 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(); + + 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; + 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) + { + EntityTile::onRemove(level, pos); + return; + } + + for (Container::StackID slot = 0; slot < ent->getContainerSize(); ++slot) + { + ItemStack& item = ent->getItem(slot); + 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) + { + 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); + } + } + + EntityTile::onRemove(level, 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; + + if (level->isSolidTile(pos.above())) + return true; + + if (level->getTile(pos.west()) == m_ID && level->isSolidTile(pos.west().above())) + return true; + + if (level->getTile(pos.east()) == m_ID && level->isSolidTile(pos.east().above())) + return true; + + if (level->getTile(pos.north()) == m_ID && level->isSolidTile(pos.north().above())) + return true; + + if (level->getTile(pos.south()) == m_ID && level->isSolidTile(pos.south().above())) + return true; + + TileEntity* tileEnt = level->getTileEntity(pos); + if (!tileEnt) + return false; + + ChestTileEntity* chest = (ChestTileEntity*)tileEnt; + Container* container = dynamic_cast(chest); + for (int rel = Facing::NORTH; rel <= Facing::EAST; rel++) + { + 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 = dynamic_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..e154d454e --- /dev/null +++ b/source/world/tile/ChestTile.hpp @@ -0,0 +1,22 @@ +#pragma once + +#include "EntityTile.hpp" + +class ChestTile : public EntityTile +{ +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/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/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/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 new file mode 100644 index 000000000..e17ea782d --- /dev/null +++ b/source/world/tile/FurnaceTile.cpp @@ -0,0 +1,199 @@ +#include "FurnaceTile.hpp" +#include "world/level/Level.hpp" +#include "entity/FurnaceTileEntity.hpp" + +bool FurnaceTile::keepInventory = false; + +FurnaceTile::FurnaceTile(int id, bool active) : EntityTile(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) +{ + EntityTile::onPlace(level, pos); + RecalculateLookDirection(level, pos); +} + +bool FurnaceTile::use(Level* level, const TilePos& pos, Player* player) +{ + if (player->isSneaking() && !player->getSelectedItem().isEmpty()) + 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.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 3: data = 4; break; + } + + level->setData(pos, data); +} + +void FurnaceTile::onRemove(Level* level, const TilePos& pos) +{ + if (keepInventory) + return; + + TileEntity* tileEnt = level->getTileEntity(pos); + + if (!tileEnt) + { + EntityTile::onRemove(level, pos); + 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); + } + } + + 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; + 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..0a79e01aa --- /dev/null +++ b/source/world/tile/FurnaceTile.hpp @@ -0,0 +1,33 @@ +#pragma once + +#include "EntityTile.hpp" + +class FurnaceTile : public EntityTile +{ +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..d0b7d6427 --- /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) : EntityTile(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().isEmpty()) + 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.0f, 0.0f)); +} diff --git a/source/world/tile/MusicTile.hpp b/source/world/tile/MusicTile.hpp new file mode 100644 index 000000000..7383a0694 --- /dev/null +++ b/source/world/tile/MusicTile.hpp @@ -0,0 +1,17 @@ +#pragma once + +#include "EntityTile.hpp" + +class MusicTile : public EntityTile +{ +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/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; diff --git a/source/world/tile/Tile.cpp b/source/world/tile/Tile.cpp index 861449de6..6cba7591c 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; @@ -786,6 +791,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 +993,10 @@ void Tile::neighborChanged(Level* pLevel, const TilePos& pos, TileID tile) void Tile::onPlace(Level* pLevel, const TilePos& pos) { - } void Tile::onRemove(Level* pLevel, const TilePos& pos) { - } bool Tile::containsX(const Vec3& v) @@ -1279,4 +1307,8 @@ Tile *Tile::web, *Tile::fence, *Tile::craftingTable, - *Tile::crops; + *Tile::crops, + *Tile::musicBlock, + *Tile::furnace, + *Tile::furnaceLit, + *Tile::chest; diff --git a/source/world/tile/Tile.hpp b/source/world/tile/Tile.hpp index f9695c6fc..450588b09 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*); @@ -241,7 +243,11 @@ class Tile * web, * fence, * craftingTable, - * crops; + * crops, + * furnace, + * furnaceLit, + * musicBlock, + * chest; public: int m_TextureFrame; diff --git a/source/world/tile/TopSnowTile.cpp b/source/world/tile/TopSnowTile.cpp index b974b308b..380ab3653 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,17 @@ 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) +{ + constexpr float dispersion = 0.7f; + + 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; + level->addEntity(pItemEntity); + level->setTile(pos, TILE_AIR); +} diff --git a/source/world/tile/TopSnowTile.hpp b/source/world/tile/TopSnowTile.hpp index a1f629a65..8bd2a75bf 100644 --- a/source/world/tile/TopSnowTile.hpp +++ b/source/world/tile/TopSnowTile.hpp @@ -25,6 +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; + void playerDestroy(Level*, Player*, const TilePos& pos, TileData data) override; bool checkCanSurvive(Level*, const TilePos& pos); }; diff --git a/source/world/tile/entity/ChestTileEntity.cpp b/source/world/tile/entity/ChestTileEntity.cpp new file mode 100644 index 000000000..40610cbe8 --- /dev/null +++ b/source/world/tile/entity/ChestTileEntity.cpp @@ -0,0 +1,32 @@ +#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::setContainerChanged(StackID stackId) +{ + SimpleContainer::setContainerChanged(stackId); + TileEntity::setChanged(); +} diff --git a/source/world/tile/entity/ChestTileEntity.hpp b/source/world/tile/entity/ChestTileEntity.hpp new file mode 100644 index 000000000..61c21ef49 --- /dev/null +++ b/source/world/tile/entity/ChestTileEntity.hpp @@ -0,0 +1,19 @@ +#pragma once + +#include "TileEntity.hpp" +#include "world/level/Level.hpp" +#include "world/inventory/SimpleContainer.hpp" + +class ChestTileEntity : public TileEntity, public SimpleContainer +{ +public: + ChestTileEntity(); + +public: + void load(const CompoundTag& tag) override; + void save(CompoundTag& tag) const override; + + bool stillValid(Player* player) const override; + + void setContainerChanged(StackID stackId) override; +}; diff --git a/source/world/tile/entity/FurnaceTileEntity.cpp b/source/world/tile/entity/FurnaceTileEntity.cpp new file mode 100644 index 000000000..5b0358c7b --- /dev/null +++ b/source/world/tile/entity/FurnaceTileEntity.cpp @@ -0,0 +1,165 @@ +#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::setContainerChanged(StackID stackId) +{ + SimpleContainer::setContainerChanged(stackId); + 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; +} diff --git a/source/world/tile/entity/FurnaceTileEntity.hpp b/source/world/tile/entity/FurnaceTileEntity.hpp new file mode 100644 index 000000000..c249797fa --- /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 setContainerChanged(StackID stackId) 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..ef8414fbf --- /dev/null +++ b/source/world/tile/entity/MusicTileEntity.cpp @@ -0,0 +1,48 @@ +#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)); +} diff --git a/source/world/tile/entity/MusicTileEntity.hpp b/source/world/tile/entity/MusicTileEntity.hpp new file mode 100644 index 000000000..2bc17ee8a --- /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; +}; diff --git a/source/world/tile/entity/TileEntity.cpp b/source/world/tile/entity/TileEntity.cpp new file mode 100644 index 000000000..0acab7361 --- /dev/null +++ b/source/world/tile/entity/TileEntity.cpp @@ -0,0 +1,108 @@ +#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 = TileEntityFactory::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"; +} diff --git a/source/world/tile/entity/TileEntity.hpp b/source/world/tile/entity/TileEntity.hpp new file mode 100644 index 000000000..7396bebb8 --- /dev/null +++ b/source/world/tile/entity/TileEntity.hpp @@ -0,0 +1,49 @@ +#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..8e513dfe0 --- /dev/null +++ b/source/world/tile/entity/TileEntityType.cpp @@ -0,0 +1,32 @@ +#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" + +TileEntityType* TileEntityType::furnace; +TileEntityType* TileEntityType::chest; +TileEntityType* TileEntityType::noteblock; + +std::map TileEntityFactory::_types; + +void TileEntityFactory::initTileEntities() +{ + TileEntityType::furnace = registerTileEntity("Furnace"); + TileEntityType::chest = registerTileEntity("Chest"); + TileEntityType::noteblock = registerTileEntity("Music"); +} + +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) + { + SAFE_DELETE(it->second); + } + _types.clear(); +} diff --git a/source/world/tile/entity/TileEntityType.hpp b/source/world/tile/entity/TileEntityType.hpp new file mode 100644 index 000000000..bce47445f --- /dev/null +++ b/source/world/tile/entity/TileEntityType.hpp @@ -0,0 +1,62 @@ +#pragma once + +#include +#include +#include + +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) { return _types[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: + TileEntityType(const std::string& name, CreateFunction func) + : m_name(name) + , m_function(func) + { + } + +public: + const std::string& getName() const { return m_name; } + TileEntity* newTileEntity() const { return m_function(); } + +private: + std::string m_name; + CreateFunction m_function; + + template + static TileEntity* CreateType() { return new T(); } +}; + +template +TileEntityType* TileEntityFactory::registerTileEntity(const std::string& name) +{ + TileEntityType* type = new TileEntityType(name, &TileEntityType::CreateType); + _types[type->m_name] = type; + return type; +} \ No newline at end of file