#include "../Platform/stdafx.h" #include "LocalPlayer.h" #include "User.h" #include "../Input/Input.h" #include "../GameState/StatsCounter.h" #include "../Rendering/Particles/ParticleEngine.h" #include "../Rendering/Particles/TakeAnimationParticle.h" #include "../GameState/Options.h" #include "../UI/Screens/TextEditScreen.h" #include "../UI/Screens/ContainerScreen.h" #include "../UI/Screens/CraftingScreen.h" #include "../UI/Screens/FurnaceScreen.h" #include "../UI/Screens/TrapScreen.h" #include "MultiPlayerLocalPlayer.h" #include "../GameState/CreativeMode.h" #include "../Rendering/GameRenderer.h" #include "../Rendering/EntityRenderers/ItemInHandRenderer.h" #include "../../Minecraft.World/AI/Attributes/AttributeInstance.h" #include "../../Minecraft.World/Level/LevelData.h" #include "../../Minecraft.World/Headers/net.minecraft.world.damagesource.h" #include "../../Minecraft.World/Headers/net.minecraft.world.item.h" #include "../../Minecraft.World/Headers/net.minecraft.world.food.h" #include "../../Minecraft.World/Headers/net.minecraft.world.effect.h" #include "../../Minecraft.World/Headers/net.minecraft.world.entity.player.h" #include "../../Minecraft.World/Headers/net.minecraft.world.entity.monster.h" #include "../../Minecraft.World/Entities/ItemEntity.h" #include "../../Minecraft.World/Headers/net.minecraft.world.level.h" #include "../../Minecraft.World/Headers/net.minecraft.world.level.tile.entity.h" #include "../../Minecraft.World/Headers/net.minecraft.world.phys.h" #include "../../Minecraft.World/Headers/net.minecraft.stats.h" #include "../../Minecraft.World/Headers/com.mojang.nbt.h" #include "../../Minecraft.World/Util/Random.h" #include "../../Minecraft.World/Blocks/TileEntities/TileEntity.h" #include "../../Minecraft.World/Util/Mth.h" #include "../UI/Screens/AchievementPopup.h" #include "../Rendering/Particles/CritParticle.h" // 4J : WESTY : Added for new achievements. #include "../../Minecraft.World/Items/Item.h" #include "../../Minecraft.World/Items/MapItem.h" #include "../../Minecraft.World/Blocks/Tile.h" // 4J Stu - Added for tutorial callbacks #include "../Minecraft.h" #include "../../Minecraft.World/Entities/Mobs/Minecart.h" #include "../../Minecraft.World/Entities/Mobs/Boat.h" #include "../../Minecraft.World/Entities/Mobs/Pig.h" #include "../../Minecraft.World/Util/StringHelpers.h" #include "../GameState/Options.h" #include "../../Minecraft.World/Level/Dimensions/Dimension.h" #ifndef _DURANGO #include "../../Minecraft.World/Stats/CommonStats.h" #endif LocalPlayer::LocalPlayer(Minecraft* minecraft, Level* level, User* user, int dimension) : Player(level, user->name) { flyX = flyY = flyZ = 0.0f; // 4J added m_awardedThisSession = 0; sprintTriggerTime = 0; sprintTriggerRegisteredReturn = false; twoJumpsRegistered = false; sprintTime = 0; m_uiInactiveTicks = 0; portalTime = 0.0f; oPortalTime = 0.0f; jumpRidingTicks = 0; jumpRidingScale = 0.0f; 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; // 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; } 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( std::dynamic_pointer_cast( shared_from_this() ) ); // 4J-PB - make it a localplayer input->tick(this); if (isUsingItem() && !isRiding()) { 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; } else if (input->sprintKey) { setSprinting(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 && !input->sprintKey) || !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); } } } if (isRidingJumpable()) { if (jumpRidingTicks < 0) { jumpRidingTicks++; if (jumpRidingTicks == 0) { // reset scale (for gui) jumpRidingScale = 0; } } if (wasJumping && !input->jumping) { // jump release jumpRidingTicks = -10; sendRidingJump(); } else if (!wasJumping && input->jumping) { // jump press jumpRidingTicks = 0; jumpRidingScale = 0; } else if (wasJumping) { // calc jump scale jumpRidingTicks++; if (jumpRidingTicks < 10) { jumpRidingScale = (float)jumpRidingTicks * .1f; } else { jumpRidingScale = .8f + (2.f / ((float)(jumpRidingTicks - 9))) * .1f; } } } else { jumpRidingScale = 0; } 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; AttributeInstance* speed = getAttribute(SharedMonsterAttributes::MOVEMENT_SPEED); targetFov *= (speed->getValue() / abilities.getWalkingSpeed() + 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(std::shared_ptr tileEntity) { #ifdef ENABLE_JAVA_GUIS if (tileEntity->GetType() == eTYPE_SIGNTILEENTITY) { minecraft->setScreen(new TextEditScreen( std::dynamic_pointer_cast(tileEntity))); bool success = true; } #else bool success; if (tileEntity->GetType() == eTYPE_SIGNTILEENTITY) { success = app.LoadSignEntryMenu( GetXboxPad(), std::dynamic_pointer_cast(tileEntity)); } else if (tileEntity->GetType() == eTYPE_COMMANDBLOCKTILEENTITY) { success = app.LoadCommandBlockMenu( GetXboxPad(), std::dynamic_pointer_cast(tileEntity)); } if (success) ui.PlayUISFX(eSFX_Press); #endif } bool LocalPlayer::openContainer(std::shared_ptr container) { #ifdef ENABLE_JAVA_GUIS minecraft->setScreen(new ContainerScreen(inventory, container)); bool success = true; #else bool success = app.LoadContainerMenu(GetXboxPad(), inventory, container); if (success) ui.PlayUISFX(eSFX_Press); #endif // minecraft->setScreen(new ContainerScreen(inventory, container)); return success; } bool LocalPlayer::openHopper(std::shared_ptr container) { // minecraft->setScreen(new HopperScreen(inventory, container)); bool success = app.LoadHopperMenu(GetXboxPad(), inventory, container); if (success) ui.PlayUISFX(eSFX_Press); return success; } bool LocalPlayer::openHopper(std::shared_ptr container) { // minecraft->setScreen(new HopperScreen(inventory, container)); bool success = app.LoadHopperMenu(GetXboxPad(), inventory, container); if (success) ui.PlayUISFX(eSFX_Press); return success; } bool LocalPlayer::openHorseInventory(std::shared_ptr horse, std::shared_ptr container) { // minecraft->setScreen(new HorseInventoryScreen(inventory, container, // horse)); bool success = app.LoadHorseMenu(GetXboxPad(), inventory, container, horse); if (success) ui.PlayUISFX(eSFX_Press); return success; } bool LocalPlayer::startCrafting(int x, int y, int z) { #ifdef ENABLE_JAVA_GUIS minecraft->setScreen(new CraftingScreen(inventory, level, x, y, z)); bool success = true; #else bool success = app.LoadCrafting3x3Menu( GetXboxPad(), std::dynamic_pointer_cast(shared_from_this()), x, y, z); if (success) ui.PlayUISFX(eSFX_Press); #endif // app.LoadXuiCraftMenu(0,inventory, level, x, y, z); // minecraft->setScreen(new CraftingScreen(inventory, level, x, y, z)); return success; } bool LocalPlayer::openFireworks(int x, int y, int z) { bool success = app.LoadFireworksMenu( GetXboxPad(), std::dynamic_pointer_cast(shared_from_this()), x, y, z); if (success) ui.PlayUISFX(eSFX_Press); return success; } bool LocalPlayer::startEnchanting(int x, int y, int z, const std::wstring& name) { bool success = app.LoadEnchantingMenu(GetXboxPad(), inventory, x, y, z, level, name); 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) { #ifdef ENABLE_JAVA_GUIS // minecraft.setScreen(new RepairScreen(inventory, level, x, y, z)); // FUCK YOU 4J FIRST AND FOREMOST bool success = true; #else bool success = app.LoadRepairingMenu(GetXboxPad(), inventory, level, x, y, z); if (success) ui.PlayUISFX(eSFX_Press); #endif return success; } bool LocalPlayer::openFurnace(std::shared_ptr furnace) { #ifdef ENABLE_JAVA_GUIS minecraft->setScreen(new FurnaceScreen(inventory, furnace)); bool success = true; #else bool success = app.LoadFurnaceMenu(GetXboxPad(), inventory, furnace); if (success) ui.PlayUISFX(eSFX_Press); #endif return success; } bool LocalPlayer::openBrewingStand( std::shared_ptr brewingStand) { bool success = app.LoadBrewingStandMenu(GetXboxPad(), inventory, brewingStand); if (success) ui.PlayUISFX(eSFX_Press); // minecraft.setScreen(new BrewingStandScreen(inventory, brewingStand)); return success; } bool LocalPlayer::openBeacon(std::shared_ptr beacon) { // minecraft->setScreen(new BeaconScreen(inventory, beacon)); bool success = app.LoadBeaconMenu(GetXboxPad(), inventory, beacon); if (success) ui.PlayUISFX(eSFX_Press); return success; } bool LocalPlayer::openTrap(std::shared_ptr 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(std::shared_ptr traderTarget, const std::wstring& name) { bool success = app.LoadTradingMenu(GetXboxPad(), inventory, traderTarget, level, name); if (success) ui.PlayUISFX(eSFX_Press); // minecraft.setScreen(new MerchantScreen(inventory, traderTarget, level)); return success; } void LocalPlayer::crit(std::shared_ptr e) { std::shared_ptr critParticle = std::shared_ptr( new CritParticle((Level*)minecraft->level, e)); critParticle->CritParticlePostConstructor(); minecraft->particleEngine->add(critParticle); } void LocalPlayer::magicCrit(std::shared_ptr e) { std::shared_ptr critParticle = std::shared_ptr( new CritParticle((Level*)minecraft->level, e, eParticleType_magicCrit)); critParticle->CritParticlePostConstructor(); minecraft->particleEngine->add(critParticle); } void LocalPlayer::take(std::shared_ptr e, int orgCount) { minecraft->particleEngine->add( std::shared_ptr(new TakeAnimationParticle( (Level*)minecraft->level, e, shared_from_this(), -0.5f))); } void LocalPlayer::chat(const std::wstring& message) {} bool LocalPlayer::isSneaking() { return input->sneaking && !m_isSleeping; } void LocalPlayer::hurtTo(float newHealth, ETelemetryChallenges damageSource) { float 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->getHealth() <= 0) { int deathTime = (int)(level->getGameTime() % 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( std::dynamic_pointer_cast(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 #ifdef ENABLE_JAVA_GUIS minecraft->achievementPopup->popup(ach); #endif // 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::wool_Id, i)) justPickedupWool = true; if (justPickedupWool) { unsigned int woolCount = 0; for (unsigned int i = 0; i < 16; i++) { if (pStats->getTotalValue( GenericStats::itemsCollected(Tile::wool_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; } // 4J: removed // void LocalPlayer::sendMessage(ChatMessageComponent *message) //{ // minecraft->gui->getChat()->addMessage(message.toString(true)); //} Pos LocalPlayer::getCommandSenderWorldPosition() { return new Pos(floor(x + .5), floor(y + .5), floor(z + .5)); } std::shared_ptr LocalPlayer::getCarriedItem() { return inventory->getSelected(); } void LocalPlayer::playSound(int soundId, float volume, float pitch) { level->playLocalSound(x, y - heightOffset, z, soundId, volume, pitch, false); } bool LocalPlayer::isRidingJumpable() { return riding != NULL && riding->GetType() == eTYPE_HORSE; } float LocalPlayer::getJumpRidingScale() { return jumpRidingScale; } void LocalPlayer::sendRidingJump() {} bool LocalPlayer::hasPermission(EGameCommand command) { return level->getLevelData()->getAllowCommands(); } void LocalPlayer::onCrafted(std::shared_ptr item) { if (minecraft->localgameModes[m_iPad] != NULL) { TutorialMode* gameMode = (TutorialMode*)minecraft->localgameModes[m_iPad]; gameMode->getTutorial()->onCrafted(item); } } void LocalPlayer::setAndBroadcastCustomSkin(std::uint32_t skinId) { setCustomSkin(skinId); } void LocalPlayer::setAndBroadcastCustomCape(std::uint32_t capeId) { setCustomCape(capeId); } // 4J TODO - Remove #include "../../Minecraft.World/Level/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; std::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) std::cout << "O"; else for (unsigned int y = 127; y > 0; y--) { int t = cc->getTile(x, y, z); if (flagTileType != 0 && t == flagTileType) { std::cout << "@"; break; } else if (t != 0 && t < 10) { std::cout << t; break; } else if (t > 0) { std::cout << "#"; break; } } } std::cout << "\n"; } std::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 (mayDestroyBlockAt(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; std::shared_ptr mplp = std::dynamic_pointer_cast( 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; } std::shared_ptr 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 std::shared_ptr 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 { std::shared_ptr 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) { std::shared_ptr 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)*/) { std::shared_ptr 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) && riding->instanceof(eTYPE_MINECART)) { app.SetRichPresenceContext(m_iPad, CONTEXT_GAME_STATE_RIDING_MINECART); } else if ((riding != NULL) && riding->instanceof(eTYPE_BOAT)) { app.SetRichPresenceContext(m_iPad, CONTEXT_GAME_STATE_BOATING); } else if ((riding != NULL) && riding->instanceof(eTYPE_PIG)) { 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(std::shared_ptr 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( std::vector pAdditionalModelParts) { m_pAdditionalModelParts = pAdditionalModelParts; }