diff options
| author | daoge <3523206925@qq.com> | 2026-03-03 03:04:10 +0800 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-03-03 03:04:10 +0800 |
| commit | b3feddfef372618c8a9d7a0abcaf18cfad866c18 (patch) | |
| tree | 267761c3bb39241ba5c347bfbe2254d06686e287 /Minecraft.World/Player.cpp | |
| parent | 84c31a2331f7a0ec85b9d438992e244f60e5020f (diff) | |
feat: TU19 (Dec 2014) Features & Content (#155)
* try to resolve merge conflict
* feat: TU19 (Dec 2014) Features & Content (#32)
* December 2014 files
* Working release build
* Fix compilation issues
* Add sound to Windows64Media
* Add DLC content and force Tutorial DLC
* Revert "Add DLC content and force Tutorial DLC"
This reverts commit 97a43994725008e35fceb984d5549df9c8cea470.
* Disable broken light packing
* Disable breakpoint during DLC texture map load
Allows DLC loading but the DLC textures are still broken
* Fix post build not working
* ...
* fix vs2022 build
* fix cmake build
---------
Co-authored-by: Loki <lokirautio@gmail.com>
Diffstat (limited to 'Minecraft.World/Player.cpp')
| -rw-r--r-- | Minecraft.World/Player.cpp | 829 |
1 files changed, 490 insertions, 339 deletions
diff --git a/Minecraft.World/Player.cpp b/Minecraft.World/Player.cpp index d4832e40..700935f6 100644 --- a/Minecraft.World/Player.cpp +++ b/Minecraft.World/Player.cpp @@ -15,7 +15,9 @@ #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" @@ -24,6 +26,8 @@ #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" @@ -43,52 +47,35 @@ void Player::_init() { + registerAttributes(); + setHealth(getMaxHealth()); inventory = shared_ptr<Inventory>( new Inventory( this ) ); userType = 0; - score = 0; oBob = bob = 0.0f; - swinging = false; - swingTime = 0; - - //string name; - dimension = 0; - //string cloakTexture; 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; - - changingDimensionDelay = 20; - - - isInsidePortal = false; - - - portalTime = oPortalTime = 0.0f; - - dmgSpill = 0; - - fishing = nullptr; distanceWalk = distanceSwim = distanceFall = distanceClimb = distanceMinecart = distanceBoat = distancePig = 0; @@ -106,6 +93,8 @@ void Player::_init() defaultWalkSpeed = 0.1f; defaultFlySpeed = 0.02f; + lastLevelUpTime = 0; + m_uiGamePrivileges = 0; m_ppAdditionalModelParts=NULL; @@ -121,14 +110,13 @@ void Player::_init() m_bAwardedOnARail=false; } -Player::Player(Level *level) : Mob( level ) +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(); - // 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 - health = getMaxHealth(); + this->name = name; _init(); MemSect(11); @@ -139,14 +127,12 @@ Player::Player(Level *level) : Mob( level ) heightOffset = 1.62f; Pos *spawnPos = level->getSharedSpawnPos(); - this->moveTo(spawnPos->x + 0.5, spawnPos->y + 1, spawnPos->z + 0.5, 0, 0); + moveTo(spawnPos->x + 0.5, spawnPos->y + 1, spawnPos->z + 0.5, 0, 0); delete spawnPos; - modelName = L"humanoid"; rotOffs = 180; flameTime = 20; - textureIdx = TN_MOB_CHAR; // 4J - was L"/mob/char.png"; m_skinIndex = eDefaultSkins_Skin0; m_playerIndex = 0; m_dwSkinId = 0; @@ -158,7 +144,12 @@ Player::Player(Level *level) : Mob( level ) //m_bShownOnMaps = true; setShowOnMaps(app.GetGameHostOption(eGameHostOption_Gamertags)!=0?true:false); m_bIsGuest = false; - m_UUID = L""; + +#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() @@ -173,17 +164,20 @@ Player::~Player() //if( containerMenu != inventoryMenu ) delete containerMenu; } -int Player::getMaxHealth() +void Player::registerAttributes() { - return MAX_HEALTH; + LivingEntity::registerAttributes(); + + getAttributes()->registerAttribute(SharedMonsterAttributes::ATTACK_DAMAGE)->setBaseValue(1); } void Player::defineSynchedData() { - this->Mob::defineSynchedData(); + LivingEntity::defineSynchedData(); entityData->define(DATA_PLAYER_FLAGS_ID, (byte) 0); - entityData->define(DATA_PLAYER_RUNNING_ID, (byte) 0); + entityData->define(DATA_PLAYER_ABSORPTION_ID, (float) 0); + entityData->define(DATA_SCORE_ID, (int) 0); } shared_ptr<ItemInstance> Player::getUseItem() @@ -240,8 +234,8 @@ bool Player::isBlocking() return isUsingItem() && Item::items[useItem->id]->getUseAnimation(useItem) == UseAnim_block; } - -void Player::tick() +// 4J Stu - Added for things that should only be ticked once per simulation frame +void Player::updateFrameTick() { if (useItem != NULL) { @@ -308,7 +302,17 @@ void Player::tick() deathFadeCounter = DEATHFADE_DURATION; } } - this->Mob::tick(); +} + +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) { @@ -459,62 +463,61 @@ void Player::tick() this->drop( shared_ptr<ItemInstance>( new ItemInstance( Tile::goldenRail, 10 ) ) ); this->drop( shared_ptr<ItemInstance>( new ItemInstance( Tile::lever, 10 ) ) ); - level->setTime( 0 ); int poweredCount = 0; for(int i = 10; i < 2800; ++i) { - level->setTile(x+i,y-1,z-2,Tile::quartzBlock_Id); - level->setTile(x+i,y,z-2,Tile::quartzBlock_Id); - level->setTile(x+i,y+1,z-2,Tile::quartzBlock_Id); - level->setTile(x+i,y+2,z-2,Tile::lightGem_Id); - level->setTile(x+i,y+3,z-2,Tile::quartzBlock_Id); + 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->setTile(x+i,y-1,z-1,Tile::stoneBrick_Id); + level->setTileAndData(x+i,y-1,z-1,Tile::stoneBrick_Id,0,Tile::UPDATE_CLIENTS); if(i%20 == 0) { - level->setTile(x+i,y,z-1,Tile::notGate_on_Id); + level->setTileAndData(x+i,y,z-1,Tile::redstoneTorch_on_Id,0,Tile::UPDATE_CLIENTS); poweredCount = 4; } else { - level->setTile(x+i,y,z-1,0); + level->setTileAndData(x+i,y,z-1,0,0,Tile::UPDATE_CLIENTS); } - level->setTile(x+i,y+1,z-1,0); - level->setTile(x+i,y+2,z-1,0); - level->setTile(x+i,y+3,z-1,0); + 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->setTile(x+i,y-1,z,Tile::stoneBrick_Id); + level->setTileAndData(x+i,y-1,z,Tile::stoneBrick_Id,0,Tile::UPDATE_CLIENTS); if(poweredCount>0) { - level->setTile(x+i,y,z,Tile::goldenRail_Id); + level->setTileAndData(x+i,y,z,Tile::goldenRail_Id,0,Tile::UPDATE_CLIENTS); --poweredCount; } else { - level->setTile(x+i,y,z,Tile::rail_Id); + level->setTileAndData(x+i,y,z,Tile::rail_Id,0,Tile::UPDATE_CLIENTS); } - level->setTile(x+i,y+1,z,0); - level->setTile(x+i,y+2,z,0); - level->setTile(x+i,y+3,z,0); + 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->setTile(x+i,y-1,z+1,Tile::stoneBrick_Id); + level->setTileAndData(x+i,y-1,z+1,Tile::stoneBrick_Id,0,Tile::UPDATE_CLIENTS); if((i+5)%20 == 0) { - level->setTile(x+i,y,z+1,Tile::torch_Id); + level->setTileAndData(x+i,y,z+1,Tile::torch_Id,0,Tile::UPDATE_CLIENTS); } else { - level->setTile(x+i,y,z+1,0); + level->setTileAndData(x+i,y,z+1,0,0,Tile::UPDATE_CLIENTS); } - level->setTile(x+i,y+1,z+1,0); - level->setTile(x+i,y+2,z+1,0); - level->setTile(x+i,y+3,z+1,0); - - level->setTile(x+i,y-1,z+2,Tile::quartzBlock_Id); - level->setTile(x+i,y,z+2,Tile::quartzBlock_Id); - level->setTile(x+i,y+1,z+2,Tile::quartzBlock_Id); - level->setTile(x+i,y+2,z+2,Tile::lightGem_Id); - level->setTile(x+i,y+3,z+2,Tile::quartzBlock_Id); + 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; } @@ -523,11 +526,28 @@ void Player::tick() //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) { - level->playSound(shared_from_this(), eSoundType_RANDOM_DRINK, 0.5f, level->random->nextFloat() * 0.1f + 0.9f); + playSound(eSoundType_RANDOM_DRINK, 0.5f, level->random->nextFloat() * 0.1f + 0.9f); } if (useItem->getUseAnimation() == UseAnim_eat) { @@ -547,7 +567,7 @@ void Player::spawnEatParticles(shared_ptr<ItemInstance> useItem, int count) } // 4J Stu - Was L"mob.eat" which doesnt exist - level->playSound(shared_from_this(), eSoundType_RANDOM_EAT, 0.5f + 0.5f * random->nextInt(2), (random->nextFloat() - random->nextFloat()) * 0.2f + 1.0f); + playSound(eSoundType_RANDOM_EAT, 0.5f + 0.5f * random->nextInt(2), (random->nextFloat() - random->nextFloat()) * 0.2f + 1.0f); } } @@ -579,7 +599,7 @@ void Player::handleEntityEvent(byte id) } else { - Mob::handleEntityEvent(id); + LivingEntity::handleEntityEvent(id); } } @@ -588,7 +608,6 @@ bool Player::isImmobile() return getHealth() <= 0 || isSleeping(); } - void Player::closeContainer() { containerMenu = inventoryMenu; @@ -608,7 +627,7 @@ void Player::ride(shared_ptr<Entity> e) return; } - Mob::ride(e); + LivingEntity::ride(e); } void Player::setPlayerDefaultSkin(EDefaultSkins skin) @@ -927,10 +946,17 @@ void Player::prepareCustomTextures() void Player::rideTick() { + if (!level->isClientSide && isSneaking()) + { + ride(nullptr); + setSneaking(false); + return; + } + double preX = x, preY = y, preZ = z; float preYRot = yRot, preXRot = xRot; - this->Mob::rideTick(); + LivingEntity::rideTick(); oBob = bob; bob = 0; @@ -958,42 +984,15 @@ void Player::resetPos() { heightOffset = 1.62f; setSize(0.6f, 1.8f); - this->Mob::resetPos(); + LivingEntity::resetPos(); setHealth(getMaxHealth()); deathTime = 0; } -int Player::getCurrentSwingDuration() -{ - if (hasEffect(MobEffect::digSpeed)) - { - return SWING_DURATION - (1 + getEffect(MobEffect::digSpeed)->getAmplifier()) * 1; - } - if (hasEffect(MobEffect::digSlowdown)) - { - return SWING_DURATION + (1 + getEffect(MobEffect::digSlowdown)->getAmplifier()) * 2; - } - return SWING_DURATION; -} - void Player::serverAiStep() { - int currentSwingDuration = getCurrentSwingDuration(); - if (swinging) - { - swingTime++; - if (swingTime >= currentSwingDuration) - { - swingTime = 0; - swinging = false; - } - } - else - { - swingTime = 0; - } - - attackAnim = swingTime / (float) currentSwingDuration; + LivingEntity::serverAiStep(); + updateSwingTime(); } @@ -1001,23 +1000,25 @@ void Player::aiStep() { if (jumpTriggerTime > 0) jumpTriggerTime--; - if (level->difficulty == Difficulty::PEACEFUL && getHealth() < getMaxHealth()) + 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; - this->Mob::aiStep(); + LivingEntity::aiStep(); - this->walkingSpeed = abilities.getWalkingSpeed(); - this->flyingSpeed = defaultFlySpeed; + AttributeInstance *speed = getAttribute(SharedMonsterAttributes::MOVEMENT_SPEED); + if (!level->isClientSide) speed->setBaseValue(abilities.getWalkingSpeed()); + flyingSpeed = defaultFlySpeed; if (isSprinting()) { - walkingSpeed += abilities.getWalkingSpeed() * 0.3f; 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 @@ -1037,7 +1038,19 @@ void Player::aiStep() if (getHealth() > 0) { - vector<shared_ptr<Entity> > *entities = level->getEntities(shared_from_this(), bb->grow(1, 0, 1)); + 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) { AUTO_VAR(itEnd, entities->end()); @@ -1059,21 +1072,26 @@ void Player::touch(shared_ptr<Entity> entity) entity->playerTouch( dynamic_pointer_cast<Player>( shared_from_this() ) ); } -// 4J - Removed 1.0.1 -//bool Player::addResource(int resource) -//{ -// return inventory->add(shared_ptr<ItemInstance>( new ItemInstance(resource, 1, 0) ) ); -//} - int Player::getScore() { - return score; + 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) { - this->Mob::die(source); - this->setSize(0.2f, 0.2f); + LivingEntity::die(source); + setSize(0.2f, 0.2f); setPos(x, y, z); yd = 0.1f; @@ -1082,7 +1100,10 @@ void Player::die(DamageSource *source) { drop(shared_ptr<ItemInstance>( new ItemInstance(Item::apple, 1) ), true); } - inventory->dropAll(); + if (!level->getGameRules()->getBoolean(GameRules::RULE_KEEPINVENTORY)) + { + inventory->dropAll(); + } if (source != NULL) { @@ -1093,26 +1114,33 @@ void Player::die(DamageSource *source) { xd = zd = 0; } - this->heightOffset = 0.1f; + heightOffset = 0.1f; } -void Player::awardKillScore(shared_ptr<Entity> victim, int score) +void Player::awardKillScore(shared_ptr<Entity> victim, int awardPoints) { - this->score += score; -} + increaseScore(awardPoints); + vector<Objective *> *objectives = getScoreboard()->findObjectiveFor(ObjectiveCriteria::KILL_COUNT_ALL); -int Player::decreaseAirSupply(int currentSupply) -{ - int oxygenBonus = EnchantmentHelper::getOxygenBonus(inventory); - if (oxygenBonus > 0) + //if (victim instanceof Player) + //{ + // awardStat(Stats::playerKills, 1); + // objectives.addAll(getScoreboard().findObjectiveFor(ObjectiveCriteria::KILL_COUNT_PLAYERS)); + //} + //else + //{ + // awardStat(Stats::mobKills, 1); + //} + + if(objectives) { - if (random->nextInt(oxygenBonus + 1) > 0) + for (AUTO_VAR(it,objectives->begin()); it != objectives->end(); ++it) { - // the oxygen bonus prevents us from drowning - return currentSupply; + Objective *objective = *it; + Score *score = getScoreboard()->getPlayerScore(getAName(), objective); + score->increment(); } } - return Mob::decreaseAirSupply(currentSupply); } bool Player::isShootable() @@ -1125,9 +1153,9 @@ bool Player::isCreativeModeAllowed() return true; } -shared_ptr<ItemEntity> Player::drop() +shared_ptr<ItemEntity> Player::drop(bool all) { - return drop(inventory->removeItem(inventory->selected, 1), false); + return drop(inventory->removeItem(inventory->selected, all && inventory->getSelected() != NULL ? inventory->getSelected()->count : 1), false); } shared_ptr<ItemEntity> Player::drop(shared_ptr<ItemInstance> item) @@ -1138,6 +1166,7 @@ shared_ptr<ItemEntity> Player::drop(shared_ptr<ItemInstance> item) 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; @@ -1181,14 +1210,28 @@ void Player::reallyDrop(shared_ptr<ItemEntity> thrownItem) } -float Player::getDestroySpeed(Tile *tile) +float Player::getDestroySpeed(Tile *tile, bool hasProperTool) { float speed = inventory->getDestroySpeed(tile); - int efficiency = EnchantmentHelper::getDiggingBonus(inventory); - if (efficiency > 0 && inventory->canDestroy(tile)) + if (speed > 1) { - speed += (efficiency * efficiency + 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)) @@ -1200,7 +1243,7 @@ float Player::getDestroySpeed(Tile *tile) speed *= 1.0f - (getEffect(MobEffect::digSlowdown)->getAmplifier() + 1) * .2f; } - if (isUnderLiquid(Material::water) && !EnchantmentHelper::hasWaterWorkerBonus(inventory)) speed /= 5; + 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 @@ -1217,16 +1260,17 @@ bool Player::canDestroy(Tile *tile) void Player::readAdditionalSaveData(CompoundTag *entityTag) { - Mob::readAdditionalSaveData(entityTag); + LivingEntity::readAdditionalSaveData(entityTag); ListTag<CompoundTag> *inventoryList = (ListTag<CompoundTag> *) entityTag->getList(L"Inventory"); inventory->load(inventoryList); - dimension = entityTag->getInt(L"Dimension"); + 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) { @@ -1237,6 +1281,7 @@ void Player::readAdditionalSaveData(CompoundTag *entityTag) 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); @@ -1254,21 +1299,23 @@ void Player::readAdditionalSaveData(CompoundTag *entityTag) void Player::addAdditonalSaveData(CompoundTag *entityTag) { - Mob::addAdditonalSaveData(entityTag); + LivingEntity::addAdditonalSaveData(entityTag); entityTag->put(L"Inventory", inventory->save(new ListTag<CompoundTag>())); - entityTag->putInt(L"Dimension", dimension); + 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); @@ -1281,21 +1328,27 @@ void Player::addAdditonalSaveData(CompoundTag *entityTag) } -Pos *Player::getRespawnPosition(Level *level, CompoundTag *entityTag) +bool Player::openContainer(shared_ptr<Container> container) { - if (entityTag->contains(L"SpawnX") && entityTag->contains(L"SpawnY") && entityTag->contains(L"SpawnZ")) - { - return new Pos(entityTag->getInt(L"SpawnX"), entityTag->getInt(L"SpawnY"), entityTag->getInt(L"SpawnZ")); - } - return level->getSharedSpawnPos(); + return true; } -bool Player::openContainer(shared_ptr<Container> container) +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) +bool Player::startEnchanting(int x, int y, int z, const wstring &name) { return true; } @@ -1310,8 +1363,9 @@ bool Player::startCrafting(int x, int y, int z) return true; } -void Player::take(shared_ptr<Entity> e, int orgCount) +bool Player::openFireworks(int x, int y, int z) { + return true; } float Player::getHeadHeight() @@ -1325,8 +1379,9 @@ void Player::setDefaultHeadHeight() heightOffset = 1.62f; } -bool Player::hurt(DamageSource *source, int dmg) +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. @@ -1350,95 +1405,40 @@ bool Player::hurt(DamageSource *source, int dmg) if (dmg == 0) return false; shared_ptr<Entity> attacker = source->getEntity(); - if ( dynamic_pointer_cast<Arrow>( attacker ) != NULL ) + if ( attacker != NULL && attacker->instanceof(eTYPE_ARROW) ) { - if ((dynamic_pointer_cast<Arrow>(attacker))->owner != NULL) + shared_ptr<Arrow> arrow = dynamic_pointer_cast<Arrow>(attacker); + if ( arrow->owner != NULL) { - attacker = (dynamic_pointer_cast<Arrow>(attacker))->owner; + attacker = arrow->owner; } } - if ( dynamic_pointer_cast<Mob>( attacker ) != NULL ) - { - // aggreviate all pet wolves nearby - directAllTameWolvesOnTarget(dynamic_pointer_cast<Mob>(attacker), false); - } - return this->Mob::hurt(source, dmg); + return LivingEntity::hurt(source, dmg); } -int Player::getDamageAfterMagicAbsorb(DamageSource *damageSource, int damage) +bool Player::canHarmPlayer(shared_ptr<Player> target) { - int remainingDamage = Mob::getDamageAfterMagicAbsorb(damageSource, damage); - if (remainingDamage <= 0) - { - return 0; - } + Team *team = getTeam(); + Team *otherTeam = target->getTeam(); - int enchantmentArmor = EnchantmentHelper::getDamageProtection(inventory, damageSource); - if (enchantmentArmor > 20) + if (team == NULL) { - enchantmentArmor = 20; + return true; } - if (enchantmentArmor > 0 && enchantmentArmor <= 20) + if (!team->isAlliedTo(otherTeam)) { - int absorb = 25 - enchantmentArmor; - int v = remainingDamage * absorb + dmgSpill; - remainingDamage = v / 25; - dmgSpill = v % 25; + return true; } - - return remainingDamage; + return team->isAllowFriendlyFire(); } -bool Player::isPlayerVersusPlayer() +bool Player::canHarmPlayer(wstring targetName) { - return false; -} - -void Player::directAllTameWolvesOnTarget(shared_ptr<Mob> target, bool skipSitting) -{ - - // filter un-attackable mobs - if ((dynamic_pointer_cast<Creeper>( target ) != NULL) || (dynamic_pointer_cast<Ghast>( target) != NULL)) - { - return; - } - // never target wolves that has this player as owner - if (dynamic_pointer_cast<Wolf>(target) != NULL) - { - shared_ptr<Wolf> wolfTarget = dynamic_pointer_cast<Wolf>(target); - if (wolfTarget->isTame() && m_UUID.compare( wolfTarget->getOwnerUUID() ) == 0 ) - { - return; - } - } - if ((dynamic_pointer_cast<Player>( target ) != NULL) && !isPlayerVersusPlayer()) - { - // pvp is off - return; - } - - - // TODO: Optimize this? Most of the time players wont have pets: - vector<shared_ptr<Entity> > *nearbyWolves = level->getEntitiesOfClass(typeid(Wolf), AABB::newTemp(x, y, z, x + 1, y + 1, z + 1)->grow(16, 4, 16)); - AUTO_VAR(itEnd, nearbyWolves->end()); - for (AUTO_VAR(it, nearbyWolves->begin()); it != itEnd; it++) - { - shared_ptr<Wolf> wolf = dynamic_pointer_cast<Wolf>(*it);; - if (wolf->isTame() && wolf->getAttackTarget() == NULL && m_UUID.compare( wolf->getOwnerUUID() ) == 0) - { - if (!skipSitting || !wolf->isSitting()) - { - wolf->setSitting(false); - wolf->setAttackTarget(target); - } - } - } - delete nearbyWolves; - + return true; } -void Player::hurtArmor(int damage) +void Player::hurtArmor(float damage) { inventory->hurtArmor(damage); } @@ -1450,30 +1450,37 @@ int Player::getArmorValue() float Player::getArmorCoverPercentage() { - int count = 0; + 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; + if (inventory->armor[i] != NULL) { + count++; + } + } + return (float) count / (float) inventory->armor.length; } -void Player::actuallyHurt(DamageSource *source, int dmg) +void Player::actuallyHurt(DamageSource *source, float dmg) { - if (!source->isBypassArmor() && isBlocking()) + if (isInvulnerable()) return; + if (!source->isBypassArmor() && isBlocking() && dmg > 0) { - dmg = (1 + dmg) >> 1; + 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()); - //this->Mob::actuallyHurt(source, dmg); - health -= dmg; + float oldHealth = getHealth(); + setHealth(getHealth() - dmg); + getCombatTracker()->recordDamage(source, oldHealth, dmg); } - bool Player::openFurnace(shared_ptr<FurnaceTileEntity> container) { return true; @@ -1484,7 +1491,7 @@ bool Player::openTrap(shared_ptr<DispenserTileEntity> container) return true; } -void Player::openTextEdit(shared_ptr<SignTileEntity> sign) +void Player::openTextEdit(shared_ptr<TileEntity> sign) { } @@ -1493,7 +1500,12 @@ bool Player::openBrewingStand(shared_ptr<BrewingStandTileEntity> brewingStand) return true; } -bool Player::openTrading(shared_ptr<Merchant> traderTarget) +bool Player::openBeacon(shared_ptr<BeaconTileEntity> beacon) +{ + return true; +} + +bool Player::openTrading(shared_ptr<Merchant> traderTarget, const wstring &name) { return true; } @@ -1509,21 +1521,41 @@ void Player::openItemInstanceGui(shared_ptr<ItemInstance> itemInstance) bool Player::interact(shared_ptr<Entity> entity) { - if (entity->interact( dynamic_pointer_cast<Player>( shared_from_this() ) )) return true; + shared_ptr<Player> thisPlayer = dynamic_pointer_cast<Player>(shared_from_this()); + shared_ptr<ItemInstance> item = getSelectedItem(); - if (item != NULL && dynamic_pointer_cast<Mob>( entity ) != NULL) + 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 = item->copy(); - if(item->interactEnemy(dynamic_pointer_cast<Mob>(entity))) + 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 && !this->abilities.instabuild) + // to instabuild + if ( (item->count <= 0) && !abilities.instabuild) { removeSelectedItem(); } @@ -1548,15 +1580,6 @@ double Player::getRidingHeight() return heightOffset - 0.5f; } -void Player::swing() -{ - if (!swinging || swingTime >= getCurrentSwingDuration() / 2 || swingTime < 0) - { - swingTime = -1; - swinging = true; - } -} - void Player::attack(shared_ptr<Entity> entity) { if (!entity->isAttackable()) @@ -1564,24 +1587,22 @@ void Player::attack(shared_ptr<Entity> entity) return; } - int dmg = inventory->getAttackDamage(entity); - - if (hasEffect(MobEffect::damageBoost)) - { - dmg += (3 << getEffect(MobEffect::damageBoost)->getAmplifier()); - } - if (hasEffect(MobEffect::weakness)) + if (entity->skipAttackInteraction(shared_from_this())) { - dmg -= (2 << getEffect(MobEffect::weakness)->getAmplifier()); + return; } + float dmg = (float) getAttribute(SharedMonsterAttributes::ATTACK_DAMAGE)->getValue(); + int knockback = 0; - int magicBoost = 0; - shared_ptr<Mob> mob = dynamic_pointer_cast<Mob>(entity); - if (mob != NULL) + float magicBoost = 0; + + if ( entity->instanceof(eTYPE_LIVINGENTITY) ) { - magicBoost = EnchantmentHelper::getDamageBonus(inventory, mob); - knockback += EnchantmentHelper::getKnockbackBonus(inventory, mob); + 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()) { @@ -1590,18 +1611,18 @@ void Player::attack(shared_ptr<Entity> entity) if (dmg > 0 || magicBoost > 0) { - bool bCrit = fallDistance > 0 && !onGround && !onLadder() && !isInWater() && !hasEffect(MobEffect::blindness) && riding == NULL && mob != NULL; - if (bCrit) + bool bCrit = fallDistance > 0 && !onGround && !onLadder() && !isInWater() && !hasEffect(MobEffect::blindness) && (riding == NULL) && entity->instanceof(eTYPE_LIVINGENTITY); + if (bCrit && dmg > 0) { - dmg += random->nextInt(dmg / 2 + 2); + 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<Mob>(shared_from_this())); - if (dynamic_pointer_cast<Mob>(entity) && fireAspect > 0 && !entity->isOnFire()) + 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); @@ -1635,29 +1656,36 @@ void Player::attack(shared_ptr<Entity> entity) } setLastHurtMob(entity); - shared_ptr<Mob> mob = dynamic_pointer_cast<Mob>(entity); - if (mob) + + 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(); - if (item != NULL && dynamic_pointer_cast<Mob>( entity ) != NULL) + shared_ptr<Entity> hurtTarget = entity; + if ( entity->instanceof(eTYPE_MULTIENTITY_MOB_PART) ) { - item->hurtEnemy(dynamic_pointer_cast<Mob>(entity), dynamic_pointer_cast<Player>( shared_from_this() ) ); - if (item->count <= 0) + shared_ptr<Entity> multiMob = dynamic_pointer_cast<Entity>((dynamic_pointer_cast<MultiEntityMobPart>(entity))->parentMob.lock()); + if ( (multiMob != NULL) && multiMob->instanceof(eTYPE_LIVINGENTITY) ) { - removeSelectedItem(); + hurtTarget = dynamic_pointer_cast<LivingEntity>( multiMob ); } } - if (dynamic_pointer_cast<Mob>( entity ) != NULL) + if ( (item != NULL) && hurtTarget->instanceof(eTYPE_LIVINGENTITY) ) { - if (entity->isAlive()) + item->hurtEnemy(dynamic_pointer_cast<LivingEntity>(hurtTarget), dynamic_pointer_cast<Player>( shared_from_this() ) ); + if (item->count <= 0) { - directAllTameWolvesOnTarget(dynamic_pointer_cast<Mob>(entity), true); + removeSelectedItem(); } - // 4J Stu - Brought forward wasHurt check to Fix 66140 - Bug: Fire Aspect bypasses "Player v Player" being Disabled + } + if ( entity->instanceof(eTYPE_LIVINGENTITY) ) + { + //awardStat(Stats.damageDealt, (int) Math.round(dmg * 10)); + if (fireAspect > 0 && wasHurt) { entity->setOnFire(fireAspect * 4); @@ -1670,6 +1698,11 @@ void Player::attack(shared_ptr<Entity> entity) causeFoodExhaustion(FoodConstants::EXHAUSTION_ATTACK); } + + // if (SharedConstants::INGAME_DEBUG_OUTPUT) + // { + // //sendMessage(ChatMessageComponent.forPlainText("DMG " + dmg + ", " + magicBoost + ", " + knockback)); + // } } void Player::crit(shared_ptr<Entity> entity) @@ -1707,7 +1740,7 @@ Slot *Player::getInventorySlot(int slotId) void Player::remove() { - this->Mob::remove(); + LivingEntity::remove(); inventoryMenu->removed( dynamic_pointer_cast<Player>( shared_from_this() ) ); if (containerMenu != NULL) { @@ -1717,7 +1750,7 @@ void Player::remove() bool Player::isInWall() { - return !m_isSleeping && this->Mob::isInWall(); + return !m_isSleeping && LivingEntity::isInWall(); } bool Player::isLocalPlayer() @@ -1759,6 +1792,7 @@ Player::BedSleepingResult Player::startSleepInBed(int x, int y, int z, bool bTes 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; @@ -1778,8 +1812,10 @@ Player::BedSleepingResult Player::startSleepInBed(int x, int y, int z, bool bTes return OK; } - // 4J Stu - You can use a bed from within a minecart, and this causes all sorts of problems. - ride(nullptr); + if (isRiding()) + { + ride(nullptr); + } setSize(0.2f, 0.2f); heightOffset = .2f; @@ -1898,7 +1934,7 @@ void Player::stopSleepInBed(bool forcefulWakeUp, bool updateLevelList, bool save } if (saveRespawnPoint) { - setRespawnPosition(bedPosition); + setRespawnPosition(bedPosition, false); } } @@ -1909,7 +1945,7 @@ bool Player::checkBed() } -Pos *Player::checkBedValidRespawnPosition(Level *level, Pos *pos) +Pos *Player::checkBedValidRespawnPosition(Level *level, Pos *pos, bool forced) { // make sure the chunks around the bed exist ChunkSource *chunkSource = level->getChunkSource(); @@ -1921,6 +1957,15 @@ Pos *Player::checkBedValidRespawnPosition(Level *level, Pos *pos) // 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 @@ -2005,15 +2050,22 @@ Pos *Player::getRespawnPosition() return respawnPosition; } -void Player::setRespawnPosition(Pos *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; } } @@ -2028,7 +2080,7 @@ void Player::awardStat(Stat *stat, byteArray paramBlob) void Player::jumpFromGround() { - this->Mob::jumpFromGround(); + LivingEntity::jumpFromGround(); // 4J Stu - This seems to have been missed from 1.7.3, but do we care? //awardStat(Stats::jump, 1); @@ -2050,21 +2102,25 @@ void Player::travel(float xa, float ya) if (abilities.flying && riding == NULL) { - double ydo = this->yd; + double ydo = yd; float ofs = flyingSpeed; flyingSpeed = abilities.getFlyingSpeed(); - this->Mob::travel(xa, ya); - this->yd = ydo * 0.6; + LivingEntity::travel(xa, ya); + yd = ydo * 0.6; flyingSpeed = ofs; } else { - this->Mob::travel(xa, ya); + 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) { @@ -2142,7 +2198,7 @@ void Player::checkRidingStatistiscs(double dx, double dy, double dz) int distance = (int) Math::round(sqrt(dx * dx + dy * dy + dz * dz) * 100.0f); if (distance > 0) { - if ( dynamic_pointer_cast<Minecart>( riding ) ) + if ( riding->instanceof(eTYPE_MINECART) ) { distanceMinecart += distance; if( distanceMinecart >= 100 ) @@ -2189,7 +2245,7 @@ void Player::checkRidingStatistiscs(double dx, double dy, double dz) } } - else if (dynamic_pointer_cast<Boat>( riding ) != NULL) + else if ( riding->instanceof(eTYPE_BOAT) ) { distanceBoat += distance; if( distanceBoat >= 100 ) @@ -2199,7 +2255,7 @@ void Player::checkRidingStatistiscs(double dx, double dy, double dz) awardStat(GenericStats::boatOneM(), GenericStats::param_boat(newDistance/100) ); } } - else if (dynamic_pointer_cast<Pig>( riding ) != NULL) + else if ( riding->instanceof(eTYPE_PIG) ) { distancePig += distance; if( distancePig >= 100 ) @@ -2228,14 +2284,14 @@ void Player::causeFallDamage(float distance) awardStat(GenericStats::fallOneM(), GenericStats::param_fall(newDistance/100) ); } } - this->Mob::causeFallDamage(distance); + LivingEntity::causeFallDamage(distance); } -void Player::killed(shared_ptr<Mob> mob) +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( dynamic_pointer_cast<Monster>( mob ) != NULL || mob->GetType() == eTYPE_GHAST || mob->GetType() == eTYPE_SLIME || mob->GetType() == eTYPE_LAVASLIME || mob->GetType() == eTYPE_ENDERDRAGON) + 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()); @@ -2282,9 +2338,14 @@ void Player::killed(shared_ptr<Mob> mob) } } +void Player::makeStuckInWeb() +{ + if (!abilities.flying) LivingEntity::makeStuckInWeb(); +} + Icon *Player::getItemInHandIcon(shared_ptr<ItemInstance> item, int layer) { - Icon *icon = Mob::getItemInHandIcon(item, layer); + Icon *icon = LivingEntity::getItemInHandIcon(item, layer); if (item->id == Item::fishingRod->id && fishing != NULL) { icon = Item::fishingRod->getEmptyIcon(); @@ -2317,21 +2378,9 @@ shared_ptr<ItemInstance> Player::getArmor(int pos) return inventory->getArmor(pos); } -void Player::handleInsidePortal() -{ - if (changingDimensionDelay > 0) - { - changingDimensionDelay = 10; - return; - } - - isInsidePortal = true; -} - void Player::increaseXp(int i) { - // Update xp calculations from 1.3 - score += i; + increaseScore(i); int max = INT_MAX - totalExperience; if (i > max) { @@ -2342,17 +2391,26 @@ void Player::increaseXp(int i) while (experienceProgress >= 1) { experienceProgress = (experienceProgress - 1) * getXpNeededForNextLevel(); - levelUp(); + giveExperienceLevels(1); experienceProgress /= getXpNeededForNextLevel(); } } -void Player::withdrawExperienceLevels(int amount) +void Player::giveExperienceLevels(int amount) { - experienceLevel -= 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; } } @@ -2370,11 +2428,6 @@ int Player::getXpNeededForNextLevel() return 17; } -void Player::levelUp() -{ - experienceLevel++; -} - /** * This method adds on to the player's exhaustion, which may decrease the * player's food level. @@ -2433,13 +2486,49 @@ void Player::startUsingItem(shared_ptr<ItemInstance> instance, int duration) #endif } -bool Player::mayBuild(int x, int y, int z) +bool Player::mayDestroyBlockAt(int x, int y, int z) { - return abilities.mayBuild; + 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) { @@ -2459,8 +2548,9 @@ wstring Player::getAName() return name; } -void Player::changeDimension(int i) +bool Player::shouldShowName() { + return true; } void Player::restoreFrom(shared_ptr<Player> oldPlayer, bool restoreAll) @@ -2469,21 +2559,24 @@ void Player::restoreFrom(shared_ptr<Player> oldPlayer, bool restoreAll) { inventory->replaceWith(oldPlayer->inventory); - health = oldPlayer->health; + setHealth(oldPlayer->getHealth()); foodData = oldPlayer->foodData; experienceLevel = oldPlayer->experienceLevel; totalExperience = oldPlayer->totalExperience; experienceProgress = oldPlayer->experienceProgress; - score = oldPlayer->score; + setScore(oldPlayer->getScore()); + portalEntranceDir = oldPlayer->portalEntranceDir; + } + else if (level->getGameRules()->getBoolean(GameRules::RULE_KEEPINVENTORY)) + { + inventory->replaceWith(oldPlayer->inventory); - for(AUTO_VAR(it, oldPlayer->activeEffects.begin()); it != oldPlayer->activeEffects.end(); ++it) - { - MobEffectInstance *instance = it->second; - addEffectNoUpdate( instance ); - } - oldPlayer->activeEffects.clear(); + experienceLevel = oldPlayer->experienceLevel; + totalExperience = oldPlayer->totalExperience; + experienceProgress = oldPlayer->experienceProgress; + setScore(oldPlayer->getScore()); } enderChestInventory = oldPlayer->enderChestInventory; } @@ -2508,25 +2601,83 @@ wstring Player::getName() wstring Player::getDisplayName() { - return displayName; + //PlayerTeam.formatNameForTeam(getTeam(), name); + + // If player display name is not set, return name + return m_displayName.size() > 0 ? m_displayName : name; } -//Language getLanguage() { return Language.getInstance(); } -//String localize(String key, Object... args) { return getLanguage().getElement(key, args); } +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(); + 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() @@ -2561,7 +2712,7 @@ int Player::hash_fnct(const shared_ptr<Player> k) #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? + return (int)std::hash<wstring>{}(k->name); // 4J Stu - Names are completely unique? #endif //__PS3__ } @@ -2770,12 +2921,12 @@ bool Player::isAllowedToInteract(shared_ptr<Entity> target) bool allowed = true; if(app.GetGameHostOption(eGameHostOption_TrustPlayers) == 0) { - if (target->GetType() == eTYPE_MINECART) + if (target->instanceof(eTYPE_MINECART)) { if (getPlayerGamePrivilege(Player::ePlayerGamePrivilege_CanUseContainers) == 0) { shared_ptr<Minecart> minecart = dynamic_pointer_cast<Minecart>( target ); - if (minecart->type == Minecart::CHEST) + if (minecart->getType() == Minecart::TYPE_CHEST) allowed = false; } |
