2026-03-01 12:16:08 +08:00
# include "stdafx.h"
# include "LocalPlayer.h"
# include "User.h"
# include "Input.h"
# include "StatsCounter.h"
# include "ParticleEngine.h"
# include "TakeAnimationParticle.h"
# include "Options.h"
# include "TextEditScreen.h"
# include "ContainerScreen.h"
# include "CraftingScreen.h"
# include "FurnaceScreen.h"
# include "TrapScreen.h"
# include "MultiPlayerLocalPlayer.h"
# include "CreativeMode.h"
# include "GameRenderer.h"
# include "ItemInHandRenderer.h"
# include "..\Minecraft.World\LevelData.h"
# include "..\Minecraft.World\net.minecraft.world.damagesource.h"
# include "..\Minecraft.World\net.minecraft.world.item.h"
# include "..\Minecraft.World\net.minecraft.world.food.h"
# include "..\Minecraft.World\net.minecraft.world.effect.h"
# include "..\Minecraft.World\net.minecraft.world.entity.player.h"
# include "..\Minecraft.World\ItemEntity.h"
# include "..\Minecraft.World\net.minecraft.world.level.h"
# include "..\Minecraft.World\net.minecraft.world.phys.h"
# include "..\Minecraft.World\net.minecraft.stats.h"
# include "..\Minecraft.World\com.mojang.nbt.h"
# include "..\Minecraft.World\Random.h"
# include "..\Minecraft.World\Mth.h"
# include "AchievementPopup.h"
# include "CritParticle.h"
// 4J : WESTY : Added for new achievements.
# include "..\Minecraft.World\item.h"
# include "..\Minecraft.World\mapitem.h"
# include "..\Minecraft.World\tile.h"
// 4J Stu - Added for tutorial callbacks
# include "Minecraft.h"
# include "..\Minecraft.World\Minecart.h"
# include "..\Minecraft.World\Boat.h"
# include "..\Minecraft.World\Pig.h"
# include "..\Minecraft.World\StringHelpers.h"
# include "Options.h"
# include "..\Minecraft.World\Dimension.h"
# ifndef _DURANGO
# include "..\Minecraft.World\CommonStats.h"
# endif
LocalPlayer : : LocalPlayer ( Minecraft * minecraft , Level * level , User * user , int dimension ) : Player ( level )
{
flyX = flyY = flyZ = 0.0f ; // 4J added
m_awardedThisSession = 0 ;
sprintTriggerTime = 0 ;
sprintTriggerRegisteredReturn = false ;
twoJumpsRegistered = false ;
sprintTime = 0 ;
m_uiInactiveTicks = 0 ;
yBob = xBob = yBobO = xBobO = 0.0f ;
this - > minecraft = minecraft ;
this - > dimension = dimension ;
if ( user ! = NULL & & user - > name . length ( ) > 0 )
{
customTextureUrl = L " http://s3.amazonaws.com/MinecraftSkins/ " + user - > name + L " .png " ;
}
if ( user ! = NULL )
{
this - > name = user - > name ;
m_UUID = name ;
//wprintf(L"Created LocalPlayer with name %ls\n", name.c_str() );
// check to see if this player's xuid is in the list of special players
MOJANG_DATA * pMojangData = app . GetMojangDataForXuid ( getOnlineXuid ( ) ) ;
if ( pMojangData )
{
customTextureUrl = pMojangData - > wchSkin ;
}
}
input = NULL ;
m_iPad = - 1 ;
m_iScreenSection = C4JRender : : VIEWPORT_TYPE_FULLSCREEN ; // assume singleplayer default
m_bPlayerRespawned = false ;
ullButtonsPressed = 0LL ;
ullDpad_last = ullDpad_this = ullDpad_filtered = 0 ;
// 4J-PB - moved in from the minecraft structure
//ticks=0;
missTime = 0 ;
lastClickTick [ 0 ] = 0 ;
lastClickTick [ 1 ] = 0 ;
isRaining = false ;
m_bIsIdle = false ;
m_iThirdPersonView = 0 ;
// 4J Stu - Added for telemetry
SetSessionTimerStart ( ) ;
// 4J - added for auto repeat in creative mode
lastClickState = lastClick_invalid ;
lastClickTolerance = 0.0f ;
m_bHasAwardedStayinFrosty = false ;
}
LocalPlayer : : ~ LocalPlayer ( )
{
if ( this - > input ! = NULL )
delete input ;
}
// 4J - added noEntityCubes parameter
void LocalPlayer : : move ( double xa , double ya , double za , bool noEntityCubes )
{
if ( ClientConstants : : DEADMAU5_CAMERA_CHEATS )
{
if ( shared_from_this ( ) = = minecraft - > player & & minecraft - > options - > isFlying )
{
noPhysics = true ;
float tmp = walkDist ; // update
calculateFlight ( ( float ) xa , ( float ) ya , ( float ) za ) ;
fallDistance = 0.0f ;
yd = 0.0f ;
Player : : move ( flyX , flyY , flyZ , noEntityCubes ) ;
onGround = true ;
walkDist = tmp ;
}
else
{
noPhysics = false ;
Player : : move ( xa , ya , za , noEntityCubes ) ;
}
}
else
{
Player : : move ( xa , ya , za , noEntityCubes ) ;
}
}
void LocalPlayer : : calculateFlight ( float xa , float ya , float za )
{
xa = xa * minecraft - > options - > flySpeed ;
ya = 0 ;
za = za * minecraft - > options - > flySpeed ;
flyX = smoothFlyX . getNewDeltaValue ( xa , .35f * minecraft - > options - > sensitivity ) ;
flyY = smoothFlyY . getNewDeltaValue ( ya , .35f * minecraft - > options - > sensitivity ) ;
flyZ = smoothFlyZ . getNewDeltaValue ( za , .35f * minecraft - > options - > sensitivity ) ;
}
void LocalPlayer : : serverAiStep ( )
{
Player : : serverAiStep ( ) ;
2026-03-02 15:58:20 +07:00
2026-03-01 12:16:08 +08:00
if ( abilities . flying & & abilities . mayfly )
{
// snap y rotation for flying to nearest 90 degrees in world space
float fMag = sqrtf ( input - > xa * input - > xa + input - > ya * input - > ya ) ;
// Don't bother for tiny inputs
if ( fMag > = 0.1f )
{
// Get angle (in player rotated space) of input controls
float yRotInput = atan2f ( input - > ya , input - > xa ) * ( 180.0f / PI ) ;
// Now get in world space
float yRotFinal = yRotInput + yRot ;
// Snap this to nearest 90 degrees
float yRotSnapped = floorf ( ( yRotFinal / 45.0f ) + 0.5f ) * 45.0f ;
// Find out how much we had to move to do this snap
float yRotDiff = yRotSnapped - yRotFinal ;
// Apply the same difference to the player rotated space angle
float yRotInputAdjust = yRotInput + yRotDiff ;
2026-03-02 15:58:20 +07:00
2026-03-01 12:16:08 +08:00
// Calculate final x/y player-space movement required
this - > xxa = cos ( yRotInputAdjust * ( PI / 180.0f ) ) * fMag ;
this - > yya = sin ( yRotInputAdjust * ( PI / 180.0f ) ) * fMag ;
}
else
{
this - > xxa = input - > xa ;
this - > yya = input - > ya ;
}
}
else
{
this - > xxa = input - > xa ;
this - > yya = input - > ya ;
}
this - > jumping = input - > jumping ;
yBobO = yBob ;
xBobO = xBob ;
xBob + = ( xRot - xBob ) * 0.5 ;
yBob + = ( yRot - yBob ) * 0.5 ;
// TODO 4J - Remove
//if (input->jumping)
// mapPlayerChunk(8);
}
bool LocalPlayer : : isEffectiveAI ( )
{
return true ;
}
void LocalPlayer : : aiStep ( )
{
if ( sprintTime > 0 )
{
sprintTime - - ;
if ( sprintTime = = 0 )
{
setSprinting ( false ) ;
}
}
if ( sprintTriggerTime > 0 ) sprintTriggerTime - - ;
if ( minecraft - > gameMode - > isCutScene ( ) )
{
x = z = 0.5 ;
x = 0 ;
z = 0 ;
yRot = tickCount / 12.0f ;
xRot = 10 ;
y = 68.5 ;
return ;
}
oPortalTime = portalTime ;
if ( isInsidePortal )
{
if ( ! level - > isClientSide )
{
if ( riding ! = NULL ) this - > ride ( nullptr ) ;
}
if ( minecraft - > screen ! = NULL ) minecraft - > setScreen ( NULL ) ;
if ( portalTime = = 0 )
{
minecraft - > soundEngine - > playUI ( eSoundType_PORTAL_TRIGGER , 1 , random - > nextFloat ( ) * 0.4f + 0.8f ) ;
}
portalTime + = 1 / 80.0f ;
if ( portalTime > = 1 )
{
portalTime = 1 ;
}
isInsidePortal = false ;
}
else if ( hasEffect ( MobEffect : : confusion ) & & getEffect ( MobEffect : : confusion ) - > getDuration ( ) > ( SharedConstants : : TICKS_PER_SECOND * 3 ) )
{
portalTime + = 1 / 150.0f ;
if ( portalTime > 1 )
{
portalTime = 1 ;
}
}
else
{
if ( portalTime > 0 ) portalTime - = 1 / 20.0f ;
if ( portalTime < 0 ) portalTime = 0 ;
}
if ( changingDimensionDelay > 0 ) changingDimensionDelay - - ;
bool wasJumping = input - > jumping ;
float runTreshold = 0.8f ;
2026-03-02 01:21:56 +08:00
float sprintForward = input - > sprintForward ;
2026-03-01 12:16:08 +08:00
2026-03-02 01:21:56 +08:00
bool wasRunning = sprintForward > = runTreshold ;
2026-03-02 17:10:34 +07:00
//input->tick( std::dynamic_pointer_cast<Player>( shared_from_this() ) );
2026-03-01 12:16:08 +08:00
// 4J-PB - make it a localplayer
input - > tick ( this ) ;
2026-03-02 01:21:56 +08:00
sprintForward = input - > sprintForward ;
2026-03-01 12:16:08 +08:00
if ( isUsingItem ( ) )
{
input - > xa * = 0.2f ;
input - > ya * = 0.2f ;
sprintTriggerTime = 0 ;
}
// this.heightOffset = input.sneaking?1.30f:1.62f; // 4J - this was already commented out
if ( input - > sneaking ) // 4J - removed - TODO replace
{
if ( ySlideOffset < 0.2f ) ySlideOffset = 0.2f ;
}
checkInTile ( x - bbWidth * 0.35 , bb - > y0 + 0.5 , z + bbWidth * 0.35 ) ;
checkInTile ( x - bbWidth * 0.35 , bb - > y0 + 0.5 , z - bbWidth * 0.35 ) ;
checkInTile ( x + bbWidth * 0.35 , bb - > y0 + 0.5 , z - bbWidth * 0.35 ) ;
checkInTile ( x + bbWidth * 0.35 , bb - > y0 + 0.5 , z + bbWidth * 0.35 ) ;
bool enoughFoodToSprint = getFoodData ( ) - > getFoodLevel ( ) > FoodConstants : : MAX_FOOD * FoodConstants : : FOOD_SATURATION_LOW ;
// 4J Stu - If we can fly, then we should be able to sprint without requiring food. This is particularly a problem for people who save a survival
// world with low food, then reload it in creative.
if ( abilities . mayfly | | isAllowedToFly ( ) ) enoughFoodToSprint = true ;
2026-03-02 01:21:56 +08:00
bool forwardEnoughToTriggerSprint = sprintForward > = runTreshold ;
bool forwardReturnedToDeadzone = sprintForward = = 0.0f ;
bool forwardEnoughToContinueSprint = sprintForward > = runTreshold ;
# ifdef _WINDOWS64
if ( GetXboxPad ( ) = = 0 & & input - > usingKeyboardMovement )
{
forwardEnoughToContinueSprint = sprintForward > 0.0f ;
}
# endif
2026-03-01 18:50:55 +08:00
# ifdef _WINDOWS64
// Keyboard sprint: Ctrl held while moving forward
2026-03-02 01:21:56 +08:00
if ( GetXboxPad ( ) = = 0 & & input - > usingKeyboardMovement & & KMInput . IsKeyDown ( VK_CONTROL ) & & sprintForward > 0.0f & &
2026-03-01 18:50:55 +08:00
enoughFoodToSprint & & ! isUsingItem ( ) & & ! hasEffect ( MobEffect : : blindness ) & & onGround )
{
if ( ! isSprinting ( ) ) setSprinting ( true ) ;
}
# endif
2026-03-01 12:16:08 +08:00
// 4J - altered this slightly to make sure that the joypad returns to below returnTreshold in between registering two movements up to runThreshold
if ( onGround & & ! isSprinting ( ) & & enoughFoodToSprint & & ! isUsingItem ( ) & & ! hasEffect ( MobEffect : : blindness ) )
{
2026-03-02 01:21:56 +08:00
if ( ! wasRunning & & forwardEnoughToTriggerSprint )
2026-03-01 12:16:08 +08:00
{
if ( sprintTriggerTime = = 0 )
{
sprintTriggerTime = 7 ;
sprintTriggerRegisteredReturn = false ;
}
else
{
if ( sprintTriggerRegisteredReturn )
{
setSprinting ( true ) ;
sprintTriggerTime = 0 ;
sprintTriggerRegisteredReturn = false ;
}
}
}
2026-03-02 01:21:56 +08:00
else if ( ( sprintTriggerTime > 0 ) & & forwardReturnedToDeadzone ) // zero sprintForward here signifies that we have returned to the deadzone
2026-03-01 12:16:08 +08:00
{
sprintTriggerRegisteredReturn = true ;
}
}
if ( isSneaking ( ) ) sprintTriggerTime = 0 ;
// 4J-PB - try not stopping sprint on collision
//if (isSprinting() && (input->ya < runTreshold || horizontalCollision || !enoughFoodToSprint))
2026-03-02 01:21:56 +08:00
if ( isSprinting ( ) & & ( ! forwardEnoughToContinueSprint | | ! enoughFoodToSprint | | isSneaking ( ) | | isUsingItem ( ) ) )
2026-03-01 12:16:08 +08:00
{
setSprinting ( false ) ;
2026-03-02 15:58:20 +07:00
}
2026-03-01 12:16:08 +08:00
// 4J Stu - Fix for #52705 - Customer Encountered: Player can fly in bed while being in Creative mode.
if ( ! isSleeping ( ) & & ( abilities . mayfly | | isAllowedToFly ( ) ) )
{
// 4J altered to require jump button to released after being tapped twice to trigger move between flying / not flying
if ( ! wasJumping & & input - > jumping )
{
if ( jumpTriggerTime = = 0 )
{
jumpTriggerTime = 10 ; // was 7
twoJumpsRegistered = false ;
}
else
{
twoJumpsRegistered = true ;
}
}
else if ( ( ! input - > jumping ) & & ( jumpTriggerTime > 0 ) & & twoJumpsRegistered )
{
# ifndef _CONTENT_PACKAGE
printf ( " flying was %s \n " , abilities . flying ? " on " : " off " ) ;
# endif
abilities . flying = ! abilities . flying ;
# ifndef _CONTENT_PACKAGE
printf ( " flying is %s \n " , abilities . flying ? " on " : " off " ) ;
# endif
jumpTriggerTime = 0 ;
twoJumpsRegistered = false ;
if ( abilities . flying ) input - > sneaking = false ; // 4J added - would we ever intentially want to go into flying mode whilst sneaking?
}
}
else if ( abilities . flying )
{
# ifdef _DEBUG_MENUS_ENABLED
if ( ! abilities . debugflying )
# endif
{
abilities . flying = false ;
}
}
2026-03-02 15:58:20 +07:00
2026-03-01 12:16:08 +08:00
if ( abilities . flying )
{
// yd = 0;
// 4J - note that the 0.42 added for going down is to make it match with what happens when you jump - jumping itself adds 0.42 to yd in Mob::jumpFromGround
if ( ullButtonsPressed & ( 1LL < < MINECRAFT_ACTION_SNEAK_TOGGLE ) ) yd - = ( 0.15 + 0.42 ) ; // 4J - for flying mode, MINECRAFT_ACTION_SNEAK_TOGGLE isn't a toggle but just indicates that this button is down
if ( input - > jumping )
{
noJumpDelay = 0 ;
yd + = 0.15 ;
}
// snap y rotation to nearest 90 degree axis aligned value
float yRotSnapped = floorf ( ( yRot / 90.0f ) + 0.5f ) * 90.0f ;
if ( InputManager . GetJoypadMapVal ( m_iPad ) = = 0 )
{
if ( ullDpad_filtered & ( 1LL < < MINECRAFT_ACTION_DPAD_RIGHT ) )
{
xd = - 0.15 * cos ( yRotSnapped * PI / 180 ) ;
zd = - 0.15 * sin ( yRotSnapped * PI / 180 ) ;
}
else if ( ullDpad_filtered & ( 1LL < < MINECRAFT_ACTION_DPAD_LEFT ) )
{
xd = 0.15 * cos ( yRotSnapped * PI / 180 ) ;
zd = 0.15 * sin ( yRotSnapped * PI / 180 ) ;
}
}
}
Player : : aiStep ( ) ;
// 4J-PB - If we're in Creative Mode, allow flying on ground
if ( ! abilities . mayfly & & ! isAllowedToFly ( ) )
{
if ( onGround & & abilities . flying )
{
# ifdef _DEBUG_MENUS_ENABLED
if ( ! abilities . debugflying )
# endif
{
abilities . flying = false ;
}
}
}
if ( abilities . flying ) //minecraft->options->isFlying )
{
Vec3 * viewVector = getViewVector ( 1.0f ) ;
// 4J-PB - To let the player build easily while flying, we need to change this
# ifdef _DEBUG_MENUS_ENABLED
if ( abilities . debugflying )
{
flyX = ( float ) viewVector - > x * input - > ya ;
flyY = ( float ) viewVector - > y * input - > ya ;
flyZ = ( float ) viewVector - > z * input - > ya ;
}
else
# endif
{
if ( isSprinting ( ) )
{
// Accelrate up to full speed if we are sprinting, moving in the direction of the view vector
flyX = ( float ) viewVector - > x * input - > ya ;
flyY = ( float ) viewVector - > y * input - > ya ;
flyZ = ( float ) viewVector - > z * input - > ya ;
float scale = ( ( float ) ( SPRINT_DURATION - sprintTime ) ) / 10.0f ;
scale = scale * scale ;
if ( scale > 1.0f ) scale = 1.0f ;
flyX * = scale ;
flyY * = scale ;
flyZ * = scale ;
}
else
{
flyX = 0.0f ;
flyY = 0.0f ;
flyZ = 0.0f ;
if ( ullDpad_filtered & ( 1LL < < MINECRAFT_ACTION_DPAD_UP ) )
{
flyY = 0.1f ;
}
if ( ullDpad_filtered & ( 1LL < < MINECRAFT_ACTION_DPAD_DOWN ) )
{
flyY = - 0.1f ;
}
}
}
Player : : move ( flyX , flyY , flyZ ) ;
fallDistance = 0.0f ;
yd = 0.0f ;
onGround = true ;
}
// Check if the player is idle and the rich presence needs updated
if ( ! m_bIsIdle & & InputManager . GetIdleSeconds ( m_iPad ) > PLAYER_IDLE_TIME )
{
ProfileManager . SetCurrentGameActivity ( m_iPad , CONTEXT_PRESENCE_IDLE , false ) ;
m_bIsIdle = true ;
}
else if ( m_bIsIdle & & InputManager . GetIdleSeconds ( m_iPad ) < PLAYER_IDLE_TIME )
{
// Are we offline or online, and how many players are there
if ( g_NetworkManager . GetPlayerCount ( ) > 1 )
{
// only do it for this player here - each player will run this code
if ( g_NetworkManager . IsLocalGame ( ) )
{
ProfileManager . SetCurrentGameActivity ( m_iPad , CONTEXT_PRESENCE_MULTIPLAYEROFFLINE , false ) ;
}
else
{
ProfileManager . SetCurrentGameActivity ( m_iPad , CONTEXT_PRESENCE_MULTIPLAYER , false ) ;
2026-03-02 15:58:20 +07:00
}
2026-03-01 12:16:08 +08:00
}
else
{
if ( g_NetworkManager . IsLocalGame ( ) )
{
ProfileManager . SetCurrentGameActivity ( m_iPad , CONTEXT_PRESENCE_MULTIPLAYER_1POFFLINE , false ) ;
}
else
{
ProfileManager . SetCurrentGameActivity ( m_iPad , CONTEXT_PRESENCE_MULTIPLAYER_1P , false ) ;
}
}
updateRichPresence ( ) ;
m_bIsIdle = false ;
}
}
void LocalPlayer : : changeDimension ( int i )
{
if ( ! level - > isClientSide )
{
if ( dimension = = 1 & & i = = 1 )
{
awardStat ( GenericStats : : winGame ( ) , GenericStats : : param_noArgs ( ) ) ;
//minecraft.setScreen(new WinScreen());
# ifndef _CONTENT_PACKAGE
app . DebugPrintf ( " LocalPlayer::changeDimension from 1 to 1 but WinScreen has not been implemented. \n " ) ;
__debugbreak ( ) ;
# endif
}
else
{
awardStat ( GenericStats : : theEnd ( ) , GenericStats : : param_theEnd ( ) ) ;
minecraft - > soundEngine - > playUI ( eSoundType_PORTAL_TRAVEL , 1 , random - > nextFloat ( ) * 0.4f + 0.8f ) ;
}
}
}
float LocalPlayer : : getFieldOfViewModifier ( )
{
float targetFov = 1.0f ;
// modify for movement
if ( abilities . flying ) targetFov * = 1.1f ;
targetFov * = ( ( walkingSpeed * getWalkingSpeedModifier ( ) ) / defaultWalkSpeed + 1 ) / 2 ;
// modify for bow =)
if ( isUsingItem ( ) & & getUseItem ( ) - > id = = Item : : bow - > id )
{
int ticksHeld = getTicksUsingItem ( ) ;
float scale = ( float ) ticksHeld / BowItem : : MAX_DRAW_DURATION ;
if ( scale > 1 )
{
scale = 1 ;
}
else
{
scale * = scale ;
}
targetFov * = 1.0f - scale * .15f ;
}
return targetFov ;
}
void LocalPlayer : : addAdditonalSaveData ( CompoundTag * entityTag )
{
Player : : addAdditonalSaveData ( entityTag ) ;
entityTag - > putInt ( L " Score " , score ) ;
}
void LocalPlayer : : readAdditionalSaveData ( CompoundTag * entityTag )
{
Player : : readAdditionalSaveData ( entityTag ) ;
score = entityTag - > getInt ( L " Score " ) ;
}
void LocalPlayer : : closeContainer ( )
{
Player : : closeContainer ( ) ;
minecraft - > setScreen ( NULL ) ;
// 4J - Close any xui here
// Fix for #9164 - CRASH: MP: Title crashes upon opening a chest and having another user destroy it.
ui . PlayUISFX ( eSFX_Back ) ;
ui . CloseUIScenes ( m_iPad ) ;
}
2026-03-02 15:58:20 +07:00
void LocalPlayer : : openTextEdit ( std : : shared_ptr < SignTileEntity > sign )
2026-03-01 12:16:08 +08:00
{
bool success = app . LoadSignEntryMenu ( GetXboxPad ( ) , sign ) ;
if ( success ) ui . PlayUISFX ( eSFX_Press ) ;
//minecraft->setScreen(new TextEditScreen(sign));
}
2026-03-02 15:58:20 +07:00
bool LocalPlayer : : openContainer ( std : : shared_ptr < Container > container )
2026-03-01 12:16:08 +08:00
{
bool success = app . LoadContainerMenu ( GetXboxPad ( ) , inventory , container ) ;
if ( success ) ui . PlayUISFX ( eSFX_Press ) ;
//minecraft->setScreen(new ContainerScreen(inventory, container));
return success ;
}
bool LocalPlayer : : startCrafting ( int x , int y , int z )
{
2026-03-02 17:10:34 +07:00
bool success = app . LoadCrafting3x3Menu ( GetXboxPad ( ) , std : : dynamic_pointer_cast < LocalPlayer > ( shared_from_this ( ) ) , x , y , z ) ;
2026-03-01 12:16:08 +08:00
if ( success ) ui . PlayUISFX ( eSFX_Press ) ;
//app.LoadXuiCraftMenu(0,inventory, level, x, y, z);
//minecraft->setScreen(new CraftingScreen(inventory, level, x, y, z));
return success ;
}
bool LocalPlayer : : startEnchanting ( int x , int y , int z )
{
bool success = app . LoadEnchantingMenu ( GetXboxPad ( ) , inventory , x , y , z , level ) ;
if ( success ) ui . PlayUISFX ( eSFX_Press ) ;
//minecraft.setScreen(new EnchantmentScreen(inventory, level, x, y, z));
return success ;
}
bool LocalPlayer : : startRepairing ( int x , int y , int z )
{
bool success = app . LoadRepairingMenu ( GetXboxPad ( ) , inventory , level , x , y , z ) ;
if ( success ) ui . PlayUISFX ( eSFX_Press ) ;
//minecraft.setScreen(new RepairScreen(inventory, level, x, y, z));
return success ;
}
2026-03-02 15:58:20 +07:00
bool LocalPlayer : : openFurnace ( std : : shared_ptr < FurnaceTileEntity > furnace )
2026-03-01 12:16:08 +08:00
{
bool success = app . LoadFurnaceMenu ( GetXboxPad ( ) , inventory , furnace ) ;
if ( success ) ui . PlayUISFX ( eSFX_Press ) ;
//minecraft->setScreen(new FurnaceScreen(inventory, furnace));
return success ;
}
2026-03-02 15:58:20 +07:00
bool LocalPlayer : : openBrewingStand ( std : : shared_ptr < BrewingStandTileEntity > brewingStand )
2026-03-01 12:16:08 +08:00
{
bool success = app . LoadBrewingStandMenu ( GetXboxPad ( ) , inventory , brewingStand ) ;
if ( success ) ui . PlayUISFX ( eSFX_Press ) ;
//minecraft.setScreen(new BrewingStandScreen(inventory, brewingStand));
return success ;
}
2026-03-02 15:58:20 +07:00
bool LocalPlayer : : openTrap ( std : : shared_ptr < DispenserTileEntity > trap )
2026-03-01 12:16:08 +08:00
{
bool success = app . LoadTrapMenu ( GetXboxPad ( ) , inventory , trap ) ;
if ( success ) ui . PlayUISFX ( eSFX_Press ) ;
//minecraft->setScreen(new TrapScreen(inventory, trap));
return success ;
}
2026-03-02 15:58:20 +07:00
bool LocalPlayer : : openTrading ( std : : shared_ptr < Merchant > traderTarget )
2026-03-01 12:16:08 +08:00
{
bool success = app . LoadTradingMenu ( GetXboxPad ( ) , inventory , traderTarget , level ) ;
if ( success ) ui . PlayUISFX ( eSFX_Press ) ;
//minecraft.setScreen(new MerchantScreen(inventory, traderTarget, level));
return success ;
}
2026-03-02 15:58:20 +07:00
void LocalPlayer : : crit ( std : : shared_ptr < Entity > e )
2026-03-01 12:16:08 +08:00
{
2026-03-02 15:58:20 +07:00
std : : shared_ptr < CritParticle > critParticle = std : : shared_ptr < CritParticle > ( new CritParticle ( ( Level * ) minecraft - > level , e ) ) ;
2026-03-01 12:16:08 +08:00
critParticle - > CritParticlePostConstructor ( ) ;
minecraft - > particleEngine - > add ( critParticle ) ;
}
2026-03-02 15:58:20 +07:00
void LocalPlayer : : magicCrit ( std : : shared_ptr < Entity > e )
2026-03-01 12:16:08 +08:00
{
2026-03-02 15:58:20 +07:00
std : : shared_ptr < CritParticle > critParticle = std : : shared_ptr < CritParticle > ( new CritParticle ( ( Level * ) minecraft - > level , e , eParticleType_magicCrit ) ) ;
2026-03-01 12:16:08 +08:00
critParticle - > CritParticlePostConstructor ( ) ;
minecraft - > particleEngine - > add ( critParticle ) ;
}
2026-03-02 15:58:20 +07:00
void LocalPlayer : : take ( std : : shared_ptr < Entity > e , int orgCount )
2026-03-01 12:16:08 +08:00
{
2026-03-02 15:58:20 +07:00
minecraft - > particleEngine - > add ( std : : shared_ptr < TakeAnimationParticle > ( new TakeAnimationParticle ( ( Level * ) minecraft - > level , e , shared_from_this ( ) , - 0.5f ) ) ) ;
2026-03-01 12:16:08 +08:00
}
void LocalPlayer : : chat ( const wstring & message )
{
}
bool LocalPlayer : : isSneaking ( )
{
return input - > sneaking & & ! m_isSleeping ;
}
void LocalPlayer : : hurtTo ( int newHealth , ETelemetryChallenges damageSource )
{
int dmg = getHealth ( ) - newHealth ;
if ( dmg < = 0 )
{
setHealth ( newHealth ) ;
if ( dmg < 0 )
{
invulnerableTime = invulnerableDuration / 2 ;
}
}
else
{
lastHurt = dmg ;
setHealth ( getHealth ( ) ) ;
invulnerableTime = invulnerableDuration ;
actuallyHurt ( DamageSource : : genericSource , dmg ) ;
hurtTime = hurtDuration = 10 ;
}
if ( this - > health < = 0 )
{
int deathTime = ( int ) ( level - > getTime ( ) % Level : : TICKS_PER_DAY ) / 1000 ;
int carriedId = inventory - > getSelected ( ) = = NULL ? 0 : inventory - > getSelected ( ) - > id ;
TelemetryManager - > RecordPlayerDiedOrFailed ( GetXboxPad ( ) , 0 , y , 0 , 0 , carriedId , 0 , damageSource ) ;
// if there are any xuiscenes up for this player, close them
if ( ui . GetMenuDisplayed ( GetXboxPad ( ) ) )
{
ui . CloseUIScenes ( GetXboxPad ( ) ) ;
}
}
}
void LocalPlayer : : respawn ( )
{
// Select the right payer to respawn
minecraft - > respawnPlayer ( GetXboxPad ( ) , 0 , 0 ) ;
}
void LocalPlayer : : animateRespawn ( )
{
// Player.animateRespawn(this, level);
}
void LocalPlayer : : displayClientMessage ( int messageId )
{
minecraft - > gui - > displayClientMessage ( messageId , GetXboxPad ( ) ) ;
}
void LocalPlayer : : awardStat ( Stat * stat , byteArray param )
{
# ifdef _DURANGO
// 4J-JEV: Maybe we want to fine tune this later? #TODO
2026-03-02 15:58:20 +07:00
if ( ! ProfileManager . IsGuest ( GetXboxPad ( ) )
2026-03-01 12:16:08 +08:00
& & app . CanRecordStatsAndAchievements ( )
& & ProfileManager . IsFullVersion ( )
)
{
2026-03-02 17:10:34 +07:00
stat - > handleParamBlob ( std : : dynamic_pointer_cast < LocalPlayer > ( shared_from_this ( ) ) , param ) ;
2026-03-01 12:16:08 +08:00
}
delete [ ] param . data ;
# else
int count = CommonStats : : readParam ( param ) ;
delete [ ] param . data ;
if ( ! app . CanRecordStatsAndAchievements ( ) ) return ;
if ( stat = = NULL ) return ;
if ( stat - > isAchievement ( ) )
{
Achievement * ach = ( Achievement * ) stat ;
// 4J-PB - changed to attempt to award everytime - the award may need a storage device, so needs a primary player, and the player may not have been a primary player when they first 'got' the award
// so let the award manager figure it out
//if (!minecraft->stats[m_iPad]->hasTaken(ach))
{
// 4J-PB - Don't display the java popup
//minecraft->achievementPopup->popup(ach);
// 4J Stu - Added this function in the libraries as some achievements don't get awarded to all players
// e.g. Splitscreen players cannot get theme/avatar/gamerpic and Trial players cannot get any
// This causes some extreme flooding of some awards
if ( ProfileManager . CanBeAwarded ( m_iPad , ach - > getAchievementID ( ) ) )
{
// 4J Stu - We don't (currently) care about the gamerscore, so setting to a default of 0 points
TelemetryManager - > RecordAchievementUnlocked ( m_iPad , ach - > getAchievementID ( ) , 0 ) ;
// 4J Stu - Some awards cause a menu to popup. This can be bad, especially if you are surrounded by mobs!
// We cannot pause the game unless in offline single player, but lets at least do it then
if ( g_NetworkManager . IsLocalGame ( ) & & g_NetworkManager . GetPlayerCount ( ) = = 1 & & ProfileManager . GetAwardType ( ach - > getAchievementID ( ) ) ! = eAwardType_Achievement )
{
ui . CloseUIScenes ( m_iPad ) ;
ui . NavigateToScene ( m_iPad , eUIScene_PauseMenu ) ;
}
}
// 4J-JEV: To stop spamming trophies.
unsigned long long achBit = ( ( unsigned long long ) 1 ) < < ach - > getAchievementID ( ) ;
if ( ! ( achBit & m_awardedThisSession ) )
{
ProfileManager . Award ( m_iPad , ach - > getAchievementID ( ) ) ;
if ( ProfileManager . IsFullVersion ( ) )
m_awardedThisSession | = achBit ;
}
}
minecraft - > stats [ m_iPad ] - > award ( stat , level - > difficulty , count ) ;
}
else
{
// 4J : WESTY : Added for new achievements.
StatsCounter * pStats = minecraft - > stats [ m_iPad ] ;
pStats - > award ( stat , level - > difficulty , count ) ;
// 4J-JEV: Check achievements for unlocks.
// LEADER OF THE PACK
if ( stat = = GenericStats : : tamedEntity ( eTYPE_WOLF ) )
{
// Check to see if we have befriended 5 wolves! Is this really the best place to do this??!!
if ( pStats - > getTotalValue ( GenericStats : : tamedEntity ( eTYPE_WOLF ) ) > = 5 )
{
awardStat ( GenericStats : : leaderOfThePack ( ) , GenericStats : : param_noArgs ( ) ) ;
}
}
// MOAR TOOLS
{
Stat * toolStats [ 4 ] [ 5 ] ;
toolStats [ 0 ] [ 0 ] = GenericStats : : itemsCrafted ( Item : : shovel_wood - > id ) ;
toolStats [ 0 ] [ 1 ] = GenericStats : : itemsCrafted ( Item : : shovel_stone - > id ) ;
toolStats [ 0 ] [ 2 ] = GenericStats : : itemsCrafted ( Item : : shovel_iron - > id ) ;
toolStats [ 0 ] [ 3 ] = GenericStats : : itemsCrafted ( Item : : shovel_diamond - > id ) ;
toolStats [ 0 ] [ 4 ] = GenericStats : : itemsCrafted ( Item : : shovel_gold - > id ) ;
2026-03-02 15:58:20 +07:00
toolStats [ 1 ] [ 0 ] = GenericStats : : itemsCrafted ( Item : : pickAxe_wood - > id ) ;
2026-03-01 12:16:08 +08:00
toolStats [ 1 ] [ 1 ] = GenericStats : : itemsCrafted ( Item : : pickAxe_stone - > id ) ;
toolStats [ 1 ] [ 2 ] = GenericStats : : itemsCrafted ( Item : : pickAxe_iron - > id ) ;
toolStats [ 1 ] [ 3 ] = GenericStats : : itemsCrafted ( Item : : pickAxe_diamond - > id ) ;
toolStats [ 1 ] [ 4 ] = GenericStats : : itemsCrafted ( Item : : pickAxe_gold - > id ) ;
toolStats [ 2 ] [ 0 ] = GenericStats : : itemsCrafted ( Item : : hatchet_wood - > id ) ;
toolStats [ 2 ] [ 1 ] = GenericStats : : itemsCrafted ( Item : : hatchet_stone - > id ) ;
toolStats [ 2 ] [ 2 ] = GenericStats : : itemsCrafted ( Item : : hatchet_iron - > id ) ;
toolStats [ 2 ] [ 3 ] = GenericStats : : itemsCrafted ( Item : : hatchet_diamond - > id ) ;
toolStats [ 2 ] [ 4 ] = GenericStats : : itemsCrafted ( Item : : hatchet_gold - > id ) ;
toolStats [ 3 ] [ 0 ] = GenericStats : : itemsCrafted ( Item : : hoe_wood - > id ) ;
toolStats [ 3 ] [ 1 ] = GenericStats : : itemsCrafted ( Item : : hoe_stone - > id ) ;
toolStats [ 3 ] [ 2 ] = GenericStats : : itemsCrafted ( Item : : hoe_iron - > id ) ;
toolStats [ 3 ] [ 3 ] = GenericStats : : itemsCrafted ( Item : : hoe_diamond - > id ) ;
toolStats [ 3 ] [ 4 ] = GenericStats : : itemsCrafted ( Item : : hoe_gold - > id ) ;
2026-03-02 15:58:20 +07:00
2026-03-01 12:16:08 +08:00
bool justCraftedTool = false ;
for ( int i = 0 ; i < 4 ; i + + )
{
for ( int j = 0 ; j < 5 ; j + + )
{
if ( stat = = toolStats [ i ] [ j ] )
{
justCraftedTool = true ;
break ;
}
}
}
if ( justCraftedTool )
{
bool awardNow = true ;
for ( int i = 0 ; i < 4 ; i + + )
{
bool craftedThisTool = false ;
for ( int j = 0 ; j < 5 ; j + + )
{
if ( pStats - > getTotalValue ( toolStats [ i ] [ j ] ) > 0 )
craftedThisTool = true ;
}
if ( ! craftedThisTool )
{
awardNow = false ;
break ;
}
}
if ( awardNow )
{
awardStat ( GenericStats : : MOARTools ( ) , GenericStats : : param_noArgs ( ) ) ;
}
}
2026-03-02 15:58:20 +07:00
2026-03-01 12:16:08 +08:00
}
# ifdef _XBOX
// AWARD: Have we killed 10 creepers?
if ( pStats - > getTotalValue ( GenericStats : : killsCreeper ( ) ) > = 10 )
{
awardStat ( GenericStats : : kill10Creepers ( ) , GenericStats : : param_noArgs ( ) ) ;
}
// AWARD : Have we been playing for 100 game days?
if ( pStats - > getTotalValue ( GenericStats : : timePlayed ( ) ) > = ( Level : : TICKS_PER_DAY * 100 ) )
{
awardStat ( GenericStats : : play100Days ( ) , GenericStats : : param_noArgs ( ) ) ;
}
// AWARD : Have we mined 100 blocks?
if ( pStats - > getTotalValue ( GenericStats : : totalBlocksMined ( ) ) > = 100 )
{
awardStat ( GenericStats : : mine100Blocks ( ) , GenericStats : : param_noArgs ( ) ) ;
}
# endif
# ifdef _EXTENDED_ACHIEVEMENTS
// AWARD : Porkchop, cook and eat a porkchop.
{
Stat * cookPorkchop , * eatPorkchop ;
cookPorkchop = GenericStats : : itemsCrafted ( Item : : porkChop_cooked_Id ) ;
eatPorkchop = GenericStats : : itemsUsed ( Item : : porkChop_cooked_Id ) ;
if ( stat = = cookPorkchop | | stat = = eatPorkchop )
{
int numCookPorkchop , numEatPorkchop ;
numCookPorkchop = pStats - > getTotalValue ( cookPorkchop ) ;
numEatPorkchop = pStats - > getTotalValue ( eatPorkchop ) ;
app . DebugPrintf (
" [AwardStat] Check unlock 'Porkchop': "
" pork_cooked=%i, pork_eaten=%i. \n " ,
numCookPorkchop , numEatPorkchop
) ;
if ( ( 0 < numCookPorkchop ) & & ( 0 < numEatPorkchop ) )
{
awardStat ( GenericStats : : porkChop ( ) , GenericStats : : param_porkChop ( ) ) ;
}
}
}
// AWARD : Passing the Time, play for 100 minecraft days.
{
Stat * timePlayed = GenericStats : : timePlayed ( ) ;
if ( stat = = timePlayed )
{
int iPlayedTicks , iRequiredTicks ;
iPlayedTicks = pStats - > getTotalValue ( timePlayed ) ;
iRequiredTicks = Level : : TICKS_PER_DAY * 100 ;
/* app.DebugPrintf(
" [AwardStat] Check unlock 'Passing the Time': "
" total_ticks=%i, req=%i. \n " ,
iPlayedTicks , iRequiredTicks
) ; */
if ( iPlayedTicks > = iRequiredTicks )
{
awardStat ( GenericStats : : passingTheTime ( ) , GenericStats : : param_passingTheTime ( ) ) ;
}
}
}
// AWARD : The Haggler, Acquire 30 emeralds.
{
Stat * emeraldMined , * emeraldBought ;
emeraldMined = GenericStats : : blocksMined ( Tile : : emeraldOre_Id ) ;
emeraldBought = GenericStats : : itemsBought ( Item : : emerald_Id ) ;
if ( stat = = emeraldMined | | stat = = emeraldBought )
{
int numEmeraldMined , numEmeraldBought , totalSum ;
numEmeraldMined = pStats - > getTotalValue ( emeraldMined ) ;
numEmeraldBought = pStats - > getTotalValue ( emeraldBought ) ;
totalSum = numEmeraldMined + numEmeraldBought ;
2026-03-02 15:58:20 +07:00
2026-03-01 12:16:08 +08:00
app . DebugPrintf (
" [AwardStat] Check unlock 'The Haggler': "
" emerald_mined=%i, emerald_bought=%i, sum=%i. \n " ,
numEmeraldMined , numEmeraldBought , totalSum
) ;
if ( totalSum > = 30 ) awardStat ( GenericStats : : theHaggler ( ) , GenericStats : : param_theHaggler ( ) ) ;
}
}
// AWARD : Pot Planter, craft and place a flowerpot.
{
Stat * craftFlowerpot , * placeFlowerpot ;
craftFlowerpot = GenericStats : : itemsCrafted ( Item : : flowerPot_Id ) ;
placeFlowerpot = GenericStats : : blocksPlaced ( Tile : : flowerPot_Id ) ;
if ( stat = = craftFlowerpot | | stat = = placeFlowerpot )
{
if ( ( pStats - > getTotalValue ( craftFlowerpot ) > 0 ) & & ( pStats - > getTotalValue ( placeFlowerpot ) > 0 ) )
{
awardStat ( GenericStats : : potPlanter ( ) , GenericStats : : param_potPlanter ( ) ) ;
}
}
}
// AWARD : It's a Sign, craft and place a sign.
{
Stat * craftSign , * placeWallsign , * placeSignpost ;
craftSign = GenericStats : : itemsCrafted ( Item : : sign_Id ) ;
placeWallsign = GenericStats : : blocksPlaced ( Tile : : wallSign_Id ) ;
placeSignpost = GenericStats : : blocksPlaced ( Tile : : sign_Id ) ;
if ( stat = = craftSign | | stat = = placeWallsign | | stat = = placeSignpost )
{
int numCraftedSigns , numPlacedWallSign , numPlacedSignpost ;
numCraftedSigns = pStats - > getTotalValue ( craftSign ) ;
numPlacedWallSign = pStats - > getTotalValue ( placeWallsign ) ;
numPlacedSignpost = pStats - > getTotalValue ( placeSignpost ) ;
app . DebugPrintf (
" [AwardStat] Check unlock 'It's a Sign': "
" crafted=%i, placedWallSigns=%i, placedSignposts=%i. \n " ,
numCraftedSigns , numPlacedWallSign , numPlacedSignpost
) ;
if ( ( numCraftedSigns > 0 ) & & ( ( numPlacedWallSign + numPlacedSignpost ) > 0 ) )
{
awardStat ( GenericStats : : itsASign ( ) , GenericStats : : param_itsASign ( ) ) ;
}
}
}
// AWARD : Rainbow Collection, collect all different colours of wool.
{
bool justPickedupWool = false ;
2026-03-02 15:58:20 +07:00
2026-03-01 12:16:08 +08:00
for ( int i = 0 ; i < 16 ; i + + )
if ( stat = = GenericStats : : itemsCollected ( Tile : : cloth_Id , i ) )
justPickedupWool = true ;
if ( justPickedupWool )
{
unsigned int woolCount = 0 ;
2026-03-02 15:58:20 +07:00
2026-03-01 12:16:08 +08:00
for ( unsigned int i = 0 ; i < 16 ; i + + )
{
if ( pStats - > getTotalValue ( GenericStats : : itemsCollected ( Tile : : cloth_Id , i ) ) > 0 )
woolCount + + ;
}
if ( woolCount > = 16 ) awardStat ( GenericStats : : rainbowCollection ( ) , GenericStats : : param_rainbowCollection ( ) ) ;
}
}
// AWARD : Adventuring Time, visit at least 17 biomes
{
bool justEnteredBiome = false ;
for ( int i = 0 ; i < 23 ; i + + )
if ( stat = = GenericStats : : enteredBiome ( i ) )
justEnteredBiome = true ;
if ( justEnteredBiome )
{
unsigned int biomeCount = 0 ;
for ( unsigned int i = 0 ; i < 23 ; i + + )
{
if ( pStats - > getTotalValue ( GenericStats : : enteredBiome ( i ) ) > 0 )
biomeCount + + ;
}
if ( biomeCount > = 17 ) awardStat ( GenericStats : : adventuringTime ( ) , GenericStats : : param_adventuringTime ( ) ) ;
}
}
# endif
}
# endif
}
bool LocalPlayer : : isSolidBlock ( int x , int y , int z )
{
return level - > isSolidBlockingTile ( x , y , z ) ;
}
bool LocalPlayer : : checkInTile ( double x , double y , double z )
{
int xTile = Mth : : floor ( x ) ;
int yTile = Mth : : floor ( y ) ;
int zTile = Mth : : floor ( z ) ;
double xd = x - xTile ;
double zd = z - zTile ;
if ( isSolidBlock ( xTile , yTile , zTile ) | | isSolidBlock ( xTile , yTile + 1 , zTile ) )
{
bool west = ! isSolidBlock ( xTile - 1 , yTile , zTile ) & & ! isSolidBlock ( xTile - 1 , yTile + 1 , zTile ) ;
bool east = ! isSolidBlock ( xTile + 1 , yTile , zTile ) & & ! isSolidBlock ( xTile + 1 , yTile + 1 , zTile ) ;
bool north = ! isSolidBlock ( xTile , yTile , zTile - 1 ) & & ! isSolidBlock ( xTile , yTile + 1 , zTile - 1 ) ;
bool south = ! isSolidBlock ( xTile , yTile , zTile + 1 ) & & ! isSolidBlock ( xTile , yTile + 1 , zTile + 1 ) ;
int dir = - 1 ;
double closest = 9999 ;
if ( west & & xd < closest )
{
closest = xd ;
dir = 0 ;
}
if ( east & & 1 - xd < closest )
{
closest = 1 - xd ;
dir = 1 ;
}
if ( north & & zd < closest )
{
closest = zd ;
dir = 4 ;
}
if ( south & & 1 - zd < closest )
{
closest = 1 - zd ;
dir = 5 ;
}
float speed = 0.1f ;
if ( dir = = 0 ) this - > xd = - speed ;
if ( dir = = 1 ) this - > xd = + speed ;
if ( dir = = 4 ) this - > zd = - speed ;
if ( dir = = 5 ) this - > zd = + speed ;
}
return false ;
}
void LocalPlayer : : setSprinting ( bool value )
{
Player : : setSprinting ( value ) ;
if ( value = = false ) sprintTime = 0 ;
else sprintTime = SPRINT_DURATION ;
}
void LocalPlayer : : setExperienceValues ( float experienceProgress , int totalExp , int experienceLevel )
{
this - > experienceProgress = experienceProgress ;
this - > totalExperience = totalExp ;
this - > experienceLevel = experienceLevel ;
}
bool LocalPlayer : : hasPermission ( EGameCommand command )
{
return level - > getLevelData ( ) - > getAllowCommands ( ) ;
}
2026-03-02 15:58:20 +07:00
void LocalPlayer : : onCrafted ( std : : shared_ptr < ItemInstance > item )
2026-03-01 12:16:08 +08:00
{
if ( minecraft - > localgameModes [ m_iPad ] ! = NULL )
{
TutorialMode * gameMode = ( TutorialMode * ) minecraft - > localgameModes [ m_iPad ] ;
gameMode - > getTutorial ( ) - > onCrafted ( item ) ;
}
}
void LocalPlayer : : setAndBroadcastCustomSkin ( DWORD skinId )
{
setCustomSkin ( skinId ) ;
}
void LocalPlayer : : setAndBroadcastCustomCape ( DWORD capeId )
{
setCustomCape ( capeId ) ;
}
// 4J TODO - Remove
# include "..\Minecraft.World\LevelChunk.h"
void LocalPlayer : : mapPlayerChunk ( const unsigned int flagTileType )
{
int cx = this - > xChunk ;
int cz = this - > zChunk ;
int pZ = ( ( int ) floor ( this - > z ) ) % 16 ;
int pX = ( ( int ) floor ( this - > x ) ) % 16 ;
cout < < " player in chunk ( " < < cx < < " , " < < cz < < " ) at ( "
< < this - > x < < " , " < < this - > y < < " , " < < this - > z < < " ) \n " ;
for ( int v = - 1 ; v < 2 ; v + + )
for ( unsigned int z = 0 ; z < 16 ; z + + )
{
for ( int u = - 1 ; u < 2 ; u + + )
for ( unsigned int x = 0 ; x < 16 ; x + + )
{
LevelChunk * cc = level - > getChunk ( cx + u , cz + v ) ;
if ( x = = pX & & z = = pZ & & u = = 0 & & v = = 0 )
cout < < " O " ;
else for ( unsigned int y = 127 ; y > 0 ; y - - )
{
2026-03-02 15:58:20 +07:00
int t = cc - > getTile ( x , y , z ) ;
2026-03-01 12:16:08 +08:00
if ( flagTileType ! = 0 & & t = = flagTileType ) { cout < < " @ " ; break ; }
else if ( t ! = 0 & & t < 10 ) { cout < < t ; break ; }
else if ( t > 0 ) { cout < < " # " ; break ; }
}
}
cout < < " \n " ;
}
cout < < " \n " ;
}
void LocalPlayer : : handleMouseDown ( int button , bool down )
{
// 4J Stu - We should not accept any input while asleep, except the above to wake up
if ( isSleeping ( ) & & level ! = NULL & & level - > isClientSide )
{
return ;
}
if ( ! down ) missTime = 0 ;
if ( button = = 0 & & missTime > 0 ) return ;
if ( down & & minecraft - > hitResult ! = NULL & & minecraft - > hitResult - > type = = HitResult : : TILE & & button = = 0 )
{
int x = minecraft - > hitResult - > x ;
int y = minecraft - > hitResult - > y ;
int z = minecraft - > hitResult - > z ;
// 4J - addition to stop layer mining out of the top or bottom of the world
// 4J Stu - Allow this for The End
if ( ( ( y = = 0 ) | | ( ( y = = 127 ) & & level - > dimension - > hasCeiling ) ) & & level - > dimension - > id ! = 1 ) return ;
minecraft - > gameMode - > continueDestroyBlock ( x , y , z , minecraft - > hitResult - > f ) ;
2026-03-02 15:58:20 +07:00
2026-03-01 12:16:08 +08:00
if ( mayBuild ( x , y , z ) )
{
minecraft - > particleEngine - > crack ( x , y , z , minecraft - > hitResult - > f ) ;
swing ( ) ;
}
}
else
{
minecraft - > gameMode - > stopDestroyBlock ( ) ;
}
}
bool LocalPlayer : : creativeModeHandleMouseClick ( int button , bool buttonPressed )
{
if ( buttonPressed )
{
if ( lastClickState = = lastClick_oldRepeat )
{
return false ;
}
// Are we in an auto-repeat situation? - If so only tell the game that we've clicked if we move more than a unit away from our last
// click position in any axis
if ( lastClickState ! = lastClick_invalid )
{
// If we're in disabled mode already (set when sprinting) then don't do anything - if we're sprinting, we don't auto-repeat at all.
// With auto repeat on, we can quickly place fires causing photosensitivity issues due to rapid flashing
if ( lastClickState = = lastClick_disabled ) return false ;
// If we've started sprinting, go into this mode & also don't do anything
// Ignore repeate when sleeping
if ( isSprinting ( ) )
{
lastClickState = lastClick_disabled ;
return false ;
}
// Get distance from last click point in each axis
2026-03-02 15:58:20 +07:00
float dX = ( float ) x - lastClickX ;
2026-03-01 12:16:08 +08:00
float dY = ( float ) y - lastClickY ;
float dZ = ( float ) z - lastClickZ ;
bool newClick = false ;
float ddx = dX - lastClickdX ;
float ddy = dY - lastClickdY ;
float ddz = dZ - lastClickdZ ;
if ( lastClickState = = lastClick_moving )
{
float deltaChange = sqrtf ( ddx * ddx + ddy * ddy + ddz * ddz ) ;
if ( deltaChange < 0.01f )
{
lastClickState = lastClick_stopped ;
lastClickTolerance = 0.0f ;
}
}
else if ( lastClickState = = lastClick_stopped )
{
float deltaChange = sqrtf ( ddx * ddx + ddy * ddy + ddz * ddz ) ;
if ( deltaChange > = 0.01f )
{
lastClickState = lastClick_moving ;
lastClickTolerance = 0.0f ;
}
else
{
lastClickTolerance + = 0.1f ;
if ( lastClickTolerance > 0.7f )
{
lastClickTolerance = 0.0f ;
lastClickState = lastClick_init ;
}
}
}
lastClickdX = dX ;
lastClickdY = dY ;
lastClickdZ = dZ ;
// If we have moved more than one unit in any one axis, then register a new click
// The new click position is normalised at one unit in the direction of movement, so that we don't gradually drift away if we detect the movement a fraction over
// the unit distance each time
if ( fabsf ( dX ) > = 1.0f )
{
dX = ( dX < 0.0f ) ? ceilf ( dX ) : floorf ( dX ) ;
newClick = true ;
}
else if ( fabsf ( dY ) > = 1.0f )
{
dY = ( dY < 0.0f ) ? ceilf ( dY ) : floorf ( dY ) ;
newClick = true ;
}
else if ( fabsf ( dZ ) > = 1.0f )
{
dZ = ( dZ < 0.0f ) ? ceilf ( dZ ) : floorf ( dZ ) ;
newClick = true ;
}
if ( ( ! newClick ) & & ( lastClickTolerance > 0.0f ) )
{
float fTarget = 1.0f - lastClickTolerance ;
if ( fabsf ( dX ) > = fTarget ) newClick = true ;
if ( fabsf ( dY ) > = fTarget ) newClick = true ;
if ( fabsf ( dZ ) > = fTarget ) newClick = true ;
}
if ( newClick )
{
lastClickX + = dX ;
lastClickY + = dY ;
lastClickZ + = dZ ;
// Get a more accurate pick from the position where the new click should ideally have come from, rather than
// where we happen to be now (ie a rounded number of units from the last Click position)
double oldX = x ;
double oldY = y ;
double oldZ = z ;
x = lastClickX ;
y = lastClickY ;
z = lastClickZ ;
minecraft - > gameRenderer - > pick ( 1 ) ;
x = oldX ;
y = oldY ;
z = oldZ ;
handleMouseClick ( button ) ;
if ( lastClickState = = lastClick_stopped )
{
lastClickState = lastClick_init ;
lastClickTolerance = 0.0f ;
}
else
{
lastClickState = lastClick_moving ;
lastClickTolerance = 0.0f ;
}
}
}
else
{
// First click - just record position & handle
lastClickX = ( float ) x ;
lastClickY = ( float ) y ;
lastClickZ = ( float ) z ;
// If we actually placed an item, then move into the init state as we are going to be doing the special creative mode auto repeat
bool itemPlaced = handleMouseClick ( button ) ;
// If we're sprinting or riding, don't auto-repeat at all. With auto repeat on, we can quickly place fires causing photosensitivity issues due to rapid flashing
// Also ignore repeats when the player is sleeping
if ( isSprinting ( ) | | isRiding ( ) | | isSleeping ( ) )
{
lastClickState = lastClick_disabled ;
}
else
{
if ( itemPlaced )
{
lastClickState = lastClick_init ;
lastClickTolerance = 0.0f ;
}
else
{
// Didn't place an item - might actually be activating a switch or door or something - just do a standard auto repeat in this case
lastClickState = lastClick_oldRepeat ;
}
}
return true ;
}
}
else
{
lastClickState = lastClick_invalid ;
}
return false ;
}
bool LocalPlayer : : handleMouseClick ( int button )
{
bool returnItemPlaced = false ;
if ( button = = 0 & & missTime > 0 ) return false ;
2026-03-02 15:58:20 +07:00
if ( button = = 0 )
2026-03-01 12:16:08 +08:00
{
//app.DebugPrintf("handleMouseClick - Player %d is swinging\n",GetXboxPad());
swing ( ) ;
}
2026-03-02 15:58:20 +07:00
bool mayUse = true ;
2026-03-01 12:16:08 +08:00
// 4J-PB - Adding a special case in here for sleeping in a bed in a multiplayer game - we need to wake up, and we don't have the inbedchatscreen with a button
if ( button = = 1 & & ( isSleeping ( ) & & level ! = NULL & & level - > isClientSide ) )
{
if ( lastClickState = = lastClick_oldRepeat ) return false ;
2026-03-02 17:10:34 +07:00
std : : shared_ptr < MultiplayerLocalPlayer > mplp = std : : dynamic_pointer_cast < MultiplayerLocalPlayer > ( shared_from_this ( ) ) ;
2026-03-01 12:16:08 +08:00
if ( mplp & & mplp - > connection ) mplp - > StopSleeping ( ) ;
2026-03-02 15:58:20 +07:00
2026-03-01 12:16:08 +08:00
}
// 4J Stu - We should not accept any input while asleep, except the above to wake up
if ( isSleeping ( ) & & level ! = NULL & & level - > isClientSide )
{
return false ;
}
2026-03-02 15:58:20 +07:00
std : : shared_ptr < ItemInstance > oldItem = inventory - > getSelected ( ) ;
2026-03-01 12:16:08 +08:00
if ( minecraft - > hitResult = = NULL )
{
if ( button = = 0 & & minecraft - > localgameModes [ GetXboxPad ( ) ] - > hasMissTime ( ) ) missTime = 10 ;
}
else if ( minecraft - > hitResult - > type = = HitResult : : ENTITY )
{
if ( button = = 0 )
{
minecraft - > gameMode - > attack ( minecraft - > localplayers [ GetXboxPad ( ) ] , minecraft - > hitResult - > entity ) ;
}
if ( button = = 1 )
{
2026-03-02 15:58:20 +07:00
// 4J-PB - if we milk a cow here, and end up with a bucket of milk, the if (mayUse && button == 1) further down will
2026-03-01 12:16:08 +08:00
// then empty our bucket if we're pointing at a tile
2026-03-02 15:58:20 +07:00
// It looks like interact really should be returning a result so we can check this, but it's possibly just the
2026-03-01 12:16:08 +08:00
// milk bucket that causes a problem
if ( minecraft - > hitResult - > entity - > GetType ( ) = = eTYPE_COW )
{
// If I have an empty bucket in my hand, it's going to be filled with milk, so turn off mayUse
2026-03-02 15:58:20 +07:00
std : : shared_ptr < ItemInstance > item = inventory - > getSelected ( ) ;
2026-03-01 12:16:08 +08:00
if ( item & & ( item - > id = = Item : : bucket_empty_Id ) )
{
mayUse = false ;
}
}
if ( minecraft - > gameMode - > interact ( minecraft - > localplayers [ GetXboxPad ( ) ] , minecraft - > hitResult - > entity ) )
{
mayUse = false ;
}
}
}
else if ( minecraft - > hitResult - > type = = HitResult : : TILE )
{
int x = minecraft - > hitResult - > x ;
int y = minecraft - > hitResult - > y ;
int z = minecraft - > hitResult - > z ;
int face = minecraft - > hitResult - > f ;
if ( button = = 0 )
{
// 4J - addition to stop layer mining out of the top or bottom of the world
// 4J Stu - Allow this for The End
if ( ! ( ( y = = 0 ) | | ( ( y = = 127 ) & & level - > dimension - > hasCeiling ) ) | | level - > dimension - > id = = 1 )
{
minecraft - > gameMode - > startDestroyBlock ( x , y , z , minecraft - > hitResult - > f ) ;
}
}
else
{
2026-03-02 15:58:20 +07:00
std : : shared_ptr < ItemInstance > item = oldItem ;
2026-03-01 12:16:08 +08:00
int oldCount = item ! = NULL ? item - > count : 0 ;
bool usedItem = false ;
if ( minecraft - > gameMode - > useItemOn ( minecraft - > localplayers [ GetXboxPad ( ) ] , level , item , x , y , z , face , minecraft - > hitResult - > pos , false , & usedItem ) )
{
// Presume that if we actually used the held item, then we've placed it
if ( usedItem )
{
returnItemPlaced = true ;
}
mayUse = false ;
//app.DebugPrintf("Player %d is swinging\n",GetXboxPad());
swing ( ) ;
}
if ( item = = NULL )
{
return false ;
}
if ( item - > count = = 0 )
{
inventory - > items [ inventory - > selected ] = nullptr ;
}
else if ( item - > count ! = oldCount | | minecraft - > localgameModes [ GetXboxPad ( ) ] - > hasInfiniteItems ( ) )
{
minecraft - > gameRenderer - > itemInHandRenderer - > itemPlaced ( ) ;
}
}
}
if ( mayUse & & button = = 1 )
{
2026-03-02 15:58:20 +07:00
std : : shared_ptr < ItemInstance > item = inventory - > getSelected ( ) ;
2026-03-01 12:16:08 +08:00
if ( item ! = NULL )
{
if ( minecraft - > gameMode - > useItem ( minecraft - > localplayers [ GetXboxPad ( ) ] , level , item ) )
{
minecraft - > gameRenderer - > itemInHandRenderer - > itemUsed ( ) ;
}
}
}
return returnItemPlaced ;
}
void LocalPlayer : : updateRichPresence ( )
{
if ( ( m_iPad ! = - 1 ) /* && !ui.GetMenuDisplayed(m_iPad)*/ )
{
2026-03-02 15:58:20 +07:00
std : : shared_ptr < ItemInstance > selectedItem = inventory - > getSelected ( ) ;
2026-03-01 12:16:08 +08:00
if ( selectedItem ! = NULL & & selectedItem - > id = = Item : : fishingRod_Id )
{
app . SetRichPresenceContext ( m_iPad , CONTEXT_GAME_STATE_FISHING ) ;
}
else if ( selectedItem ! = NULL & & selectedItem - > id = = Item : : map_Id )
{
app . SetRichPresenceContext ( m_iPad , CONTEXT_GAME_STATE_MAP ) ;
2026-03-02 15:58:20 +07:00
}
2026-03-02 17:10:34 +07:00
else if ( riding ! = NULL & & std : : dynamic_pointer_cast < Minecart > ( riding ) ! = NULL )
2026-03-01 12:16:08 +08:00
{
app . SetRichPresenceContext ( m_iPad , CONTEXT_GAME_STATE_RIDING_MINECART ) ;
}
2026-03-02 17:10:34 +07:00
else if ( riding ! = NULL & & std : : dynamic_pointer_cast < Boat > ( riding ) ! = NULL )
2026-03-01 12:16:08 +08:00
{
app . SetRichPresenceContext ( m_iPad , CONTEXT_GAME_STATE_BOATING ) ;
}
2026-03-02 17:10:34 +07:00
else if ( riding ! = NULL & & std : : dynamic_pointer_cast < Pig > ( riding ) ! = NULL )
2026-03-01 12:16:08 +08:00
{
app . SetRichPresenceContext ( m_iPad , CONTEXT_GAME_STATE_RIDING_PIG ) ;
}
else if ( this - > dimension = = - 1 )
{
app . SetRichPresenceContext ( m_iPad , CONTEXT_GAME_STATE_NETHER ) ;
}
else if ( minecraft - > soundEngine - > GetIsPlayingStreamingCDMusic ( ) )
{
app . SetRichPresenceContext ( m_iPad , CONTEXT_GAME_STATE_CD ) ;
}
else
{
app . SetRichPresenceContext ( m_iPad , CONTEXT_GAME_STATE_BLANK ) ;
}
}
}
// 4J Stu - Added for telemetry
void LocalPlayer : : SetSessionTimerStart ( void )
{
m_sessionTimeStart = app . getAppTime ( ) ;
m_dimensionTimeStart = m_sessionTimeStart ;
}
float LocalPlayer : : getSessionTimer ( void )
{
return app . getAppTime ( ) - m_sessionTimeStart ;
}
float LocalPlayer : : getAndResetChangeDimensionTimer ( )
{
float appTime = app . getAppTime ( ) ;
float returnVal = appTime - m_dimensionTimeStart ;
m_dimensionTimeStart = appTime ;
return returnVal ;
}
2026-03-02 15:58:20 +07:00
void LocalPlayer : : handleCollectItem ( std : : shared_ptr < ItemInstance > item )
2026-03-01 12:16:08 +08:00
{
if ( item ! = NULL )
{
unsigned int itemCountAnyAux = 0 ;
unsigned int itemCountThisAux = 0 ;
for ( unsigned int k = 0 ; k < inventory - > items . length ; + + k )
{
if ( inventory - > items [ k ] ! = NULL )
{
// do they have the item
if ( inventory - > items [ k ] - > id = = item - > id )
{
unsigned int quantity = inventory - > items [ k ] - > GetCount ( ) ;
itemCountAnyAux + = quantity ;
if ( inventory - > items [ k ] - > getAuxValue ( ) = = item - > getAuxValue ( ) )
{
itemCountThisAux + = quantity ;
}
}
}
}
TutorialMode * gameMode = ( TutorialMode * ) minecraft - > localgameModes [ m_iPad ] ;
gameMode - > getTutorial ( ) - > onTake ( item , itemCountAnyAux , itemCountThisAux ) ;
}
if ( ui . IsContainerMenuDisplayed ( m_iPad ) )
{
ui . HandleInventoryUpdated ( m_iPad ) ;
}
}
void LocalPlayer : : SetPlayerAdditionalModelParts ( vector < ModelPart * > pAdditionalModelParts )
{
m_pAdditionalModelParts = pAdditionalModelParts ;
}