diff options
| author | daoge_cmd <3523206925@qq.com> | 2026-03-01 12:16:08 +0800 |
|---|---|---|
| committer | daoge_cmd <3523206925@qq.com> | 2026-03-01 12:16:08 +0800 |
| commit | b691c43c44ff180d10e7d4a9afc83b98551ff586 (patch) | |
| tree | 3e9849222cbc6ba49f2f1fc6e5fe7179632c7390 /Minecraft.Client/LocalPlayer.cpp | |
| parent | def8cb415354ac390b7e89052a50605285f1aca9 (diff) | |
Initial commit
Diffstat (limited to 'Minecraft.Client/LocalPlayer.cpp')
| -rw-r--r-- | Minecraft.Client/LocalPlayer.cpp | 1616 |
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; +} + |
