#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" #include "..\Minecraft.World\CommonStats.h" 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( 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<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<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< 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 sign) { bool success = app.LoadSignEntryMenu(GetXboxPad(), sign ); if( success ) ui.PlayUISFX(eSFX_Press); //minecraft->setScreen(new TextEditScreen(sign)); } bool LocalPlayer::openContainer(shared_ptr 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( 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 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 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 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 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 e) { shared_ptr critParticle = shared_ptr( new CritParticle((Level *)minecraft->level, e) ); critParticle->CritParticlePostConstructor(); minecraft->particleEngine->add(critParticle); } void LocalPlayer::magicCrit(shared_ptr e) { shared_ptr critParticle = shared_ptr( new CritParticle((Level *)minecraft->level, e, eParticleType_magicCrit) ); critParticle->CritParticlePostConstructor(); minecraft->particleEngine->add(critParticle); } void LocalPlayer::take(shared_ptr e, int orgCount) { minecraft->particleEngine->add( shared_ptr( 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) { 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 _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 } } 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 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 ("<x<<","<y<<","<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 mplp = 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; } 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 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 { 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) { 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)*/ ) { 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 && dynamic_pointer_cast(riding) != NULL) { app.SetRichPresenceContext(m_iPad,CONTEXT_GAME_STATE_RIDING_MINECART); } else if(riding != NULL && dynamic_pointer_cast(riding) != NULL) { app.SetRichPresenceContext(m_iPad,CONTEXT_GAME_STATE_BOATING); } else if(riding != NULL && dynamic_pointer_cast(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 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(vectorpAdditionalModelParts) { m_pAdditionalModelParts=pAdditionalModelParts; }