LCEMP/Minecraft.Client/LocalPlayer.cpp
coah 7da64d57a8 add keyboard/mouse controls for menus and gameplay
wired up keyboard and mouse input for menu navigation, inventory cursor, crafting, and general UI interaction. added WASD movement, mouse look, left ctrl sprint, left shift sneak, and all the keybinds for gameplay. also fixed the sound engine crashing when it cant find a sound asset, and set up the x64 build with proper post-build steps for dlls and redist
2026-03-01 20:02:02 -06:00

1623 lines
46 KiB
C++

#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;
#ifdef _WINDOWS64
if (input->sprinting && onGround && enoughFoodToSprint && !isUsingItem() && !hasEffect(MobEffect::blindness) && !isSneaking())
{
setSprinting(true);
}
#endif
// 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;
}