diff --git a/CppProperties.json b/CppProperties.json new file mode 100644 index 00000000..f598ba59 --- /dev/null +++ b/CppProperties.json @@ -0,0 +1,21 @@ +{ + "configurations": [ + { + "inheritEnvironments": [ + "msvc_x64" + ], + "name": "x64-Debug", + "includePath": [ + "${env.INCLUDE}", + "${workspaceRoot}\\**" + ], + "defines": [ + "WIN32", + "_DEBUG", + "UNICODE", + "_UNICODE" + ], + "intelliSenseMode": "windows-msvc-x64" + } + ] +} \ No newline at end of file diff --git a/Minecraft.Client/CMakeLists.txt b/Minecraft.Client/CMakeLists.txt index 7e435a1d..51f9c32f 100644 --- a/Minecraft.Client/CMakeLists.txt +++ b/Minecraft.Client/CMakeLists.txt @@ -70,6 +70,11 @@ set_source_files_properties(compat_shims.cpp PROPERTIES SKIP_PRECOMPILE_HEADERS configure_compiler_target(Minecraft.Client) +if(PLATFORM_NAME STREQUAL "Windows64") + target_compile_options(Minecraft.Client PRIVATE $<$:/Zi>) + target_link_options(Minecraft.Client PRIVATE $<$:/DEBUG /OPT:REF /OPT:ICF>) +endif() + set(MINECRAFT_CLIENT_USE_4J_DEBUG_LIBS TRUE) if(CMAKE_CROSSCOMPILING AND CMAKE_CXX_COMPILER_ID STREQUAL "Clang") set(MINECRAFT_CLIENT_USE_4J_DEBUG_LIBS FALSE) @@ -88,6 +93,7 @@ if(MINECRAFT_CLIENT_USE_4J_DEBUG_LIBS) d3dcompiler XInput9_1_0 wsock32 + dbghelp legacy_stdio_definitions 4JLibs.${PLATFORM_NAME}.Input 4JLibs.${PLATFORM_NAME}.Profile @@ -102,6 +108,7 @@ else() d3dcompiler XInput9_1_0 wsock32 + dbghelp legacy_stdio_definitions "${CMAKE_CURRENT_SOURCE_DIR}/${PLATFORM_NAME}/4JLibs/libs/4J_Input.lib" "${CMAKE_CURRENT_SOURCE_DIR}/${PLATFORM_NAME}/4JLibs/libs/4J_Storage.lib" diff --git a/Minecraft.Client/ClientConnection.cpp b/Minecraft.Client/ClientConnection.cpp index 7afec5c7..66faf03f 100644 --- a/Minecraft.Client/ClientConnection.cpp +++ b/Minecraft.Client/ClientConnection.cpp @@ -3486,6 +3486,10 @@ void ClientConnection::handleTileEntityData(shared_ptr pac { dynamic_pointer_cast(te)->load(packet->tag); } + else if (packet->type == TileEntityDataPacket::TYPE_FLOWER_POT && dynamic_pointer_cast(te) != nullptr) + { + dynamic_pointer_cast(te)->load(packet->tag); + } } } } @@ -3834,8 +3838,10 @@ void ClientConnection::handlePlayerAbilities(shared_ptr p player->abilities.instabuild = playerAbilitiesPacket->canInstabuild(); player->abilities.invulnerable = playerAbilitiesPacket->isInvulnerable(); player->abilities.mayfly = playerAbilitiesPacket->canFly(); + player->abilities.spectatorMode = playerAbilitiesPacket->isNoPhysics(); player->abilities.setFlyingSpeed(playerAbilitiesPacket->getFlyingSpeed()); player->abilities.setWalkingSpeed(playerAbilitiesPacket->getWalkingSpeed()); + player->noPhysics = playerAbilitiesPacket->isNoPhysics(); } void ClientConnection::handleSoundEvent(shared_ptr packet) diff --git a/Minecraft.Client/Common/Media/MediaWindows64/CraftingMenu1080.swf b/Minecraft.Client/Common/Media/MediaWindows64/CraftingMenu1080.swf new file mode 100644 index 00000000..b7ab2583 Binary files /dev/null and b/Minecraft.Client/Common/Media/MediaWindows64/CraftingMenu1080.swf differ diff --git a/Minecraft.Client/Common/Media/MediaWindows64/CraftingMenu720.swf b/Minecraft.Client/Common/Media/MediaWindows64/CraftingMenu720.swf new file mode 100644 index 00000000..8fe77af3 Binary files /dev/null and b/Minecraft.Client/Common/Media/MediaWindows64/CraftingMenu720.swf differ diff --git a/Minecraft.Client/Common/Media/MediaWindows64/CraftingMenuSplit1080.swf b/Minecraft.Client/Common/Media/MediaWindows64/CraftingMenuSplit1080.swf new file mode 100644 index 00000000..38e8810f Binary files /dev/null and b/Minecraft.Client/Common/Media/MediaWindows64/CraftingMenuSplit1080.swf differ diff --git a/Minecraft.Client/Common/Media/MediaWindows64/CraftingMenuSplit720.swf b/Minecraft.Client/Common/Media/MediaWindows64/CraftingMenuSplit720.swf new file mode 100644 index 00000000..8c9b8964 Binary files /dev/null and b/Minecraft.Client/Common/Media/MediaWindows64/CraftingMenuSplit720.swf differ diff --git a/Minecraft.Client/Common/UI/UIControl_MinecraftPlayer.cpp b/Minecraft.Client/Common/UI/UIControl_MinecraftPlayer.cpp index 3fecbde7..e225d297 100644 --- a/Minecraft.Client/Common/UI/UIControl_MinecraftPlayer.cpp +++ b/Minecraft.Client/Common/UI/UIControl_MinecraftPlayer.cpp @@ -77,11 +77,28 @@ void UIControl_MinecraftPlayer::render(IggyCustomDrawCallbackRegion *region) glTranslatef(0, pMinecraft->localplayers[containerMenu->getPad()]->heightOffset, 0); EntityRenderDispatcher::instance->playerRotY = 180; + auto lp = pMinecraft->localplayers[containerMenu->getPad()]; + float owp = lp->walkAnimPos; + float ows = lp->walkAnimSpeed; + float owsO = lp->walkAnimSpeedO; + if (lp->isElytraFlying()) + { + lp->walkAnimPos = 0.0f; + lp->walkAnimSpeed = 0.0f; + lp->walkAnimSpeedO = 0.0f; + } + // 4J Stu - Turning on hideGui while we do this stops the name rendering in split-screen bool wasHidingGui = pMinecraft->options->hideGui; pMinecraft->options->hideGui = true; - EntityRenderDispatcher::instance->render(pMinecraft->localplayers[containerMenu->getPad()], 0, 0, 0, 0, 1, false, false); + EntityRenderDispatcher::instance->isInventoryRender = true; + EntityRenderDispatcher::instance->render(lp, 0, 0, 0, 0, 1, false, false); + EntityRenderDispatcher::instance->isInventoryRender = false; pMinecraft->options->hideGui = wasHidingGui; + + lp->walkAnimPos = owp; + lp->walkAnimSpeed = ows; + lp->walkAnimSpeedO = owsO; //pMinecraft->localplayers[m_iPad]->glow = 0; pMinecraft->localplayers[containerMenu->getPad()]->yBodyRot = oybr; diff --git a/Minecraft.Client/Common/UI/UIScene_LaunchMoreOptionsMenu.cpp b/Minecraft.Client/Common/UI/UIScene_LaunchMoreOptionsMenu.cpp index 1832e40c..3e022f68 100644 --- a/Minecraft.Client/Common/UI/UIScene_LaunchMoreOptionsMenu.cpp +++ b/Minecraft.Client/Common/UI/UIScene_LaunchMoreOptionsMenu.cpp @@ -80,6 +80,8 @@ UIScene_LaunchMoreOptionsMenu::UIScene_LaunchMoreOptionsMenu(int iPad, void *ini m_bUpdateCheats = false; // Update cheat checkboxes UpdateCheats(); + m_bPendingTabSwitch = false; + m_iPendingTabIndex = -1; m_checkboxes[eLaunchCheckbox_Online].init(app.GetString(IDS_ONLINE_GAME),eLaunchCheckbox_Online,bOnlineGame); m_checkboxes[eLaunchCheckbox_InviteOnly].init(app.GetString(IDS_INVITE_ONLY),eLaunchCheckbox_InviteOnly,bInviteOnly); @@ -240,6 +242,14 @@ void UIScene_LaunchMoreOptionsMenu::tick() UpdateOnline(); m_bUpdateOnline = false; } + if (m_bPendingTabSwitch) + { + m_bPendingTabSwitch = false; + m_tabIndex = m_iPendingTabIndex; + updateTooltips(); + IggyDataValue result; + IggyResult out = IggyPlayerCallMethodRS(getMovie(), &result, IggyPlayerRootPath(getMovie()), m_funcChangeTab, 0, nullptr); + } } void UIScene_LaunchMoreOptionsMenu::handleDestroy() @@ -339,6 +349,42 @@ void UIScene_LaunchMoreOptionsMenu::handleTouchInput(unsigned int iPad, S32 x, S } #endif +#ifdef _WINDOWS64 +bool UIScene_LaunchMoreOptionsMenu::handleMouseClick(F32 x, F32 y) +{ + if (!hasFocus(m_iPad) || getMovie() == nullptr || g_KBMInput.IsMouseGrabbed() || !g_KBMInput.IsKBMActive()) + return false; + + if (m_bIgnoreInput) + return false; + + float sceneMouseX = x; + float sceneMouseY = y; + + if (sceneMouseY >= 190.0f && sceneMouseY <= 270.0f) + { + int newTab = -1; + if (sceneMouseX >= 480.0f && sceneMouseX <= 700.0f) + newTab = TAB_WORLD_OPTIONS; + else if (sceneMouseX > 700.0f && sceneMouseX <= 960.0f) + newTab = TAB_GAME_OPTIONS; + + if (newTab >= 0) + { + if (m_tabIndex != newTab) + { + m_bPendingTabSwitch = true; + m_iPendingTabIndex = newTab; + ui.PlayUISFX(eSFX_Press); + } + return true; + } + } + + return UIScene::handleMouseClick(x, y); +} +#endif + #if defined(__PSVITA__) || defined(_WINDOWS64) UIControl* UIScene_LaunchMoreOptionsMenu::GetMainPanel() { diff --git a/Minecraft.Client/Common/UI/UIScene_LaunchMoreOptionsMenu.h b/Minecraft.Client/Common/UI/UIScene_LaunchMoreOptionsMenu.h index caf9a71f..4c099c80 100644 --- a/Minecraft.Client/Common/UI/UIScene_LaunchMoreOptionsMenu.h +++ b/Minecraft.Client/Common/UI/UIScene_LaunchMoreOptionsMenu.h @@ -146,6 +146,9 @@ public: #endif // INPUT virtual void handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled); +#ifdef _WINDOWS64 + virtual bool handleMouseClick(F32 x, F32 y); +#endif virtual void handleFocusChange(F64 controlId, F64 childId); virtual void handleTimerComplete(int id); static int KeyboardCompleteSeedCallback(LPVOID lpParam,const bool bRes); @@ -162,6 +165,9 @@ private: bool m_bUpdateOnline; // If true, update online settings on next tick void UpdateOnline(); + bool m_bPendingTabSwitch; + int m_iPendingTabIndex; + #ifdef __PSVITA__ virtual void handleTouchInput(unsigned int iPad, S32 x, S32 y, int iId, bool bPressed, bool bRepeat, bool bReleased); #endif //__PSVITA__ diff --git a/Minecraft.Client/Common/UI/UIScene_MainMenu.cpp b/Minecraft.Client/Common/UI/UIScene_MainMenu.cpp index c0f8b8a1..3149f4ed 100644 --- a/Minecraft.Client/Common/UI/UIScene_MainMenu.cpp +++ b/Minecraft.Client/Common/UI/UIScene_MainMenu.cpp @@ -1,4 +1,9 @@ #include "stdafx.h" +#include "../../Lighting.h" +#ifdef _WINDOWS64 +#include "../../Windows64/Iggy/gdraw/gdraw_d3d11.h" +#include "../../Windows64/4JLibs/inc/4J_Render.h" +#endif #include "../../../Minecraft.World/Mth.h" #include "../../../Minecraft.World/StringHelpers.h" #include "../../../Minecraft.World/Random.h" @@ -553,7 +558,61 @@ void UIScene_MainMenu::customDrawSplash(IggyCustomDrawCallbackRegion *region) // Finish GDraw and anything else that needs to be finalised - ui.endCustomDraw(region); + ui.endCustomDraw(region); +} + +void UIScene_MainMenu::render(S32 width, S32 height, C4JRender::eViewportType viewport) +{ + UIScene::render(width, height, viewport); + + Minecraft *pMinecraft = Minecraft::GetInstance(); + if (!pMinecraft || !pMinecraft->font) return; + + Font *font = pMinecraft->font; + const wstring part1 = L"neo"; + const wstring part2 = L"Legacy"; + const float scale = 0.6f; + const int pad = 4; + + ScreenSizeCalculator ssc(pMinecraft->options, pMinecraft->width_phys, pMinecraft->height_phys); + S32 sw = ssc.getWidth(); + S32 sh = ssc.getHeight(); + +#ifdef _WINDOWS64 + RenderManager.StartFrame(); + gdraw_D3D11_setViewport_4J(); +#endif + RenderManager.Set_matrixDirty(); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0, sw, sh, 0, 1000, 3000); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glTranslatef(0.0f, 0.0f, -2000.0f); + + Lighting::turnOff(); + glDisable(GL_DEPTH_TEST); + glDepthMask(false); + glDisable(GL_CULL_FACE); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glEnable(GL_TEXTURE_2D); + glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + + int totalW = static_cast((font->width(part1) + font->width(part2)) * scale); + int tx = sw - totalW - pad; + int ty = sh - static_cast(8 * scale) - pad; + + glPushMatrix(); + glTranslatef(static_cast(tx), static_cast(ty), 0.0f); + glScalef(scale, scale, scale); + font->drawShadow(part1, 0, 0, 0x66A254FA); + font->drawShadow(part2, font->width(part1), 0, 0x66ffffff); + glPopMatrix(); + + glDepthMask(true); + glEnable(GL_DEPTH_TEST); } int UIScene_MainMenu::MustSignInReturned(void *pParam, int iPad, C4JStorage::EMessageResult result) diff --git a/Minecraft.Client/Common/UI/UIScene_MainMenu.h b/Minecraft.Client/Common/UI/UIScene_MainMenu.h index 0a3665ee..02f9c642 100644 --- a/Minecraft.Client/Common/UI/UIScene_MainMenu.h +++ b/Minecraft.Client/Common/UI/UIScene_MainMenu.h @@ -118,6 +118,7 @@ public: virtual EUIScene getSceneType() { return eUIScene_MainMenu;} virtual void customDraw(IggyCustomDrawCallbackRegion *region); + virtual void render(S32 width, S32 height, C4JRender::eViewportType viewport); protected: void customDrawSplash(IggyCustomDrawCallbackRegion *region); diff --git a/Minecraft.Client/Common/res/TitleUpdate/res/textures/blocks/magma.png b/Minecraft.Client/Common/res/TitleUpdate/res/textures/blocks/magma.png new file mode 100644 index 00000000..b48e5985 Binary files /dev/null and b/Minecraft.Client/Common/res/TitleUpdate/res/textures/blocks/magma.png differ diff --git a/Minecraft.Client/Common/res/TitleUpdate/res/textures/blocks/magma.txt b/Minecraft.Client/Common/res/TitleUpdate/res/textures/blocks/magma.txt new file mode 100644 index 00000000..5b51a2fc --- /dev/null +++ b/Minecraft.Client/Common/res/TitleUpdate/res/textures/blocks/magma.txt @@ -0,0 +1,4 @@ +interp:1 +0*8 +1*8 +2*8 diff --git a/Minecraft.Client/Common/res/TitleUpdate/res/textures/blocks/prismarine.png b/Minecraft.Client/Common/res/TitleUpdate/res/textures/blocks/prismarine.png index d9c1f9e9..12093564 100644 Binary files a/Minecraft.Client/Common/res/TitleUpdate/res/textures/blocks/prismarine.png and b/Minecraft.Client/Common/res/TitleUpdate/res/textures/blocks/prismarine.png differ diff --git a/Minecraft.Client/Common/res/TitleUpdate/res/textures/blocks/prismarine.txt b/Minecraft.Client/Common/res/TitleUpdate/res/textures/blocks/prismarine.txt index b710199d..82dc9405 100644 --- a/Minecraft.Client/Common/res/TitleUpdate/res/textures/blocks/prismarine.txt +++ b/Minecraft.Client/Common/res/TitleUpdate/res/textures/blocks/prismarine.txt @@ -1,440 +1,23 @@ -0*15 -1*15 -2*15 -3*15 -4*15 -5*15 -6*15 -7*15 -8*15 -9*15 -10*15 -11*15 -12*15 -13*15 -14*15 -15*15 -16*15 -17*15 -18*15 -19*15 -20*15 -21*15 -22*15 -23*15 -24*15 -25*15 -26*15 -27*15 -28*15 -29*15 -30*15 -31*15 -32*15 -33*15 -34*15 -35*15 -36*15 -37*15 -38*15 -39*15 -40*15 -41*15 -42*15 -43*15 -44*15 -45*15 -46*15 -47*15 -48*15 -49*15 -50*15 -51*15 -52*15 -53*15 -54*15 -55*15 -56*15 -57*15 -58*15 -59*15 -60*15 -61*15 -62*15 -63*15 -64*15 -65*15 -66*15 -67*15 -68*15 -69*15 -70*15 -71*15 -72*15 -73*15 -74*15 -75*15 -76*15 -77*15 -78*15 -79*15 -80*15 -81*15 -82*15 -83*15 -84*15 -85*15 -86*15 -87*15 -88*15 -89*15 -90*15 -91*15 -92*15 -93*15 -94*15 -95*15 -96*15 -97*15 -98*15 -99*15 -100*15 -101*15 -102*15 -103*15 -104*15 -105*15 -106*15 -107*15 -108*15 -109*15 -110*15 -111*15 -112*15 -113*15 -114*15 -115*15 -116*15 -117*15 -118*15 -119*15 -120*15 -121*15 -122*15 -123*15 -124*15 -125*15 -126*15 -127*15 -128*15 -129*15 -130*15 -131*15 -132*15 -133*15 -134*15 -135*15 -136*15 -137*15 -138*15 -139*15 -140*15 -141*15 -142*15 -143*15 -144*15 -145*15 -146*15 -147*15 -148*15 -149*15 -150*15 -151*15 -152*15 -153*15 -154*15 -155*15 -156*15 -157*15 -158*15 -159*15 -160*15 -161*15 -162*15 -163*15 -164*15 -165*15 -166*15 -167*15 -168*15 -169*15 -170*15 -171*15 -172*15 -173*15 -174*15 -175*15 -176*15 -177*15 -178*15 -179*15 -180*15 -181*15 -182*15 -183*15 -184*15 -185*15 -186*15 -187*15 -188*15 -189*15 -190*15 -191*15 -192*15 -193*15 -194*15 -195*15 -196*15 -197*15 -198*15 -199*15 -200*15 -201*15 -202*15 -203*15 -204*15 -205*15 -206*15 -207*15 -208*15 -209*15 -210*15 -211*15 -212*15 -213*15 -214*15 -215*15 -216*15 -217*15 -218*15 -219*15 -220*15 -221*15 -222*15 -223*15 -224*15 -225*15 -226*15 -227*15 -228*15 -229*15 -230*15 -231*15 -232*15 -233*15 -234*15 -235*15 -236*15 -237*15 -238*15 -239*15 -240*15 -241*15 -242*15 -243*15 -244*15 -245*15 -246*15 -247*15 -248*15 -249*15 -250*15 -251*15 -252*15 -253*15 -254*15 -255*15 -256*15 -257*15 -258*15 -259*15 -260*15 -261*15 -262*15 -263*15 -264*15 -265*15 -266*15 -267*15 -268*15 -269*15 -270*15 -271*15 -272*15 -273*15 -274*15 -275*15 -276*15 -277*15 -278*15 -279*15 -280*15 -281*15 -282*15 -283*15 -284*15 -285*15 -286*15 -287*15 -288*15 -289*15 -290*15 -291*15 -292*15 -293*15 -294*15 -295*15 -296*15 -297*15 -298*15 -299*15 -300*15 -301*15 -302*15 -303*15 -304*15 -305*15 -306*15 -307*15 -308*15 -309*15 -310*15 -311*15 -312*15 -313*15 -314*15 -315*15 -316*15 -317*15 -318*15 -319*15 -320*15 -321*15 -322*15 -323*15 -324*15 -325*15 -326*15 -327*15 -328*15 -329*15 -330*15 -331*15 -332*15 -333*15 -334*15 -335*15 -336*15 -337*15 -338*15 -339*15 -340*15 -341*15 -342*15 -343*15 -344*15 -345*15 -346*15 -347*15 -348*15 -349*15 -350*15 -351*15 -352*15 -353*15 -354*15 -355*15 -356*15 -357*15 -358*15 -359*15 -360*15 -361*15 -362*15 -363*15 -364*15 -365*15 -366*15 -367*15 -368*15 -369*15 -370*15 -371*15 -372*15 -373*15 -374*15 -375*15 -376*15 -377*15 -378*15 -379*15 -380*15 -381*15 -382*15 -383*15 -384*15 -385*15 -386*15 -387*15 -388*15 -389*15 -390*15 -391*15 -392*15 -393*15 -394*15 -395*15 -396*15 -397*15 -398*15 -399*15 -400*15 -401*15 -402*15 -403*15 -404*15 -405*15 -406*15 -407*15 -408*15 -409*15 -410*15 -411*15 -412*15 -413*15 -414*15 -415*15 -416*15 -417*15 -418*15 -419*15 -420*15 -421*15 -422*15 -423*15 -424*15 -425*15 -426*15 -427*15 -428*15 -429*15 -430*15 -431*15 -432*15 -433*15 -434*15 -435*15 -436*15 -437*15 -438*15 -439*15 \ No newline at end of file +interp:1 +0*300 +1*300 +0*300 +2*300 +0*300 +3*300 +0*300 +1*300 +2*300 +1*300 +3*300 +1*300 +0*300 +2*300 +1*300 +2*300 +3*300 +2*300 +0*300 +3*300 +1*300 +3*300 diff --git a/Minecraft.Client/Common/res/TitleUpdate/res/textures/blocks/prismarineold.png b/Minecraft.Client/Common/res/TitleUpdate/res/textures/blocks/prismarineold.png new file mode 100644 index 00000000..d9c1f9e9 Binary files /dev/null and b/Minecraft.Client/Common/res/TitleUpdate/res/textures/blocks/prismarineold.png differ diff --git a/Minecraft.Client/DurangoMedia/loc/stringsGeneric.xml b/Minecraft.Client/DurangoMedia/loc/stringsGeneric.xml index ea89a2e9..560025fe 100644 --- a/Minecraft.Client/DurangoMedia/loc/stringsGeneric.xml +++ b/Minecraft.Client/DurangoMedia/loc/stringsGeneric.xml @@ -4726,8 +4726,8 @@ At night monsters come out, make sure to build a shelter before that happens. - What's This? - + Info + Share To Facebook diff --git a/Minecraft.Client/GameRenderer.cpp b/Minecraft.Client/GameRenderer.cpp index 891c595f..9a891c30 100644 --- a/Minecraft.Client/GameRenderer.cpp +++ b/Minecraft.Client/GameRenderer.cpp @@ -214,9 +214,8 @@ void GameRenderer::tick(bool first) // 4J - add bFirst { shared_ptr yBobPlayer = dynamic_pointer_cast(mc->cameraTargetPlayer); float target = 0.0f; - if (yBobPlayer != nullptr && yBobPlayer->isElytraFlying()) + if (yBobPlayer != nullptr && yBobPlayer->isElytraFlying()) { - target = (float)(atan(-yBobPlayer->yd * 0.2) * 15.0); } m_yBob += (target - m_yBob) * 0.8f; @@ -482,7 +481,8 @@ void GameRenderer::bobView(float a) glRotatef((float) tilt, 1, 0, 0); float yBobAngle = m_yBobO + (m_yBob - m_yBobO) * a; - if (fabsf(yBobAngle) > 0.001f) + shared_ptr yBobLocalPlayer = dynamic_pointer_cast(mc->cameraTargetPlayer); + if (fabsf(yBobAngle) > 0.001f && yBobLocalPlayer != nullptr && yBobLocalPlayer->isElytraFlying() && !yBobLocalPlayer->ThirdPersonView()) glRotatef(yBobAngle, 1, 0, 0); } @@ -742,7 +742,11 @@ void GameRenderer::setupCamera(float a, int eye) glTranslatef(static_cast(zoom_x), static_cast(-zoom_y), 0); glScaled(zoom, zoom, 1); } - gluPerspective(fov, aspect, 0.05f, renderDistance * 2); + { + auto _sp = dynamic_pointer_cast(mc->cameraTargetPlayer); + float nearClip = (_sp && _sp->abilities.spectatorMode) ? 0.99f : 0.05f; + gluPerspective(fov, aspect, nearClip, renderDistance * 2); + } if (mc->gameMode->isCutScene()) { @@ -825,7 +829,11 @@ void GameRenderer::renderItemInHand(float a, int eye) glTranslatef(static_cast(zoom_x), static_cast(-zoom_y), 0); glScaled(zoom, zoom, 1); } - gluPerspective(fov, aspect, 0.05f, renderDistance * 2); + { + auto _sp = dynamic_pointer_cast(mc->cameraTargetPlayer); + float nearClip = (_sp && _sp->abilities.spectatorMode) ? 0.99f : 0.05f; + gluPerspective(fov, aspect, nearClip, renderDistance * 2); + } if (mc->gameMode->isCutScene()) { diff --git a/Minecraft.Client/Gui.cpp b/Minecraft.Client/Gui.cpp index 731eb480..e89e7b53 100644 --- a/Minecraft.Client/Gui.cpp +++ b/Minecraft.Client/Gui.cpp @@ -685,9 +685,13 @@ void Gui::render(float a, bool mouseFree, int xMouse, int yMouse) { characterDisplayTimer[iPad] = 30; } + else if( minecraft->player->isElytraFlying() ) + { + characterDisplayTimer[iPad] = 30; + } else if( minecraft->player->abilities.flying) { - characterDisplayTimer[iPad] = 5; // quickly get rid of the player display if they stop flying + characterDisplayTimer[iPad] = 5; } else if( characterDisplayTimer[iPad] > 0 ) { @@ -696,8 +700,9 @@ void Gui::render(float a, bool mouseFree, int xMouse, int yMouse) bool displayCrouch = minecraft->player->isSneaking() || ( characterDisplayTimer[iPad] > 0 ); bool displaySprint = minecraft->player->isSprinting() || ( characterDisplayTimer[iPad] > 0 ); bool displayFlying = minecraft->player->abilities.flying || ( characterDisplayTimer[iPad] > 0 ); + bool displayElytra = minecraft->player->isElytraFlying() || ( characterDisplayTimer[iPad] > 0 ); - if( bDisplayGui && (displayCrouch || displaySprint || displayFlying) ) + if( bDisplayGui && (displayCrouch || displaySprint || displayElytra || displayFlying) ) { EntityRenderDispatcher::instance->prepare(minecraft->level, minecraft->textures, minecraft->font, minecraft->cameraTargetPlayer, minecraft->crosshairPickMob, minecraft->options, a); glEnable(GL_RESCALE_NORMAL); @@ -714,7 +719,10 @@ void Gui::render(float a, bool mouseFree, int xMouse, int yMouse) #endif glPushMatrix(); - glTranslatef(static_cast(xo), static_cast(yo), 50); + float pitchOffset = minecraft->player->isElytraFlying() + ? -minecraft->player->xRot / 180.0f * 40.0f + : 0.0f; + glTranslatef(static_cast(xo), static_cast(yo) + pitchOffset, 50); // correct paper doll aspect ratio float ss = 12.0f; @@ -756,7 +764,8 @@ void Gui::render(float a, bool mouseFree, int xMouse, int yMouse) Lighting::turnOn(); glRotatef(-45 - 90, 0, 1, 0); - const float xRotAngle = -(float) atan(yd / 40.0f) * 20; + bool elytraFlying = minecraft->player->isElytraFlying(); + const float xRotAngle = elytraFlying ? 0.0f : -(float) atan(yd / 40.0f) * 20; glRotatef(xRotAngle, 1, 0, 0); float bodyRot = (minecraft->player->yBodyRotO + (minecraft->player->yBodyRot - minecraft->player->yBodyRotO)); // Fixed rotation angle of degrees, adjusted by bodyRot to negate the rotation that occurs in the renderer @@ -768,7 +777,7 @@ void Gui::render(float a, bool mouseFree, int xMouse, int yMouse) // Set head rotation to body rotation to make head static minecraft->player->yRot = bodyRot; minecraft->player->yRotO = minecraft->player->yRot; - minecraft->player->xRot = xRotAngle; + minecraft->player->xRot = elytraFlying ? oxr : xRotAngle; minecraft->player->onFire = 0; minecraft->player->setSharedFlag(Entity::FLAG_ONFIRE, false); diff --git a/Minecraft.Client/HumanoidModel.cpp b/Minecraft.Client/HumanoidModel.cpp index bd362789..597d2b5a 100644 --- a/Minecraft.Client/HumanoidModel.cpp +++ b/Minecraft.Client/HumanoidModel.cpp @@ -1387,13 +1387,19 @@ void HumanoidModel::setupAnim(float time, float r, float bob, float yRot, float if (elytraCrouching) { arm0->xRot = PI; arm0->yRot = 0.0f; arm0->zRot = 0.0f; arm0->y = 2.0f; + if (sleeve0) { sleeve0->xRot = PI; sleeve0->yRot = 0.0f; sleeve0->zRot = 0.0f; sleeve0->y = 2.0f; } + arm1->xRot = 0.0f; arm1->yRot = 0.0f; arm1->zRot = 0.0f; arm1->y = 2.0f; + if (sleeve1) { sleeve1->xRot = 0.0f; sleeve1->yRot = 0.0f; sleeve1->zRot = 0.0f; sleeve1->y = 2.0f; } + leg0->xRot = 0.0f; leg0->yRot = 0.0f; leg0->zRot = 0.0f; leg1->xRot = 0.0f; leg1->yRot = 0.0f; leg1->zRot = 0.0f; + if (pants0) { pants0->xRot = 0.0f; pants0->yRot = 0.0f; pants0->zRot = 0.0f; } + if (pants1) { pants1->xRot = 0.0f; pants1->yRot = 0.0f; pants1->zRot = 0.0f; } } else { - float elytraTime = (float)(entity->tickCount) * 0.3f; + float elytraTime = (float)(entity->tickCount) * 0.3f; float spd2 = (float)(entity->xd * entity->xd + entity->yd * entity->yd + entity->zd * entity->zd); float fDamp = spd2 / 0.2f; fDamp = fDamp * fDamp * fDamp; @@ -1404,14 +1410,30 @@ void HumanoidModel::setupAnim(float time, float r, float bob, float yRot, float arm0->xRot = Mth::cos(elytraTime + PI) * armAmp; arm0->yRot = 0.0f; arm0->zRot = 0.0f; arm0->y = 2.0f; + if (sleeve0) { sleeve0->xRot = arm0->xRot; sleeve0->yRot = 0.0f; sleeve0->zRot = 0.0f; sleeve0->y = 2.0f; } + + if (attackTime > -9990.0f) + { + float elytraSwing = 1.0f - attackTime; + elytraSwing *= elytraSwing; elytraSwing *= elytraSwing; + elytraSwing = 1.0f - elytraSwing; + float elytraAA = Mth::sin(elytraSwing * PI); + float elytraBB = Mth::sin(attackTime * PI) * -(head->xRot - 0.7f) * 0.75f; + arm0->xRot -= elytraAA * 1.2f + elytraBB; + arm0->zRot = Mth::sin(attackTime * PI) * -0.4f; + if (sleeve0) { sleeve0->xRot -= elytraAA * 1.2f + elytraBB; sleeve0->zRot = Mth::sin(attackTime * PI) * -0.4f; } + } arm1->xRot = Mth::cos(elytraTime) * armAmp; arm1->yRot = 0.0f; arm1->zRot = 0.0f; arm1->y = 2.0f; + if (sleeve1) { sleeve1->xRot = arm1->xRot; sleeve1->yRot = 0.0f; sleeve1->zRot = 0.0f; sleeve1->y = 2.0f; } leg0->xRot = Mth::cos(elytraTime) * legAmp; leg0->yRot = 0.0f; leg0->zRot = 0.0f; leg1->xRot = Mth::cos(elytraTime + PI) * legAmp; leg1->yRot = 0.0f; leg1->zRot = 0.0f; + if (pants0) { pants0->xRot = leg0->xRot; pants0->yRot = 0.0f; pants0->zRot = 0.0f; } + if (pants1) { pants1->xRot = leg1->xRot; pants1->yRot = 0.0f; pants1->zRot = 0.0f; } } body->xRot = 0.0f; diff --git a/Minecraft.Client/LocalPlayer.cpp b/Minecraft.Client/LocalPlayer.cpp index 6e2677ab..799103a3 100644 --- a/Minecraft.Client/LocalPlayer.cpp +++ b/Minecraft.Client/LocalPlayer.cpp @@ -128,6 +128,8 @@ LocalPlayer::LocalPlayer(Minecraft *minecraft, Level *level, User *user, int dim LocalPlayer::~LocalPlayer() { + if (minecraft != nullptr && minecraft->soundEngine != nullptr) + minecraft->soundEngine->stopElytraSound(); if( this->input != nullptr ) delete input; } @@ -168,6 +170,9 @@ bool LocalPlayer::isEffectiveAi() const void LocalPlayer::aiStep() { + if (abilities.spectatorMode) + noPhysics = true; + if (sprintTime > 0) { sprintTime--; diff --git a/Minecraft.Client/Logger.cpp b/Minecraft.Client/Logger.cpp new file mode 100644 index 00000000..fff8e1ff --- /dev/null +++ b/Minecraft.Client/Logger.cpp @@ -0,0 +1,340 @@ +#include "stdafx.h" + +#undef OutputDebugStringA +#undef OutputDebugStringW +#undef OutputDebugString + +#include "Logger.h" +#include +#include +#include + +static FILE *s_logFile = nullptr; +static CRITICAL_SECTION s_cs; +static bool s_csInit = false; + +static const char *s_wittyComments[] = +{ + "Who set us up the TNT?", + "Everything's a creeper when you think about it", + "I bet Notch wouldn't have this problem", + "Oops.", + "Why did you do that?", + "Uh... Did I do that?", + "I just don't know what went wrong", + "Quite honestly, I wouldn't worry myself about that.", + "This doesn't make any sense!", + "This is a bug, not a feature!", + "The server is on fire.", + "My bad.", + "I feel sad now :(", + "Don't be sad, have a hug! <3", + "You should try our sister game, Minceraft!", + "Hi. I'm Minecraft, and I'm a crashaholic.", + "Ooh. Shiny.", + "This is not good.", + "Abandon ship!", + "Oh - I know what I did wrong!", + "Shall we play a game?", + "Quite honestly, I wouldn't worry myself about that.", + "Sorry, Dave.", + "We're sorry, we're sorry, we're sorry...", + "Never gonna give you up.", + "A wild Herobrine appeared.", + "Surprise! Haha. Well, this is awkward.", + "Would you like a cupcake?", + "Oh man. I thought that might happen.", + "This is not a good sign.", +}; + +static void writeRaw(const char *msg) +{ + if (!s_logFile) return; + fputs(msg, s_logFile); + fflush(s_logFile); +} + +static void writeLineA(const char *msg) +{ + if (!s_logFile) return; + EnterCriticalSection(&s_cs); + fputs(msg, s_logFile); + fputc('\n', s_logFile); + fflush(s_logFile); + LeaveCriticalSection(&s_cs); +} + +static void getTimestamp(char *buf, size_t size) +{ + time_t t = time(nullptr); + struct tm tm_info; + localtime_s(&tm_info, &t); + strftime(buf, size, "%Y-%m-%d %H:%M:%S", &tm_info); +} + +static void getCpuName(char *buf, size_t size) +{ + int regs[4] = {}; + char name[49] = {}; + __cpuid(regs, 0x80000002); memcpy(name, regs, 16); + __cpuid(regs, 0x80000003); memcpy(name + 16, regs, 16); + __cpuid(regs, 0x80000004); memcpy(name + 32, regs, 16); + name[48] = '\0'; + const char *p = name; + while (*p == ' ') p++; + strncpy_s(buf, size, p, _TRUNCATE); +} + +static void getOsName(char *buf, size_t size) +{ + DWORD major = 0, minor = 0, build = 0; + + HKEY hKey = nullptr; + if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, + L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", 0, KEY_READ, &hKey) == ERROR_SUCCESS) + { + DWORD val = 0, sz = sizeof(val); + if (RegQueryValueExW(hKey, L"CurrentMajorVersionNumber", nullptr, nullptr, (LPBYTE)&val, &sz) == ERROR_SUCCESS) major = val; + sz = sizeof(val); + if (RegQueryValueExW(hKey, L"CurrentMinorVersionNumber", nullptr, nullptr, (LPBYTE)&val, &sz) == ERROR_SUCCESS) minor = val; + sz = sizeof(val); + if (RegQueryValueExW(hKey, L"CurrentBuildNumber", nullptr, nullptr, (LPBYTE)&val, &sz) == ERROR_SUCCESS) build = val; + if (build == 0) + { + wchar_t strBuild[16] = {}; + DWORD strSz = sizeof(strBuild); + if (RegQueryValueExW(hKey, L"CurrentBuildNumber", nullptr, nullptr, (LPBYTE)strBuild, &strSz) == ERROR_SUCCESS) + build = (DWORD)_wtoi(strBuild); + } + RegCloseKey(hKey); + } + + SYSTEM_INFO si = {}; + GetNativeSystemInfo(&si); + const char *arch = (si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) ? "amd64" : "x86"; + + snprintf(buf, size, "Windows %lu.%lu (Build %lu) (%s)", major, minor, build, arch); +} + +static const char *exceptionName(DWORD code) +{ + switch (code) + { + case EXCEPTION_ACCESS_VIOLATION: return "ACCESS_VIOLATION"; + case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: return "ARRAY_BOUNDS_EXCEEDED"; + case EXCEPTION_BREAKPOINT: return "BREAKPOINT"; + case EXCEPTION_DATATYPE_MISALIGNMENT: return "DATATYPE_MISALIGNMENT"; + case EXCEPTION_FLT_DENORMAL_OPERAND: return "FLT_DENORMAL_OPERAND"; + case EXCEPTION_FLT_DIVIDE_BY_ZERO: return "FLT_DIVIDE_BY_ZERO"; + case EXCEPTION_FLT_INEXACT_RESULT: return "FLT_INEXACT_RESULT"; + case EXCEPTION_FLT_INVALID_OPERATION: return "FLT_INVALID_OPERATION"; + case EXCEPTION_FLT_OVERFLOW: return "FLT_OVERFLOW"; + case EXCEPTION_FLT_STACK_CHECK: return "FLT_STACK_CHECK"; + case EXCEPTION_FLT_UNDERFLOW: return "FLT_UNDERFLOW"; + case EXCEPTION_ILLEGAL_INSTRUCTION: return "ILLEGAL_INSTRUCTION"; + case EXCEPTION_IN_PAGE_ERROR: return "IN_PAGE_ERROR"; + case EXCEPTION_INT_DIVIDE_BY_ZERO: return "INT_DIVIDE_BY_ZERO"; + case EXCEPTION_INT_OVERFLOW: return "INT_OVERFLOW"; + case EXCEPTION_INVALID_DISPOSITION: return "INVALID_DISPOSITION"; + case EXCEPTION_NONCONTINUABLE_EXCEPTION: return "NONCONTINUABLE_EXCEPTION"; + case EXCEPTION_PRIV_INSTRUCTION: return "PRIV_INSTRUCTION"; + case EXCEPTION_SINGLE_STEP: return "SINGLE_STEP"; + case EXCEPTION_STACK_OVERFLOW: return "STACK_OVERFLOW"; + default: return "UNKNOWN"; + } +} + +static LONG WINAPI crashHandler(EXCEPTION_POINTERS *ep) +{ + if (!s_logFile) return EXCEPTION_CONTINUE_SEARCH; + + DWORD code = ep->ExceptionRecord->ExceptionCode; + const char *name = exceptionName(code); + + srand((unsigned)time(nullptr)); + const char *comment = s_wittyComments[rand() % (sizeof(s_wittyComments) / sizeof(s_wittyComments[0]))]; + + char timestamp[64]; + getTimestamp(timestamp, sizeof(timestamp)); + + char description[128]; + if (code == EXCEPTION_ACCESS_VIOLATION && ep->ExceptionRecord->NumberParameters >= 2) + { + const char *op = ep->ExceptionRecord->ExceptionInformation[0] == 1 ? "write" : "read"; + snprintf(description, sizeof(description), + "%s (0x%08X) - Attempted to %s from address 0x%p", + name, code, op, (void*)ep->ExceptionRecord->ExceptionInformation[1]); + } + else + { + snprintf(description, sizeof(description), "%s (0x%08X)", name, code); + } + + EnterCriticalSection(&s_cs); + + fprintf(s_logFile, + "\n\n---- neoLegacy Crash Report ----\n" + "// %s\n\n" + "Time: %s\n" + "Description: %s\n\n", + comment, timestamp, description); + + HANDLE proc = GetCurrentProcess(); + HANDLE thread = GetCurrentThread(); + + SymInitialize(proc, nullptr, TRUE); + SymSetOptions(SYMOPT_LOAD_LINES | SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS); + + CONTEXT ctx = *ep->ContextRecord; + + STACKFRAME64 frame = {}; +#ifdef _WIN64 + DWORD machineType = IMAGE_FILE_MACHINE_AMD64; + frame.AddrPC.Offset = ctx.Rip; + frame.AddrStack.Offset = ctx.Rsp; +#else + DWORD machineType = IMAGE_FILE_MACHINE_I386; + frame.AddrPC.Offset = ctx.Eip; + frame.AddrStack.Offset = ctx.Esp; +#endif + frame.AddrPC.Mode = AddrModeFlat; + frame.AddrStack.Mode = AddrModeFlat; + + char symBuf[sizeof(SYMBOL_INFO) + MAX_SYM_NAME]; + SYMBOL_INFO *sym = (SYMBOL_INFO*)symBuf; + + IMAGEHLP_LINE64 line = {}; + line.SizeOfStruct = sizeof(IMAGEHLP_LINE64); + + fputs("Stack Trace:\n", s_logFile); + + for (int i = 0; i < 64; i++) + { + if (!StackWalk64(machineType, proc, thread, &frame, &ctx, + nullptr, SymFunctionTableAccess64, SymGetModuleBase64, nullptr)) + break; + if (frame.AddrPC.Offset == 0) break; + + sym->SizeOfStruct = sizeof(SYMBOL_INFO); + sym->MaxNameLen = MAX_SYM_NAME; + + DWORD64 disp64 = 0; + DWORD disp32 = 0; + + if (SymFromAddr(proc, frame.AddrPC.Offset, &disp64, sym)) + { + if (SymGetLineFromAddr64(proc, frame.AddrPC.Offset, &disp32, &line)) + fprintf(s_logFile, "\tat %s (%s:%lu)\n", sym->Name, line.FileName, line.LineNumber); + else + fprintf(s_logFile, "\tat %s+0x%llx\n", sym->Name, disp64); + } + else + { + fprintf(s_logFile, "\tat 0x%016llX\n", frame.AddrPC.Offset); + } + } + + SymCleanup(proc); + + fprintf(s_logFile, + "\nA detailed walkthrough of the error, its code path and all known details is as follows:\n" + "---------------------------------------------------------------------------\n\n"); + + fprintf(s_logFile, "-- System Details --\nDetails:\n"); + + fprintf(s_logFile, "\tneoLegacy Version: %s\n", NEOL_VERSION); + + char osBuf[128]; + getOsName(osBuf, sizeof(osBuf)); + fprintf(s_logFile, "\tOperating System: %s\n", osBuf); + + char cpuBuf[64]; + getCpuName(cpuBuf, sizeof(cpuBuf)); + fprintf(s_logFile, "\tCPU: %s\n", cpuBuf); + + MEMORYSTATUSEX mem = {}; + mem.dwLength = sizeof(mem); + GlobalMemoryStatusEx(&mem); + DWORDLONG freeMB = mem.ullAvailPhys / (1024 * 1024); + DWORDLONG totalMB = mem.ullTotalPhys / (1024 * 1024); + fprintf(s_logFile, "\tMemory: %llu bytes (%llu MB) free / %llu bytes (%llu MB) total\n", + mem.ullAvailPhys, freeMB, mem.ullTotalPhys, totalMB); + + fflush(s_logFile); + LeaveCriticalSection(&s_cs); + + return EXCEPTION_CONTINUE_SEARCH; +} + +void Logger::init() +{ + InitializeCriticalSection(&s_cs); + s_csInit = true; + + char exePath[MAX_PATH] = {}; + GetModuleFileNameA(nullptr, exePath, MAX_PATH); + char *slash = strrchr(exePath, '\\'); + if (slash) *(slash + 1) = '\0'; + strcat_s(exePath, "neoLegacy_log.txt"); + + fopen_s(&s_logFile, exePath, "w"); + if (!s_logFile) return; + + SetUnhandledExceptionFilter(crashHandler); + + char timestamp[64]; + getTimestamp(timestamp, sizeof(timestamp)); + + char osBuf[128]; + getOsName(osBuf, sizeof(osBuf)); + + char cpuBuf[64]; + getCpuName(cpuBuf, sizeof(cpuBuf)); + + fprintf(s_logFile, + "neoLegacy log started %s\n" + "Version: %s | OS: %s | CPU: %s\n" + "--------------------------------------------------------------------------\n\n", + timestamp, NEOL_VERSION, osBuf, cpuBuf); + fflush(s_logFile); +} + +void Logger::shutdown() +{ + if (s_logFile) + { + char timestamp[64]; + getTimestamp(timestamp, sizeof(timestamp)); + fprintf(s_logFile, "\n--------------------------------------------------------------------------\n"); + fprintf(s_logFile, "neoLegacy log ended %s\n", timestamp); + fclose(s_logFile); + s_logFile = nullptr; + } + if (s_csInit) + { + DeleteCriticalSection(&s_cs); + s_csInit = false; + } +} + +void Logger::interceptA(const char *msg) +{ +#ifndef _FINAL_BUILD + OutputDebugStringA(msg); +#endif + writeLineA(msg); +} + +void Logger::interceptW(const wchar_t *msg) +{ +#ifndef _FINAL_BUILD + OutputDebugStringW(msg); +#endif + if (!s_logFile) return; + int needed = WideCharToMultiByte(CP_UTF8, 0, msg, -1, nullptr, 0, nullptr, nullptr); + if (needed <= 0) return; + char *buf = new char[needed]; + WideCharToMultiByte(CP_UTF8, 0, msg, -1, buf, needed, nullptr, nullptr); + writeLineA(buf); + delete[] buf; +} diff --git a/Minecraft.Client/Logger.h b/Minecraft.Client/Logger.h new file mode 100644 index 00000000..5e06be6d --- /dev/null +++ b/Minecraft.Client/Logger.h @@ -0,0 +1,12 @@ +#pragma once + +#define NEOL_VERSION "TU43" + +class Logger +{ +public: + static void init(); + static void shutdown(); + static void interceptA(const char *msg); + static void interceptW(const wchar_t *msg); +}; diff --git a/Minecraft.Client/Minecraft.cpp b/Minecraft.Client/Minecraft.cpp index 05194bef..f398f2ca 100644 --- a/Minecraft.Client/Minecraft.cpp +++ b/Minecraft.Client/Minecraft.cpp @@ -4342,10 +4342,10 @@ void Minecraft::releaseLevel(int message) // time when exiting from an online game void Minecraft::forceStatsSave(int idx) { - //4J Gordon: Force a stats save + if (idx < 0 || idx >= 4 || stats[idx] == nullptr) return; + stats[idx]->save(idx, true); - //4J Gordon: If the player is signed in, save the leaderboards if( ProfileManager.IsSignedInLive(idx) ) { int tempLockedProfile = ProfileManager.GetLockedProfile(); diff --git a/Minecraft.Client/MultiPlayerLocalPlayer.cpp b/Minecraft.Client/MultiPlayerLocalPlayer.cpp index edfaf629..74f44054 100644 --- a/Minecraft.Client/MultiPlayerLocalPlayer.cpp +++ b/Minecraft.Client/MultiPlayerLocalPlayer.cpp @@ -4,6 +4,7 @@ #include "ClientConnection.h" #include "../Minecraft.World/net.minecraft.world.level.h" #include "../Minecraft.World/net.minecraft.network.h" +#include "../Minecraft.World/PlayerActionPacket.h" #include "../Minecraft.World/Mth.h" #include "../Minecraft.World/AABB.h" #include "../Minecraft.World/net.minecraft.stats.h" @@ -76,7 +77,7 @@ void MultiplayerLocalPlayer::tick() { if (isRiding()) { - connection->send(std::make_shared(yRot, xRot, onGround, abilities.flying)); + connection->send(std::make_shared(yRot, xRot, onGround, abilities.flying || isElytraFlying())); connection->send(std::make_shared(xxa, yya, input->jumping, input->sneaking)); } else @@ -131,26 +132,26 @@ void MultiplayerLocalPlayer::sendPosition() bool rot = rydd != 0 || rxdd != 0; if (riding != nullptr) { - connection->send(std::make_shared(xd, -999, -999, zd, yRot, xRot, onGround, abilities.flying)); + connection->send(std::make_shared(xd, -999, -999, zd, yRot, xRot, onGround, abilities.flying || isElytraFlying())); move = false; } else { if (move && rot) { - connection->send(std::make_shared(x, bb->y0, y, z, yRot, xRot, onGround, abilities.flying)); + connection->send(std::make_shared(x, bb->y0, y, z, yRot, xRot, onGround, abilities.flying || isElytraFlying())); } else if (move) { - connection->send(std::make_shared(x, bb->y0, y, z, onGround, abilities.flying)); + connection->send(std::make_shared(x, bb->y0, y, z, onGround, abilities.flying || isElytraFlying())); } else if (rot) { - connection->send(std::make_shared(yRot, xRot, onGround, abilities.flying)); + connection->send(std::make_shared(yRot, xRot, onGround, abilities.flying || isElytraFlying())); } else { - connection->send(std::make_shared(onGround, abilities.flying)); + connection->send(std::make_shared(onGround, abilities.flying || isElytraFlying())); } } @@ -210,6 +211,21 @@ void MultiplayerLocalPlayer::actuallyHurt(DamageSource *source, float dmg) setHealth(getHealth() - dmg); } +void MultiplayerLocalPlayer::onElytraKineticDamage(float damage) +{ + if (damage <= 0.0f) return; + int dmg = (int)damage; + if ((float)dmg < damage) dmg++; + if (dmg < 1) dmg = 1; + connection->send(std::make_shared(PlayerActionPacket::ELYTRA_IMPACT, dmg, 0, 0, 0)); +} + +void MultiplayerLocalPlayer::onElytraFallDamage(int amount) +{ + if (amount <= 0) return; + connection->send(std::make_shared(PlayerActionPacket::ELYTRA_FALL_DAMAGE, amount, 0, 0, 0)); +} + // 4J Added override to capture event for tutorial messages void MultiplayerLocalPlayer::completeUsingItem() { diff --git a/Minecraft.Client/MultiPlayerLocalPlayer.h b/Minecraft.Client/MultiPlayerLocalPlayer.h index e7871154..3b7c1a04 100644 --- a/Minecraft.Client/MultiPlayerLocalPlayer.h +++ b/Minecraft.Client/MultiPlayerLocalPlayer.h @@ -44,6 +44,8 @@ public: virtual void respawn(); protected: virtual void actuallyHurt(DamageSource *source, float dmg); + virtual void onElytraKineticDamage(float damage); + virtual void onElytraFallDamage(int amount); // 4J Added override to capture event for tutorial messages virtual void completeUsingItem(); diff --git a/Minecraft.Client/PlayerConnection.cpp b/Minecraft.Client/PlayerConnection.cpp index 327738e1..d0f99c5c 100644 --- a/Minecraft.Client/PlayerConnection.cpp +++ b/Minecraft.Client/PlayerConnection.cpp @@ -12,6 +12,8 @@ #include "../Minecraft.World/net.minecraft.world.level.dimension.h" #include "../Minecraft.World/net.minecraft.world.item.h" #include "../Minecraft.World/net.minecraft.world.item.trading.h" +#include "../Minecraft.World/net.minecraft.world.damagesource.h" +#include "../Minecraft.World/net.minecraft.world.effect.h" #include "../Minecraft.World/net.minecraft.world.inventory.h" #include "../Minecraft.World/net.minecraft.world.level.tile.entity.h" #include "../Minecraft.World/net.minecraft.world.level.saveddata.h" @@ -85,6 +87,8 @@ PlayerConnection::PlayerConnection(MinecraftServer *server, Connection *connecti done = false; tickCount = 0; aboveGroundTickCount = 0; + elytraDurabilityTicks = 0; + elytraBoostCooldown = 0; xLastOk = yLastOk = zLastOk = 0; synched = true; hasDoneFirstTickFourKit = false; @@ -460,14 +464,20 @@ void PlayerConnection::handleMovePlayer(shared_ptr packet) return; } bool newOk = level->getCubes(player, playerBB->copy()->shrink(r, r, r))->empty(); - if (oldOk && (fail || !newOk) && !player->isSleeping()) + if (oldOk && (fail || !newOk) && !player->isSleeping() && !player->abilities.spectatorMode) { teleport(xLastOk, yLastOk, zLastOk, yRotT, xRotT); return; } + if (player->abilities.spectatorMode) + { + xLastOk = xt; + yLastOk = yt; + zLastOk = zt; + } AABB *testBox = playerBB->copy()->grow(r, r, r)->expand(0, -0.55, 0); // && server.level.getCubes(player, testBox).size() == 0 - if (!server->isFlightAllowed() && !player->gameMode->isCreative() && !level->containsAnyBlocks(testBox) && !player->isAllowedToFly() ) + if (!server->isFlightAllowed() && !player->gameMode->isCreative() && !level->containsAnyBlocks(testBox) && !player->isAllowedToFly() && !player->abilities.spectatorMode) { if (oyDist >= (-0.5f / 16.0f)) { @@ -490,9 +500,32 @@ void PlayerConnection::handleMovePlayer(shared_ptr packet) player->onGround = packet->onGround; server->getPlayers()->move(player); - player->doCheckFallDamage(player->y - startY, packet->onGround); + if (packet->isFlying) + player->fallDistance = 0.0f; + else + player->doCheckFallDamage(player->y - startY, packet->onGround); + + { + shared_ptr chestItem = player->inventory->armor[LivingEntity::SLOT_CHEST - 1]; + bool elytraFlying = packet->isFlying && !player->onGround + && chestItem != nullptr && dynamic_cast(chestItem->getItem()) != nullptr; + if (elytraFlying) + { + if (++elytraDurabilityTicks >= 20) + { + elytraDurabilityTicks = 0; + chestItem->hurtAndBreak(1, dynamic_pointer_cast(player)); + } + if (elytraBoostCooldown > 0) + elytraBoostCooldown--; + } + else + { + elytraDurabilityTicks = 0; + } + } } - else if ((tickCount % SharedConstants::TICKS_PER_SECOND) == 0) + else if ((tickCount % SharedConstants::TICKS_PER_SECOND) == 0 && !player->abilities.spectatorMode) { teleport(xLastOk, yLastOk, zLastOk, player->yRot, player->xRot); } @@ -574,6 +607,20 @@ void PlayerConnection::handlePlayerAction(shared_ptr packet) player->releaseUsingItem(); return; } + else if (packet->action == PlayerActionPacket::ELYTRA_IMPACT) + { + float damage = (float)packet->x; + if (damage > 0.0f) + player->hurt(DamageSource::flyIntoWall, damage); + return; + } + else if (packet->action == PlayerActionPacket::ELYTRA_FALL_DAMAGE) + { + float damage = (float)packet->x; + if (damage > 0.0f) + player->hurt(DamageSource::fall, damage); + return; + } bool shouldVerifyLocation = false; if (packet->action == PlayerActionPacket::START_DESTROY_BLOCK) shouldVerifyLocation = true; @@ -668,6 +715,22 @@ void PlayerConnection::handleUseItem(shared_ptr packet) } #endif player->gameMode->useItem(player, level, item); + + if (item->getItem() != nullptr && item->getItem()->getBaseItemType() == Item::eBaseItemType_fireworks && !player->abilities.instabuild) + { + shared_ptr chestItem = player->inventory->armor[LivingEntity::SLOT_CHEST - 1]; + bool elytraFlying = !player->onGround + && chestItem != nullptr + && dynamic_cast(chestItem->getItem()) != nullptr + && ElytraItem::isFlyEnabled(chestItem); + if (elytraFlying && elytraBoostCooldown == 0) + { + elytraBoostCooldown = 20; + item->count--; + if (item->count <= 0) + player->inventory->items[player->inventory->selected] = nullptr; + } + } } else if ((packet->getY() < server->getMaxBuildHeight() - 1) || (packet->getFace() != Facing::UP && packet->getY() < server->getMaxBuildHeight())) { @@ -1412,6 +1475,33 @@ if (cmd == L"tp" || cmd == L"teleport") shared_ptr packet = GiveItemCommand::preparePacket(target, item, amount, aux); server->getCommandDispatcher()->performCommand(player, eGameCommand_Give, packet->data); } +#ifndef _CONTENT_PACKAGE + else if (cmd == L"spectator") + { + player->m_spectatorMode = !player->m_spectatorMode; + if (player->m_spectatorMode) + { + player->abilities.mayfly = true; + player->abilities.flying = true; + player->abilities.invulnerable = true; + player->abilities.spectatorMode = true; + player->abilities.setFlyingSpeed(0.1f); + player->noPhysics = true; + player->addEffect(new MobEffectInstance(MobEffect::nightVision->id, 0x7fffffff, 0, true)); + info(L"Spectator mode enabled."); + } + else + { + player->gameMode->getGameModeForPlayer()->updatePlayerAbilities(&player->abilities); + player->abilities.spectatorMode = false; + player->abilities.setFlyingSpeed(0.05f); + player->noPhysics = false; + player->removeEffect(MobEffect::nightVision->id); + info(L"Spectator mode disabled."); + } + player->onUpdateAbilities(); + } +#endif } void PlayerConnection::handleAnimate(shared_ptr packet) diff --git a/Minecraft.Client/PlayerConnection.h b/Minecraft.Client/PlayerConnection.h index 0a2d80b9..796499c3 100644 --- a/Minecraft.Client/PlayerConnection.h +++ b/Minecraft.Client/PlayerConnection.h @@ -28,6 +28,8 @@ private: shared_ptr player; int tickCount; int aboveGroundTickCount; + int elytraDurabilityTicks; + int elytraBoostCooldown; bool didTick; bool hasDoneFirstTickFourKit; diff --git a/Minecraft.Client/ServerPlayer.cpp b/Minecraft.Client/ServerPlayer.cpp index cd321df0..17bcd7c8 100644 --- a/Minecraft.Client/ServerPlayer.cpp +++ b/Minecraft.Client/ServerPlayer.cpp @@ -191,6 +191,7 @@ ServerPlayer::ServerPlayer(MinecraftServer *server, Level *level, const wstring& connection = nullptr; lastMoveX = lastMoveZ = 0; spewTimer = 0; + m_spectatorMode = false; lastRecordedHealthAndAbsorption = FLT_MIN; lastSentHealth = -99999999; lastSentFood = -99999999; diff --git a/Minecraft.Client/ServerPlayer.h b/Minecraft.Client/ServerPlayer.h index b14f5f0a..b759cd52 100644 --- a/Minecraft.Client/ServerPlayer.h +++ b/Minecraft.Client/ServerPlayer.h @@ -31,6 +31,7 @@ public: vector entitiesToRemove; unordered_set seenChunks; int spewTimer; + bool m_spectatorMode; // 4J-Added, for 'Adventure Time' achievement. Biome *currentBiome; diff --git a/Minecraft.Client/StitchedTexture.cpp b/Minecraft.Client/StitchedTexture.cpp index 049fb574..16bc592f 100644 --- a/Minecraft.Client/StitchedTexture.cpp +++ b/Minecraft.Client/StitchedTexture.cpp @@ -43,6 +43,7 @@ StitchedTexture::StitchedTexture(const wstring &name, const wstring &filename) : frameOverride = nullptr; flags = 0; frames = nullptr; + m_bInterpolate = false; m_fileName = filename; } @@ -84,7 +85,8 @@ void StitchedTexture::init(Texture *source, vector *frames, int x, in { this->source = source; this->frames = frames; - frame = -1; // Force an update of animated textures + frame = -1; + m_bInterpolate = false; this->x = x; this->y = y; this->width = width; @@ -209,12 +211,21 @@ void StitchedTexture::cycleFrames() current = frameOverride->at(frame); int newFrame = current.first; - if (oldFrame != newFrame && newFrame >= 0 && newFrame < frames->size()) + if (oldFrame != newFrame && newFrame >= 0 && newFrame < (int)frames->size()) { source->blit(x, y, frames->at(newFrame), rotated); } } - + else if (m_bInterpolate) + { + int curIdx = current.first; + int nextIdx = frameOverride->at((frame + 1) % frameOverride->size()).first; + if (curIdx >= 0 && curIdx < (int)frames->size() && nextIdx >= 0 && nextIdx < (int)frames->size()) + { + float t = static_cast(subFrame) / static_cast(current.second); + source->blitInterpolated(x, y, frames->at(curIdx), frames->at(nextIdx), t); + } + } } else { @@ -324,6 +335,11 @@ void StitchedTexture::loadAnimationFrames(const wstring &string) for(auto& it : tokens) { token = trimString(it); + if (token == L"interp:1") + { + m_bInterpolate = true; + continue; + } int multiPos = token.find_first_of('*'); if (multiPos > 0) { diff --git a/Minecraft.Client/StitchedTexture.h b/Minecraft.Client/StitchedTexture.h index da88536a..3698ebd4 100644 --- a/Minecraft.Client/StitchedTexture.h +++ b/Minecraft.Client/StitchedTexture.h @@ -42,6 +42,7 @@ protected: protected: int frame; int subFrame; + bool m_bInterpolate; public: static StitchedTexture *create(const wstring &name); diff --git a/Minecraft.Client/Texture.cpp b/Minecraft.Client/Texture.cpp index ec2d9264..cc1b3122 100644 --- a/Minecraft.Client/Texture.cpp +++ b/Minecraft.Client/Texture.cpp @@ -367,6 +367,38 @@ void Texture::writeAsPNG(const wstring &filename) #endif } +void Texture::blitInterpolated(int x, int y, Texture *srcA, Texture *srcB, float t) +{ + ByteBuffer *bufA = srcA->getData(0); + ByteBuffer *bufB = srcB->getData(0); + if (!bufA || !bufB) return; + + bufA->position(0); + bufB->position(0); + + int sw = srcA->getWidth(); + int sh = srcA->getHeight(); + + for (int srcY = 0; srcY < sh; srcY++) + { + int dstY = y + srcY; + for (int srcX = 0; srcX < sw; srcX++) + { + int srcPos = (srcY * sw + srcX) * 4; + int dstPos = (dstY * width + (x + srcX)) * 4; + for (int c = 0; c < 4; c++) + { + int a = bufA->get(srcPos + c) & 0xFF; + int b = bufB->get(srcPos + c) & 0xFF; + data[0]->put(dstPos + c, (BYTE)(a + (int)((b - a) * t))); + } + } + } + + if (immediateUpdate) updateOnGPU(); + else updated = false; +} + void Texture::blit(int x, int y, Texture *source) { blit(x, y, source, false); diff --git a/Minecraft.Client/Texture.h b/Minecraft.Client/Texture.h index 5dcf2b48..c6f30aaf 100644 --- a/Minecraft.Client/Texture.h +++ b/Minecraft.Client/Texture.h @@ -82,6 +82,7 @@ public: void writeAsPNG(const wstring &filename); void blit(int x, int y, Texture *source); void blit(int x, int y, Texture *source, bool rotated); + void blitInterpolated(int x, int y, Texture *srcA, Texture *srcB, float t); void transferFromBuffer(intArray buffer); void transferFromImage(BufferedImage *image); int getManagerId(); diff --git a/Minecraft.Client/TileRenderer.cpp b/Minecraft.Client/TileRenderer.cpp index c6001c37..121cdd82 100644 --- a/Minecraft.Client/TileRenderer.cpp +++ b/Minecraft.Client/TileRenderer.cpp @@ -15,6 +15,7 @@ #include "LevelRenderer.h" #include "Options.h" #include "../Minecraft.World/TallGrass2.h" +#include "../Minecraft.World/FlowerPotTileEntity.h" bool TileRenderer::fancy = true; @@ -932,9 +933,6 @@ bool TileRenderer::tesselateFlowerPotInWorld(FlowerPotTile *tt, int x, int y, in switch (type) { - case FlowerPotTile::TYPE_FLOWER_RED: - plant = Tile::rose; - break; case FlowerPotTile::TYPE_FLOWER_YELLOW: plant = Tile::flower; break; @@ -954,7 +952,32 @@ bool TileRenderer::tesselateFlowerPotInWorld(FlowerPotTile *tt, int x, int y, in } else { - if (type == FlowerPotTile::TYPE_CACTUS) + if (type == FlowerPotTile::TYPE_FLOWER_RED) + { + int aux = 0; + shared_ptr te = level->getTileEntity(x, y, z); + shared_ptr fpte = te ? std::dynamic_pointer_cast(te) : nullptr; + if (fpte && fpte->hasFlower()) + aux = fpte->getFlowerAuxValue(); + tesselateCrossTexture(Tile::rose, aux, x, y, z, 0.75f); + } + else if (type == FlowerPotTile::TYPE_FLOWER_BLUE_ORCHID) + { + tesselateCrossTexture(Tile::rose, Rose::BLUE_ORCHID, x, y, z, 0.75f); + } + else if (type == FlowerPotTile::TYPE_FLOWER_ALLIUM) + { + tesselateCrossTexture(Tile::rose, Rose::ALLIUM, x, y, z, 0.75f); + } + else if (type == FlowerPotTile::TYPE_FLOWER_AZURE_BLUET) + { + tesselateCrossTexture(Tile::rose, Rose::AZURE_BLUET, x, y, z, 0.75f); + } + else if (type == FlowerPotTile::TYPE_FLOWER_OXEYE_DAISY) + { + tesselateCrossTexture(Tile::rose, Rose::OXEYE_DAISY, x, y, z, 0.75f); + } + else if (type == FlowerPotTile::TYPE_CACTUS) { // Force drawing of all faces else the cactus misses faces diff --git a/Minecraft.Client/Windows64/Windows64_Minecraft.cpp b/Minecraft.Client/Windows64/Windows64_Minecraft.cpp index 07574f73..80e8beef 100644 --- a/Minecraft.Client/Windows64/Windows64_Minecraft.cpp +++ b/Minecraft.Client/Windows64/Windows64_Minecraft.cpp @@ -1547,6 +1547,8 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance, UNREFERENCED_PARAMETER(hPrevInstance); UNREFERENCED_PARAMETER(lpCmdLine); + Logger::init(); + // 4J-Win64: set CWD to exe dir so asset paths resolve correctly { char szExeDir[MAX_PATH] = {}; @@ -1718,6 +1720,7 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance, if( FAILED( InitDevice() ) ) { CleanupDevice(); + Logger::shutdown(); return 0; } // Restore fullscreen state from previous session. Route through the @@ -1747,6 +1750,7 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance, } } + Logger::shutdown(); return (int) msg.wParam; #endif @@ -1787,6 +1791,7 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance, if (pMinecraft == nullptr) { CleanupDevice(); + Logger::shutdown(); return 1; } g_bResizeReady = true; diff --git a/Minecraft.Client/cmake/sources/Common.cmake b/Minecraft.Client/cmake/sources/Common.cmake index 4017f32e..7e695c0c 100644 --- a/Minecraft.Client/cmake/sources/Common.cmake +++ b/Minecraft.Client/cmake/sources/Common.cmake @@ -451,6 +451,8 @@ set(_MINECRAFT_CLIENT_COMMON_NET_MINECRAFT_CLIENT "${CMAKE_CURRENT_SOURCE_DIR}/KeyMapping.h" "${CMAKE_CURRENT_SOURCE_DIR}/Lighting.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/Lighting.h" + "${CMAKE_CURRENT_SOURCE_DIR}/Logger.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/Logger.h" "${CMAKE_CURRENT_SOURCE_DIR}/MemoryTracker.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/MemoryTracker.h" "${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.cpp" diff --git a/Minecraft.Client/stdafx.h b/Minecraft.Client/stdafx.h index a4bb0aab..57a5fbb7 100644 --- a/Minecraft.Client/stdafx.h +++ b/Minecraft.Client/stdafx.h @@ -351,6 +351,16 @@ typedef XUID GameSessionUID; +#include "Logger.h" +#ifndef _FINAL_BUILD +#undef OutputDebugString +#undef OutputDebugStringA +#undef OutputDebugStringW +#define OutputDebugStringA(s) Logger::interceptA(s) +#define OutputDebugStringW(s) Logger::interceptW(s) +#define OutputDebugString(s) Logger::interceptW(s) +#endif + #ifdef _FINAL_BUILD #define printf BREAKTHECOMPILE #define wprintf BREAKTHECOMPILE diff --git a/Minecraft.World/Abilities.cpp b/Minecraft.World/Abilities.cpp index 1177937b..5e8497d2 100644 --- a/Minecraft.World/Abilities.cpp +++ b/Minecraft.World/Abilities.cpp @@ -9,6 +9,7 @@ Abilities::Abilities() mayfly = false; instabuild = false; mayBuild = true; + spectatorMode = false; flyingSpeed = 0.05f; walkingSpeed = 0.1f; diff --git a/Minecraft.World/Abilities.h b/Minecraft.World/Abilities.h index f5895cdd..b66f80ec 100644 --- a/Minecraft.World/Abilities.h +++ b/Minecraft.World/Abilities.h @@ -10,6 +10,7 @@ public: bool mayfly; bool instabuild; bool mayBuild; + bool spectatorMode; private: float flyingSpeed; diff --git a/Minecraft.World/Animal.cpp b/Minecraft.World/Animal.cpp index d789aa5d..54a06dcd 100644 --- a/Minecraft.World/Animal.cpp +++ b/Minecraft.World/Animal.cpp @@ -49,10 +49,7 @@ void Animal::aiStep() setInLoveValue(getInLoveValue()-1); if (getInLoveValue() % 10 == 0) { - double xa = random->nextGaussian() * 0.02; - double ya = random->nextGaussian() * 0.02; - double za = random->nextGaussian() * 0.02; - level->addParticle(eParticleType_heart, x + random->nextFloat() * bbWidth * 2 - bbWidth, y + .5f + random->nextFloat() * bbHeight, z + random->nextFloat() * bbWidth * 2 - bbWidth, xa, ya, za); + level->broadcastEntityEvent(shared_from_this(), EntityEvent::IN_LOVE_HEARTS); } } else @@ -106,7 +103,7 @@ void Animal::checkHurtTarget(shared_ptr target, float d) loveTime++; if (loveTime % 4 == 0) { - level->addParticle(eParticleType_heart, x + random->nextFloat() * bbWidth * 2 - bbWidth, y + .5f + random->nextFloat() * bbHeight, z + random->nextFloat() * bbWidth * 2 - bbWidth, 0, 0, 0); + level->broadcastEntityEvent(shared_from_this(), EntityEvent::IN_LOVE_HEARTS); } if (loveTime == 20 * 3) breedWith(a); @@ -145,13 +142,7 @@ void Animal::breedWith(shared_ptr target) offspring->setAge(-20 * 60 * 20); offspring->moveTo(x, y, z, yRot, xRot); offspring->setDespawnProtected(); - for (int i = 0; i < 7; i++) - { - double xa = random->nextGaussian() * 0.02; - double ya = random->nextGaussian() * 0.02; - double za = random->nextGaussian() * 0.02; - level->addParticle(eParticleType_heart, x + random->nextFloat() * bbWidth * 2 - bbWidth, y + .5f + random->nextFloat() * bbHeight, z + random->nextFloat() * bbWidth * 2 - bbWidth, xa, ya, za); - } + level->broadcastEntityEvent(shared_from_this(), EntityEvent::IN_LOVE_HEARTS); level->addEntity(offspring); level->addEntity(std::make_shared(level, x, y, z, random->nextInt(4) + 1)); diff --git a/Minecraft.World/BreedGoal.cpp b/Minecraft.World/BreedGoal.cpp index 10aa6844..94d6e8b7 100644 --- a/Minecraft.World/BreedGoal.cpp +++ b/Minecraft.World/BreedGoal.cpp @@ -9,6 +9,7 @@ #include "ExperienceOrb.h" #include "GenericStats.h" +#include "EntityEvent.h" BreedGoal::BreedGoal(Animal *animal, double speedModifier) { @@ -108,15 +109,8 @@ void BreedGoal::breed() offspring->setDespawnProtected(); level->addEntity(offspring); - Random *random = animal->getRandom(); - for (int i = 0; i < 7; i++) - { - double xa = random->nextGaussian() * 0.02; - double ya = random->nextGaussian() * 0.02; - double za = random->nextGaussian() * 0.02; - level->addParticle(eParticleType_heart, animal->x + random->nextFloat() * animal->bbWidth * 2 - animal->bbWidth, animal->y + .5f + random->nextFloat() * animal->bbHeight, animal->z + random->nextFloat() - * animal->bbWidth * 2 - animal->bbWidth, xa, ya, za); - } + level->broadcastEntityEvent(animal->shared_from_this(), EntityEvent::IN_LOVE_HEARTS); // 4J-PB - Fix for 106869- Customer Encountered: TU12: Content: Gameplay: Breeding animals does not give any Experience Orbs. + Random *random = animal->getRandom(); level->addEntity(std::make_shared(level, animal->x, animal->y, animal->z, random->nextInt(7) + 1)); } diff --git a/Minecraft.World/ChatPacket.h b/Minecraft.World/ChatPacket.h index 7ffebc5e..f763d55a 100644 --- a/Minecraft.World/ChatPacket.h +++ b/Minecraft.World/ChatPacket.h @@ -48,6 +48,7 @@ public: e_ChatDeathDragonBreath, e_ChatDeathAnvil, e_ChatDeathFallingBlock, + e_ChatDeathFlyIntoWall, e_ChatDeathThorns, e_ChatDeathFellAccidentLadder, diff --git a/Minecraft.World/Class.h b/Minecraft.World/Class.h index e7128086..ffca35e7 100644 --- a/Minecraft.World/Class.h +++ b/Minecraft.World/Class.h @@ -320,6 +320,7 @@ enum eINSTANCEOF eTYPE_DAYLIGHTDETECTORTILEENTITY = eTYPE_TILEENTITY | 0x10, eTYPE_HOPPERTILEENTITY = eTYPE_TILEENTITY | 0x11, eTYPE_BANNERTILEENTITY = eTYPE_TILEENTITY | 0x12, + eTYPE_FLOWERPOTTILEENTITY = eTYPE_TILEENTITY | 0x13, eTYPE_DISPENSERTILEENTITY = eTYPE_TILEENTITY | BIT_DISPENSERTILEENTITY, eTYPE_DROPPERTILEENTITY = eTYPE_DISPENSERTILEENTITY | 0x1, @@ -548,6 +549,7 @@ public: classes->push_back( SUBCLASS(eTYPE_DAYLIGHTDETECTORTILEENTITY )->addParent( eTYPE_TILEENTITY ) ); classes->push_back( SUBCLASS(eTYPE_HOPPERTILEENTITY )->addParent( eTYPE_TILEENTITY ) ); classes->push_back( SUBCLASS(eTYPE_BANNERTILEENTITY )->addParent( eTYPE_TILEENTITY ) ); + classes->push_back( SUBCLASS(eTYPE_FLOWERPOTTILEENTITY )->addParent( eTYPE_TILEENTITY ) ); classes->push_back( SUBCLASS(eTYPE_DISPENSERTILEENTITY )->addParent( eTYPE_TILEENTITY ) ); classes->push_back( SUBCLASS(eTYPE_DROPPERTILEENTITY )->addParent( eTYPE_DISPENSERTILEENTITY ) ); diff --git a/Minecraft.World/CustomLevelSource.cpp b/Minecraft.World/CustomLevelSource.cpp index ae38f192..991c1b1e 100644 --- a/Minecraft.World/CustomLevelSource.cpp +++ b/Minecraft.World/CustomLevelSource.cpp @@ -569,6 +569,15 @@ void CustomLevelSource::postProcess(ChunkSource *parent, int xt, int zt) } PIXEndNamedEvent(); + if (true) + { + int fx = xo + pprandom->nextInt(16) + 8; + int fy = 40 + pprandom->nextInt(10); + int fz = zo + pprandom->nextInt(16) + 8; + FossilFeature fossil; + fossil.place(level, pprandom, fx, fy, fz); + } + PIXBeginNamedEvent(0,"Biome decorate"); biome->decorate(level, pprandom, xo, zo); PIXEndNamedEvent(); diff --git a/Minecraft.World/DamageSource.cpp b/Minecraft.World/DamageSource.cpp index 838c77ab..2770c42f 100644 --- a/Minecraft.World/DamageSource.cpp +++ b/Minecraft.World/DamageSource.cpp @@ -22,6 +22,7 @@ DamageSource *DamageSource::dragonbreath = (new DamageSource(ChatPacket::e_ChatD DamageSource *DamageSource::wither = (new DamageSource(ChatPacket::e_ChatDeathWither))->bypassArmor(); DamageSource *DamageSource::anvil = (new DamageSource(ChatPacket::e_ChatDeathAnvil)); DamageSource *DamageSource::fallingBlock = (new DamageSource(ChatPacket::e_ChatDeathFallingBlock)); +DamageSource *DamageSource::flyIntoWall = new DamageSource(ChatPacket::e_ChatDeathFlyIntoWall); DamageSource *DamageSource::mobAttack(shared_ptr mob) { diff --git a/Minecraft.World/DamageSource.h b/Minecraft.World/DamageSource.h index f5f2a397..9b073560 100644 --- a/Minecraft.World/DamageSource.h +++ b/Minecraft.World/DamageSource.h @@ -29,6 +29,7 @@ public: static DamageSource *wither; static DamageSource *anvil; static DamageSource *fallingBlock; + static DamageSource *flyIntoWall; static DamageSource *mobAttack(shared_ptr mob); static DamageSource *playerAttack(shared_ptr player); diff --git a/Minecraft.World/Explosion.cpp b/Minecraft.World/Explosion.cpp index 65e719e4..da2a16ff 100644 --- a/Minecraft.World/Explosion.cpp +++ b/Minecraft.World/Explosion.cpp @@ -1,5 +1,6 @@ #include "stdafx.h" #include "net.minecraft.world.entity.h" +#include "net.minecraft.world.entity.player.h" #include "net.minecraft.world.entity.item.h" #include "net.minecraft.world.item.enchantment.h" #include "net.minecraft.world.level.h" @@ -146,9 +147,18 @@ void Explosion::explode() if(canDamage) e->hurt(DamageSource::explosion(this), static_cast((pow * pow + pow) / 2 * 8 * r + 1)); double kbPower = ProtectionEnchantment::getExplosionKnockbackAfterDampener(e, pow); - e->xd += xa *kbPower; - e->yd += ya *kbPower; - e->zd += za *kbPower; + bool isCreativeFlying = false; + if (e->instanceof(eTYPE_PLAYER)) + { + shared_ptr p = dynamic_pointer_cast(e); + if (p && p->abilities.flying) isCreativeFlying = true; + } + if (!isCreativeFlying) + { + e->xd += xa * kbPower; + e->yd += ya * kbPower; + e->zd += za * kbPower; + } if (e->instanceof(eTYPE_PLAYER)) diff --git a/Minecraft.World/FireworksItem.cpp b/Minecraft.World/FireworksItem.cpp index f1cdf309..79c3bf9b 100644 --- a/Minecraft.World/FireworksItem.cpp +++ b/Minecraft.World/FireworksItem.cpp @@ -19,6 +19,25 @@ FireworksItem::FireworksItem(int id) : Item(id) { } +shared_ptr FireworksItem::use(shared_ptr instance, Level *level, shared_ptr player) +{ + if (player->isElytraFlying() && player->m_elytraRocketCooldown == 0) + { + player->m_elytraRocketCooldown = 20; + player->startFireworkBoost(instance); + player->playSound(eSoundType_FIREWORKS_LAUNCH, 3.0f, 1.0f); + if (!level->isClientSide) + { + shared_ptr rocket(new FireworksRocketEntity(level, player->x, player->y, player->z, instance)); + level->addEntity(rocket); + if (!player->abilities.instabuild) + instance->count--; + } + return instance; + } + return instance; +} + bool FireworksItem::useOn(shared_ptr instance, shared_ptr player, Level *level, int x, int y, int z, int face, float clickX, float clickY, float clickZ, bool bTestUseOnOnly) { // 4J-JEV: Fix for xb1 #173493 - CU7: Content: UI: Missing tooltip for Firework Rocket. diff --git a/Minecraft.World/FireworksItem.h b/Minecraft.World/FireworksItem.h index afc6cb59..20d2f9f7 100644 --- a/Minecraft.World/FireworksItem.h +++ b/Minecraft.World/FireworksItem.h @@ -27,5 +27,6 @@ public: FireworksItem(int id); bool useOn(shared_ptr instance, shared_ptr player, Level *level, int x, int y, int z, int face, float clickX, float clickY, float clickZ, bool bTestUseOnOnly=false); + virtual shared_ptr use(shared_ptr instance, Level *level, shared_ptr player); void appendHoverText(shared_ptr itemInstance, shared_ptr player, vector *lines, bool advanced); }; \ No newline at end of file diff --git a/Minecraft.World/FlowerPotTile.cpp b/Minecraft.World/FlowerPotTile.cpp index 552cf3dc..cee49218 100644 --- a/Minecraft.World/FlowerPotTile.cpp +++ b/Minecraft.World/FlowerPotTile.cpp @@ -4,14 +4,20 @@ #include "net.minecraft.world.level.h" #include "net.minecraft.world.level.tile.h" #include "FlowerPotTile.h" +#include "FlowerPotTileEntity.h" -FlowerPotTile::FlowerPotTile(int id) : Tile(id, Material::decoration, isSolidRender() ) +FlowerPotTile::FlowerPotTile(int id) : BaseEntityTile(id, Material::decoration, isSolidRender()) { setLightBlock(0); updateDefaultShape(); sendTileData(); } +shared_ptr FlowerPotTile::newTileEntity(Level *level) +{ + return std::make_shared(); +} + void FlowerPotTile::createBlockStateDefinition() { if (!m_blockStateDefinition) @@ -71,6 +77,15 @@ bool FlowerPotTile::use(Level *level, int x, int y, int z, shared_ptr pl { level->setData(x, y, z, type, Tile::UPDATE_CLIENTS); + shared_ptr teBase = level->getTileEntity(x, y, z); + shared_ptr te = teBase ? std::dynamic_pointer_cast(teBase) : nullptr; + if (te == nullptr) + { + te = std::make_shared(); + level->setTileEntity(x, y, z, te); + } + te->setFlowerItem(item->getItem()->id, item->getAuxValue()); + if (!player->abilities.instabuild) { if (--item->count <= 0) @@ -87,30 +102,24 @@ bool FlowerPotTile::use(Level *level, int x, int y, int z, shared_ptr pl int FlowerPotTile::cloneTileId(Level *level, int x, int y, int z) { - shared_ptr item = getItemFromType(level->getData(x, y, z)); + shared_ptr te = level->getTileEntity(x, y, z); + shared_ptr fpte = te ? std::dynamic_pointer_cast(te) : nullptr; + if (fpte && fpte->hasFlower()) + return fpte->getFlowerItemId(); - if (item == nullptr) - { - return Item::flower_pot_Id; - } - else - { - return item->id; - } + shared_ptr item = getItemFromType(level->getData(x, y, z)); + return item ? item->id : Item::flower_pot_Id; } int FlowerPotTile::cloneTileData(Level *level, int x, int y, int z) { - shared_ptr item = getItemFromType(level->getData(x, y, z)); + shared_ptr te = level->getTileEntity(x, y, z); + shared_ptr fpte = te ? std::dynamic_pointer_cast(te) : nullptr; + if (fpte && fpte->hasFlower()) + return fpte->getFlowerAuxValue(); - if (item == nullptr) - { - return Item::flower_pot_Id; - } - else - { - return item->getAuxValue(); - } + shared_ptr item = getItemFromType(level->getData(x, y, z)); + return item ? item->getAuxValue() : 0; } bool FlowerPotTile::useOwnCloneData() @@ -133,15 +142,26 @@ void FlowerPotTile::neighborChanged(Level *level, int x, int y, int z, int type) } } +void FlowerPotTile::onRemove(Level *level, int x, int y, int z, int id, int data) +{ + if (data > 0) + { + shared_ptr te = level->getTileEntity(x, y, z); + shared_ptr fpte = te ? std::dynamic_pointer_cast(te) : nullptr; + shared_ptr item; + if (fpte && fpte->hasFlower()) + item = std::make_shared(fpte->getFlowerItemId(), 1, fpte->getFlowerAuxValue()); + else + item = getItemFromType(data); + if (item != nullptr) + popResource(level, x, y, z, item); + } + BaseEntityTile::onRemove(level, x, y, z, id, data); +} + void FlowerPotTile::spawnResources(Level *level, int x, int y, int z, int data, float odds, int playerBonusLevel) { Tile::spawnResources(level, x, y, z, data, odds, playerBonusLevel); - - if (data > 0) - { - shared_ptr item = getItemFromType(data); - if (item != nullptr) popResource(level, x, y, z, item); - } } int FlowerPotTile::getResource(int data, Random *random, int playerBonusLevel) @@ -154,7 +174,15 @@ shared_ptr FlowerPotTile::getItemFromType(int type) switch (type) { case TYPE_FLOWER_RED: - return std::make_shared(Tile::rose); + return std::make_shared(Tile::rose, 1, 0); + case TYPE_FLOWER_BLUE_ORCHID: + return std::make_shared(Tile::rose, 1, Rose::BLUE_ORCHID); + case TYPE_FLOWER_ALLIUM: + return std::make_shared(Tile::rose, 1, Rose::ALLIUM); + case TYPE_FLOWER_AZURE_BLUET: + return std::make_shared(Tile::rose, 1, Rose::AZURE_BLUET); + case TYPE_FLOWER_OXEYE_DAISY: + return std::make_shared(Tile::rose, 1, Rose::OXEYE_DAISY); case TYPE_FLOWER_YELLOW: return std::make_shared(Tile::flower); case TYPE_CACTUS: @@ -216,4 +244,4 @@ int FlowerPotTile::getTypeFromItem(shared_ptr item) } return 0; -} \ No newline at end of file +} diff --git a/Minecraft.World/FlowerPotTile.h b/Minecraft.World/FlowerPotTile.h index a18c96f6..7094c274 100644 --- a/Minecraft.World/FlowerPotTile.h +++ b/Minecraft.World/FlowerPotTile.h @@ -1,8 +1,8 @@ #pragma once -#include "Tile.h" +#include "BaseEntityTile.h" -class FlowerPotTile : public Tile +class FlowerPotTile : public BaseEntityTile { public: static const int TYPE_FLOWER_RED = 1; @@ -16,8 +16,13 @@ public: static const int TYPE_CACTUS = 9; static const int TYPE_DEAD_BUSH = 10; static const int TYPE_FERN = 11; + static const int TYPE_FLOWER_BLUE_ORCHID = 12; + static const int TYPE_FLOWER_ALLIUM = 13; + static const int TYPE_FLOWER_AZURE_BLUET = 14; + static const int TYPE_FLOWER_OXEYE_DAISY = 15; FlowerPotTile(int id); + shared_ptr newTileEntity(Level *level); virtual void createBlockStateDefinition() override; virtual int defaultBlockState() override; virtual int convertBlockStateToLegacyData(BlockState *state) override; @@ -34,6 +39,7 @@ public: bool useOwnCloneData(); bool mayPlace(Level *level, int x, int y, int z); void neighborChanged(Level *level, int x, int y, int z, int type); + void onRemove(Level *level, int x, int y, int z, int id, int data); using Tile::spawnResources; void spawnResources(Level *level, int x, int y, int z, int data, float odds, int playerBonusLevel); int getResource(int data, Random *random, int playerBonusLevel); diff --git a/Minecraft.World/FlowerPotTileEntity.cpp b/Minecraft.World/FlowerPotTileEntity.cpp new file mode 100644 index 00000000..7709863e --- /dev/null +++ b/Minecraft.World/FlowerPotTileEntity.cpp @@ -0,0 +1,60 @@ +#include "stdafx.h" +#include "com.mojang.nbt.h" +#include "net.minecraft.network.packet.h" +#include "FlowerPotTileEntity.h" +#include "TileEntityDataPacket.h" + +FlowerPotTileEntity::FlowerPotTileEntity() : flowerItemId(-1), flowerAuxValue(0) +{ +} + +void FlowerPotTileEntity::setFlowerItem(int itemId, int auxValue) +{ + flowerItemId = itemId; + flowerAuxValue = auxValue; +} + +int FlowerPotTileEntity::getFlowerItemId() const +{ + return flowerItemId; +} + +int FlowerPotTileEntity::getFlowerAuxValue() const +{ + return flowerAuxValue; +} + +bool FlowerPotTileEntity::hasFlower() const +{ + return flowerItemId >= 0; +} + +void FlowerPotTileEntity::save(CompoundTag *tag) +{ + TileEntity::save(tag); + tag->putInt(L"Item", flowerItemId); + tag->putInt(L"Data", flowerAuxValue); +} + +void FlowerPotTileEntity::load(CompoundTag *tag) +{ + TileEntity::load(tag); + flowerItemId = tag->getInt(L"Item"); + flowerAuxValue = tag->getInt(L"Data"); +} + +shared_ptr FlowerPotTileEntity::getUpdatePacket() +{ + CompoundTag *tag = new CompoundTag(); + save(tag); + return std::make_shared(x, y, z, TileEntityDataPacket::TYPE_FLOWER_POT, tag); +} + +shared_ptr FlowerPotTileEntity::clone() +{ + shared_ptr e = std::make_shared(); + TileEntity::clone(e); + e->flowerItemId = flowerItemId; + e->flowerAuxValue = flowerAuxValue; + return e; +} diff --git a/Minecraft.World/FlowerPotTileEntity.h b/Minecraft.World/FlowerPotTileEntity.h new file mode 100644 index 00000000..2f0ef5d4 --- /dev/null +++ b/Minecraft.World/FlowerPotTileEntity.h @@ -0,0 +1,25 @@ +#pragma once +#include "TileEntity.h" + +class FlowerPotTileEntity : public TileEntity +{ +public: + eINSTANCEOF GetType() { return eTYPE_FLOWERPOTTILEENTITY; } + static TileEntity *create() { return new FlowerPotTileEntity(); } + +private: + int flowerItemId; + int flowerAuxValue; + +public: + FlowerPotTileEntity(); + void setFlowerItem(int itemId, int auxValue); + int getFlowerItemId() const; + int getFlowerAuxValue() const; + bool hasFlower() const; + + void save(CompoundTag *tag); + void load(CompoundTag *tag); + shared_ptr getUpdatePacket(); + virtual shared_ptr clone(); +}; diff --git a/Minecraft.World/FoodConstants.cpp b/Minecraft.World/FoodConstants.cpp index a8e024a2..da431eb3 100644 --- a/Minecraft.World/FoodConstants.cpp +++ b/Minecraft.World/FoodConstants.cpp @@ -25,7 +25,7 @@ const float FoodConstants::FOOD_SATURATION_MAX = 1.0f; const float FoodConstants::FOOD_SATURATION_SUPERNATURAL = 1.2f; // some exhaustion guidelines -const float FoodConstants::EXHAUSTION_HEAL = 3.0f; +const float FoodConstants::EXHAUSTION_HEAL = 4.0f; const float FoodConstants::EXHAUSTION_JUMP = .2f; const float FoodConstants::EXHAUSTION_SPRINT_JUMP = FoodConstants::EXHAUSTION_JUMP * 4; const float FoodConstants::EXHAUSTION_MINE = .025f; diff --git a/Minecraft.World/FoodData.cpp b/Minecraft.World/FoodData.cpp index ab0ced84..0b13d9e4 100644 --- a/Minecraft.World/FoodData.cpp +++ b/Minecraft.World/FoodData.cpp @@ -68,7 +68,8 @@ void FoodData::tick(shared_ptr player) else if (player->level->getGameRules()->getBoolean(GameRules::RULE_NATURAL_REGENERATION) && foodLevel >= FoodConstants::HEAL_LEVEL && player->isHurt()) { tickTimer++; - if (tickTimer >= FoodConstants::HEALTH_TICK_COUNT) + int threshold = (saturationLevel > 0.0f) ? 10 : FoodConstants::HEALTH_TICK_COUNT; + if (tickTimer >= threshold) { player->heal(1); addExhaustion(FoodConstants::EXHAUSTION_HEAL); diff --git a/Minecraft.World/FossilFeature.cpp b/Minecraft.World/FossilFeature.cpp new file mode 100644 index 00000000..1760e847 --- /dev/null +++ b/Minecraft.World/FossilFeature.cpp @@ -0,0 +1,235 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.tile.h" +#include "RotatedPillarTile.h" +#include "FossilFeature.h" + +struct FossilBlock +{ + signed char x, y, z; + signed char data; +}; + +struct FossilStructure +{ + int width, height, length; + const FossilBlock *blocks; + int blockCount; +}; + +static const int AXIS_Y = RotatedPillarTile::FACING_Y; +static const int AXIS_X = RotatedPillarTile::FACING_X; +static const int AXIS_Z = RotatedPillarTile::FACING_Z; +static const int COAL = -1; + +static const FossilBlock skull01[] = { + {1,1,0,AXIS_Y},{4,1,0,AXIS_Y},{1,2,0,AXIS_Y},{2,2,0,AXIS_Y},{3,2,0,AXIS_Y},{4,2,0,AXIS_Y}, + {2,3,0,AXIS_X},{3,3,0,AXIS_X}, + {1,0,1,AXIS_X},{2,0,1,AXIS_X},{3,0,1,AXIS_X},{4,0,1,AXIS_X}, + {0,1,1,AXIS_Y},{5,1,1,AXIS_Y},{0,2,1,AXIS_Y},{5,2,1,AXIS_Y},{0,3,1,AXIS_Y},{5,3,1,AXIS_Y}, + {1,4,1,AXIS_X},{2,4,1,AXIS_X},{3,4,1,AXIS_X},{4,4,1,AXIS_X}, + {1,0,2,AXIS_X},{2,0,2,AXIS_X},{3,0,2,AXIS_X},{4,0,2,AXIS_X}, + {0,1,2,AXIS_Z},{5,1,2,AXIS_Z},{0,2,2,AXIS_Z},{5,2,2,AXIS_Z},{0,3,2,AXIS_Z},{5,3,2,AXIS_Z}, + {1,4,2,AXIS_X},{2,4,2,AXIS_X},{3,4,2,AXIS_X},{4,4,2,AXIS_X}, + {1,0,3,AXIS_X},{2,0,3,AXIS_X},{3,0,3,AXIS_X},{4,0,3,AXIS_X}, + {0,1,3,AXIS_Z},{5,1,3,AXIS_Z},{0,2,3,AXIS_Z},{5,2,3,AXIS_Z},{0,3,3,AXIS_Z},{5,3,3,AXIS_Z}, + {1,4,3,AXIS_X},{2,4,3,AXIS_X},{3,4,3,AXIS_X},{4,4,3,AXIS_X}, + {1,0,4,AXIS_X},{4,0,4,AXIS_X}, + {0,1,4,AXIS_Z},{5,1,4,AXIS_Z},{0,2,4,AXIS_Z},{5,2,4,AXIS_Z},{0,3,4,AXIS_Z},{5,3,4,AXIS_Z}, + {1,4,4,AXIS_X},{2,4,4,AXIS_X},{3,4,4,AXIS_X},{4,4,4,AXIS_X}, + {1,0,5,AXIS_X},{4,0,5,AXIS_X}, + {0,1,5,AXIS_Y},{5,1,5,AXIS_Y},{0,2,5,AXIS_Y},{5,2,5,AXIS_Y},{0,3,5,AXIS_Y},{5,3,5,AXIS_Y}, + {1,4,5,AXIS_X},{2,4,5,AXIS_X},{3,4,5,AXIS_X},{4,4,5,AXIS_X}, + {1,1,6,AXIS_Y},{2,1,6,AXIS_X},{3,1,6,AXIS_X},{4,1,6,AXIS_Y}, + {1,2,6,AXIS_Y},{2,2,6,AXIS_X},{3,2,6,AXIS_X},{4,2,6,AXIS_Y}, + {1,3,6,AXIS_Y},{2,3,6,AXIS_X},{3,3,6,AXIS_X},{4,3,6,AXIS_Y}, +}; + +static const FossilBlock skull02[] = { + {1,1,0,AXIS_Y},{5,1,0,AXIS_Y},{1,2,0,AXIS_Y},{2,2,0,AXIS_X},{3,2,0,AXIS_X},{4,2,0,AXIS_X},{5,2,0,AXIS_Y}, + {1,3,0,AXIS_Y},{3,3,0,AXIS_Y},{5,3,0,AXIS_Y},{2,4,0,AXIS_X},{4,4,0,AXIS_X}, + {1,0,1,AXIS_X},{2,0,1,AXIS_X},{3,0,1,AXIS_X},{4,0,1,AXIS_X},{5,0,1,AXIS_X}, + {0,1,1,AXIS_Y},{6,1,1,AXIS_Y},{0,2,1,AXIS_Y},{6,2,1,AXIS_Y},{0,3,1,AXIS_Y},{6,3,1,AXIS_Y}, + {1,4,1,AXIS_X},{2,4,1,AXIS_X},{3,4,1,AXIS_X},{4,4,1,AXIS_X},{5,4,1,AXIS_X}, + {1,0,2,AXIS_X},{2,0,2,AXIS_X},{3,0,2,AXIS_X},{4,0,2,AXIS_X},{5,0,2,AXIS_X}, + {0,1,2,AXIS_Z},{6,1,2,AXIS_Z},{0,2,2,AXIS_Z},{6,2,2,AXIS_Z},{0,3,2,AXIS_Z},{6,3,2,AXIS_Z}, + {1,4,2,AXIS_X},{2,4,2,AXIS_X},{3,4,2,AXIS_X},{4,4,2,AXIS_X},{5,4,2,AXIS_X}, + {1,0,3,AXIS_X},{2,0,3,AXIS_X},{3,0,3,AXIS_X},{4,0,3,AXIS_X},{5,0,3,AXIS_X}, + {0,1,3,AXIS_Y},{6,1,3,AXIS_Y},{0,2,3,AXIS_Y},{6,2,3,AXIS_Y},{0,3,3,AXIS_Y},{6,3,3,AXIS_Y}, + {1,4,3,AXIS_X},{2,4,3,AXIS_X},{3,4,3,AXIS_X},{4,4,3,AXIS_X},{5,4,3,AXIS_X}, + {1,1,4,AXIS_Y},{2,1,4,AXIS_X},{3,1,4,AXIS_X},{4,1,4,AXIS_X},{5,1,4,AXIS_Y}, + {1,2,4,AXIS_Y},{2,2,4,AXIS_X},{3,2,4,AXIS_X},{4,2,4,AXIS_X},{5,2,4,AXIS_Y}, + {1,3,4,AXIS_Y},{2,3,4,AXIS_X},{3,3,4,AXIS_X},{4,3,4,AXIS_X},{5,3,4,AXIS_Y}, +}; + +static const FossilBlock skull03[] = { + {0,0,0,AXIS_Y},{4,0,0,AXIS_Y}, + {0,1,0,AXIS_Y},{1,1,0,AXIS_X},{2,1,0,AXIS_X},{3,1,0,AXIS_X},{4,1,0,AXIS_Y}, + {0,2,0,AXIS_Y},{2,2,0,AXIS_Y},{4,2,0,AXIS_Y}, + {0,3,0,AXIS_X},{1,3,0,AXIS_X},{2,3,0,AXIS_X},{3,3,0,AXIS_X},{4,3,0,AXIS_X}, + {1,0,1,AXIS_X},{2,0,1,AXIS_X},{3,0,1,AXIS_X}, + {0,1,1,AXIS_Z},{4,1,1,AXIS_Z},{0,2,1,AXIS_Z},{4,2,1,AXIS_Z}, + {1,3,1,AXIS_X},{2,3,1,AXIS_X},{3,3,1,AXIS_X}, + {1,0,2,AXIS_X},{2,0,2,AXIS_X},{3,0,2,AXIS_X}, + {0,1,2,AXIS_Z},{4,1,2,AXIS_Z},{0,2,2,AXIS_Z},{4,2,2,AXIS_Z}, + {1,3,2,AXIS_X},{2,3,2,AXIS_X},{3,3,2,AXIS_X}, + {1,0,3,AXIS_X},{2,0,3,AXIS_X},{3,0,3,AXIS_X}, + {0,1,3,AXIS_Z},{4,1,3,AXIS_Z},{0,2,3,AXIS_Z},{4,2,3,AXIS_Z}, + {1,3,3,AXIS_X},{2,3,3,AXIS_X},{3,3,3,AXIS_X}, + {1,0,4,AXIS_X},{2,0,4,AXIS_X},{3,0,4,AXIS_X}, + {0,1,4,AXIS_Y},{1,1,4,AXIS_X},{2,1,4,AXIS_X},{3,1,4,AXIS_X},{4,1,4,AXIS_Y}, + {0,2,4,AXIS_Y},{1,2,4,AXIS_X},{2,2,4,AXIS_X},{3,2,4,AXIS_X},{4,2,4,AXIS_Y}, +}; + +static const FossilBlock skull04[] = { + {0,0,0,AXIS_Y},{3,0,0,AXIS_Y}, + {0,1,0,AXIS_Y},{1,1,0,AXIS_X},{2,1,0,AXIS_X},{3,1,0,AXIS_Y}, + {0,2,0,AXIS_Y},{3,2,0,AXIS_Y}, + {0,3,0,AXIS_X},{1,3,0,AXIS_X},{2,3,0,AXIS_X},{3,3,0,AXIS_X}, + {1,0,1,AXIS_X},{2,0,1,AXIS_X}, + {0,1,1,AXIS_Y},{3,1,1,AXIS_Y},{0,2,1,AXIS_Y},{3,2,1,AXIS_Y}, + {1,3,1,AXIS_X},{2,3,1,AXIS_X}, + {1,0,2,AXIS_X},{2,0,2,AXIS_X}, + {0,1,2,AXIS_Y},{3,1,2,AXIS_Y},{0,2,2,AXIS_Y},{3,2,2,AXIS_Y}, + {1,3,2,AXIS_X},{2,3,2,AXIS_X}, + {1,1,3,AXIS_Y},{2,1,3,AXIS_Y},{1,2,3,AXIS_Y},{2,2,3,AXIS_Y}, +}; + +static const FossilBlock spine01[] = { + {1,2,0,AXIS_Z}, + {0,0,1,AXIS_Y},{2,0,1,AXIS_Y},{0,1,1,AXIS_Y},{2,1,1,AXIS_Y},{1,2,1,AXIS_Z}, + {1,2,2,AXIS_Z}, + {0,0,3,AXIS_Y},{2,0,3,AXIS_Y},{0,1,3,AXIS_Y},{2,1,3,AXIS_Y},{1,2,3,AXIS_Z}, + {1,2,4,AXIS_Z}, + {0,0,5,AXIS_Y},{2,0,5,AXIS_Y},{0,1,5,AXIS_Y},{2,1,5,AXIS_Y},{1,2,5,AXIS_Z}, + {1,2,6,AXIS_Z}, + {0,0,7,AXIS_Y},{2,0,7,AXIS_Y},{0,1,7,AXIS_Y},{2,1,7,AXIS_Y},{1,2,7,AXIS_Z}, + {1,2,8,AXIS_Z}, + {0,0,9,AXIS_Y},{2,0,9,AXIS_Y},{0,1,9,AXIS_Y},{2,1,9,AXIS_Y},{1,2,9,AXIS_Z}, + {1,2,10,AXIS_Z}, + {0,0,11,AXIS_Y},{2,0,11,AXIS_Y},{0,1,11,AXIS_Y},{2,1,11,AXIS_Y},{1,2,11,AXIS_Z}, + {1,2,12,AXIS_Z}, +}; + +static const FossilBlock spine02[] = { + {2,3,0,AXIS_Z}, + {1,0,1,AXIS_X},{3,0,1,AXIS_X},{0,1,1,AXIS_Y},{4,1,1,AXIS_Y},{0,2,1,AXIS_Y},{4,2,1,AXIS_Y}, + {1,3,1,AXIS_X},{2,3,1,AXIS_Z},{3,3,1,AXIS_X}, + {2,3,2,AXIS_Z}, + {1,0,3,AXIS_X},{3,0,3,AXIS_X},{0,1,3,AXIS_Y},{4,1,3,AXIS_Y},{0,2,3,AXIS_Y},{4,2,3,AXIS_Y}, + {1,3,3,AXIS_X},{2,3,3,AXIS_Z},{3,3,3,AXIS_X}, + {2,3,4,AXIS_Z}, + {1,0,5,AXIS_X},{3,0,5,AXIS_X},{0,1,5,AXIS_Y},{4,1,5,AXIS_Y},{0,2,5,AXIS_Y},{4,2,5,AXIS_Y}, + {1,3,5,AXIS_X},{2,3,5,AXIS_Z},{3,3,5,AXIS_X}, + {2,3,6,AXIS_Z}, + {1,0,7,AXIS_X},{3,0,7,AXIS_X},{0,1,7,AXIS_Y},{4,1,7,AXIS_Y},{0,2,7,AXIS_Y},{4,2,7,AXIS_Y}, + {1,3,7,AXIS_X},{2,3,7,AXIS_Z},{3,3,7,AXIS_X}, + {2,3,8,AXIS_Z}, + {1,0,9,AXIS_X},{3,0,9,AXIS_X},{0,1,9,AXIS_Y},{4,1,9,AXIS_Y},{0,2,9,AXIS_Y},{4,2,9,AXIS_Y}, + {1,3,9,AXIS_X},{2,3,9,AXIS_Z},{3,3,9,AXIS_X}, + {2,3,10,AXIS_Z}, + {1,0,11,AXIS_X},{3,0,11,AXIS_X},{0,1,11,AXIS_Y},{4,1,11,AXIS_Y},{0,2,11,AXIS_Y},{4,2,11,AXIS_Y}, + {1,3,11,AXIS_X},{2,3,11,AXIS_Z},{3,3,11,AXIS_X}, + {2,3,12,AXIS_Z}, +}; + +static const FossilBlock spine03[] = { + {3,3,0,AXIS_Z}, + {0,0,1,AXIS_Y},{1,0,1,AXIS_X},{5,0,1,AXIS_X},{6,0,1,AXIS_Y}, + {0,1,1,AXIS_Y},{6,1,1,AXIS_Y},{0,2,1,AXIS_Y},{6,2,1,AXIS_Y}, + {0,3,1,AXIS_X},{1,3,1,AXIS_X},{2,3,1,AXIS_X},{3,3,1,AXIS_Z},{4,3,1,AXIS_X},{5,3,1,AXIS_X},{6,3,1,AXIS_X}, + {3,3,2,AXIS_Z}, + {0,0,3,AXIS_Y},{1,0,3,AXIS_X},{5,0,3,AXIS_X},{6,0,3,AXIS_Y}, + {0,1,3,AXIS_Y},{6,1,3,AXIS_Y},{0,2,3,AXIS_Y},{6,2,3,AXIS_Y}, + {0,3,3,AXIS_X},{1,3,3,AXIS_X},{2,3,3,AXIS_X},{3,3,3,AXIS_Z},{4,3,3,AXIS_X},{5,3,3,AXIS_X},{6,3,3,AXIS_X}, + {3,3,4,AXIS_Z}, + {0,0,5,AXIS_Y},{1,0,5,AXIS_X},{5,0,5,AXIS_X},{6,0,5,AXIS_Y}, + {0,1,5,AXIS_Y},{6,1,5,AXIS_Y},{0,2,5,AXIS_Y},{6,2,5,AXIS_Y}, + {0,3,5,AXIS_X},{1,3,5,AXIS_X},{2,3,5,AXIS_X},{3,3,5,AXIS_Z},{4,3,5,AXIS_X},{5,3,5,AXIS_X},{6,3,5,AXIS_X}, + {3,3,6,AXIS_Z}, + {0,0,7,AXIS_Y},{1,0,7,AXIS_X},{5,0,7,AXIS_X},{6,0,7,AXIS_Y}, + {0,1,7,AXIS_Y},{6,1,7,AXIS_Y},{0,2,7,AXIS_Y},{6,2,7,AXIS_Y}, + {0,3,7,AXIS_X},{1,3,7,AXIS_X},{2,3,7,AXIS_X},{3,3,7,AXIS_Z},{4,3,7,AXIS_X},{5,3,7,AXIS_X},{6,3,7,AXIS_X}, + {3,3,8,AXIS_Z}, + {0,0,9,AXIS_Y},{1,0,9,AXIS_X},{5,0,9,AXIS_X},{6,0,9,AXIS_Y}, + {0,1,9,AXIS_Y},{6,1,9,AXIS_Y},{0,2,9,AXIS_Y},{6,2,9,AXIS_Y}, + {0,3,9,AXIS_X},{1,3,9,AXIS_X},{2,3,9,AXIS_X},{3,3,9,AXIS_Z},{4,3,9,AXIS_X},{5,3,9,AXIS_X},{6,3,9,AXIS_X}, + {3,3,10,AXIS_Z}, + {0,0,11,AXIS_Y},{1,0,11,AXIS_X},{5,0,11,AXIS_X},{6,0,11,AXIS_Y}, + {0,1,11,AXIS_Y},{6,1,11,AXIS_Y},{0,2,11,AXIS_Y},{6,2,11,AXIS_Y}, + {0,3,11,AXIS_X},{1,3,11,AXIS_X},{2,3,11,AXIS_X},{3,3,11,AXIS_Z},{4,3,11,AXIS_X},{5,3,11,AXIS_X},{6,3,11,AXIS_X}, + {3,3,12,AXIS_Z}, +}; + +static const FossilBlock spine04[] = { + {4,4,0,AXIS_Z}, + {1,0,1,AXIS_X},{2,0,1,AXIS_X},{6,0,1,AXIS_X},{7,0,1,AXIS_X}, + {0,1,1,AXIS_Y},{8,1,1,AXIS_Y},{0,2,1,AXIS_Y},{8,2,1,AXIS_Y},{0,3,1,AXIS_Y},{8,3,1,AXIS_Y}, + {0,4,1,AXIS_X},{1,4,1,AXIS_X},{2,4,1,AXIS_X},{3,4,1,AXIS_X},{4,4,1,AXIS_Z},{5,4,1,AXIS_X},{6,4,1,AXIS_X},{7,4,1,AXIS_X},{8,4,1,AXIS_X}, + {4,4,2,AXIS_Z}, + {1,0,3,AXIS_X},{2,0,3,AXIS_X},{6,0,3,AXIS_X},{7,0,3,AXIS_X}, + {0,1,3,AXIS_Y},{8,1,3,AXIS_Y},{0,2,3,AXIS_Y},{8,2,3,AXIS_Y},{0,3,3,AXIS_Y},{8,3,3,AXIS_Y}, + {0,4,3,AXIS_X},{1,4,3,AXIS_X},{2,4,3,AXIS_X},{3,4,3,AXIS_X},{4,4,3,AXIS_Z},{5,4,3,AXIS_X},{6,4,3,AXIS_X},{7,4,3,AXIS_X},{8,4,3,AXIS_X}, + {4,4,4,AXIS_Z}, + {1,0,5,AXIS_X},{2,0,5,AXIS_X},{6,0,5,AXIS_X},{7,0,5,AXIS_X}, + {0,1,5,AXIS_Y},{8,1,5,AXIS_Y},{0,2,5,AXIS_Y},{8,2,5,AXIS_Y},{0,3,5,AXIS_Y},{8,3,5,AXIS_Y}, + {0,4,5,AXIS_X},{1,4,5,AXIS_X},{2,4,5,AXIS_X},{3,4,5,AXIS_X},{4,4,5,AXIS_Z},{5,4,5,AXIS_X},{6,4,5,AXIS_X},{7,4,5,AXIS_X},{8,4,5,AXIS_X}, + {4,4,6,AXIS_Z}, + {1,0,7,AXIS_X},{2,0,7,AXIS_X},{6,0,7,AXIS_X},{7,0,7,AXIS_X}, + {0,1,7,AXIS_Y},{8,1,7,AXIS_Y},{0,2,7,AXIS_Y},{8,2,7,AXIS_Y},{0,3,7,AXIS_Y},{8,3,7,AXIS_Y}, + {0,4,7,AXIS_X},{1,4,7,AXIS_X},{2,4,7,AXIS_X},{3,4,7,AXIS_X},{4,4,7,AXIS_Z},{5,4,7,AXIS_X},{6,4,7,AXIS_X},{7,4,7,AXIS_X},{8,4,7,AXIS_X}, + {4,4,8,AXIS_Z}, + {1,0,9,AXIS_X},{2,0,9,AXIS_X},{6,0,9,AXIS_X},{7,0,9,AXIS_X}, + {0,1,9,AXIS_Y},{8,1,9,AXIS_Y},{0,2,9,AXIS_Y},{8,2,9,AXIS_Y},{0,3,9,AXIS_Y},{8,3,9,AXIS_Y}, + {0,4,9,AXIS_X},{1,4,9,AXIS_X},{2,4,9,AXIS_X},{3,4,9,AXIS_X},{4,4,9,AXIS_Z},{5,4,9,AXIS_X},{6,4,9,AXIS_X},{7,4,9,AXIS_X},{8,4,9,AXIS_X}, + {4,4,10,AXIS_Z}, + {1,0,11,AXIS_X},{2,0,11,AXIS_X},{6,0,11,AXIS_X},{7,0,11,AXIS_X}, + {0,1,11,AXIS_Y},{8,1,11,AXIS_Y},{0,2,11,AXIS_Y},{8,2,11,AXIS_Y},{0,3,11,AXIS_Y},{8,3,11,AXIS_Y}, + {0,4,11,AXIS_X},{1,4,11,AXIS_X},{2,4,11,AXIS_X},{3,4,11,AXIS_X},{4,4,11,AXIS_Z},{5,4,11,AXIS_X},{6,4,11,AXIS_X},{7,4,11,AXIS_X},{8,4,11,AXIS_X}, + {4,4,12,AXIS_Z}, +}; + +static const FossilStructure skullStructures[4] = { + {6, 5, 7, skull01, (int)(sizeof(skull01)/sizeof(skull01[0]))}, + {7, 5, 5, skull02, (int)(sizeof(skull02)/sizeof(skull02[0]))}, + {5, 4, 5, skull03, (int)(sizeof(skull03)/sizeof(skull03[0]))}, + {4, 4, 4, skull04, (int)(sizeof(skull04)/sizeof(skull04[0]))}, +}; + +static const FossilStructure spineStructures[4] = { + {3, 3, 13, spine01, (int)(sizeof(spine01)/sizeof(spine01[0]))}, + {5, 4, 13, spine02, (int)(sizeof(spine02)/sizeof(spine02[0]))}, + {7, 4, 13, spine03, (int)(sizeof(spine03)/sizeof(spine03[0]))}, + {9, 5, 13, spine04, (int)(sizeof(spine04)/sizeof(spine04[0]))}, +}; + +bool FossilFeature::place(Level *level, Random *random, int x, int y, int z) +{ + bool useSpine = random->nextInt(2) == 0; + bool useCoal = random->nextInt(2) == 0; + int variant = random->nextInt(4); + + const FossilStructure &s = useSpine ? spineStructures[variant] : skullStructures[variant]; + + int ox = x - s.width / 2; + int oy = y; + int oz = z - s.length / 2; + + for (int i = 0; i < s.blockCount; i++) + { + const FossilBlock &b = s.blocks[i]; + int bx = ox + b.x; + int by = oy + b.y; + int bz = oz + b.z; + + if (by < 1 || by >= Level::maxBuildHeight) + continue; + + if (useCoal) + level->setTileAndData(bx, by, bz, Tile::coal_ore_Id, 0, Tile::UPDATE_CLIENTS); + else + level->setTileAndData(bx, by, bz, Tile::bone_block_Id, b.data, Tile::UPDATE_CLIENTS); + } + + return true; +} diff --git a/Minecraft.World/FossilFeature.h b/Minecraft.World/FossilFeature.h new file mode 100644 index 00000000..afc18e4a --- /dev/null +++ b/Minecraft.World/FossilFeature.h @@ -0,0 +1,9 @@ +#pragma once + +#include "Feature.h" + +class FossilFeature : public Feature +{ +public: + bool place(Level *level, Random *random, int x, int y, int z) override; +}; diff --git a/Minecraft.World/HellRandomLevelSource.cpp b/Minecraft.World/HellRandomLevelSource.cpp index 64a70b96..a20f5d64 100644 --- a/Minecraft.World/HellRandomLevelSource.cpp +++ b/Minecraft.World/HellRandomLevelSource.cpp @@ -487,6 +487,15 @@ void HellRandomLevelSource::postProcess(ChunkSource *parent, int xt, int zt) FlowerFeature(Tile::mushroom_red_Id).place(level, pprandom, x, y, z); } + OreFeature magmaFeature(Tile::magma_Id, 0, 160, Tile::netherrack_Id); + for (int i = 0; i < 4; i++) + { + int x = xo + pprandom->nextInt(16); + int y = pprandom->nextInt(9) + 27; + int z = zo + pprandom->nextInt(16); + magmaFeature.place(level, pprandom, x, y, z); + } + OreFeature quartzFeature(Tile::quartz_ore_Id, 0, 13, Tile::netherrack_Id); for (int i = 0; i < 16; i++) { diff --git a/Minecraft.World/HugeMushroomFeature.cpp b/Minecraft.World/HugeMushroomFeature.cpp index 97c59305..9d91facc 100644 --- a/Minecraft.World/HugeMushroomFeature.cpp +++ b/Minecraft.World/HugeMushroomFeature.cpp @@ -19,6 +19,7 @@ bool HugeMushroomFeature::place(Level *level, Random *random, int x, int y, int if (forcedType >= 0) type = forcedType; int treeHeight = random->nextInt(3) + 4; + if (random->nextBoolean()) treeHeight *= 2; bool free = true; if (y < 1 || y + treeHeight + 1 >= Level::maxBuildHeight) return false; diff --git a/Minecraft.World/IglooFeature.cpp b/Minecraft.World/IglooFeature.cpp new file mode 100644 index 00000000..99422f2d --- /dev/null +++ b/Minecraft.World/IglooFeature.cpp @@ -0,0 +1,218 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.level.tile.entity.h" +#include "Biome.h" +#include "Item.h" +#include "ItemInstance.h" +#include "WeighedTreasure.h" +#include "Villager.h" +#include "Zombie.h" +#include "IglooFeature.h" + +bool IglooFeature::place(Level *level, Random *random, int x, int y, int z) +{ + while (y > 2 && level->isEmptyTile(x, y - 1, z)) + y--; + + Biome *biome = level->getBiome(x, z); + if (biome != Biome::iceFlats && biome != Biome::iceMountains && + biome != Biome::coldTaiga && biome != Biome::coldTaigaHills) + return false; + + for (int dx = -3; dx <= 3; dx++) + for (int dz = -3; dz <= 3; dz++) + if (abs(dx) + abs(dz) < 6) + level->setTileAndData(x + dx, y, z + dz, Tile::snow_Id, 0, Tile::UPDATE_CLIENTS); + + for (int dy = 1; dy <= 2; dy++) + { + for (int dx = -3; dx <= 3; dx++) + { + for (int dz = -3; dz <= 3; dz++) + { + bool onPerimeter = (abs(dx) == 3 || abs(dz) == 3) && abs(dx) + abs(dz) < 6; + if (!onPerimeter) + continue; + if (dx == 0 && dz == 3) + continue; + if ((dx == 3 || dx == -3) && dz == 0 && dy == 1) + level->setTileAndData(x + dx, y + dy, z + dz, Tile::ice_Id, 0, Tile::UPDATE_CLIENTS); + else + level->setTileAndData(x + dx, y + dy, z + dz, Tile::snow_Id, 0, Tile::UPDATE_CLIENTS); + } + } + } + + level->setTileAndData(x, y + 3, z + 3, Tile::snow_Id, 0, Tile::UPDATE_CLIENTS); + + level->setTileAndData(x - 1, y, z + 4, Tile::snow_Id, 0, Tile::UPDATE_CLIENTS); + level->setTileAndData(x, y, z + 4, Tile::snow_Id, 0, Tile::UPDATE_CLIENTS); + level->setTileAndData(x + 1, y, z + 4, Tile::snow_Id, 0, Tile::UPDATE_CLIENTS); + level->setTileAndData(x - 1, y + 1, z + 4, Tile::snow_Id, 0, Tile::UPDATE_CLIENTS); + level->setTileAndData(x + 1, y + 1, z + 4, Tile::snow_Id, 0, Tile::UPDATE_CLIENTS); + level->setTileAndData(x - 1, y + 2, z + 4, Tile::snow_Id, 0, Tile::UPDATE_CLIENTS); + level->setTileAndData(x + 1, y + 2, z + 4, Tile::snow_Id, 0, Tile::UPDATE_CLIENTS); + level->setTileAndData(x, y + 3, z + 4, Tile::snow_Id, 0, Tile::UPDATE_CLIENTS); + + for (int dx = -2; dx <= 2; dx++) + for (int dz = -2; dz <= 2; dz++) + if (abs(dx) == 2 || abs(dz) == 2) + level->setTileAndData(x + dx, y + 3, z + dz, Tile::snow_Id, 0, Tile::UPDATE_CLIENTS); + + for (int dx = -1; dx <= 1; dx++) + for (int dz = -1; dz <= 1; dz++) + level->setTileAndData(x + dx, y + 4, z + dz, Tile::snow_Id, 0, Tile::UPDATE_CLIENTS); + + for (int dy = 1; dy <= 2; dy++) + for (int dx = -2; dx <= 2; dx++) + for (int dz = -2; dz <= 2; dz++) + level->setTileAndData(x + dx, y + dy, z + dz, 0, 0, Tile::UPDATE_CLIENTS); + + for (int dx = -1; dx <= 1; dx++) + for (int dz = -1; dz <= 1; dz++) + level->setTileAndData(x + dx, y + 3, z + dz, 0, 0, Tile::UPDATE_CLIENTS); + + for (int dx = -1; dx <= 1; dx++) + for (int dz = -1; dz <= 1; dz++) + level->setTileAndData(x + dx, y + 1, z + dz, Tile::carpet_Id, 0, Tile::UPDATE_CLIENTS); + + level->setTileAndData(x - 1, y + 1, z - 2, Tile::carpet_Id, 8, Tile::UPDATE_CLIENTS); + level->setTileAndData(x, y + 1, z - 2, Tile::carpet_Id, 8, Tile::UPDATE_CLIENTS); + level->setTileAndData(x + 1, y + 1, z - 2, Tile::carpet_Id, 8, Tile::UPDATE_CLIENTS); + + level->setTileAndData(x + 2, y + 1, z + 1, Tile::furnace_Id, 4, Tile::UPDATE_CLIENTS); + level->setTileAndData(x + 2, y + 1, z, Tile::redstone_torch_Id, 5, Tile::UPDATE_CLIENTS); + level->setTileAndData(x + 2, y + 1, z - 1, Tile::crafting_table_Id, 0, Tile::UPDATE_CLIENTS); + + level->setTileAndData(x - 2, y + 1, z, Tile::bed_Id, 2, Tile::UPDATE_CLIENTS); + level->setTileAndData(x - 2, y + 1, z - 1, Tile::bed_Id, 2 | 8, Tile::UPDATE_CLIENTS); + + level->setTileAndData(x - 2, y + 1, z - 2, Tile::snow_Id, 0, Tile::UPDATE_CLIENTS); + level->setTileAndData(x + 2, y + 1, z - 2, Tile::snow_Id, 0, Tile::UPDATE_CLIENTS); + level->setTileAndData(x - 2, y + 1, z + 2, Tile::snow_Id, 0, Tile::UPDATE_CLIENTS); + level->setTileAndData(x + 2, y + 1, z + 2, Tile::snow_Id, 0, Tile::UPDATE_CLIENTS); + level->setTileAndData(x - 2, y + 2, z - 2, Tile::snow_Id, 0, Tile::UPDATE_CLIENTS); + level->setTileAndData(x + 2, y + 2, z - 2, Tile::snow_Id, 0, Tile::UPDATE_CLIENTS); + level->setTileAndData(x - 2, y + 2, z + 2, Tile::snow_Id, 0, Tile::UPDATE_CLIENTS); + level->setTileAndData(x + 2, y + 2, z + 2, Tile::snow_Id, 0, Tile::UPDATE_CLIENTS); + + if (random->nextInt(2) == 0) + placeBasement(level, random, x, y, z); + + return true; +} + +void IglooFeature::placeBasement(Level *level, Random *random, int x, int y, int z) +{ + int by = y - 9; + int rz = z + 2; + + for (int dx = -3; dx <= 3; dx++) + { + for (int dz = -4; dz <= 4; dz++) + { + for (int dy = 0; dy <= 4; dy++) + { + bool isWall = abs(dx) == 3 || abs(dz) == 4 || dy == 0 || dy == 4; + if (isWall) + { + int data = 0; + if (random->nextInt(5) == 0) + data = 2; + else if (random->nextInt(4) == 0) + data = 1; + level->setTileAndData(x + dx, by + dy, rz + dz, Tile::stonebrick_Id, data, Tile::UPDATE_CLIENTS); + } + else + { + level->setTileAndData(x + dx, by + dy, rz + dz, 0, 0, Tile::UPDATE_CLIENTS); + } + } + } + } + + level->setTileAndData(x, y, z - 1, Tile::trapdoor_Id, 4, Tile::UPDATE_CLIENTS); + + for (int i = 1; i <= 5; i++) + level->setTileAndData(x, y - i, z - 1, Tile::ladder_Id, 3, Tile::UPDATE_CLIENTS); + + for (int dy = 1; dy <= 3; dy++) + level->setTileAndData(x, by + dy, z - 1, Tile::ladder_Id, 3, Tile::UPDATE_CLIENTS); + + level->setTileAndData(x + 2, by + 1, rz - 2, Tile::chest_Id, 4, Tile::UPDATE_CLIENTS); + auto chestEntity = dynamic_pointer_cast(level->getTileEntity(x + 2, by + 1, rz - 2)); + if (chestEntity) + { + static WeighedTreasure *iglooTreasure[] = { + new WeighedTreasure(Item::coal_Id, 0, 1, 4, 15), + new WeighedTreasure(Item::apple_Id, 0, 1, 3, 15), + new WeighedTreasure(Item::wheat_Id, 0, 2, 3, 10), + new WeighedTreasure(Item::gold_nugget_Id, 0, 1, 3, 10), + new WeighedTreasure(Item::rotten_flesh_Id,0, 1, 1, 10), + new WeighedTreasure(Item::stone_axe_Id, 0, 1, 1, 2), + new WeighedTreasure(Item::emerald_Id, 0, 1, 1, 1), + }; + WeighedTreasureArray treasureArray(iglooTreasure, 7); + int rolls = 2 + random->nextInt(7); + WeighedTreasure::addChestItems(random, treasureArray, chestEntity, rolls); + chestEntity->setItem(0, make_shared(Item::golden_apple_Id, 1, 0)); + } + + level->setTileAndData(x - 2, by + 2, rz - 2, Tile::brewing_stand_Id, 0, Tile::UPDATE_CLIENTS); + auto brewEntity = dynamic_pointer_cast(level->getTileEntity(x - 2, by + 2, rz - 2)); + if (brewEntity) + brewEntity->setItem(1, make_shared(Item::potion_Id, 1, 16392)); + level->setTileAndData(x - 2, by + 1, rz, Tile::cauldron_Id, 2, Tile::UPDATE_CLIENTS); + + level->setTileAndData(x - 2, by + 1, rz - 3, Tile::spruce_stairs_Id, 7, Tile::UPDATE_CLIENTS); + level->setTileAndData(x - 2, by + 1, rz - 2, Tile::wooden_slab_Id, 9, Tile::UPDATE_CLIENTS); + level->setTileAndData(x - 2, by + 1, rz - 1, Tile::spruce_stairs_Id, 6, Tile::UPDATE_CLIENTS); + + level->setTileAndData(x + 2, by + 1, rz - 1, Tile::carpet_Id, 14, Tile::UPDATE_CLIENTS); + level->setTileAndData(x + 2, by + 1, rz, Tile::carpet_Id, 14, Tile::UPDATE_CLIENTS); + level->setTileAndData(x + 2, by + 1, rz + 1, Tile::carpet_Id, 14, Tile::UPDATE_CLIENTS); + + for (int cdx = -2; cdx <= 2; cdx++) + { + bool isBar = (cdx % 2 != 0); + level->setTileAndData(x + cdx, by + 1, rz + 2, isBar ? Tile::iron_bars_Id : Tile::stonebrick_Id, 0, Tile::UPDATE_CLIENTS); + level->setTileAndData(x + cdx, by + 2, rz + 2, isBar ? Tile::iron_bars_Id : Tile::stonebrick_Id, 0, Tile::UPDATE_CLIENTS); + level->setTileAndData(x + cdx, by + 3, rz + 2, Tile::stonebrick_Id, 0, Tile::UPDATE_CLIENTS); + } + + for (int dy = 1; dy <= 3; dy++) + { + level->setTileAndData(x - 2, by + dy, rz + 3, Tile::stonebrick_Id, 0, Tile::UPDATE_CLIENTS); + level->setTileAndData(x, by + dy, rz + 3, Tile::stonebrick_Id, 0, Tile::UPDATE_CLIENTS); + level->setTileAndData(x + 2, by + dy, rz + 3, Tile::stonebrick_Id, 0, Tile::UPDATE_CLIENTS); + } + + level->setTileAndData(x + 2, by + 3, rz + 1, Tile::web_Id, 0, Tile::UPDATE_CLIENTS); + + level->setTileAndData(x - 1, by + 3, z - 1, Tile::torch_Id, 3, Tile::UPDATE_CLIENTS); + level->setTileAndData(x + 1, by + 3, z - 1, Tile::torch_Id, 3, Tile::UPDATE_CLIENTS); + level->setTileAndData(x, by + 3, rz + 1, Tile::torch_Id, 4, Tile::UPDATE_CLIENTS); + level->setTileAndData(x, by + 2, rz + 1, Tile::wall_standing_sign_Id, 2, Tile::UPDATE_CLIENTS); + auto signEntity = dynamic_pointer_cast(level->getTileEntity(x, by + 2, rz + 1)); + if (signEntity) + { + wstring empty = L""; + wstring line1 = L"<----"; + wstring line2 = L"---->"; + signEntity->SetMessage(0, empty); + signEntity->SetMessage(1, line1); + signEntity->SetMessage(2, line2); + signEntity->SetMessage(3, empty); + } + level->setTileAndData(x - 2, by + 2, rz - 3, Tile::flower_pot_Id, 9, Tile::UPDATE_CLIENTS); + + auto zombieVillager = std::make_shared(level); + zombieVillager->setVillager(true); + zombieVillager->moveTo(x - 1 + 0.5, by + 1, rz + 3 + 0.5, 0, 0); + level->addEntity(zombieVillager); + + auto villager = std::make_shared(level); + villager->moveTo(x + 1 + 0.5, by + 1, rz + 3 + 0.5, 0, 0); + level->addEntity(villager); +} diff --git a/Minecraft.World/IglooFeature.h b/Minecraft.World/IglooFeature.h new file mode 100644 index 00000000..a8e391ba --- /dev/null +++ b/Minecraft.World/IglooFeature.h @@ -0,0 +1,12 @@ +#pragma once + +#include "Feature.h" + +class IglooFeature : public Feature +{ +public: + bool place(Level *level, Random *random, int x, int y, int z) override; + +private: + void placeBasement(Level *level, Random *random, int x, int y, int z); +}; diff --git a/Minecraft.World/LivingEntity.cpp b/Minecraft.World/LivingEntity.cpp index ed710c5f..eda0924a 100644 --- a/Minecraft.World/LivingEntity.cpp +++ b/Minecraft.World/LivingEntity.cpp @@ -753,23 +753,28 @@ void LivingEntity::removeEffect(int effectId) void LivingEntity::onEffectAdded(MobEffectInstance *effect) { effectsDirty = true; - if (!level->isClientSide) MobEffect::effects[effect->getId()]->addAttributeModifiers(dynamic_pointer_cast(shared_from_this()), getAttributes(), effect->getAmplifier()); + int _id0 = effect->getId(); + if (!level->isClientSide && _id0 >= 0 && _id0 < MobEffect::NUM_EFFECTS && MobEffect::effects[_id0]) + MobEffect::effects[_id0]->addAttributeModifiers(dynamic_pointer_cast(shared_from_this()), getAttributes(), effect->getAmplifier()); } void LivingEntity::onEffectUpdated(MobEffectInstance *effect, bool doRefreshAttributes) { effectsDirty = true; - if (doRefreshAttributes && !level->isClientSide) + int _id1 = effect->getId(); + if (doRefreshAttributes && !level->isClientSide && _id1 >= 0 && _id1 < MobEffect::NUM_EFFECTS && MobEffect::effects[_id1]) { - MobEffect::effects[effect->getId()]->removeAttributeModifiers(dynamic_pointer_cast(shared_from_this()), getAttributes(), effect->getAmplifier()); - MobEffect::effects[effect->getId()]->addAttributeModifiers(dynamic_pointer_cast(shared_from_this()), getAttributes(), effect->getAmplifier()); + MobEffect::effects[_id1]->removeAttributeModifiers(dynamic_pointer_cast(shared_from_this()), getAttributes(), effect->getAmplifier()); + MobEffect::effects[_id1]->addAttributeModifiers(dynamic_pointer_cast(shared_from_this()), getAttributes(), effect->getAmplifier()); } } void LivingEntity::onEffectRemoved(MobEffectInstance *effect) { effectsDirty = true; - if (!level->isClientSide) MobEffect::effects[effect->getId()]->removeAttributeModifiers(dynamic_pointer_cast(shared_from_this()), getAttributes(), effect->getAmplifier()); + int _id2 = effect->getId(); + if (!level->isClientSide && _id2 >= 0 && _id2 < MobEffect::NUM_EFFECTS && MobEffect::effects[_id2]) + MobEffect::effects[_id2]->removeAttributeModifiers(dynamic_pointer_cast(shared_from_this()), getAttributes(), effect->getAmplifier()); } void LivingEntity::heal(float heal) diff --git a/Minecraft.World/MineShaftFeature.cpp b/Minecraft.World/MineShaftFeature.cpp index ebf59025..b331d7e6 100644 --- a/Minecraft.World/MineShaftFeature.cpp +++ b/Minecraft.World/MineShaftFeature.cpp @@ -1,5 +1,6 @@ #include "stdafx.h" #include "net.minecraft.world.level.levelgen.structure.h" +#include "net.minecraft.world.level.biome.h" #include "JavaMath.h" #include "Mth.h" @@ -37,7 +38,16 @@ bool MineShaftFeature::isFeatureChunk(int x, int z, bool bIsSuperflat) forcePlacement = levelGenOptions->isFeatureChunk(x,z,eFeature_Mineshaft); } - return forcePlacement || (random->nextDouble() < chance && random->nextInt(80) < max(abs(x), abs(z))); + double effectiveChance = chance; + if (level != nullptr) + { + Biome *b = level->getBiome(x * 16, z * 16); + if (b == Biome::mesa || b == Biome::mesaPlateauF || b == Biome::mesaPlateau || + b == Biome::mesaBryce || b == Biome::mesaPlateauFM || b == Biome::mesaPlateauM) + effectiveChance = 0.9; + } + + return forcePlacement || (random->nextDouble() < effectiveChance && random->nextInt(80) < max(abs(x), abs(z))); } StructureStart *MineShaftFeature::createStructureStart(int x, int z) diff --git a/Minecraft.World/MobEffectInstance.cpp b/Minecraft.World/MobEffectInstance.cpp index 117311c3..470db177 100644 --- a/Minecraft.World/MobEffectInstance.cpp +++ b/Minecraft.World/MobEffectInstance.cpp @@ -1,6 +1,12 @@ #include "stdafx.h" #include "net.minecraft.world.effect.h" +static MobEffect *safeEffect(int id) +{ + if (id < 0 || id >= MobEffect::NUM_EFFECTS) return nullptr; + return MobEffect::effects[id]; +} + void MobEffectInstance::_init(int id, int duration, int amplifier) { this->id = id; @@ -104,7 +110,8 @@ bool MobEffectInstance::tick(shared_ptr target) { if (duration > 0) { - if (MobEffect::effects[id]->isDurationEffectTick(duration, amplifier)) + MobEffect *effect = safeEffect(id); + if (effect && effect->isDurationEffectTick(duration, amplifier)) { applyEffect(target); } @@ -122,19 +129,22 @@ void MobEffectInstance::applyEffect(shared_ptr mob) { if (duration > 0) { - MobEffect::effects[id]->applyEffectTick(mob, amplifier); + MobEffect *effect = safeEffect(id); + if (effect) effect->applyEffectTick(mob, amplifier); } } int MobEffectInstance::getDescriptionId() { - return MobEffect::effects[id]->getDescriptionId(); + MobEffect *effect = safeEffect(id); + return effect ? effect->getDescriptionId() : 0; } // 4J Added int MobEffectInstance::getPostfixDescriptionId() { - return MobEffect::effects[id]->getPostfixDescriptionId(); + MobEffect *effect = safeEffect(id); + return effect ? effect->getPostfixDescriptionId() : 0; } int MobEffectInstance::hashCode() @@ -182,6 +192,7 @@ CompoundTag *MobEffectInstance::save(CompoundTag *tag) MobEffectInstance *MobEffectInstance::load(CompoundTag *tag) { int id = tag->getByte(L"Id"); + if (id < 0 || id >= MobEffect::NUM_EFFECTS) return nullptr; int amplifier = tag->getByte(L"Amplifier"); int duration = tag->getInt(L"Duration"); boolean ambient = tag->getBoolean(L"Ambient"); diff --git a/Minecraft.World/OreRecipies.cpp b/Minecraft.World/OreRecipies.cpp index d408f3af..cb7aebb3 100644 --- a/Minecraft.World/OreRecipies.cpp +++ b/Minecraft.World/OreRecipies.cpp @@ -32,8 +32,6 @@ void OreRecipies::_init() ADD_OBJECT(map[7],Tile::hayBlock); ADD_OBJECT(map[7],new ItemInstance(Item::wheat, 9)); - ADD_OBJECT(map[8],Tile::slimeBlock); - ADD_OBJECT(map[8],new ItemInstance(Item::slime_ball, 9)); } void OreRecipies::addRecipes(Recipes *r) { diff --git a/Minecraft.World/OreRecipies.h b/Minecraft.World/OreRecipies.h index 0c675f3f..f2bef1f7 100644 --- a/Minecraft.World/OreRecipies.h +++ b/Minecraft.World/OreRecipies.h @@ -1,6 +1,6 @@ #pragma once -#define MAX_ORE_RECIPES 9 +#define MAX_ORE_RECIPES 8 class OreRecipies { diff --git a/Minecraft.World/PlainsBiome.cpp b/Minecraft.World/PlainsBiome.cpp index c55df71d..4f672c59 100644 --- a/Minecraft.World/PlainsBiome.cpp +++ b/Minecraft.World/PlainsBiome.cpp @@ -80,5 +80,12 @@ void PlainsBiome::decorate(Level* level, Random* rand, int xo, int zo) DOUBLE_PLANT_GENERATOR->place(level, rand, j1, l1, k1); } } - Biome::decorate(level, rand, xo, zo); + if (rand->nextInt(10) == 0) + decorator->treeCount = 1; + else + decorator->treeCount = 0; + + Biome::decorate(level, rand, xo, zo); + + decorator->treeCount = -999; } \ No newline at end of file diff --git a/Minecraft.World/Player.cpp b/Minecraft.World/Player.cpp index df1f8bd6..82af814c 100644 --- a/Minecraft.World/Player.cpp +++ b/Minecraft.World/Player.cpp @@ -91,6 +91,9 @@ void Player::_init() m_elytraImpactYd = 0.0f; m_wasElytraFlying = false; m_elytraFallProtectTicks = 0; + m_elytraMaxY = 0.0f; + m_fireworkBoostTicks = 0; + m_elytraRocketCooldown = 0; takeXpDelay = 0; experienceLevel = totalExperience = 0; @@ -1016,6 +1019,7 @@ void Player::aiStep() { if (jumpTriggerTime > 0) jumpTriggerTime--; if (m_elytraFallProtectTicks > 0) m_elytraFallProtectTicks--; + if (m_elytraRocketCooldown > 0) m_elytraRocketCooldown--; if (level->difficulty == Difficulty::PEACEFUL && getHealth() < getMaxHealth() && level->getGameRules()->getBoolean(GameRules::RULE_NATURAL_REGENERATION)) @@ -1025,7 +1029,7 @@ void Player::aiStep() inventory->tick(); oBob = bob; - if (jumping && !onGround && yd < 0.0 && !isElytraFlying() && !abilities.flying && jumpTriggerTime == 0) + if (jumping && !onGround && yd < 0.0 && fallDistance > 0.0f && !isElytraFlying() && !abilities.flying && jumpTriggerTime == 0) { shared_ptr chestItem = inventory->armor[LivingEntity::SLOT_CHEST - 1]; if (chestItem != nullptr && chestItem->getItem() != nullptr) @@ -1044,20 +1048,12 @@ void Player::aiStep() { ticksElytraFlying++; - if (!level->isClientSide && ticksElytraFlying % 20 == 0) - { - shared_ptr chestItem = inventory->armor[LivingEntity::SLOT_CHEST - 1]; - if (chestItem != nullptr && dynamic_cast(chestItem->getItem()) != nullptr) - { - chestItem->hurtAndBreak(1, dynamic_pointer_cast(shared_from_this())); - if (!ElytraItem::isFlyEnabled(chestItem)) - setElytraFlying(false); - } - else - { - setElytraFlying(false); - } - } + shared_ptr chestItem = inventory->armor[LivingEntity::SLOT_CHEST - 1]; + if (chestItem == nullptr + || chestItem->count <= 0 + || dynamic_cast(chestItem->getItem()) == nullptr + || !ElytraItem::isFlyEnabled(chestItem)) + setElytraFlying(false); } @@ -2171,6 +2167,8 @@ void Player::travel(float xa, float ya) double preHorizSpeed = Mth::sqrt(xd * xd + zd * zd); + if ((float)y > m_elytraMaxY) m_elytraMaxY = (float)y; + if (yd > -0.5) fallDistance = 1.0f; @@ -2217,10 +2215,19 @@ void Player::travel(float xa, float ya) if (jumping && abilities.instabuild) yd += (double)abilities.getFlyingSpeed() * 3.0; - m_elytraImpactYd = (float)yd; + if (m_fireworkBoostTicks > 0) + { + Vec3 *look = getLookAngle(); + xd += look->x * 0.1 + (look->x * 1.5 - xd) * 0.5; + yd += look->y * 0.1 + (look->y * 1.5 - yd) * 0.5; + zd += look->z * 0.1 + (look->z * 1.5 - zd) * 0.5; + m_fireworkBoostTicks--; + } + + m_elytraImpactYd = (float)yd; move(xd, yd, zd); - if (horizontalCollision && !verticalCollision) + if (horizontalCollision) { double postHorizSpeed = Mth::sqrt(xd * xd + zd * zd); double speedLost = preHorizSpeed - postHorizSpeed; @@ -2428,6 +2435,14 @@ void Player::causeFallDamage(float distance) { if (abilities.mayfly) return; + if (isElytraFlying()) + { + int dmg = Mth::ceil(distance - 3.0f); + if (dmg > 0) + onElytraFallDamage(dmg); + return; + } + if (distance >= 2) { distanceFall += static_cast(Math::round(distance * 100.0)); @@ -2835,9 +2850,26 @@ bool Player::isElytraFlying() return getPlayerFlag(FLAG_ELYTRA_FLYING); } +void Player::startFireworkBoost(shared_ptr firework) +{ + int flight = 0; + if (firework != nullptr && firework->hasTag()) + { + CompoundTag *fw = firework->getTag()->getCompound(FireworksItem::TAG_FIREWORKS); + if (fw != nullptr && fw->contains(FireworksItem::TAG_FLIGHT)) + flight = fw->getByte(FireworksItem::TAG_FLIGHT); + } + m_fireworkBoostTicks = (SharedConstants::TICKS_PER_SECOND / 2) * (flight + 1) + random->nextInt(6) + random->nextInt(7); +} + void Player::onElytraKineticDamage(float damage) { - hurt(DamageSource::fall, damage); + hurt(DamageSource::flyIntoWall, damage); +} + +void Player::onElytraFallDamage(int amount) +{ + hurt(DamageSource::fall, (float)amount); } void Player::setElytraFlying(bool flying) @@ -2846,8 +2878,9 @@ void Player::setElytraFlying(bool flying) if (flying) { m_wasElytraFlying = false; - setSize(0.6f, 0.6f); - fallDistance = 0.0f; + setSize(0.6f, 0.6f); + fallDistance = 0.0f; + m_elytraMaxY = (float)y; } else { @@ -2855,8 +2888,9 @@ void Player::setElytraFlying(bool flying) m_wasElytraFlying = true; m_elytraFallProtectTicks = 60; - setSize(0.6f, 1.8f); - + m_fireworkBoostTicks = 0; + m_elytraRocketCooldown = 0; + setSize(0.6f, 1.8f); } } diff --git a/Minecraft.World/Player.h b/Minecraft.World/Player.h index 59979f9a..482d2a1e 100644 --- a/Minecraft.World/Player.h +++ b/Minecraft.World/Player.h @@ -85,13 +85,21 @@ public: float rotateElytraX; float rotateElytraY; float rotateElytraZ; - float m_elytraImpactYd; - bool m_wasElytraFlying; - int m_elytraFallProtectTicks; + float m_elytraImpactYd; + bool m_wasElytraFlying; + int m_elytraFallProtectTicks; + float m_elytraMaxY; + +public: + int m_fireworkBoostTicks; + int m_elytraRocketCooldown; +public: + void startFireworkBoost(shared_ptr firework); bool isElytraFlying(); virtual void setElytraFlying(bool flying); virtual void onElytraKineticDamage(float damage); + virtual void onElytraFallDamage(int amount); public: diff --git a/Minecraft.World/PlayerAbilitiesPacket.cpp b/Minecraft.World/PlayerAbilitiesPacket.cpp index d02f164c..a569d6e1 100644 --- a/Minecraft.World/PlayerAbilitiesPacket.cpp +++ b/Minecraft.World/PlayerAbilitiesPacket.cpp @@ -9,6 +9,7 @@ PlayerAbilitiesPacket::PlayerAbilitiesPacket() _isFlying = false; _canFly = false; instabuild = false; + _noPhysics = false; flyingSpeed = 0.0f; walkingSpeed = 0.0f; } @@ -19,6 +20,7 @@ PlayerAbilitiesPacket::PlayerAbilitiesPacket(Abilities *abilities) setFlying(abilities->flying); setCanFly(abilities->mayfly); setInstabuild(abilities->instabuild); + setNoPhysics(abilities->spectatorMode); setFlyingSpeed(abilities->getFlyingSpeed()); setWalkingSpeed(abilities->getWalkingSpeed()); } @@ -31,6 +33,7 @@ void PlayerAbilitiesPacket::read(DataInputStream *dis) setFlying((bitfield & FLAG_FLYING) > 0); setCanFly((bitfield & FLAG_CAN_FLY) > 0); setInstabuild((bitfield & FLAG_INSTABUILD) > 0); + setNoPhysics((bitfield & FLAG_NOPHYSICS) > 0); setFlyingSpeed(dis->readFloat()); setWalkingSpeed(dis->readFloat()); } @@ -43,6 +46,7 @@ void PlayerAbilitiesPacket::write(DataOutputStream *dos) if (isFlying()) bitfield |= FLAG_FLYING; if (canFly()) bitfield |= FLAG_CAN_FLY; if (canInstabuild()) bitfield |= FLAG_INSTABUILD; + if (isNoPhysics()) bitfield |= FLAG_NOPHYSICS; dos->writeByte(bitfield); dos->writeFloat(flyingSpeed); @@ -124,6 +128,16 @@ void PlayerAbilitiesPacket::setWalkingSpeed(float walkingSpeed) this->walkingSpeed = walkingSpeed; } +bool PlayerAbilitiesPacket::isNoPhysics() +{ + return _noPhysics; +} + +void PlayerAbilitiesPacket::setNoPhysics(bool noPhysics) +{ + _noPhysics = noPhysics; +} + bool PlayerAbilitiesPacket::canBeInvalidated() { return true; diff --git a/Minecraft.World/PlayerAbilitiesPacket.h b/Minecraft.World/PlayerAbilitiesPacket.h index 3998c66b..a706a164 100644 --- a/Minecraft.World/PlayerAbilitiesPacket.h +++ b/Minecraft.World/PlayerAbilitiesPacket.h @@ -11,11 +11,13 @@ private: static const int FLAG_FLYING = 1 << 1; static const int FLAG_CAN_FLY = 1 << 2; static const int FLAG_INSTABUILD = 1 << 3; + static const int FLAG_NOPHYSICS = 1 << 4; bool invulnerable; bool _isFlying; bool _canFly; bool instabuild; + bool _noPhysics; float flyingSpeed; float walkingSpeed; @@ -40,6 +42,8 @@ public: void setFlyingSpeed(float flySpeed); float getWalkingSpeed(); void setWalkingSpeed(float walkingSpeed); + bool isNoPhysics(); + void setNoPhysics(bool noPhysics); bool canBeInvalidated(); bool isInvalidatedBy(shared_ptr packet); diff --git a/Minecraft.World/PlayerActionPacket.cpp b/Minecraft.World/PlayerActionPacket.cpp index aaa3e37e..6fe8e162 100644 --- a/Minecraft.World/PlayerActionPacket.cpp +++ b/Minecraft.World/PlayerActionPacket.cpp @@ -10,6 +10,8 @@ const int PlayerActionPacket::STOP_DESTROY_BLOCK = 2; const int PlayerActionPacket::DROP_ALL_ITEMS = 3; const int PlayerActionPacket::DROP_ITEM = 4; const int PlayerActionPacket::RELEASE_USE_ITEM = 5; +const int PlayerActionPacket::ELYTRA_IMPACT = 6; +const int PlayerActionPacket::ELYTRA_FALL_DAMAGE = 7; PlayerActionPacket::PlayerActionPacket() { diff --git a/Minecraft.World/PlayerActionPacket.h b/Minecraft.World/PlayerActionPacket.h index 647777cd..56314d1b 100644 --- a/Minecraft.World/PlayerActionPacket.h +++ b/Minecraft.World/PlayerActionPacket.h @@ -12,6 +12,8 @@ public: static const int DROP_ALL_ITEMS; static const int DROP_ITEM; static const int RELEASE_USE_ITEM; + static const int ELYTRA_IMPACT; + static const int ELYTRA_FALL_DAMAGE; int x, y, z, face, action; diff --git a/Minecraft.World/PotionItem.cpp b/Minecraft.World/PotionItem.cpp index 58d91ce6..4c5bcdbe 100644 --- a/Minecraft.World/PotionItem.cpp +++ b/Minecraft.World/PotionItem.cpp @@ -392,7 +392,7 @@ vector > *PotionItem::getUniquePotionValues() int effectsHashCode = 1; for(auto& effect : *effects) { - effectsHashCode = 31*effectsHashCode + (effect ? 0 : effect->hashCode()); + effectsHashCode = 31*effectsHashCode + (effect ? effect->hashCode() : 0); delete effect; } diff --git a/Minecraft.World/RandomLevelSource.cpp b/Minecraft.World/RandomLevelSource.cpp index aa6ae9e0..287263a6 100644 --- a/Minecraft.World/RandomLevelSource.cpp +++ b/Minecraft.World/RandomLevelSource.cpp @@ -782,6 +782,24 @@ void RandomLevelSource::postProcess(ChunkSource *parent, int xt, int zt) } PIXEndNamedEvent(); + if (pprandom->nextInt(64) == 0) + { + int fx = xo + pprandom->nextInt(16) + 8; + int fy = 40 + pprandom->nextInt(10); + int fz = zo + pprandom->nextInt(16) + 8; + FossilFeature fossil; + fossil.place(level, pprandom, fx, fy, fz); + } + + if (pprandom->nextInt(48) == 0) + { + int ix = xo + pprandom->nextInt(16) + 8; + int iy = level->maxBuildHeight; + int iz = zo + pprandom->nextInt(16) + 8; + IglooFeature igloo; + igloo.place(level, pprandom, ix, iy, iz); + } + PIXBeginNamedEvent(0,"Biome decorate"); biome->decorate(level, pprandom, xo, zo); PIXEndNamedEvent(); diff --git a/Minecraft.World/Recipes.cpp b/Minecraft.World/Recipes.cpp index b2eb70e2..3501d1e1 100644 --- a/Minecraft.World/Recipes.cpp +++ b/Minecraft.World/Recipes.cpp @@ -517,6 +517,58 @@ void Recipes::_compileRecipes() L'#', Tile::snow, L'S'); +addShapedRecipy(new ItemInstance(Tile::red_nether_brick, 1), // + L"sscicig", + L"WB", // + L"BW", // + + L'W', Item::netherwart_seeds, L'B', new ItemInstance(Item::nether_brick_Id, 1, 0), + L'S'); + + addShapedRecipy(new ItemInstance(Tile::end_bricks, 4), // + L"ssctg", + L"##", // + L"##", // + + L'#', Tile::endStone, + L'S'); + + addShapedRecipy(new ItemInstance(Tile::nether_wart_block, 1), // + L"ssscig", + L"###", // + L"###", // + L"###", // + + L'#', Item::netherwart_seeds, + L'D'); + + addShapedRecipy(new ItemInstance((Tile*)Tile::magma, 1), // + L"sscig", + L"##", // + L"##", // + + L'#', Item::magma_cream, + L'D'); + + addShapedRecipy(new ItemInstance(Tile::slimeBlock, 1), // + L"ssscig", + L"###", // + L"###", // + L"###", // + + L'#', Item::slime_ball, + L'D'); + + addShapedRecipy(new ItemInstance(Item::slime_ball, 9), // + L"sctg", + L"#", // + + L'#', Tile::slimeBlock, + L'D'); + + + + addShapedRecipy(new ItemInstance(Tile::clay, 1), // L"sscig", L"##", // diff --git a/Minecraft.World/StructureRecipies.cpp b/Minecraft.World/StructureRecipies.cpp index 54e29580..fadbf6ce 100644 --- a/Minecraft.World/StructureRecipies.cpp +++ b/Minecraft.World/StructureRecipies.cpp @@ -1,6 +1,7 @@ #include "stdafx.h" #include "net.minecraft.world.item.h" #include "net.minecraft.world.level.tile.h" +#include "DyePowderItem.h" #include "Recipy.h" #include "Recipes.h" #include "StructureRecipies.h" @@ -16,6 +17,20 @@ void StructureRecipies::addRecipes(Recipes *r) L'S'); + r->addShapedRecipy(new ItemInstance((Tile*)Tile::bone_block, 1), // + L"sssczg", + L"###", // + L"###", // + L"###", // + + L'#', new ItemInstance(Item::dye, 1, DyePowderItem::WHITE), + L'S'); + + r->addShapelessRecipy(new ItemInstance(Item::dye, 9, DyePowderItem::WHITE), // + L"tg", + (Tile*)Tile::bone_block, + L'D'); + r->addShapedRecipy(new ItemInstance(Tile::sandStone, 4, SandStoneTile::TYPE_SMOOTHSIDE), // L"ssczg", L"##", // diff --git a/Minecraft.World/ThrownPotion.cpp b/Minecraft.World/ThrownPotion.cpp index 170f6474..1ec72efa 100644 --- a/Minecraft.World/ThrownPotion.cpp +++ b/Minecraft.World/ThrownPotion.cpp @@ -111,6 +111,7 @@ void ThrownPotion::onHit(HitResult *res) for(auto& effect : *mobEffects) { int id = effect->getId(); + if (id < 0 || id >= MobEffect::NUM_EFFECTS || !MobEffect::effects[id]) continue; if (MobEffect::effects[id]->isInstantenous()) { MobEffect::effects[id]->applyInstantenousEffect(getOwner(), e, effect->getAmplifier(), scale); diff --git a/Minecraft.World/Tile.cpp b/Minecraft.World/Tile.cpp index 0a8db3a9..63d5f8a0 100644 --- a/Minecraft.World/Tile.cpp +++ b/Minecraft.World/Tile.cpp @@ -576,7 +576,7 @@ void Tile::staticCtor() Tile::log2 = (new TreeTile2(162))->setDestroyTime(2.0f)->setSoundType(Tile::SOUND_WOOD)->setIconName(L"log")->setDescriptionId(IDS_TILE_LOG)->sendTileData()->setUseDescriptionId(IDS_DESC_LOG); Tile::woodStairsAcacia = (new StairTile(163, Tile::wood, TreeTile::ACACIA_TRUNK))->setBaseItemTypeAndMaterial(Item::eBaseItemType_stairs, Item::eMaterial_acaciawood)->setIconName(L"stairsWoodAcacia")->setDescriptionId(IDS_TILE_STAIRS_ACACIAWOOD)->sendTileData()->setUseDescriptionId(IDS_DESC_STAIRS); Tile::woodStairsDark = (new StairTile(164, Tile::wood, TreeTile::DARK_TRUNK))->setBaseItemTypeAndMaterial(Item::eBaseItemType_stairs, Item::eMaterial_darkwood)->setIconName(L"stairsWoodDark")->setDescriptionId(IDS_TILE_STAIRS_DARKWOOD)->sendTileData()->setUseDescriptionId(IDS_DESC_STAIRS); - Tile::slimeBlock = (new SlimeTile(165))->setSoundType(SOUND_SLIME)->setIconName(L"slime")->setDescriptionId(IDS_TILE_SLIME_BLOCK)->setUseDescriptionId(IDS_DESC_SLIME_BLOCK)->disableMipmap(); + Tile::slimeBlock = (new SlimeTile(165))->setBaseItemTypeAndMaterial(Item::eBaseItemType_block, Item::eMaterial_slime)->setSoundType(SOUND_SLIME)->setIconName(L"slime")->setDescriptionId(IDS_TILE_SLIME_BLOCK)->setUseDescriptionId(IDS_DESC_SLIME_BLOCK)->disableMipmap(); Tile::barrier = (new BarrierTile(166, Material::stone, false)) ->setIndestructible()->setExplodeable(6000000)->setSoundType(Tile::SOUND_STONE)->setIconName(L"barrier")->setDescriptionId(IDS_TILE_BARRIER)->setNotCollectStatistics()->setUseDescriptionId(IDS_DESC_BARRIER); Tile::iron_trapdoor = (new TrapDoorTile(167, Material::metal))->setBaseItemTypeAndMaterial(Item::eBaseItemType_door, Item::eMaterial_trap)->setDestroyTime(5.0f)->setSoundType(Tile::SOUND_METAL)->setIconName(L"iron_trapdoor")->setDescriptionId(IDS_TILE_IRON_TRAPDOOR)->setNotCollectStatistics()->sendTileData()->setUseDescriptionId(IDS_DESC_TRAPDOOR); diff --git a/Minecraft.World/TileEntity.cpp b/Minecraft.World/TileEntity.cpp index 4efc6731..58cb02fe 100644 --- a/Minecraft.World/TileEntity.cpp +++ b/Minecraft.World/TileEntity.cpp @@ -33,6 +33,7 @@ void TileEntity::staticCtor() TileEntity::setId(HopperTileEntity::create, eTYPE_HOPPERTILEENTITY, L"Hopper"); TileEntity::setId(ComparatorTileEntity::create, eTYPE_COMPARATORTILEENTITY, L"Comparator"); TileEntity::setId(BannerTileEntity::create, eTYPE_BANNERTILEENTITY, L"Banner"); + TileEntity::setId(FlowerPotTileEntity::create, eTYPE_FLOWERPOTTILEENTITY, L"FlowerPot"); } void TileEntity::setId(tileEntityCreateFn createFn, eINSTANCEOF clas, wstring id) diff --git a/Minecraft.World/TileEntityDataPacket.h b/Minecraft.World/TileEntityDataPacket.h index b6f6f043..284818e2 100644 --- a/Minecraft.World/TileEntityDataPacket.h +++ b/Minecraft.World/TileEntityDataPacket.h @@ -13,6 +13,7 @@ public: static const int TYPE_BEACON = 3; static const int TYPE_SKULL = 4; static const int TYPE_BANNER = 5; + static const int TYPE_FLOWER_POT = 6; int x, y, z; int type; diff --git a/Minecraft.World/VillageFeature.cpp b/Minecraft.World/VillageFeature.cpp index bde8a66c..8e890319 100644 --- a/Minecraft.World/VillageFeature.cpp +++ b/Minecraft.World/VillageFeature.cpp @@ -14,6 +14,10 @@ void VillageFeature::staticCtor() { allowedBiomes.push_back( Biome::plains ); allowedBiomes.push_back( Biome::desert ); + allowedBiomes.push_back( Biome::savanna ); + allowedBiomes.push_back( Biome::savannaPlateau ); + allowedBiomes.push_back( Biome::savannaM ); + allowedBiomes.push_back( Biome::savannaPlateauM ); } void VillageFeature::_init(int iXZSize) diff --git a/Minecraft.World/VillagePieces.cpp b/Minecraft.World/VillagePieces.cpp index 23713c98..ece08934 100644 --- a/Minecraft.World/VillagePieces.cpp +++ b/Minecraft.World/VillagePieces.cpp @@ -268,6 +268,7 @@ VillagePieces::VillagePiece::VillagePiece() heightPosition = -1; spawnedVillagerCount = 0; isDesertVillage = false; + isAcaciaVillage = false; startPiece = nullptr; // for reflection } @@ -276,11 +277,13 @@ VillagePieces::VillagePiece::VillagePiece(StartPiece *startPiece, int genDepth) { heightPosition = -1; isDesertVillage = false; + isAcaciaVillage = false; spawnedVillagerCount = 0; this->startPiece = startPiece; if (startPiece != nullptr) { this->isDesertVillage = startPiece->isDesertVillage; + this->isAcaciaVillage = startPiece->isAcaciaVillage; } } @@ -289,6 +292,7 @@ void VillagePieces::VillagePiece::addAdditonalSaveData(CompoundTag *tag) tag->putInt(L"HPos", heightPosition); tag->putInt(L"VCount", spawnedVillagerCount); tag->putBoolean(L"Desert", isDesertVillage); + tag->putBoolean(L"Acacia", isAcaciaVillage); } void VillagePieces::VillagePiece::readAdditonalSaveData(CompoundTag *tag) @@ -296,6 +300,7 @@ void VillagePieces::VillagePiece::readAdditonalSaveData(CompoundTag *tag) heightPosition = tag->getInt(L"HPos"); spawnedVillagerCount = tag->getInt(L"VCount"); isDesertVillage = tag->getBoolean(L"Desert"); + isAcaciaVillage = tag->getBoolean(L"Acacia"); } StructurePiece *VillagePieces::VillagePiece::generateHouseNorthernLeft(StartPiece *startPiece, list *pieces, Random *random, int yOff, int zOff) @@ -413,29 +418,30 @@ int VillagePieces::VillagePiece::biomeBlock(int tile, int data) if (isDesertVillage) { if (tile == Tile::log_Id) - { return Tile::sandstone_Id; - } else if (tile == Tile::cobblestone_Id) - { return Tile::sandstone_Id; - } else if (tile == Tile::planks_Id) - { return Tile::sandstone_Id; - } else if (tile == Tile::oak_stairs_Id) - { return Tile::sandstone_stairs_Id; - } else if (tile == Tile::stone_stairs_Id) - { return Tile::sandstone_stairs_Id; - } else if (tile == Tile::gravel_Id) - { return Tile::sandstone_Id; - } + } + else if (isAcaciaVillage) + { + if (tile == Tile::log_Id) + return Tile::log2_Id; + else if (tile == Tile::planks_Id) + return Tile::planks_Id; + else if (tile == Tile::oak_stairs_Id) + return Tile::acacia_stairs_Id; + else if (tile == Tile::fence_Id) + return Tile::acacia_fence_Id; + else if (tile == Tile::fence_gate_Id) + return Tile::acacia_fence_gate_Id; } return tile; } @@ -445,17 +451,18 @@ int VillagePieces::VillagePiece::biomeData(int tile, int data) if (isDesertVillage) { if (tile == Tile::log_Id) - { return 0; - } else if (tile == Tile::cobblestone_Id) - { return SandStoneTile::TYPE_DEFAULT; - } else if (tile == Tile::planks_Id) - { return SandStoneTile::TYPE_SMOOTHSIDE; - } + } + else if (isAcaciaVillage) + { + if (tile == Tile::log_Id) + return (data & 0xC) | 0; + else if (tile == Tile::planks_Id) + return 4; } return data; } @@ -580,6 +587,8 @@ VillagePieces::StartPiece::StartPiece(BiomeSource *biomeSource, int genDepth, Ra Biome *biome = biomeSource->getBiome(west, north); isDesertVillage = biome == Biome::desert || biome == Biome::desertHills; + isAcaciaVillage = biome == Biome::savanna || biome == Biome::savannaPlateau || + biome == Biome::savannaM || biome == Biome::savannaPlateauM; } VillagePieces::StartPiece::~StartPiece() @@ -718,8 +727,21 @@ bool VillagePieces::StraightRoad::postProcess(Level *level, Random *random, Boun if (chunkBB->isInside(x, 64, z)) { int y = level->getTopSolidBlock(x, z) - 1; - level->setTileAndData(x, y, z, roadTile, 0, Tile::UPDATE_CLIENTS); - // level->setTileAndData(x, y - 1, z, baseTile, 0, Tile::UPDATE_CLIENTS); + int tileAtY = level->getTile(x, y, z); + if (tileAtY == Tile::water_Id || tileAtY == Tile::flowing_water_Id) + { + level->setTileAndData(x, y, z, Tile::planks_Id, 0, Tile::UPDATE_CLIENTS); + int fill = y - 1; + while (fill >= 0 && (level->getTile(x, fill, z) == Tile::water_Id || level->getTile(x, fill, z) == Tile::flowing_water_Id)) + { + level->setTileAndData(x, fill, z, Tile::planks_Id, 0, Tile::UPDATE_CLIENTS); + fill--; + } + } + else + { + level->setTileAndData(x, y, z, roadTile, 0, Tile::UPDATE_CLIENTS); + } } } } diff --git a/Minecraft.World/VillagePieces.h b/Minecraft.World/VillagePieces.h index 7570d65b..6630dda7 100644 --- a/Minecraft.World/VillagePieces.h +++ b/Minecraft.World/VillagePieces.h @@ -70,6 +70,7 @@ private: private: int spawnedVillagerCount; bool isDesertVillage; + bool isAcaciaVillage; protected: StartPiece *startPiece; @@ -124,6 +125,7 @@ public: // these fields are only used in generation step and aren't serialized :{ BiomeSource *biomeSource; bool isDesertVillage; + bool isAcaciaVillage; int villageSize; bool isLibraryAdded; diff --git a/Minecraft.World/cmake/sources/Common.cmake b/Minecraft.World/cmake/sources/Common.cmake index 361c68e3..18c109e8 100644 --- a/Minecraft.World/cmake/sources/Common.cmake +++ b/Minecraft.World/cmake/sources/Common.cmake @@ -1500,6 +1500,10 @@ set(_MINECRAFT_WORLD_COMMON_NET_MINECRAFT_WORLD_LEVEL_LEVELGEN_FEATURE "${CMAKE_CURRENT_SOURCE_DIR}/EndPodiumFeature.h" "${CMAKE_CURRENT_SOURCE_DIR}/Feature.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/Feature.h" + "${CMAKE_CURRENT_SOURCE_DIR}/FossilFeature.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/FossilFeature.h" + "${CMAKE_CURRENT_SOURCE_DIR}/IglooFeature.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/IglooFeature.h" "${CMAKE_CURRENT_SOURCE_DIR}/FlowerFeature.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/FlowerFeature.h" "${CMAKE_CURRENT_SOURCE_DIR}/GroundBushFeature.cpp" @@ -1866,6 +1870,8 @@ set(_MINECRAFT_WORLD_COMMON_NET_MINECRAFT_WORLD_LEVEL_TILE "${CMAKE_CURRENT_SOURCE_DIR}/FireTile.h" "${CMAKE_CURRENT_SOURCE_DIR}/FlowerPotTile.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/FlowerPotTile.h" + "${CMAKE_CURRENT_SOURCE_DIR}/FlowerPotTileEntity.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/FlowerPotTileEntity.h" "${CMAKE_CURRENT_SOURCE_DIR}/FurnaceTile.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/FurnaceTile.h" "${CMAKE_CURRENT_SOURCE_DIR}/GlassTile.cpp" diff --git a/Minecraft.World/net.minecraft.world.level.levelgen.feature.h b/Minecraft.World/net.minecraft.world.level.levelgen.feature.h index dc780682..2f61e80e 100644 --- a/Minecraft.World/net.minecraft.world.level.levelgen.feature.h +++ b/Minecraft.World/net.minecraft.world.level.levelgen.feature.h @@ -36,6 +36,8 @@ #include "RoofTreeFeature.h" #include "DesertWellFeature.h" +#include "FossilFeature.h" +#include "IglooFeature.h" #include "MegaTreeFeature.h" #include "VinesFeature.h" #include "GroundBushFeature.h" diff --git a/Minecraft.World/net.minecraft.world.level.tile.entity.h b/Minecraft.World/net.minecraft.world.level.tile.entity.h index 13e66943..8d62567c 100644 --- a/Minecraft.World/net.minecraft.world.level.tile.entity.h +++ b/Minecraft.World/net.minecraft.world.level.tile.entity.h @@ -22,3 +22,4 @@ #include "EnderChestTileEntity.h" #include "ItemFrame.h" #include "BannerTileEntity.h" +#include "FlowerPotTileEntity.h"