Files
Loki Rautio 087b7e7abf Revert "Project modernization (#630)"
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.
2026-03-07 21:12:22 -06:00

3156 lines
79 KiB
C++

// 4J TODO
// All the instanceof s from Java have been converted to dynamic_cast in this file
// Once all the classes are finished it may be that we do not need to use dynamic_cast
// for every test and a simple virtual function should suffice. We probably only need
// dynamic_cast to find one of the classes that an object derives from, and not to find
// the derived class itself (which should own the virtual GetType function)
#include "stdafx.h"
#include "JavaMath.h"
#include "net.minecraft.h"
#include "net.minecraft.world.h"
#include "net.minecraft.stats.h"
#include "net.minecraft.world.level.h"
#include "net.minecraft.world.level.chunk.h"
#include "net.minecraft.world.phys.h"
#include "net.minecraft.world.entity.h"
#include "net.minecraft.world.entity.ai.attributes.h"
#include "net.minecraft.world.entity.animal.h"
#include "net.minecraft.world.entity.boss.h"
#include "net.minecraft.world.entity.monster.h"
#include "net.minecraft.world.entity.item.h"
#include "net.minecraft.world.item.h"
#include "net.minecraft.world.item.enchantment.h"
#include "net.minecraft.world.level.dimension.h"
#include "net.minecraft.world.level.material.h"
#include "net.minecraft.world.level.tile.h"
#include "net.minecraft.world.level.tile.entity.h"
#include "net.minecraft.world.scores.h"
#include "net.minecraft.world.scores.criteria.h"
#include "net.minecraft.world.entity.projectile.h"
#include "net.minecraft.world.inventory.h"
#include "net.minecraft.world.damagesource.h"
#include "net.minecraft.world.effect.h"
#include "net.minecraft.world.food.h"
#include "Inventory.h"
#include "Player.h"
#include "ParticleTypes.h"
#include "..\Minecraft.Client\Textures.h"
#include "..\Minecraft.Client\LocalPlayer.h"
#include "..\Minecraft.Client\HumanoidModel.h"
#include "SoundTypes.h"
void Player::_init()
{
registerAttributes();
setHealth(getMaxHealth());
inventory = shared_ptr<Inventory>( new Inventory( this ) );
userType = 0;
oBob = bob = 0.0f;
xCloakO = yCloakO = zCloakO = 0.0;
xCloak = yCloak = zCloak = 0.0;
m_isSleeping = false;
customTextureUrl = L"";
customTextureUrl2 = L"";
m_uiPlayerCurrentSkin=0;
bedPosition = NULL;
sleepCounter = 0;
deathFadeCounter=0;
bedOffsetX = bedOffsetY = bedOffsetZ = 0.0f;
stats = NULL;
respawnPosition = NULL;
respawnForced = false;
minecartAchievementPos = NULL;
fishing = nullptr;
distanceWalk = distanceSwim = distanceFall = distanceClimb = distanceMinecart = distanceBoat = distancePig = 0;
m_uiDebugOptions=0L;
jumpTriggerTime = 0;
takeXpDelay = 0;
experienceLevel = totalExperience = 0;
experienceProgress = 0.0f;
useItem = nullptr;
useItemDuration = 0;
defaultWalkSpeed = 0.1f;
defaultFlySpeed = 0.02f;
lastLevelUpTime = 0;
m_uiGamePrivileges = 0;
m_ppAdditionalModelParts=NULL;
m_bCheckedForModelParts=false;
m_bCheckedDLCForModelParts=false;
#if defined(__PS3__) || defined(__ORBIS__)
m_ePlayerNameValidState=ePlayerNameValid_NotSet;
#endif
enderChestInventory = shared_ptr<PlayerEnderChestContainer>(new PlayerEnderChestContainer());
m_bAwardedOnARail=false;
}
Player::Player(Level *level, const wstring &name) : LivingEntity( level )
{
// 4J Stu - This function call had to be moved here from the Entity ctor to ensure that
// the derived version of the function is called
this->defineSynchedData();
this->name = name;
_init();
MemSect(11);
inventoryMenu = new InventoryMenu(inventory, !level->isClientSide, this);
MemSect(0);
containerMenu = inventoryMenu;
heightOffset = 1.62f;
Pos *spawnPos = level->getSharedSpawnPos();
moveTo(spawnPos->x + 0.5, spawnPos->y + 1, spawnPos->z + 0.5, 0, 0);
delete spawnPos;
rotOffs = 180;
flameTime = 20;
m_skinIndex = eDefaultSkins_Skin0;
m_playerIndex = 0;
m_dwSkinId = 0;
m_dwCapeId = 0;
// 4J Added
m_xuid = INVALID_XUID;
m_OnlineXuid = INVALID_XUID;
//m_bShownOnMaps = true;
setShowOnMaps(app.GetGameHostOption(eGameHostOption_Gamertags)!=0?true:false);
m_bIsGuest = false;
#ifndef _XBOX_ONE
// 4J: Set UUID to name on none-XB1 consoles, may change in future but for now
// ownership of animals on these consoles is done by name
setUUID(name);
#endif
}
Player::~Player()
{
// TODO 4J
//printf("A player has been destroyed.\n");
delete inventoryMenu;
// 4J Stu - Fix for #10938 - CRASH - Game hardlocks when client has an open chest and Xbox Guide while host exits without saving.
// If the container menu is not the inventory menu, then the player has a menu open. These get deleted when the xui scene
// is destroyed, so we can not delete it here
//if( containerMenu != inventoryMenu ) delete containerMenu;
}
void Player::registerAttributes()
{
LivingEntity::registerAttributes();
getAttributes()->registerAttribute(SharedMonsterAttributes::ATTACK_DAMAGE)->setBaseValue(1);
}
void Player::defineSynchedData()
{
LivingEntity::defineSynchedData();
entityData->define(DATA_PLAYER_FLAGS_ID, (byte) 0);
entityData->define(DATA_PLAYER_ABSORPTION_ID, (float) 0);
entityData->define(DATA_SCORE_ID, (int) 0);
}
shared_ptr<ItemInstance> Player::getUseItem()
{
return useItem;
}
int Player::getUseItemDuration()
{
return useItemDuration;
}
bool Player::isUsingItem()
{
return useItem != NULL;
}
int Player::getTicksUsingItem()
{
if (isUsingItem())
{
return useItem->getUseDuration() - useItemDuration;
}
return 0;
}
void Player::releaseUsingItem()
{
if (useItem != NULL)
{
useItem->releaseUsing(level, dynamic_pointer_cast<Player>( shared_from_this() ), useItemDuration);
// 4J Stu - Fix for various bugs where an incorrect bow was displayed when it broke (#70859,#93972,#93974)
if (useItem->count == 0)
{
removeSelectedItem();
}
}
stopUsingItem();
}
void Player::stopUsingItem()
{
useItem = nullptr;
useItemDuration = 0;
if (!level->isClientSide)
{
setUsingItemFlag(false);
}
}
bool Player::isBlocking()
{
return isUsingItem() && Item::items[useItem->id]->getUseAnimation(useItem) == UseAnim_block;
}
// 4J Stu - Added for things that should only be ticked once per simulation frame
void Player::updateFrameTick()
{
if (useItem != NULL)
{
shared_ptr<ItemInstance> item = inventory->getSelected();
// 4J Stu - Fix for #45508 - TU5: Gameplay: Eating one piece of food will result in a second piece being eaten as well
// Original code was item != useItem. Changed this now to use the equals function, and add the NULL check as well for the other possible not equals (useItem is not NULL if we are here)
// This is because the useItem and item could be different objects due to an inventory update from the server, but still be the same item (with the same id,count and auxvalue)
if (item == NULL || !item->equals(useItem) )
{
stopUsingItem();
}
else
{
if (useItemDuration <= 25 && useItemDuration % 4 == 0)
{
spawnEatParticles(item, 5);
}
if (--useItemDuration == 0)
{
if (!level->isClientSide)
{
completeUsingItem();
}
}
}
}
if (takeXpDelay > 0) takeXpDelay--;
if (isSleeping())
{
sleepCounter++;
if (sleepCounter > SLEEP_DURATION)
{
sleepCounter = SLEEP_DURATION;
}
if (!level->isClientSide)
{
if (!checkBed())
{
stopSleepInBed(true, true, false);
}
else if (level->isDay())
{
stopSleepInBed(false, true, true);
}
}
}
else if (sleepCounter > 0)
{
sleepCounter++;
if (sleepCounter >= (SLEEP_DURATION + WAKE_UP_DURATION))
{
sleepCounter = 0;
}
}
if(!isAlive())
{
deathFadeCounter++;
if (deathFadeCounter > DEATHFADE_DURATION)
{
deathFadeCounter = DEATHFADE_DURATION;
}
}
}
void Player::tick()
{
if(level->isClientSide)
{
// 4J Stu - Server player calls this differently so that it only happens once per simulation tick
updateFrameTick();
}
LivingEntity::tick();
if (!level->isClientSide)
{
if (containerMenu != NULL && !containerMenu->stillValid( dynamic_pointer_cast<Player>( shared_from_this() ) ))
{
closeContainer();
containerMenu = inventoryMenu;
}
}
if (isOnFire() && (abilities.invulnerable || hasInvulnerablePrivilege() ) )
{
clearFire();
}
xCloakO = xCloak;
yCloakO = yCloak;
zCloakO = zCloak;
double xca = x - xCloak;
double yca = y - yCloak;
double zca = z - zCloak;
double m = 10;
if (xca > m) xCloakO = xCloak = x;
if (zca > m) zCloakO = zCloak = z;
if (yca > m) yCloakO = yCloak = y;
if (xca < -m) xCloakO = xCloak = x;
if (zca < -m) zCloakO = zCloak = z;
if (yca < -m) yCloakO = yCloak = y;
xCloak += xca * 0.25;
zCloak += zca * 0.25;
yCloak += yca * 0.25;
if (riding == NULL)
{
if( minecartAchievementPos != NULL )
{
delete minecartAchievementPos;
minecartAchievementPos = NULL;
}
}
if (!level->isClientSide)
{
foodData.tick(dynamic_pointer_cast<Player>(shared_from_this()));
}
// 4J Stu Debugging
if (!level->isClientSide)
{
static int count = 0;
if( count++ == 100 )
{
#if 0
#ifdef _WINDOWS64
// Drop some items so we have them in inventory to play with
this->drop( shared_ptr<ItemInstance>( new ItemInstance(Tile::recordPlayer) ) );
this->drop( shared_ptr<ItemInstance>( new ItemInstance(Item::map) ) );
this->drop( shared_ptr<ItemInstance>( new ItemInstance(Item::record_01) ) );
this->drop( shared_ptr<ItemInstance>( new ItemInstance(Item::record_02) ) );
this->drop( shared_ptr<ItemInstance>(new ItemInstance( Item::pickAxe_diamond, 1 )) );
#endif
#ifdef __PS3__
// #ifdef _DEBUG
// // Drop some items so we have them in inventory to play with
// this->drop( shared_ptr<ItemInstance>( new ItemInstance(Tile::recordPlayer) ) );
// this->drop( shared_ptr<ItemInstance>( new ItemInstance(Item::map) ) );
// this->drop( shared_ptr<ItemInstance>( new ItemInstance(Item::record_01) ) );
// this->drop( shared_ptr<ItemInstance>( new ItemInstance(Item::record_02) ) );
// this->drop( shared_ptr<ItemInstance>(new ItemInstance( Item::pickAxe_diamond, 1 )) );
// #endif
#endif
#ifdef _DURANGO
// Drop some items so we have them in inventory to play with
this->drop( shared_ptr<ItemInstance>( new ItemInstance(Tile::recordPlayer) ) );
this->drop( shared_ptr<ItemInstance>( new ItemInstance(Item::map) ) );
this->drop( shared_ptr<ItemInstance>( new ItemInstance(Item::record_01) ) );
this->drop( shared_ptr<ItemInstance>( new ItemInstance(Item::record_02) ) );
this->drop( shared_ptr<ItemInstance>(new ItemInstance( Item::pickAxe_diamond, 1 )) );
#endif
#endif
// 4J-PB - Throw items out at the start of the level
//this->drop( new ItemInstance( Item::pickAxe_diamond, 1 ) );
//this->drop( new ItemInstance( Tile::workBench, 1 ) );
//this->drop( new ItemInstance( Tile::treeTrunk, 8 ) );
//this->drop( shared_ptr<ItemInstance>( new ItemInstance( Item::milk, 3 ) ) );
//this->drop( shared_ptr<ItemInstance>( new ItemInstance( Item::sugar, 2 ) ) );
//this->drop( new ItemInstance( Tile::stoneBrick, 8 ) );
//this->drop( shared_ptr<ItemInstance>( new ItemInstance( Item::wheat, 3 ) ) );
//this->drop( shared_ptr<ItemInstance>( new ItemInstance( Item::egg, 1 ) ) );
//this->drop( new ItemInstance( Item::bow, 1 ) );
//this->drop( new ItemInstance( Item::arrow, 10 ) );
//this->drop( shared_ptr<ItemInstance>( new ItemInstance( Item::saddle, 10 ) ) );
//this->drop( shared_ptr<ItemInstance>( new ItemInstance( Tile::fence, 64 ) ) );
//this->drop( shared_ptr<ItemInstance>( new ItemInstance( Tile::fence, 64 ) ) );
//this->drop( shared_ptr<ItemInstance>( new ItemInstance( Tile::fence, 64 ) ) );
//shared_ptr<Mob> mob = dynamic_pointer_cast<Mob>(Pig::_class->newInstance( level ));
//mob->moveTo(x+1, y, z+1, level->random->nextFloat() * 360, 0);
//level->addEntity(mob);
// 4J : WESTY : Spawn some wolves to befriend!
/*
shared_ptr<Mob> mob1 = dynamic_pointer_cast<Mob>(Wolf::_class->newInstance( level ));
mob1->moveTo(x+1, y, z+1, level->random->nextFloat() * 360, 0);
level->addEntity(mob1);
shared_ptr<Mob> mob2 = dynamic_pointer_cast<Mob>(Wolf::_class->newInstance( level ));
mob2->moveTo(x+2, y, z+1, level->random->nextFloat() * 360, 0);
level->addEntity(mob2);
shared_ptr<Mob> mob3 = dynamic_pointer_cast<Mob>(Wolf::_class->newInstance( level ));
mob3->moveTo(x+1, y, z+2, level->random->nextFloat() * 360, 0);
level->addEntity(mob3);
shared_ptr<Mob> mob4 = dynamic_pointer_cast<Mob>(Wolf::_class->newInstance( level ));
mob4->moveTo(x+3, y, z+1, level->random->nextFloat() * 360, 0);
level->addEntity(mob4);
shared_ptr<Mob> mob5 = dynamic_pointer_cast<Mob>(Wolf::_class->newInstance( level ));
mob5->moveTo(x+1, y, z+3, level->random->nextFloat() * 360, 0);
level->addEntity(mob5);
*/
// inventory.add(new ItemInstance(Item.potion, 1, PotionBrewing.THROWABLE_MASK | 0xc));
// addEffect(new MobEffectInstance(MobEffect.blindness.id, 60));
// increaseXp(10);
{
// ItemInstance itemInstance = new ItemInstance(Item.pickAxe_diamond);
// itemInstance.enchant(Enchantment.diggingBonus, 3);
// inventory.add(itemInstance);
}
}
#if 0
// 4J Stu - This makes a tunnel with a powered track just over length to get the On A Rail achievement
// It needs a few items at the start to get you going (a level and some powered rails) and of course a
// minecart. For some reason some of the torches come off so it will also need some fixing along the way.
static bool madeTrack = false;
if( !madeTrack )
{
this->drop( shared_ptr<ItemInstance>( new ItemInstance( Item::minecart, 1 ) ) );
this->drop( shared_ptr<ItemInstance>( new ItemInstance( Tile::goldenRail, 10 ) ) );
this->drop( shared_ptr<ItemInstance>( new ItemInstance( Tile::lever, 10 ) ) );
int poweredCount = 0;
for(int i = 10; i < 2800; ++i)
{
level->setTileAndData(x+i,y-1,z-2,Tile::quartzBlock_Id,0,Tile::UPDATE_CLIENTS);
level->setTileAndData(x+i,y,z-2,Tile::quartzBlock_Id,0,Tile::UPDATE_CLIENTS);
level->setTileAndData(x+i,y+1,z-2,Tile::quartzBlock_Id,0,Tile::UPDATE_CLIENTS);
level->setTileAndData(x+i,y+2,z-2,Tile::glowstone_Id,0,Tile::UPDATE_CLIENTS);
level->setTileAndData(x+i,y+3,z-2,Tile::quartzBlock_Id,0,Tile::UPDATE_CLIENTS);
level->setTileAndData(x+i,y-1,z-1,Tile::stoneBrick_Id,0,Tile::UPDATE_CLIENTS);
if(i%20 == 0)
{
level->setTileAndData(x+i,y,z-1,Tile::redstoneTorch_on_Id,0,Tile::UPDATE_CLIENTS);
poweredCount = 4;
}
else
{
level->setTileAndData(x+i,y,z-1,0,0,Tile::UPDATE_CLIENTS);
}
level->setTileAndData(x+i,y+1,z-1,0,0,Tile::UPDATE_CLIENTS);
level->setTileAndData(x+i,y+2,z-1,0,0,Tile::UPDATE_CLIENTS);
level->setTileAndData(x+i,y+3,z-1,0,0,Tile::UPDATE_CLIENTS);
level->setTileAndData(x+i,y-1,z,Tile::stoneBrick_Id,0,Tile::UPDATE_CLIENTS);
if(poweredCount>0)
{
level->setTileAndData(x+i,y,z,Tile::goldenRail_Id,0,Tile::UPDATE_CLIENTS);
--poweredCount;
}
else
{
level->setTileAndData(x+i,y,z,Tile::rail_Id,0,Tile::UPDATE_CLIENTS);
}
level->setTileAndData(x+i,y+1,z,0,0,Tile::UPDATE_CLIENTS);
level->setTileAndData(x+i,y+2,z,0,0,Tile::UPDATE_CLIENTS);
level->setTileAndData(x+i,y+3,z,0,0,Tile::UPDATE_CLIENTS);
level->setTileAndData(x+i,y-1,z+1,Tile::stoneBrick_Id,0,Tile::UPDATE_CLIENTS);
if((i+5)%20 == 0)
{
level->setTileAndData(x+i,y,z+1,Tile::torch_Id,0,Tile::UPDATE_CLIENTS);
}
else
{
level->setTileAndData(x+i,y,z+1,0,0,Tile::UPDATE_CLIENTS);
}
level->setTileAndData(x+i,y+1,z+1,0,0,Tile::UPDATE_CLIENTS);
level->setTileAndData(x+i,y+2,z+1,0,0,Tile::UPDATE_CLIENTS);
level->setTileAndData(x+i,y+3,z+1,0,0,Tile::UPDATE_CLIENTS);
level->setTileAndData(x+i,y-1,z+2,Tile::quartzBlock_Id,0,Tile::UPDATE_CLIENTS);
level->setTileAndData(x+i,y,z+2,Tile::quartzBlock_Id,0,Tile::UPDATE_CLIENTS);
level->setTileAndData(x+i,y+1,z+2,Tile::quartzBlock_Id,0,Tile::UPDATE_CLIENTS);
level->setTileAndData(x+i,y+2,z+2,Tile::glowstone_Id,0,Tile::UPDATE_CLIENTS);
level->setTileAndData(x+i,y+3,z+2,Tile::quartzBlock_Id,0,Tile::UPDATE_CLIENTS);
}
madeTrack = true;
}
#endif
}
//End 4J sTU
}
int Player::getPortalWaitTime()
{
return abilities.invulnerable ? 0 : SharedConstants::TICKS_PER_SECOND * 4;
}
int Player::getDimensionChangingDelay()
{
return SharedConstants::TICKS_PER_SECOND / 2;
}
void Player::playSound(int iSound, float volume, float pitch)
{
// this sound method will play locally for the local player, and
// broadcast to remote players
level->playPlayerSound(dynamic_pointer_cast<Player>(shared_from_this()), iSound, volume, pitch);
}
void Player::spawnEatParticles(shared_ptr<ItemInstance> useItem, int count)
{
if (useItem->getUseAnimation() == UseAnim_drink)
{
playSound(eSoundType_RANDOM_DRINK, 0.5f, level->random->nextFloat() * 0.1f + 0.9f);
}
if (useItem->getUseAnimation() == UseAnim_eat)
{
for (int i = 0; i < count; i++)
{
Vec3 *d = Vec3::newTemp((random->nextFloat() - 0.5) * 0.1, Math::random() * 0.1 + 0.1, 0);
d->xRot(-xRot * PI / 180);
d->yRot(-yRot * PI / 180);
Vec3 *p = Vec3::newTemp((random->nextFloat() - 0.5) * 0.3, -random->nextFloat() * 0.6 - 0.3, 0.6);
p->xRot(-xRot * PI / 180);
p->yRot(-yRot * PI / 180);
p = p->add(x, y + getHeadHeight(), z);
level->addParticle(PARTICLE_ICONCRACK(useItem->getItem()->id,0), p->x, p->y, p->z, d->x, d->y + 0.05, d->z);
}
// 4J Stu - Was L"mob.eat" which doesnt exist
playSound(eSoundType_RANDOM_EAT, 0.5f + 0.5f * random->nextInt(2), (random->nextFloat() - random->nextFloat()) * 0.2f + 1.0f);
}
}
void Player::completeUsingItem()
{
if (useItem != NULL)
{
spawnEatParticles(useItem, 16);
int oldCount = useItem->count;
shared_ptr<ItemInstance> itemInstance = useItem->useTimeDepleted(level, dynamic_pointer_cast<Player>(shared_from_this()));
if (itemInstance != useItem || (itemInstance != NULL && itemInstance->count != oldCount))
{
inventory->items[inventory->selected] = itemInstance;
if (itemInstance->count == 0)
{
inventory->items[inventory->selected] = nullptr;
}
}
stopUsingItem();
}
}
void Player::handleEntityEvent(byte id)
{
if (id == EntityEvent::USE_ITEM_COMPLETE)
{
completeUsingItem();
}
else
{
LivingEntity::handleEntityEvent(id);
}
}
bool Player::isImmobile()
{
return getHealth() <= 0 || isSleeping();
}
void Player::closeContainer()
{
containerMenu = inventoryMenu;
}
void Player::ride(shared_ptr<Entity> e)
{
if (riding != NULL && e == NULL)
{
if (!level->isClientSide) findStandUpPosition(riding);
if (riding != NULL)
{
riding->rider = weak_ptr<Entity>();
}
riding = nullptr;
return;
}
this->abilities.flying = false;
LivingEntity::ride(e);
}
void Player::setPlayerDefaultSkin(EDefaultSkins skin)
{
#ifndef _CONTENT_PACKAGE
wprintf(L"Setting default skin to %d for player %ls\n", skin, name.c_str() );
#endif
m_skinIndex = skin;
}
void Player::setCustomSkin(DWORD skinId)
{
#ifndef _CONTENT_PACKAGE
wprintf(L"Attempting to set skin to %08X for player %ls\n", skinId, name.c_str() );
#endif
EDefaultSkins playerSkin = eDefaultSkins_ServerSelected;
// reset the idle
setIsIdle(false);
setAnimOverrideBitmask(getSkinAnimOverrideBitmask(skinId));
if( !GET_IS_DLC_SKIN_FROM_BITMASK(skinId) )
{
// GET_UGC_SKIN_ID_FROM_BITMASK will always be zero - this was for a possible custom skin editor skin
DWORD ugcSkinIndex = GET_UGC_SKIN_ID_FROM_BITMASK(skinId);
DWORD defaultSkinIndex = GET_DEFAULT_SKIN_ID_FROM_BITMASK(skinId);
if( ugcSkinIndex == 0 && defaultSkinIndex > 0 )
{
playerSkin = (EDefaultSkins) defaultSkinIndex;
}
}
if( playerSkin == eDefaultSkins_ServerSelected)
{
playerSkin = (EDefaultSkins)(m_playerIndex + 1);
}
// We always set a default skin, since we may be waiting for the player's custom skin to be transmitted
setPlayerDefaultSkin( playerSkin );
m_dwSkinId = skinId;
this->customTextureUrl = app.getSkinPathFromId(skinId);
// set the new player additional boxes
/*vector<ModelPart *> *pvModelParts=app.GetAdditionalModelParts(m_dwSkinId);
if(pvModelParts==NULL)
{
// we don't have the data from the dlc skin yet
app.DebugPrintf("Couldn't get model parts for skin %X\n",m_dwSkinId);
// do we have it from the DLC pack?
DLCSkinFile *pDLCSkinFile = app.m_dlcManager.getSkinFile(this->customTextureUrl);
if(pDLCSkinFile!=NULL)
{
DWORD dwBoxC=pDLCSkinFile->getAdditionalBoxesCount();
if(dwBoxC!=0)
{
app.DebugPrintf("Got model parts from DLCskin for skin %X\n",m_dwSkinId);
pvModelParts=app.SetAdditionalSkinBoxes(m_dwSkinId,pDLCSkinFile->getAdditionalBoxes());
this->SetAdditionalModelParts(pvModelParts);
}
else
{
this->SetAdditionalModelParts(NULL);
}
app.SetAnimOverrideBitmask(pDLCSkinFile->getSkinID(),pDLCSkinFile->getAnimOverrideBitmask());
}
else
{
this->SetAdditionalModelParts(NULL);
}
}
else
{
app.DebugPrintf("Got model parts from app.GetAdditionalModelParts for skin %X\n",m_dwSkinId);
this->SetAdditionalModelParts(pvModelParts);
}*/
// reset the check for model parts
m_bCheckedForModelParts=false;
m_bCheckedDLCForModelParts=false;
this->SetAdditionalModelParts(NULL);
}
unsigned int Player::getSkinAnimOverrideBitmask(DWORD skinId)
{
unsigned long bitmask = 0L;
if( GET_IS_DLC_SKIN_FROM_BITMASK(skinId) )
{
// Temp check for anim override
switch( GET_DLC_SKIN_ID_FROM_BITMASK(skinId) )
{
case 0x2://SP1_ZOMBIE:
case 0x3://SP1_HEROBRINE:
case 0xc8://SP3_ZOMBIE_PIGMAN:
case 0xc9://SP3_ZOMBIE_HEROBRINE:
case 0x1f8: // SPH_4JMUMMY
case 0x220: // SPH_AOT_MUMMY
case 0x23a: // SPH_CLIMAX_ZOMBIEBUSINESSMAN
case 0x23d: // SPH_CLIMAX_EVILROBOT
case 0x247: // SPH_CLIMAX_ZOMBIE
case 0x194: // SOA_DEADLIGHT_SKINNY_ZOMBIE
case 0x195: // SOA_DEADLIGHT_FEMALE_ZOMBIE
bitmask = 1<<HumanoidModel::eAnim_ArmsOutFront;
break;
case 0x1fa://SPH_GHOST:
bitmask = 1<<HumanoidModel::eAnim_ArmsOutFront | 1<<HumanoidModel::eAnim_NoLegAnim;
break;
case 0x1f4://SPH_GRIMREAPER:
bitmask = 1<<HumanoidModel::eAnim_ArmsDown | 1<<HumanoidModel::eAnim_NoLegAnim;
break;
case 0x1f7: // SPH_4J_FRANKENSTEIN
//bitmask = 1<<HumanoidModel::eAnim_HasIdle;
break;
break;
default:
// This is not one of the prefined skins
// Does the app have an anim override for this skin?
bitmask=app.GetAnimOverrideBitmask(skinId);
break;
}
}
return bitmask;
}
void Player::setXuid(PlayerUID xuid)
{
m_xuid = xuid;
#ifdef _XBOX_ONE
// 4J Stu - For XboxOne (and probably in the future all other platforms) we store a UUID for the player to use as the owner key for tamed animals
// This should just be a string version of the xuid
setUUID( xuid.toString() );
#endif
}
void Player::setCustomCape(DWORD capeId)
{
#ifndef _CONTENT_PACKAGE
wprintf(L"Attempting to set cape to %08X for player %s\n", capeId, name.c_str() );
#endif
m_dwCapeId = capeId;
if(capeId > 0)
{
this->customTextureUrl2 = Player::getCapePathFromId(capeId);
}
else
{
MOJANG_DATA *pMojangData=app.GetMojangDataForXuid(getOnlineXuid());
if(pMojangData)
{
// Cape
if(pMojangData->wchCape)
{
this->customTextureUrl2= pMojangData->wchCape;
}
else
{
if(app.DefaultCapeExists())
{
this->customTextureUrl2= wstring(L"Special_Cape.png");
}
else
{
this->customTextureUrl2= wstring(L"");
}
}
}
else
{
// if there is a custom default cloak, then set it here
if(app.DefaultCapeExists())
{
this->customTextureUrl2= wstring(L"Special_Cape.png");
}
else
{
this->customTextureUrl2 =wstring(L"");
}
}
}
}
DWORD Player::getCapeIdFromPath(const wstring &cape)
{
bool dlcCape = false;
unsigned int capeId = 0;
if(cape.size() >= 14)
{
dlcCape = cape.substr(0,3).compare(L"dlc") == 0;
wstring capeValue = cape.substr(7,cape.size());
capeValue = capeValue.substr(0,capeValue.find_first_of(L'.'));
std::wstringstream ss;
// 4J Stu - dlc skins are numbered using decimal to make it easier for artists/people to number manually
// Everything else is numbered using hex
if(dlcCape)
ss << std::dec << capeValue.c_str();
else
ss << std::hex << capeValue.c_str();
ss >> capeId;
capeId = MAKE_SKIN_BITMASK(dlcCape, capeId);
}
return capeId;
}
wstring Player::getCapePathFromId(DWORD capeId)
{
// 4J Stu - This function maps the encoded DWORD we store in the player profile
// to a filename that is stored as a memory texture and shared between systems in game
wchar_t chars[256];
if( GET_IS_DLC_SKIN_FROM_BITMASK(capeId) )
{
// 4J Stu - DLC skins are numbered using decimal rather than hex to make it easier to number manually
swprintf(chars,256,L"dlccape%08d.png",GET_DLC_SKIN_ID_FROM_BITMASK(capeId));
}
else
{
DWORD ugcCapeIndex = GET_UGC_SKIN_ID_FROM_BITMASK(capeId);
DWORD defaultCapeIndex = GET_DEFAULT_SKIN_ID_FROM_BITMASK(capeId);
if( ugcCapeIndex == 0 )
{
swprintf(chars,256,L"defcape%08X.png",defaultCapeIndex);
}
else
{
swprintf(chars,256,L"ugccape%08X.png",ugcCapeIndex);
}
}
return chars;
}
void Player::ChangePlayerSkin()
{
if(app.vSkinNames.size()>0)
{
m_uiPlayerCurrentSkin++;
if(m_uiPlayerCurrentSkin>app.vSkinNames.size())
{
m_uiPlayerCurrentSkin=0;
this->customTextureUrl=L"";
}
else
{
if(m_uiPlayerCurrentSkin>0)
{
// change this players custom texture url
this->customTextureUrl=app.vSkinNames[m_uiPlayerCurrentSkin-1];
}
}
}
}
void Player::prepareCustomTextures()
{
MOJANG_DATA *pMojangData=app.GetMojangDataForXuid(getOnlineXuid());
if(pMojangData)
{
// Skin
if(pMojangData->wchSkin)
{
this->customTextureUrl= pMojangData->wchSkin;
}
// 4J Stu - Don't update the cape here, it gets set elsewhere
// Cape
//if(pMojangData->wchCape)
//{
// this->customTextureUrl2= pMojangData->wchCape;
//}
//else
//{
// if(app.DefaultCapeExists())
// {
// this->customTextureUrl2= wstring(L"Default_Cape.png");
// }
// else
// {
// this->customTextureUrl2= wstring(L"");
// }
//}
}
else
{
// 4J Stu - Don't update the cape here, it gets set elsewhere
// if there is a custom default cloak, then set it here
//if(app.DefaultCapeExists())
//{
// this->customTextureUrl2= wstring(L"Default_Cape.png");
//}
//else
//{
// this->customTextureUrl2 =wstring(L"");
//}
}
/*cloakTexture = wstring(L"http://s3.amazonaws.com/MinecraftCloaks/").append( name ).append( L".png" );*/
//this->customTextureUrl2 = cloakTexture;
}
void Player::rideTick()
{
if (!level->isClientSide && isSneaking())
{
ride(nullptr);
setSneaking(false);
return;
}
double preX = x, preY = y, preZ = z;
float preYRot = yRot, preXRot = xRot;
LivingEntity::rideTick();
oBob = bob;
bob = 0;
checkRidingStatistiscs(x - preX, y - preY, z - preZ);
// riding can be set to null inside 'Entity::rideTick()'.
if ( riding != NULL && (riding->GetType() & eTYPE_PIG) == eTYPE_PIG )
{
// 4J Stu - I don't know why we would want to do this, but it means that the players head is locked in position and can't move around
//xRot = preXRot;
//yRot = preYRot;
shared_ptr<Pig> pig = dynamic_pointer_cast<Pig>(riding);
yBodyRot = pig->yBodyRot;
while (yBodyRot - yBodyRotO < -180)
yBodyRotO -= 360;
while (yBodyRot - yBodyRotO >= 180)
yBodyRotO += 360;
}
}
void Player::resetPos()
{
heightOffset = 1.62f;
setSize(0.6f, 1.8f);
LivingEntity::resetPos();
setHealth(getMaxHealth());
deathTime = 0;
}
void Player::serverAiStep()
{
LivingEntity::serverAiStep();
updateSwingTime();
}
void Player::aiStep()
{
if (jumpTriggerTime > 0) jumpTriggerTime--;
if (level->difficulty == Difficulty::PEACEFUL && getHealth() < getMaxHealth() && level->getGameRules()->getBoolean(GameRules::RULE_NATURAL_REGENERATION))
{
if (tickCount % 20 * 12 == 0) heal(1);
}
inventory->tick();
oBob = bob;
LivingEntity::aiStep();
AttributeInstance *speed = getAttribute(SharedMonsterAttributes::MOVEMENT_SPEED);
if (!level->isClientSide) speed->setBaseValue(abilities.getWalkingSpeed());
flyingSpeed = defaultFlySpeed;
if (isSprinting())
{
flyingSpeed += defaultFlySpeed * 0.3f;
}
setSpeed((float) speed->getValue());
float tBob = (float) sqrt(xd * xd + zd * zd);
// 4J added - we were getting a NaN with zero xd & zd
if(( xd * xd + zd * zd ) < 0.00001f )
{
tBob = 0.0f;
}
float tTilt = (float) atan(-yd * 0.2f) * 15.0f;
if (tBob > 0.1f) tBob = 0.1f;
if (!onGround || getHealth() <= 0) tBob = 0;
if (onGround || getHealth() <= 0) tTilt = 0;
bob += (tBob - bob) * 0.4f;
tilt += (tTilt - tilt) * 0.8f;
if (getHealth() > 0)
{
AABB *pickupArea = NULL;
if (riding != NULL && !riding->removed)
{
// if the player is riding, also touch entities under the
// pig/horse
pickupArea = bb->minmax(riding->bb)->grow(1, 0, 1);
}
else
{
pickupArea = bb->grow(1, .5, 1);
}
vector<shared_ptr<Entity> > *entities = level->getEntities(shared_from_this(), pickupArea);
if (entities != NULL)
{
for (auto& e : *entities)
{
if ( e && !e->removed)
{
touch(e);
}
}
}
}
}
void Player::touch(shared_ptr<Entity> entity)
{
entity->playerTouch( dynamic_pointer_cast<Player>( shared_from_this() ) );
}
int Player::getScore()
{
return entityData->getInteger(DATA_SCORE_ID);
}
void Player::setScore(int value)
{
entityData->set(DATA_SCORE_ID, value);
}
void Player::increaseScore(int amount)
{
int score = getScore();
entityData->set(DATA_SCORE_ID, score + amount);
}
void Player::die(DamageSource *source)
{
LivingEntity::die(source);
setSize(0.2f, 0.2f);
setPos(x, y, z);
yd = 0.1f;
// 4J - TODO need to use a xuid
if ( app.isXuidNotch( m_xuid ) )
{
drop(shared_ptr<ItemInstance>( new ItemInstance(Item::apple, 1) ), true);
}
if (!level->getGameRules()->getBoolean(GameRules::RULE_KEEPINVENTORY))
{
inventory->dropAll();
}
if (source != NULL)
{
xd = -Mth::cos((hurtDir + yRot) * PI / 180) * 0.1f;
zd = -Mth::sin((hurtDir + yRot) * PI / 180) * 0.1f;
}
else
{
xd = zd = 0;
}
heightOffset = 0.1f;
}
void Player::awardKillScore(shared_ptr<Entity> victim, int awardPoints)
{
increaseScore(awardPoints);
vector<Objective *> *objectives = getScoreboard()->findObjectiveFor(ObjectiveCriteria::KILL_COUNT_ALL);
//if (victim instanceof Player)
//{
// awardStat(Stats::playerKills, 1);
// objectives.addAll(getScoreboard().findObjectiveFor(ObjectiveCriteria::KILL_COUNT_PLAYERS));
//}
//else
//{
// awardStat(Stats::mobKills, 1);
//}
if(objectives)
{
for (auto& objective : *objectives)
{
if ( objective )
{
Score *score = getScoreboard()->getPlayerScore(getAName(), objective);
if ( score )
score->increment();
}
}
}
}
bool Player::isShootable()
{
return true;
}
bool Player::isCreativeModeAllowed()
{
return true;
}
shared_ptr<ItemEntity> Player::drop(bool all)
{
return drop(inventory->removeItem(inventory->selected, all && inventory->getSelected() != NULL ? inventory->getSelected()->count : 1), false);
}
shared_ptr<ItemEntity> Player::drop(shared_ptr<ItemInstance> item)
{
return drop(item, false);
}
shared_ptr<ItemEntity> Player::drop(shared_ptr<ItemInstance> item, bool randomly)
{
if (item == NULL) return nullptr;
if (item->count == 0) return nullptr;
shared_ptr<ItemEntity> thrownItem = shared_ptr<ItemEntity>( new ItemEntity(level, x, y - 0.3f + getHeadHeight(), z, item) );
thrownItem->throwTime = 20 * 2;
thrownItem->setThrower(getName());
float pow = 0.1f;
if (randomly)
{
float _pow = random->nextFloat() * 0.5f;
float dir = random->nextFloat() * PI * 2;
thrownItem->xd = -sin(dir) * _pow;
thrownItem->zd = cos(dir) * _pow;
thrownItem->yd = 0.2f;
}
else
{
pow = 0.3f;
thrownItem->xd = -sin(yRot / 180 * PI) * cos(xRot / 180 * PI) * pow;
thrownItem->zd = cos(yRot / 180 * PI) * cos(xRot / 180 * PI) * pow;
thrownItem->yd = -sin(xRot / 180 * PI) * pow + 0.1f;
pow = 0.02f;
float dir = random->nextFloat() * PI * 2;
pow *= random->nextFloat();
thrownItem->xd += cos(dir) * pow;
thrownItem->yd += (random->nextFloat() - random->nextFloat()) * 0.1f;
thrownItem->zd += sin(dir) * pow;
}
reallyDrop(thrownItem);
return thrownItem;
}
void Player::reallyDrop(shared_ptr<ItemEntity> thrownItem)
{
level->addEntity(thrownItem);
}
float Player::getDestroySpeed(Tile *tile, bool hasProperTool)
{
float speed = inventory->getDestroySpeed(tile);
if (speed > 1)
{
int efficiency = EnchantmentHelper::getDiggingBonus(dynamic_pointer_cast<LivingEntity>(shared_from_this()));
shared_ptr<ItemInstance> item = inventory->getSelected();
if (efficiency > 0 && item != NULL)
{
float boost = efficiency * efficiency + 1;
if (item->canDestroySpecial(tile) || speed > 1)
{
speed += boost;
}
else
{
speed += boost * 0.08f;
}
}
}
if (hasEffect(MobEffect::digSpeed))
{
speed *= 1.0f + (getEffect(MobEffect::digSpeed)->getAmplifier() + 1) * .2f;
}
if (hasEffect(MobEffect::digSlowdown))
{
speed *= 1.0f - (getEffect(MobEffect::digSlowdown)->getAmplifier() + 1) * .2f;
}
if (isUnderLiquid(Material::water) && !EnchantmentHelper::hasWaterWorkerBonus(dynamic_pointer_cast<LivingEntity>(shared_from_this()))) speed /= 5;
// 4J Stu - onGround is set to true on the client when we are flying, which means
// the dig speed is out of sync with the server. Removing this speed change when
// flying so that we always dig as the same speed
//if (!onGround) speed /= 5;
return speed;
}
bool Player::canDestroy(Tile *tile)
{
return inventory->canDestroy(tile);
}
void Player::readAdditionalSaveData(CompoundTag *entityTag)
{
LivingEntity::readAdditionalSaveData(entityTag);
ListTag<CompoundTag> *inventoryList = (ListTag<CompoundTag> *) entityTag->getList(L"Inventory");
inventory->load(inventoryList);
inventory->selected = entityTag->getInt(L"SelectedItemSlot");
m_isSleeping = entityTag->getBoolean(L"Sleeping");
sleepCounter = entityTag->getShort(L"SleepTimer");
experienceProgress = entityTag->getFloat(L"XpP");
experienceLevel = entityTag->getInt(L"XpLevel");
totalExperience = entityTag->getInt(L"XpTotal");
setScore(entityTag->getInt(L"Score"));
if (m_isSleeping)
{
bedPosition = new Pos( Mth::floor(x), Mth::floor(y), Mth::floor(z));
stopSleepInBed(true, true, false);
}
if (entityTag->contains(L"SpawnX") && entityTag->contains(L"SpawnY") && entityTag->contains(L"SpawnZ"))
{
respawnPosition = new Pos(entityTag->getInt(L"SpawnX"), entityTag->getInt(L"SpawnY"), entityTag->getInt(L"SpawnZ"));
respawnForced = entityTag->getBoolean(L"SpawnForced");
}
foodData.readAdditionalSaveData(entityTag);
abilities.loadSaveData(entityTag);
if (entityTag->contains(L"EnderItems"))
{
ListTag<CompoundTag> *enderItemsList = (ListTag<CompoundTag> *) entityTag->getList(L"EnderItems");
enderChestInventory->setItemsByTag(enderItemsList);
}
// 4J Added
m_uiGamePrivileges = entityTag->getInt(L"GamePrivileges");
}
void Player::addAdditonalSaveData(CompoundTag *entityTag)
{
LivingEntity::addAdditonalSaveData(entityTag);
entityTag->put(L"Inventory", inventory->save(new ListTag<CompoundTag>()));
entityTag->putInt(L"SelectedItemSlot", inventory->selected);
entityTag->putBoolean(L"Sleeping", m_isSleeping);
entityTag->putShort(L"SleepTimer", (short) sleepCounter);
entityTag->putFloat(L"XpP", experienceProgress);
entityTag->putInt(L"XpLevel", experienceLevel);
entityTag->putInt(L"XpTotal", totalExperience);
entityTag->putInt(L"Score", getScore());
if (respawnPosition != NULL)
{
entityTag->putInt(L"SpawnX", respawnPosition->x);
entityTag->putInt(L"SpawnY", respawnPosition->y);
entityTag->putInt(L"SpawnZ", respawnPosition->z);
entityTag->putBoolean(L"SpawnForced", respawnForced);
}
foodData.addAdditonalSaveData(entityTag);
abilities.addSaveData(entityTag);
entityTag->put(L"EnderItems", enderChestInventory->createTag());
// 4J Added
entityTag->putInt(L"GamePrivileges",m_uiGamePrivileges);
}
bool Player::openContainer(shared_ptr<Container> container)
{
return true;
}
bool Player::openHopper(shared_ptr<HopperTileEntity> container)
{
return true;
}
bool Player::openHopper(shared_ptr<MinecartHopper> container)
{
return true;
}
bool Player::openHorseInventory(shared_ptr<EntityHorse> horse, shared_ptr<Container> container)
{
return true;
}
bool Player::startEnchanting(int x, int y, int z, const wstring &name)
{
return true;
}
bool Player::startRepairing(int x, int y, int z)
{
return true;
}
bool Player::startCrafting(int x, int y, int z)
{
return true;
}
bool Player::openFireworks(int x, int y, int z)
{
return true;
}
float Player::getHeadHeight()
{
return 0.12f;
}
void Player::setDefaultHeadHeight()
{
heightOffset = 1.62f;
}
bool Player::hurt(DamageSource *source, float dmg)
{
if (isInvulnerable()) return false;
if ( hasInvulnerablePrivilege() || (abilities.invulnerable && !source->isBypassInvul()) ) return false;
// 4J-JEV: Fix for PSVita: #3987 - [IN GAME] The user can take damage/die, when attempting to re-enter fly mode when falling from a height.
if ( source == DamageSource::fall && isAllowedToFly() && abilities.flying ) return false;
noActionTime = 0;
if (getHealth() <= 0) return false;
if (isSleeping() && !level->isClientSide)
{
stopSleepInBed(true, true, false);
}
if ( source->scalesWithDifficulty() )
{
if (level->difficulty == Difficulty::PEACEFUL) dmg = 0;
if (level->difficulty == Difficulty::EASY) dmg = dmg / 2 + 1;
if (level->difficulty == Difficulty::HARD) dmg = dmg * 3 / 2;
}
if (dmg == 0) return false;
shared_ptr<Entity> attacker = source->getEntity();
if ( attacker != NULL && attacker->instanceof(eTYPE_ARROW) )
{
shared_ptr<Arrow> arrow = dynamic_pointer_cast<Arrow>(attacker);
if ( arrow->owner != NULL)
{
attacker = arrow->owner;
}
}
return LivingEntity::hurt(source, dmg);
}
bool Player::canHarmPlayer(shared_ptr<Player> target)
{
Team *team = getTeam();
Team *otherTeam = target->getTeam();
if (team == NULL)
{
return true;
}
if (!team->isAlliedTo(otherTeam))
{
return true;
}
return team->isAllowFriendlyFire();
}
bool Player::canHarmPlayer(wstring targetName)
{
return true;
}
void Player::hurtArmor(float damage)
{
inventory->hurtArmor(damage);
}
int Player::getArmorValue()
{
return inventory->getArmorValue();
}
float Player::getArmorCoverPercentage()
{
int count = 0;
for (int i = 0; i < inventory->armor.length; i++)
{
if (inventory->armor[i] != NULL) {
count++;
}
}
return (float) count / (float) inventory->armor.length;
}
void Player::actuallyHurt(DamageSource *source, float dmg)
{
if (isInvulnerable()) return;
if (!source->isBypassArmor() && isBlocking() && dmg > 0)
{
dmg = (1 + dmg) * .5f;
}
dmg = getDamageAfterArmorAbsorb(source, dmg);
dmg = getDamageAfterMagicAbsorb(source, dmg);
float originalDamage = dmg;
dmg = max(dmg - getAbsorptionAmount(), 0.0f);
setAbsorptionAmount(getAbsorptionAmount() - (originalDamage - dmg));
if (dmg == 0) return;
causeFoodExhaustion(source->getFoodExhaustion());
float oldHealth = getHealth();
setHealth(getHealth() - dmg);
getCombatTracker()->recordDamage(source, oldHealth, dmg);
}
bool Player::openFurnace(shared_ptr<FurnaceTileEntity> container)
{
return true;
}
bool Player::openTrap(shared_ptr<DispenserTileEntity> container)
{
return true;
}
void Player::openTextEdit(shared_ptr<TileEntity> sign)
{
}
bool Player::openBrewingStand(shared_ptr<BrewingStandTileEntity> brewingStand)
{
return true;
}
bool Player::openBeacon(shared_ptr<BeaconTileEntity> beacon)
{
return true;
}
bool Player::openTrading(shared_ptr<Merchant> traderTarget, const wstring &name)
{
return true;
}
/**
* Opens an iteminstance-dependent user interface.
*
* @param itemInstance
*/
void Player::openItemInstanceGui(shared_ptr<ItemInstance> itemInstance)
{
}
bool Player::interact(shared_ptr<Entity> entity)
{
shared_ptr<Player> thisPlayer = dynamic_pointer_cast<Player>(shared_from_this());
shared_ptr<ItemInstance> item = getSelectedItem();
shared_ptr<ItemInstance> itemClone = (item != NULL) ? item->copy() : nullptr;
if ( entity->interact(thisPlayer) )
{
// [EB]: Added rude check to see if we're still talking about the
// same item; this code caused bucket->milkbucket to be deleted because
// the milkbuckets' stack got decremented to 0.
if (item != NULL && item == getSelectedItem())
{
if (item->count <= 0 && !abilities.instabuild)
{
removeSelectedItem();
}
else if (item->count < itemClone->count && abilities.instabuild)
{
item->count = itemClone->count;
}
}
return true;
}
if ( (item != NULL) && entity->instanceof(eTYPE_LIVINGENTITY) )
{
// 4J - PC Comments
// Hack to prevent item stacks from decrementing if the player has
// the ability to instabuild
if(this->abilities.instabuild) item = itemClone;
if(item->interactEnemy(thisPlayer, dynamic_pointer_cast<LivingEntity>(entity)))
{
// 4J - PC Comments
// Don't remove the item in hand if the player has the ability
// to instabuild
if ( (item->count <= 0) && !abilities.instabuild)
{
removeSelectedItem();
}
return true;
}
}
return false;
}
shared_ptr<ItemInstance> Player::getSelectedItem()
{
return inventory->getSelected();
}
void Player::removeSelectedItem()
{
inventory->setItem(inventory->selected, nullptr);
}
double Player::getRidingHeight()
{
return heightOffset - 0.5f;
}
void Player::attack(shared_ptr<Entity> entity)
{
if (!entity->isAttackable())
{
return;
}
if (entity->skipAttackInteraction(shared_from_this()))
{
return;
}
float dmg = (float) getAttribute(SharedMonsterAttributes::ATTACK_DAMAGE)->getValue();
int knockback = 0;
float magicBoost = 0;
if ( entity->instanceof(eTYPE_LIVINGENTITY) )
{
shared_ptr<Player> thisPlayer = dynamic_pointer_cast<Player>(shared_from_this());
shared_ptr<LivingEntity> mob = dynamic_pointer_cast<LivingEntity>(entity);
magicBoost = EnchantmentHelper::getDamageBonus(thisPlayer, mob);
knockback += EnchantmentHelper::getKnockbackBonus(thisPlayer, mob);
}
if (isSprinting())
{
knockback += 1;
}
if (dmg > 0 || magicBoost > 0)
{
bool bCrit = fallDistance > 0 && !onGround && !onLadder() && !isInWater() && !hasEffect(MobEffect::blindness) && (riding == NULL) && entity->instanceof(eTYPE_LIVINGENTITY);
if (bCrit && dmg > 0)
{
dmg *= 1.5f;
}
dmg += magicBoost;
// Ensure we put the entity on fire if we're hitting with a
// fire-enchanted weapon
bool setOnFireTemporatily = false;
int fireAspect = EnchantmentHelper::getFireAspect(dynamic_pointer_cast<LivingEntity>(shared_from_this()));
if ( entity->instanceof(eTYPE_MOB) && fireAspect > 0 && !entity->isOnFire())
{
setOnFireTemporatily = true;
entity->setOnFire(1);
}
DamageSource *damageSource = DamageSource::playerAttack(dynamic_pointer_cast<Player>(shared_from_this()));
bool wasHurt = entity->hurt(damageSource, dmg);
delete damageSource;
if (wasHurt)
{
if (knockback > 0)
{
entity->push(-Mth::sin(yRot * PI / 180) * knockback * .5f, 0.1, Mth::cos(yRot * PI / 180) * knockback * .5f);
xd *= 0.6;
zd *= 0.6;
setSprinting(false);
}
if (bCrit)
{
crit(entity);
}
if (magicBoost > 0)
{
magicCrit(entity);
}
if (dmg >= 18)
{
awardStat(GenericStats::overkill(),GenericStats::param_overkill(dmg));
}
setLastHurtMob(entity);
if ( entity->instanceof(eTYPE_LIVINGENTITY) )
{
shared_ptr<LivingEntity> mob = dynamic_pointer_cast<LivingEntity>(entity);
ThornsEnchantment::doThornsAfterAttack(shared_from_this(), mob, random);
}
}
shared_ptr<ItemInstance> item = getSelectedItem();
shared_ptr<Entity> hurtTarget = entity;
if ( entity->instanceof(eTYPE_MULTIENTITY_MOB_PART) )
{
shared_ptr<Entity> multiMob = dynamic_pointer_cast<Entity>((dynamic_pointer_cast<MultiEntityMobPart>(entity))->parentMob.lock());
if ( (multiMob != NULL) && multiMob->instanceof(eTYPE_LIVINGENTITY) )
{
hurtTarget = dynamic_pointer_cast<LivingEntity>( multiMob );
}
}
if ( (item != NULL) && hurtTarget->instanceof(eTYPE_LIVINGENTITY) )
{
item->hurtEnemy(dynamic_pointer_cast<LivingEntity>(hurtTarget), dynamic_pointer_cast<Player>( shared_from_this() ) );
if (item->count <= 0)
{
removeSelectedItem();
}
}
if ( entity->instanceof(eTYPE_LIVINGENTITY) )
{
//awardStat(Stats.damageDealt, (int) Math.round(dmg * 10));
if (fireAspect > 0 && wasHurt)
{
entity->setOnFire(fireAspect * 4);
}
else if (setOnFireTemporatily)
{
entity->clearFire();
}
}
causeFoodExhaustion(FoodConstants::EXHAUSTION_ATTACK);
}
// if (SharedConstants::INGAME_DEBUG_OUTPUT)
// {
// //sendMessage(ChatMessageComponent.forPlainText("DMG " + dmg + ", " + magicBoost + ", " + knockback));
// }
}
void Player::crit(shared_ptr<Entity> entity)
{
}
void Player::magicCrit(shared_ptr<Entity> entity)
{
}
void Player::respawn()
{
deathFadeCounter=0;
}
void Player::animateRespawn(shared_ptr<Player> player, Level *level)
{
for (int i = 0; i < 45; i++)
{
float angle = i * PI * 4.0f / 25.0f;
float xo = Mth::cos(angle) * 0.7f;
float zo = Mth::sin(angle) * 0.7f;
level->addParticle(eParticleType_netherportal, player->x + xo, player->y - player->heightOffset + 1.62f - i * .05f, player->z + zo, 0, 0, 0);
}
}
Slot *Player::getInventorySlot(int slotId)
{
return NULL;
}
void Player::remove()
{
LivingEntity::remove();
inventoryMenu->removed( dynamic_pointer_cast<Player>( shared_from_this() ) );
if (containerMenu != NULL)
{
containerMenu->removed( dynamic_pointer_cast<Player>( shared_from_this() ) );
}
}
bool Player::isInWall()
{
return !m_isSleeping && LivingEntity::isInWall();
}
bool Player::isLocalPlayer()
{
return false;
}
Player::BedSleepingResult Player::startSleepInBed(int x, int y, int z, bool bTestUse)
{
if (!level->isClientSide || bTestUse)
{
if (isSleeping() || !isAlive())
{
return OTHER_PROBLEM;
}
if (!level->dimension->isNaturalDimension())
{
// may not sleep in this dimension
return NOT_POSSIBLE_HERE;
}
// 4J-PB - I'm going to move the position of these tests below
// The distance check should be before the day check, otherwise you can use the bed in daytime from far away
// and you'll get the message about only sleeping at night
if (abs(this->x - x) > 3 || abs(this->y - y) > 2 || abs(this->z - z) > 3)
{
// too far away
return TOO_FAR_AWAY;
}
if (!bTestUse)
{
// 4J-PB - We still want the tooltip for Sleep
double hRange = 8;
double vRange = 5;
vector<shared_ptr<Entity> > *monsters = level->getEntitiesOfClass(typeid(Monster), AABB::newTemp(x - hRange, y - vRange, z - hRange, x + hRange, y + vRange, z + hRange));
if (!monsters->empty())
{
delete monsters;
return NOT_SAFE;
}
delete monsters;
}
// This causes a message to be displayed, so we do want to show the tooltip in test mode
if (!bTestUse && level->isDay())
{
// may not sleep during day
return NOT_POSSIBLE_NOW;
}
}
if(bTestUse)
{
// 4J-PB - we're just testing use, and we get here, then the bed can be used
return OK;
}
if (isRiding())
{
ride(nullptr);
}
setSize(0.2f, 0.2f);
heightOffset = .2f;
if (level->hasChunkAt(x, y, z))
{
int data = level->getData(x, y, z);
int direction = BedTile::getDirection(data);
float xo = .5f, zo = .5f;
switch (direction)
{
case Direction::SOUTH:
zo = .9f;
break;
case Direction::NORTH:
zo = .1f;
break;
case Direction::WEST:
xo = .1f;
break;
case Direction::EAST:
xo = .9f;
break;
}
setBedOffset(direction);
setPos(x + xo, y + 15.0f / 16.0f, z + zo);
}
else
{
setPos(x + .5f, y + 15.0f / 16.0f, z + .5f);
}
m_isSleeping = true;
sleepCounter = 0;
bedPosition = new Pos(x, y, z);
xd = zd = yd = 0;
if (!level->isClientSide)
{
level->updateSleepingPlayerList();
}
return OK;
}
void Player::setBedOffset(int bedDirection)
{
// place position on pillow and feet at bottom
bedOffsetX = 0;
bedOffsetZ = 0;
switch (bedDirection)
{
case Direction::SOUTH:
bedOffsetZ = -1.8f;
break;
case Direction::NORTH:
bedOffsetZ = 1.8f;
break;
case Direction::WEST:
bedOffsetX = 1.8f;
break;
case Direction::EAST:
bedOffsetX = -1.8f;
break;
}
}
/**
*
* @param forcefulWakeUp
* If the player has been forced to wake up. When this happens,
* the client will skip the wake-up animation. For example, when
* the player is hurt or the bed is destroyed.
* @param updateLevelList
* If the level's sleeping player list needs to be updated. This
* is usually the case.
* @param saveRespawnPoint
* TODO
*/
void Player::stopSleepInBed(bool forcefulWakeUp, bool updateLevelList, bool saveRespawnPoint)
{
setSize(0.6f, 1.8f);
setDefaultHeadHeight();
Pos *pos = bedPosition;
Pos *standUp = bedPosition;
if (pos != NULL && level->getTile(pos->x, pos->y, pos->z) == Tile::bed_Id)
{
BedTile::setOccupied(level, pos->x, pos->y, pos->z, false);
standUp = BedTile::findStandUpPosition(level, pos->x, pos->y, pos->z, 0);
if (standUp == NULL)
{
standUp = new Pos(pos->x, pos->y + 1, pos->z);
}
setPos(standUp->x + .5f, standUp->y + heightOffset + .1f, standUp->z + .5f);
}
m_isSleeping = false;
if (!level->isClientSide && updateLevelList)
{
level->updateSleepingPlayerList();
}
if (forcefulWakeUp)
{
sleepCounter = 0;
}
else
{
sleepCounter = SLEEP_DURATION;
}
if (saveRespawnPoint)
{
setRespawnPosition(bedPosition, false);
}
}
bool Player::checkBed()
{
return (level->getTile(bedPosition->x, bedPosition->y, bedPosition->z) == Tile::bed_Id);
}
Pos *Player::checkBedValidRespawnPosition(Level *level, Pos *pos, bool forced)
{
// make sure the chunks around the bed exist
ChunkSource *chunkSource = level->getChunkSource();
chunkSource->create((pos->x - 3) >> 4, (pos->z - 3) >> 4);
chunkSource->create((pos->x + 3) >> 4, (pos->z - 3) >> 4);
chunkSource->create((pos->x - 3) >> 4, (pos->z + 3) >> 4);
chunkSource->create((pos->x + 3) >> 4, (pos->z + 3) >> 4);
// make sure the bed is still standing
if (level->getTile(pos->x, pos->y, pos->z) != Tile::bed_Id)
{
Material *bottomMaterial = level->getMaterial(pos->x, pos->y, pos->z);
Material *topMaterial = level->getMaterial(pos->x, pos->y + 1, pos->z);
bool freeFeet = !bottomMaterial->isSolid() && !bottomMaterial->isLiquid();
bool freeHead = !topMaterial->isSolid() && !topMaterial->isLiquid();
if (forced && freeFeet && freeHead)
{
return pos;
}
return NULL;
}
// make sure the bed still has a stand-up position
Pos *standUp = BedTile::findStandUpPosition(level, pos->x, pos->y, pos->z, 0);
return standUp;
}
float Player::getSleepRotation()
{
if (bedPosition != NULL)
{
int data = level->getData(bedPosition->x, bedPosition->y, bedPosition->z);
int direction = BedTile::getDirection(data);
switch (direction)
{
case Direction::SOUTH:
return 90;
case Direction::WEST:
return 0;
case Direction::NORTH:
return 270;
case Direction::EAST:
return 180;
}
}
return 0;
}
bool Player::isSleeping()
{
return m_isSleeping;
}
bool Player::isSleepingLongEnough()
{
return m_isSleeping && sleepCounter >= SLEEP_DURATION;
}
int Player::getSleepTimer()
{
return sleepCounter;
}
// 4J-PB - added for death fade
int Player::getDeathFadeTimer()
{
return deathFadeCounter;
}
bool Player::getPlayerFlag(int flag)
{
return (entityData->getByte(DATA_PLAYER_FLAGS_ID) & (1 << flag)) != 0;
}
void Player::setPlayerFlag(int flag, bool value)
{
byte currentValue = entityData->getByte(DATA_PLAYER_FLAGS_ID);
if (value)
{
entityData->set(DATA_PLAYER_FLAGS_ID, (byte) (currentValue | (1 << flag)));
}
else
{
entityData->set(DATA_PLAYER_FLAGS_ID, (byte) (currentValue & ~(1 << flag)));
}
}
/**
* This method is currently only relevant to client-side players. It will
* try to load the messageId from the language file and display it to the
* client.
*/
void Player::displayClientMessage(int messageId)
{
}
Pos *Player::getRespawnPosition()
{
return respawnPosition;
}
bool Player::isRespawnForced()
{
return respawnForced;
}
void Player::setRespawnPosition(Pos *respawnPosition, bool forced)
{
if (respawnPosition != NULL)
{
this->respawnPosition = new Pos(*respawnPosition);
respawnForced = forced;
}
else
{
this->respawnPosition = NULL;
respawnForced = false;
}
}
void Player::awardStat(Stat *stat, byteArray paramBlob)
{
if (paramBlob.data != NULL)
{
delete [] paramBlob.data;
}
}
void Player::jumpFromGround()
{
LivingEntity::jumpFromGround();
// 4J Stu - This seems to have been missed from 1.7.3, but do we care?
//awardStat(Stats::jump, 1);
if (isSprinting())
{
causeFoodExhaustion(FoodConstants::EXHAUSTION_SPRINT_JUMP);
}
else
{
causeFoodExhaustion(FoodConstants::EXHAUSTION_JUMP);
}
}
void Player::travel(float xa, float ya)
{
double preX = x, preY = y, preZ = z;
if (abilities.flying && riding == NULL)
{
double ydo = yd;
float ofs = flyingSpeed;
flyingSpeed = abilities.getFlyingSpeed();
LivingEntity::travel(xa, ya);
yd = ydo * 0.6;
flyingSpeed = ofs;
}
else
{
LivingEntity::travel(xa, ya);
}
checkMovementStatistiscs(x - preX, y - preY, z - preZ);
}
float Player::getSpeed()
{
return (float) getAttribute(SharedMonsterAttributes::MOVEMENT_SPEED)->getValue();
}
void Player::checkMovementStatistiscs(double dx, double dy, double dz)
{
if (riding != NULL)
{
return;
}
if (isUnderLiquid(Material::water))
{
int distance = (int) Math::round(sqrt(dx * dx + dy * dy + dz * dz) * 100.0f);
if (distance > 0)
{
//awardStat(Stats::diveOneCm, distance);
causeFoodExhaustion(FoodConstants::EXHAUSTION_SWIM * distance * .01f);
}
}
else if (isInWater())
{
int horizontalDistance = (int) Math::round(sqrt(dx * dx + dz * dz) * 100.0f);
if (horizontalDistance > 0)
{
distanceSwim += horizontalDistance;
if( distanceSwim >= 100 )
{
int newDistance = distanceSwim - (distanceSwim % 100);
distanceSwim -= newDistance;
awardStat( GenericStats::swimOneM(), GenericStats::param_swim(newDistance/100) );
}
causeFoodExhaustion(FoodConstants::EXHAUSTION_SWIM * horizontalDistance * .01f);
}
}
else if (onLadder())
{
if (dy > 0)
{
distanceClimb += (int) Math::round(dy * 100.0f);
if( distanceClimb >= 100 )
{
int newDistance = distanceClimb - (distanceClimb % 100);
distanceClimb -= newDistance;
awardStat( GenericStats::climbOneM(), GenericStats::param_climb(newDistance/100) );
}
}
}
else if (onGround)
{
int horizontalDistance = (int) Math::round(sqrt(dx * dx + dz * dz) * 100.0f);
if (horizontalDistance > 0)
{
distanceWalk += horizontalDistance;
if( distanceWalk >= 100 )
{
int newDistance = distanceWalk - (distanceWalk % 100);
distanceWalk -= newDistance;
awardStat( GenericStats::walkOneM(), GenericStats::param_walk(newDistance/100) );
}
if (isSprinting())
{
causeFoodExhaustion(FoodConstants::EXHAUSTION_SPRINT * horizontalDistance * .01f);
}
else
{
causeFoodExhaustion(FoodConstants::EXHAUSTION_WALK * horizontalDistance * .01f);
}
}
}
}
void Player::checkRidingStatistiscs(double dx, double dy, double dz)
{
if (riding != NULL)
{
int distance = (int) Math::round(sqrt(dx * dx + dy * dy + dz * dz) * 100.0f);
if (distance > 0)
{
if ( riding->instanceof(eTYPE_MINECART) )
{
distanceMinecart += distance;
if( distanceMinecart >= 100 )
{
int newDistance = distanceMinecart - (distanceMinecart % 100);
distanceMinecart -= newDistance;
awardStat( GenericStats::minecartOneM(), GenericStats::param_minecart(newDistance/100) );
}
int dist = 0;
if (minecartAchievementPos == NULL)
{
minecartAchievementPos = new Pos(Mth::floor(x), Mth::floor(y), Mth::floor(z));
}
// 4J-PB - changed this because our world isn't big enough to go 1000m
else
{
// 4-JEV, changed slightly to add extra parameters for event on durango.
int dist = minecartAchievementPos->dist(Mth::floor(x), Mth::floor(y), Mth::floor(z));
#ifdef _XBOX_ONE
// 4J-PB - send the event to cause the progress bar to increase on XB1
if (m_bAwardedOnARail==false)
{
if(dist < 500)
{
if((dist>0) && (dist%100==0))
{
awardStat(GenericStats::onARail(), GenericStats::param_onARail(dist));
}
}
else
{
awardStat(GenericStats::onARail(), GenericStats::param_onARail(dist));
m_bAwardedOnARail=true;
}
}
#else
if ((m_bAwardedOnARail==false) && (dist >= 500))
{
awardStat(GenericStats::onARail(), GenericStats::param_onARail(dist));
m_bAwardedOnARail=true;
}
#endif
}
}
else if ( riding->instanceof(eTYPE_BOAT) )
{
distanceBoat += distance;
if( distanceBoat >= 100 )
{
int newDistance = distanceBoat - (distanceBoat % 100);
distanceBoat -= newDistance;
awardStat(GenericStats::boatOneM(), GenericStats::param_boat(newDistance/100) );
}
}
else if ( riding->instanceof(eTYPE_PIG) )
{
distancePig += distance;
if( distancePig >= 100 )
{
int newDistance = distancePig - (distancePig % 100);
distancePig -= newDistance;
awardStat(GenericStats::pigOneM(), GenericStats::param_pig(newDistance/100) );
}
}
}
}
}
void Player::causeFallDamage(float distance)
{
if (abilities.mayfly) return;
if (distance >= 2)
{
distanceFall += (int) Math::round(distance * 100.0);
if( distanceFall >= 100 )
{
int newDistance = distanceFall - (distanceFall % 100);
distanceFall -= newDistance;
awardStat(GenericStats::fallOneM(), GenericStats::param_fall(newDistance/100) );
}
}
LivingEntity::causeFallDamage(distance);
}
void Player::killed(shared_ptr<LivingEntity> mob)
{
// 4J-PB - added the lavaslime enemy - fix for #64007 - TU7: Code: Achievements: TCR#073: Killing Magma Cubes doesn't unlock "Monster Hunter" Achievement.
if( mob->instanceof(eTYPE_ENEMY) || mob->GetType() == eTYPE_GHAST || mob->GetType() == eTYPE_SLIME || mob->GetType() == eTYPE_LAVASLIME || mob->GetType() == eTYPE_ENDERDRAGON)
{
awardStat(GenericStats::killEnemy(), GenericStats::param_noArgs());
switch( mob->GetType() )
{
case eTYPE_CREEPER:
awardStat(GenericStats::killsCreeper(), GenericStats::param_noArgs());
break;
case eTYPE_SKELETON:
if( mob->isRiding() && mob->riding->GetType() == eTYPE_SPIDER )
awardStat(GenericStats::killsSpiderJockey(), GenericStats::param_noArgs());
else
awardStat(GenericStats::killsSkeleton(), GenericStats::param_noArgs());
break;
case eTYPE_SPIDER:
if( mob->rider.lock() != NULL && mob->rider.lock()->GetType() == eTYPE_SKELETON )
awardStat(GenericStats::killsSpiderJockey(), GenericStats::param_noArgs());
else
awardStat(GenericStats::killsSpider(), GenericStats::param_noArgs());
break;
case eTYPE_ZOMBIE:
awardStat(GenericStats::killsZombie(), GenericStats::param_noArgs());
break;
case eTYPE_PIGZOMBIE:
if( level->dimension->id == 0 )
awardStat(GenericStats::killsZombiePigman(), GenericStats::param_noArgs());
else
awardStat(GenericStats::killsNetherZombiePigman(), GenericStats::param_noArgs());
break;
case eTYPE_GHAST:
awardStat(GenericStats::killsGhast(), GenericStats::param_noArgs());
break;
case eTYPE_SLIME:
awardStat(GenericStats::killsSlime(), GenericStats::param_noArgs());
break;
case eTYPE_ENDERDRAGON:
awardStat(GenericStats::killsEnderdragon(), GenericStats::param_noArgs());
break;
}
}
else if( mob->GetType() == eTYPE_COW )
{
awardStat(GenericStats::killCow(), GenericStats::param_noArgs());
}
}
void Player::makeStuckInWeb()
{
if (!abilities.flying) LivingEntity::makeStuckInWeb();
}
Icon *Player::getItemInHandIcon(shared_ptr<ItemInstance> item, int layer)
{
Icon *icon = LivingEntity::getItemInHandIcon(item, layer);
if (item->id == Item::fishingRod->id && fishing != NULL)
{
icon = Item::fishingRod->getEmptyIcon();
}
else if (item->getItem()->hasMultipleSpriteLayers())
{
return item->getItem()->getLayerIcon(item->getAuxValue(), layer);
}
else if (useItem != NULL && item->id == Item::bow_Id)
{
int ticksHeld = (item->getUseDuration() - useItemDuration);
if (ticksHeld >= BowItem::MAX_DRAW_DURATION - 2)
{
return Item::bow->getDrawnIcon(2);
}
if (ticksHeld > (2 * BowItem::MAX_DRAW_DURATION) / 3)
{
return Item::bow->getDrawnIcon(1);
}
if (ticksHeld > 0)
{
return Item::bow->getDrawnIcon(0);
}
}
return icon;
}
shared_ptr<ItemInstance> Player::getArmor(int pos)
{
return inventory->getArmor(pos);
}
void Player::increaseXp(int i)
{
increaseScore(i);
int max = INT_MAX - totalExperience;
if (i > max)
{
i = max;
}
experienceProgress += (float) i / getXpNeededForNextLevel();
totalExperience += i;
while (experienceProgress >= 1)
{
experienceProgress = (experienceProgress - 1) * getXpNeededForNextLevel();
giveExperienceLevels(1);
experienceProgress /= getXpNeededForNextLevel();
}
}
void Player::giveExperienceLevels(int amount)
{
experienceLevel += amount;
if (experienceLevel < 0)
{
experienceLevel = 0;
experienceProgress = 0;
totalExperience = 0;
}
if (amount > 0 && experienceLevel % 5 == 0 && lastLevelUpTime < tickCount - SharedConstants::TICKS_PER_SECOND * 5.0f)
{
float vol = experienceLevel > 30 ? 1 : experienceLevel / 30.0f;
level->playEntitySound(shared_from_this(), eSoundType_RANDOM_LEVELUP, vol * 0.75f, 1);
lastLevelUpTime = tickCount;
}
}
int Player::getXpNeededForNextLevel()
{
// Update xp calculations from 1.3
if (experienceLevel >= 30)
{
return 17 + 15 * 3 + (experienceLevel - 30) * 7;
}
if (experienceLevel >= 15)
{
return 17 + (experienceLevel - 15) * 3;
}
return 17;
}
/**
* This method adds on to the player's exhaustion, which may decrease the
* player's food level.
*
* @param amount
* Amount of exhaustion to add, between 0 and 20 (setting it to
* 20 will guarantee that at least 1, and at most 4, food points
* are deducted). See FoodConstants for cost suggestions.
*/
void Player::causeFoodExhaustion(float amount)
{
if( isAllowedToIgnoreExhaustion() || ( isAllowedToFly() && abilities.flying) ) return;
if (abilities.invulnerable || hasInvulnerablePrivilege() ) return;
// 4J Stu - Added 1.8.2 bug fix (TU6) - If players cannot eat, then their food bar should not decrease due to exhaustion
if(app.GetGameHostOption(eGameHostOption_TrustPlayers) == 0 && getPlayerGamePrivilege(Player::ePlayerGamePrivilege_CannotBuild) != 0) return;
if (!level->isClientSide)
{
foodData.addExhaustion(amount);
}
}
FoodData *Player::getFoodData()
{
return &foodData;
}
bool Player::canEat(bool magicalItem)
{
return (magicalItem || foodData.needsFood()) && !abilities.invulnerable && !hasInvulnerablePrivilege();
}
bool Player::isHurt()
{
return getHealth() > 0 && getHealth() < getMaxHealth();
}
void Player::startUsingItem(shared_ptr<ItemInstance> instance, int duration)
{
if (instance == useItem) return;
useItem = instance;
useItemDuration = duration;
if (!level->isClientSide)
{
setUsingItemFlag(true);
}
// 4J-JEV, hook for ItemUsed event, and ironbelly achievement.
awardStat(GenericStats::itemsUsed(instance->getItem()->id),
GenericStats::param_itemsUsed(dynamic_pointer_cast<Player>(shared_from_this()),instance));
#if (!defined _DURANGO) && (defined _EXTENDED_ACHIEVEMENTS)
if ( (instance->getItem()->id == Item::rotten_flesh_Id) && (getFoodData()->getFoodLevel() == 0) )
awardStat(GenericStats::ironBelly(), GenericStats::param_ironBelly());
#endif
}
bool Player::mayDestroyBlockAt(int x, int y, int z)
{
if (abilities.mayBuild)
{
return true;
}
int t = level->getTile(x, y, z);
if (t > 0) {
Tile *tile = Tile::tiles[t];
if (tile->material->isDestroyedByHand())
{
return true;
}
else if (getSelectedItem() != NULL)
{
shared_ptr<ItemInstance> carried = getSelectedItem();
if (carried->canDestroySpecial(tile) || carried->getDestroySpeed(tile) > 1)
{
return true;
}
}
}
return false;
}
bool Player::mayUseItemAt(int x, int y, int z, int face, shared_ptr<ItemInstance> item)
{
if (abilities.mayBuild)
{
return true;
}
if (item != NULL)
{
return item->mayBePlacedInAdventureMode();
}
return false;
}
int Player::getExperienceReward(shared_ptr<Player> killedBy)
{
if (level->getGameRules()->getBoolean(GameRules::RULE_KEEPINVENTORY)) return 0;
int reward = experienceLevel * 7;
if (reward > 100)
{
return 100;
}
return reward;
}
bool Player::isAlwaysExperienceDropper()
{
// players always drop experience
return true;
}
wstring Player::getAName()
{
return name;
}
bool Player::shouldShowName()
{
return true;
}
void Player::restoreFrom(shared_ptr<Player> oldPlayer, bool restoreAll)
{
if(restoreAll)
{
inventory->replaceWith(oldPlayer->inventory);
setHealth(oldPlayer->getHealth());
foodData = oldPlayer->foodData;
experienceLevel = oldPlayer->experienceLevel;
totalExperience = oldPlayer->totalExperience;
experienceProgress = oldPlayer->experienceProgress;
setScore(oldPlayer->getScore());
portalEntranceDir = oldPlayer->portalEntranceDir;
}
else if (level->getGameRules()->getBoolean(GameRules::RULE_KEEPINVENTORY))
{
inventory->replaceWith(oldPlayer->inventory);
experienceLevel = oldPlayer->experienceLevel;
totalExperience = oldPlayer->totalExperience;
experienceProgress = oldPlayer->experienceProgress;
setScore(oldPlayer->getScore());
}
enderChestInventory = oldPlayer->enderChestInventory;
}
bool Player::makeStepSound()
{
return !abilities.flying;
}
void Player::onUpdateAbilities()
{
}
void Player::setGameMode(GameType *mode)
{
}
wstring Player::getName()
{
return name;
}
wstring Player::getDisplayName()
{
//PlayerTeam.formatNameForTeam(getTeam(), name);
// If player display name is not set, return name
return m_displayName.size() > 0 ? m_displayName : name;
}
wstring Player::getNetworkName()
{
// 4J: We can only transmit gamertag in network packets
return name;
}
Level *Player::getCommandSenderWorld()
{
return level;
}
shared_ptr<PlayerEnderChestContainer> Player::getEnderChestInventory()
{
return enderChestInventory;
}
shared_ptr<ItemInstance> Player::getCarried(int slot)
{
if (slot == 0) return inventory->getSelected();
return inventory->armor[slot - 1];
}
shared_ptr<ItemInstance> Player::getCarriedItem()
{
return inventory->getSelected();
}
void Player::setEquippedSlot(int slot, shared_ptr<ItemInstance> item)
{
inventory->armor[slot] = item;
}
bool Player::isInvisibleTo(shared_ptr<Player> player)
{
return isInvisible();
}
ItemInstanceArray Player::getEquipmentSlots()
{
return inventory->armor;
}
bool Player::isCapeHidden()
{
return getPlayerFlag(FLAG_HIDE_CAPE);
}
bool Player::isPushedByWater()
{
return !abilities.flying;
}
Scoreboard *Player::getScoreboard()
{
return level->getScoreboard();
}
Team *Player::getTeam()
{
return getScoreboard()->getPlayersTeam(name);
}
void Player::setAbsorptionAmount(float absorptionAmount)
{
if (absorptionAmount < 0) absorptionAmount = 0;
getEntityData()->set(DATA_PLAYER_ABSORPTION_ID, absorptionAmount);
}
float Player::getAbsorptionAmount()
{
return getEntityData()->getFloat(DATA_PLAYER_ABSORPTION_ID);
}
int Player::getTexture()
{
switch(m_skinIndex)
{
case eDefaultSkins_Skin0:
return TN_MOB_CHAR; // 4J - was L"/mob/char.png";
case eDefaultSkins_Skin1:
return TN_MOB_CHAR1; // 4J - was L"/mob/char1.png";
case eDefaultSkins_Skin2:
return TN_MOB_CHAR2; // 4J - was L"/mob/char2.png";
case eDefaultSkins_Skin3:
return TN_MOB_CHAR3; // 4J - was L"/mob/char3.png";
case eDefaultSkins_Skin4:
return TN_MOB_CHAR4; // 4J - was L"/mob/char4.png";
case eDefaultSkins_Skin5:
return TN_MOB_CHAR5; // 4J - was L"/mob/char5.png";
case eDefaultSkins_Skin6:
return TN_MOB_CHAR6; // 4J - was L"/mob/char6.png";
case eDefaultSkins_Skin7:
return TN_MOB_CHAR7; // 4J - was L"/mob/char7.png";
default:
return TN_MOB_CHAR; // 4J - was L"/mob/char.png";
}
}
int Player::hash_fnct(const shared_ptr<Player> k)
{
// TODO 4J Stu - Should we just be using the pointers and hashing them?
#ifdef __PS3__
return (int)boost::hash_value( k->name ); // 4J Stu - Names are completely unique?
#else
return (int)std::hash<wstring>{}(k->name); // 4J Stu - Names are completely unique?
#endif //__PS3__
}
bool Player::eq_test(const shared_ptr<Player> x, const shared_ptr<Player> y)
{
// TODO 4J Stu - Should we just be using the pointers and comparing them for equality?
return x->name.compare( y->name ) == 0; // 4J Stu - Names are completely unique?
}
unsigned int Player::getPlayerGamePrivilege(EPlayerGamePrivileges privilege)
{
return Player::getPlayerGamePrivilege(m_uiGamePrivileges,privilege);
}
unsigned int Player::getPlayerGamePrivilege(unsigned int uiGamePrivileges, EPlayerGamePrivileges privilege)
{
if( privilege == ePlayerGamePrivilege_All )
{
return uiGamePrivileges;
}
else if (privilege < ePlayerGamePrivilege_MAX )
{
return uiGamePrivileges&(1<<privilege);
}
return 0;
}
void Player::setPlayerGamePrivilege(EPlayerGamePrivileges privilege, unsigned int value)
{
Player::setPlayerGamePrivilege(m_uiGamePrivileges,privilege,value);
}
void Player::setPlayerGamePrivilege(unsigned int &uiGamePrivileges, EPlayerGamePrivileges privilege, unsigned int value)
{
if( privilege == ePlayerGamePrivilege_All )
{
uiGamePrivileges = value;
}
else if(privilege ==ePlayerGamePrivilege_HOST)
{
if(value == 0)
{
Player::setPlayerGamePrivilege(uiGamePrivileges,ePlayerGamePrivilege_Op,0);
}
else
{
Player::setPlayerGamePrivilege(uiGamePrivileges,ePlayerGamePrivilege_Op,1);
Player::setPlayerGamePrivilege(uiGamePrivileges,ePlayerGamePrivilege_CanToggleInvisible,1);
Player::setPlayerGamePrivilege(uiGamePrivileges,ePlayerGamePrivilege_CanToggleFly,1);
Player::setPlayerGamePrivilege(uiGamePrivileges,ePlayerGamePrivilege_CanToggleClassicHunger,1);
Player::setPlayerGamePrivilege(uiGamePrivileges,ePlayerGamePrivilege_CanTeleport,1);
}
}
else if (privilege < ePlayerGamePrivilege_MAX )
{
if(value!=0)
{
uiGamePrivileges|=(1<<privilege);
}
else
{
// Some privileges will turn other things off as well
switch(privilege)
{
case ePlayerGamePrivilege_CanToggleInvisible:
Player::setPlayerGamePrivilege(uiGamePrivileges,ePlayerGamePrivilege_Invisible,0);
Player::setPlayerGamePrivilege(uiGamePrivileges,ePlayerGamePrivilege_Invulnerable,0);
break;
case ePlayerGamePrivilege_CanToggleFly:
Player::setPlayerGamePrivilege(uiGamePrivileges,ePlayerGamePrivilege_CanFly,0);
break;
case ePlayerGamePrivilege_CanToggleClassicHunger:
Player::setPlayerGamePrivilege(uiGamePrivileges,ePlayerGamePrivilege_ClassicHunger,0);
break;
case ePlayerGamePrivilege_Op:
Player::setPlayerGamePrivilege(uiGamePrivileges,ePlayerGamePrivilege_CanToggleInvisible,0);
Player::setPlayerGamePrivilege(uiGamePrivileges,ePlayerGamePrivilege_CanToggleFly,0);
Player::setPlayerGamePrivilege(uiGamePrivileges,ePlayerGamePrivilege_CanToggleClassicHunger,0);
Player::setPlayerGamePrivilege(uiGamePrivileges,ePlayerGamePrivilege_CanTeleport,0);
break;
}
// off
uiGamePrivileges&=~(1<<privilege);
}
}
}
bool Player::isAllowedToUse(Tile *tile)
{
bool allowed = true;
if(tile != NULL && app.GetGameHostOption(eGameHostOption_TrustPlayers) == 0)
{
allowed = false;
if(getPlayerGamePrivilege(Player::ePlayerGamePrivilege_CanUseDoorsAndSwitches) != 0)
{
switch(tile->id)
{
case Tile::door_wood_Id:
case Tile::button_stone_Id:
case Tile::button_wood_Id:
case Tile::lever_Id:
case Tile::fenceGate_Id:
case Tile::trapdoor_Id:
allowed = true;
break;
}
}
if(getPlayerGamePrivilege(Player::ePlayerGamePrivilege_CanUseContainers) != 0)
{
switch(tile->id)
{
case Tile::chest_Id:
case Tile::furnace_Id:
case Tile::furnace_lit_Id:
case Tile::dispenser_Id:
case Tile::brewingStand_Id:
case Tile::enchantTable_Id:
case Tile::workBench_Id:
case Tile::anvil_Id:
case Tile::enderChest_Id:
allowed = true;
break;
}
}
if(!allowed && getPlayerGamePrivilege(Player::ePlayerGamePrivilege_CannotBuild) == 0)
{
switch(tile->id)
{
case Tile::door_wood_Id:
case Tile::button_stone_Id:
case Tile::button_wood_Id:
case Tile::lever_Id:
case Tile::fenceGate_Id:
case Tile::trapdoor_Id:
case Tile::chest_Id:
case Tile::furnace_Id:
case Tile::furnace_lit_Id:
case Tile::dispenser_Id:
case Tile::brewingStand_Id:
case Tile::enchantTable_Id:
case Tile::workBench_Id:
case Tile::anvil_Id:
case Tile::enderChest_Id:
allowed = false;
break;
default:
allowed = true;
break;
}
}
}
return allowed;
}
bool Player::isAllowedToUse(shared_ptr<ItemInstance> item)
{
bool allowed = true;
if(item != NULL && app.GetGameHostOption(eGameHostOption_TrustPlayers) == 0)
{
if(getPlayerGamePrivilege(Player::ePlayerGamePrivilege_CannotBuild) != 0)
{
allowed = false;
}
// 4J Stu - TU8 Players should always be able to eat food items, even if the build option is turned of
switch(item->id)
{
// food
case Item::mushroomStew_Id:
case Item::apple_Id:
case Item::bread_Id:
case Item::porkChop_raw_Id:
case Item::porkChop_cooked_Id:
case Item::apple_gold_Id:
case Item::fish_raw_Id:
case Item::fish_cooked_Id:
case Item::cookie_Id:
case Item::beef_cooked_Id:
case Item::beef_raw_Id:
case Item::chicken_cooked_Id:
case Item::chicken_raw_Id:
case Item::melon_Id:
case Item::rotten_flesh_Id:
// bow
case Item::bow_Id:
case Item::sword_diamond_Id:
case Item::sword_gold_Id:
case Item::sword_iron_Id:
case Item::sword_stone_Id:
case Item::sword_wood_Id:
allowed = true;
break;
}
}
return allowed;
}
bool Player::isAllowedToInteract(shared_ptr<Entity> target)
{
bool allowed = true;
if(app.GetGameHostOption(eGameHostOption_TrustPlayers) == 0)
{
if (target->instanceof(eTYPE_MINECART))
{
if (getPlayerGamePrivilege(Player::ePlayerGamePrivilege_CanUseContainers) == 0)
{
shared_ptr<Minecart> minecart = dynamic_pointer_cast<Minecart>( target );
if (minecart->getType() == Minecart::TYPE_CHEST)
allowed = false;
}
}
else
{
if(getPlayerGamePrivilege(Player::ePlayerGamePrivilege_CannotBuild) != 0)
{
allowed = false;
}
if(getPlayerGamePrivilege(Player::ePlayerGamePrivilege_CannotMine) != 0)
{
allowed = false;
}
}
}
return allowed;
}
bool Player::isAllowedToMine()
{
bool allowed = true;
if(app.GetGameHostOption(eGameHostOption_TrustPlayers) == 0)
{
if(getPlayerGamePrivilege(Player::ePlayerGamePrivilege_CannotMine) != 0)
{
allowed = false;
}
}
return allowed;
}
bool Player::isAllowedToAttackPlayers()
{
bool allowed = true;
if( hasInvisiblePrivilege() || ((app.GetGameHostOption(eGameHostOption_TrustPlayers) == 0) && getPlayerGamePrivilege(Player::ePlayerGamePrivilege_CannotAttackPlayers)) )
{
allowed = false;
}
return allowed;
}
bool Player::isAllowedToAttackAnimals()
{
bool allowed = true;
if( (app.GetGameHostOption(eGameHostOption_TrustPlayers) == 0) && getPlayerGamePrivilege(Player::ePlayerGamePrivilege_CannotAttackAnimals) )
{
allowed = false;
}
return allowed;
}
bool Player::isAllowedToHurtEntity(shared_ptr<Entity> target)
{
bool allowed = true;
if(!isAllowedToMine())
{
switch(target->GetType())
{
case eTYPE_HANGING_ENTITY:
case eTYPE_PAINTING:
case eTYPE_ITEM_FRAME:
// 4J-JEV: Fix for #88212,
// Untrusted players shouldn't be able to damage minecarts or boats.
case eTYPE_BOAT:
case eTYPE_MINECART:
allowed = false;
break;
};
}
return allowed;
}
bool Player::isAllowedToFly()
{
bool allowed = false;
if(app.GetGameHostOption(eGameHostOption_HostCanFly) != 0 && getPlayerGamePrivilege(Player::ePlayerGamePrivilege_CanFly) != 0)
{
allowed = true;
}
return allowed;
}
bool Player::isAllowedToIgnoreExhaustion()
{
bool allowed = false;
if( (app.GetGameHostOption(eGameHostOption_HostCanChangeHunger) != 0 && getPlayerGamePrivilege(Player::ePlayerGamePrivilege_ClassicHunger) != 0) ||
(isAllowedToFly() && abilities.flying) )
{
allowed = true;
}
return allowed;
}
bool Player::isAllowedToTeleport()
{
bool allowed = false;
if( isModerator() && getPlayerGamePrivilege(Player::ePlayerGamePrivilege_CanTeleport) != 0)
{
allowed = true;
}
return allowed;
}
bool Player::hasInvisiblePrivilege()
{
bool enabled = false;
if(app.GetGameHostOption(eGameHostOption_HostCanBeInvisible) != 0 && getPlayerGamePrivilege(Player::ePlayerGamePrivilege_Invisible) != 0)
{
enabled = true;
}
return enabled;
}
bool Player::hasInvulnerablePrivilege()
{
bool enabled = false;
if(app.GetGameHostOption(eGameHostOption_HostCanBeInvisible) != 0 && getPlayerGamePrivilege(Player::ePlayerGamePrivilege_Invulnerable) != 0)
{
enabled = true;
}
return enabled;
}
bool Player::isModerator()
{
return getPlayerGamePrivilege(Player::ePlayerGamePrivilege_Op) != 0;
}
void Player::enableAllPlayerPrivileges(unsigned int &uigamePrivileges, bool enable)
{
Player::setPlayerGamePrivilege(uigamePrivileges, Player::ePlayerGamePrivilege_CannotMine, enable?0:1);
Player::setPlayerGamePrivilege(uigamePrivileges, Player::ePlayerGamePrivilege_CannotBuild, enable?0:1);
Player::setPlayerGamePrivilege(uigamePrivileges, Player::ePlayerGamePrivilege_CannotAttackPlayers, enable?0:1);
Player::setPlayerGamePrivilege(uigamePrivileges, Player::ePlayerGamePrivilege_CannotAttackAnimals, enable?0:1);
Player::setPlayerGamePrivilege(uigamePrivileges, Player::ePlayerGamePrivilege_CanUseDoorsAndSwitches, enable?1:0);
Player::setPlayerGamePrivilege(uigamePrivileges, Player::ePlayerGamePrivilege_CanUseContainers, enable?1:0);
}
void Player::enableAllPlayerPrivileges(bool enable)
{
Player::enableAllPlayerPrivileges(m_uiGamePrivileges,enable);
}
bool Player::canCreateParticles()
{
return !hasInvisiblePrivilege();
}
vector<ModelPart *> *Player::GetAdditionalModelParts()
{
if(m_ppAdditionalModelParts==NULL && !m_bCheckedForModelParts)
{
bool hasCustomTexture = !customTextureUrl.empty();
bool customTextureIsDefaultSkin = customTextureUrl.substr(0,3).compare(L"def") == 0;
// see if we can find the parts
m_ppAdditionalModelParts=app.GetAdditionalModelParts(m_dwSkinId);
// If it's a default texture (which has no parts), we have the parts, or we already have the texture (in which case we should have parts if there are any) then we are done
if(!hasCustomTexture || customTextureIsDefaultSkin || m_ppAdditionalModelParts != NULL || app.IsFileInMemoryTextures(customTextureUrl))
{
m_bCheckedForModelParts=true;
}
if(m_ppAdditionalModelParts == NULL && !m_bCheckedDLCForModelParts)
{
m_bCheckedDLCForModelParts = true;
// we don't have the data from the dlc skin yet
app.DebugPrintf("m_bCheckedForModelParts Couldn't get model parts for skin %X\n",m_dwSkinId);
// do we have it from the DLC pack?
DLCSkinFile *pDLCSkinFile = app.m_dlcManager.getSkinFile(this->customTextureUrl);
if(pDLCSkinFile!=NULL)
{
DWORD dwBoxC=pDLCSkinFile->getAdditionalBoxesCount();
if(dwBoxC!=0)
{
app.DebugPrintf("m_bCheckedForModelParts Got model parts from DLCskin for skin %X\n",m_dwSkinId);
m_ppAdditionalModelParts=app.SetAdditionalSkinBoxes(m_dwSkinId,pDLCSkinFile->getAdditionalBoxes());
}
app.SetAnimOverrideBitmask(pDLCSkinFile->getSkinID(),pDLCSkinFile->getAnimOverrideBitmask());
m_bCheckedForModelParts=true;
}
}
if(m_bCheckedForModelParts) setAnimOverrideBitmask(getSkinAnimOverrideBitmask(m_dwSkinId));
}
return m_ppAdditionalModelParts;
}
void Player::SetAdditionalModelParts(vector<ModelPart *> *ppAdditionalModelParts)
{
m_ppAdditionalModelParts=ppAdditionalModelParts;
}
#if defined(__PS3__) || defined(__ORBIS__)
Player::ePlayerNameValidState Player::GetPlayerNameValidState(void)
{
return m_ePlayerNameValidState;
}
void Player::SetPlayerNameValidState(bool bState)
{
if(bState)
{
m_ePlayerNameValidState=ePlayerNameValid_True;
}
else
{
m_ePlayerNameValidState=ePlayerNameValid_False;
}
}
#endif