diff --git a/game/assets/lang/en_US.lang b/game/assets/lang/en_US.lang index d85d3c0b3..1002d7955 100644 --- a/game/assets/lang/en_US.lang +++ b/game/assets/lang/en_US.lang @@ -104,6 +104,7 @@ options.guiScale.small=Small options.guiScale.normal=Normal options.guiScale.large=Large options.advancedOpengl=Advanced OpenGL +options.enableVsync=Enable Vsync performance.max=Max FPS performance.balanced=Balanced diff --git a/platforms/android/AppPlatform_android.cpp b/platforms/android/AppPlatform_android.cpp index ceb1f9872..f3547ef58 100644 --- a/platforms/android/AppPlatform_android.cpp +++ b/platforms/android/AppPlatform_android.cpp @@ -141,6 +141,20 @@ void AppPlatform_android::setScreenSize(int width, int height) m_ScreenHeight = height; } +void AppPlatform_android::setVSyncEnabled(bool enabled) +{ + EGLDisplay display = eglGetCurrentDisplay(); + if (display == EGL_NO_DISPLAY) + return; + + eglSwapInterval(display, enabled ? 1 : 0); +} + +bool AppPlatform_android::isVsyncSwitchable() const +{ + return eglGetCurrentDisplay() != EGL_NO_DISPLAY; +} + void AppPlatform_android::initAndroidApp(android_app* ptr) { m_app = ptr; diff --git a/platforms/android/AppPlatform_android.hpp b/platforms/android/AppPlatform_android.hpp index ce7f219a4..b18433f5c 100644 --- a/platforms/android/AppPlatform_android.hpp +++ b/platforms/android/AppPlatform_android.hpp @@ -55,6 +55,8 @@ class AppPlatform_android : public AppPlatform void setExternalStoragePath(const std::string& path); AssetFile readAssetFile(const std::string&, bool) const override; + void setVSyncEnabled(bool enabled) override; + bool isVsyncSwitchable() const override; private: void changeKeyboardVisibility(bool bShown); diff --git a/platforms/macos/projects/Minecraft/Minecraft.xcodeproj/project.pbxproj b/platforms/macos/projects/Minecraft/Minecraft.xcodeproj/project.pbxproj index d8e42801d..974fe9f2f 100644 --- a/platforms/macos/projects/Minecraft/Minecraft.xcodeproj/project.pbxproj +++ b/platforms/macos/projects/Minecraft/Minecraft.xcodeproj/project.pbxproj @@ -194,6 +194,34 @@ 8426107D2AE989730065905F /* UpdateBlockPacket.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 840DD64C2AC810620006A435 /* UpdateBlockPacket.cpp */; }; 8426107E2AE989730065905F /* RakNetInstance.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 840DD64E2AC810620006A435 /* RakNetInstance.cpp */; }; 842610882AE98A4C0065905F /* libRakNet.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 84FFBD7E2ACA2876005A8CCF /* libRakNet.a */; }; + 84358AFE2F4550B30077EA44 /* TileEntityType.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84358AFC2F4550B30077EA44 /* TileEntityType.cpp */; }; + 84358AFF2F4550B30077EA44 /* ChestTileEntity.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84358AF42F4550B30077EA44 /* ChestTileEntity.cpp */; }; + 84358B002F4550B30077EA44 /* FurnaceTileEntity.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84358AF62F4550B30077EA44 /* FurnaceTileEntity.cpp */; }; + 84358B012F4550B30077EA44 /* TileEntity.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84358AFA2F4550B30077EA44 /* TileEntity.cpp */; }; + 84358B022F4550B30077EA44 /* MusicTileEntity.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84358AF82F4550B30077EA44 /* MusicTileEntity.cpp */; }; + 84358B032F4550B30077EA44 /* TileEntity.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84358AF92F4550B30077EA44 /* TileEntity.hpp */; }; + 84358B042F4550B30077EA44 /* FurnaceTileEntity.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84358AF52F4550B30077EA44 /* FurnaceTileEntity.hpp */; }; + 84358B052F4550B30077EA44 /* ChestTileEntity.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84358AF32F4550B30077EA44 /* ChestTileEntity.hpp */; }; + 84358B062F4550B30077EA44 /* MusicTileEntity.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84358AF72F4550B30077EA44 /* MusicTileEntity.hpp */; }; + 84358B072F4550B30077EA44 /* TileEntityType.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84358AFB2F4550B30077EA44 /* TileEntityType.hpp */; }; + 84358B102F4550CC0077EA44 /* FurnaceTile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84358B0D2F4550CC0077EA44 /* FurnaceTile.cpp */; }; + 84358B112F4550CC0077EA44 /* EntityTile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84358B0B2F4550CC0077EA44 /* EntityTile.cpp */; }; + 84358B122F4550CC0077EA44 /* ChestTile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84358B092F4550CC0077EA44 /* ChestTile.cpp */; }; + 84358B132F4550CC0077EA44 /* MusicTile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84358B0F2F4550CC0077EA44 /* MusicTile.cpp */; }; + 84358B142F4550CC0077EA44 /* ChestTile.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84358B082F4550CC0077EA44 /* ChestTile.hpp */; }; + 84358B152F4550CC0077EA44 /* FurnaceTile.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84358B0C2F4550CC0077EA44 /* FurnaceTile.hpp */; }; + 84358B162F4550CC0077EA44 /* EntityTile.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84358B0A2F4550CC0077EA44 /* EntityTile.hpp */; }; + 84358B172F4550CC0077EA44 /* MusicTile.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84358B0E2F4550CC0077EA44 /* MusicTile.hpp */; }; + 84358B192F45511C0077EA44 /* NoteParticle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84358B182F45511C0077EA44 /* NoteParticle.cpp */; }; + 84358B1B2F4551290077EA44 /* Facing.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84358B1A2F4551290077EA44 /* Facing.cpp */; }; + 84358B1E2F4551500077EA44 /* CoalItem.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84358B1D2F4551500077EA44 /* CoalItem.cpp */; }; + 84358B1F2F4551500077EA44 /* CoalItem.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84358B1C2F4551500077EA44 /* CoalItem.hpp */; }; + 84358B242F4551730077EA44 /* FurnaceResultSlot.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84358B232F4551730077EA44 /* FurnaceResultSlot.cpp */; }; + 84358B252F4551730077EA44 /* FurnaceMenu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84358B212F4551730077EA44 /* FurnaceMenu.cpp */; }; + 84358B262F4551730077EA44 /* FurnaceMenu.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84358B202F4551730077EA44 /* FurnaceMenu.hpp */; }; + 84358B272F4551730077EA44 /* FurnaceResultSlot.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84358B222F4551730077EA44 /* FurnaceResultSlot.hpp */; }; + 84358B2A2F45519B0077EA44 /* FurnaceScreen.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84358B282F45519B0077EA44 /* FurnaceScreen.hpp */; }; + 84358B2B2F45519B0077EA44 /* FurnaceScreen.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84358B292F45519B0077EA44 /* FurnaceScreen.cpp */; }; 8435BB192DCD47F400D38282 /* SoundStreamOAL.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8435BB182DCD47F400D38282 /* SoundStreamOAL.cpp */; }; 8435BB1A2DCD47F400D38282 /* SoundStreamOAL.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8435BB182DCD47F400D38282 /* SoundStreamOAL.cpp */; }; 8441F98F2DB4E911005977BD /* SoundSystemOAL.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8441F9862DB4E911005977BD /* SoundSystemOAL.cpp */; }; @@ -961,11 +989,6 @@ 84E7BF272F286A08002D3936 /* SimpleContainer.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84E7BF172F286A08002D3936 /* SimpleContainer.hpp */; }; 84E7BF282F286A08002D3936 /* Slot.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84E7BF182F286A08002D3936 /* Slot.cpp */; }; 84E7BF292F286A08002D3936 /* Slot.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84E7BF192F286A08002D3936 /* Slot.hpp */; }; - 84E7BF2F2F286A20002D3936 /* CompoundContainer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84E7BF2A2F286A20002D3936 /* CompoundContainer.cpp */; }; - 84E7BF302F286A20002D3936 /* CompoundContainer.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84E7BF2B2F286A20002D3936 /* CompoundContainer.hpp */; }; - 84E7BF312F286A20002D3936 /* Container.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84E7BF2C2F286A20002D3936 /* Container.hpp */; }; - 84E7BF322F286A20002D3936 /* ContainerListener.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84E7BF2D2F286A20002D3936 /* ContainerListener.cpp */; }; - 84E7BF332F286A20002D3936 /* ContainerListener.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84E7BF2E2F286A20002D3936 /* ContainerListener.hpp */; }; 84E7BF362F286A6A002D3936 /* ItemStack.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84E7BF342F286A6A002D3936 /* ItemStack.cpp */; }; 84E7BF372F286A6A002D3936 /* ItemStack.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84E7BF352F286A6A002D3936 /* ItemStack.hpp */; }; 84E8DCE52E84ACBC00B30789 /* libRenderer.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 8406FD292AF1814500B09C1D /* libRenderer.a */; }; @@ -1259,6 +1282,15 @@ 84EEED642F0A01B700F741B6 /* ViewportOrigin.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84EEED622F0A01B700F741B6 /* ViewportOrigin.hpp */; }; 84EEED682F0A02F300F741B6 /* ErrorHandlerOGL.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84EEED662F0A02F300F741B6 /* ErrorHandlerOGL.cpp */; }; 84EEED692F0A02F300F741B6 /* ErrorHandlerOGL.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84EEED672F0A02F300F741B6 /* ErrorHandlerOGL.hpp */; }; + 84F0DA762F45FAE100906960 /* ContainerContentChangeListener.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84F0DA702F45FAE100906960 /* ContainerContentChangeListener.hpp */; }; + 84F0DA772F45FAE100906960 /* ContainerListener.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84F0DA722F45FAE100906960 /* ContainerListener.hpp */; }; + 84F0DA782F45FAE100906960 /* Container.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84F0DA6F2F45FAE100906960 /* Container.hpp */; }; + 84F0DA792F45FAE100906960 /* CompoundContainer.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84F0DA6D2F45FAE100906960 /* CompoundContainer.hpp */; }; + 84F0DA7A2F45FAE100906960 /* ContainerSizeChangeListener.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84F0DA742F45FAE100906960 /* ContainerSizeChangeListener.hpp */; }; + 84F0DA7B2F45FAE100906960 /* ContainerContentChangeListener.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84F0DA712F45FAE100906960 /* ContainerContentChangeListener.cpp */; }; + 84F0DA7C2F45FAE100906960 /* CompoundContainer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84F0DA6E2F45FAE100906960 /* CompoundContainer.cpp */; }; + 84F0DA7D2F45FAE100906960 /* ContainerListener.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84F0DA732F45FAE100906960 /* ContainerListener.cpp */; }; + 84F0DA7E2F45FAE100906960 /* ContainerSizeChangeListener.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84F0DA752F45FAE100906960 /* ContainerSizeChangeListener.cpp */; }; 84F4880E2EEA4F5C00309B1E /* FixedPipelineStateNull.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84F487F02EEA4F5C00309B1E /* FixedPipelineStateNull.cpp */; }; 84F4880F2EEA4F5C00309B1E /* FixedPipelineStateNull.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 84F487F12EEA4F5C00309B1E /* FixedPipelineStateNull.hpp */; }; 84F488102EEA4F5C00309B1E /* FogStateNull.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84F487F22EEA4F5C00309B1E /* FogStateNull.cpp */; }; @@ -2234,6 +2266,34 @@ 84336BA52B1EB57E00097DB0 /* Settings_iOS_Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Settings_iOS_Debug.xcconfig; path = ../Configuration/Settings_iOS_Debug.xcconfig; sourceTree = ""; }; 84336BA82B1EB88500097DB0 /* Settings_macOS.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Settings_macOS.xcconfig; path = ../Configuration/Settings_macOS.xcconfig; sourceTree = ""; }; 84336BA92B1EB9C200097DB0 /* Settings_macOS_Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Settings_macOS_Debug.xcconfig; path = ../Configuration/Settings_macOS_Debug.xcconfig; sourceTree = ""; }; + 84358AF32F4550B30077EA44 /* ChestTileEntity.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ChestTileEntity.hpp; sourceTree = ""; }; + 84358AF42F4550B30077EA44 /* ChestTileEntity.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ChestTileEntity.cpp; sourceTree = ""; }; + 84358AF52F4550B30077EA44 /* FurnaceTileEntity.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = FurnaceTileEntity.hpp; sourceTree = ""; }; + 84358AF62F4550B30077EA44 /* FurnaceTileEntity.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FurnaceTileEntity.cpp; sourceTree = ""; }; + 84358AF72F4550B30077EA44 /* MusicTileEntity.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = MusicTileEntity.hpp; sourceTree = ""; }; + 84358AF82F4550B30077EA44 /* MusicTileEntity.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = MusicTileEntity.cpp; sourceTree = ""; }; + 84358AF92F4550B30077EA44 /* TileEntity.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = TileEntity.hpp; sourceTree = ""; }; + 84358AFA2F4550B30077EA44 /* TileEntity.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = TileEntity.cpp; sourceTree = ""; }; + 84358AFB2F4550B30077EA44 /* TileEntityType.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = TileEntityType.hpp; sourceTree = ""; }; + 84358AFC2F4550B30077EA44 /* TileEntityType.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = TileEntityType.cpp; sourceTree = ""; }; + 84358B082F4550CC0077EA44 /* ChestTile.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ChestTile.hpp; sourceTree = ""; }; + 84358B092F4550CC0077EA44 /* ChestTile.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ChestTile.cpp; sourceTree = ""; }; + 84358B0A2F4550CC0077EA44 /* EntityTile.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = EntityTile.hpp; sourceTree = ""; }; + 84358B0B2F4550CC0077EA44 /* EntityTile.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = EntityTile.cpp; sourceTree = ""; }; + 84358B0C2F4550CC0077EA44 /* FurnaceTile.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = FurnaceTile.hpp; sourceTree = ""; }; + 84358B0D2F4550CC0077EA44 /* FurnaceTile.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FurnaceTile.cpp; sourceTree = ""; }; + 84358B0E2F4550CC0077EA44 /* MusicTile.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = MusicTile.hpp; sourceTree = ""; }; + 84358B0F2F4550CC0077EA44 /* MusicTile.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = MusicTile.cpp; sourceTree = ""; }; + 84358B182F45511C0077EA44 /* NoteParticle.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = NoteParticle.cpp; sourceTree = ""; }; + 84358B1A2F4551290077EA44 /* Facing.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Facing.cpp; sourceTree = ""; }; + 84358B1C2F4551500077EA44 /* CoalItem.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = CoalItem.hpp; sourceTree = ""; }; + 84358B1D2F4551500077EA44 /* CoalItem.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = CoalItem.cpp; sourceTree = ""; }; + 84358B202F4551730077EA44 /* FurnaceMenu.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = FurnaceMenu.hpp; sourceTree = ""; }; + 84358B212F4551730077EA44 /* FurnaceMenu.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FurnaceMenu.cpp; sourceTree = ""; }; + 84358B222F4551730077EA44 /* FurnaceResultSlot.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = FurnaceResultSlot.hpp; sourceTree = ""; }; + 84358B232F4551730077EA44 /* FurnaceResultSlot.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FurnaceResultSlot.cpp; sourceTree = ""; }; + 84358B282F45519B0077EA44 /* FurnaceScreen.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = FurnaceScreen.hpp; sourceTree = ""; }; + 84358B292F45519B0077EA44 /* FurnaceScreen.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FurnaceScreen.cpp; sourceTree = ""; }; 8435BB172DCD47F400D38282 /* SoundStreamOAL.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = SoundStreamOAL.hpp; sourceTree = ""; }; 8435BB182DCD47F400D38282 /* SoundStreamOAL.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = SoundStreamOAL.cpp; sourceTree = ""; }; 8441F9852DB4E911005977BD /* CustomSoundSystem.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = CustomSoundSystem.hpp; sourceTree = ""; }; @@ -2710,11 +2770,6 @@ 84E7BF172F286A08002D3936 /* SimpleContainer.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = SimpleContainer.hpp; sourceTree = ""; }; 84E7BF182F286A08002D3936 /* Slot.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Slot.cpp; sourceTree = ""; }; 84E7BF192F286A08002D3936 /* Slot.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Slot.hpp; sourceTree = ""; }; - 84E7BF2A2F286A20002D3936 /* CompoundContainer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CompoundContainer.cpp; sourceTree = ""; }; - 84E7BF2B2F286A20002D3936 /* CompoundContainer.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = CompoundContainer.hpp; sourceTree = ""; }; - 84E7BF2C2F286A20002D3936 /* Container.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Container.hpp; sourceTree = ""; }; - 84E7BF2D2F286A20002D3936 /* ContainerListener.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ContainerListener.cpp; sourceTree = ""; }; - 84E7BF2E2F286A20002D3936 /* ContainerListener.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ContainerListener.hpp; sourceTree = ""; }; 84E7BF342F286A6A002D3936 /* ItemStack.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ItemStack.cpp; sourceTree = ""; }; 84E7BF352F286A6A002D3936 /* ItemStack.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ItemStack.hpp; sourceTree = ""; }; 84EAE8DD2AF1EAA1000894E8 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; @@ -3013,6 +3068,15 @@ 84EEED622F0A01B700F741B6 /* ViewportOrigin.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ViewportOrigin.hpp; sourceTree = ""; }; 84EEED662F0A02F300F741B6 /* ErrorHandlerOGL.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ErrorHandlerOGL.cpp; sourceTree = ""; }; 84EEED672F0A02F300F741B6 /* ErrorHandlerOGL.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ErrorHandlerOGL.hpp; sourceTree = ""; }; + 84F0DA6D2F45FAE100906960 /* CompoundContainer.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = CompoundContainer.hpp; sourceTree = ""; }; + 84F0DA6E2F45FAE100906960 /* CompoundContainer.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = CompoundContainer.cpp; sourceTree = ""; }; + 84F0DA6F2F45FAE100906960 /* Container.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Container.hpp; sourceTree = ""; }; + 84F0DA702F45FAE100906960 /* ContainerContentChangeListener.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ContainerContentChangeListener.hpp; sourceTree = ""; }; + 84F0DA712F45FAE100906960 /* ContainerContentChangeListener.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ContainerContentChangeListener.cpp; sourceTree = ""; }; + 84F0DA722F45FAE100906960 /* ContainerListener.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ContainerListener.hpp; sourceTree = ""; }; + 84F0DA732F45FAE100906960 /* ContainerListener.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ContainerListener.cpp; sourceTree = ""; }; + 84F0DA742F45FAE100906960 /* ContainerSizeChangeListener.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ContainerSizeChangeListener.hpp; sourceTree = ""; }; + 84F0DA752F45FAE100906960 /* ContainerSizeChangeListener.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ContainerSizeChangeListener.cpp; sourceTree = ""; }; 84F487F02EEA4F5C00309B1E /* FixedPipelineStateNull.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FixedPipelineStateNull.cpp; sourceTree = ""; }; 84F487F12EEA4F5C00309B1E /* FixedPipelineStateNull.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = FixedPipelineStateNull.hpp; sourceTree = ""; }; 84F487F22EEA4F5C00309B1E /* FogStateNull.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FogStateNull.cpp; sourceTree = ""; }; @@ -3750,12 +3814,8 @@ 840DD6562AC810620006A435 /* world */ = { isa = PBXGroup; children = ( - 84E7BF2A2F286A20002D3936 /* CompoundContainer.cpp */, - 84E7BF2B2F286A20002D3936 /* CompoundContainer.hpp */, - 84E7BF2C2F286A20002D3936 /* Container.hpp */, - 84E7BF2D2F286A20002D3936 /* ContainerListener.cpp */, - 84E7BF2E2F286A20002D3936 /* ContainerListener.hpp */, 840DD6572AC810620006A435 /* entity */, + 84358B1A2F4551290077EA44 /* Facing.cpp */, 8477B3A82C4DC3B3004E1AC5 /* Facing.hpp */, 840DD6682AC810620006A435 /* gamemode */, 84E7BF092F286A08002D3936 /* inventory */, @@ -3852,6 +3912,8 @@ children = ( 84B9D8962F3BE0A900FD67C4 /* ArmorItem.hpp */, 84B9D8972F3BE0A900FD67C4 /* ArmorItem.cpp */, + 84358B1C2F4551500077EA44 /* CoalItem.hpp */, + 84358B1D2F4551500077EA44 /* CoalItem.cpp */, 84B9D8982F3BE0A900FD67C4 /* HoeItem.hpp */, 84B9D8992F3BE0A900FD67C4 /* HoeItem.cpp */, 84B9D89A2F3BE0A900FD67C4 /* SeedItem.hpp */, @@ -4042,6 +4104,7 @@ 840DD6CE2AC810620006A435 /* particle */ = { isa = PBXGroup; children = ( + 84358B182F45511C0077EA44 /* NoteParticle.cpp */, 840DD6CF2AC810620006A435 /* BubbleParticle.cpp */, 840DD6D02AC810620006A435 /* ExplodeParticle.cpp */, 8470AF3B2BE9B6FA00BCA54E /* FireworkParticle.hpp */, @@ -4077,104 +4140,113 @@ 840DD6E12AC810620006A435 /* tile */ = { isa = PBXGroup; children = ( - 84B9D8A22F3BE0B200FD67C4 /* CropsTile.hpp */, - 84B9D8A32F3BE0B200FD67C4 /* CropsTile.cpp */, - 840DD6E22AC810620006A435 /* BookshelfTile.cpp */, + 84358AFD2F4550B30077EA44 /* entity */, 840DD6E32AC810620006A435 /* BookshelfTile.hpp */, - 840DD6E42AC810620006A435 /* Bush.cpp */, + 840DD6E22AC810620006A435 /* BookshelfTile.cpp */, 840DD6E52AC810620006A435 /* Bush.hpp */, - 84E1C9BF2E7FDC26007D2F5D /* CactusTile.cpp */, + 840DD6E42AC810620006A435 /* Bush.cpp */, 84E1C9C02E7FDC26007D2F5D /* CactusTile.hpp */, - 840DD6E62AC810620006A435 /* ClayTile.cpp */, + 84E1C9BF2E7FDC26007D2F5D /* CactusTile.cpp */, + 84358B082F4550CC0077EA44 /* ChestTile.hpp */, + 84358B092F4550CC0077EA44 /* ChestTile.cpp */, 840DD6E72AC810620006A435 /* ClayTile.hpp */, - 840DD6E82AC810620006A435 /* ClothTile.cpp */, + 840DD6E62AC810620006A435 /* ClayTile.cpp */, 840DD6E92AC810620006A435 /* ClothTile.hpp */, - 84DED1B52F30970A004001C5 /* CraftingTableTile.cpp */, + 840DD6E82AC810620006A435 /* ClothTile.cpp */, 84DED1B62F30970A004001C5 /* CraftingTableTile.hpp */, - 84E1C9C12E7FDC26007D2F5D /* DeadBush.cpp */, + 84DED1B52F30970A004001C5 /* CraftingTableTile.cpp */, + 84B9D8A22F3BE0B200FD67C4 /* CropsTile.hpp */, + 84B9D8A32F3BE0B200FD67C4 /* CropsTile.cpp */, 84E1C9C22E7FDC26007D2F5D /* DeadBush.hpp */, - 840DD6EA2AC810620006A435 /* DirtTile.cpp */, + 84E1C9C12E7FDC26007D2F5D /* DeadBush.cpp */, 840DD6EB2AC810620006A435 /* DirtTile.hpp */, - 840DD6EC2AC810620006A435 /* DoorTile.cpp */, + 840DD6EA2AC810620006A435 /* DirtTile.cpp */, 840DD6ED2AC810620006A435 /* DoorTile.hpp */, - 840DD6EE2AC810620006A435 /* FarmTile.cpp */, + 840DD6EC2AC810620006A435 /* DoorTile.cpp */, + 84358B0A2F4550CC0077EA44 /* EntityTile.hpp */, + 84358B0B2F4550CC0077EA44 /* EntityTile.cpp */, 840DD6EF2AC810620006A435 /* FarmTile.hpp */, - 84E1C9C32E7FDC26007D2F5D /* FenceTile.cpp */, + 840DD6EE2AC810620006A435 /* FarmTile.cpp */, 84E1C9C42E7FDC26007D2F5D /* FenceTile.hpp */, - 840DD6F02AC810620006A435 /* FireTile.cpp */, + 84E1C9C32E7FDC26007D2F5D /* FenceTile.cpp */, 840DD6F12AC810620006A435 /* FireTile.hpp */, - 840DD6F22AC810620006A435 /* GlassTile.cpp */, + 840DD6F02AC810620006A435 /* FireTile.cpp */, + 84358B0C2F4550CC0077EA44 /* FurnaceTile.hpp */, + 84358B0D2F4550CC0077EA44 /* FurnaceTile.cpp */, 840DD6F32AC810620006A435 /* GlassTile.hpp */, - 84E1C9C52E7FDC26007D2F5D /* GlowstoneTile.cpp */, + 840DD6F22AC810620006A435 /* GlassTile.cpp */, 84E1C9C62E7FDC26007D2F5D /* GlowstoneTile.hpp */, - 840DD6F42AC810620006A435 /* GrassTile.cpp */, + 84E1C9C52E7FDC26007D2F5D /* GlowstoneTile.cpp */, 840DD6F52AC810620006A435 /* GrassTile.hpp */, - 840DD6F62AC810620006A435 /* GravelTile.cpp */, + 840DD6F42AC810620006A435 /* GrassTile.cpp */, 840DD6F72AC810620006A435 /* GravelTile.hpp */, - 840DD6F82AC810620006A435 /* HalfTransparentTile.cpp */, + 840DD6F62AC810620006A435 /* GravelTile.cpp */, 840DD6F92AC810620006A435 /* HalfTransparentTile.hpp */, - 840DD6FA2AC810620006A435 /* IceTile.cpp */, + 840DD6F82AC810620006A435 /* HalfTransparentTile.cpp */, 840DD6FB2AC810620006A435 /* IceTile.hpp */, - 840DD6FC2AC810620006A435 /* InvisibleTile.cpp */, + 840DD6FA2AC810620006A435 /* IceTile.cpp */, 840DD6FD2AC810620006A435 /* InvisibleTile.hpp */, - 840DD6FE2AC810620006A435 /* LadderTile.cpp */, + 840DD6FC2AC810620006A435 /* InvisibleTile.cpp */, 840DD6FF2AC810620006A435 /* LadderTile.hpp */, - 840DD7002AC810620006A435 /* LeafTile.cpp */, + 840DD6FE2AC810620006A435 /* LadderTile.cpp */, 840DD7012AC810620006A435 /* LeafTile.hpp */, - 840DD7022AC810620006A435 /* LiquidTile.cpp */, + 840DD7002AC810620006A435 /* LeafTile.cpp */, 840DD7032AC810620006A435 /* LiquidTile.hpp */, - 840DD7042AC810620006A435 /* LiquidTileDynamic.cpp */, + 840DD7022AC810620006A435 /* LiquidTile.cpp */, 840DD7052AC810620006A435 /* LiquidTileDynamic.hpp */, - 840DD7062AC810620006A435 /* LiquidTileStatic.cpp */, + 840DD7042AC810620006A435 /* LiquidTileDynamic.cpp */, 840DD7072AC810620006A435 /* LiquidTileStatic.hpp */, - 840DD7082AC810620006A435 /* MetalTile.cpp */, + 840DD7062AC810620006A435 /* LiquidTileStatic.cpp */, 840DD7092AC810620006A435 /* MetalTile.hpp */, - 840DD70A2AC810620006A435 /* ObsidianTile.cpp */, + 840DD7082AC810620006A435 /* MetalTile.cpp */, + 84358B0E2F4550CC0077EA44 /* MusicTile.hpp */, + 84358B0F2F4550CC0077EA44 /* MusicTile.cpp */, 840DD70B2AC810620006A435 /* ObsidianTile.hpp */, - 840DD70C2AC810620006A435 /* OreTile.cpp */, + 840DD70A2AC810620006A435 /* ObsidianTile.cpp */, 840DD70D2AC810620006A435 /* OreTile.hpp */, - 84E1C9C72E7FDC26007D2F5D /* PumpkinTile.cpp */, + 840DD70C2AC810620006A435 /* OreTile.cpp */, 84E1C9C82E7FDC26007D2F5D /* PumpkinTile.hpp */, - 840DD70E2AC810620006A435 /* RedStoneOreTile.cpp */, + 84E1C9C72E7FDC26007D2F5D /* PumpkinTile.cpp */, 840DD70F2AC810620006A435 /* RedStoneOreTile.hpp */, - 840DD7102AC810620006A435 /* ReedTile.cpp */, + 840DD70E2AC810620006A435 /* RedStoneOreTile.cpp */, 840DD7112AC810620006A435 /* ReedTile.hpp */, - 84E78C852B58B66B00D515EF /* RocketLauncherTile.cpp */, + 840DD7102AC810620006A435 /* ReedTile.cpp */, 84E78C862B58B66B00D515EF /* RocketLauncherTile.hpp */, - 840DD7122AC810620006A435 /* SandStoneTile.cpp */, + 84E78C852B58B66B00D515EF /* RocketLauncherTile.cpp */, 840DD7132AC810620006A435 /* SandStoneTile.hpp */, - 840DD7142AC810620006A435 /* SandTile.cpp */, + 840DD7122AC810620006A435 /* SandStoneTile.cpp */, 840DD7152AC810620006A435 /* SandTile.hpp */, - 840DD7162AC810620006A435 /* Sapling.cpp */, + 840DD7142AC810620006A435 /* SandTile.cpp */, 840DD7172AC810620006A435 /* Sapling.hpp */, - 84E1C9C92E7FDC26007D2F5D /* SoulSandTile.cpp */, + 840DD7162AC810620006A435 /* Sapling.cpp */, 84E1C9CA2E7FDC26007D2F5D /* SoulSandTile.hpp */, - 840DD7182AC810620006A435 /* SpongeTile.cpp */, + 84E1C9C92E7FDC26007D2F5D /* SoulSandTile.cpp */, 840DD7192AC810620006A435 /* SpongeTile.hpp */, - 840DD71A2AC810620006A435 /* StairTile.cpp */, + 840DD7182AC810620006A435 /* SpongeTile.cpp */, 840DD71B2AC810620006A435 /* StairTile.hpp */, - 840DD71C2AC810620006A435 /* StoneSlabTile.cpp */, + 840DD71A2AC810620006A435 /* StairTile.cpp */, 840DD71D2AC810620006A435 /* StoneSlabTile.hpp */, - 840DD71E2AC810620006A435 /* StoneTile.cpp */, + 840DD71C2AC810620006A435 /* StoneSlabTile.cpp */, 840DD71F2AC810620006A435 /* StoneTile.hpp */, - 84E1C9CB2E7FDC26007D2F5D /* TallGrass.cpp */, + 840DD71E2AC810620006A435 /* StoneTile.cpp */, 84E1C9CC2E7FDC26007D2F5D /* TallGrass.hpp */, - 840DD7202AC810620006A435 /* Tile.cpp */, + 84E1C9CB2E7FDC26007D2F5D /* TallGrass.cpp */, 840DD7212AC810620006A435 /* Tile.hpp */, - 840DD7222AC810620006A435 /* TntTile.cpp */, + 840DD7202AC810620006A435 /* Tile.cpp */, 840DD7232AC810620006A435 /* TntTile.hpp */, - 840DD7242AC810620006A435 /* TopSnowTile.cpp */, + 840DD7222AC810620006A435 /* TntTile.cpp */, 840DD7252AC810620006A435 /* TopSnowTile.hpp */, - 840DD7262AC810620006A435 /* TorchTile.cpp */, + 840DD7242AC810620006A435 /* TopSnowTile.cpp */, 840DD7272AC810620006A435 /* TorchTile.hpp */, - 840DD7282AC810620006A435 /* TransparentTile.cpp */, + 840DD7262AC810620006A435 /* TorchTile.cpp */, 840DD7292AC810620006A435 /* TransparentTile.hpp */, - 840DD72A2AC810620006A435 /* TreeTile.cpp */, + 840DD7282AC810620006A435 /* TransparentTile.cpp */, 840DD72B2AC810620006A435 /* TreeTile.hpp */, - 84E1C9CD2E7FDC26007D2F5D /* Web.cpp */, + 840DD72A2AC810620006A435 /* TreeTile.cpp */, 84E1C9CE2E7FDC26007D2F5D /* Web.hpp */, - 840DD72C2AC810620006A435 /* WireTile.cpp */, + 84E1C9CD2E7FDC26007D2F5D /* Web.cpp */, 840DD72D2AC810620006A435 /* WireTile.hpp */, + 840DD72C2AC810620006A435 /* WireTile.cpp */, ); path = tile; sourceTree = ""; @@ -4566,6 +4638,23 @@ name = macOS; sourceTree = ""; }; + 84358AFD2F4550B30077EA44 /* entity */ = { + isa = PBXGroup; + children = ( + 84358AF32F4550B30077EA44 /* ChestTileEntity.hpp */, + 84358AF42F4550B30077EA44 /* ChestTileEntity.cpp */, + 84358AF52F4550B30077EA44 /* FurnaceTileEntity.hpp */, + 84358AF62F4550B30077EA44 /* FurnaceTileEntity.cpp */, + 84358AF72F4550B30077EA44 /* MusicTileEntity.hpp */, + 84358AF82F4550B30077EA44 /* MusicTileEntity.cpp */, + 84358AF92F4550B30077EA44 /* TileEntity.hpp */, + 84358AFA2F4550B30077EA44 /* TileEntity.cpp */, + 84358AFB2F4550B30077EA44 /* TileEntityType.hpp */, + 84358AFC2F4550B30077EA44 /* TileEntityType.cpp */, + ); + path = entity; + sourceTree = ""; + }; 8441F9872DB4E911005977BD /* openal */ = { isa = PBXGroup; children = ( @@ -4949,6 +5038,8 @@ 84E022E82F2A0211003C8FFE /* inventory */ = { isa = PBXGroup; children = ( + 84358B282F45519B0077EA44 /* FurnaceScreen.hpp */, + 84358B292F45519B0077EA44 /* FurnaceScreen.cpp */, 84E022E92F2A0211003C8FFE /* ChestScreen.cpp */, 84E022EA2F2A0211003C8FFE /* ChestScreen.hpp */, 844337192F405E1C00EB3115 /* ClassicCraftingScreen_Console.cpp */, @@ -4995,6 +5086,15 @@ 84E7BF092F286A08002D3936 /* inventory */ = { isa = PBXGroup; children = ( + 84F0DA6D2F45FAE100906960 /* CompoundContainer.hpp */, + 84F0DA6E2F45FAE100906960 /* CompoundContainer.cpp */, + 84F0DA6F2F45FAE100906960 /* Container.hpp */, + 84F0DA702F45FAE100906960 /* ContainerContentChangeListener.hpp */, + 84F0DA712F45FAE100906960 /* ContainerContentChangeListener.cpp */, + 84F0DA722F45FAE100906960 /* ContainerListener.hpp */, + 84F0DA732F45FAE100906960 /* ContainerListener.cpp */, + 84F0DA742F45FAE100906960 /* ContainerSizeChangeListener.hpp */, + 84F0DA752F45FAE100906960 /* ContainerSizeChangeListener.cpp */, 84E7BF0A2F286A08002D3936 /* ArmorSlot.cpp */, 84E7BF0B2F286A08002D3936 /* ArmorSlot.hpp */, 84E022FE2F2A0245003C8FFE /* ChestMenu.cpp */, @@ -5006,6 +5106,10 @@ 84E7BF0F2F286A08002D3936 /* CraftingContainer.hpp */, 84DED19A2F3095FD004001C5 /* CraftingMenu.cpp */, 84DED19B2F3095FD004001C5 /* CraftingMenu.hpp */, + 84358B202F4551730077EA44 /* FurnaceMenu.hpp */, + 84358B212F4551730077EA44 /* FurnaceMenu.cpp */, + 84358B222F4551730077EA44 /* FurnaceResultSlot.hpp */, + 84358B232F4551730077EA44 /* FurnaceResultSlot.cpp */, 84E7BF102F286A08002D3936 /* InventoryMenu.cpp */, 84E7BF112F286A08002D3936 /* InventoryMenu.hpp */, 84E7BF122F286A08002D3936 /* ResultContainer.cpp */, @@ -5806,6 +5910,7 @@ 84A2FF182DB61D440090CE3E /* SoundPathRepository.hpp in Headers */, 84EB09FB2EE2CCA8008FB007 /* TextureData.hpp in Headers */, 84B1E0302E04FD4500ED000A /* ArrowRenderer.hpp in Headers */, + 84358B2A2F45519B0077EA44 /* FurnaceScreen.hpp in Headers */, 84AA8C2B2B32F3F3003F5B82 /* LightUpdate.hpp in Headers */, 84AA8C2D2B32F3F3003F5B82 /* PatchManager.hpp in Headers */, 84AA8C2F2B32F3F3003F5B82 /* RenderChunk.hpp in Headers */, @@ -5845,6 +5950,10 @@ 84E1C9D42E7FDC26007D2F5D /* FenceTile.hpp in Headers */, 84BF637B2AF186C8008A9995 /* ItemEntity.hpp in Headers */, 84BF637D2AF186C8008A9995 /* Mob.hpp in Headers */, + 84358B142F4550CC0077EA44 /* ChestTile.hpp in Headers */, + 84358B152F4550CC0077EA44 /* FurnaceTile.hpp in Headers */, + 84358B162F4550CC0077EA44 /* EntityTile.hpp in Headers */, + 84358B172F4550CC0077EA44 /* MusicTile.hpp in Headers */, 84BF637E2AF186C8008A9995 /* Player.hpp in Headers */, 8477B3A92C4DC3B3004E1AC5 /* Facing.hpp in Headers */, 84BF637F2AF186C8008A9995 /* PrimedTnt.hpp in Headers */, @@ -5858,7 +5967,6 @@ 84BF63862AF186C8008A9995 /* Inventory.hpp in Headers */, 8477B3B52C4DC414004E1AC5 /* ChunkTilePos.hpp in Headers */, 84BF63872AF186C8008A9995 /* Item.hpp in Headers */, - 84E7BF332F286A20002D3936 /* ContainerListener.hpp in Headers */, 84BF63892AF186C8008A9995 /* TileItem.hpp in Headers */, 84BF638A2AF186C8008A9995 /* TilePlanterItem.hpp in Headers */, 84E7BF292F286A08002D3936 /* Slot.hpp in Headers */, @@ -5872,6 +5980,11 @@ 84BF638F2AF186C8008A9995 /* BiomeSource.hpp in Headers */, 84BF63902AF186C8008A9995 /* ChunkCache.hpp in Headers */, 84BF63912AF186C8008A9995 /* ChunkSource.hpp in Headers */, + 84F0DA762F45FAE100906960 /* ContainerContentChangeListener.hpp in Headers */, + 84F0DA772F45FAE100906960 /* ContainerListener.hpp in Headers */, + 84F0DA782F45FAE100906960 /* Container.hpp in Headers */, + 84F0DA792F45FAE100906960 /* CompoundContainer.hpp in Headers */, + 84F0DA7A2F45FAE100906960 /* ContainerSizeChangeListener.hpp in Headers */, 84BF63922AF186C8008A9995 /* LevelChunk.hpp in Headers */, 84BF63932AF186C8008A9995 /* PerformanceTestChunkSource.hpp in Headers */, 84E023012F2A0245003C8FFE /* ChestMenu.hpp in Headers */, @@ -5915,6 +6028,8 @@ 84BF63A92AF186C8008A9995 /* RegionFile.hpp in Headers */, 84BF63AA2AF186C8008A9995 /* TickNextTickData.hpp in Headers */, 84BF63AB2AF186C8008A9995 /* Particle.hpp in Headers */, + 84358B262F4551730077EA44 /* FurnaceMenu.hpp in Headers */, + 84358B272F4551730077EA44 /* FurnaceResultSlot.hpp in Headers */, 84E7BF232F286A08002D3936 /* ResultContainer.hpp in Headers */, 84E1C9E62E7FDC72007D2F5D /* AuxTileItem.hpp in Headers */, 84E7BF272F286A08002D3936 /* SimpleContainer.hpp in Headers */, @@ -5975,7 +6090,6 @@ 84BF63D22AF186C9008A9995 /* TorchTile.hpp in Headers */, 84BF63D32AF186C9008A9995 /* TransparentTile.hpp in Headers */, 84BF63D42AF186C9008A9995 /* TreeTile.hpp in Headers */, - 84E7BF312F286A20002D3936 /* Container.hpp in Headers */, 84BF63D52AF186C9008A9995 /* WireTile.hpp in Headers */, 84AA8B512B32F39A003F5B82 /* BinaryHeap.hpp in Headers */, 84AA8B532B32F39A003F5B82 /* Node.hpp in Headers */, @@ -6001,6 +6115,7 @@ 84E78C842B58B5FB00D515EF /* RocketItem.hpp in Headers */, 84E78C882B58B66B00D515EF /* RocketLauncherTile.hpp in Headers */, 8477B3B72C4DC414004E1AC5 /* EmptyLevelChunk.hpp in Headers */, + 84358B1F2F4551500077EA44 /* CoalItem.hpp in Headers */, 84E1C9E82E7FDC72007D2F5D /* ClothItem.hpp in Headers */, 8470AF2B2BE9B60A00BCA54E /* EntityType.hpp in Headers */, 8445E7A42D769329008DC834 /* EntityTypeDescriptor.hpp in Headers */, @@ -6009,8 +6124,12 @@ 84B9D8A12F3BE0A900FD67C4 /* SeedItem.hpp in Headers */, 8470AF2D2BE9B60A00BCA54E /* MobFactory.hpp in Headers */, 8470AF392BE9B6D100BCA54E /* GameType.hpp in Headers */, + 84358B032F4550B30077EA44 /* TileEntity.hpp in Headers */, + 84358B042F4550B30077EA44 /* FurnaceTileEntity.hpp in Headers */, + 84358B052F4550B30077EA44 /* ChestTileEntity.hpp in Headers */, + 84358B062F4550B30077EA44 /* MusicTileEntity.hpp in Headers */, + 84358B072F4550B30077EA44 /* TileEntityType.hpp in Headers */, 8470AF3D2BE9B6FA00BCA54E /* FireworkParticle.hpp in Headers */, - 84E7BF302F286A20002D3936 /* CompoundContainer.hpp in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -6785,6 +6904,7 @@ 84B8AEFD2AF1896F008DE93D /* InvalidLicenseScreen.cpp in Sources */, 84B8AEFE2AF1896F008DE93D /* JoinGameScreen.cpp in Sources */, 844336FB2F405DCA00EB3115 /* VerticalLayout.cpp in Sources */, + 84358B2B2F45519B0077EA44 /* FurnaceScreen.cpp in Sources */, 84B8AEFF2AF1896F008DE93D /* OptionsScreen.cpp in Sources */, 84B8AF002AF1896F008DE93D /* PauseScreen.cpp in Sources */, 84B8AF012AF1896F008DE93D /* ProgressScreen.cpp in Sources */, @@ -6961,7 +7081,6 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 84E7BF322F286A20002D3936 /* ContainerListener.cpp in Sources */, 84BF630C2AF18631008A9995 /* Entity.cpp in Sources */, 84BF630D2AF18631008A9995 /* FallingTile.cpp in Sources */, 84BF630E2AF18631008A9995 /* ItemEntity.cpp in Sources */, @@ -6976,6 +7095,11 @@ 84BF63162AF18631008A9995 /* SurvivalMode.cpp in Sources */, 84BF63172AF18631008A9995 /* CameraItem.cpp in Sources */, 84DED1AF2F309611004001C5 /* ShapedRecipe.cpp in Sources */, + 84358AFE2F4550B30077EA44 /* TileEntityType.cpp in Sources */, + 84358AFF2F4550B30077EA44 /* ChestTileEntity.cpp in Sources */, + 84358B002F4550B30077EA44 /* FurnaceTileEntity.cpp in Sources */, + 84358B012F4550B30077EA44 /* TileEntity.cpp in Sources */, + 84358B022F4550B30077EA44 /* MusicTileEntity.cpp in Sources */, 84E1C9E52E7FDC72007D2F5D /* AuxTileItem.cpp in Sources */, 84E1C9D52E7FDC26007D2F5D /* GlowstoneTile.cpp in Sources */, 84BF63182AF18631008A9995 /* DoorItem.cpp in Sources */, @@ -6983,6 +7107,7 @@ 84B9D8A42F3BE0B200FD67C4 /* CropsTile.cpp in Sources */, 8445E7A22D769329008DC834 /* EntityType.cpp in Sources */, 84BF631A2AF18631008A9995 /* Item.cpp in Sources */, + 84358B1B2F4551290077EA44 /* Facing.cpp in Sources */, 84BF631C2AF18631008A9995 /* TileItem.cpp in Sources */, 84E1C9F02E7FDC89007D2F5D /* VegetationFeature.cpp in Sources */, 84BF631D2AF18631008A9995 /* TilePlanterItem.cpp in Sources */, @@ -6995,8 +7120,11 @@ 84BF63222AF18631008A9995 /* BiomeSource.cpp in Sources */, 84BF63232AF18631008A9995 /* ChunkCache.cpp in Sources */, 84E1C9CF2E7FDC26007D2F5D /* CactusTile.cpp in Sources */, + 84358B242F4551730077EA44 /* FurnaceResultSlot.cpp in Sources */, + 84358B252F4551730077EA44 /* FurnaceMenu.cpp in Sources */, 84E7BF1A2F286A08002D3936 /* ArmorSlot.cpp in Sources */, 84BF63242AF18631008A9995 /* ChunkSource.cpp in Sources */, + 84358B192F45511C0077EA44 /* NoteParticle.cpp in Sources */, 84BF63252AF18631008A9995 /* LevelChunk.cpp in Sources */, 84BF63262AF18631008A9995 /* PerformanceTestChunkSource.cpp in Sources */, 84EE51312E8DCC9000D3DCA2 /* DataLayer.cpp in Sources */, @@ -7026,8 +7154,11 @@ 84B9D89C2F3BE0A900FD67C4 /* ArmorItem.cpp in Sources */, 84B9D89D2F3BE0A900FD67C4 /* HoeItem.cpp in Sources */, 84B9D89E2F3BE0A900FD67C4 /* SeedItem.cpp in Sources */, + 84358B102F4550CC0077EA44 /* FurnaceTile.cpp in Sources */, + 84358B112F4550CC0077EA44 /* EntityTile.cpp in Sources */, + 84358B122F4550CC0077EA44 /* ChestTile.cpp in Sources */, + 84358B132F4550CC0077EA44 /* MusicTile.cpp in Sources */, 84BF63382AF18631008A9995 /* LevelListener.cpp in Sources */, - 84E7BF2F2F286A20002D3936 /* CompoundContainer.cpp in Sources */, 84BF63392AF18631008A9995 /* Material.cpp in Sources */, 84BF633A2AF18631008A9995 /* Region.cpp in Sources */, 84BF633B2AF18631008A9995 /* ChunkStorage.cpp in Sources */, @@ -7097,6 +7228,7 @@ 84BF63662AF18631008A9995 /* MetalTile.cpp in Sources */, 84BF63672AF18631008A9995 /* ObsidianTile.cpp in Sources */, 84CCBC932E61880600E251AF /* EntityFactory.cpp in Sources */, + 84358B1E2F4551500077EA44 /* CoalItem.cpp in Sources */, 84BF63682AF18631008A9995 /* OreTile.cpp in Sources */, 84B1E0392E04FD7900ED000A /* Arrow.cpp in Sources */, 84BF63692AF18631008A9995 /* RedStoneOreTile.cpp in Sources */, @@ -7140,6 +7272,10 @@ 84E78C7B2B58B3E000D515EF /* Rocket.cpp in Sources */, 84E78C832B58B5FB00D515EF /* RocketItem.cpp in Sources */, 845BBCB62F2EA81700C7232C /* ToolItem.cpp in Sources */, + 84F0DA7B2F45FAE100906960 /* ContainerContentChangeListener.cpp in Sources */, + 84F0DA7C2F45FAE100906960 /* CompoundContainer.cpp in Sources */, + 84F0DA7D2F45FAE100906960 /* ContainerListener.cpp in Sources */, + 84F0DA7E2F45FAE100906960 /* ContainerSizeChangeListener.cpp in Sources */, 84E78C872B58B66B00D515EF /* RocketLauncherTile.cpp in Sources */, 8470AF2C2BE9B60A00BCA54E /* MobFactory.cpp in Sources */, 84E1C9E92E7FDC72007D2F5D /* SlabItem.cpp in Sources */, diff --git a/platforms/sdl/sdl2/main.cpp b/platforms/sdl/sdl2/main.cpp index 3cbaabbe5..b7780d5bd 100644 --- a/platforms/sdl/sdl2/main.cpp +++ b/platforms/sdl/sdl2/main.cpp @@ -63,16 +63,11 @@ static void initGraphics() exit(EXIT_FAILURE); } - // Enable V-Sync - // Not setting this explicitly results in undefined behavior - if (SDL_GL_SetSwapInterval(-1) == -1) // Try adaptive + // Vsync is controlled through the AppPlatform, + // default to no vsync here, let platform set it when needed + if (SDL_GL_SetSwapInterval(0) == -1) { - LOG_W("Adaptive V-Sync is not supported on this platform. Falling back to standard V-Sync..."); - // fallback to standard - if (SDL_GL_SetSwapInterval(1) == -1) - { - LOG_W("Setting the swap interval for V-Sync is not supported on this platform!"); - } + LOG_W("Setting the swap interval is not supported on this platform!"); } if (!mce::Platform::OGL::InitBindings()) @@ -443,6 +438,7 @@ int main(int argc, char *argv[]) // Start MCPE g_pAppPlatform = new UsedAppPlatform(storagePath, window); g_pAppPlatform->m_externalStorageDir = storagePath; + g_pAppPlatform->setVSyncEnabled(true); g_pApp = new NinecraftApp; g_pApp->m_pPlatform = g_pAppPlatform; g_pApp->init(); diff --git a/platforms/windows/AppPlatform_win32.cpp b/platforms/windows/AppPlatform_win32.cpp index f54c64812..b177fef9a 100644 --- a/platforms/windows/AppPlatform_win32.cpp +++ b/platforms/windows/AppPlatform_win32.cpp @@ -484,6 +484,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..bac0f7eda 100644 --- a/platforms/windows/AppPlatform_win32.hpp +++ b/platforms/windows/AppPlatform_win32.hpp @@ -82,6 +82,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 00f703ea1..b614ca405 100644 --- a/platforms/windows/main.cpp +++ b/platforms/windows/main.cpp @@ -152,6 +152,8 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine if (!g_AppPlatform.initGraphics(Minecraft::width, Minecraft::height)) goto _cleanup; + g_AppPlatform.setVSyncEnabled(true); + g_pApp = new NinecraftApp; g_pApp->m_pPlatform = &g_AppPlatform; diff --git a/platforms/windows/projects/Client/Client.vcxproj b/platforms/windows/projects/Client/Client.vcxproj index c046d534d..8da4fa551 100644 --- a/platforms/windows/projects/Client/Client.vcxproj +++ b/platforms/windows/projects/Client/Client.vcxproj @@ -258,6 +258,7 @@ + @@ -434,6 +435,7 @@ + diff --git a/platforms/windows/projects/Client/Client.vcxproj.filters b/platforms/windows/projects/Client/Client.vcxproj.filters index 55cd0f467..35b451268 100644 --- a/platforms/windows/projects/Client/Client.vcxproj.filters +++ b/platforms/windows/projects/Client/Client.vcxproj.filters @@ -677,6 +677,9 @@ Header Files\GUI\Screens\Inventory + + Header Files\GUI\Screens\Inventory + @@ -1204,5 +1207,8 @@ Source Files\GUI\Screens\Inventory + + Source Files\GUI\Screens\Inventory + \ No newline at end of file diff --git a/platforms/windows/projects/World/World.vcxproj b/platforms/windows/projects/World/World.vcxproj index 8a8312eca..7b610602c 100644 --- a/platforms/windows/projects/World/World.vcxproj +++ b/platforms/windows/projects/World/World.vcxproj @@ -202,7 +202,7 @@ - + @@ -210,7 +210,7 @@ - + @@ -228,6 +228,22 @@ + + + + + + + + + + + + + + + + @@ -371,8 +387,8 @@ - - + + @@ -380,7 +396,7 @@ - + @@ -397,6 +413,20 @@ + + + + + + + + + + + + + + diff --git a/platforms/windows/projects/World/World.vcxproj.filters b/platforms/windows/projects/World/World.vcxproj.filters index 18a19e701..85317d225 100644 --- a/platforms/windows/projects/World/World.vcxproj.filters +++ b/platforms/windows/projects/World/World.vcxproj.filters @@ -105,6 +105,12 @@ {3a97a5e5-77e6-43eb-9890-3fa79b287cd1} + + {53363441-c079-4adf-a246-481be4b68199} + + + {9dca9e2a-e126-4598-a852-105be172d198} + @@ -581,9 +587,6 @@ Source Files\Level\LevelGen\Chunk - - Source Files - Source Files\Inventory @@ -608,9 +611,6 @@ Source Files\Inventory - - Source Files - Source Files\Inventory @@ -659,6 +659,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 + @@ -1084,12 +1138,6 @@ Header Files\Level - - Header Files - - - Header Files - Header Files\Inventory @@ -1111,9 +1159,6 @@ Header Files\Inventory - - Header Files - Header Files\Inventory @@ -1126,15 +1171,6 @@ Header Files\Item - - Header Files\Item - - - Header Files\Item - - - Header Files\Item - Header Files\Item @@ -1171,5 +1207,56 @@ Header Files\Item + + Header Files\Inventory + + + Header Files\Inventory + + + Header Files\Item + + + Header Files\Tile\Entity + + + Header Files\Tile\Entity + + + Header Files\Tile\Entity + + + Header Files\Tile\Entity + + + Header Files\Tile\Entity + + + Header Files\Tile + + + Header Files\Tile + + + Header Files\Tile + + + Header Files\Tile + + + Header Files\Inventory + + + Header Files\Inventory + + + Header Files\Inventory + + + Header Files\Inventory + + + Header Files\Inventory + \ No newline at end of file diff --git a/platforms/xdk360/AppPlatform_xdk360.cpp b/platforms/xdk360/AppPlatform_xdk360.cpp index a0fb29f2f..5ffba8025 100644 --- a/platforms/xdk360/AppPlatform_xdk360.cpp +++ b/platforms/xdk360/AppPlatform_xdk360.cpp @@ -337,6 +337,17 @@ const std::string& AppPlatform_xdk360::getKeyboardText() const return m_keyboardText; } +void AppPlatform_xdk360::setVSyncEnabled(bool enabled) +{ + // You have to reset/recreate the D3D device to change the vsync setting + // @TODO: Someone with a windows machine or xbox please do and test this. +} + +bool AppPlatform_xdk360::isVsyncSwitchable() const +{ + return false; +} + bool AppPlatform_xdk360::initGraphics(unsigned int width, unsigned int height) { m_bHasGraphics = true; diff --git a/platforms/xdk360/AppPlatform_xdk360.hpp b/platforms/xdk360/AppPlatform_xdk360.hpp index d1409aaff..d1fb3b859 100644 --- a/platforms/xdk360/AppPlatform_xdk360.hpp +++ b/platforms/xdk360/AppPlatform_xdk360.hpp @@ -52,6 +52,8 @@ class AppPlatform_xdk360 : public AppPlatform bool initGraphics(unsigned int width, unsigned int height); void createWindowSizeDependentResources(unsigned int width, unsigned int height); void swapBuffers(); + void setVSyncEnabled(bool enabled) override; + bool isVsyncSwitchable() const override; private: int m_ScreenWidth; diff --git a/platforms/xdk360/main.cpp b/platforms/xdk360/main.cpp index 6e932bce3..4353d31c7 100644 --- a/platforms/xdk360/main.cpp +++ b/platforms/xdk360/main.cpp @@ -53,6 +53,8 @@ void __cdecl main() if (!g_AppPlatform.initGraphics(Minecraft::width, Minecraft::height)) goto _cleanup; + g_AppPlatform.setVSyncEnabled(true); + g_pApp = new NinecraftApp; g_pApp->m_pPlatform = &g_AppPlatform; g_AppPlatform.m_externalStorageDir = "savedrive:"; diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index 1dff550b4..057f3ac44 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -124,8 +124,9 @@ add_library(reminecraftpe-core STATIC client/gui/screens/inventory/ContainerScreen.cpp client/gui/screens/inventory/InventoryScreen.cpp client/gui/screens/inventory/CraftingScreen.cpp - client/gui/screens/inventory/ClassicCraftingScreen_Console.cpp + client/gui/screens/inventory/FurnaceScreen.cpp client/gui/screens/inventory/ChestScreen.cpp + client/gui/screens/inventory/ClassicCraftingScreen_Console.cpp client/gui/components/ScrolledSelectionList.cpp client/gui/components/AvailableGamesList.cpp client/gui/components/RolledSelectionList.cpp @@ -352,24 +353,29 @@ add_library(reminecraftpe-core STATIC world/item/TilePlanterItem.cpp world/item/CameraItem.cpp world/item/TileItem.cpp + world/item/CoalItem.cpp world/item/Inventory.cpp world/item/crafting/Recipes.cpp world/item/crafting/FurnaceRecipes.cpp 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 @@ -388,6 +394,7 @@ add_library(reminecraftpe-core STATIC world/item/SlabItem.cpp world/particle/RedDustParticle.cpp world/particle/TerrainParticle.cpp + world/particle/NoteParticle.cpp world/particle/BubbleParticle.cpp world/particle/ExplodeParticle.cpp world/particle/ParticleEngine.cpp @@ -423,6 +430,10 @@ add_library(reminecraftpe-core STATIC world/tile/OreTile.cpp world/tile/StairTile.cpp world/tile/SandStoneTile.cpp + world/tile/EntityTile.cpp + world/tile/ChestTile.cpp + world/tile/FurnaceTile.cpp + world/tile/MusicTile.cpp world/tile/FireTile.cpp world/tile/StoneSlabTile.cpp world/tile/LiquidTile.cpp @@ -444,6 +455,12 @@ add_library(reminecraftpe-core STATIC world/tile/Web.cpp world/tile/FenceTile.cpp world/tile/CraftingTableTile.cpp + world/tile/entity/TileEntity.cpp + world/tile/entity/TileEntityType.cpp + world/tile/entity/ChestTileEntity.cpp + world/tile/entity/FurnaceTileEntity.cpp + world/tile/entity/MusicTileEntity.cpp + world/Facing.cpp renderer/GL/GL.cpp renderer/Attribute.cpp renderer/ConstantBufferMetaData.cpp diff --git a/source/client/app/AppPlatform.cpp b/source/client/app/AppPlatform.cpp index ef28f0f4b..7eba59212 100644 --- a/source/client/app/AppPlatform.cpp +++ b/source/client/app/AppPlatform.cpp @@ -320,6 +320,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 9cf1b6cc6..b7852bd21 100644 --- a/source/client/app/AppPlatform.hpp +++ b/source/client/app/AppPlatform.hpp @@ -107,6 +107,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 3b7cf56e9..ab0768fc3 100644 --- a/source/client/app/Minecraft.cpp +++ b/source/client/app/Minecraft.cpp @@ -173,12 +173,12 @@ GameMode* Minecraft::_createGameMode(GameType gameType, Level& level) } } -void Minecraft::_reloadInput() +void Minecraft::reloadInput() { if (m_pInputHolder) delete m_pInputHolder; - if (isTouchscreen()) + if (useTouchscreen()) { m_pInputHolder = new TouchInputHolder(this, getOptions()); } @@ -206,7 +206,7 @@ void Minecraft::_reloadInput() m_pLocalPlayer->m_pMoveInput = m_pInputHolder->getMoveInput(); } - getOptions()->field_19 = !isTouchscreen(); + getOptions()->m_bUseMouseToBreak = !useTouchscreen(); } int Minecraft::getLicenseId() @@ -246,7 +246,7 @@ void Minecraft::grabMouse() // This will call grabMouse again, so why are we calling it here? //setScreen(nullptr); - if (useController() || isTouchscreen()) + if (useController() || useTouchscreen()) return; // don't actually try to grab the mouse platform()->setMouseGrabbed(true); @@ -254,7 +254,7 @@ void Minecraft::grabMouse() void Minecraft::recenterMouse() { - if (useController() || isTouchscreen()) + if (useController() || useTouchscreen()) return; platform()->recenterMouse(); @@ -364,9 +364,14 @@ bool Minecraft::isTouchscreen() const return m_bIsTouchscreen; } +bool Minecraft::useTouchscreen() const +{ + return isTouchscreen() && !getOptions()->m_bUseController.get(); +} + bool Minecraft::useSplitControls() const { - return !m_bIsTouchscreen || getOptions()->m_splitControls.get(); + return !useTouchscreen() || getOptions()->m_splitControls.get(); } bool Minecraft::useController() const @@ -625,7 +630,7 @@ void Minecraft::tickInput() #endif } - if (getOptions()->field_19) + if (!getOptions()->m_bUseMouseToBreak) continue; // @TODO: Replace with KeyboardBuildInput @@ -672,7 +677,7 @@ void Minecraft::tickMouse() * This is exactly what Minecraft Java does too **/ - if (useController() || isTouchscreen()) + if (useController() || useTouchscreen()) return; // don't actually try to recenter the mouse if (platform()->getRecenterMouseEveryTick()) // just for SDL1 diff --git a/source/client/app/Minecraft.hpp b/source/client/app/Minecraft.hpp index edd594852..6a112aaad 100644 --- a/source/client/app/Minecraft.hpp +++ b/source/client/app/Minecraft.hpp @@ -44,9 +44,6 @@ class Minecraft : public App void _resetPlayer(Player* player); GameMode* _createGameMode(GameType gameType, Level& level); -protected: - void _reloadInput(); - public: int getLicenseId(); void setScreen(Screen * pScreen); @@ -79,11 +76,13 @@ class Minecraft : public App void handlePointerPressedButtonRelease(); void handleKeyboardClosed(); void resetInput(); + void reloadInput(); void sendMessage(const std::string& message); void respawnPlayer(); void freeResources(bool bCopyMap); std::string getVersionString(const std::string& str = Util::EMPTY_STRING) const; bool isTouchscreen() const; + bool useTouchscreen() const; bool useSplitControls() const; bool useController() const; @@ -118,6 +117,7 @@ class Minecraft : public App private: // Value provided by the OS static float _renderScaleMultiplier; + public: static float getRenderScaleMultiplier() { return _renderScaleMultiplier; } static void setRenderScaleMultiplier(float value) { _renderScaleMultiplier = value; } diff --git a/source/client/app/NinecraftApp.cpp b/source/client/app/NinecraftApp.cpp index 1c7bd839c..3c9fa61f4 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 38ccbf65c..48d187826 100644 --- a/source/client/gui/Gui.cpp +++ b/source/client/gui/Gui.cpp @@ -260,8 +260,6 @@ void Gui::renderSlot(int slot, int x, int y, float f) ItemRenderer::singleton().renderGuiItem(m_pMinecraft->m_pFont, m_pMinecraft->m_pTextures, item, x, y, true); } - - //ItemRenderer::renderGuiItemDecorations(m_pMinecraft->m_pFont, m_pMinecraft->m_pTextures, item, x, y); } void Gui::renderSlotOverlay(int slot, int x, int y, float f) @@ -335,7 +333,8 @@ void Gui::handleClick(int clickID, int mouseX, int mouseY) if (slot == -1) return; - if (m_pMinecraft->isTouchscreen() && slot == getNumSlots() - 1) + // Final slot on touch opens inventory + if (m_pMinecraft->useTouchscreen() && slot == getNumSlots() - 1) { if (m_pMinecraft->m_pGameMode->isSurvivalType()) m_pMinecraft->setScreen(new InventoryScreen(m_pMinecraft->m_pLocalPlayer)); @@ -384,7 +383,7 @@ void Gui::handleKeyPressed(int keyCode) if (slotL || slotR) { int maxItems = getNumSlots() - 1; - if (m_pMinecraft->isTouchscreen()) + if (m_pMinecraft->useTouchscreen()) maxItems--; SlotID* slot = &m_pMinecraft->m_pLocalPlayer->m_pInventory->m_selectedSlot; @@ -706,7 +705,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 +727,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 +746,7 @@ int Gui::getNumSlots() int Gui::getNumUsableSlots() { - return getNumSlots() - m_pMinecraft->isTouchscreen(); + return getNumSlots() - m_pMinecraft->useTouchscreen(); } RectangleArea Gui::getRectangleArea(bool b) diff --git a/source/client/gui/Gui.hpp b/source/client/gui/Gui.hpp index 9957d2f65..156f13788 100644 --- a/source/client/gui/Gui.hpp +++ b/source/client/gui/Gui.hpp @@ -42,6 +42,7 @@ class Gui : public GuiComponent private: static bool _isVignetteAvailable; + public: static bool isVignetteAvailable() { return _isVignetteAvailable; } static void setIsVignetteAvailable(bool value) { _isVignetteAvailable = value; } diff --git a/source/client/gui/Screen.cpp b/source/client/gui/Screen.cpp index 230a85921..7aec2e65e 100644 --- a/source/client/gui/Screen.cpp +++ b/source/client/gui/Screen.cpp @@ -393,7 +393,7 @@ void Screen::pointerPressed(const MenuPointer& pointer, MouseButtonType btn) // { m_pClickedElement = element; - if (!m_pMinecraft->isTouchscreen()) + if (!m_pMinecraft->useTouchscreen()) { if (_useController()) m_pMinecraft->m_pSoundEngine->playUI(C_SOUND_UI_PRESS); diff --git a/source/client/gui/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..26e7afa1f 100644 --- a/source/client/gui/components/OptionList.cpp +++ b/source/client/gui/components/OptionList.cpp @@ -173,7 +173,8 @@ void OptionList::initControlsMenu() if (!m_pMinecraft->isTouchscreen()) m_items[idxSplit]->setEnabled(false); - m_items[idxController]->setEnabled(false); + if (!m_pMinecraft->m_pPlatform->hasGamepad()) + m_items[idxController]->setEnabled(false); } void OptionList::initVideoMenu() diff --git a/source/client/gui/components/TextBox.cpp b/source/client/gui/components/TextBox.cpp index f18371498..36d64344d 100644 --- a/source/client/gui/components/TextBox.cpp +++ b/source/client/gui/components/TextBox.cpp @@ -149,7 +149,8 @@ bool TextBox::pointerPressed(Minecraft* pMinecraft, const MenuPointer& pointer) #ifndef HANDLE_CHARS_SEPARATELY -char TextBox::guessCharFromKey(int key) { +char TextBox::guessCharFromKey(int key) +{ bool bShiftPressed = m_pParent->m_pMinecraft->platform()->shiftPressed(); char chr = '\0'; if (key >= AKEYCODE_A && key <= AKEYCODE_Z) @@ -218,13 +219,15 @@ void TextBox::handleButtonPress(Minecraft* pMinecraft, int key) #ifndef HANDLE_CHARS_SEPARATELY char guess = guessCharFromKey(key); - if (guess != '\0') { + if (guess != '\0') + { handleTextChar(guess); return; } #endif - switch (key) { + switch (key) + { case AKEYCODE_DEL: { // handled elsewhere, do not dupe @@ -298,7 +301,8 @@ void TextBox::handleTextChar(Minecraft* pMinecraft, int k) if (!hasFocus()) return; - switch (k) { + switch (k) + { case '\b': // BACKSPACE case '\x7f': // DELETE { @@ -546,10 +550,13 @@ void TextBox::recalculateScroll() } else { - if (m_scrollPos == int(m_text.length())) { + if (m_scrollPos == int(m_text.length())) + { LOG_W("Text Box Is Too Small"); break; - } else { + } + else + { m_scrollPos++; } } diff --git a/source/client/gui/screens/inventory/ChestScreen.cpp b/source/client/gui/screens/inventory/ChestScreen.cpp index 312618638..2df77fff9 100644 --- a/source/client/gui/screens/inventory/ChestScreen.cpp +++ b/source/client/gui/screens/inventory/ChestScreen.cpp @@ -31,15 +31,14 @@ SlotDisplay ChestScreen::_createSlotDisplay(const Slot& slot) { constexpr int slotSize = 18; int rows = m_pContainer->getContainerSize() / 9; - int verticalOffset = (rows - 4) * slotSize; switch (slot.m_group) { case Slot::CONTAINER: - return SlotDisplay(8 + (slot.m_slot % 9) * slotSize, 18 + verticalOffset + ((slot.m_slot / 9) - 1) * slotSize, slotSize); + return SlotDisplay(8 + (slot.m_slot % 9) * slotSize, 18 + (slot.m_slot / 9) * slotSize, slotSize); case Slot::INVENTORY: - return SlotDisplay(8 + (slot.m_slot % 9) * slotSize, 103 + verticalOffset + ((slot.m_slot / 9) - 1) * slotSize, slotSize); + return SlotDisplay(8 + (slot.m_slot % 9) * slotSize, (rows * 18) + 13 + (slot.m_slot / 9) * slotSize, slotSize); case Slot::HOTBAR: - return SlotDisplay(8 + (slot.m_slot % 9) * slotSize, 142, slotSize); + return SlotDisplay(8 + (slot.m_slot % 9) * slotSize, 89 + rows * slotSize, slotSize); default: return SlotDisplay(); } diff --git a/source/client/gui/screens/inventory/ContainerScreen.cpp b/source/client/gui/screens/inventory/ContainerScreen.cpp index 991e74357..98d0b447d 100644 --- a/source/client/gui/screens/inventory/ContainerScreen.cpp +++ b/source/client/gui/screens/inventory/ContainerScreen.cpp @@ -234,14 +234,14 @@ void ContainerScreen::render(float partialTicks) void ContainerScreen::pointerPressed(const MenuPointer& pointer, MouseButtonType button) { Screen::pointerPressed(pointer, button); - if (m_pMinecraft->isTouchscreen()) return; + if (m_pMinecraft->useTouchscreen()) return; slotClicked(pointer, button); } void ContainerScreen::pointerReleased(const MenuPointer& pointer, MouseButtonType button) { Screen::pointerReleased(pointer, button); - if (m_pMinecraft->isTouchscreen() && m_timeSlotDragged < 5) + if (m_pMinecraft->useTouchscreen() && m_timeSlotDragged < 5) slotClicked(pointer, button); m_timeSlotDragged = 0; } @@ -253,7 +253,7 @@ void ContainerScreen::handlePointerPressed(bool isPressed) m_timeSlotDragged++; else m_timeSlotDragged = 0; - if (m_pMinecraft->isTouchscreen() && m_timeSlotDragged % 5 == 0) + if (m_pMinecraft->useTouchscreen() && m_timeSlotDragged % 5 == 0) { slotClicked(m_menuPointer, MOUSE_BUTTON_RIGHT); } diff --git a/source/client/gui/screens/inventory/FurnaceScreen.cpp b/source/client/gui/screens/inventory/FurnaceScreen.cpp new file mode 100644 index 000000000..b28b6d538 --- /dev/null +++ b/source/client/gui/screens/inventory/FurnaceScreen.cpp @@ -0,0 +1,57 @@ +#include "FurnaceScreen.hpp" +#include "world/inventory/FurnaceMenu.hpp" +#include "renderer/ShaderConstants.hpp" + +FurnaceScreen::FurnaceScreen(Inventory* inventory, FurnaceTileEntity* container) + : ContainerScreen(new FurnaceMenu(inventory, container)), m_inventory(inventory), m_furnace(container) +{ + m_uiTheme = UI_JAVA; +} + +void FurnaceScreen::tick() +{ + ContainerScreen::tick(); +} + +void FurnaceScreen::_renderLabels() +{ + m_pFont->draw(m_furnace->getName(), 66, 6, 0x404040); + m_pFont->draw(m_inventory->getName(), 8, m_imageHeight - 96 + 2, 0x404040); +} + +void FurnaceScreen::_renderBg(float a) +{ + currentShaderColor = Color::WHITE; + + m_pMinecraft->m_pTextures->loadAndBindTexture("gui/furnace.png"); + + blit(m_leftPos, m_topPos, 0, 0, m_imageWidth, m_imageHeight, 0, 0); + + int p; + if (m_furnace->isLit()) + { + p = m_furnace->getLitProgress(12); + blit(m_leftPos + 56, m_topPos + 36 + 12 - p, 176, 12 - p, 14, p + 2, 0, 0); + } + + p = m_furnace->getBurnProgress(24); + blit(m_leftPos + 79, m_topPos + 34, 176, 14, p + 1, 16, 0, 0); +} + +SlotDisplay FurnaceScreen::_createSlotDisplay(const Slot& slot) +{ + constexpr int slotSize = 18; + switch (slot.m_group) + { + case Slot::INPUT: + return SlotDisplay(56, 17 + (slot.m_slot % 2) * (slotSize * 2)); + case Slot::OUTPUT: + return SlotDisplay(116, 35); + case Slot::INVENTORY: + return SlotDisplay(8 + (slot.m_slot % 9) * slotSize, 84 + ((slot.m_slot / 9) - 1) * slotSize, slotSize); + case Slot::HOTBAR: + return SlotDisplay(8 + (slot.m_slot % 9) * slotSize, 142, slotSize); + default: + return SlotDisplay(); + } +} \ No newline at end of file diff --git a/source/client/gui/screens/inventory/FurnaceScreen.hpp b/source/client/gui/screens/inventory/FurnaceScreen.hpp new file mode 100644 index 000000000..5b84e36d3 --- /dev/null +++ b/source/client/gui/screens/inventory/FurnaceScreen.hpp @@ -0,0 +1,20 @@ +#pragma once + +#include "ContainerScreen.hpp" +#include "world/tile/entity/FurnaceTileEntity.hpp" + +class FurnaceScreen : public ContainerScreen +{ +public: + FurnaceScreen(Inventory* inventory, FurnaceTileEntity* container); + void tick() override; + +protected: + void _renderLabels() override; + void _renderBg(float a) override; + SlotDisplay _createSlotDisplay(const Slot&) override; + +private: + Inventory* m_inventory; + FurnaceTileEntity* m_furnace; +}; diff --git a/source/client/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 85742cbbf..772734bf5 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 @@ -133,7 +141,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, int index, Slot* slot, ItemStack& item, bool isResultSlot) { - // @TODO: Replicate ContainerSetSlotPacket +#if NETWORK_PROTOCOL_VERSION >= 5 + m_pMinecraft->m_pRakNetInstance->send(new ContainerSetSlotPacket(m_pContainerMenu->m_containerId, index, item)); +#endif } diff --git a/source/client/multiplayer/MultiplayerLocalPlayer.hpp b/source/client/multiplayer/MultiplayerLocalPlayer.hpp index d1a7e2bec..fffd41ec5 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, int index, 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 603847af5..5079976d5 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 @@ -729,7 +730,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 +777,9 @@ void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& guid, ContainerS if (pContainerMenu->m_containerId != packet->m_containerId) return; + pContainerMenu->m_bBroadcastChanges = false; pContainerMenu->setItem(packet->m_slot, packet->m_item); + pContainerMenu->m_bBroadcastChanges = true; } void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& guid, ContainerSetDataPacket* packet) @@ -797,7 +800,9 @@ void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& guid, ContainerS if (pContainerMenu->m_containerId != packet->m_containerId) return; + pContainerMenu->m_bBroadcastChanges = false; pContainerMenu->setData(packet->m_slot, packet->m_value); + pContainerMenu->m_bBroadcastChanges = true; } void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& guid, ContainerSetContentPacket* packet) @@ -818,7 +823,9 @@ void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& guid, ContainerS if (pContainerMenu->m_containerId != packet->m_containerId) return; + pContainerMenu->m_bBroadcastChanges = false; pContainerMenu->setAll(packet->m_items); + pContainerMenu->m_bBroadcastChanges = true; } void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& guid, LevelDataPacket* packet) diff --git a/source/client/options/Options.cpp b/source/client/options/Options.cpp index 4aea60bbc..3b5f082d3 100644 --- a/source/client/options/Options.cpp +++ b/source/client/options/Options.cpp @@ -41,11 +41,11 @@ void Options::_initDefaultValues() field_16 = 0; field_240 = 1; field_1C = "Default"; - field_19 = 1; + m_bUseMouseToBreak = true; #ifdef ORIGINAL_CODE m_viewDistance.set(2); m_thirdPerson.set(0); - field_19 = 0; + m_bUseMouseToBreak = false; #endif // Force this on until we get a proper UI @@ -59,7 +59,7 @@ static UITheme GetDefaultUiTheme(Minecraft* mc) #if MC_PLATFORM_XBOX360 return UI_CONSOLE; #else - return mc->platform()->isTouchscreen() ? UI_POCKET : UI_JAVA; + return (mc->useTouchscreen() || mc->isTouchscreen()) ? UI_POCKET : UI_JAVA; #endif } @@ -98,7 +98,7 @@ Options::Options(Minecraft* mc, const std::string& folderPath) : //, m_limitFramerate("gfx_fpslimit", "options.framerateLimit", 0, ValuesBuilder().add(performance.max").add("performance.balanced").add("performance.powersaver")) //, m_bMipmaps("gfx_mipmaps", "options.mipmaps") //, m_moreWorldOptions("misc_moreworldoptions", "options.moreWorldOptions", true) - //, m_vSync("enableVsync", "options.enableVsync") + , m_vSync("enableVsync", "options.enableVsync", true) { add(m_musicVolume); add(m_masterVolume); @@ -126,10 +126,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"; @@ -674,6 +676,9 @@ void Options::initResourceDependentOptions() if (!Screen::isMenuPanoramaAvailable()) m_menuPanorama.set(false); + + if (!m_pMinecraft->platform()->isVsyncSwitchable()) + m_vSync.set(false); } const std::string& OptionEntry::getDisplayName() const @@ -803,3 +808,17 @@ void UIThemeOption::apply() m_pMinecraft->getOptions()->m_logoType.apply(); } } + +void ControllerOption::apply() +{ + // @TODO: This works but ultimately needs to be a multi-select (KBM, controller, touch) instead of a single option. + // Either that or figure out an automatic way to switch between them based on input like how Minecraft Bedrock does + // For now, I just wanted to be able to switch to controller input on mobile devices. + if (m_pMinecraft && m_pMinecraft->m_pInputHolder) + m_pMinecraft->reloadInput(); +} + +void VsyncOption::apply() +{ + m_pMinecraft->platform()->setVSyncEnabled(get()); +} diff --git a/source/client/options/Options.hpp b/source/client/options/Options.hpp index 99bacdf1a..b15eb695b 100644 --- a/source/client/options/Options.hpp +++ b/source/client/options/Options.hpp @@ -214,6 +214,22 @@ class GraphicsOption : public BoolOption void apply() override; }; +class ControllerOption : public BoolOption +{ +public: + ControllerOption(const std::string& key, const std::string& name, bool initial = true) : BoolOption(key, name, initial) {} + + void apply() override; +}; + +class VsyncOption : public BoolOption +{ +public: + VsyncOption(const std::string& key, const std::string& name, bool initial = true) : BoolOption(key, name, initial) {} + + void apply() override; +}; + class FancyGraphicsOption : public GraphicsOption { public: @@ -329,6 +345,7 @@ class Options { public: struct KeyBind; + private: static bool _hasResourcePack(const ResourcePack& pack, ResourcePackStack& packs); static void _tryAddResourcePack(const std::string& name, ResourcePackStack& packs); @@ -358,6 +375,7 @@ class Options void _initDefaultValues(); void _load(); AsyncTask _saveAsync(); + public: Options(Minecraft*, const std::string& folderPath = ""); @@ -399,7 +417,7 @@ class Options uint8_t field_16; FancyGraphicsOption m_fancyGraphics; AOOption m_ambientOcclusion; - uint8_t field_19; // use Mouse as input for breaking + bool m_bUseMouseToBreak; std::string field_1C; ValuesOption m_difficulty; BoolOption m_hideGui; @@ -419,7 +437,7 @@ class Options GraphicsOption m_fancyGrass; GraphicsOption m_biomeColors; BoolOption m_splitControls; - BoolOption m_bUseController; + ControllerOption m_bUseController; BoolOption m_dynamicHand; BoolOption m_menuPanorama; GuiScaleOption m_guiScale; @@ -428,6 +446,7 @@ class Options LogoTypeOption m_logoType; HUDSizeOption m_hudSize; BoolOption m_classicCrafting; + VsyncOption m_vSync; ResourcePackStack m_resourcePacks; }; @@ -473,6 +492,7 @@ class Options OPTION(m_viewBobbing); \ OPTION(m_anaglyphs); \ OPTION(m_blockOutlines); \ + OPTION(m_vSync); \ OPTION(m_fancyGrass); \ OPTION(m_biomeColors); \ OPTION(m_dynamicHand); \ diff --git a/source/client/player/LocalPlayer.cpp b/source/client/player/LocalPlayer.cpp index ec62ed15b..787d65806 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; @@ -132,18 +133,24 @@ void LocalPlayer::swing() void LocalPlayer::startCrafting(const TilePos& pos) { m_pMinecraft->getScreenChooser()->pushCraftingScreen(this, pos); + + Player::startCrafting(pos); } -/*void LocalPlayer::openFurnace(FurnaceTileEntity* furnace) +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 +163,15 @@ void LocalPlayer::closeContainer() /*void LocalPlayer::openTrap(DispenserTileEntity* tileEntity) { m_pMinecraft->setScreen(new TrapScreen(m_pInventory, tileEntity)); + + Player::openTrap(tileEntity); }*/ /*void LocalPlayer::openTextEdit(SignTileEntity* tileEntity) { m_pMinecraft->setScreen(new TextEditScreen(tileEntity)); + + Player::openTextEdit(tileEntity); }*/ void LocalPlayer::reset() diff --git a/source/client/player/LocalPlayer.hpp b/source/client/player/LocalPlayer.hpp index 1eecb1288..076c5abe1 100644 --- a/source/client/player/LocalPlayer.hpp +++ b/source/client/player/LocalPlayer.hpp @@ -39,7 +39,7 @@ class LocalPlayer : public Player void setPlayerGameType(GameType gameType) override; void swing() override; void startCrafting(const TilePos&) override; - //void openFurnace(FurnaceTileEntity* furnace) override; + void openFurnace(FurnaceTileEntity* furnace) override; void openContainer(Container* container) override; void closeContainer() override; //void openTrap(DispenserTileEntity* tileEntity) override; diff --git a/source/client/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..aed0ab5c7 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,45 @@ void Chunk::rebuild() break; } + 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 d71fd3d4e..c265d6b5e 100644 --- a/source/client/renderer/GameRenderer.cpp +++ b/source/client/renderer/GameRenderer.cpp @@ -643,7 +643,7 @@ void GameRenderer::render(const Timer& timer) int mouseY = -9999; bool bMouseData = false; - if (m_pMinecraft->isTouchscreen()) + if (m_pMinecraft->useTouchscreen()) { int pointerId = Multitouch::getFirstActivePointerIdExThisUpdate(); if (pointerId >= 0) diff --git a/source/client/renderer/LevelRenderer.cpp b/source/client/renderer/LevelRenderer.cpp index c7202fe54..a8dc66049 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) @@ -1137,7 +1137,7 @@ void LevelRenderer::tick() typedef std::vector ChunkVector; typedef ChunkVector::iterator ChunkVectorIterator; -bool LevelRenderer::updateDirtyChunks(const Entity& camera, bool b) +bool LevelRenderer::updateDirtyChunks(const Entity& camera, bool force) { constexpr int C_MAX = 3; DirtyChunkSorter dcs(camera); @@ -1148,7 +1148,7 @@ bool LevelRenderer::updateDirtyChunks(const Entity& camera, bool b) for (size_t i = 0; i < pendingChunkSize; i++) { Chunk* pChunk = m_dirtyChunks[i]; - if (!b) + if (!force) { if (pChunk->distanceToSqr(camera) > 1024.0f) { @@ -1163,7 +1163,8 @@ bool LevelRenderer::updateDirtyChunks(const Entity& camera, bool b) if (--j <= 0) continue; - for (int k = j; --k != 0;) { + for (int k = j; --k != 0;) + { pChunks[k - 1] = pChunks[k]; } @@ -1421,6 +1422,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 +1584,12 @@ void LevelRenderer::renderEntities(Vec3 pos, Culler* culler, float f) EntityRenderDispatcher::off = camera->m_posPrev + (camera->m_pos - camera->m_posPrev) * f; - const EntityVector* pVec = m_pLevel->getAllEntities(); + const EntityMap* pVec = m_pLevel->getAllEntities(); m_totalEntities = int(pVec->size()); - for (int i = 0; i < m_totalEntities; i++) - { - const Entity* entity = (*pVec)[i]; + for (EntityMap::const_iterator it = pVec->begin(); it != pVec->end(); ++it) + { + const Entity* entity = it->second; if (!entity->shouldRender(pos)) continue; @@ -1599,6 +1605,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 c5dac8dc3..e9cdbbfeb 100644 --- a/source/client/renderer/LogoRenderer.cpp +++ b/source/client/renderer/LogoRenderer.cpp @@ -414,8 +414,10 @@ Tile* TitleTile::getRandomTile(Tile* except1, Tile* except2) for (;;) { id = _random.nextInt(256); - for (int i = 0; i < _tileBlockListSize; i++) { - if (_tileBlockList[i] == id) { + for (int i = 0; i < _tileBlockListSize; i++) + { + if (_tileBlockList[i] == id) + { // N.B. Air does not have a tile id = TILE_AIR; break; diff --git a/source/client/renderer/TileRenderer.cpp b/source/client/renderer/TileRenderer.cpp index 575742689..026c42d68 100644 --- a/source/client/renderer/TileRenderer.cpp +++ b/source/client/renderer/TileRenderer.cpp @@ -204,7 +204,7 @@ void TileRenderer::renderEast(Tile* tile, const Vec3& pos, int texture) texV_d = C_RATIO * (texY + aabb.max.y * 16.0f - 0.01f); } - Tesselator& t = Tesselator::instance; + Tesselator& t = m_tessellator; if (m_ambientOcclusion) { @@ -281,7 +281,7 @@ void TileRenderer::renderWest(Tile* tile, const Vec3& pos, int texture) texV_d = C_RATIO * (texY + aabb.max.y * 16.0f - 0.01f); } - Tesselator& t = Tesselator::instance; + Tesselator& t = m_tessellator; if (m_ambientOcclusion) { @@ -358,7 +358,7 @@ void TileRenderer::renderSouth(Tile* tile, const Vec3& pos, int texture) texV_d = C_RATIO * (texY + aabb.max.y * 16.0f - 0.01f); } - Tesselator& t = Tesselator::instance; + Tesselator& t = m_tessellator; if (m_ambientOcclusion) { @@ -435,7 +435,7 @@ void TileRenderer::renderNorth(Tile* tile, const Vec3& pos, int texture) texV_d = C_RATIO * (texY + aabb.max.y * 16.0f - 0.01f); } - Tesselator& t = Tesselator::instance; + Tesselator& t = m_tessellator; if (m_ambientOcclusion) { @@ -506,7 +506,7 @@ void TileRenderer::renderFaceDown(Tile* tile, const Vec3& pos, int texture) texV_2 = C_RATIO * (texY + 15.99f); } - Tesselator& t = Tesselator::instance; + Tesselator& t = m_tessellator; if (m_ambientOcclusion) { @@ -577,7 +577,7 @@ void TileRenderer::renderFaceUp(Tile* tile, const Vec3& pos, int texture) texV_2 = C_RATIO * (texY + 15.99f); } - Tesselator& t = Tesselator::instance; + Tesselator& t = m_tessellator; if (m_ambientOcclusion) { @@ -641,7 +641,7 @@ void TileRenderer::tesselateCrossTexture(const FullTile& tile, const Vec3& pos, float x1 = cenX - 0.45f, x2 = cenX + 0.45f; float z1 = cenZ - 0.45f, z2 = cenZ + 0.45f; - Tesselator& t = Tesselator::instance; + Tesselator& t = m_tessellator; // face 1 t.vertexUV(x1, newY + 1, z1, texU_l, texV_u); t.vertexUV(x1, newY + 0, z1, texU_l, texV_d); @@ -693,7 +693,7 @@ void TileRenderer::tesselateRowTexture(Tile* tile, int data, const Vec3& pos) float x0 = pos.x + 0.25f, x1 = pos.x + 0.75f; float z0 = pos.z, z1 = pos.z + 1.0f; - Tesselator& t = Tesselator::instance; + Tesselator& t = m_tessellator; t.vertexUV(x0, pos.y + 1.0, z0, u0, v0); t.vertexUV(x0, pos.y + 0.0, z0, u0, v1); t.vertexUV(x0, pos.y + 0.0, z1, u1, v1); @@ -739,7 +739,7 @@ bool TileRenderer::tesselateBlockInWorld(Tile* tile, const TilePos& pos, float r if (tile == Tile::grass) r = g = b = 1.0f; - Tesselator& t = Tesselator::instance; + Tesselator& t = m_tessellator; float fLightHere = tile->getBrightness(m_pTileSource, pos); bool bDrewAnything = false; @@ -871,7 +871,7 @@ bool TileRenderer::tesselateBlockInWorld(Tile* tile, const TilePos& pos) bool TileRenderer::tesselateCrossInWorld(Tile* tile, const TilePos& pos) { - Tesselator& t = Tesselator::instance; + Tesselator& t = m_tessellator; float bright = tile->getBrightness(m_pTileSource, pos); int color = getTileColor(tile, pos); @@ -888,7 +888,7 @@ bool TileRenderer::tesselateCrossInWorld(Tile* tile, const TilePos& pos) bool TileRenderer::tesselateRowInWorld(Tile* tile, const TilePos& pos) { - Tesselator& t = Tesselator::instance; + Tesselator& t = m_tessellator; Color color = getTileColor(tile, pos); color.a = 1.0f; @@ -906,7 +906,7 @@ bool TileRenderer::tesselateWaterInWorld(Tile* tile1, const TilePos& pos) LiquidTile* tile = (LiquidTile*)tile1; bool bRenderFaceDown, bRenderFaceUp, bRenderSides[4]; - Tesselator& t = Tesselator::instance; + Tesselator& t = m_tessellator; bRenderFaceDown = tile->shouldRenderFace(m_pTileSource, pos.above(), Facing::UP); bRenderFaceUp = tile->shouldRenderFace(m_pTileSource, pos.below(), Facing::DOWN); @@ -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/HumanoidMobRenderer.cpp b/source/client/renderer/entity/HumanoidMobRenderer.cpp index c448e50c7..31f1d6c26 100644 --- a/source/client/renderer/entity/HumanoidMobRenderer.cpp +++ b/source/client/renderer/entity/HumanoidMobRenderer.cpp @@ -203,3 +203,9 @@ void HumanoidMobRenderer::renderHand(const Entity& entity, float a) } #endif } + +void HumanoidMobRenderer::scale(const Mob &mob, Matrix &matrix, float a) +{ + // players are actually 15/16ths the size than any other bipedal mob + matrix.scale((mob.isPlayer()) ? (15.0f / 16.0f) : 1.0f); +} diff --git a/source/client/renderer/entity/HumanoidMobRenderer.hpp b/source/client/renderer/entity/HumanoidMobRenderer.hpp index bcb072c89..62b900098 100644 --- a/source/client/renderer/entity/HumanoidMobRenderer.hpp +++ b/source/client/renderer/entity/HumanoidMobRenderer.hpp @@ -21,6 +21,7 @@ class HumanoidMobRenderer : public MobRenderer virtual void onGraphicsReset() override; void renderHand(const Entity& entity, float a); + void scale(const Mob& mob, Matrix& matrix, float a); public: HumanoidModel* m_pHumanoidModel; diff --git a/source/client/renderer/entity/ItemRenderer.cpp b/source/client/renderer/entity/ItemRenderer.cpp index aa3d9973f..e55794ba5 100644 --- a/source/client/renderer/entity/ItemRenderer.cpp +++ b/source/client/renderer/entity/ItemRenderer.cpp @@ -192,7 +192,8 @@ void ItemRenderer::renderGuiItemOverlay(Font* font, Textures* textures, ItemStac return; // Draw damage amount - if (item.isDamaged()) { + if (item.isDamaged()) + { int duraWidth = ceilf(13.0f - static_cast(item.getDamageValue()) * 13.0f / static_cast(item.getMaxDamage())); int duraPercent = ceilf(255.0f - static_cast(item.getDamageValue()) * 255.0f / static_cast(item.getMaxDamage())); @@ -207,7 +208,8 @@ void ItemRenderer::renderGuiItemOverlay(Font* font, Textures* textures, ItemStac blitRect(t, x + 2, y + 13, duraWidth, 1, duraColor); } - if (item.m_count <= 1) { + if (item.m_count <= 1) + { return; } diff --git a/source/client/renderer/entity/MobRenderer.cpp b/source/client/renderer/entity/MobRenderer.cpp index 13d7ba76e..c3af5f015 100644 --- a/source/client/renderer/entity/MobRenderer.cpp +++ b/source/client/renderer/entity/MobRenderer.cpp @@ -90,7 +90,7 @@ void MobRenderer::render(const Entity& entity, const Vec3& pos, float rot, float MatrixStack::Ref matrix = MatrixStack::World.push(); m_pModel->m_attackTime = getAttackAnim(mob, a); - m_pModel->m_bRiding = false; + m_pModel->m_bRiding = mob.isRiding(); m_pModel->m_bIsBaby = mob.isBaby(); if (m_pArmorModel != nullptr) diff --git a/source/client/renderer/entity/SheepRenderer.cpp b/source/client/renderer/entity/SheepRenderer.cpp index 3e864a296..ed2ef91b8 100644 --- a/source/client/renderer/entity/SheepRenderer.cpp +++ b/source/client/renderer/entity/SheepRenderer.cpp @@ -15,7 +15,7 @@ int SheepRenderer::prepareArmor(const Mob& mob, int layer, float a) const Sheep& sheep = (const Sheep&)mob; if (layer == 0 && !sheep.isSheared()) { - bindTexture("/mob/sheep_fur.png"); + bindTexture("mob/sheep_fur.png"); float brightness = sheep.getBrightness(a); int color = sheep.getColor(); currentShaderColor = Sheep::COLOR[color]; diff --git a/source/client/sound/SoundData.cpp b/source/client/sound/SoundData.cpp index 44c6bb1ed..23ab30a8c 100644 --- a/source/client/sound/SoundData.cpp +++ b/source/client/sound/SoundData.cpp @@ -81,11 +81,13 @@ bool SoundDesc::_load(const char* category, const char *name) } location.path = "sound/" + std::string(name) + ".pcm"; ret = _loadPcm(location); - if (!ret) { + if (!ret) + { m_codecType = AudioCodec::NONE; LOG_W("Failed to load sound \"%s\"!", name); return false; - } else + } + else return true; } diff --git a/source/client/sound/SoundEngine.hpp b/source/client/sound/SoundEngine.hpp index 620a108e0..e01b33f4e 100644 --- a/source/client/sound/SoundEngine.hpp +++ b/source/client/sound/SoundEngine.hpp @@ -43,6 +43,7 @@ class SoundEngine public: SoundSystem* m_pSoundSystem; + private: SoundRepository m_sounds; SoundPathRepository m_songs; diff --git a/source/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 66c9b30f8..8ea1c3763 100644 --- a/source/common/Random.cpp +++ b/source/common/Random.cpp @@ -35,7 +35,8 @@ void Random::setSeed(int32_t seed) void Random::init_genrand(uint32_t s) { mt[0] = s & 0xffffffffUL; - for (mti = 1; mti < N; mti++) { + for (mti = 1; mti < N; mti++) + { mt[mti] = (1812433253UL * (mt[mti - 1] ^ (mt[mti - 1] >> 30)) + mti); /* See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier. */ @@ -65,11 +66,13 @@ uint32_t Random::genrand_int32() if (mti == N+1) /* if init_genrand() has not been called, */ init_genrand(5489UL); /* a default initial seed is used */ - for (kk=0;kk> 1) ^ mag01[y & 0x1UL]; } - for (;kk> 1) ^ mag01[y & 0x1UL]; } diff --git a/source/network/Packet.hpp b/source/network/Packet.hpp index 12fe4688d..ce60160da 100644 --- a/source/network/Packet.hpp +++ b/source/network/Packet.hpp @@ -18,8 +18,9 @@ //#define NETWORK_PROTOCOL_VERSION 3 // 0.2.0 (actual client crashes with unrecognized entities) //#define NETWORK_PROTOCOL_VERSION 4 // 0.3.0 //#define NETWORK_PROTOCOL_VERSION 5 // 0.3.2 -#define NETWORK_PROTOCOL_VERSION 6 // 0.3.3 +#define NETWORK_PROTOCOL_VERSION 6 // 0.3.3 //#define NETWORK_PROTOCOL_VERSION 7 // 0.4.0 +//#define NETWORK_PROTOCOL_VERSION 29 // 0.12.1 class NetEventCallback; 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/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 c48af8dd7..d5bc21ebc 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++; @@ -53,12 +62,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 +80,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 +89,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,13 +123,13 @@ void ServerPlayer::refreshContainer(ContainerMenu* menu, const std::vector= 5 - if (!isResultSlot) - { + // @TODO: See my gripes in ContainerMenu::slotChanged + // But ultimately this is a bandaid for the fact that the client has authority over the inventory in PE + if (!isResultSlot && slot->m_group != Slot::INVENTORY && slot->m_group != Slot::HOTBAR) m_pLevel->m_pRakNetInstance->send(new ContainerSetSlotPacket(menu->m_containerId, index, item)); - } #endif } @@ -113,6 +144,9 @@ void ServerPlayer::doCloseContainer() { if (m_pContainerMenu) m_pContainerMenu->removed(this); + else + LOG_W("Container is missing @ doCloseContainer!"); + setContainerMenu(nullptr); // m_pInventoryMenu on Java, nullptr on Pocket } @@ -128,5 +162,7 @@ void ServerPlayer::setContainerMenu(ContainerMenu* menu) { m_pContainerMenu->m_containerId = m_containerId; m_pContainerMenu->addSlotListener(this); + refreshContainer(m_pContainerMenu, m_pContainerMenu->cloneItems()); + m_pContainerMenu->broadcastChanges(); } } \ No newline at end of file diff --git a/source/server/ServerPlayer.hpp b/source/server/ServerPlayer.hpp index ddc2b7d53..2f08d5191 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, int index, Slot* slot, ItemStack& item, bool isResultSlot) override; void setContainerData(ContainerMenu* menu, int id, int value) override; void doCloseContainer(); diff --git a/source/server/ServerSideNetworkHandler.cpp b/source/server/ServerSideNetworkHandler.cpp index e00afbbc2..0651c8037 100644 --- a/source/server/ServerSideNetworkHandler.cpp +++ b/source/server/ServerSideNetworkHandler.cpp @@ -218,9 +218,9 @@ void ServerSideNetworkHandler::handle(const RakNet::RakNetGUID& guid, ReadyPacke #if NETWORK_PROTOCOL_VERSION >= 3 // send the connecting player info about all entities in the world - for (size_t i = 0; i < m_pLevel->m_entities.size(); i++) + for (EntityMap::iterator it = m_pLevel->m_entities.begin(); it != m_pLevel->m_entities.end(); ++it) { - Entity* entity = m_pLevel->m_entities[i]; + Entity* entity = it->second; if (canReplicateEntity(entity)) { AddMobPacket packet(*((Mob*)entity)); @@ -640,6 +640,7 @@ void ServerSideNetworkHandler::handle(const RakNet::RakNetGUID& guid, ContainerS switch (pContainerMenu->m_containerType) { case Container::FURNACE: + case Container::CONTAINER: pContainerMenu->setItem(packet->m_slot, packet->m_item); break; default: diff --git a/source/world/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/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 57841470f..8e8ba64a9 100644 --- a/source/world/entity/Entity.cpp +++ b/source/world/entity/Entity.cpp @@ -28,6 +28,9 @@ void Entity::_init() field_28 = 0; field_30 = 1.0f; m_dimensionId = DIMENSION_OVERWORLD; + _riderId = 0; + _ridingId = 0; + m_bRiding = false; m_bBlocksBuilding = false; m_pLevel = nullptr; m_tintColor = Color::WHITE; @@ -469,6 +472,14 @@ void Entity::tick() void Entity::baseTick() { //@TODO: untangle the gotos + if (const Entity* riding = getRiding()) + { + // if you were riding an entity and they no longer exist, stop + if ((!riding && _ridingId > 0) || riding->m_bRemoved) + { + setRiding(nullptr); + } + } field_90 = m_walkDist; m_oPos = m_pos; @@ -745,6 +756,9 @@ void Entity::playerTouch(Player* player) void Entity::push(Entity* bud) { + if (bud == getRider() || bud == getRiding()) + return; + float diffX = bud->m_pos.x - m_pos.x; float diffZ = bud->m_pos.z - m_pos.z; float maxDiff = Mth::absMax(diffX, diffZ); @@ -947,6 +961,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; + + 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() { } @@ -956,6 +1010,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 (_ridingId <= 0) + return nullptr; + + if (Entity* riding = m_pLevel->getEntity(_ridingId)) + return riding; + + return nullptr; +} + +Entity* Entity::getRider() const +{ + if (_riderId <= 0) + return nullptr; + + if (Entity* rider = m_pLevel->getEntity(_riderId)) + return rider; + + return nullptr; +} + +void Entity::setRider(Entity* rider) +{ + _riderId = (rider) ? rider->m_EntityID : 0; +} + +void Entity::setRiding(Entity* riding) +{ + _ridingId = (riding) ? riding->m_EntityID : 0; + setSharedFlag(C_ENTITY_FLAG_RIDING, riding); +} + /*void Entity::thunderHit(LightningBolt* bolt) { burn(5); diff --git a/source/world/entity/Entity.hpp b/source/world/entity/Entity.hpp index 4969f2008..9fec6801f 100644 --- a/source/world/entity/Entity.hpp +++ b/source/world/entity/Entity.hpp @@ -113,11 +113,9 @@ class Entity Entity(Level*); virtual ~Entity(); -protected: +public: virtual bool getSharedFlag(SharedFlag flag) const; virtual void setSharedFlag(SharedFlag flag, bool value); - -public: virtual void reset(); virtual void setLevel(Level*); virtual void removed(); @@ -168,7 +166,7 @@ class Entity virtual bool isPushable() const { return false; } virtual bool isShootable() const { return false; } virtual bool isOnFire() const { return m_fireTicks > 0 || getSharedFlag(C_ENTITY_FLAG_ONFIRE); } - virtual bool isRiding() const { return /*m_pRiding != nullptr ||*/ getSharedFlag(C_ENTITY_FLAG_RIDING); } + virtual bool isRiding() const { return getRiding() || getSharedFlag(C_ENTITY_FLAG_RIDING); } virtual bool isSneaking() const { return getSharedFlag(C_ENTITY_FLAG_SNEAKING); } virtual void setSneaking(bool value) { setSharedFlag(C_ENTITY_FLAG_SNEAKING, value); } virtual bool isAlive() const { return m_bRemoved; } @@ -199,9 +197,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; @@ -226,6 +233,10 @@ class Entity (m_pos.z - pos.z) * (m_pos.z - pos.z); } +private: + Entity::ID _ridingId; + Entity::ID _riderId; + protected: SynchedEntityData m_entityData; bool m_bMakeStepSound; @@ -243,12 +254,14 @@ class Entity float field_30; //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/FallingTile.cpp b/source/world/entity/FallingTile.cpp index b750e71ff..838b63a0a 100644 --- a/source/world/entity/FallingTile.cpp +++ b/source/world/entity/FallingTile.cpp @@ -78,7 +78,10 @@ void FallingTile::tick() if (!m_bOnGround) { if (field_E0 > 100 && !m_pLevel->m_bIsClientSide) + { + spawnAtLocation(getTile(), 1); remove(); + } return; } @@ -93,7 +96,7 @@ void FallingTile::tick() } else { - // @TODO: spawn resources? + spawnAtLocation(getTile(), 1); } } diff --git a/source/world/entity/Mob.cpp b/source/world/entity/Mob.cpp index 339086332..459ef5571 100644 --- a/source/world/entity/Mob.cpp +++ b/source/world/entity/Mob.cpp @@ -56,7 +56,7 @@ void Mob::_init() m_lPos = Vec3::ZERO; m_lRot = Vec2::ZERO; m_lastHurt = 0; - m_pEntLookedAt = nullptr; + m_entLookedAtId = 0; m_bSwinging = false; m_swingTime = 0; m_ambientSoundTime = 0; @@ -758,7 +758,7 @@ void Mob::aiStep() { Entity* pEnt = *it; if (pEnt->isPushable()) - pEnt->push(this); + pEnt->push(this); } } @@ -786,6 +786,13 @@ void Mob::lookAt(Entity* pEnt, float a3, float a4) -rotlerp(m_rot.y, x2 * 180.0f / float(M_PI), a3))); } +Entity *Mob::getLookingAt() const +{ + if (m_entLookedAtId == 0) + return nullptr; + return m_pLevel->getEntity(m_entLookedAtId); +} + bool Mob::canSpawn() { return m_pLevel->getCubes(this, m_hitbox)->empty(); @@ -832,7 +839,7 @@ void Mob::updateAi() Entity* nearestPlayer = m_pLevel->getNearestPlayer(*this, 8.0f); if (nearestPlayer) { - m_pEntLookedAt = nearestPlayer; + m_entLookedAtId = nearestPlayer->m_EntityID; field_120 = m_random.nextInt(20) + 10; } @@ -843,17 +850,18 @@ void Mob::updateAi() } // @TODO: we get a crash here when a Player leaves - if (m_pEntLookedAt) + if (m_entLookedAtId > 0) { - lookAt(m_pEntLookedAt, 10.0f, getMaxHeadXRot()); + Entity* pEnt = m_pLevel->getEntity(m_entLookedAtId); + lookAt(pEnt, 10.0f, getMaxHeadXRot()); // gaze timer field_120--; // if the entity was removed, or we're too far away, or our gaze timer is up - if (field_120 < 0 || m_pEntLookedAt->m_bRemoved || m_pEntLookedAt->distanceToSqr(this) > 64.0f) + if (field_120 < 0 || pEnt->m_bRemoved || pEnt->distanceToSqr(this) > 64.0f) // stop staring - m_pEntLookedAt = nullptr; + m_entLookedAtId = 0; } else { diff --git a/source/world/entity/Mob.hpp b/source/world/entity/Mob.hpp index 4ad46de92..784a7940e 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; @@ -143,7 +143,7 @@ class Mob : public Entity Vec3 m_lPos; Vec2 m_lRot; int m_lastHurt; - Entity* m_pEntLookedAt; + Entity::ID m_entLookedAtId; float v020_field_104; diff --git a/source/world/entity/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..7ba77895e 100644 --- a/source/world/entity/Pig.cpp +++ b/source/world/entity/Pig.cpp @@ -6,6 +6,8 @@ SPDX-License-Identifier: BSD-1-Clause ********************************************************************/ #include "Pig.hpp" +#include "Player.hpp" +#include "world/level/Level.hpp" Pig::Pig(Level* pLevel) : Animal(pLevel) { @@ -14,13 +16,41 @@ Pig::Pig(Level* pLevel) : Animal(pLevel) m_texture = "mob/pig.png"; setSize(0.9f, 0.9f); // some dataitem stuff + setSaddle(true); } int Pig::getDeathLoot() const { - 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 fb1d9ae77..0e93310d9 100644 --- a/source/world/entity/Pig.hpp +++ b/source/world/entity/Pig.hpp @@ -19,7 +19,7 @@ class Pig : public Animal std::string getHurtSound() const override { return "mob.pig"; } int getDeathLoot() const override; int getMaxHealth() const override { return 10; } - bool interact(Player*) override { return false; } + bool interact(Player*) override; bool hasSaddle() const { return false; } void setSaddle(bool b); diff --git a/source/world/entity/Player.cpp b/source/world/entity/Player.cpp index 9c9911036..2ce2c9012 100644 --- a/source/world/entity/Player.cpp +++ b/source/world/entity/Player.cpp @@ -63,8 +63,8 @@ Player::Player(Level* pLevel, GameType playerGameType) : Mob(pLevel) Player::~Player() { - delete m_pInventory; delete m_pInventoryMenu; + delete m_pInventory; } void Player::reallyDrop(ItemEntity* pEnt) @@ -72,6 +72,10 @@ void Player::reallyDrop(ItemEntity* pEnt) m_pLevel->addEntity(pEnt); } +void Player::_handleOpenedContainerMenu() +{ +} + void Player::reset() { Mob::reset(); @@ -320,7 +324,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 +390,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 +498,9 @@ void Player::respawn() void Player::rideTick() { - + Mob::rideTick(); + m_oBob = m_bob; + m_bob = 0.0f; } void Player::setDefaultHeadHeight() @@ -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 35d7431fb..c2c5dcdd2 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; } @@ -72,9 +74,9 @@ class Player : public Mob 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 e176275ca..1b2662fb5 100644 --- a/source/world/entity/Spider.hpp +++ b/source/world/entity/Spider.hpp @@ -11,7 +11,7 @@ class Spider : public Monster std::string getDeathSound() const override { return "mob.spiderdeath"; } std::string getHurtSound() const override { return "mob.spider"; } int getDeathLoot() const override { return ITEM_STRING; } - float getRideHeight() const { return m_bbHeight * 0.75f - 0.5f; } + float getRideHeight() const override { return m_bbHeight * 0.75f - 0.5f; } Entity* findAttackTarget() override; void checkHurtTarget(Entity* ent, float var2) override; diff --git a/source/world/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..7766dd63c 100644 --- a/source/world/inventory/ChestMenu.cpp +++ b/source/world/inventory/ChestMenu.cpp @@ -7,16 +7,18 @@ ChestMenu::ChestMenu(Container* inventory, Container* container) { int rows = m_pContainer->getContainerSize() / 9; + // Chest slots for (int row = 0; row < rows; ++row) { for (int col = 0; col < 9; ++col) - addSlot(new Slot(m_pContainer, col + row * 9)); + addSlot(new Slot(m_pContainer, col + row * 9, Slot::CONTAINER)); } + // Inventory slots for (int row = 0; row < 3; ++row) { for (int col = 0; col < 9; ++col) - addSlot(new Slot(inventory, col + row * 9 + 9)); + addSlot(new Slot(inventory, col + row * 9 + 9, Slot::INVENTORY)); } for (int col = 0; col < 9; ++col) diff --git a/source/world/inventory/ChestMenu.hpp b/source/world/inventory/ChestMenu.hpp index ea787bea3..04eb68da5 100644 --- a/source/world/inventory/ChestMenu.hpp +++ b/source/world/inventory/ChestMenu.hpp @@ -1,7 +1,7 @@ #pragma once #include "ContainerMenu.hpp" -#include "world/Container.hpp" +#include "Container.hpp" #include "world/entity/Player.hpp" class ChestMenu : public ContainerMenu diff --git a/source/world/inventory/CompoundContainer.cpp b/source/world/inventory/CompoundContainer.cpp new file mode 100644 index 000000000..21adf1e2d --- /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, SlotID slot) override + { + if (m_pOwner) + m_pOwner->setContainerChanged(slot + m_offset); + } + +private: + CompoundContainer* m_pOwner; + int m_offset; +}; + +CompoundContainer::CompoundContainer(const std::string& name, Container* c1, Container* c2) : + m_name(name), m_pLeftContainer(c1), m_pRightContainer(c2), m_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(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::setContainerChanged(SlotID slot) +{ + for (ContentChangeListeners::iterator it = m_contentChangeListeners.begin(); it != m_contentChangeListeners.end(); ++it) + { + ContainerContentChangeListener* listener = *it; + listener->containerContentChanged(this, slot); + } +} + +bool CompoundContainer::stillValid(Player* player) const +{ + 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/CompoundContainer.hpp b/source/world/inventory/CompoundContainer.hpp similarity index 57% rename from source/world/CompoundContainer.hpp rename to source/world/inventory/CompoundContainer.hpp index 5a4884591..a429f1ceb 100644 --- a/source/world/CompoundContainer.hpp +++ b/source/world/inventory/CompoundContainer.hpp @@ -1,17 +1,25 @@ #pragma once +#include #include #include "Container.hpp" +#include "ContainerContentChangeListener.hpp" class CompoundContainer : public Container { private: + class ChildListener; + std::string m_name; Container* m_pLeftContainer; Container* m_pRightContainer; + ContentChangeListeners m_contentChangeListeners; + ChildListener* m_pLeftListener; + ChildListener* m_pRightListener; public: CompoundContainer(const std::string& name, Container* c1, Container* c2); + ~CompoundContainer() override; uint16_t getContainerSize() const override; @@ -25,7 +33,10 @@ class CompoundContainer : public Container int getMaxStackSize() override; - void setChanged() override; + void setContainerChanged(SlotID slot) override; bool stillValid(Player* player) const override; + + void addContentChangeListener(ContainerContentChangeListener* listener) override; + void removeContentChangeListener(ContainerContentChangeListener* listener) override; }; diff --git a/source/world/inventory/Container.hpp b/source/world/inventory/Container.hpp new file mode 100644 index 000000000..6e6956689 --- /dev/null +++ b/source/world/inventory/Container.hpp @@ -0,0 +1,55 @@ +#pragma once + +#include + +#include "world/item/ItemStack.hpp" + +#define C_MAX_CONTAINER_STACK_SIZE (64) + +class ContainerContentChangeListener; +class ContainerSizeChangeListener; + +class Container +{ +protected: + typedef std::set ContentChangeListeners; + typedef std::set SizeChangeListeners; + +public: + typedef uint16_t Size; + +public: + enum Type + { + CONTAINER, + CRAFTING, + FURNACE, + DISPENSER + }; + +public: + virtual Size getContainerSize() const = 0; + virtual ItemStack& getItem(int index) = 0; + virtual ItemStack* tryGetItem(int index) + { + if (index >= 0 && index < getContainerSize()) + return &getItem(index); + else + return nullptr; + } + virtual ItemStack removeItem(int index, int count) = 0; + virtual void setItem(int index, const ItemStack& item) = 0; + virtual std::string getName() const = 0; + virtual int getMaxStackSize() + { + return C_MAX_CONTAINER_STACK_SIZE; + } + // Was called setChanged in Java + virtual void setContainerChanged(SlotID slot) = 0; + virtual bool stillValid(Player* player) const = 0; + + virtual void addContentChangeListener(ContainerContentChangeListener* listener) {} + virtual void addSizeChangeListener(ContainerSizeChangeListener* listener) {} + virtual void removeContentChangeListener(ContainerContentChangeListener* listener) {} + virtual void removeSizeChangeListener(ContainerSizeChangeListener* listener) {} +}; \ No newline at end of file diff --git a/source/world/inventory/ContainerContentChangeListener.cpp b/source/world/inventory/ContainerContentChangeListener.cpp new file mode 100644 index 000000000..bb72498aa --- /dev/null +++ b/source/world/inventory/ContainerContentChangeListener.cpp @@ -0,0 +1,2 @@ +#include "ContainerContentChangeListener.hpp" + diff --git a/source/world/inventory/ContainerContentChangeListener.hpp b/source/world/inventory/ContainerContentChangeListener.hpp new file mode 100644 index 000000000..ed0f7139f --- /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, SlotID slot) = 0; +}; diff --git a/source/world/ContainerListener.cpp b/source/world/inventory/ContainerListener.cpp similarity index 81% rename from source/world/ContainerListener.cpp rename to source/world/inventory/ContainerListener.cpp index ddfc041fa..d0377bd7c 100644 --- a/source/world/ContainerListener.cpp +++ b/source/world/inventory/ContainerListener.cpp @@ -7,5 +7,5 @@ ContainerListener::~ContainerListener() void ContainerListener::refreshContainerItems(ContainerMenu* menu) { - refreshContainer(menu, menu->copyItems()); + refreshContainer(menu, menu->cloneItems()); } diff --git a/source/world/ContainerListener.hpp b/source/world/inventory/ContainerListener.hpp similarity index 75% rename from source/world/ContainerListener.hpp rename to source/world/inventory/ContainerListener.hpp index a159ae5db..79a63bb7e 100644 --- a/source/world/ContainerListener.hpp +++ b/source/world/inventory/ContainerListener.hpp @@ -4,6 +4,7 @@ class ContainerMenu; class ItemStack; +class Slot; class ContainerListener { @@ -12,6 +13,6 @@ class ContainerListener virtual void refreshContainer(ContainerMenu* menu, const std::vector& items) {} virtual void refreshContainerItems(ContainerMenu* menu); - virtual void slotChanged(ContainerMenu* menu, int index, ItemStack& item, bool isResultSlot) {} + virtual void slotChanged(ContainerMenu* menu, int index, Slot* slot, ItemStack& item, bool isResultSlot) {} virtual void setContainerData(ContainerMenu* menu, int id, int value) {} }; \ No newline at end of file diff --git a/source/world/inventory/ContainerMenu.cpp b/source/world/inventory/ContainerMenu.cpp index 8c083c908..936cb1d3e 100644 --- a/source/world/inventory/ContainerMenu.cpp +++ b/source/world/inventory/ContainerMenu.cpp @@ -1,14 +1,15 @@ #include "ContainerMenu.hpp" -#include "Slot.hpp" #include "world/item/ItemStack.hpp" #include "world/item/Inventory.hpp" -#include "world/Container.hpp" -#include "world/ContainerListener.hpp" +#include "Slot.hpp" +#include "Container.hpp" +#include "ContainerListener.hpp" ContainerMenu::ContainerMenu(Container::Type containerType) : m_changeUid(0) , m_containerId(0) , m_containerType(containerType) + , m_bBroadcastChanges(true) { } @@ -18,8 +19,24 @@ ContainerMenu::~ContainerMenu() /*for (std::vector::iterator it = m_listeners.begin(); it != m_listeners.end(); ++it) delete (*it);*/ + _clearSlots(); +} + +void ContainerMenu::_clearSlots() +{ for (std::vector::iterator it = m_slots.begin(); it != m_slots.end(); ++it) - delete (*it); + { + Slot* slot = *it; + + // @HACK: I don't like this + Container* pContainer = slot->m_pContainer; + if (pContainer) + pContainer->removeContentChangeListener(this); + + delete slot; + } + + m_slots.clear(); } void ContainerMenu::addSlot(Slot* slot) @@ -27,35 +44,40 @@ void ContainerMenu::addSlot(Slot* slot) slot->m_index = m_slots.size(); m_slots.push_back(slot); m_lastSlots.push_back(ItemStack::EMPTY); + + // @HACK: holy hack + Container* pContainer = slot->m_pContainer; + if (pContainer) + pContainer->addContentChangeListener(this); } void ContainerMenu::addSlotListener(ContainerListener* listener) { - m_listeners.push_back(listener); - - // Not done on PE - /*std::vector snapshot = copyItems(); - listener->refreshContainer(this, snapshot); - broadcastChanges();*/ + m_listeners.insert(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(SlotID slot) +{ + ItemStack& current = m_slots[slot]->getItem(); + if (m_lastSlots[slot] != current) + { + m_lastSlots[slot] = ItemStack(current); + for (ContainerListeners::iterator it = m_listeners.begin(); it != m_listeners.end(); ++it) + (*it)->slotChanged(this, slot, m_slots[slot], m_lastSlots[slot], isResultSlot()); + } +} + void ContainerMenu::broadcastChanges() { for (size_t i = 0; i < m_slots.size(); ++i) { - ItemStack& current = m_slots[i]->getItem(); - if (m_lastSlots[i] != current) - { - m_lastSlots[i] = ItemStack(current); - for (std::vector::iterator it = m_listeners.begin(); it != m_listeners.end(); ++it) - (*it)->slotChanged(this, i, m_lastSlots[i], isResultSlot()); - } + broadcastChanges(i); } } @@ -74,12 +96,39 @@ void ContainerMenu::slotsChanged(Container*) broadcastChanges(); } -std::vector ContainerMenu::copyItems() +void ContainerMenu::containerContentChanged(Container* container, SlotID containerSlot) +{ + // containerSlot is an index within a specific container, but m_slots contains + // slots from multiple containers. We need to find which slot in m_slots corresponds + // to this container slot by matching both the container and the slot index. + for (size_t i = 0; i < m_slots.size(); ++i) + { + Slot* slot = m_slots[i]; + if (slot->m_pContainer == container && slot->m_slot == containerSlot) + { + if (m_bBroadcastChanges) + broadcastChanges(i); + return; + } + } +} + +std::vector ContainerMenu::cloneItems() { std::vector content; for (std::vector::iterator it = m_slots.begin(); it != m_slots.end(); ++it) - content.push_back((*it)->getItem()); + { + Slot* slot = *it; + // @TODO: I really do not like this. + // Firstly, inventories shouldn't be owned by the client + // Secondly, we shouldn't be checking types directly like this + // Ultimately this HAS to have two different storages, one for the inventory and one for the container + if (slot->m_group == Slot::INVENTORY || slot->m_group == Slot::HOTBAR) + continue; + const ItemStack& item = (*it)->getItem(); + content.push_back(item); + } return content; } @@ -217,8 +266,6 @@ ItemStack ContainerMenu::clicked(int slotIndex, MouseButtonType mouseButton, boo if (!slot) return result; - slot->setChanged(); - ItemStack& slotItem = slot->getItem(); if (!slotItem.isEmpty()) @@ -259,6 +306,7 @@ ItemStack ContainerMenu::clicked(int slotIndex, MouseButtonType mouseButton, boo if (carried.m_count <= slot->getMaxStackSize()) { std::swap(carried, slotItem); + slot->setChanged(); } } else if (slotItem.getId() == carried.getId()) @@ -279,6 +327,7 @@ ItemStack ContainerMenu::clicked(int slotIndex, MouseButtonType mouseButton, boo inv->setCarried(ItemStack::EMPTY); slotItem.m_count += count; + slot->setChanged(); break; } case MOUSE_BUTTON_RIGHT: @@ -295,6 +344,7 @@ ItemStack ContainerMenu::clicked(int slotIndex, MouseButtonType mouseButton, boo inv->setCarried(ItemStack::EMPTY); slotItem.m_count += count; + slot->setChanged(); break; } default: @@ -328,38 +378,51 @@ ItemStack ContainerMenu::clicked(int slotIndex, MouseButtonType mouseButton, boo void ContainerMenu::setItem(int index, ItemStack item) { m_slots[index]->set(item); + if (!m_bBroadcastChanges && index >= 0 && index < (int)m_lastSlots.size()) + { + m_lastSlots[index] = ItemStack(m_slots[index]->getItem()); + } } void ContainerMenu::setAll(const std::vector& items) { - 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); +} \ No newline at end of file diff --git a/source/world/inventory/ContainerMenu.hpp b/source/world/inventory/ContainerMenu.hpp index f7b299806..45d1aa360 100644 --- a/source/world/inventory/ContainerMenu.hpp +++ b/source/world/inventory/ContainerMenu.hpp @@ -3,30 +3,38 @@ #include #include #include "world/item/ItemStack.hpp" -#include "world/Container.hpp" #include "client/player/input/MouseDevice.hpp" +#include "Container.hpp" +#include "ContainerContentChangeListener.hpp" class Player; class Inventory; class Slot; class ContainerListener; -class ContainerMenu +class ContainerMenu : public ContainerContentChangeListener { +protected: + typedef std::set ContainerListeners; + public: ContainerMenu(Container::Type containerType); virtual ~ContainerMenu(); +protected: + void _clearSlots(); + public: void addSlot(Slot* slot); virtual void addSlotListener(ContainerListener* listener); void sendData(int id, int value); + virtual void broadcastChanges(SlotID slot); virtual void broadcastChanges(); virtual void removed(Player* player); virtual void slotsChanged(Container* container); // Called getItems in PE and Java - std::vector copyItems(); + std::vector cloneItems(); Slot* getSlotFor(Container* container, int index); Slot* getSlot(int index); virtual ItemStack clicked(int slotIndex, MouseButtonType mouseButton, bool quickMove, Player* player); @@ -50,14 +58,18 @@ class ContainerMenu //Unused virtual bool isPauseScreen() const { return false; } +public: + void containerContentChanged(Container* container, SlotID slot) override; + protected: std::vector m_lastSlots; uint16_t m_changeUid; - std::vector m_listeners; - std::set unsynchedPlayers; + ContainerListeners m_listeners; + std::set m_unsynchedPlayers; public: int m_containerId; Container::Type m_containerType; std::vector m_slots; + bool m_bBroadcastChanges; }; diff --git a/source/world/inventory/ContainerSizeChangeListener.cpp b/source/world/inventory/ContainerSizeChangeListener.cpp new file mode 100644 index 000000000..671ca58bd --- /dev/null +++ b/source/world/inventory/ContainerSizeChangeListener.cpp @@ -0,0 +1 @@ +#include "ContainerSizeChangeListener.hpp" diff --git a/source/world/inventory/ContainerSizeChangeListener.hpp b/source/world/inventory/ContainerSizeChangeListener.hpp new file mode 100644 index 000000000..f39a00780 --- /dev/null +++ b/source/world/inventory/ContainerSizeChangeListener.hpp @@ -0,0 +1,12 @@ +#pragma once +#include +#include "Container.hpp" + +class ContainerSizeChangeListener +{ +public: + virtual ~ContainerSizeChangeListener() {} + +public: + virtual void containerSizeChanged(Container::Size size) = 0; +}; diff --git a/source/world/inventory/CraftingContainer.cpp b/source/world/inventory/CraftingContainer.cpp index 548c6db7b..edfc5a04c 100644 --- a/source/world/inventory/CraftingContainer.cpp +++ b/source/world/inventory/CraftingContainer.cpp @@ -73,7 +73,7 @@ void CraftingContainer::setItem(int index, const ItemStack& item) } } -void CraftingContainer::setChanged() +void CraftingContainer::setContainerChanged(SlotID slot) { } diff --git a/source/world/inventory/CraftingContainer.hpp b/source/world/inventory/CraftingContainer.hpp index 5fecc7d91..f450e2c9f 100644 --- a/source/world/inventory/CraftingContainer.hpp +++ b/source/world/inventory/CraftingContainer.hpp @@ -1,8 +1,8 @@ #pragma once #include -#include "world/Container.hpp" #include "world/item/ItemStack.hpp" +#include "Container.hpp" #include "ContainerMenu.hpp" class ContainerMenu; @@ -25,7 +25,7 @@ class CraftingContainer : public Container ItemStack removeItem(int index, int amount) override; void setItem(int index, const ItemStack& item) override; - void setChanged() override; + void setContainerChanged(SlotID slot) override; bool stillValid(Player* player) const override; private: diff --git a/source/world/inventory/CraftingMenu.cpp b/source/world/inventory/CraftingMenu.cpp index aeb9d016b..728aad397 100644 --- a/source/world/inventory/CraftingMenu.cpp +++ b/source/world/inventory/CraftingMenu.cpp @@ -36,6 +36,9 @@ CraftingMenu::CraftingMenu(Inventory* inventory, const TilePos& tilePos, Level* CraftingMenu::~CraftingMenu() { + _clearSlots(); + + // clearSlots must be called before these are deleted delete m_pCraftSlots; delete m_pResultSlots; } diff --git a/source/world/inventory/FurnaceMenu.cpp b/source/world/inventory/FurnaceMenu.cpp new file mode 100644 index 000000000..b3c3cb2f3 --- /dev/null +++ b/source/world/inventory/FurnaceMenu.cpp @@ -0,0 +1,100 @@ +#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) +{ + if (index == 0) + m_furnace->m_tickCount = value; + else if (index == 1) + m_furnace->m_litTime = value; + else if (index == 2) + m_furnace->m_litDuration = value; +} + +ItemStack FurnaceMenu::quickMoveStack(int index) +{ + ItemStack item; + Slot* slot = getSlot(index); + if (slot && slot->hasItem()) + { + ItemStack& slotItem = slot->getItem(); + item = slotItem.copy(); + if (index == 2) + moveItemStackTo(slotItem, 3, 39, true); + else if (index >= 3 && index < 30) + moveItemStackTo(slotItem, 30, 39, false); + else if (index >= 30 && index < 39) + moveItemStackTo(slotItem, 3, 30, false); + else + moveItemStackTo(slotItem, 3, 39, false); + + if (slotItem.m_count == 0) + slot->set(ItemStack::EMPTY); + else + slot->setChanged(); + + if (slotItem.m_count == item.m_count) + return ItemStack::EMPTY; + + slot->onTake(slotItem); + } + + return item; +} diff --git a/source/world/inventory/FurnaceMenu.hpp b/source/world/inventory/FurnaceMenu.hpp new file mode 100644 index 000000000..42025cf41 --- /dev/null +++ b/source/world/inventory/FurnaceMenu.hpp @@ -0,0 +1,24 @@ +#pragma once + +#include "ContainerMenu.hpp" +#include "world/entity/Player.hpp" +#include + +class FurnaceMenu : public ContainerMenu +{ +public: + FurnaceMenu(Inventory* inventory, FurnaceTileEntity* container); + +public: + bool stillValid(Player* player) const override; + void addSlotListener(ContainerListener* listener) override; + void broadcastChanges() override; + void setData(int, int) override; + ItemStack quickMoveStack(int index) override; + +private: + FurnaceTileEntity* m_furnace; + int m_lastCookTime; + int m_lastBurnTime; + int m_lastLitDuration; +}; diff --git a/source/world/inventory/FurnaceResultSlot.cpp b/source/world/inventory/FurnaceResultSlot.cpp new file mode 100644 index 000000000..84d628de0 --- /dev/null +++ b/source/world/inventory/FurnaceResultSlot.cpp @@ -0,0 +1,19 @@ +#include "FurnaceResultSlot.hpp" +#include "../item/Item.hpp" +#include "world/entity/Player.hpp" + +FurnaceResultSlot::FurnaceResultSlot(Player* player, Container* container, int slotIndex) + : Slot(container, slotIndex, OUTPUT), m_pPlayer(player) +{ +} + +bool FurnaceResultSlot::mayPlace(const ItemStack&) const +{ + return false; +} + +void FurnaceResultSlot::onTake(ItemStack& inst) +{ + inst.onCraftedBy(m_pPlayer, m_pPlayer->m_pLevel); + Slot::onTake(inst); +} diff --git a/source/world/inventory/FurnaceResultSlot.hpp b/source/world/inventory/FurnaceResultSlot.hpp new file mode 100644 index 000000000..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..1e5b2c37e 100644 --- a/source/world/inventory/InventoryMenu.cpp +++ b/source/world/inventory/InventoryMenu.cpp @@ -42,6 +42,9 @@ InventoryMenu::InventoryMenu(Inventory* inventory, bool active) InventoryMenu::~InventoryMenu() { + _clearSlots(); + + // clearSlots must be called before these are deleted delete m_pCraftSlots; delete m_pResultSlots; } diff --git a/source/world/inventory/ResultContainer.cpp b/source/world/inventory/ResultContainer.cpp index 3f6763d53..1cf3be2d6 100644 --- a/source/world/inventory/ResultContainer.cpp +++ b/source/world/inventory/ResultContainer.cpp @@ -40,7 +40,7 @@ void ResultContainer::setItem(int index, const ItemStack& item) m_item = item; } -void ResultContainer::setChanged() +void ResultContainer::setContainerChanged(SlotID slot) { } diff --git a/source/world/inventory/ResultContainer.hpp b/source/world/inventory/ResultContainer.hpp index a3d95dc65..14228c1fb 100644 --- a/source/world/inventory/ResultContainer.hpp +++ b/source/world/inventory/ResultContainer.hpp @@ -1,7 +1,7 @@ #pragma once -#include "world/Container.hpp" #include "world/item/ItemStack.hpp" +#include "Container.hpp" class Player; @@ -19,7 +19,7 @@ class ResultContainer : public Container ItemStack removeItem(int index, int amount) override; void setItem(int index, const ItemStack& item) override; - void setChanged() override; + void setContainerChanged(SlotID slot) override; bool stillValid(Player* player) const override; private: diff --git a/source/world/inventory/ResultSlot.hpp b/source/world/inventory/ResultSlot.hpp index 92dd109e6..7da681e83 100644 --- a/source/world/inventory/ResultSlot.hpp +++ b/source/world/inventory/ResultSlot.hpp @@ -1,7 +1,7 @@ #pragma once #include "Slot.hpp" -#include "world/Container.hpp" +#include "Container.hpp" class ResultSlot : public Slot { diff --git a/source/world/inventory/SimpleContainer.cpp b/source/world/inventory/SimpleContainer.cpp index 6799cd433..d6ce69a77 100644 --- a/source/world/inventory/SimpleContainer.cpp +++ b/source/world/inventory/SimpleContainer.cpp @@ -1,8 +1,10 @@ #include "SimpleContainer.hpp" +#include "ContainerContentChangeListener.hpp" +#include "ContainerSizeChangeListener.hpp" -SimpleContainer::SimpleContainer(int size, const std::string& name) : - m_items(size), - m_name(name) +SimpleContainer::SimpleContainer(int size, const std::string& name) + : m_items(size) + , m_name(name) { } @@ -25,7 +27,7 @@ ItemStack SimpleContainer::removeItem(int index, int count) { result = m_items[index]; m_items[index] = ItemStack::EMPTY; - setChanged(); + setContainerChanged(index); return result; } else @@ -34,7 +36,7 @@ ItemStack SimpleContainer::removeItem(int index, int count) if (!m_items[index].m_count) m_items[index] = ItemStack::EMPTY; - setChanged(); + setContainerChanged(index); return result; } } @@ -47,7 +49,7 @@ void SimpleContainer::setItem(int index, const ItemStack& item) if (!item.isEmpty() && item.m_count > getMaxStackSize()) m_items[index].m_count = getMaxStackSize(); - setChanged(); + setContainerChanged(index); } std::string SimpleContainer::getName() const @@ -55,8 +57,13 @@ std::string SimpleContainer::getName() const return m_name; } -void SimpleContainer::setChanged() +void SimpleContainer::setContainerChanged(SlotID slot) { + for (ContentChangeListeners::iterator it = m_contentChangeListeners.begin(); it != m_contentChangeListeners.end(); it++) + { + ContainerContentChangeListener* pListener = *it; + pListener->containerContentChanged(this, slot); + } } 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..2df4756a4 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; - - virtual std::string getName() const override; - - virtual void setChanged() override; - - virtual bool stillValid(Player* player) const override; +public: + uint16_t getContainerSize() const override; + ItemStack& getItem(int index) override; + ItemStack removeItem(int index, int count) override; + void setItem(int index, const ItemStack& item) override; + std::string getName() const override; + void setContainerChanged(SlotID slot) override; + bool stillValid(Player* player) const override; + void addContentChangeListener(ContainerContentChangeListener* listener) override; + void addSizeChangeListener(ContainerSizeChangeListener* listener) override; + void removeContentChangeListener(ContainerContentChangeListener* listener) override; + void removeSizeChangeListener(ContainerSizeChangeListener* listener) override; +public: virtual void clear(); + virtual void load(const CompoundTag& tag); + virtual void save(CompoundTag& tag) const; - 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.hpp b/source/world/inventory/Slot.hpp index 12a033823..f4286639d 100644 --- a/source/world/inventory/Slot.hpp +++ b/source/world/inventory/Slot.hpp @@ -1,6 +1,6 @@ #pragma once -#include "world/Container.hpp" +#include "Container.hpp" class ItemStack; @@ -32,7 +32,7 @@ class Slot virtual void set(const ItemStack& item); - virtual void setChanged() { m_pContainer->setChanged(); } + virtual void setChanged() { m_pContainer->setContainerChanged(m_slot); } virtual int getMaxStackSize() const { return m_pContainer->getMaxStackSize(); } @@ -42,7 +42,7 @@ class Slot public: Container* m_pContainer; - int m_slot; - int m_index; + int m_slot; // the position in the attached container + int m_index; // the position in the ContainerMenu::m_slots vector Group m_group; }; \ No newline at end of file diff --git a/source/world/item/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.cpp b/source/world/item/CoalItem.cpp new file mode 100644 index 000000000..4d3f6669e --- /dev/null +++ b/source/world/item/CoalItem.cpp @@ -0,0 +1,12 @@ +#include "CoalItem.hpp" + +CoalItem::CoalItem(int id) : Item(id) +{ + m_maxDamage = 0; + m_bStackedByData = true; +} + +std::string CoalItem::getDescriptionId(ItemStack* inst) const +{ + return (inst->getAuxValue() == 1) ? "item.charcoal" : "item.coal"; +} \ No newline at end of file diff --git a/source/world/item/CoalItem.hpp b/source/world/item/CoalItem.hpp new file mode 100644 index 000000000..fc1474401 --- /dev/null +++ b/source/world/item/CoalItem.hpp @@ -0,0 +1,12 @@ +#pragma once + +#include "Item.hpp" + +class CoalItem : public Item +{ +public: + CoalItem(int id); + + std::string getDescriptionId(ItemStack* inst) const override; + +}; \ No newline at end of file diff --git a/source/world/item/Inventory.cpp b/source/world/item/Inventory.cpp index e0471397b..a14c88e24 100644 --- a/source/world/item/Inventory.cpp +++ b/source/world/item/Inventory.cpp @@ -4,6 +4,7 @@ #include "common/Logger.hpp" #include "nbt/CompoundTag.hpp" #include "network/Packet.hpp" +#include "world/inventory/ContainerContentChangeListener.hpp" #include "Item.hpp" @@ -627,3 +628,22 @@ GameType Inventory::_getGameMode() const { return m_pPlayer->getPlayerGameType(); } + +void Inventory::setContainerChanged(SlotID slot) +{ + for (ContentChangeListeners::iterator it = m_contentChangeListeners.begin(); it != m_contentChangeListeners.end(); ++it) + { + ContainerContentChangeListener* listener = *it; + listener->containerContentChanged(this, slot); + } +} + +void Inventory::addContentChangeListener(ContainerContentChangeListener* listener) +{ + m_contentChangeListeners.insert(listener); +} + +void Inventory::removeContentChangeListener(ContainerContentChangeListener* listener) +{ + m_contentChangeListeners.erase(listener); +} diff --git a/source/world/item/Inventory.hpp b/source/world/item/Inventory.hpp index 568a1e54a..09a5c7882 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,6 +20,8 @@ class Player; // in case we're included from Player.hpp class Inventory : public Container { +private: + typedef std::set ContentChangeListeners; public: Inventory(Player*); virtual ~Inventory(); @@ -80,9 +83,11 @@ class Inventory : public Container return "Inventory"; } - void setChanged() override { } + void setContainerChanged(SlotID slot) 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; @@ -100,4 +105,5 @@ class Inventory : public Container std::vector m_items; std::vector m_armor; + ContentChangeListeners m_contentChangeListeners; }; diff --git a/source/world/item/Item.cpp b/source/world/item/Item.cpp index 1e4c353fb..94b017a79 100644 --- a/source/world/item/Item.cpp +++ b/source/world/item/Item.cpp @@ -11,19 +11,20 @@ #include "common/Logger.hpp" #include "common/Util.hpp" +#include "ArmorItem.hpp" +#include "BowItem.hpp" #include "CameraItem.hpp" +#include "CoalItem.hpp" #include "DoorItem.hpp" -#include "TileItem.hpp" -#include "TilePlanterItem.hpp" -#include "RocketItem.hpp" -#include "ToolItem.hpp" -#include "BowItem.hpp" #include "DyePowderItem.hpp" -#include "WeaponItem.hpp" #include "FoodItem.hpp" -#include "ArmorItem.hpp" #include "HoeItem.hpp" +#include "RocketItem.hpp" #include "SeedItem.hpp" +#include "TileItem.hpp" +#include "ToolItem.hpp" +#include "TilePlanterItem.hpp" +#include "WeaponItem.hpp" #define ITEM(x) ((x) - 256) @@ -339,7 +340,7 @@ void Item::initItems() ->setIcon(5, 2) ->setDescriptionId("arrow"); - Item::coal = NEW_ITEM(ITEM_COAL) + Item::coal = NEW_X_ITEMN(CoalItem, ITEM_COAL) ->setIcon(7, 0) ->setDescriptionId("coal"); diff --git a/source/world/item/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 e91a3c8f2..08d495c18 100644 --- a/source/world/level/Level.cpp +++ b/source/world/level/Level.cpp @@ -18,6 +18,7 @@ #include "network/packets/ExplodePacket.hpp" #include "world/level/levelgen/chunk/ChunkCache.hpp" #include "world/entity/MobSpawner.hpp" +#include "world/tile/entity/TileEntity.hpp" #include "Explosion.hpp" #include "Region.hpp" @@ -38,6 +39,8 @@ Level::Level(LevelStorage* pStor, const std::string& name, const LevelSettings& m_bCalculatingInitialSpawn = false; m_pChunkSource = nullptr; m_pLevelStorage = pStor; + m_tileEntityList = TileEntityVector(); + m_bUpdatingTileEntities = false; m_randValue = 42184323; m_addend = 1013904223; m_bUpdateLights = true; @@ -77,10 +80,9 @@ Level::~Level() SAFE_DELETE(m_pPathFinder); SAFE_DELETE(m_pMobSpawner); - const size_t size = m_entities.size(); - for (size_t i = 0; i < size; i++) + for (EntityMap::iterator it = m_entities.begin(); it != m_entities.end(); it++) { - Entity* pEnt = m_entities.at(i); + Entity* pEnt = it->second; //you better HOPE this is freed by Minecraft! (or a NetworkHandler) //Really should have used shared pointers and stuff. @@ -258,6 +260,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_tileEntityList; +} + +void Level::setTileEntity(const TilePos& pos, TileEntity* tileEntity) +{ + + if (tileEntity->isRemoved()) + return; + + if (m_bUpdatingTileEntities) + { + tileEntity->m_pos = pos; + m_pendingTileEntities.push_back(tileEntity); + return; + } + + m_tileEntityList.push_back(tileEntity); + LevelChunk* pChunk = getChunk(pos); + if (pChunk) + pChunk->setTileEntity(pos, tileEntity); +} + +void Level::removeTileEntity(const TilePos& pos) +{ + TileEntity* 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_tileEntityList, tileEntity); + + if (LevelChunk* pChunk = getChunk(pos)) + { + pChunk->removeTileEntity(pos); + } +} + void Level::swap(const TilePos& pos1, const TilePos& pos2) { TileID tile1 = getTile(pos1); @@ -300,22 +357,18 @@ Material* Level::getMaterial(const TilePos& pos) const Entity* Level::getEntity(Entity::ID id) const { - // @TODO: wtf? no map?? - // prioritize players first. for (std::vector::const_iterator it = m_players.begin(); it != m_players.end(); it++) { Player* pEnt = *it; - if (pEnt->m_EntityID == id) - return pEnt; - } - for (std::vector::const_iterator it = m_entities.begin(); it != m_entities.end(); it++) - { - Entity* pEnt = *it; - if (pEnt->m_EntityID == id) + if (pEnt->hashCode() == id) return pEnt; } + EntityMap::const_iterator it = m_entities.find(id); + if (it != m_entities.end()) + return it->second; + return nullptr; } @@ -328,7 +381,7 @@ unsigned int Level::getEntityCount(const EntityCategories& category) const return it->second; } -const EntityVector* Level::getAllEntities() const +const EntityMap* Level::getAllEntities() const { return &m_entities; } @@ -1190,11 +1243,29 @@ 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); @@ -1215,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()) @@ -1249,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); @@ -1315,9 +1394,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)); @@ -1620,7 +1699,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 { @@ -1654,6 +1738,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) @@ -1686,26 +1787,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_tileEntityList.size(); i++) + { + TileEntity* tileEnt = m_tileEntityList[i]; + + if (!tileEnt->isRemoved()) + { + tileEnt->tick(); + } + else + { + LevelChunk* ch = getChunk(tileEnt->m_pos); + if (ch) + ch->removeTileEntity(tileEnt->m_pos); + + m_tileEntityList.erase(m_tileEntityList.begin() + i); + i--; + + delete tileEnt; + } + } + m_bUpdatingTileEntities = false; } HitResult Level::clip(Vec3 v1, Vec3 v2, bool flag) const @@ -1941,11 +2088,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); } } @@ -1962,9 +2122,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 993830cf6..9caf0153e 100644 --- a/source/world/level/Level.hpp +++ b/source/world/level/Level.hpp @@ -14,6 +14,7 @@ #define _USE_MATH_DEFINES #endif #include +#include #include "client/renderer/LightUpdate.hpp" #include "world/tile/Tile.hpp" @@ -38,6 +39,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; } @@ -178,7 +185,7 @@ class Level : public LevelSource HitResult clip(Vec3 a, Vec3 b, bool c) const; Entity* getEntity(Entity::ID id) const; unsigned int getEntityCount(const EntityCategories&) const; - const EntityVector* getAllEntities() const; + const EntityMap* getAllEntities() const; EntityVector getEntities(Entity* pAvoid, const AABB&) const; BiomeSource* getBiomeSource() const override; LevelStorage* getLevelStorage() const { return m_pLevelStorage; } @@ -203,6 +210,7 @@ class Level : public LevelSource private: LevelData* m_pLevelData; + bool m_bUpdatingTileEntities; protected: int m_randValue; @@ -213,7 +221,7 @@ class Level : public LevelSource bool m_bInstantTicking; bool m_bIsClientSide; // if the level is controlled externally by a server. bool m_bPostProcessing; - EntityVector m_entities; + EntityMap m_entities; std::vector m_players; int m_skyDarken; uint8_t field_30; @@ -237,5 +245,7 @@ class Level : public LevelSource MobSpawner* m_pMobSpawner; std::map m_entityCountsByCategory; + TileEntityVector m_tileEntityList; + TileEntityVector m_pendingTileEntities; }; diff --git a/source/world/level/Region.cpp b/source/world/level/Region.cpp index 9a7109ce6..8d55fcd48 100644 --- a/source/world/level/Region.cpp +++ b/source/world/level/Region.cpp @@ -111,6 +111,11 @@ BiomeSource* Region::getBiomeSource() const return m_pLevel->getBiomeSource(); } +TileEntity* Region::getTileEntity(const TilePos& pos) const +{ + return m_pLevel->getTileEntity(pos); +} + Region::~Region() { delete[] field_C; diff --git a/source/world/level/Region.hpp b/source/world/level/Region.hpp index ca0101edb..e0eeea90a 100644 --- a/source/world/level/Region.hpp +++ b/source/world/level/Region.hpp @@ -21,6 +21,7 @@ class Region : public LevelSource Material* getMaterial(const TilePos& pos) const override; bool isSolidTile(const TilePos& pos) const override; BiomeSource* getBiomeSource() const override; + TileEntity* getTileEntity(const TilePos& pos) const override; virtual ~Region(); Region(const Level* level, const TilePos& min, const TilePos& max); diff --git a/source/world/level/levelgen/chunk/ChunkTilePos.hpp b/source/world/level/levelgen/chunk/ChunkTilePos.hpp index 923a430f3..258f9010d 100644 --- a/source/world/level/levelgen/chunk/ChunkTilePos.hpp +++ b/source/world/level/levelgen/chunk/ChunkTilePos.hpp @@ -19,4 +19,11 @@ struct ChunkTilePos { return ChunkTilePos(x + other.x, y + other.y, z + other.z); } -}; + + bool operator<(const ChunkTilePos& other) const + { + if (x != other.x) return x < other.x; + if (y != other.y) return y < other.y; + return z < other.z; + } +}; \ No newline at end of file diff --git a/source/world/level/levelgen/chunk/LevelChunk.cpp b/source/world/level/levelgen/chunk/LevelChunk.cpp index e0de339b4..357a805c1 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_tileEntityList.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 1908668f1..222a48533 100644 --- a/source/world/level/levelgen/chunk/RandomLevelSource.cpp +++ b/source/world/level/levelgen/chunk/RandomLevelSource.cpp @@ -320,11 +320,13 @@ void RandomLevelSource::buildSurfaces(const ChunkPos& pos, TileID* tiles, Biome* { byte1 = pBiome->field_20; byte2 = pBiome->field_21; - if (flag1) { + if (flag1) + { byte1 = 0; byte2 = Tile::gravel->m_ID; } - if (flag) { + if (flag) + { byte1 = Tile::sand->m_ID; byte2 = Tile::sand->m_ID; } diff --git a/source/world/level/levelgen/chunk/TestChunkSource.cpp b/source/world/level/levelgen/chunk/TestChunkSource.cpp index d0f5bde69..48b72e22f 100644 --- a/source/world/level/levelgen/chunk/TestChunkSource.cpp +++ b/source/world/level/levelgen/chunk/TestChunkSource.cpp @@ -59,7 +59,8 @@ LevelChunk* TestChunkSource::generateChunk(const ChunkPos& pos) *p = TILE_BEDROCK; //else if (j == 0 || k == 0) // *p = TILE_AIR; - else if (i == 65) { + else if (i == 65) + { /*if (rand() % 10 == 0) *p = TILE_ROSE; else if (rand() % 10 == 0) @@ -69,7 +70,8 @@ LevelChunk* TestChunkSource::generateChunk(const ChunkPos& pos) } else if (i > 64) *p = TILE_AIR; - else if (i == 64) { + else if (i == 64) + { //if ((j + k) % 2 == 0) *p = TILE_GRASS; } diff --git a/source/world/level/path/BinaryHeap.cpp b/source/world/level/path/BinaryHeap.cpp index 5eed80453..07faf7fd7 100644 --- a/source/world/level/path/BinaryHeap.cpp +++ b/source/world/level/path/BinaryHeap.cpp @@ -39,12 +39,12 @@ void BinaryHeap::inlined0(int num) Node* var2 = m_items[num]; int var4; - for (float var3 = var2->field_C; num > 0; num = var4) { + for (float var3 = var2->field_C; num > 0; num = var4) + { var4 = (num - 1) >> 1; Node* var5 = m_items[var4]; - if (var3 >= var5->field_C) { + if (var3 >= var5->field_C) break; - } m_items[num] = var5; var5->field_0 = num; @@ -59,39 +59,41 @@ void BinaryHeap::downHeap(int num) Node* var2 = m_items[num]; float var3 = var2->field_C; - while (true) { + while (true) + { int var4 = 1 + (num << 1); int var5 = var4 + 1; - if (var4 >= m_count) { + if (var4 >= m_count) break; - } Node* var6 = m_items[var4]; float var7 = var6->field_C; Node* var8; float var9; - if (var5 >= m_count) { + if (var5 >= m_count) + { var8 = nullptr; var9 = std::numeric_limits::infinity(); } - else { + else + { var8 = m_items[var5]; var9 = var8->field_C; } - if (var7 < var9) { - if (var7 >= var3) { + if (var7 < var9) + { + if (var7 >= var3) break; - } m_items[num] = var6; var6->field_0 = num; num = var4; } - else { - if (var9 >= var3) { + else + { + if (var9 >= var3) break; - } m_items[num] = var8; var8->field_0 = num; diff --git a/source/world/level/path/BinaryHeap.hpp b/source/world/level/path/BinaryHeap.hpp index e0142b685..57fb67525 100644 --- a/source/world/level/path/BinaryHeap.hpp +++ b/source/world/level/path/BinaryHeap.hpp @@ -29,7 +29,8 @@ class BinaryHeap void inlined0(int i); void downHeap(int i); - Node* removeTop() { + Node* removeTop() + { Node* pNode = m_items[0]; m_items[0] = m_items[--m_count]; m_items[m_count] = 0; @@ -41,15 +42,18 @@ class BinaryHeap return pNode; } - void clear() { + void clear() + { m_count = 0; } - int size() const { + int size() const + { return m_count; } - void setDistance(Node* pNode, float distance) { + void setDistance(Node* pNode, float distance) + { float oldDistance = pNode->field_C; pNode->field_C = distance; diff --git a/source/world/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/BubbleParticle.cpp b/source/world/particle/BubbleParticle.cpp index 234917b45..96642b64a 100644 --- a/source/world/particle/BubbleParticle.cpp +++ b/source/world/particle/BubbleParticle.cpp @@ -13,14 +13,14 @@ BubbleParticle::BubbleParticle(Level* level, const Vec3& pos, const Vec3& dir) : Particle(level, pos, dir) { m_rCol = m_gCol = m_bCol = 1.0f; - field_DC = PTI_BUBBLE; + m_tex = PTI_BUBBLE; setSize(0.02f, 0.02f); - field_F0 *= 0.2f + 0.6f * sharedRandom.nextFloat(); + m_size *= 0.2f + 0.6f * sharedRandom.nextFloat(); m_vel.x = dir.x * 0.2f + 0.02f * (2.0f * Mth::random() - 1.0f); m_vel.y = dir.y * 0.2f + 0.02f * (2.0f * Mth::random() - 1.0f); m_vel.z = dir.z * 0.2f + 0.02f * (2.0f * Mth::random() - 1.0f); - field_EC = int(8.0f / (Mth::random() * 0.8f + 0.2f)); + m_lifetime = int(8.0f / (Mth::random() * 0.8f + 0.2f)); } void BubbleParticle::tick() @@ -35,7 +35,7 @@ void BubbleParticle::tick() if (m_pLevel->getMaterial(m_pos) != Material::water) remove(); - field_EC--; - if (field_EC <= 0) + m_lifetime--; + if (m_lifetime <= 0) remove(); } diff --git a/source/world/particle/ExplodeParticle.cpp b/source/world/particle/ExplodeParticle.cpp index e5149e29a..c13d15f45 100644 --- a/source/world/particle/ExplodeParticle.cpp +++ b/source/world/particle/ExplodeParticle.cpp @@ -18,20 +18,20 @@ ExplodeParticle::ExplodeParticle(Level* level, const Vec3& pos, const Vec3& dir) m_vel.z = dir.z + 0.05f * (2.0f * Mth::random() - 1.0f); m_rCol = m_gCol = m_bCol = 0.7f + 0.3f * sharedRandom.nextFloat(); - field_F0 = 1.0f + 6.0f * sharedRandom.nextFloat() * sharedRandom.nextFloat(); - field_EC = int(16.0f / (0.2f + 0.8f * sharedRandom.nextFloat())) + 2; + m_size = 1.0f + 6.0f * sharedRandom.nextFloat() * sharedRandom.nextFloat(); + m_lifetime = int(16.0f / (0.2f + 0.8f * sharedRandom.nextFloat())) + 2; } void ExplodeParticle::tick() { m_oPos = m_pos; - field_E8++; - if (field_E8 > field_EC) + m_timer++; + if (m_timer > m_lifetime) remove(); m_vel.y += 0.004f; - field_DC = -8 * field_E8 / field_EC + 7; + m_tex = -8 * m_timer / m_lifetime + 7; move(m_vel); diff --git a/source/world/particle/FlameParticle.cpp b/source/world/particle/FlameParticle.cpp index 39a63d158..5e17d316e 100644 --- a/source/world/particle/FlameParticle.cpp +++ b/source/world/particle/FlameParticle.cpp @@ -23,10 +23,10 @@ FlameParticle::FlameParticle(Level* level, const Vec3& pos, const Vec3& dir) : sharedRandom.genrand_int32(); sharedRandom.genrand_int32(); - field_104 = field_F0; + field_104 = m_size; m_rCol = m_gCol = m_bCol = 1.0f; - field_EC = int(8.0f / (0.2f + 0.8f * Mth::random())) + 4; - field_DC = PTI_FLAME; + m_lifetime = int(8.0f / (0.2f + 0.8f * Mth::random())) + 4; + m_tex = PTI_FLAME; } float FlameParticle::getBrightness(float unused) const @@ -38,8 +38,8 @@ void FlameParticle::tick() { m_oPos = m_pos; - field_E8++; - if (field_E8 > field_EC) + m_timer++; + if (m_timer > m_lifetime) remove(); move(m_vel); @@ -55,7 +55,7 @@ void FlameParticle::tick() void FlameParticle::render(Tesselator& t, float f, float a, float b, float c, float d, float e) { - float mult = float(field_E8 + f) / float(field_EC); - field_F0 = field_104 * (1.0f - 0.5f * mult * mult); + float mult = float(m_timer + f) / float(m_lifetime); + m_size = field_104 * (1.0f - 0.5f * mult * mult); Particle::render(t, f, a, b, c, d, e); } diff --git a/source/world/particle/LavaParticle.cpp b/source/world/particle/LavaParticle.cpp index d76ad60bd..413eedd87 100644 --- a/source/world/particle/LavaParticle.cpp +++ b/source/world/particle/LavaParticle.cpp @@ -17,9 +17,9 @@ LavaParticle::LavaParticle(Level* level, const Vec3& pos) : m_vel *= 0.8f; m_vel.y = sharedRandom.nextFloat() * 0.4f + 0.05f; m_rCol = m_gCol = m_bCol = 1.0f; - field_104 = field_F0 = field_F0 * (0.2f + 2 * sharedRandom.nextFloat()); - field_DC = PTI_LAVA; - field_EC = int(16.0f / (0.2f + 0.8f * Mth::random())); + field_104 = m_size = m_size * (0.2f + 2 * sharedRandom.nextFloat()); + m_tex = PTI_LAVA; + m_lifetime = int(16.0f / (0.2f + 0.8f * Mth::random())); } float LavaParticle::getBrightness(float unused) const @@ -31,11 +31,11 @@ void LavaParticle::tick() { m_oPos = m_pos; - field_E8++; - if (field_E8 > field_EC) + m_timer++; + if (m_timer > m_lifetime) remove(); - float a = float(field_E8) / float(field_EC); + float a = float(m_timer) / float(m_lifetime); float b = sharedRandom.nextFloat(); if (a < b) { @@ -55,7 +55,7 @@ void LavaParticle::tick() void LavaParticle::render(Tesselator& t, float f, float a, float b, float c, float d, float e) { - float mult = float(field_E8 + f) / float(field_EC); - field_F0 = field_104 * (1.0f - mult * mult); + float mult = float(m_timer + f) / float(m_lifetime); + m_size = field_104 * (1.0f - mult * mult); Particle::render(t, f, a, b, c, d, e); } diff --git a/source/world/particle/NoteParticle.cpp b/source/world/particle/NoteParticle.cpp new file mode 100644 index 000000000..149dee931 --- /dev/null +++ b/source/world/particle/NoteParticle.cpp @@ -0,0 +1,47 @@ +#include "Particle.hpp" + +NoteParticle::NoteParticle(Level* level, const Vec3& pos, const Vec3& dir, float scale) : + Particle(level, pos, Vec3::ZERO) +{ + m_vel.x *= 0.01f; + m_vel.y *= 0.01f; + m_vel.z *= 0.01f; + m_vel.y += 0.2f; + m_rCol = Mth::sin((dir.x + 0.0f) * M_PI * 2.0f) * 0.65f + 0.35f; + m_gCol = Mth::sin((dir.x + (1.0f / 3.0f)) * M_PI * 2.0f) * 0.65f + 0.35f; + m_bCol = Mth::sin((dir.x + (2.0f / 3.0f)) * M_PI * 2.0f) * 0.65f + 0.35f; + m_size *= 0.75f * scale; + m_oSize = m_size; + m_lifetime = 6; + m_bNoPhysics = false; + m_tex = PTI_NOTE; +} + +void NoteParticle::tick() +{ + m_oPos = m_pos; + + m_timer++; + if (m_timer > m_lifetime) + remove(); + + move(m_vel); + if (m_pos.y == m_oPos.y) + { + m_vel *= Vec3(1.1f, 1.0f, 1.1f); + } + + m_vel *= 0.66f; + + if (m_bOnGround) + { + m_vel *= Vec3(0.7f, 1.0f, 0.7f); + } +} + +void NoteParticle::render(Tesselator& t, float f, float a, float b, float c, float d, float e) +{ + float mult = float(m_timer + f) / float(m_lifetime) * 32.0f; + m_size = m_oSize * Mth::clamp(mult, 0.0f, 1.0f); + Particle::render(t, f, a, b, c, d, e); +} diff --git a/source/world/particle/Particle.cpp b/source/world/particle/Particle.cpp index 8b276c1db..e29863500 100644 --- a/source/world/particle/Particle.cpp +++ b/source/world/particle/Particle.cpp @@ -12,12 +12,12 @@ float Particle::xOff, Particle::yOff, Particle::zOff; void Particle::_init() { - field_DC = 0; + m_tex = 0; field_E0 = 0.0f; field_E4 = 0.0f; - field_E8 = 0; - field_EC = 0; - field_F0 = 0.0f; + m_timer = 0; + m_lifetime = 0; + m_size = 0.0f; field_F4 = 0.0f; m_rCol = 1.0f; m_gCol = 1.0f; @@ -45,8 +45,8 @@ Particle::Particle(Level* level, const Vec3& pos, const Vec3& dir) : Entity(leve field_E0 = 3.0f * sharedRandom.nextFloat(); field_E4 = 3.0f * sharedRandom.nextFloat(); - field_F0 = 2.0f * (0.5f + 0.5f * sharedRandom.nextFloat()); - field_EC = int(4.0f / (0.1f + 0.9f * sharedRandom.nextFloat())); + m_size = 2.0f * (0.5f + 0.5f * sharedRandom.nextFloat()); + m_lifetime = int(4.0f / (0.1f + 0.9f * sharedRandom.nextFloat())); } int Particle::getParticleTexture() @@ -57,7 +57,7 @@ int Particle::getParticleTexture() Particle* Particle::scale(float f) { setSize(0.2f * f, 0.2f * f); - field_F0 *= f; + m_size *= f; return this; } @@ -73,7 +73,7 @@ void Particle::render(Tesselator& t, float f, float a4, float a5, float a6, floa { constexpr float C_MAGIC_1 = 0.062438f; // @BUG: Slightly bigger than 1/16.0f - int texture = field_DC; + int texture = m_tex; int texX = texture % 16; if (texture < 0) texture += 15; @@ -86,11 +86,11 @@ void Particle::render(Tesselator& t, float f, float a4, float a5, float a6, floa float posZ = Mth::Lerp(m_oPos.z, m_pos.z, f) - zOff; float fBright = m_bIsUnlit ? 1.0f : getBrightness(f); - float sizeX = a4 * field_F0 * 0.1f; - float sizeY = a5 * field_F0 * 0.1f; - float sizeZ = a6 * field_F0 * 0.1f; - float siz2X = a7 * field_F0 * 0.1f; - float siz2Z = a8 * field_F0 * 0.1f; + float sizeX = a4 * m_size * 0.1f; + float sizeY = a5 * m_size * 0.1f; + float sizeZ = a6 * m_size * 0.1f; + float siz2X = a7 * m_size * 0.1f; + float siz2Z = a8 * m_size * 0.1f; t.color(m_rCol * fBright, m_gCol * fBright, m_bCol * fBright); t.vertexUV(posX - sizeX - siz2X, posY - sizeY, posZ - sizeZ - siz2Z, texU_1 + C_MAGIC_1, texV_1 + C_MAGIC_1); @@ -102,8 +102,8 @@ void Particle::render(Tesselator& t, float f, float a4, float a5, float a6, floa void Particle::tick() { m_oPos = m_pos; - field_E8++; - if (field_E8 >= field_EC) + m_timer++; + if (m_timer >= m_lifetime) remove(); m_vel.y -= field_F4 * 0.04f; diff --git a/source/world/particle/Particle.hpp b/source/world/particle/Particle.hpp index 85d4814bc..9d58ab074 100644 --- a/source/world/particle/Particle.hpp +++ b/source/world/particle/Particle.hpp @@ -23,7 +23,8 @@ enum eParticleTextureIndex { PTI_BUBBLE = 32, PTI_FLAME = 48, - PTI_LAVA + PTI_LAVA, + PTI_NOTE = 64 }; class Particle : public Entity @@ -45,12 +46,12 @@ class Particle : public Entity Particle* setPower(float); public: - int field_DC; + int m_tex; float field_E0; float field_E4; - int field_E8; - int field_EC; - float field_F0; + int m_timer; + int m_lifetime; + float m_size; float field_F4; float m_rCol; float m_gCol; @@ -138,3 +139,14 @@ class LavaParticle : public Particle public: float field_104; }; + +class NoteParticle : public Particle +{ +public: + NoteParticle(Level*, const Vec3& pos, const Vec3& dir, float scale = 2.0f); + void tick() override; + void render(Tesselator&, float, float, float, float, float, float) override; + +public: + float m_oSize; +}; \ No newline at end of file diff --git a/source/world/particle/RedDustParticle.cpp b/source/world/particle/RedDustParticle.cpp index bae587260..b7edf53a4 100644 --- a/source/world/particle/RedDustParticle.cpp +++ b/source/world/particle/RedDustParticle.cpp @@ -20,21 +20,21 @@ RedDustParticle::RedDustParticle(Level* level, const Vec3& pos, const Vec3& dir) m_gCol = f * dir.y * (Mth::random() * 0.2f + 0.8f); m_bCol = f * dir.z * (Mth::random() * 0.2f + 0.8f); - field_104 = field_F0 = field_F0 * 0.75f; + field_104 = m_size = m_size * 0.75f; m_bNoPhysics = false; - field_EC = int(8.0f / (0.2f + 0.8f * Mth::random())); + m_lifetime = int(8.0f / (0.2f + 0.8f * Mth::random())); } void RedDustParticle::render(Tesselator& t, float f, float a, float b, float c, float d, float e) { - float mult = 32.0f * (float(field_E8 + f) / float(field_EC)); + float mult = 32.0f * (float(m_timer + f) / float(m_lifetime)); if (mult < 0.0f) mult = 0.0f; if (mult > 1.0f) mult = 1.0f; - field_F0 = field_104 * mult; + m_size = field_104 * mult; Particle::render(t, f, a, b, c, d, e); } @@ -42,12 +42,12 @@ void RedDustParticle::tick() { m_oPos = m_pos; - field_E8++; - if (field_E8 > field_EC) + m_timer++; + if (m_timer > m_lifetime) remove(); m_vel.y += 0.004f; - field_DC = -8 * field_E8 / field_EC + 7; + m_tex = -8 * m_timer / m_lifetime + 7; move(m_vel); diff --git a/source/world/particle/SmokeParticle.cpp b/source/world/particle/SmokeParticle.cpp index 2c781a660..812e291ee 100644 --- a/source/world/particle/SmokeParticle.cpp +++ b/source/world/particle/SmokeParticle.cpp @@ -17,21 +17,21 @@ SmokeParticle::SmokeParticle(Level* level, const Vec3& pos, const Vec3& dir, flo m_bCol = m_gCol = m_rCol = Mth::random() * 0.5f; - field_104 = field_F0 = field_F0 * 0.75f * a9; + field_104 = m_size = m_size * 0.75f * a9; m_bNoPhysics = false; - field_EC = int((a9 * 8.0f) / (0.2f + 0.8f * Mth::random())); + m_lifetime = int((a9 * 8.0f) / (0.2f + 0.8f * Mth::random())); } void SmokeParticle::render(Tesselator& t, float f, float a, float b, float c, float d, float e) { - float mult = 32.0f * (float(field_E8 + f) / float(field_EC)); + float mult = 32.0f * (float(m_timer + f) / float(m_lifetime)); if (mult < 0.0f) mult = 0.0f; if (mult > 1.0f) mult = 1.0f; - field_F0 = field_104 * mult; + m_size = field_104 * mult; Particle::render(t, f, a, b, c, d, e); } @@ -39,12 +39,12 @@ void SmokeParticle::tick() { m_oPos = m_pos; - field_E8++; - if (field_E8 > field_EC) + m_timer++; + if (m_timer > m_lifetime) remove(); m_vel.y += 0.004f; - field_DC = -8 * field_E8 / field_EC + 7; + m_tex = -8 * m_timer / m_lifetime + 7; move(m_vel); diff --git a/source/world/particle/TerrainParticle.cpp b/source/world/particle/TerrainParticle.cpp index 3235e2b95..cf6b02f60 100644 --- a/source/world/particle/TerrainParticle.cpp +++ b/source/world/particle/TerrainParticle.cpp @@ -12,10 +12,10 @@ void TerrainParticle::_init(Tile* tile) { m_pTile = tile; - field_DC = tile->m_TextureFrame; + m_tex = tile->m_TextureFrame; field_F4 = tile->field_28; m_rCol = m_gCol = m_bCol = 0.6f; - field_F0 *= 0.5f; + m_size *= 0.5f; } TerrainParticle::TerrainParticle(Level* level, const Vec3& pos, Tile* tile) : @@ -36,7 +36,7 @@ TerrainParticle* TerrainParticle::init(const TilePos& tilePos, Facing::Name face face = Facing::DOWN; #endif - field_DC = m_pTile->getTexture(m_pLevel, tilePos, face); + m_tex = m_pTile->getTexture(m_pLevel, tilePos, face); if (m_pTile == Tile::grass && face != Facing::UP) return this; @@ -58,7 +58,7 @@ void TerrainParticle::render(Tesselator& t, float f, float a4, float a5, float a { constexpr float C_MAGIC_1 = 0.015609f; // @BUG: Slightly bigger than 1/64.0f - int texture = field_DC; + int texture = m_tex; int texX = texture % 16; if (texture < 0) texture += 15; @@ -71,11 +71,11 @@ void TerrainParticle::render(Tesselator& t, float f, float a4, float a5, float a float posZ = Mth::Lerp(m_oPos.z, m_pos.z, f) - zOff; float fBright = getBrightness(f); - float sizeX = a4 * field_F0 * 0.1f; - float sizeY = a5 * field_F0 * 0.1f; - float sizeZ = a6 * field_F0 * 0.1f; - float siz2X = a7 * field_F0 * 0.1f; - float siz2Z = a8 * field_F0 * 0.1f; + float sizeX = a4 * m_size * 0.1f; + float sizeY = a5 * m_size * 0.1f; + float sizeZ = a6 * m_size * 0.1f; + float siz2X = a7 * m_size * 0.1f; + float siz2Z = a8 * m_size * 0.1f; t.color(m_rCol * fBright, m_gCol * fBright, m_bCol * fBright); t.vertexUV(posX - sizeX - siz2X, posY - sizeY, posZ - sizeZ - siz2Z, texU_1 + C_MAGIC_1, texV_1 + C_MAGIC_1); diff --git a/source/world/phys/Vec3.hpp b/source/world/phys/Vec3.hpp index a3725336e..efb4333ae 100644 --- a/source/world/phys/Vec3.hpp +++ b/source/world/phys/Vec3.hpp @@ -123,6 +123,13 @@ class Vec3 (*this) += -b; } + void operator*=(const Vec3& b) + { + x *= b.x; + y *= b.y; + z *= b.z; + } + void operator+=(float f) { x += f; diff --git a/source/world/tile/ChestTile.cpp b/source/world/tile/ChestTile.cpp new file mode 100644 index 000000000..6db79d413 --- /dev/null +++ b/source/world/tile/ChestTile.cpp @@ -0,0 +1,224 @@ +#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; + + int id_north = level->getTile(pos.north()); + int id_south = level->getTile(pos.south()); + int id_west = level->getTile(pos.west()); + int id_east = level->getTile(pos.east()); + + bool isDoubleNS = (id_north == m_ID || id_south == m_ID); + bool isDoubleWE = (id_west == m_ID || id_east == m_ID); + + if (!isDoubleNS && !isDoubleWE) + { + Facing::Name front = Facing::SOUTH; + + if (Tile::solid[id_north] && !Tile::solid[id_south]) + front = Facing::SOUTH; + else if (Tile::solid[id_south] && !Tile::solid[id_north]) + front = Facing::NORTH; + else if (Tile::solid[id_west] && !Tile::solid[id_east]) + front = Facing::EAST; + else if (Tile::solid[id_east] && !Tile::solid[id_west]) + front = Facing::WEST; + + return (face == front) ? m_TextureFrame + 1 : m_TextureFrame; + } + + bool left = false; + Facing::Name front = Facing::SOUTH; + + if (isDoubleWE) + { + left = id_west == m_ID; + TilePos side = left ? pos.west() : pos.east(); + + int id_behind = level->getTile(side.north()); + int id_infront = level->getTile(side.south()); + + if ((Tile::solid[id_north] || Tile::solid[id_behind]) && !Tile::solid[id_south] && !Tile::solid[id_infront]) + front = Facing::SOUTH; + else if ((Tile::solid[id_south] || Tile::solid[id_infront]) && !Tile::solid[id_north] && !Tile::solid[id_behind]) + front = Facing::NORTH; + + if (front == Facing::SOUTH) + left = !left; + if (face == front) + return m_TextureFrame + (left ? 15 : 16); + if (face == Facing::OPPOSITE[front]) + return m_TextureFrame + (left ? 32 : 31); + return m_TextureFrame; + } + + if (isDoubleNS) + { + front = Facing::EAST; + left = id_north == m_ID; + TilePos side = left ? pos.north() : pos.south(); + + int id_behind = level->getTile(side.west()); + int id_infront = level->getTile(side.east()); + + if ((Tile::solid[id_west] || Tile::solid[id_behind]) && !Tile::solid[id_east] && !Tile::solid[id_infront]) + front = Facing::EAST; + else if ((Tile::solid[id_east] || Tile::solid[id_infront]) && !Tile::solid[id_west] && !Tile::solid[id_behind]) + { + front = Facing::WEST; + left = !left; + } + + if (face == front) + return m_TextureFrame + (left ? 15 : 16); + if (face == Facing::OPPOSITE[front]) + return m_TextureFrame + (left ? 32 : 31); + return m_TextureFrame; + } + + return m_TextureFrame; +} + +int ChestTile::getTexture(Facing::Name face) const +{ + switch (face) + { + case Facing::UP: + case Facing::DOWN: + return m_TextureFrame - 1; + case Facing::SOUTH: + return m_TextureFrame + 1; + default: + return m_TextureFrame; + } +} + +bool ChestTile::mayPlace(const Level* level, const TilePos& pos) const +{ + return !hasNeighbors(level, pos, 1); +} + +bool ChestTile::hasNeighbors(const Level* level, const TilePos& pos, int count) const +{ + int neighbors = 0; + for (int i = 0; i < 4; ++i) + { + Facing::Name f = static_cast(Facing::HORIZONTAL[i]); + TilePos relative = pos.relative(f); + if (level->getTile(relative) == m_ID) + { + neighbors++; + if (neighbors > count) return true; + + if (hasNeighbors(level, relative, 0)) return true; + } + } + + return false; +} + +void ChestTile::onRemove(Level* level, const TilePos& pos) +{ + ChestTileEntity* ent = static_cast(level->getTileEntity(pos)); + + if (!ent) + EntityTile::onRemove(level, pos); + + for (int slot = 0; slot < ent->getContainerSize(); ++slot) + { + ItemStack& item = ent->getItem(slot); + if (!item.isEmpty()) + { + TilePos offset( + (m_chestRandom.nextFloat() * 0.8f) + 0.1f, + (m_chestRandom.nextFloat() * 0.8f) + 0.1f, + (m_chestRandom.nextFloat() * 0.8f) + 0.1f + ); + + while (item.m_count > 0) + { + int toRemove = std::min(m_chestRandom.nextInt(21) + 10, static_cast(item.m_count)); + item.m_count -= toRemove; + ItemEntity* itemEnt = new ItemEntity(level, pos + offset, ItemStack(item.getItem()->m_itemID, toRemove, item.getAuxValue())); + float spread = 0.05f; + itemEnt->m_vel.x = (double)((float)m_chestRandom.nextGaussian() * spread); + itemEnt->m_vel.y = (double)((float)m_chestRandom.nextGaussian() * spread + 0.2f); + itemEnt->m_vel.z = (double)((float)m_chestRandom.nextGaussian() * spread); + level->addEntity(itemEnt); + } + } + } + + 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 + TilePos(-1, 1, 0))) + return true; + + if (level->getTile(pos.east()) == m_ID && level->isSolidTile(pos + TilePos(1, 1, 0))) + return true; + + if (level->getTile(pos.north()) == m_ID && level->isSolidTile(pos + TilePos(0, 1, -1))) + return true; + + if (level->getTile(pos.south()) == m_ID && level->isSolidTile(pos + TilePos(0, 1, 1))) + return true; + + TileEntity* tileEnt = level->getTileEntity(pos); + if (!tileEnt) + return false; + + ChestTileEntity* chest = static_cast(tileEnt); + Container* container = static_cast(chest); + for (int rel = Facing::NORTH; rel <= Facing::EAST; rel++) + { + TilePos relPos = pos.relative(static_cast(rel)); + if (level->getTile(pos.relative(static_cast(rel))) == m_ID) + { + TileEntity* nearbyTileEntity = level->getTileEntity(relPos); + if (!nearbyTileEntity) + continue; + + Container* nearbyContainer = static_cast(static_cast(nearbyTileEntity)); + if (rel % 2 == 0) + container = new CompoundContainer("Large chest", nearbyContainer, container); + else + container = new CompoundContainer("Large chest", container, nearbyContainer); + break; + } + } + + player->openContainer(container); + return true; +} + +bool ChestTile::hasTileEntity() const +{ + return true; +} + +TileEntity* ChestTile::newTileEntity() +{ + return new ChestTileEntity(); +} diff --git a/source/world/tile/ChestTile.hpp b/source/world/tile/ChestTile.hpp new file mode 100644 index 000000000..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..372077028 --- /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, 0)); +} 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 7c5646745..14878f2d2 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; \ No newline at end of file diff --git a/source/world/tile/Tile.hpp b/source/world/tile/Tile.hpp index aad6af4c2..61388b225 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..9bec5bc70 100644 --- a/source/world/tile/TopSnowTile.cpp +++ b/source/world/tile/TopSnowTile.cpp @@ -32,7 +32,7 @@ bool TopSnowTile::isSolidRender() const int TopSnowTile::getResource(TileData data, Random* random) const { - return 0; + return Item::snowBall->m_itemID; } int TopSnowTile::getResourceCount(Random* random) const @@ -84,3 +84,19 @@ void TopSnowTile::tick(Level* level, const TilePos& pos, Random* random) level->setTile(pos, TILE_AIR); } } + +void TopSnowTile::playerDestroy(Level* level, Player* player, const TilePos& pos, TileData data) +{ + float dispersion = 0.7f; + + Vec3 offset ( + (level->m_random.nextFloat() * dispersion) + (1.0f - dispersion) * 0.5f, + (level->m_random.nextFloat() * dispersion) + (1.0f - dispersion) * 0.5f, + (level->m_random.nextFloat() * dispersion) + (1.0f - dispersion) * 0.5f + ); + + ItemEntity* pItemEntity = new ItemEntity(level, Vec3(pos) + offset, ItemStack(getResource(data, &level->m_random), 1, 0)); + pItemEntity->m_throwTime = 10; + level->addEntity(pItemEntity); + level->setTile(pos, TILE_AIR); +} diff --git a/source/world/tile/TopSnowTile.hpp b/source/world/tile/TopSnowTile.hpp index 82af8e659..cafd89ea2 100644 --- a/source/world/tile/TopSnowTile.hpp +++ b/source/world/tile/TopSnowTile.hpp @@ -24,6 +24,7 @@ class TopSnowTile : public Tile void neighborChanged(Level*, const TilePos& pos, TileID tile) override; bool shouldRenderFace(const LevelSource*, const TilePos& pos, Facing::Name face) const override; void tick(Level*, const TilePos& pos, Random*) override; + virtual void playerDestroy(Level*, Player*, const TilePos& pos, TileData data); bool checkCanSurvive(Level*, const TilePos& pos); }; diff --git a/source/world/tile/entity/ChestTileEntity.cpp b/source/world/tile/entity/ChestTileEntity.cpp new file mode 100644 index 000000000..e18c18130 --- /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(SlotID slot) +{ + SimpleContainer::setContainerChanged(slot); + TileEntity::setChanged(); +} diff --git a/source/world/tile/entity/ChestTileEntity.hpp b/source/world/tile/entity/ChestTileEntity.hpp new file mode 100644 index 000000000..0fb177245 --- /dev/null +++ b/source/world/tile/entity/ChestTileEntity.hpp @@ -0,0 +1,17 @@ +#pragma once + +#include "TileEntity.hpp" +#include "world/level/Level.hpp" +#include "world/inventory/SimpleContainer.hpp" + +class ChestTileEntity : public TileEntity, public SimpleContainer { +public: + ChestTileEntity(); + + void load(const CompoundTag& tag) override; + void save(CompoundTag& tag) const override; + + bool stillValid(Player* player) const override; + + void setContainerChanged(SlotID slot) override; +}; diff --git a/source/world/tile/entity/FurnaceTileEntity.cpp b/source/world/tile/entity/FurnaceTileEntity.cpp new file mode 100644 index 000000000..28e7ead28 --- /dev/null +++ b/source/world/tile/entity/FurnaceTileEntity.cpp @@ -0,0 +1,164 @@ +#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(SlotID slot) +{ + SimpleContainer::setContainerChanged(slot); + TileEntity::setChanged(); +} + +void FurnaceTileEntity::load(const CompoundTag& tag) +{ + TileEntity::load(tag); + SimpleContainer::load(tag); + + m_litTime = tag.getInt16("BurnTime"); + m_tickCount = tag.getInt16("CookTime"); + m_litDuration = FurnaceRecipes::singleton().getBurnDuration(getItem(1)); +} + +void FurnaceTileEntity::save(CompoundTag& tag) const +{ + TileEntity::save(tag); + tag.putInt16("BurnTime", m_litTime); + tag.putInt16("CookTime", m_tickCount); + SimpleContainer::save(tag); +} + +std::string FurnaceTileEntity::getName() const +{ + return "Furnace"; +} + +int FurnaceTileEntity::getBurnProgress(int height) +{ + return m_tickCount * height / C_BURN_TIME; +} + +int FurnaceTileEntity::getLitProgress(int height) +{ + if (m_litDuration == 0) + m_litDuration = C_BURN_TIME; + + return m_litTime * height / m_litDuration; +} + +bool FurnaceTileEntity::isLit() +{ + return m_litTime > 0; +} \ No newline at end of file diff --git a/source/world/tile/entity/FurnaceTileEntity.hpp b/source/world/tile/entity/FurnaceTileEntity.hpp new file mode 100644 index 000000000..0a50b691e --- /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(SlotID slot) override; + void load(const CompoundTag& tag) override; + void save(CompoundTag& tag) const override; + std::string getName() const override; + +public: + int getBurnProgress(int height); + int getLitProgress(int height); + bool isLit(); + +public: + int m_litTime; + int m_litDuration; + int m_tickCount; +}; diff --git a/source/world/tile/entity/MusicTileEntity.cpp b/source/world/tile/entity/MusicTileEntity.cpp new file mode 100644 index 000000000..d38630bd7 --- /dev/null +++ b/source/world/tile/entity/MusicTileEntity.cpp @@ -0,0 +1,46 @@ +#include "MusicTileEntity.hpp" +#include "world/level/Level.hpp" + +MusicTileEntity::MusicTileEntity() : TileEntity(), m_note(0), m_bOn(false) +{ + m_pType = TileEntityType::noteblock; +} + +void MusicTileEntity::load(const CompoundTag& tag) +{ + TileEntity::load(tag); + m_note = static_cast(Mth::clamp(tag.getInt8("note"), 0, 24)); +} + +void MusicTileEntity::save(CompoundTag& tag) const +{ + TileEntity::save(tag); + tag.putInt8("note", m_note); +} + +void MusicTileEntity::tune() +{ + m_note = (m_note + 1) % 25; + setChanged(); +} + +void MusicTileEntity::play(Level* pLevel, const TilePos& pos) +{ + // noteblocks only play if the block above them is air + if (pLevel->getMaterial(TilePos(pos.x, pos.y + 1, pos.z)) != Material::air) + return; + + int instrument = 0; + Material* below = pLevel->getMaterial(TilePos(pos.x, pos.y - 1, pos.z)); + + if (below == Material::stone) + instrument = 1; + else if (below == Material::sand) + instrument = 2; + else if (below == Material::glass) + instrument = 3; + else if (below == Material::wood) + instrument = 4; + + pLevel->tileEvent(TileEvent(pos, instrument, m_note)); +} \ No newline at end of file diff --git a/source/world/tile/entity/MusicTileEntity.hpp b/source/world/tile/entity/MusicTileEntity.hpp new file mode 100644 index 000000000..9a8a1a60a --- /dev/null +++ b/source/world/tile/entity/MusicTileEntity.hpp @@ -0,0 +1,21 @@ +#pragma once + +#include "TileEntity.hpp" + +class MusicTileEntity : public TileEntity +{ +public: + MusicTileEntity(); + +public: + void load(const CompoundTag& tag) override; + void save(CompoundTag& tag) const override; + +public: + void tune(); + void play(Level* pLevel, const TilePos& pos); + +public: + uint8_t m_note; + bool m_bOn; +}; \ No newline at end of file diff --git a/source/world/tile/entity/TileEntity.cpp b/source/world/tile/entity/TileEntity.cpp new file mode 100644 index 000000000..8cf5078c1 --- /dev/null +++ b/source/world/tile/entity/TileEntity.cpp @@ -0,0 +1,106 @@ +#include "TileEntity.hpp" +#include "common/Logger.hpp" +#include "world/level/Level.hpp" + +TileEntity::TileEntity() : m_bRemove(false), m_pLevel(nullptr) +{ +} + +TileEntity::~TileEntity() +{ +} + +TileEntity* TileEntity::LoadTileEntity(const CompoundTag& tag) +{ + if (!tag.contains("id")) + { + LOG_W("Skipping TileEntity with no id!"); + return nullptr; + } + + std::string id = tag.getString("id"); + const TileEntityType* type = 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"; +} \ No newline at end of file diff --git a/source/world/tile/entity/TileEntity.hpp b/source/world/tile/entity/TileEntity.hpp new file mode 100644 index 000000000..2a3666fc7 --- /dev/null +++ b/source/world/tile/entity/TileEntity.hpp @@ -0,0 +1,50 @@ +#pragma once + +#include "TileEntityType.hpp" +#include "world/level/TilePos.hpp" +#include "world/phys/Vec3.hpp" +#include "nbt/CompoundTag.hpp" + +class Tile; +class Level; +class Packet; + +class TileEntity +{ +public: + TileEntity(); + virtual ~TileEntity(); + +protected: + virtual std::string getId() const; + +public: + virtual void load(const CompoundTag& tag); + virtual void save(CompoundTag& tag) const; + virtual void tick(); + virtual Packet* getUpdatePacket(); + const TileEntityType* getType() const; + virtual int getData() const; + virtual Tile* getTile() const; + +public: + static TileEntity* LoadTileEntity(const CompoundTag& tag); + +public: + float distanceToSqr(const Vec3& vec) const; + bool isRemoved() const; + void setRemoved(); + void clearRemoved(); + + void setData(int data); + void setChanged(); + +protected: + const TileEntityType* m_pType; + bool m_bRemove; + +public: + Level* m_pLevel; + TilePos m_pos; +}; + diff --git a/source/world/tile/entity/TileEntityType.cpp b/source/world/tile/entity/TileEntityType.cpp new file mode 100644 index 000000000..18589f526 --- /dev/null +++ b/source/world/tile/entity/TileEntityType.cpp @@ -0,0 +1,51 @@ +#include "TileEntityType.hpp" +#include "FurnaceTileEntity.hpp" +#include "ChestTileEntity.hpp" +#include "MusicTileEntity.hpp" +// #include "MobSpawnerTileEntity.hpp" +// #include "DispenserTileEntity.hpp" +// #include "SignTileEntity.hpp" +// #include "RecordPlayerTileEntity.hpp" +// #include "PistonMovingTileEntity.hpp" + +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(); +} + +const TileEntityType* TileEntityFactory::getType(const std::string& name) +{ + return _types[name]; +} + +TileEntityType::TileEntityType(const std::string& name, CreateFunction func) : _name(name), _function(func) +{ +} + +const std::string& TileEntityType::getName() const +{ + return _name; +} + +TileEntity* TileEntityType::newTileEntity() const +{ + return _function(); +} \ No newline at end of file diff --git a/source/world/tile/entity/TileEntityType.hpp b/source/world/tile/entity/TileEntityType.hpp new file mode 100644 index 000000000..d1ad2b6ff --- /dev/null +++ b/source/world/tile/entity/TileEntityType.hpp @@ -0,0 +1,58 @@ +#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); + +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); + +public: + const std::string& getName() const; + TileEntity* newTileEntity() const; + +private: + std::string _name; + CreateFunction _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->_name] = type; + return type; +} \ No newline at end of file