// 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 "../Platform/stdafx.h" #include "../Util/JavaMath.h" #include "../Headers/net.minecraft.h" #include "../Headers/net.minecraft.world.h" #include "../Headers/net.minecraft.stats.h" #include "../Headers/net.minecraft.world.level.h" #include "../Headers/net.minecraft.world.level.chunk.h" #include "../Headers/net.minecraft.world.phys.h" #include "../Headers/net.minecraft.world.entity.h" #include "../Headers/net.minecraft.world.entity.ai.attributes.h" #include "../Headers/net.minecraft.world.entity.animal.h" #include "../Headers/net.minecraft.world.entity.boss.h" #include "../Headers/net.minecraft.world.entity.monster.h" #include "../Headers/net.minecraft.world.entity.item.h" #include "../Headers/net.minecraft.world.item.h" #include "../Headers/net.minecraft.world.item.enchantment.h" #include "../Headers/net.minecraft.world.level.dimension.h" #include "../Headers/net.minecraft.world.level.material.h" #include "../Headers/net.minecraft.world.level.tile.h" #include "../Headers/net.minecraft.world.level.tile.entity.h" #include "../Headers/net.minecraft.world.scores.h" #include "../Headers/net.minecraft.world.scores.criteria.h" #include "../Headers/net.minecraft.world.entity.projectile.h" #include "../Headers/net.minecraft.world.inventory.h" #include "../Headers/net.minecraft.world.damagesource.h" #include "../Headers/net.minecraft.world.effect.h" #include "../Headers/net.minecraft.world.food.h" #include "../Containers/Inventory.h" #include "Player.h" #include "../Util/ParticleTypes.h" #include #include #include "../../Minecraft.Client/Textures/Textures.h" #include #include "../../Minecraft.Client/Player/LocalPlayer.h" #include "../../Minecraft.Client/Rendering/Models/HumanoidModel.h" #include "../Util/SoundTypes.h" void Player::_init() { registerAttributes(); setHealth(getMaxHealth()); inventory = std::shared_ptr(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 = std::shared_ptr( new PlayerEnderChestContainer()); m_bAwardedOnARail = false; } Player::Player(Level* level, const std::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, (uint8_t)0); entityData->define(DATA_PLAYER_ABSORPTION_ID, (float)0); entityData->define(DATA_SCORE_ID, (int)0); } std::shared_ptr 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, std::dynamic_pointer_cast(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) { std::shared_ptr 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( std::dynamic_pointer_cast(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(std::dynamic_pointer_cast(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( std::shared_ptr( new ItemInstance(Tile::recordPlayer) ) ); this->drop( std::shared_ptr( new ItemInstance(Item::map) ) ); this->drop( std::shared_ptr( new ItemInstance(Item::record_01) ) ); this->drop( std::shared_ptr( new ItemInstance(Item::record_02) ) ); this->drop( std::shared_ptr(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( new ItemInstance(Tile::recordPlayer) ) ); // this->drop( shared_ptr( new ItemInstance(Item::map) ) ); // this->drop( shared_ptr( new ItemInstance(Item::record_01) ) ); // this->drop( shared_ptr( new ItemInstance(Item::record_02) ) ); // this->drop( shared_ptr(new ItemInstance( Item::pickAxe_diamond, 1 )) ); // #endif #endif #ifdef _DURANGO // Drop some items so we have them in inventory to play with this->drop( std::shared_ptr( new ItemInstance(Tile::recordPlayer) ) ); this->drop( std::shared_ptr( new ItemInstance(Item::map) ) ); this->drop( std::shared_ptr( new ItemInstance(Item::record_01) ) ); this->drop( std::shared_ptr( new ItemInstance(Item::record_02) ) ); this->drop( std::shared_ptr(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( new ItemInstance( // Item::milk, 3 ) ) ); this->drop( shared_ptr( new // ItemInstance( Item::sugar, 2 ) ) ); this->drop( new ItemInstance( // Tile::stoneBrick, 8 ) ); this->drop( shared_ptr( // new ItemInstance( Item::wheat, 3 ) ) ); this->drop( // shared_ptr( new ItemInstance( Item::egg, 1 ) ) ); // this->drop( new ItemInstance( Item::bow, 1 ) ); // this->drop( new ItemInstance( Item::arrow, 10 ) ); // this->drop( shared_ptr( new ItemInstance( // Item::saddle, 10 ) ) ); this->drop( shared_ptr( new // ItemInstance( Tile::fence, 64 ) ) ); this->drop( // shared_ptr( new ItemInstance( Tile::fence, 64 ) ) // ); this->drop( shared_ptr( new ItemInstance( // Tile::fence, 64 ) ) ); // shared_ptr mob = // std::dynamic_pointer_cast(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 mob1 = std::dynamic_pointer_cast(Wolf::_class->newInstance( level )); mob1->moveTo(x+1, y, z+1, level->random->nextFloat() * 360, 0); level->addEntity(mob1); shared_ptr mob2 = std::dynamic_pointer_cast(Wolf::_class->newInstance( level )); mob2->moveTo(x+2, y, z+1, level->random->nextFloat() * 360, 0); level->addEntity(mob2); shared_ptr mob3 = std::dynamic_pointer_cast(Wolf::_class->newInstance( level )); mob3->moveTo(x+1, y, z+2, level->random->nextFloat() * 360, 0); level->addEntity(mob3); shared_ptr mob4 = std::dynamic_pointer_cast(Wolf::_class->newInstance( level )); mob4->moveTo(x+3, y, z+1, level->random->nextFloat() * 360, 0); level->addEntity(mob4); shared_ptr mob5 = std::dynamic_pointer_cast(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( std::shared_ptr( new ItemInstance( Item::minecart, 1 ) ) ); this->drop( std::shared_ptr( new ItemInstance( Tile::goldenRail, 10 ) ) ); this->drop( std::shared_ptr( 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( std::dynamic_pointer_cast(shared_from_this()), iSound, volume, pitch); } void Player::spawnEatParticles(std::shared_ptr 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{(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{(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; std::shared_ptr itemInstance = useItem->useTimeDepleted( level, std::dynamic_pointer_cast(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(uint8_t 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(std::shared_ptr e) { if (riding != NULL && e == NULL) { if (!level->isClientSide) findStandUpPosition(riding); if (riding != NULL) { riding->rider = std::weak_ptr(); } riding = nullptr; return; } 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(std::uint32_t 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 std::uint32_t ugcSkinIndex = GET_UGC_SKIN_ID_FROM_BITMASK(skinId); std::uint32_t 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 *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) { const int additionalBoxCount = pDLCSkinFile->getAdditionalBoxesCount(); if(additionalBoxCount != 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(std::uint32_t 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< 0) { this->customTextureUrl2 = Player::getCapePathFromId(capeId); } else { MOJANG_DATA* pMojangData = app.GetMojangDataForXuid(getOnlineXuid()); if (pMojangData) { // Cape if (pMojangData->wchCape[0] != 0) { this->customTextureUrl2 = pMojangData->wchCape; } else { if (app.DefaultCapeExists()) { this->customTextureUrl2 = std::wstring(L"Special_Cape.png"); } else { this->customTextureUrl2 = std::wstring(L""); } } } else { // if there is a custom default cloak, then set it here if (app.DefaultCapeExists()) { this->customTextureUrl2 = std::wstring(L"Special_Cape.png"); } else { this->customTextureUrl2 = std::wstring(L""); } } } } std::uint32_t Player::getCapeIdFromPath(const std::wstring& cape) { bool dlcCape = false; std::uint32_t capeId = 0; if (cape.size() >= 14) { dlcCape = cape.substr(0, 3).compare(L"dlc") == 0; std::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; } std::wstring Player::getCapePathFromId(std::uint32_t 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 { std::uint32_t ugcCapeIndex = GET_UGC_SKIN_ID_FROM_BITMASK(capeId); std::uint32_t 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[0] != 0) { 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; std::shared_ptr pig = std::dynamic_pointer_cast(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; 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); } std::vector >* entities = level->getEntities(shared_from_this(), &pickupArea); if (entities != NULL) { AUTO_VAR(itEnd, entities->end()); for (AUTO_VAR(it, entities->begin()); it != itEnd; it++) { std::shared_ptr e = *it; // entities->at(i); if (!e->removed) { touch(e); } } } } } void Player::touch(std::shared_ptr entity) { entity->playerTouch(std::dynamic_pointer_cast(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(std::shared_ptr(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(std::shared_ptr victim, int awardPoints) { increaseScore(awardPoints); std::vector* 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_VAR(it, objectives->begin()); it != objectives->end(); ++it) { Objective* objective = *it; Score* score = getScoreboard()->getPlayerScore(getAName(), objective); score->increment(); } } } bool Player::isShootable() { return true; } bool Player::isCreativeModeAllowed() { return true; } std::shared_ptr Player::drop(bool all) { return drop(inventory->removeItem(inventory->selected, all && inventory->getSelected() != NULL ? inventory->getSelected()->count : 1), false); } std::shared_ptr Player::drop(std::shared_ptr item) { return drop(item, false); } std::shared_ptr Player::drop(std::shared_ptr item, bool randomly) { if (item == NULL) return nullptr; if (item->count == 0) return nullptr; std::shared_ptr thrownItem = std::shared_ptr( 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(std::shared_ptr thrownItem) { level->addEntity(thrownItem); } float Player::getDestroySpeed(Tile* tile, bool hasProperTool) { float speed = inventory->getDestroySpeed(tile); if (speed > 1) { int efficiency = EnchantmentHelper::getDiggingBonus( std::dynamic_pointer_cast(shared_from_this())); std::shared_ptr 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( std::dynamic_pointer_cast(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* inventoryList = (ListTag*)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* enderItemsList = (ListTag*)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())); 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(std::shared_ptr container) { return true; } bool Player::openHopper(std::shared_ptr container) { return true; } bool Player::openHopper(std::shared_ptr container) { return true; } bool Player::openHorseInventory(std::shared_ptr horse, std::shared_ptr container) { return true; } bool Player::startEnchanting(int x, int y, int z, const std::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; std::shared_ptr attacker = source->getEntity(); if (attacker != NULL && attacker->instanceof(eTYPE_ARROW)) { std::shared_ptr arrow = std::dynamic_pointer_cast(attacker); if (arrow->owner != NULL) { attacker = arrow->owner; } } return LivingEntity::hurt(source, dmg); } bool Player::canHarmPlayer(std::shared_ptr 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(std::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 = std::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(std::shared_ptr container) { return true; } bool Player::openTrap(std::shared_ptr container) { return true; } void Player::openTextEdit(std::shared_ptr sign) {} bool Player::openBrewingStand( std::shared_ptr brewingStand) { return true; } bool Player::openBeacon(std::shared_ptr beacon) { return true; } bool Player::openTrading(std::shared_ptr traderTarget, const std::wstring& name) { return true; } /** * Opens an iteminstance-dependent user interface. * * @param itemInstance */ void Player::openItemInstanceGui(std::shared_ptr itemInstance) {} bool Player::interact(std::shared_ptr entity) { std::shared_ptr thisPlayer = std::dynamic_pointer_cast(shared_from_this()); std::shared_ptr item = getSelectedItem(); std::shared_ptr 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, std::dynamic_pointer_cast(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; } std::shared_ptr Player::getSelectedItem() { return inventory->getSelected(); } void Player::removeSelectedItem() { inventory->setItem(inventory->selected, nullptr); } double Player::getRidingHeight() { return heightOffset - 0.5f; } void Player::attack(std::shared_ptr 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)) { std::shared_ptr thisPlayer = std::dynamic_pointer_cast(shared_from_this()); std::shared_ptr mob = std::dynamic_pointer_cast(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( std::dynamic_pointer_cast(shared_from_this())); if (entity->instanceof(eTYPE_MOB) && fireAspect > 0 && !entity->isOnFire()) { setOnFireTemporatily = true; entity->setOnFire(1); } DamageSource* damageSource = DamageSource::playerAttack( std::dynamic_pointer_cast(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)) { std::shared_ptr mob = std::dynamic_pointer_cast(entity); ThornsEnchantment::doThornsAfterAttack(shared_from_this(), mob, random); } } std::shared_ptr item = getSelectedItem(); std::shared_ptr hurtTarget = entity; if (entity->instanceof(eTYPE_MULTIENTITY_MOB_PART)) { std::shared_ptr multiMob = std::dynamic_pointer_cast( (std::dynamic_pointer_cast(entity)) ->parentMob.lock()); if ((multiMob != NULL) && multiMob->instanceof(eTYPE_LIVINGENTITY)) { hurtTarget = std::dynamic_pointer_cast(multiMob); } } if ((item != NULL) && hurtTarget->instanceof(eTYPE_LIVINGENTITY)) { item->hurtEnemy( std::dynamic_pointer_cast(hurtTarget), std::dynamic_pointer_cast(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(std::shared_ptr entity) {} void Player::magicCrit(std::shared_ptr entity) {} void Player::respawn() { deathFadeCounter = 0; } void Player::animateRespawn(std::shared_ptr 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( std::dynamic_pointer_cast(shared_from_this())); if (containerMenu != NULL) { containerMenu->removed( std::dynamic_pointer_cast(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; AABB monster_bb = AABB(x, y, z, x, y, z).grow(hRange, vRange, hRange); std::vector >* monsters = level->getEntitiesOfClass(typeid(Monster), &monster_bb); 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) { uint8_t currentValue = entityData->getByte(DATA_PLAYER_FLAGS_ID); if (value) { entityData->set(DATA_PLAYER_FLAGS_ID, (uint8_t)(currentValue | (1 << flag))); } else { entityData->set(DATA_PLAYER_FLAGS_ID, (uint8_t)(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(std::shared_ptr 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; default: break; } } else if (mob->GetType() == eTYPE_COW) { awardStat(GenericStats::killCow(), GenericStats::param_noArgs()); } } void Player::makeStuckInWeb() { if (!abilities.flying) LivingEntity::makeStuckInWeb(); } Icon* Player::getItemInHandIcon(std::shared_ptr 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; } std::shared_ptr 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(std::shared_ptr 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( std::dynamic_pointer_cast(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) { std::shared_ptr 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, std::shared_ptr item) { if (abilities.mayBuild) { return true; } if (item != NULL) { return item->mayBePlacedInAdventureMode(); } return false; } int Player::getExperienceReward(std::shared_ptr 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; } std::wstring Player::getAName() { return name; } bool Player::shouldShowName() { return true; } void Player::restoreFrom(std::shared_ptr 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) {} std::wstring Player::getName() { return name; } std::wstring Player::getDisplayName() { // PlayerTeam.formatNameForTeam(getTeam(), name); // If player display name is not set, return name return m_displayName.size() > 0 ? m_displayName : name; } std::wstring Player::getNetworkName() { // 4J: We can only transmit gamertag in network packets return name; } Level* Player::getCommandSenderWorld() { return level; } std::shared_ptr Player::getEnderChestInventory() { return enderChestInventory; } std::shared_ptr Player::getCarried(int slot) { if (slot == 0) return inventory->getSelected(); return inventory->armor[slot - 1]; } std::shared_ptr Player::getCarriedItem() { return inventory->getSelected(); } void Player::setEquippedSlot(int slot, std::shared_ptr item) { inventory->armor[slot] = item; } bool Player::isInvisibleTo(std::shared_ptr 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 std::shared_ptr 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()(k->name); #endif // __PS3__ } bool Player::eq_test(const std::shared_ptr x, const std::shared_ptr 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; default: 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; default: 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; default: 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(std::shared_ptr 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; default: break; } } return allowed; } bool Player::isAllowedToInteract(std::shared_ptr target) { bool allowed = true; if (app.GetGameHostOption(eGameHostOption_TrustPlayers) == 0) { if (target->instanceof(eTYPE_MINECART)) { if (getPlayerGamePrivilege( Player::ePlayerGamePrivilege_CanUseContainers) == 0) { std::shared_ptr minecart = std::dynamic_pointer_cast(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(std::shared_ptr 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; default: 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(); } std::vector* 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) { const int additionalBoxCount = pDLCSkinFile->getAdditionalBoxesCount(); if (additionalBoxCount != 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( std::vector* 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