aboutsummaryrefslogtreecommitdiff
path: root/Minecraft.Client/LocalPlayer.cpp
diff options
context:
space:
mode:
authordaoge_cmd <3523206925@qq.com>2026-03-01 12:16:08 +0800
committerdaoge_cmd <3523206925@qq.com>2026-03-01 12:16:08 +0800
commitb691c43c44ff180d10e7d4a9afc83b98551ff586 (patch)
tree3e9849222cbc6ba49f2f1fc6e5fe7179632c7390 /Minecraft.Client/LocalPlayer.cpp
parentdef8cb415354ac390b7e89052a50605285f1aca9 (diff)
Initial commit
Diffstat (limited to 'Minecraft.Client/LocalPlayer.cpp')
-rw-r--r--Minecraft.Client/LocalPlayer.cpp1616
1 files changed, 1616 insertions, 0 deletions
diff --git a/Minecraft.Client/LocalPlayer.cpp b/Minecraft.Client/LocalPlayer.cpp
new file mode 100644
index 00000000..ea66b9e1
--- /dev/null
+++ b/Minecraft.Client/LocalPlayer.cpp
@@ -0,0 +1,1616 @@
+#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();
+
+ 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;
+
+ // 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;
+
+ bool wasRunning = input->ya >= runTreshold;
+ //input->tick( dynamic_pointer_cast<Player>( shared_from_this() ) );
+ // 4J-PB - make it a localplayer
+ input->tick( this );
+ 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;
+
+ // 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))
+ {
+ if( !wasRunning && input->ya >= runTreshold )
+ {
+ if (sprintTriggerTime == 0)
+ {
+ sprintTriggerTime = 7;
+ sprintTriggerRegisteredReturn = false;
+ }
+ else
+ {
+ if( sprintTriggerRegisteredReturn )
+ {
+ setSprinting(true);
+ sprintTriggerTime = 0;
+ sprintTriggerRegisteredReturn = false;
+ }
+ }
+ }
+ else if( ( sprintTriggerTime > 0 ) && ( input->ya == 0.0f ) ) // ya of 0.0f here signifies that we have returned to the deadzone
+ {
+ sprintTriggerRegisteredReturn = true;
+ }
+ }
+ if (isSneaking()) sprintTriggerTime = 0;
+ // 4J-PB - try not stopping sprint on collision
+ //if (isSprinting() && (input->ya < runTreshold || horizontalCollision || !enoughFoodToSprint))
+ if (isSprinting() && (input->ya < runTreshold || !enoughFoodToSprint))
+ {
+ setSprinting(false);
+ }
+
+ // 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;
+ }
+ }
+
+
+ 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);
+ }
+ }
+ 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 );
+}
+
+void LocalPlayer::openTextEdit(shared_ptr<SignTileEntity> sign)
+{
+ bool success = app.LoadSignEntryMenu(GetXboxPad(), sign );
+ if( success ) ui.PlayUISFX(eSFX_Press);
+ //minecraft->setScreen(new TextEditScreen(sign));
+}
+
+bool LocalPlayer::openContainer(shared_ptr<Container> container)
+{
+ 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)
+{
+ bool success = app.LoadCrafting3x3Menu(GetXboxPad(), dynamic_pointer_cast<LocalPlayer>( shared_from_this() ), x, y, z );
+ 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;
+}
+
+bool LocalPlayer::openFurnace(shared_ptr<FurnaceTileEntity> furnace)
+{
+ bool success = app.LoadFurnaceMenu(GetXboxPad(),inventory, furnace);
+ if( success ) ui.PlayUISFX(eSFX_Press);
+ //minecraft->setScreen(new FurnaceScreen(inventory, furnace));
+ return success;
+}
+
+bool LocalPlayer::openBrewingStand(shared_ptr<BrewingStandTileEntity> brewingStand)
+{
+ bool success = app.LoadBrewingStandMenu(GetXboxPad(),inventory, brewingStand);
+ if( success ) ui.PlayUISFX(eSFX_Press);
+ //minecraft.setScreen(new BrewingStandScreen(inventory, brewingStand));
+ return success;
+}
+
+bool LocalPlayer::openTrap(shared_ptr<DispenserTileEntity> trap)
+{
+ bool success = app.LoadTrapMenu(GetXboxPad(),inventory, trap);
+ if( success ) ui.PlayUISFX(eSFX_Press);
+ //minecraft->setScreen(new TrapScreen(inventory, trap));
+ return success;
+}
+
+bool LocalPlayer::openTrading(shared_ptr<Merchant> traderTarget)
+{
+ bool success = app.LoadTradingMenu(GetXboxPad(),inventory, traderTarget, level);
+ if( success ) ui.PlayUISFX(eSFX_Press);
+ //minecraft.setScreen(new MerchantScreen(inventory, traderTarget, level));
+ return success;
+}
+
+void LocalPlayer::crit(shared_ptr<Entity> e)
+{
+ shared_ptr<CritParticle> critParticle = shared_ptr<CritParticle>( new CritParticle((Level *)minecraft->level, e) );
+ critParticle->CritParticlePostConstructor();
+ minecraft->particleEngine->add(critParticle);
+}
+
+void LocalPlayer::magicCrit(shared_ptr<Entity> e)
+{
+ shared_ptr<CritParticle> critParticle = shared_ptr<CritParticle>( new CritParticle((Level *)minecraft->level, e, eParticleType_magicCrit) );
+ critParticle->CritParticlePostConstructor();
+ minecraft->particleEngine->add(critParticle);
+}
+
+void LocalPlayer::take(shared_ptr<Entity> e, int orgCount)
+{
+ minecraft->particleEngine->add( shared_ptr<TakeAnimationParticle>( new TakeAnimationParticle((Level *)minecraft->level, e, shared_from_this(), -0.5f) ) );
+}
+
+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
+ if ( !ProfileManager.IsGuest(GetXboxPad())
+ && app.CanRecordStatsAndAchievements()
+ && ProfileManager.IsFullVersion()
+ )
+ {
+ stat->handleParamBlob(dynamic_pointer_cast<LocalPlayer>(shared_from_this()), param);
+ }
+ 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);
+ toolStats[1][0] = GenericStats::itemsCrafted(Item::pickAxe_wood->id);
+ 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);
+
+ 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());
+ }
+ }
+
+ }
+
+#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;
+
+ 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;
+
+ for (int i=0; i<16; i++)
+ if ( stat == GenericStats::itemsCollected(Tile::cloth_Id, i) )
+ justPickedupWool = true;
+
+ if (justPickedupWool)
+ {
+ unsigned int woolCount = 0;
+
+ 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();
+}
+
+void LocalPlayer::onCrafted(shared_ptr<ItemInstance> item)
+{
+ 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--)
+ {
+ int t = cc->getTile(x,y,z);
+ 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);
+
+ 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
+ float dX = (float)x - lastClickX;
+ 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;
+ if (button == 0)
+ {
+ //app.DebugPrintf("handleMouseClick - Player %d is swinging\n",GetXboxPad());
+ swing();
+ }
+
+ bool mayUse = true;
+
+ // 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;
+
+
+ shared_ptr<MultiplayerLocalPlayer> mplp = dynamic_pointer_cast<MultiplayerLocalPlayer>( shared_from_this() );
+
+ if(mplp && mplp->connection) mplp->StopSleeping();
+
+ }
+ // 4J Stu - We should not accept any input while asleep, except the above to wake up
+ if(isSleeping() && level != NULL && level->isClientSide)
+ {
+ return false;
+ }
+
+ shared_ptr<ItemInstance> oldItem = inventory->getSelected();
+
+ 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)
+ {
+ // 4J-PB - if we milk a cow here, and end up with a bucket of milk, the if (mayUse && button == 1) further down will
+ // then empty our bucket if we're pointing at a tile
+ // It looks like interact really should be returning a result so we can check this, but it's possibly just the
+ // 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
+ shared_ptr<ItemInstance> item = inventory->getSelected();
+ 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
+ {
+ shared_ptr<ItemInstance> item = oldItem;
+ 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)
+ {
+ shared_ptr<ItemInstance> item = inventory->getSelected();
+ 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)*/ )
+ {
+ shared_ptr<ItemInstance> selectedItem = inventory->getSelected();
+ 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);
+ }
+ else if(riding != NULL && dynamic_pointer_cast<Minecart>(riding) != NULL)
+ {
+ app.SetRichPresenceContext(m_iPad,CONTEXT_GAME_STATE_RIDING_MINECART);
+ }
+ else if(riding != NULL && dynamic_pointer_cast<Boat>(riding) != NULL)
+ {
+ app.SetRichPresenceContext(m_iPad,CONTEXT_GAME_STATE_BOATING);
+ }
+ else if(riding != NULL && dynamic_pointer_cast<Pig>(riding) != NULL)
+ {
+ 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;
+}
+
+void LocalPlayer::handleCollectItem(shared_ptr<ItemInstance> item)
+{
+ 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;
+}
+