This code was not tested and breaks in Release builds, reverting to restore
functionality of the nightly. All in-game menus do not work and generating
a world crashes.
This reverts commit a9be52c41a.
1699 lines
47 KiB
C++
1699 lines
47 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\AttributeInstance.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\net.minecraft.world.entity.monster.h"
|
|
#include "..\Minecraft.World\ItemEntity.h"
|
|
#include "..\Minecraft.World\net.minecraft.world.level.h"
|
|
#include "..\Minecraft.World\net.minecraft.world.level.tile.entity.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\TileEntity.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, 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();
|
|
|
|
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() && !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;
|
|
|
|
bool forwardEnoughToTriggerSprint = input->ya >= runTreshold;
|
|
bool forwardReturnedToDeadzone = input->ya == 0.0f;
|
|
bool forwardEnoughToContinueSprint = input->ya >= runTreshold;
|
|
|
|
// 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 && forwardEnoughToTriggerSprint )
|
|
{
|
|
if (sprintTriggerTime == 0)
|
|
{
|
|
sprintTriggerTime = 7;
|
|
sprintTriggerRegisteredReturn = false;
|
|
}
|
|
else
|
|
{
|
|
if( sprintTriggerRegisteredReturn )
|
|
{
|
|
setSprinting(true);
|
|
sprintTriggerTime = 0;
|
|
sprintTriggerRegisteredReturn = false;
|
|
}
|
|
}
|
|
}
|
|
else if( ( sprintTriggerTime > 0 ) && forwardReturnedToDeadzone ) // zero sprintForward here signifies that we have returned to the deadzone
|
|
{
|
|
sprintTriggerRegisteredReturn = true;
|
|
}
|
|
}
|
|
if (isSneaking()) sprintTriggerTime = 0;
|
|
#ifdef _WINDOWS64
|
|
if (input->sprinting && !isSprinting() && 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() && (!forwardEnoughToContinueSprint || !enoughFoodToSprint || isSneaking() || isUsingItem()))
|
|
{
|
|
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 = 7; // the 4J team changed it to 10 because of additional requirements to initiate flight
|
|
twoJumpsRegistered = false;
|
|
}
|
|
else
|
|
{
|
|
twoJumpsRegistered = true;
|
|
}
|
|
}
|
|
else if(jumpTriggerTime > 0 && twoJumpsRegistered) //the 4J team checked if the player was NOT jumping after the two jumps, aka had let go of the jump button
|
|
{
|
|
#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(shared_ptr<TileEntity> tileEntity)
|
|
{
|
|
bool success;
|
|
|
|
if (tileEntity->GetType() == eTYPE_SIGNTILEENTITY)
|
|
{
|
|
success = app.LoadSignEntryMenu(GetXboxPad(), dynamic_pointer_cast<SignTileEntity>(tileEntity));
|
|
}
|
|
else if (tileEntity->GetType() == eTYPE_COMMANDBLOCKTILEENTITY)
|
|
{
|
|
success = app.LoadCommandBlockMenu(GetXboxPad(), dynamic_pointer_cast<CommandBlockEntity>(tileEntity));
|
|
}
|
|
|
|
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::openHopper(shared_ptr<HopperTileEntity> 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(shared_ptr<MinecartHopper> 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(shared_ptr<EntityHorse> horse, shared_ptr<Container> 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)
|
|
{
|
|
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::openFireworks(int x, int y, int z)
|
|
{
|
|
bool success = app.LoadFireworksMenu(GetXboxPad(), dynamic_pointer_cast<LocalPlayer>( shared_from_this() ), x, y, z );
|
|
if( success ) ui.PlayUISFX(eSFX_Press);
|
|
return success;
|
|
}
|
|
|
|
bool LocalPlayer::startEnchanting(int x, int y, int z, const 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)
|
|
{
|
|
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::openBeacon(shared_ptr<BeaconTileEntity> 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(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, const 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(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(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(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::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));
|
|
}
|
|
|
|
shared_ptr<ItemInstance> 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(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(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;
|
|
|
|
|
|
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) && 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(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;
|
|
}
|