From 9125d3f06d6e5549971964db0b4ff81e724ae7b8 Mon Sep 17 00:00:00 2001 From: disintegrate Date: Wed, 18 Mar 2026 10:45:02 +0800 Subject: [PATCH 1/7] Climbable trapdoors if connected to a ladder. ### Previous Behavior Trapdoors still has collision even if opened, making it difficult to climb with. ### New Behavior - Added a new condition in `LivingEntity::onLadder()` in case it's a trapdoor, then return as true (as if its a ladder). - If there's a ladder under the (opened) trapdoor, then its collision box returns as a nullptr. **Files:** - `LivingEntity.cpp` - `TrapDoorTile.cpp` **Methods:** - `LivingEntity::onLadder()` - `TrapDoorTile::getAABB(Level *level, int x, int y, int z)` --- Minecraft.World/LivingEntity.cpp | 25 +++++++++++++++++++++++-- Minecraft.World/TrapDoorTile.cpp | 12 ++++++++++++ 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/Minecraft.World/LivingEntity.cpp b/Minecraft.World/LivingEntity.cpp index 3af9efe962..192823f1f6 100644 --- a/Minecraft.World/LivingEntity.cpp +++ b/Minecraft.World/LivingEntity.cpp @@ -986,9 +986,30 @@ bool LivingEntity::onLadder() int yt = Mth::floor(bb->y0); int zt = Mth::floor(z); - // 4J-PB - TU9 - add climbable vines int iTile = level->getTile(xt, yt, zt); - return (iTile== Tile::ladder_Id) || (iTile== Tile::vine_Id); + switch (iTile) + { + case Tile::ladder_Id: + case Tile::vine_Id: // 4J-PB - TU9 - add climbable vines + return true; + case Tile::trapdoor_Id: // hexagonny - add climbable (opened) trapdoors + { + int data = level->getData(xt, yt, zt); + bool isOpen = (data & 0x4) != 0; + if (isOpen) + { + int belowTile = level->getTile(xt, yt - 1, zt); + if (belowTile == Tile::ladder_Id) + { + return true; + } + } + break; + } + default: + break; + } + return false; } bool LivingEntity::isShootable() diff --git a/Minecraft.World/TrapDoorTile.cpp b/Minecraft.World/TrapDoorTile.cpp index 6ea64172ee..af00275fe6 100644 --- a/Minecraft.World/TrapDoorTile.cpp +++ b/Minecraft.World/TrapDoorTile.cpp @@ -50,6 +50,18 @@ AABB *TrapDoorTile::getTileAABB(Level *level, int x, int y, int z) AABB *TrapDoorTile::getAABB(Level *level, int x, int y, int z) { + int data = level->getData(x, y, z); + bool isOpen = (data & 0x4) != 0; + + if (isOpen) + { + int belowTile = level->getTile(x, y - 1, z); + if (belowTile == Tile::ladder_Id) + { + return nullptr; // No collision when open above ladder + } + } + // Default behaviour updateShape(level, x, y, z); return Tile::getAABB(level, x, y, z); } From 9983a74198725ab4d6b4c4f4863958ddf3927286 Mon Sep 17 00:00:00 2001 From: disintegrate Date: Wed, 18 Mar 2026 11:45:44 +0800 Subject: [PATCH 2/7] Added a check above the trapdoor. Made so that it avoids alternating use of ladders (or vines) and trapdoors. --- Minecraft.World/LivingEntity.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/Minecraft.World/LivingEntity.cpp b/Minecraft.World/LivingEntity.cpp index 192823f1f6..2a5982b8a4 100644 --- a/Minecraft.World/LivingEntity.cpp +++ b/Minecraft.World/LivingEntity.cpp @@ -998,10 +998,15 @@ bool LivingEntity::onLadder() bool isOpen = (data & 0x4) != 0; if (isOpen) { + int aboveTile = level->getTile(xt, yt + 1, zt); int belowTile = level->getTile(xt, yt - 1, zt); - if (belowTile == Tile::ladder_Id) + switch (aboveTile) { - return true; + case Tile::ladder_Id: + case Tile::vine_Id: + return false; // Opened trapdoor should only be climbable when it's only at the top. + default: + return belowTile == Tile::ladder_Id; } } break; From ab9a7ae8319e8d6133a555b5c6010e10919b36cb Mon Sep 17 00:00:00 2001 From: disintegrate Date: Wed, 18 Mar 2026 17:00:50 +0800 Subject: [PATCH 3/7] Added a check if trapdoor is attached. Just incase if neighborChanged is modified. --- Minecraft.World/LivingEntity.cpp | 10 +++------- Minecraft.World/TrapDoorTile.cpp | 15 +++++++++------ 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/Minecraft.World/LivingEntity.cpp b/Minecraft.World/LivingEntity.cpp index 2a5982b8a4..7876e88c40 100644 --- a/Minecraft.World/LivingEntity.cpp +++ b/Minecraft.World/LivingEntity.cpp @@ -994,19 +994,15 @@ bool LivingEntity::onLadder() return true; case Tile::trapdoor_Id: // hexagonny - add climbable (opened) trapdoors { - int data = level->getData(xt, yt, zt); - bool isOpen = (data & 0x4) != 0; - if (isOpen) + if ((level->getData(xt, yt, zt) & 0x4) != 0) { - int aboveTile = level->getTile(xt, yt + 1, zt); - int belowTile = level->getTile(xt, yt - 1, zt); - switch (aboveTile) + switch (level->getTile(xt, yt + 1, zt)) { case Tile::ladder_Id: case Tile::vine_Id: return false; // Opened trapdoor should only be climbable when it's only at the top. default: - return belowTile == Tile::ladder_Id; + return level->getTile(xt, yt - 1, zt) == Tile::ladder_Id; } } break; diff --git a/Minecraft.World/TrapDoorTile.cpp b/Minecraft.World/TrapDoorTile.cpp index af00275fe6..173435b84c 100644 --- a/Minecraft.World/TrapDoorTile.cpp +++ b/Minecraft.World/TrapDoorTile.cpp @@ -51,14 +51,17 @@ AABB *TrapDoorTile::getTileAABB(Level *level, int x, int y, int z) AABB *TrapDoorTile::getAABB(Level *level, int x, int y, int z) { int data = level->getData(x, y, z); - bool isOpen = (data & 0x4) != 0; - - if (isOpen) + if (isOpen(data)) { - int belowTile = level->getTile(x, y - 1, z); - if (belowTile == Tile::ladder_Id) + int xt = x, zt = z; + if ((data & 3) == 0) zt++; + if ((data & 3) == 1) zt--; + if ((data & 3) == 2) xt++; + if ((data & 3) == 3) xt--; + + if (level->getTile(x, y - 1, z) == Tile::ladder_Id && attachesTo(level->getTile(xt, y, zt))) { - return nullptr; // No collision when open above ladder + return nullptr; // No collision when open above ladder (add is attached to a block) } } // Default behaviour From af7fe090f22dabc9957d8b52e987df3baa70843c Mon Sep 17 00:00:00 2001 From: disintegrate Date: Mon, 23 Mar 2026 12:32:55 +0800 Subject: [PATCH 4/7] Reshapes collision instead of null --- Minecraft.World/TrapDoorTile.cpp | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/Minecraft.World/TrapDoorTile.cpp b/Minecraft.World/TrapDoorTile.cpp index 173435b84c..da856b920b 100644 --- a/Minecraft.World/TrapDoorTile.cpp +++ b/Minecraft.World/TrapDoorTile.cpp @@ -4,7 +4,7 @@ #include "net.minecraft.world.level.tile.h" #include "net.minecraft.h" #include "TrapDoorTile.h" - +#include "AABB.h" TrapDoorTile::TrapDoorTile(int id, Material *material) : Tile(id, material,isSolidRender()) { @@ -54,14 +54,22 @@ AABB *TrapDoorTile::getAABB(Level *level, int x, int y, int z) if (isOpen(data)) { int xt = x, zt = z; - if ((data & 3) == 0) zt++; - if ((data & 3) == 1) zt--; - if ((data & 3) == 2) xt++; - if ((data & 3) == 3) xt--; + int dir = data & 3; + + if (dir == 0) zt++; + if (dir == 1) zt--; + if (dir == 2) xt++; + if (dir == 3) xt--; if (level->getTile(x, y - 1, z) == Tile::ladder_Id && attachesTo(level->getTile(xt, y, zt))) { - return nullptr; // No collision when open above ladder (add is attached to a block) + float r = 2 / 16.0f; // From LadderTile + + // Using only newTemp from AABB to just manipulate collision to our liking + if (dir == 0) return AABB::newTemp(x, y, z + 1 - r, x + 1, y + 1, z + 1); + if (dir == 1) return AABB::newTemp(x, y, z, x + 1, y + 1, z + r); + if (dir == 2) return AABB::newTemp(x + 1 - r, y, z, x + 1, y + 1, z + 1); + if (dir == 3) return AABB::newTemp(x, y, z, x + r, y + 1, z + 1); } } // Default behaviour From 687c9624fe6cc9dca038bb781a274341ad97af0b Mon Sep 17 00:00:00 2001 From: disintegrate Date: Tue, 24 Mar 2026 10:18:06 +0800 Subject: [PATCH 5/7] Moved updateShape before the checks The same result but neglecting the update tick could make weird effects. --- Minecraft.World/TrapDoorTile.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Minecraft.World/TrapDoorTile.cpp b/Minecraft.World/TrapDoorTile.cpp index da856b920b..71dbcf0704 100644 --- a/Minecraft.World/TrapDoorTile.cpp +++ b/Minecraft.World/TrapDoorTile.cpp @@ -50,6 +50,7 @@ AABB *TrapDoorTile::getTileAABB(Level *level, int x, int y, int z) AABB *TrapDoorTile::getAABB(Level *level, int x, int y, int z) { + updateShape(level, x, y, z); int data = level->getData(x, y, z); if (isOpen(data)) { @@ -63,17 +64,18 @@ AABB *TrapDoorTile::getAABB(Level *level, int x, int y, int z) if (level->getTile(x, y - 1, z) == Tile::ladder_Id && attachesTo(level->getTile(xt, y, zt))) { - float r = 2 / 16.0f; // From LadderTile + float r = 2 / 16.0f; // Using only newTemp from AABB to just manipulate collision to our liking + // I tried looking at AABB.cpp more but couldn't find any helper methods to make this more readable. + // Though, I can just make new helpers but kinda scared because its not within the contributing scope. + // SO IF ITS NOT ALLOWED, GOODLUCK UNDERSTANDING. if (dir == 0) return AABB::newTemp(x, y, z + 1 - r, x + 1, y + 1, z + 1); if (dir == 1) return AABB::newTemp(x, y, z, x + 1, y + 1, z + r); if (dir == 2) return AABB::newTemp(x + 1 - r, y, z, x + 1, y + 1, z + 1); if (dir == 3) return AABB::newTemp(x, y, z, x + r, y + 1, z + 1); } } - // Default behaviour - updateShape(level, x, y, z); return Tile::getAABB(level, x, y, z); } From d9ef6ab57b60c398d568965748655f51afecd938 Mon Sep 17 00:00:00 2001 From: disintegrate Date: Tue, 24 Mar 2026 18:20:02 +0800 Subject: [PATCH 6/7] No more extra logic in TrapDoorTile.. Going full Java parity. Ngl, having a different collision box was a cool idea but probably best for implementing new tiles. --- Minecraft.World/LadderTile.cpp | 2 +- Minecraft.World/TrapDoorTile.cpp | 26 -------------------------- 2 files changed, 1 insertion(+), 27 deletions(-) diff --git a/Minecraft.World/LadderTile.cpp b/Minecraft.World/LadderTile.cpp index 438d2bbf79..c0c8716218 100644 --- a/Minecraft.World/LadderTile.cpp +++ b/Minecraft.World/LadderTile.cpp @@ -27,7 +27,7 @@ void LadderTile::updateShape(LevelSource *level, int x, int y, int z, int forceD void LadderTile::setShape(int data) { int dir = data; - float r = 2 / 16.0f; + float r = 3 / 16.0f; if (dir == 2) setShape(0, 0, 1 - r, 1, 1, 1); if (dir == 3) setShape(0, 0, 0, 1, 1, r); diff --git a/Minecraft.World/TrapDoorTile.cpp b/Minecraft.World/TrapDoorTile.cpp index 71dbcf0704..69ccfd5119 100644 --- a/Minecraft.World/TrapDoorTile.cpp +++ b/Minecraft.World/TrapDoorTile.cpp @@ -4,7 +4,6 @@ #include "net.minecraft.world.level.tile.h" #include "net.minecraft.h" #include "TrapDoorTile.h" -#include "AABB.h" TrapDoorTile::TrapDoorTile(int id, Material *material) : Tile(id, material,isSolidRender()) { @@ -51,31 +50,6 @@ AABB *TrapDoorTile::getTileAABB(Level *level, int x, int y, int z) AABB *TrapDoorTile::getAABB(Level *level, int x, int y, int z) { updateShape(level, x, y, z); - int data = level->getData(x, y, z); - if (isOpen(data)) - { - int xt = x, zt = z; - int dir = data & 3; - - if (dir == 0) zt++; - if (dir == 1) zt--; - if (dir == 2) xt++; - if (dir == 3) xt--; - - if (level->getTile(x, y - 1, z) == Tile::ladder_Id && attachesTo(level->getTile(xt, y, zt))) - { - float r = 2 / 16.0f; - - // Using only newTemp from AABB to just manipulate collision to our liking - // I tried looking at AABB.cpp more but couldn't find any helper methods to make this more readable. - // Though, I can just make new helpers but kinda scared because its not within the contributing scope. - // SO IF ITS NOT ALLOWED, GOODLUCK UNDERSTANDING. - if (dir == 0) return AABB::newTemp(x, y, z + 1 - r, x + 1, y + 1, z + 1); - if (dir == 1) return AABB::newTemp(x, y, z, x + 1, y + 1, z + r); - if (dir == 2) return AABB::newTemp(x + 1 - r, y, z, x + 1, y + 1, z + 1); - if (dir == 3) return AABB::newTemp(x, y, z, x + r, y + 1, z + 1); - } - } return Tile::getAABB(level, x, y, z); } From b1340925aff1a49a1296284b076ae8756e0ababf Mon Sep 17 00:00:00 2001 From: disintegrate Date: Mon, 30 Mar 2026 13:23:02 +0800 Subject: [PATCH 7/7] TrapDoorTile placement freedom. Commented attachedTo checks, made so mayPlace always return true, and implemented setPlacedBy for directional placing when the player placed the trapdoor on the floor or ceiling. --- Minecraft.World/LadderTile.cpp | 159 +- Minecraft.World/LivingEntity.cpp | 3075 ++++++++++++++++-------------- Minecraft.World/TrapDoorTile.cpp | 328 ++-- Minecraft.World/TrapDoorTile.h | 112 +- 4 files changed, 1987 insertions(+), 1687 deletions(-) diff --git a/Minecraft.World/LadderTile.cpp b/Minecraft.World/LadderTile.cpp index c0c8716218..bd2031f38b 100644 --- a/Minecraft.World/LadderTile.cpp +++ b/Minecraft.World/LadderTile.cpp @@ -1,112 +1,147 @@ -#include "stdafx.h" -#include "net.minecraft.world.level.h" #include "LadderTile.h" +#include "net.minecraft.world.level.h" +#include "stdafx.h" - -LadderTile::LadderTile(int id) : Tile(id, Material::decoration,isSolidRender()) +LadderTile::LadderTile(int id) : Tile(id, Material::decoration, isSolidRender()) { } AABB *LadderTile::getAABB(Level *level, int x, int y, int z) { - updateShape(level, x, y, z); - return Tile::getAABB(level, x, y, z); + updateShape(level, x, y, z); + return Tile::getAABB(level, x, y, z); } AABB *LadderTile::getTileAABB(Level *level, int x, int y, int z) { - updateShape(level, x, y, z); - return Tile::getTileAABB(level, x, y, z); + updateShape(level, x, y, z); + return Tile::getTileAABB(level, x, y, z); } void LadderTile::updateShape(LevelSource *level, int x, int y, int z, int forceData, shared_ptr forceEntity) // 4J added forceData, forceEntity param { - setShape(level->getData(x, y, z)); + setShape(level->getData(x, y, z)); } void LadderTile::setShape(int data) { - int dir = data; - float r = 3 / 16.0f; - - if (dir == 2) setShape(0, 0, 1 - r, 1, 1, 1); - if (dir == 3) setShape(0, 0, 0, 1, 1, r); - if (dir == 4) setShape(1 - r, 0, 0, 1, 1, 1); - if (dir == 5) setShape(0, 0, 0, r, 1, 1); + int dir = data; + float r = 3 / 16.0f; + + if (dir == 2) + { + setShape(0, 0, 1 - r, 1, 1, 1); + } + if (dir == 3) + { + setShape(0, 0, 0, 1, 1, r); + } + if (dir == 4) + { + setShape(1 - r, 0, 0, 1, 1, 1); + } + if (dir == 5) + { + setShape(0, 0, 0, r, 1, 1); + } } bool LadderTile::blocksLight() { - return false; + return false; } bool LadderTile::isSolidRender(bool isServerLevel) { - return false; + return false; } bool LadderTile::isCubeShaped() { - return false; + return false; } int LadderTile::getRenderShape() { - return Tile::SHAPE_LADDER; + return Tile::SHAPE_LADDER; } bool LadderTile::mayPlace(Level *level, int x, int y, int z) { - if (level->isSolidBlockingTile(x - 1, y, z)) - { - return true; - } - else if (level->isSolidBlockingTile(x + 1, y, z)) - { - return true; - } - else if (level->isSolidBlockingTile(x, y, z - 1)) - { - return true; - } - else if (level->isSolidBlockingTile(x, y, z + 1)) - { - return true; - } - return false; + if (level->isSolidBlockingTile(x - 1, y, z)) + { + return true; + } + else if (level->isSolidBlockingTile(x + 1, y, z)) + { + return true; + } + else if (level->isSolidBlockingTile(x, y, z - 1)) + { + return true; + } + else if (level->isSolidBlockingTile(x, y, z + 1)) + { + return true; + } + return false; } int LadderTile::getPlacedOnFaceDataValue(Level *level, int x, int y, int z, int face, float clickX, float clickY, float clickZ, int itemValue) { - int dir = level->getData(x, y, z); - - if ((dir == 0 || face == 2) && level->isSolidBlockingTile(x, y, z + 1)) dir = 2; - if ((dir == 0 || face == 3) && level->isSolidBlockingTile(x, y, z - 1)) dir = 3; - if ((dir == 0 || face == 4) && level->isSolidBlockingTile(x + 1, y, z)) dir = 4; - if ((dir == 0 || face == 5) && level->isSolidBlockingTile(x - 1, y, z)) dir = 5; - - return dir; + int dir = level->getData(x, y, z); + + if ((dir == 0 || face == 2) && level->isSolidBlockingTile(x, y, z + 1)) + { + dir = 2; + } + if ((dir == 0 || face == 3) && level->isSolidBlockingTile(x, y, z - 1)) + { + dir = 3; + } + if ((dir == 0 || face == 4) && level->isSolidBlockingTile(x + 1, y, z)) + { + dir = 4; + } + if ((dir == 0 || face == 5) && level->isSolidBlockingTile(x - 1, y, z)) + { + dir = 5; + } + + return dir; } void LadderTile::neighborChanged(Level *level, int x, int y, int z, int type) { - int face = level->getData(x, y, z); - bool ok = false; - - if (face == 2 && level->isSolidBlockingTile(x, y, z + 1)) ok = true; - if (face == 3 && level->isSolidBlockingTile(x, y, z - 1)) ok = true; - if (face == 4 && level->isSolidBlockingTile(x + 1, y, z)) ok = true; - if (face == 5 && level->isSolidBlockingTile(x - 1, y, z)) ok = true; - if (!ok) - { - spawnResources(level, x, y, z, face, 0); - level->removeTile(x, y, z); - } - - Tile::neighborChanged(level, x, y, z, type); + int face = level->getData(x, y, z); + bool ok = false; + + if (face == 2 && level->isSolidBlockingTile(x, y, z + 1)) + { + ok = true; + } + if (face == 3 && level->isSolidBlockingTile(x, y, z - 1)) + { + ok = true; + } + if (face == 4 && level->isSolidBlockingTile(x + 1, y, z)) + { + ok = true; + } + if (face == 5 && level->isSolidBlockingTile(x - 1, y, z)) + { + ok = true; + } + if (!ok) + { + spawnResources(level, x, y, z, face, 0); + level->removeTile(x, y, z); + } + + Tile::neighborChanged(level, x, y, z, type); } -int LadderTile::getResourceCount(Random* random) +int LadderTile::getResourceCount(Random *random) { - return 1; -} \ No newline at end of file + return 1; +} diff --git a/Minecraft.World/LivingEntity.cpp b/Minecraft.World/LivingEntity.cpp index 7876e88c40..53c0fbe338 100644 --- a/Minecraft.World/LivingEntity.cpp +++ b/Minecraft.World/LivingEntity.cpp @@ -1,37 +1,36 @@ -#include "stdafx.h" +#include "LivingEntity.h" +#include "..\Minecraft.Client\EntityTracker.h" +#include "..\Minecraft.Client\ServerLevel.h" +#include "..\Minecraft.Client\Textures.h" +#include "BasicTypeContainers.h" +#include "GenericStats.h" +#include "ItemEntity.h" #include "JavaMath.h" #include "Mth.h" +#include "ParticleTypes.h" +#include "SoundTypes.h" +#include "com.mojang.nbt.h" #include "net.minecraft.network.packet.h" -#include "net.minecraft.world.level.h" -#include "net.minecraft.world.level.tile.h" -#include "net.minecraft.world.phys.h" -#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.damagesource.h" +#include "net.minecraft.world.effect.h" #include "net.minecraft.world.entity.ai.attributes.h" #include "net.minecraft.world.entity.ai.control.h" #include "net.minecraft.world.entity.ai.navigation.h" #include "net.minecraft.world.entity.ai.sensing.h" -#include "net.minecraft.world.entity.player.h" #include "net.minecraft.world.entity.animal.h" +#include "net.minecraft.world.entity.h" #include "net.minecraft.world.entity.monster.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.item.alchemy.h" +#include "net.minecraft.world.item.enchantment.h" #include "net.minecraft.world.item.h" -#include "net.minecraft.world.level.h" #include "net.minecraft.world.level.chunk.h" +#include "net.minecraft.world.level.h" #include "net.minecraft.world.level.material.h" -#include "net.minecraft.world.damagesource.h" -#include "net.minecraft.world.effect.h" -#include "net.minecraft.world.item.alchemy.h" -#include "net.minecraft.world.item.enchantment.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.phys.h" #include "net.minecraft.world.scores.h" -#include "com.mojang.nbt.h" -#include "LivingEntity.h" -#include "..\Minecraft.Client\Textures.h" -#include "..\Minecraft.Client\ServerLevel.h" -#include "..\Minecraft.Client\EntityTracker.h" -#include "SoundTypes.h" -#include "BasicTypeContainers.h" -#include "ParticleTypes.h" -#include "GenericStats.h" -#include "ItemEntity.h" +#include "stdafx.h" const double LivingEntity::MIN_MOVEMENT_DISTANCE = 0.005; @@ -39,894 +38,958 @@ AttributeModifier *LivingEntity::SPEED_MODIFIER_SPRINTING = (new AttributeModifi void LivingEntity::_init() { - attributes = nullptr; - combatTracker = new CombatTracker(this); - lastEquipment = ItemInstanceArray(5); - - swinging = false; - swingTime = 0; - removeArrowTime = 0; - lastHealth = 0.0f; - - hurtTime = 0; - hurtDuration = 0; - hurtDir = 0.0f; - deathTime = 0; - attackTime = 0; - oAttackAnim = attackAnim = 0.0f; - - walkAnimSpeedO = 0.0f; - walkAnimSpeed = 0.0f; - walkAnimPos = 0.0f; - invulnerableDuration = 20; - oTilt = tilt = 0.0f; - timeOffs = 0.0f; - rotA = 0.0f; - yBodyRot = yBodyRotO = 0.0f; - yHeadRot = yHeadRotO = 0.0f; - flyingSpeed = 0.02f; - - lastHurtByPlayer = nullptr; - lastHurtByPlayerTime = 0; - dead = false; - noActionTime = 0; - oRun = run = 0.0f; - animStep = animStepO = 0.0f; - rotOffs = 0.0f; - deathScore = 0; - lastHurt = 0.0f; - jumping = false; - - xxa = 0.0f; - yya = 0.0f; - yRotA = 0.0f; - lSteps = 0; - lx = ly = lz = lyr = lxr = 0.0; - - effectsDirty = false; - - lastHurtByMob = nullptr; - lastHurtByMobTimestamp = 0; - lastHurtMob = nullptr; - lastHurtMobTimestamp = 0; - - speed = 0.0f; - noJumpDelay = 0; - absorptionAmount = 0.0f; -} - -LivingEntity::LivingEntity( Level* level) : Entity(level) -{ - MemSect(56); - _init(); - MemSect(0); - - // 4J Stu - This will not call the correct derived function, so moving to each derived class - //setHealth(0); - //registerAttributes(); - - blocksBuilding = true; - - rotA = static_cast(Math::random() + 1) * 0.01f; - setPos(x, y, z); - timeOffs = static_cast(Math::random()) * 12398; - yRot = static_cast(Math::random() * PI * 2); - yHeadRot = yRot; - - footSize = 0.5f; + attributes = nullptr; + combatTracker = new CombatTracker(this); + lastEquipment = ItemInstanceArray(5); + + swinging = false; + swingTime = 0; + removeArrowTime = 0; + lastHealth = 0.0f; + + hurtTime = 0; + hurtDuration = 0; + hurtDir = 0.0f; + deathTime = 0; + attackTime = 0; + oAttackAnim = attackAnim = 0.0f; + + walkAnimSpeedO = 0.0f; + walkAnimSpeed = 0.0f; + walkAnimPos = 0.0f; + invulnerableDuration = 20; + oTilt = tilt = 0.0f; + timeOffs = 0.0f; + rotA = 0.0f; + yBodyRot = yBodyRotO = 0.0f; + yHeadRot = yHeadRotO = 0.0f; + flyingSpeed = 0.02f; + + lastHurtByPlayer = nullptr; + lastHurtByPlayerTime = 0; + dead = false; + noActionTime = 0; + oRun = run = 0.0f; + animStep = animStepO = 0.0f; + rotOffs = 0.0f; + deathScore = 0; + lastHurt = 0.0f; + jumping = false; + + xxa = 0.0f; + yya = 0.0f; + yRotA = 0.0f; + lSteps = 0; + lx = ly = lz = lyr = lxr = 0.0; + + effectsDirty = false; + + lastHurtByMob = nullptr; + lastHurtByMobTimestamp = 0; + lastHurtMob = nullptr; + lastHurtMobTimestamp = 0; + + speed = 0.0f; + noJumpDelay = 0; + absorptionAmount = 0.0f; +} + +LivingEntity::LivingEntity(Level *level) : Entity(level) +{ + MemSect(56); + _init(); + MemSect(0); + + // 4J Stu - This will not call the correct derived function, so moving to each derived class + // setHealth(0); + // registerAttributes(); + + blocksBuilding = true; + + rotA = static_cast(Math::random() + 1) * 0.01f; + setPos(x, y, z); + timeOffs = static_cast(Math::random()) * 12398; + yRot = static_cast(Math::random() * PI * 2); + yHeadRot = yRot; + + footSize = 0.5f; } LivingEntity::~LivingEntity() { - for(auto& it : activeEffects) - { - delete it.second; - } + for (auto &it : activeEffects) + { + delete it.second; + } - delete attributes; - delete combatTracker; + delete attributes; + delete combatTracker; - if(lastEquipment.data != nullptr) delete [] lastEquipment.data; + if (lastEquipment.data != nullptr) + { + delete[] lastEquipment.data; + } } void LivingEntity::defineSynchedData() { - entityData->define(DATA_EFFECT_COLOR_ID, 0); - entityData->define(DATA_EFFECT_AMBIENCE_ID, static_cast(0)); - entityData->define(DATA_ARROW_COUNT_ID, static_cast(0)); - entityData->define(DATA_HEALTH_ID, 1.0f); + entityData->define(DATA_EFFECT_COLOR_ID, 0); + entityData->define(DATA_EFFECT_AMBIENCE_ID, static_cast(0)); + entityData->define(DATA_ARROW_COUNT_ID, static_cast(0)); + entityData->define(DATA_HEALTH_ID, 1.0f); } void LivingEntity::registerAttributes() { - getAttributes()->registerAttribute(SharedMonsterAttributes::MAX_HEALTH); - getAttributes()->registerAttribute(SharedMonsterAttributes::KNOCKBACK_RESISTANCE); - getAttributes()->registerAttribute(SharedMonsterAttributes::MOVEMENT_SPEED); + getAttributes()->registerAttribute(SharedMonsterAttributes::MAX_HEALTH); + getAttributes()->registerAttribute(SharedMonsterAttributes::KNOCKBACK_RESISTANCE); + getAttributes()->registerAttribute(SharedMonsterAttributes::MOVEMENT_SPEED); - if (!useNewAi()) - { - getAttribute(SharedMonsterAttributes::MOVEMENT_SPEED)->setBaseValue(0.1f); - } + if (!useNewAi()) + { + getAttribute(SharedMonsterAttributes::MOVEMENT_SPEED)->setBaseValue(0.1f); + } } void LivingEntity::checkFallDamage(double ya, bool onGround) { - if (!isInWater()) - { - // double-check if we've reached water in this move tick - updateInWaterState(); - } - - if (onGround && fallDistance > 0) - { - int xt = Mth::floor(x); - int yt = Mth::floor(y - 0.2f - heightOffset); - int zt = Mth::floor(z); - int t = level->getTile(xt, yt, zt); - if (t == 0) - { - int renderShape = level->getTileRenderShape(xt, yt - 1, zt); - if (renderShape == Tile::SHAPE_FENCE || renderShape == Tile::SHAPE_WALL || renderShape == Tile::SHAPE_FENCE_GATE) - { - t = level->getTile(xt, yt - 1, zt); - } - } - - if (t > 0) - { - Tile::tiles[t]->fallOn(level, xt, yt, zt, shared_from_this(), fallDistance); - } - } - - Entity::checkFallDamage(ya, onGround); + if (!isInWater()) + { + // double-check if we've reached water in this move tick + updateInWaterState(); + } + + if (onGround && fallDistance > 0) + { + int xt = Mth::floor(x); + int yt = Mth::floor(y - 0.2f - heightOffset); + int zt = Mth::floor(z); + int t = level->getTile(xt, yt, zt); + if (t == 0) + { + int renderShape = level->getTileRenderShape(xt, yt - 1, zt); + if (renderShape == Tile::SHAPE_FENCE || renderShape == Tile::SHAPE_WALL || renderShape == Tile::SHAPE_FENCE_GATE) + { + t = level->getTile(xt, yt - 1, zt); + } + } + + if (t > 0) + { + Tile::tiles[t]->fallOn(level, xt, yt, zt, shared_from_this(), fallDistance); + } + } + + Entity::checkFallDamage(ya, onGround); } bool LivingEntity::isWaterMob() { - return false; + return false; } void LivingEntity::baseTick() { - oAttackAnim = attackAnim; - Entity::baseTick(); - - if (isAlive() && isInWall()) - { - hurt(DamageSource::inWall, 1); - } - - if (isFireImmune() || level->isClientSide) clearFire(); - shared_ptr thisPlayer = dynamic_pointer_cast(shared_from_this()); - bool isInvulnerable = (thisPlayer != nullptr && thisPlayer->abilities.invulnerable); - - if (isAlive() && isUnderLiquid(Material::water)) - { - if(!isWaterMob() && !hasEffect(MobEffect::waterBreathing->id) && !isInvulnerable) - { - setAirSupply(decreaseAirSupply(getAirSupply())); - if (getAirSupply() == -20) - { - setAirSupply(0); - if(canCreateParticles()) - { - for (int i = 0; i < 8; i++) - { - float xo = random->nextFloat() - random->nextFloat(); - float yo = random->nextFloat() - random->nextFloat(); - float zo = random->nextFloat() - random->nextFloat(); - level->addParticle(eParticleType_bubble, x + xo, y + yo, z + zo, xd, yd, zd); - } - } - hurt(DamageSource::drown, 2); - } - } - - clearFire(); - if ( !level->isClientSide && isRiding() && riding->instanceof(eTYPE_LIVINGENTITY) ) - { - ride(nullptr); - } - } - else - { - setAirSupply(TOTAL_AIR_SUPPLY); - } - - oTilt = tilt; - - if (attackTime > 0) attackTime--; - if (hurtTime > 0) hurtTime--; - if (invulnerableTime > 0) invulnerableTime--; - if (getHealth() <= 0) - { - tickDeath(); - } - - if (lastHurtByPlayerTime > 0) lastHurtByPlayerTime--; - else - { - // Note - this used to just set to nullptr, but that has to create a new shared_ptr and free an old one, when generally this won't be doing anything at all. This - // is the lightweight but ugly alternative - if( lastHurtByPlayer ) - { - lastHurtByPlayer.reset(); - } - } - if (lastHurtMob != nullptr && !lastHurtMob->isAlive()) - { - lastHurtMob = nullptr; - } - - // If lastHurtByMob is dead, remove it - if (lastHurtByMob != nullptr && !lastHurtByMob->isAlive()) - { - setLastHurtByMob(nullptr); - } - - // Update effects - tickEffects(); - - animStepO = animStep; - - yBodyRotO = yBodyRot; - yHeadRotO = yHeadRot; - yRotO = yRot; - xRotO = xRot; + oAttackAnim = attackAnim; + Entity::baseTick(); + + if (isAlive() && isInWall()) + { + hurt(DamageSource::inWall, 1); + } + + if (isFireImmune() || level->isClientSide) + { + clearFire(); + } + shared_ptr thisPlayer = dynamic_pointer_cast(shared_from_this()); + bool isInvulnerable = (thisPlayer != nullptr && thisPlayer->abilities.invulnerable); + + if (isAlive() && isUnderLiquid(Material::water)) + { + if (!isWaterMob() && !hasEffect(MobEffect::waterBreathing->id) && !isInvulnerable) + { + setAirSupply(decreaseAirSupply(getAirSupply())); + if (getAirSupply() == -20) + { + setAirSupply(0); + if (canCreateParticles()) + { + for (int i = 0; i < 8; i++) + { + float xo = random->nextFloat() - random->nextFloat(); + float yo = random->nextFloat() - random->nextFloat(); + float zo = random->nextFloat() - random->nextFloat(); + level->addParticle(eParticleType_bubble, x + xo, y + yo, z + zo, xd, yd, zd); + } + } + hurt(DamageSource::drown, 2); + } + } + + clearFire(); + if (!level->isClientSide && isRiding() && riding->instanceof(eTYPE_LIVINGENTITY)) + { + ride(nullptr); + } + } + else + { + setAirSupply(TOTAL_AIR_SUPPLY); + } + + oTilt = tilt; + + if (attackTime > 0) + { + attackTime--; + } + if (hurtTime > 0) + { + hurtTime--; + } + if (invulnerableTime > 0) + { + invulnerableTime--; + } + if (getHealth() <= 0) + { + tickDeath(); + } + + if (lastHurtByPlayerTime > 0) + { + lastHurtByPlayerTime--; + } + else + { + // Note - this used to just set to nullptr, but that has to create a new shared_ptr and free an old one, when generally this won't be doing anything at all. This + // is the lightweight but ugly alternative + if (lastHurtByPlayer) + { + lastHurtByPlayer.reset(); + } + } + if (lastHurtMob != nullptr && !lastHurtMob->isAlive()) + { + lastHurtMob = nullptr; + } + + // If lastHurtByMob is dead, remove it + if (lastHurtByMob != nullptr && !lastHurtByMob->isAlive()) + { + setLastHurtByMob(nullptr); + } + + // Update effects + tickEffects(); + + animStepO = animStep; + + yBodyRotO = yBodyRot; + yHeadRotO = yHeadRot; + yRotO = yRot; + xRotO = xRot; } bool LivingEntity::isBaby() { - return false; + return false; } void LivingEntity::tickDeath() { - deathTime++; - if (deathTime == 20) - { - // 4J Stu - Added level->isClientSide check from 1.2 to fix XP orbs being created client side - if(!level->isClientSide && (lastHurtByPlayerTime > 0 || isAlwaysExperienceDropper()) ) - { - if (!isBaby() && level->getGameRules()->getBoolean(GameRules::RULE_DOMOBLOOT)) - { - int xpCount = this->getExperienceReward(lastHurtByPlayer); - while (xpCount > 0) - { - int newCount = ExperienceOrb::getExperienceValue(xpCount); - xpCount -= newCount; - level->addEntity(std::make_shared(level, x, y, z, newCount)); - } - } - } - - remove(); - for (int i = 0; i < 20; i++) - { - double xa = random->nextGaussian() * 0.02; - double ya = random->nextGaussian() * 0.02; - double za = random->nextGaussian() * 0.02; - level->addParticle(eParticleType_explode, x + random->nextFloat() * bbWidth * 2 - bbWidth, y + random->nextFloat() * bbHeight, z + random->nextFloat() * bbWidth * 2 - bbWidth, xa, ya, za); - } - } + deathTime++; + if (deathTime == 20) + { + // 4J Stu - Added level->isClientSide check from 1.2 to fix XP orbs being created client side + if (!level->isClientSide && (lastHurtByPlayerTime > 0 || isAlwaysExperienceDropper())) + { + if (!isBaby() && level->getGameRules()->getBoolean(GameRules::RULE_DOMOBLOOT)) + { + int xpCount = this->getExperienceReward(lastHurtByPlayer); + while (xpCount > 0) + { + int newCount = ExperienceOrb::getExperienceValue(xpCount); + xpCount -= newCount; + level->addEntity(std::make_shared(level, x, y, z, newCount)); + } + } + } + + remove(); + for (int i = 0; i < 20; i++) + { + double xa = random->nextGaussian() * 0.02; + double ya = random->nextGaussian() * 0.02; + double za = random->nextGaussian() * 0.02; + level->addParticle(eParticleType_explode, x + random->nextFloat() * bbWidth * 2 - bbWidth, y + random->nextFloat() * bbHeight, z + random->nextFloat() * bbWidth * 2 - bbWidth, xa, ya, za); + } + } } int LivingEntity::decreaseAirSupply(int currentSupply) { - int oxygenBonus = EnchantmentHelper::getOxygenBonus(dynamic_pointer_cast(shared_from_this())); - if (oxygenBonus > 0) - { - if (random->nextInt(oxygenBonus + 1) > 0) - { - // the oxygen bonus prevents us from drowning - return currentSupply; - } - } - if(instanceof(eTYPE_PLAYER)) - { - app.DebugPrintf("++++++++++ %s: Player decreasing air supply to %d\n", level->isClientSide ? "CLIENT" : "SERVER", currentSupply - 1 ); - } - return currentSupply - 1; + int oxygenBonus = EnchantmentHelper::getOxygenBonus(dynamic_pointer_cast(shared_from_this())); + if (oxygenBonus > 0) + { + if (random->nextInt(oxygenBonus + 1) > 0) + { + // the oxygen bonus prevents us from drowning + return currentSupply; + } + } + if (instanceof(eTYPE_PLAYER)) + { + app.DebugPrintf("++++++++++ %s: Player decreasing air supply to %d\n", level->isClientSide ? "CLIENT" : "SERVER", currentSupply - 1); + } + return currentSupply - 1; } int LivingEntity::getExperienceReward(shared_ptr killedBy) { - return 0; + return 0; } bool LivingEntity::isAlwaysExperienceDropper() { - return false; + return false; } Random *LivingEntity::getRandom() { - return random; + return random; } shared_ptr LivingEntity::getLastHurtByMob() { - return lastHurtByMob; + return lastHurtByMob; } int LivingEntity::getLastHurtByMobTimestamp() { - return lastHurtByMobTimestamp; + return lastHurtByMobTimestamp; } void LivingEntity::setLastHurtByMob(shared_ptr target) { - lastHurtByMob = target; - lastHurtByMobTimestamp = tickCount; + lastHurtByMob = target; + lastHurtByMobTimestamp = tickCount; } shared_ptr LivingEntity::getLastHurtMob() { - return lastHurtMob; + return lastHurtMob; } int LivingEntity::getLastHurtMobTimestamp() { - return lastHurtMobTimestamp; + return lastHurtMobTimestamp; } void LivingEntity::setLastHurtMob(shared_ptr target) { - if ( target->instanceof(eTYPE_LIVINGENTITY) ) - { - lastHurtMob = dynamic_pointer_cast(target); - } - else - { - lastHurtMob = nullptr; - } - lastHurtMobTimestamp = tickCount; + if (target->instanceof(eTYPE_LIVINGENTITY)) + { + lastHurtMob = dynamic_pointer_cast(target); + } + else + { + lastHurtMob = nullptr; + } + lastHurtMobTimestamp = tickCount; } int LivingEntity::getNoActionTime() { - return noActionTime; + return noActionTime; } void LivingEntity::addAdditonalSaveData(CompoundTag *entityTag) { - entityTag->putFloat(L"HealF", getHealth()); - entityTag->putShort(L"Health", static_cast(ceil(getHealth()))); - entityTag->putShort(L"HurtTime", static_cast(hurtTime)); - entityTag->putShort(L"DeathTime", static_cast(deathTime)); - entityTag->putShort(L"AttackTime", static_cast(attackTime)); - entityTag->putFloat(L"AbsorptionAmount", getAbsorptionAmount()); - - ItemInstanceArray items = getEquipmentSlots(); - for (unsigned int i = 0; i < items.length; ++i) - { - shared_ptr item = items[i]; - if (item != nullptr) - { - attributes->removeItemModifiers(item); - } - } - - entityTag->put(L"Attributes", SharedMonsterAttributes::saveAttributes(getAttributes())); - - for (unsigned int i = 0; i < items.length; ++i) - { - shared_ptr item = items[i]; - if (item != nullptr) - { - attributes->addItemModifiers(item); - } - } - - if (!activeEffects.empty()) - { - ListTag *listTag = new ListTag(); - - for(auto & it : activeEffects) - { - MobEffectInstance *effect = it.second; - listTag->add(effect->save(new CompoundTag())); - } - entityTag->put(L"ActiveEffects", listTag); - } + entityTag->putFloat(L"HealF", getHealth()); + entityTag->putShort(L"Health", static_cast(ceil(getHealth()))); + entityTag->putShort(L"HurtTime", static_cast(hurtTime)); + entityTag->putShort(L"DeathTime", static_cast(deathTime)); + entityTag->putShort(L"AttackTime", static_cast(attackTime)); + entityTag->putFloat(L"AbsorptionAmount", getAbsorptionAmount()); + + ItemInstanceArray items = getEquipmentSlots(); + for (unsigned int i = 0; i < items.length; ++i) + { + shared_ptr item = items[i]; + if (item != nullptr) + { + attributes->removeItemModifiers(item); + } + } + + entityTag->put(L"Attributes", SharedMonsterAttributes::saveAttributes(getAttributes())); + + for (unsigned int i = 0; i < items.length; ++i) + { + shared_ptr item = items[i]; + if (item != nullptr) + { + attributes->addItemModifiers(item); + } + } + + if (!activeEffects.empty()) + { + ListTag *listTag = new ListTag(); + + for (auto &it : activeEffects) + { + MobEffectInstance *effect = it.second; + listTag->add(effect->save(new CompoundTag())); + } + entityTag->put(L"ActiveEffects", listTag); + } } void LivingEntity::readAdditionalSaveData(CompoundTag *tag) { - setAbsorptionAmount(tag->getFloat(L"AbsorptionAmount")); - - if (tag->contains(L"Attributes") && level != nullptr && !level->isClientSide) - { - SharedMonsterAttributes::loadAttributes(getAttributes(), (ListTag *) tag->getList(L"Attributes")); - } - - if (tag->contains(L"ActiveEffects")) - { - ListTag *effects = (ListTag *) tag->getList(L"ActiveEffects"); - for (int i = 0; i < effects->size(); i++) - { - CompoundTag *effectTag = effects->get(i); - MobEffectInstance *effect = MobEffectInstance::load(effectTag); - activeEffects.insert( unordered_map::value_type( effect->getId(), effect ) ); - } - } - - if (tag->contains(L"HealF")) - { - setHealth( tag->getFloat(L"HealF") ); - } - else - { - Tag *healthTag = tag->get(L"Health"); - if (healthTag == nullptr) - { - setHealth(getMaxHealth()); - } - else if (healthTag->getId() == Tag::TAG_Float) - { - setHealth(static_cast(healthTag)->data); - } - else if (healthTag->getId() == Tag::TAG_Short) - { - // pre-1.6 health - setHealth((float) static_cast(healthTag)->data); - } - } - - hurtTime = tag->getShort(L"HurtTime"); - deathTime = tag->getShort(L"DeathTime"); - attackTime = tag->getShort(L"AttackTime"); + setAbsorptionAmount(tag->getFloat(L"AbsorptionAmount")); + + if (tag->contains(L"Attributes") && level != nullptr && !level->isClientSide) + { + SharedMonsterAttributes::loadAttributes(getAttributes(), (ListTag *)tag->getList(L"Attributes")); + } + + if (tag->contains(L"ActiveEffects")) + { + ListTag *effects = (ListTag *)tag->getList(L"ActiveEffects"); + for (int i = 0; i < effects->size(); i++) + { + CompoundTag *effectTag = effects->get(i); + MobEffectInstance *effect = MobEffectInstance::load(effectTag); + activeEffects.insert(unordered_map::value_type(effect->getId(), effect)); + } + } + + if (tag->contains(L"HealF")) + { + setHealth(tag->getFloat(L"HealF")); + } + else + { + Tag *healthTag = tag->get(L"Health"); + if (healthTag == nullptr) + { + setHealth(getMaxHealth()); + } + else if (healthTag->getId() == Tag::TAG_Float) + { + setHealth(static_cast(healthTag)->data); + } + else if (healthTag->getId() == Tag::TAG_Short) + { + // pre-1.6 health + setHealth((float)static_cast(healthTag)->data); + } + } + + hurtTime = tag->getShort(L"HurtTime"); + deathTime = tag->getShort(L"DeathTime"); + attackTime = tag->getShort(L"AttackTime"); } void LivingEntity::tickEffects() { - bool removed = false; + bool removed = false; for (auto it = activeEffects.begin(); it != activeEffects.end();) { - MobEffectInstance *effect = it->second; - removed = false; - if (!effect->tick(dynamic_pointer_cast(shared_from_this()))) - { - if (!level->isClientSide) - { - it = activeEffects.erase( it ); - onEffectRemoved(effect); - delete effect; - removed = true; - } - } - else if (effect->getDuration() % (SharedConstants::TICKS_PER_SECOND * 30) == 0) - { - // update effects every 30 seconds to synchronize client-side - // timer - onEffectUpdated(effect, false); - } - if(!removed) - { - ++it; - } - } - if (effectsDirty) - { - if (!level->isClientSide) - { - if (activeEffects.empty()) - { - entityData->set(DATA_EFFECT_AMBIENCE_ID, static_cast(0)); - entityData->set(DATA_EFFECT_COLOR_ID, 0); - setInvisible(false); - setWeakened(false); - } - else - { - vector values; - for(auto& it : activeEffects) - { - values.push_back(it.second); - } - int colorValue = PotionBrewing::getColorValue(&values); - entityData->set(DATA_EFFECT_AMBIENCE_ID, PotionBrewing::areAllEffectsAmbient(&values) ? static_cast(1) : static_cast(0)); - values.clear(); - entityData->set(DATA_EFFECT_COLOR_ID, colorValue); - setInvisible(hasEffect(MobEffect::invisibility->id)); - setWeakened(hasEffect(MobEffect::weakness->id)); - } - } - effectsDirty = false; - } - int colorValue = entityData->getInteger(DATA_EFFECT_COLOR_ID); - bool ambient = entityData->getByte(DATA_EFFECT_AMBIENCE_ID) > 0; - - if (colorValue > 0) - { - boolean doParticle = false; - - if (!isInvisible()) - { - doParticle = random->nextBoolean(); - } - else - { - // much fewer particles when invisible - doParticle = random->nextInt(15) == 0; - } - - if (ambient) doParticle &= random->nextInt(5) == 0; - - if (doParticle) - { - // int colorValue = entityData.getInteger(DATA_EFFECT_COLOR_ID); - if (colorValue > 0) - { - double red = static_cast((colorValue >> 16) & 0xff) / 255.0; - double green = static_cast((colorValue >> 8) & 0xff) / 255.0; - double blue = static_cast((colorValue >> 0) & 0xff) / 255.0; - - level->addParticle(ambient? eParticleType_mobSpellAmbient : eParticleType_mobSpell, x + (random->nextDouble() - 0.5) * bbWidth, y + random->nextDouble() * bbHeight - heightOffset, z + (random->nextDouble() - 0.5) * bbWidth, red, green, blue); - } - } - } + MobEffectInstance *effect = it->second; + removed = false; + if (!effect->tick(dynamic_pointer_cast(shared_from_this()))) + { + if (!level->isClientSide) + { + it = activeEffects.erase(it); + onEffectRemoved(effect); + delete effect; + removed = true; + } + } + else if (effect->getDuration() % (SharedConstants::TICKS_PER_SECOND * 30) == 0) + { + // update effects every 30 seconds to synchronize client-side + // timer + onEffectUpdated(effect, false); + } + if (!removed) + { + ++it; + } + } + if (effectsDirty) + { + if (!level->isClientSide) + { + if (activeEffects.empty()) + { + entityData->set(DATA_EFFECT_AMBIENCE_ID, static_cast(0)); + entityData->set(DATA_EFFECT_COLOR_ID, 0); + setInvisible(false); + setWeakened(false); + } + else + { + vector values; + for (auto &it : activeEffects) + { + values.push_back(it.second); + } + int colorValue = PotionBrewing::getColorValue(&values); + entityData->set(DATA_EFFECT_AMBIENCE_ID, PotionBrewing::areAllEffectsAmbient(&values) ? static_cast(1) : static_cast(0)); + values.clear(); + entityData->set(DATA_EFFECT_COLOR_ID, colorValue); + setInvisible(hasEffect(MobEffect::invisibility->id)); + setWeakened(hasEffect(MobEffect::weakness->id)); + } + } + effectsDirty = false; + } + int colorValue = entityData->getInteger(DATA_EFFECT_COLOR_ID); + bool ambient = entityData->getByte(DATA_EFFECT_AMBIENCE_ID) > 0; + + if (colorValue > 0) + { + boolean doParticle = false; + + if (!isInvisible()) + { + doParticle = random->nextBoolean(); + } + else + { + // much fewer particles when invisible + doParticle = random->nextInt(15) == 0; + } + + if (ambient) + { + doParticle &= random->nextInt(5) == 0; + } + + if (doParticle) + { + // int colorValue = entityData.getInteger(DATA_EFFECT_COLOR_ID); + if (colorValue > 0) + { + double red = static_cast((colorValue >> 16) & 0xff) / 255.0; + double green = static_cast((colorValue >> 8) & 0xff) / 255.0; + double blue = static_cast((colorValue >> 0) & 0xff) / 255.0; + + level->addParticle(ambient ? eParticleType_mobSpellAmbient : eParticleType_mobSpell, x + (random->nextDouble() - 0.5) * bbWidth, y + random->nextDouble() * bbHeight - heightOffset, z + (random->nextDouble() - 0.5) * bbWidth, red, green, blue); + } + } + } } void LivingEntity::removeAllEffects() { for (auto it = activeEffects.begin(); it != activeEffects.end();) { - MobEffectInstance *effect = it->second;//activeEffects.get(effectId); + MobEffectInstance *effect = it->second; // activeEffects.get(effectId); - if (!level->isClientSide) - { - it = activeEffects.erase(it); - onEffectRemoved(effect); - delete effect; - } - else - { - ++it; - } - } + if (!level->isClientSide) + { + it = activeEffects.erase(it); + onEffectRemoved(effect); + delete effect; + } + else + { + ++it; + } + } } vector *LivingEntity::getActiveEffects() { - vector *active = new vector(); + vector *active = new vector(); - for(auto& it : activeEffects) - { - active->push_back(it.second); - } + for (auto &it : activeEffects) + { + active->push_back(it.second); + } - return active; + return active; } bool LivingEntity::hasEffect(int id) { - return activeEffects.find(id) != activeEffects.end();; + return activeEffects.find(id) != activeEffects.end(); + ; } bool LivingEntity::hasEffect(MobEffect *effect) { - return activeEffects.find(effect->id) != activeEffects.end(); + return activeEffects.find(effect->id) != activeEffects.end(); } MobEffectInstance *LivingEntity::getEffect(MobEffect *effect) { - MobEffectInstance *effectInst = nullptr; + MobEffectInstance *effectInst = nullptr; auto it = activeEffects.find(effect->id); - if(it != activeEffects.end() ) effectInst = it->second; + if (it != activeEffects.end()) + { + effectInst = it->second; + } - return effectInst; + return effectInst; } void LivingEntity::addEffect(MobEffectInstance *newEffect) { - if (!canBeAffected(newEffect)) - { - return; - } - - if (activeEffects.find(newEffect->getId()) != activeEffects.end() ) - { - // replace effect and update - MobEffectInstance *effectInst = activeEffects.find(newEffect->getId())->second; - effectInst->update(newEffect); - onEffectUpdated(effectInst, true); - } - else - { - activeEffects.insert( unordered_map::value_type( newEffect->getId(), newEffect ) ); - onEffectAdded(newEffect); - } + if (!canBeAffected(newEffect)) + { + return; + } + + if (activeEffects.find(newEffect->getId()) != activeEffects.end()) + { + // replace effect and update + MobEffectInstance *effectInst = activeEffects.find(newEffect->getId())->second; + effectInst->update(newEffect); + onEffectUpdated(effectInst, true); + } + else + { + activeEffects.insert(unordered_map::value_type(newEffect->getId(), newEffect)); + onEffectAdded(newEffect); + } } // 4J Added void LivingEntity::addEffectNoUpdate(MobEffectInstance *newEffect) { - if (!canBeAffected(newEffect)) - { - return; - } + if (!canBeAffected(newEffect)) + { + return; + } - if (activeEffects.find(newEffect->getId()) != activeEffects.end() ) - { - // replace effect and update - MobEffectInstance *effectInst = activeEffects.find(newEffect->getId())->second; - effectInst->update(newEffect); - } - else - { - activeEffects.insert( unordered_map::value_type( newEffect->getId(), newEffect ) ); - } + if (activeEffects.find(newEffect->getId()) != activeEffects.end()) + { + // replace effect and update + MobEffectInstance *effectInst = activeEffects.find(newEffect->getId())->second; + effectInst->update(newEffect); + } + else + { + activeEffects.insert(unordered_map::value_type(newEffect->getId(), newEffect)); + } } bool LivingEntity::canBeAffected(MobEffectInstance *newEffect) { - if (getMobType() == UNDEAD) - { - int id = newEffect->getId(); - if (id == MobEffect::regeneration->id || id == MobEffect::poison->id) - { - return false; - } - } + if (getMobType() == UNDEAD) + { + int id = newEffect->getId(); + if (id == MobEffect::regeneration->id || id == MobEffect::poison->id) + { + return false; + } + } - return true; + return true; } bool LivingEntity::isInvertedHealAndHarm() { - return getMobType() == UNDEAD; + return getMobType() == UNDEAD; } void LivingEntity::removeEffectNoUpdate(int effectId) { auto it = activeEffects.find(effectId); if (it != activeEffects.end()) - { - MobEffectInstance *effect = it->second; - if(effect != nullptr) - { - delete effect; - } - activeEffects.erase(it); - } + { + MobEffectInstance *effect = it->second; + if (effect != nullptr) + { + delete effect; + } + activeEffects.erase(it); + } } void LivingEntity::removeEffect(int effectId) { auto it = activeEffects.find(effectId); if (it != activeEffects.end()) - { - MobEffectInstance *effect = it->second; - if(effect != nullptr) - { - onEffectRemoved(effect); - delete effect; - } - activeEffects.erase(it); - } + { + MobEffectInstance *effect = it->second; + if (effect != nullptr) + { + onEffectRemoved(effect); + delete effect; + } + activeEffects.erase(it); + } } void LivingEntity::onEffectAdded(MobEffectInstance *effect) { - effectsDirty = true; - if (!level->isClientSide) MobEffect::effects[effect->getId()]->addAttributeModifiers(dynamic_pointer_cast(shared_from_this()), getAttributes(), effect->getAmplifier()); + effectsDirty = true; + if (!level->isClientSide) + { + MobEffect::effects[effect->getId()]->addAttributeModifiers(dynamic_pointer_cast(shared_from_this()), getAttributes(), effect->getAmplifier()); + } } void LivingEntity::onEffectUpdated(MobEffectInstance *effect, bool doRefreshAttributes) { - effectsDirty = true; - if (doRefreshAttributes && !level->isClientSide) - { - MobEffect::effects[effect->getId()]->removeAttributeModifiers(dynamic_pointer_cast(shared_from_this()), getAttributes(), effect->getAmplifier()); - MobEffect::effects[effect->getId()]->addAttributeModifiers(dynamic_pointer_cast(shared_from_this()), getAttributes(), effect->getAmplifier()); - } + effectsDirty = true; + if (doRefreshAttributes && !level->isClientSide) + { + MobEffect::effects[effect->getId()]->removeAttributeModifiers(dynamic_pointer_cast(shared_from_this()), getAttributes(), effect->getAmplifier()); + MobEffect::effects[effect->getId()]->addAttributeModifiers(dynamic_pointer_cast(shared_from_this()), getAttributes(), effect->getAmplifier()); + } } void LivingEntity::onEffectRemoved(MobEffectInstance *effect) { - effectsDirty = true; - if (!level->isClientSide) MobEffect::effects[effect->getId()]->removeAttributeModifiers(dynamic_pointer_cast(shared_from_this()), getAttributes(), effect->getAmplifier()); + effectsDirty = true; + if (!level->isClientSide) + { + MobEffect::effects[effect->getId()]->removeAttributeModifiers(dynamic_pointer_cast(shared_from_this()), getAttributes(), effect->getAmplifier()); + } } void LivingEntity::heal(float heal) { - float health = getHealth(); - if (health > 0) - { - setHealth(health + heal); - } + float health = getHealth(); + if (health > 0) + { + setHealth(health + heal); + } } float LivingEntity::getHealth() { - return entityData->getFloat(DATA_HEALTH_ID); + return entityData->getFloat(DATA_HEALTH_ID); } void LivingEntity::setHealth(float health) { - entityData->set(DATA_HEALTH_ID, Mth::clamp(health, 0.0f, getMaxHealth())); + entityData->set(DATA_HEALTH_ID, Mth::clamp(health, 0.0f, getMaxHealth())); } bool LivingEntity::hurt(DamageSource *source, float dmg) { - if (isInvulnerable()) return false; - - // 4J Stu - Reworked this function a bit to show hurt damage on the client before the server responds. - // Fix for #8823 - Gameplay: Confirmation that a monster or animal has taken damage from an attack is highly delayed - // 4J Stu - Change to the fix to only show damage when attacked, rather than collision damage - // Fix for #10299 - When in corners, passive mobs may show that they are taking damage. - // 4J Stu - Change to the fix for TU6, as source is never nullptr due to changes in 1.8.2 to what source actually is - if (level->isClientSide && dynamic_cast(source) == nullptr) return false; - noActionTime = 0; - if (getHealth() <= 0) return false; - - if ( source->isFire() && hasEffect(MobEffect::fireResistance) ) - { - // 4J-JEV, for new achievement Stayin'Frosty, TODO merge with Java version. - if ( this->instanceof(eTYPE_PLAYER) && (source == DamageSource::lava) ) // Only award when in lava (not any fire). - { - shared_ptr plr = dynamic_pointer_cast(shared_from_this()); - plr->awardStat(GenericStats::stayinFrosty(),GenericStats::param_stayinFrosty()); - } - return false; - } - - if ((source == DamageSource::anvil || source == DamageSource::fallingBlock) && getCarried(SLOT_HELM) != nullptr) - { - getCarried(SLOT_HELM)->hurtAndBreak(static_cast(dmg * 4 + random->nextFloat() * dmg * 2.0f), dynamic_pointer_cast( shared_from_this() )); - dmg *= 0.75f; - } - - walkAnimSpeed = 1.5f; - - bool sound = true; - if (invulnerableTime > invulnerableDuration / 2.0f) - { - if (dmg <= lastHurt) return false; - if(!level->isClientSide) actuallyHurt(source, dmg - lastHurt); - lastHurt = dmg; - sound = false; - } - else - { - lastHurt = dmg; - lastHealth = getHealth(); - invulnerableTime = invulnerableDuration; - if (!level->isClientSide) actuallyHurt(source, dmg); - hurtTime = hurtDuration = 10; - } - - hurtDir = 0; - - shared_ptr sourceEntity = source->getEntity(); - if (sourceEntity != nullptr) - { - if ( sourceEntity->instanceof(eTYPE_LIVINGENTITY) ) - { - setLastHurtByMob(dynamic_pointer_cast(sourceEntity)); - } - - if ( sourceEntity->instanceof(eTYPE_PLAYER) ) - { - lastHurtByPlayerTime = PLAYER_HURT_EXPERIENCE_TIME; - lastHurtByPlayer = dynamic_pointer_cast(sourceEntity); - } - else if ( sourceEntity->instanceof(eTYPE_WOLF) ) - { - shared_ptr w = dynamic_pointer_cast(sourceEntity); - if (w->isTame()) - { - lastHurtByPlayerTime = PLAYER_HURT_EXPERIENCE_TIME; - lastHurtByPlayer = nullptr; - } - } - } - - if (sound && level->isClientSide) - { - return false; - } - - if (sound) - { - level->broadcastEntityEvent(shared_from_this(), EntityEvent::HURT); - if (source != DamageSource::drown) markHurt(); - if (sourceEntity != nullptr) - { - double xd = sourceEntity->x - x; - double zd = sourceEntity->z - z; - while (xd * xd + zd * zd < 0.0001) - { - xd = (Math::random() - Math::random()) * 0.01; - zd = (Math::random() - Math::random()) * 0.01; - } - hurtDir = static_cast(atan2(zd, xd) * 180 / PI) - yRot; - knockback(sourceEntity, dmg, xd, zd); - } - else - { - hurtDir = static_cast((int)((Math::random() * 2) * 180)); // 4J This cast is the same as Java - } - } - - MemSect(31); - if (getHealth() <= 0) - { - if (sound) playSound(getDeathSound(), getSoundVolume(), getVoicePitch()); - die(source); - } - else - { - if (sound) playSound(getHurtSound(), getSoundVolume(), getVoicePitch()); - } - MemSect(0); - - return true; + if (isInvulnerable()) + { + return false; + } + + // 4J Stu - Reworked this function a bit to show hurt damage on the client before the server responds. + // Fix for #8823 - Gameplay: Confirmation that a monster or animal has taken damage from an attack is highly delayed + // 4J Stu - Change to the fix to only show damage when attacked, rather than collision damage + // Fix for #10299 - When in corners, passive mobs may show that they are taking damage. + // 4J Stu - Change to the fix for TU6, as source is never nullptr due to changes in 1.8.2 to what source actually is + if (level->isClientSide && dynamic_cast(source) == nullptr) + { + return false; + } + noActionTime = 0; + if (getHealth() <= 0) + { + return false; + } + + if (source->isFire() && hasEffect(MobEffect::fireResistance)) + { + // 4J-JEV, for new achievement Stayin'Frosty, TODO merge with Java version. + if (this->instanceof(eTYPE_PLAYER) && (source == DamageSource::lava)) // Only award when in lava (not any fire). + { + shared_ptr plr = dynamic_pointer_cast(shared_from_this()); + plr->awardStat(GenericStats::stayinFrosty(), GenericStats::param_stayinFrosty()); + } + return false; + } + + if ((source == DamageSource::anvil || source == DamageSource::fallingBlock) && getCarried(SLOT_HELM) != nullptr) + { + getCarried(SLOT_HELM)->hurtAndBreak(static_cast(dmg * 4 + random->nextFloat() * dmg * 2.0f), dynamic_pointer_cast(shared_from_this())); + dmg *= 0.75f; + } + + walkAnimSpeed = 1.5f; + + bool sound = true; + if (invulnerableTime > invulnerableDuration / 2.0f) + { + if (dmg <= lastHurt) + { + return false; + } + if (!level->isClientSide) + { + actuallyHurt(source, dmg - lastHurt); + } + lastHurt = dmg; + sound = false; + } + else + { + lastHurt = dmg; + lastHealth = getHealth(); + invulnerableTime = invulnerableDuration; + if (!level->isClientSide) + { + actuallyHurt(source, dmg); + } + hurtTime = hurtDuration = 10; + } + + hurtDir = 0; + + shared_ptr sourceEntity = source->getEntity(); + if (sourceEntity != nullptr) + { + if (sourceEntity->instanceof(eTYPE_LIVINGENTITY)) + { + setLastHurtByMob(dynamic_pointer_cast(sourceEntity)); + } + + if (sourceEntity->instanceof(eTYPE_PLAYER)) + { + lastHurtByPlayerTime = PLAYER_HURT_EXPERIENCE_TIME; + lastHurtByPlayer = dynamic_pointer_cast(sourceEntity); + } + else if (sourceEntity->instanceof(eTYPE_WOLF)) + { + shared_ptr w = dynamic_pointer_cast(sourceEntity); + if (w->isTame()) + { + lastHurtByPlayerTime = PLAYER_HURT_EXPERIENCE_TIME; + lastHurtByPlayer = nullptr; + } + } + } + + if (sound && level->isClientSide) + { + return false; + } + + if (sound) + { + level->broadcastEntityEvent(shared_from_this(), EntityEvent::HURT); + if (source != DamageSource::drown) + { + markHurt(); + } + if (sourceEntity != nullptr) + { + double xd = sourceEntity->x - x; + double zd = sourceEntity->z - z; + while (xd * xd + zd * zd < 0.0001) + { + xd = (Math::random() - Math::random()) * 0.01; + zd = (Math::random() - Math::random()) * 0.01; + } + hurtDir = static_cast(atan2(zd, xd) * 180 / PI) - yRot; + knockback(sourceEntity, dmg, xd, zd); + } + else + { + hurtDir = static_cast((int)((Math::random() * 2) * 180)); // 4J This cast is the same as Java + } + } + + MemSect(31); + if (getHealth() <= 0) + { + if (sound) + { + playSound(getDeathSound(), getSoundVolume(), getVoicePitch()); + } + die(source); + } + else + { + if (sound) + { + playSound(getHurtSound(), getSoundVolume(), getVoicePitch()); + } + } + MemSect(0); + + return true; } void LivingEntity::breakItem(shared_ptr itemInstance) { - playSound(eSoundType_RANDOM_BREAK, 0.8f, 0.8f + level->random->nextFloat() * 0.4f); - - for (int i = 0; i < 5; i++) - { - Vec3 *d = Vec3::newTemp((random->nextFloat() - 0.5) * 0.1, Math::random() * 0.1 + 0.1, 0); - d->xRot(-xRot * PI / 180); - d->yRot(-yRot * PI / 180); + playSound(eSoundType_RANDOM_BREAK, 0.8f, 0.8f + level->random->nextFloat() * 0.4f); - Vec3 *p = Vec3::newTemp((random->nextFloat() - 0.5) * 0.3, -random->nextFloat() * 0.6 - 0.3, 0.6); - p->xRot(-xRot * PI / 180); - p->yRot(-yRot * PI / 180); - p = p->add(x, y + getHeadHeight(), z); - level->addParticle(PARTICLE_ICONCRACK(itemInstance->getItem()->id,0), p->x, p->y, p->z, d->x, d->y + 0.05, d->z); - } + for (int i = 0; i < 5; i++) + { + Vec3 *d = Vec3::newTemp((random->nextFloat() - 0.5) * 0.1, Math::random() * 0.1 + 0.1, 0); + d->xRot(-xRot * PI / 180); + d->yRot(-yRot * PI / 180); + + Vec3 *p = Vec3::newTemp((random->nextFloat() - 0.5) * 0.3, -random->nextFloat() * 0.6 - 0.3, 0.6); + p->xRot(-xRot * PI / 180); + p->yRot(-yRot * PI / 180); + p = p->add(x, y + getHeadHeight(), z); + level->addParticle(PARTICLE_ICONCRACK(itemInstance->getItem()->id, 0), p->x, p->y, p->z, d->x, d->y + 0.05, d->z); + } } void LivingEntity::die(DamageSource *source) { - shared_ptr sourceEntity = source->getEntity(); - shared_ptr killer = getKillCredit(); - if (deathScore >= 0 && killer != nullptr) killer->awardKillScore(shared_from_this(), deathScore); - - if (sourceEntity != nullptr) sourceEntity->killed( dynamic_pointer_cast( shared_from_this() ) ); - - dead = true; - - if (!level->isClientSide) - { - int playerBonus = 0; - - shared_ptr player = nullptr; - if ( (sourceEntity != nullptr) && sourceEntity->instanceof(eTYPE_PLAYER) ) - { - player = dynamic_pointer_cast(sourceEntity); - playerBonus = EnchantmentHelper::getKillingLootBonus(dynamic_pointer_cast(player)); - } - - if (!isBaby() && level->getGameRules()->getBoolean(GameRules::RULE_DOMOBLOOT)) - { - dropDeathLoot(lastHurtByPlayerTime > 0, playerBonus); - dropEquipment(lastHurtByPlayerTime > 0, playerBonus); - if (lastHurtByPlayerTime > 0) - { - int rareLoot = random->nextInt(200) - playerBonus; - if (rareLoot < 5) - { - dropRareDeathLoot((rareLoot <= 0) ? 1 : 0); - } - } - } - - // 4J-JEV, hook for Durango mobKill event. - if (player != nullptr) - { - player->awardStat(GenericStats::killMob(),GenericStats::param_mobKill(player, dynamic_pointer_cast(shared_from_this()), source)); - } - } - - level->broadcastEntityEvent(shared_from_this(), EntityEvent::DEATH); + shared_ptr sourceEntity = source->getEntity(); + shared_ptr killer = getKillCredit(); + if (deathScore >= 0 && killer != nullptr) + { + killer->awardKillScore(shared_from_this(), deathScore); + } + + if (sourceEntity != nullptr) + { + sourceEntity->killed(dynamic_pointer_cast(shared_from_this())); + } + + dead = true; + + if (!level->isClientSide) + { + int playerBonus = 0; + + shared_ptr player = nullptr; + if ((sourceEntity != nullptr) && sourceEntity->instanceof(eTYPE_PLAYER)) + { + player = dynamic_pointer_cast(sourceEntity); + playerBonus = EnchantmentHelper::getKillingLootBonus(dynamic_pointer_cast(player)); + } + + if (!isBaby() && level->getGameRules()->getBoolean(GameRules::RULE_DOMOBLOOT)) + { + dropDeathLoot(lastHurtByPlayerTime > 0, playerBonus); + dropEquipment(lastHurtByPlayerTime > 0, playerBonus); + if (lastHurtByPlayerTime > 0) + { + int rareLoot = random->nextInt(200) - playerBonus; + if (rareLoot < 5) + { + dropRareDeathLoot((rareLoot <= 0) ? 1 : 0); + } + } + } + + // 4J-JEV, hook for Durango mobKill event. + if (player != nullptr) + { + player->awardStat(GenericStats::killMob(), GenericStats::param_mobKill(player, dynamic_pointer_cast(shared_from_this()), source)); + } + } + + level->broadcastEntityEvent(shared_from_this(), EntityEvent::DEATH); } void LivingEntity::dropEquipment(bool byPlayer, int playerBonusLevel) @@ -935,45 +998,47 @@ void LivingEntity::dropEquipment(bool byPlayer, int playerBonusLevel) void LivingEntity::knockback(shared_ptr source, float dmg, double xd, double zd) { - if (random->nextDouble() < getAttribute(SharedMonsterAttributes::KNOCKBACK_RESISTANCE)->getValue()) - { - return; - } + if (random->nextDouble() < getAttribute(SharedMonsterAttributes::KNOCKBACK_RESISTANCE)->getValue()) + { + return; + } - hasImpulse = true; - float dd = Mth::sqrt(xd * xd + zd * zd); - float pow = 0.4f; + hasImpulse = true; + float dd = Mth::sqrt(xd * xd + zd * zd); + float pow = 0.4f; - this->xd /= 2; - yd /= 2; - this->zd /= 2; + this->xd /= 2; + yd /= 2; + this->zd /= 2; - this->xd -= xd / dd * pow; - yd += pow; - this->zd -= zd / dd * pow; + this->xd -= xd / dd * pow; + yd += pow; + this->zd -= zd / dd * pow; - if (yd > 0.4f) yd = 0.4f; + if (yd > 0.4f) + { + yd = 0.4f; + } } int LivingEntity::getHurtSound() { - return eSoundType_DAMAGE_HURT; + return eSoundType_DAMAGE_HURT; } int LivingEntity::getDeathSound() { - return eSoundType_DAMAGE_HURT; + return eSoundType_DAMAGE_HURT; } /** -* Drop extra rare loot. Only occurs roughly 5% of the time, rareRootLevel -* is set to 1 (otherwise 0) 1% of the time. -* -* @param rareLootLevel -*/ + * Drop extra rare loot. Only occurs roughly 5% of the time, rareRootLevel + * is set to 1 (otherwise 0) 1% of the time. + * + * @param rareLootLevel + */ void LivingEntity::dropRareDeathLoot(int rareLootLevel) { - } void LivingEntity::dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel) @@ -982,103 +1047,103 @@ void LivingEntity::dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel) bool LivingEntity::onLadder() { - int xt = Mth::floor(x); - int yt = Mth::floor(bb->y0); - int zt = Mth::floor(z); - - int iTile = level->getTile(xt, yt, zt); - switch (iTile) - { - case Tile::ladder_Id: - case Tile::vine_Id: // 4J-PB - TU9 - add climbable vines - return true; - case Tile::trapdoor_Id: // hexagonny - add climbable (opened) trapdoors - { - if ((level->getData(xt, yt, zt) & 0x4) != 0) - { - switch (level->getTile(xt, yt + 1, zt)) - { - case Tile::ladder_Id: - case Tile::vine_Id: - return false; // Opened trapdoor should only be climbable when it's only at the top. - default: - return level->getTile(xt, yt - 1, zt) == Tile::ladder_Id; - } - } - break; - } - default: - break; - } - return false; + int xt = Mth::floor(x); + int yt = Mth::floor(bb->y0); + int zt = Mth::floor(z); + + int iTile = level->getTile(xt, yt, zt); + switch (iTile) + { + case Tile::ladder_Id: + case Tile::vine_Id: // 4J-PB - TU9 - add climbable vines + return true; + case Tile::trapdoor_Id: // hexagonny - add climbable (opened) trapdoors + { + if ((level->getData(xt, yt, zt) & 0x4) != 0) + { + switch (level->getTile(xt, yt + 1, zt)) + { + case Tile::ladder_Id: + case Tile::vine_Id: + return false; // Opened trapdoor should only be climbable when it's only at the top. + default: + return level->getTile(xt, yt - 1, zt) == Tile::ladder_Id; + } + } + break; + } + default: + break; + } + return false; } bool LivingEntity::isShootable() { - return true; + return true; } bool LivingEntity::isAlive() { - return !removed && getHealth() > 0; + return !removed && getHealth() > 0; } void LivingEntity::causeFallDamage(float distance) { - Entity::causeFallDamage(distance); - MobEffectInstance *jumpBoost = getEffect(MobEffect::jump); - float padding = jumpBoost != nullptr ? jumpBoost->getAmplifier() + 1 : 0; - - int dmg = static_cast(ceil(distance - 3 - padding)); - if (dmg > 0) - { - // 4J - new sounds here brought forward from 1.2.3 - if (dmg > 4) - { - playSound(eSoundType_DAMAGE_FALL_BIG, 1, 1); - } - else - { - playSound(eSoundType_DAMAGE_FALL_SMALL, 1, 1); - } - hurt(DamageSource::fall, dmg); - - int t = level->getTile( Mth::floor(x), Mth::floor(y - 0.2f - this->heightOffset), Mth::floor(z)); - if (t > 0) - { - const Tile::SoundType *soundType = Tile::tiles[t]->soundType; - MemSect(31); - playSound(soundType->getStepSound(), soundType->getVolume() * 0.5f, soundType->getPitch() * 0.75f); - MemSect(0); - } - } + Entity::causeFallDamage(distance); + MobEffectInstance *jumpBoost = getEffect(MobEffect::jump); + float padding = jumpBoost != nullptr ? jumpBoost->getAmplifier() + 1 : 0; + + int dmg = static_cast(ceil(distance - 3 - padding)); + if (dmg > 0) + { + // 4J - new sounds here brought forward from 1.2.3 + if (dmg > 4) + { + playSound(eSoundType_DAMAGE_FALL_BIG, 1, 1); + } + else + { + playSound(eSoundType_DAMAGE_FALL_SMALL, 1, 1); + } + hurt(DamageSource::fall, dmg); + + int t = level->getTile(Mth::floor(x), Mth::floor(y - 0.2f - this->heightOffset), Mth::floor(z)); + if (t > 0) + { + const Tile::SoundType *soundType = Tile::tiles[t]->soundType; + MemSect(31); + playSound(soundType->getStepSound(), soundType->getVolume() * 0.5f, soundType->getPitch() * 0.75f); + MemSect(0); + } + } } void LivingEntity::animateHurt() { - hurtTime = hurtDuration = 10; - hurtDir = 0; + hurtTime = hurtDuration = 10; + hurtDir = 0; } /** -* Fetches the mob's armor value, from 0 (no armor) to 20 (full armor) -* -* @return -*/ + * Fetches the mob's armor value, from 0 (no armor) to 20 (full armor) + * + * @return + */ int LivingEntity::getArmorValue() { - int val = 0; - ItemInstanceArray items = getEquipmentSlots(); - for (unsigned int i = 0; i < items.length; ++i) - { - shared_ptr item = items[i]; - if (item != nullptr && dynamic_cast(item->getItem()) != nullptr) - { - int baseProtection = static_cast(item->getItem())->defense; - val += baseProtection; - } - } - return val; + int val = 0; + ItemInstanceArray items = getEquipmentSlots(); + for (unsigned int i = 0; i < items.length; ++i) + { + shared_ptr item = items[i]; + if (item != nullptr && dynamic_cast(item->getItem()) != nullptr) + { + int baseProtection = static_cast(item->getItem())->defense; + val += baseProtection; + } + } + return val; } void LivingEntity::hurtArmor(float damage) @@ -1087,242 +1152,259 @@ void LivingEntity::hurtArmor(float damage) float LivingEntity::getDamageAfterArmorAbsorb(DamageSource *damageSource, float damage) { - if (!damageSource->isBypassArmor()) - { - int absorb = 25 - getArmorValue(); - float v = (damage) * absorb; - hurtArmor(damage); - damage = v / 25; - } - return damage; + if (!damageSource->isBypassArmor()) + { + int absorb = 25 - getArmorValue(); + float v = (damage)*absorb; + hurtArmor(damage); + damage = v / 25; + } + return damage; } float LivingEntity::getDamageAfterMagicAbsorb(DamageSource *damageSource, float damage) { - // [EB]: Stupid hack :( - if ( this->instanceof(eTYPE_ZOMBIE) ) - { - damage = damage; - } - if (hasEffect(MobEffect::damageResistance) && damageSource != DamageSource::outOfWorld) - { - int absorbValue = (getEffect(MobEffect::damageResistance)->getAmplifier() + 1) * 5; - int absorb = 25 - absorbValue; - float v = (damage) * absorb; - damage = v / 25; - } - - if (damage <= 0) return 0; - - int enchantmentArmor = EnchantmentHelper::getDamageProtection(getEquipmentSlots(), damageSource); - if (enchantmentArmor > 20) - { - enchantmentArmor = 20; - } - if (enchantmentArmor > 0 && enchantmentArmor <= 20) - { - int absorb = 25 - enchantmentArmor; - float v = damage * absorb; - damage = v / 25; - } - - return damage; + // [EB]: Stupid hack :( + if (this->instanceof(eTYPE_ZOMBIE)) + { + damage = damage; + } + if (hasEffect(MobEffect::damageResistance) && damageSource != DamageSource::outOfWorld) + { + int absorbValue = (getEffect(MobEffect::damageResistance)->getAmplifier() + 1) * 5; + int absorb = 25 - absorbValue; + float v = (damage)*absorb; + damage = v / 25; + } + + if (damage <= 0) + { + return 0; + } + + int enchantmentArmor = EnchantmentHelper::getDamageProtection(getEquipmentSlots(), damageSource); + if (enchantmentArmor > 20) + { + enchantmentArmor = 20; + } + if (enchantmentArmor > 0 && enchantmentArmor <= 20) + { + int absorb = 25 - enchantmentArmor; + float v = damage * absorb; + damage = v / 25; + } + + return damage; } void LivingEntity::actuallyHurt(DamageSource *source, float dmg) { - if (isInvulnerable()) return; - dmg = getDamageAfterArmorAbsorb(source, dmg); - dmg = getDamageAfterMagicAbsorb(source, dmg); + if (isInvulnerable()) + { + return; + } + 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; + float originalDamage = dmg; + dmg = max(dmg - getAbsorptionAmount(), 0.0f); + setAbsorptionAmount(getAbsorptionAmount() - (originalDamage - dmg)); + if (dmg == 0) + { + return; + } - float oldHealth = getHealth(); - setHealth(oldHealth - dmg); - getCombatTracker()->recordDamage(source, oldHealth, dmg); - setAbsorptionAmount(getAbsorptionAmount() - dmg); + float oldHealth = getHealth(); + setHealth(oldHealth - dmg); + getCombatTracker()->recordDamage(source, oldHealth, dmg); + setAbsorptionAmount(getAbsorptionAmount() - dmg); } CombatTracker *LivingEntity::getCombatTracker() { - return combatTracker; + return combatTracker; } shared_ptr LivingEntity::getKillCredit() { - if (combatTracker->getKiller() != nullptr) return combatTracker->getKiller(); - if (lastHurtByPlayer != nullptr) return lastHurtByPlayer; - if (lastHurtByMob != nullptr) return lastHurtByMob; - return nullptr; + if (combatTracker->getKiller() != nullptr) + { + return combatTracker->getKiller(); + } + if (lastHurtByPlayer != nullptr) + { + return lastHurtByPlayer; + } + if (lastHurtByMob != nullptr) + { + return lastHurtByMob; + } + return nullptr; } float LivingEntity::getMaxHealth() { - return static_cast(getAttribute(SharedMonsterAttributes::MAX_HEALTH)->getValue()); + return static_cast(getAttribute(SharedMonsterAttributes::MAX_HEALTH)->getValue()); } int LivingEntity::getArrowCount() { - return entityData->getByte(DATA_ARROW_COUNT_ID); + return entityData->getByte(DATA_ARROW_COUNT_ID); } void LivingEntity::setArrowCount(int count) { - entityData->set(DATA_ARROW_COUNT_ID, static_cast(count)); + entityData->set(DATA_ARROW_COUNT_ID, static_cast(count)); } int LivingEntity::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; + 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 LivingEntity::swing() { - if (!swinging || swingTime >= getCurrentSwingDuration() / 2 || swingTime < 0) - { - swingTime = -1; - swinging = true; + if (!swinging || swingTime >= getCurrentSwingDuration() / 2 || swingTime < 0) + { + swingTime = -1; + swinging = true; - if (dynamic_cast(level) != nullptr) - { - static_cast(level)->getTracker()->broadcast(shared_from_this(), std::make_shared(shared_from_this(), AnimatePacket::SWING)); - } - } + if (dynamic_cast(level) != nullptr) + { + static_cast(level)->getTracker()->broadcast(shared_from_this(), std::make_shared(shared_from_this(), AnimatePacket::SWING)); + } + } } void LivingEntity::handleEntityEvent(byte id) { - if (id == EntityEvent::HURT) - { - walkAnimSpeed = 1.5f; - - invulnerableTime = invulnerableDuration; - hurtTime = hurtDuration = 10; - hurtDir = 0; - - MemSect(31); - // 4J-PB -added because villagers have no sounds - int iHurtSound=getHurtSound(); - if(iHurtSound!=-1) - { - playSound(iHurtSound, getSoundVolume(), (random->nextFloat() - random->nextFloat()) * 0.2f + 1.0f); - } - MemSect(0); - hurt(DamageSource::genericSource, 0); - } - else if (id == EntityEvent::DEATH) - { - MemSect(31); - // 4J-PB -added because villagers have no sounds - int iDeathSound=getDeathSound(); - if(iDeathSound!=-1) - { - playSound(iDeathSound, getSoundVolume(), (random->nextFloat() - random->nextFloat()) * 0.2f + 1.0f); - } - MemSect(0); - setHealth(0); - die(DamageSource::genericSource); - } - else - { - Entity::handleEntityEvent(id); - } + if (id == EntityEvent::HURT) + { + walkAnimSpeed = 1.5f; + + invulnerableTime = invulnerableDuration; + hurtTime = hurtDuration = 10; + hurtDir = 0; + + MemSect(31); + // 4J-PB -added because villagers have no sounds + int iHurtSound = getHurtSound(); + if (iHurtSound != -1) + { + playSound(iHurtSound, getSoundVolume(), (random->nextFloat() - random->nextFloat()) * 0.2f + 1.0f); + } + MemSect(0); + hurt(DamageSource::genericSource, 0); + } + else if (id == EntityEvent::DEATH) + { + MemSect(31); + // 4J-PB -added because villagers have no sounds + int iDeathSound = getDeathSound(); + if (iDeathSound != -1) + { + playSound(iDeathSound, getSoundVolume(), (random->nextFloat() - random->nextFloat()) * 0.2f + 1.0f); + } + MemSect(0); + setHealth(0); + die(DamageSource::genericSource); + } + else + { + Entity::handleEntityEvent(id); + } } void LivingEntity::outOfWorld() { - hurt(DamageSource::outOfWorld, 4); + hurt(DamageSource::outOfWorld, 4); } void LivingEntity::updateSwingTime() { - int currentSwingDuration = getCurrentSwingDuration(); - if (swinging) - { - swingTime++; - if (swingTime >= currentSwingDuration) - { - swingTime = 0; - swinging = false; - } - } - else - { - swingTime = 0; - } + int currentSwingDuration = getCurrentSwingDuration(); + if (swinging) + { + swingTime++; + if (swingTime >= currentSwingDuration) + { + swingTime = 0; + swinging = false; + } + } + else + { + swingTime = 0; + } - attackAnim = swingTime / static_cast(currentSwingDuration); + attackAnim = swingTime / static_cast(currentSwingDuration); } AttributeInstance *LivingEntity::getAttribute(Attribute *attribute) { - return getAttributes()->getInstance(attribute); + return getAttributes()->getInstance(attribute); } BaseAttributeMap *LivingEntity::getAttributes() { - if (attributes == nullptr) - { - attributes = new ServersideAttributeMap(); - } + if (attributes == nullptr) + { + attributes = new ServersideAttributeMap(); + } - return attributes; + return attributes; } MobType LivingEntity::getMobType() { - return UNDEFINED; + return UNDEFINED; } void LivingEntity::setSprinting(bool value) { - Entity::setSprinting(value); + Entity::setSprinting(value); - AttributeInstance *speed = getAttribute(SharedMonsterAttributes::MOVEMENT_SPEED); - if (speed->getModifier(eModifierId_MOB_SPRINTING) != nullptr) - { - speed->removeModifier(eModifierId_MOB_SPRINTING); - } - if (value) - { - speed->addModifier(new AttributeModifier(*SPEED_MODIFIER_SPRINTING)); - } + AttributeInstance *speed = getAttribute(SharedMonsterAttributes::MOVEMENT_SPEED); + if (speed->getModifier(eModifierId_MOB_SPRINTING) != nullptr) + { + speed->removeModifier(eModifierId_MOB_SPRINTING); + } + if (value) + { + speed->addModifier(new AttributeModifier(*SPEED_MODIFIER_SPRINTING)); + } } float LivingEntity::getSoundVolume() { - return 1; + return 1; } float LivingEntity::getVoicePitch() { - if (isBaby()) - { - return (random->nextFloat() - random->nextFloat()) * 0.2f + 1.5f; - - } - return (random->nextFloat() - random->nextFloat()) * 0.2f + 1.0f; + if (isBaby()) + { + return (random->nextFloat() - random->nextFloat()) * 0.2f + 1.5f; + } + return (random->nextFloat() - random->nextFloat()) * 0.2f + 1.0f; } bool LivingEntity::isImmobile() { - return getHealth() <= 0; + return getHealth() <= 0; } void LivingEntity::teleportTo(double x, double y, double z) { - moveTo(x, y, z, yRot, xRot); + moveTo(x, y, z, yRot, xRot); } void LivingEntity::findStandUpPosition(shared_ptr vehicle) @@ -1371,7 +1453,7 @@ void LivingEntity::findStandUpPosition(shared_ptr vehicle) bool LivingEntity::shouldShowName() { - return false; + return false; } Icon *LivingEntity::getItemInHandIcon(shared_ptr item, int layer) @@ -1380,446 +1462,537 @@ Icon *LivingEntity::getItemInHandIcon(shared_ptr item, int layer) { return item->getItem()->getLayerIcon(item->getAuxValue(), layer); } - return item->getIcon(); + return item->getIcon(); } void LivingEntity::jumpFromGround() { - yd = 0.42f; - if (hasEffect(MobEffect::jump)) - { - yd += (getEffect(MobEffect::jump)->getAmplifier() + 1) * .1f; - } - if (isSprinting()) - { - float rr = yRot * Mth::RAD_TO_GRAD; + yd = 0.42f; + if (hasEffect(MobEffect::jump)) + { + yd += (getEffect(MobEffect::jump)->getAmplifier() + 1) * .1f; + } + if (isSprinting()) + { + float rr = yRot * Mth::RAD_TO_GRAD; - xd -= Mth::sin(rr) * 0.2f; - zd += Mth::cos(rr) * 0.2f; - } - this->hasImpulse = true; + xd -= Mth::sin(rr) * 0.2f; + zd += Mth::cos(rr) * 0.2f; + } + this->hasImpulse = true; } void LivingEntity::travel(float xa, float ya) { #ifdef __PSVITA__ - // AP - dynamic_pointer_cast is a non-trivial call - Player *thisPlayer = nullptr; - if( this->instanceof(eTYPE_PLAYER) ) - { - thisPlayer = (Player*) this; - } + // AP - dynamic_pointer_cast is a non-trivial call + Player *thisPlayer = nullptr; + if (this->instanceof(eTYPE_PLAYER)) + { + thisPlayer = (Player *)this; + } #else - shared_ptr thisPlayer = dynamic_pointer_cast(shared_from_this()); + shared_ptr thisPlayer = dynamic_pointer_cast(shared_from_this()); #endif - if (isInWater() && !(thisPlayer && thisPlayer->abilities.flying) ) - { - double yo = y; - moveRelative(xa, ya, useNewAi() ? 0.04f : 0.02f); - move(xd, yd, zd); - - xd *= 0.80f; - yd *= 0.80f; - zd *= 0.80f; - yd -= 0.02; - - if (horizontalCollision && isFree(xd, yd + 0.6f - y + yo, zd)) - { - yd = 0.3f; - } - } - else if (isInLava() && !(thisPlayer && thisPlayer->abilities.flying) ) - { - double yo = y; - moveRelative(xa, ya, 0.02f); - move(xd, yd, zd); - xd *= 0.50f; - yd *= 0.50f; - zd *= 0.50f; - yd -= 0.02; - - if (horizontalCollision && isFree(xd, yd + 0.6f - y + yo, zd)) - { - yd = 0.3f; - } - } - else - { - float friction = 0.91f; - if (onGround) - { - friction = 0.6f * 0.91f; - int t = level->getTile(Mth::floor(x), Mth::floor(bb->y0) - 1, Mth::floor(z)); - if (t > 0) - { - friction = Tile::tiles[t]->friction * 0.91f; - } - } - - float friction2 = (0.6f * 0.6f * 0.91f * 0.91f * 0.6f * 0.91f) / (friction * friction * friction); - - float speed; - if (onGround) - { - speed = getSpeed() * friction2; - } - else - { - speed = flyingSpeed; - } - - moveRelative(xa, ya, speed); - - friction = 0.91f; - if (onGround) - { - friction = 0.6f * 0.91f; - int t = level->getTile( Mth::floor(x), Mth::floor(bb->y0) - 1, Mth::floor(z)); - if (t > 0) - { - friction = Tile::tiles[t]->friction * 0.91f; - } - } - if (onLadder()) - { - float max = 0.15f; - if (xd < -max) xd = -max; - if (xd > max) xd = max; - if (zd < -max) zd = -max; - if (zd > max) zd = max; - fallDistance = 0; - if (yd < -0.15) yd = -0.15; - bool playerSneaking = isSneaking() && this->instanceof(eTYPE_PLAYER); - if (playerSneaking && yd < 0) yd = 0; - } - - move(xd, yd, zd); - - if (horizontalCollision && onLadder()) - { - yd = 0.2; - } - - if (!level->isClientSide || (level->hasChunkAt(static_cast(x), 0, static_cast(z)) && level->getChunkAt(static_cast(x), static_cast(z))->loaded)) - { - yd -= 0.08; - } - else if (y > 0) - { - yd = -0.1; - } - else - { - yd = 0; - } - - yd *= 0.98f; - xd *= friction; - zd *= friction; - } - - walkAnimSpeedO = walkAnimSpeed; - double xxd = x - xo; - double zzd = z - zo; - float wst = Mth::sqrt(xxd * xxd + zzd * zzd) * 4; - if (wst > 1) wst = 1; - walkAnimSpeed += (wst - walkAnimSpeed) * 0.4f; - walkAnimPos += walkAnimSpeed; + if (isInWater() && !(thisPlayer && thisPlayer->abilities.flying)) + { + double yo = y; + moveRelative(xa, ya, useNewAi() ? 0.04f : 0.02f); + move(xd, yd, zd); + + xd *= 0.80f; + yd *= 0.80f; + zd *= 0.80f; + yd -= 0.02; + + if (horizontalCollision && isFree(xd, yd + 0.6f - y + yo, zd)) + { + yd = 0.3f; + } + } + else if (isInLava() && !(thisPlayer && thisPlayer->abilities.flying)) + { + double yo = y; + moveRelative(xa, ya, 0.02f); + move(xd, yd, zd); + xd *= 0.50f; + yd *= 0.50f; + zd *= 0.50f; + yd -= 0.02; + + if (horizontalCollision && isFree(xd, yd + 0.6f - y + yo, zd)) + { + yd = 0.3f; + } + } + else + { + float friction = 0.91f; + if (onGround) + { + friction = 0.6f * 0.91f; + int t = level->getTile(Mth::floor(x), Mth::floor(bb->y0) - 1, Mth::floor(z)); + if (t > 0) + { + friction = Tile::tiles[t]->friction * 0.91f; + } + } + + float friction2 = (0.6f * 0.6f * 0.91f * 0.91f * 0.6f * 0.91f) / (friction * friction * friction); + + float speed; + if (onGround) + { + speed = getSpeed() * friction2; + } + else + { + speed = flyingSpeed; + } + + moveRelative(xa, ya, speed); + + friction = 0.91f; + if (onGround) + { + friction = 0.6f * 0.91f; + int t = level->getTile(Mth::floor(x), Mth::floor(bb->y0) - 1, Mth::floor(z)); + if (t > 0) + { + friction = Tile::tiles[t]->friction * 0.91f; + } + } + if (onLadder()) + { + float max = 0.15f; + if (xd < -max) + { + xd = -max; + } + if (xd > max) + { + xd = max; + } + if (zd < -max) + { + zd = -max; + } + if (zd > max) + { + zd = max; + } + fallDistance = 0; + if (yd < -0.15) + { + yd = -0.15; + } + bool playerSneaking = isSneaking() && this->instanceof(eTYPE_PLAYER); + if (playerSneaking && yd < 0) + { + yd = 0; + } + } + + move(xd, yd, zd); + + if (horizontalCollision && onLadder()) + { + yd = 0.2; + } + + if (!level->isClientSide || (level->hasChunkAt(static_cast(x), 0, static_cast(z)) && level->getChunkAt(static_cast(x), static_cast(z))->loaded)) + { + yd -= 0.08; + } + else if (y > 0) + { + yd = -0.1; + } + else + { + yd = 0; + } + + yd *= 0.98f; + xd *= friction; + zd *= friction; + } + + walkAnimSpeedO = walkAnimSpeed; + double xxd = x - xo; + double zzd = z - zo; + float wst = Mth::sqrt(xxd * xxd + zzd * zzd) * 4; + if (wst > 1) + { + wst = 1; + } + walkAnimSpeed += (wst - walkAnimSpeed) * 0.4f; + walkAnimPos += walkAnimSpeed; } // 4J - added for more accurate lighting of mobs. Takes a weighted average of all tiles touched by the bounding volume of the entity - the method in the Entity class (which used to be used for // mobs too) simply gets a single tile's lighting value causing sudden changes of lighting values when entities go in and out of lit areas, for example when bobbing in the water. int LivingEntity::getLightColor(float a) { - float accum[2] = {0,0}; - float totVol = ( bb->x1 - bb->x0 ) * ( bb->y1 - bb->y0 ) * ( bb->z1 - bb->z0 ); - int xmin = Mth::floor(bb->x0); - int xmax = Mth::floor(bb->x1); - int ymin = Mth::floor(bb->y0); - int ymax = Mth::floor(bb->y1); - int zmin = Mth::floor(bb->z0); - int zmax = Mth::floor(bb->z1); - for( int xt = xmin; xt <= xmax; xt++ ) - for( int yt = ymin; yt <= ymax; yt++ ) - for( int zt = zmin; zt <= zmax; zt++ ) - { - float tilexmin = static_cast(xt); - float tilexmax = static_cast(xt + 1); - float tileymin = static_cast(yt); - float tileymax = static_cast(yt + 1); - float tilezmin = static_cast(zt); - float tilezmax = static_cast(zt + 1); - if( tilexmin < bb->x0 ) tilexmin = bb->x0; - if( tilexmax > bb->x1 ) tilexmax = bb->x1; - if( tileymin < bb->y0 ) tileymin = bb->y0; - if( tileymax > bb->y1 ) tileymax = bb->y1; - if( tilezmin < bb->z0 ) tilezmin = bb->z0; - if( tilezmax > bb->z1 ) tilezmax = bb->z1; - float tileVol = ( tilexmax - tilexmin ) * ( tileymax - tileymin ) * ( tilezmax - tilezmin ); - float frac = tileVol / totVol; - int lc = level->getLightColor(xt, yt, zt, 0); - accum[0] += frac * static_cast(lc & 0xffff); - accum[1] += frac * static_cast(lc >> 16); - } - - if( accum[0] > 240.0f ) accum[0] = 240.0f; - if( accum[1] > 240.0f ) accum[1] = 240.0f; - - return ( static_cast(accum[1])<<16) | static_cast(accum[0]); + float accum[2] = {0, 0}; + float totVol = (bb->x1 - bb->x0) * (bb->y1 - bb->y0) * (bb->z1 - bb->z0); + int xmin = Mth::floor(bb->x0); + int xmax = Mth::floor(bb->x1); + int ymin = Mth::floor(bb->y0); + int ymax = Mth::floor(bb->y1); + int zmin = Mth::floor(bb->z0); + int zmax = Mth::floor(bb->z1); + for (int xt = xmin; xt <= xmax; xt++) + { + for (int yt = ymin; yt <= ymax; yt++) + { + for (int zt = zmin; zt <= zmax; zt++) + { + float tilexmin = static_cast(xt); + float tilexmax = static_cast(xt + 1); + float tileymin = static_cast(yt); + float tileymax = static_cast(yt + 1); + float tilezmin = static_cast(zt); + float tilezmax = static_cast(zt + 1); + if (tilexmin < bb->x0) + { + tilexmin = bb->x0; + } + if (tilexmax > bb->x1) + { + tilexmax = bb->x1; + } + if (tileymin < bb->y0) + { + tileymin = bb->y0; + } + if (tileymax > bb->y1) + { + tileymax = bb->y1; + } + if (tilezmin < bb->z0) + { + tilezmin = bb->z0; + } + if (tilezmax > bb->z1) + { + tilezmax = bb->z1; + } + float tileVol = (tilexmax - tilexmin) * (tileymax - tileymin) * (tilezmax - tilezmin); + float frac = tileVol / totVol; + int lc = level->getLightColor(xt, yt, zt, 0); + accum[0] += frac * static_cast(lc & 0xffff); + accum[1] += frac * static_cast(lc >> 16); + } + } + } + + if (accum[0] > 240.0f) + { + accum[0] = 240.0f; + } + if (accum[1] > 240.0f) + { + accum[1] = 240.0f; + } + + return (static_cast(accum[1]) << 16) | static_cast(accum[0]); } bool LivingEntity::useNewAi() { - return false; + return false; } float LivingEntity::getSpeed() { - if (useNewAi()) - { - return speed; - } - else - { - return 0.1f; - } + if (useNewAi()) + { + return speed; + } + else + { + return 0.1f; + } } void LivingEntity::setSpeed(float speed) { - this->speed = speed; + this->speed = speed; } bool LivingEntity::doHurtTarget(shared_ptr target) { - setLastHurtMob(target); - return false; + setLastHurtMob(target); + return false; } bool LivingEntity::isSleeping() { - return false; + return false; } void LivingEntity::tick() { - Entity::tick(); - - if (!level->isClientSide) - { - int arrowCount = getArrowCount(); - if (arrowCount > 0) - { - if (removeArrowTime <= 0) - { - removeArrowTime = SharedConstants::TICKS_PER_SECOND * (30 - arrowCount); - } - removeArrowTime--; - if (removeArrowTime <= 0) - { - setArrowCount(arrowCount - 1); - } - } - - for (int i = 0; i < 5; i++) - { - shared_ptr previous = lastEquipment[i]; - shared_ptr current = getCarried(i); - - if (!ItemInstance::matches(current, previous)) - { - static_cast(level)->getTracker()->broadcast(shared_from_this(), std::make_shared(entityId, i, current)); - if (previous != nullptr) attributes->removeItemModifiers(previous); - if (current != nullptr) attributes->addItemModifiers(current); - lastEquipment[i] = current == nullptr ? nullptr : current->copy(); - } - } - } - - aiStep(); - - double xd = x - xo; - double zd = z - zo; - - float sideDist = xd * xd + zd * zd; - - float yBodyRotT = yBodyRot; - - float walkSpeed = 0; - oRun = run; - float tRun = 0; - if (sideDist > 0.05f * 0.05f) - { - tRun = 1; - walkSpeed = sqrt(sideDist) * 3; - yBodyRotT = (static_cast(atan2(zd, xd)) * 180 / (float) PI - 90); - } - if (attackAnim > 0) - { - yBodyRotT = yRot; - } - if (!onGround) - { - tRun = 0; - } - run = run + (tRun - run) * 0.3f; - - walkSpeed = tickHeadTurn(yBodyRotT, walkSpeed); - - while (yRot - yRotO < -180) - yRotO -= 360; - while (yRot - yRotO >= 180) - yRotO += 360; - - while (yBodyRot - yBodyRotO < -180) - yBodyRotO -= 360; - while (yBodyRot - yBodyRotO >= 180) - yBodyRotO += 360; - - while (xRot - xRotO < -180) - xRotO -= 360; - while (xRot - xRotO >= 180) - xRotO += 360; - - while (yHeadRot - yHeadRotO < -180) - yHeadRotO -= 360; - while (yHeadRot - yHeadRotO >= 180) - yHeadRotO += 360; - - animStep += walkSpeed; + Entity::tick(); + + if (!level->isClientSide) + { + int arrowCount = getArrowCount(); + if (arrowCount > 0) + { + if (removeArrowTime <= 0) + { + removeArrowTime = SharedConstants::TICKS_PER_SECOND * (30 - arrowCount); + } + removeArrowTime--; + if (removeArrowTime <= 0) + { + setArrowCount(arrowCount - 1); + } + } + + for (int i = 0; i < 5; i++) + { + shared_ptr previous = lastEquipment[i]; + shared_ptr current = getCarried(i); + + if (!ItemInstance::matches(current, previous)) + { + static_cast(level)->getTracker()->broadcast(shared_from_this(), std::make_shared(entityId, i, current)); + if (previous != nullptr) + { + attributes->removeItemModifiers(previous); + } + if (current != nullptr) + { + attributes->addItemModifiers(current); + } + lastEquipment[i] = current == nullptr ? nullptr : current->copy(); + } + } + } + + aiStep(); + + double xd = x - xo; + double zd = z - zo; + + float sideDist = xd * xd + zd * zd; + + float yBodyRotT = yBodyRot; + + float walkSpeed = 0; + oRun = run; + float tRun = 0; + if (sideDist > 0.05f * 0.05f) + { + tRun = 1; + walkSpeed = sqrt(sideDist) * 3; + yBodyRotT = (static_cast(atan2(zd, xd)) * 180 / (float)PI - 90); + } + if (attackAnim > 0) + { + yBodyRotT = yRot; + } + if (!onGround) + { + tRun = 0; + } + run = run + (tRun - run) * 0.3f; + + walkSpeed = tickHeadTurn(yBodyRotT, walkSpeed); + + while (yRot - yRotO < -180) + { + yRotO -= 360; + } + while (yRot - yRotO >= 180) + { + yRotO += 360; + } + + while (yBodyRot - yBodyRotO < -180) + { + yBodyRotO -= 360; + } + while (yBodyRot - yBodyRotO >= 180) + { + yBodyRotO += 360; + } + + while (xRot - xRotO < -180) + { + xRotO -= 360; + } + while (xRot - xRotO >= 180) + { + xRotO += 360; + } + + while (yHeadRot - yHeadRotO < -180) + { + yHeadRotO -= 360; + } + while (yHeadRot - yHeadRotO >= 180) + { + yHeadRotO += 360; + } + + animStep += walkSpeed; } float LivingEntity::tickHeadTurn(float yBodyRotT, float walkSpeed) { - float yBodyRotD = Mth::wrapDegrees(yBodyRotT - yBodyRot); - yBodyRot += yBodyRotD * 0.3f; + float yBodyRotD = Mth::wrapDegrees(yBodyRotT - yBodyRot); + yBodyRot += yBodyRotD * 0.3f; - float headDiff = Mth::wrapDegrees(yRot - yBodyRot); - bool behind = headDiff < -90 || headDiff >= 90; - if (headDiff < -75) headDiff = -75; - if (headDiff >= 75) headDiff = +75; - yBodyRot = yRot - headDiff; - if (headDiff * headDiff > 50 * 50) - { - yBodyRot += headDiff * 0.2f; - } + float headDiff = Mth::wrapDegrees(yRot - yBodyRot); + bool behind = headDiff < -90 || headDiff >= 90; + if (headDiff < -75) + { + headDiff = -75; + } + if (headDiff >= 75) + { + headDiff = +75; + } + yBodyRot = yRot - headDiff; + if (headDiff * headDiff > 50 * 50) + { + yBodyRot += headDiff * 0.2f; + } - if (behind) - { - walkSpeed *= -1; - } + if (behind) + { + walkSpeed *= -1; + } - return walkSpeed; + return walkSpeed; } void LivingEntity::aiStep() { - if (noJumpDelay > 0) noJumpDelay--; - if (lSteps > 0) - { - double xt = x + (lx - x) / lSteps; - double yt = y + (ly - y) / lSteps; - double zt = z + (lz - z) / lSteps; - - double yrd = Mth::wrapDegrees(lyr - yRot); - double xrd = Mth::wrapDegrees(lxr - xRot); - - yRot += static_cast((yrd) / lSteps); - xRot += static_cast((xrd) / lSteps); - - lSteps--; - setPos(xt, yt, zt); - setRot(yRot, xRot); - - // 4J - this collision is carried out to try and stop the lerping push the mob through the floor, - // in which case gravity can then carry on moving the mob because the collision just won't work anymore. - // BB for collision used to be calculated as: bb->shrink(1 / 32.0, 0, 1 / 32.0) - // now using a reduced BB to try and get rid of some issues where mobs pop up the sides of walls, undersides of - // trees etc. - AABB *shrinkbb = bb->shrink(0.1, 0, 0.1); - shrinkbb->y1 = shrinkbb->y0 + 0.1; - AABBList *collisions = level->getCubes(shared_from_this(), shrinkbb); - if (collisions->size() > 0) - { - double yTop = 0; - for (const auto& ab : *collisions) - { - if (ab->y1 > yTop) yTop = ab->y1; - } - - yt += yTop - bb->y0; - setPos(xt, yt, zt); - } - } - else if (!isEffectiveAi()) - { - // slow down predicted speed, to prevent mobs from sliding through - // walls etc - xd *= .98; - yd *= .98; - zd *= .98; - } - - if (abs(xd) < MIN_MOVEMENT_DISTANCE) xd = 0; - if (abs(yd) < MIN_MOVEMENT_DISTANCE) yd = 0; - if (abs(zd) < MIN_MOVEMENT_DISTANCE) zd = 0; - - if (isImmobile()) - { - jumping = false; - xxa = 0; - yya = 0; - yRotA = 0; - } - else - { - MemSect(25); - if (isEffectiveAi()) - { - if (useNewAi()) - { - newServerAiStep(); - } - else - { - serverAiStep(); - yHeadRot = yRot; - } - } - MemSect(0); - } - - if (jumping) - { - if (isInWater() || isInLava() ) - { - yd += 0.04f; - } - else if (onGround) - { - if (noJumpDelay == 0) - { - jumpFromGround(); - noJumpDelay = 10; - } - } - } - else - { - noJumpDelay = 0; - } - - - xxa *= 0.98f; - yya *= 0.98f; - yRotA *= 0.9f; - - travel(xxa, yya); - - if(!level->isClientSide) - { - pushEntities(); - } + if (noJumpDelay > 0) + { + noJumpDelay--; + } + if (lSteps > 0) + { + double xt = x + (lx - x) / lSteps; + double yt = y + (ly - y) / lSteps; + double zt = z + (lz - z) / lSteps; + + double yrd = Mth::wrapDegrees(lyr - yRot); + double xrd = Mth::wrapDegrees(lxr - xRot); + + yRot += static_cast((yrd) / lSteps); + xRot += static_cast((xrd) / lSteps); + + lSteps--; + setPos(xt, yt, zt); + setRot(yRot, xRot); + + // 4J - this collision is carried out to try and stop the lerping push the mob through the floor, + // in which case gravity can then carry on moving the mob because the collision just won't work anymore. + // BB for collision used to be calculated as: bb->shrink(1 / 32.0, 0, 1 / 32.0) + // now using a reduced BB to try and get rid of some issues where mobs pop up the sides of walls, undersides of + // trees etc. + AABB *shrinkbb = bb->shrink(0.1, 0, 0.1); + shrinkbb->y1 = shrinkbb->y0 + 0.1; + AABBList *collisions = level->getCubes(shared_from_this(), shrinkbb); + if (collisions->size() > 0) + { + double yTop = 0; + for (const auto &ab : *collisions) + { + if (ab->y1 > yTop) + { + yTop = ab->y1; + } + } + + yt += yTop - bb->y0; + setPos(xt, yt, zt); + } + } + else if (!isEffectiveAi()) + { + // slow down predicted speed, to prevent mobs from sliding through + // walls etc + xd *= .98; + yd *= .98; + zd *= .98; + } + + if (abs(xd) < MIN_MOVEMENT_DISTANCE) + { + xd = 0; + } + if (abs(yd) < MIN_MOVEMENT_DISTANCE) + { + yd = 0; + } + if (abs(zd) < MIN_MOVEMENT_DISTANCE) + { + zd = 0; + } + + if (isImmobile()) + { + jumping = false; + xxa = 0; + yya = 0; + yRotA = 0; + } + else + { + MemSect(25); + if (isEffectiveAi()) + { + if (useNewAi()) + { + newServerAiStep(); + } + else + { + serverAiStep(); + yHeadRot = yRot; + } + } + MemSect(0); + } + + if (jumping) + { + if (isInWater() || isInLava()) + { + yd += 0.04f; + } + else if (onGround) + { + if (noJumpDelay == 0) + { + jumpFromGround(); + noJumpDelay = 10; + } + } + } + else + { + noJumpDelay = 0; + } + + xxa *= 0.98f; + yya *= 0.98f; + yRotA *= 0.9f; + + travel(xxa, yya); + + if (!level->isClientSide) + { + pushEntities(); + } } void LivingEntity::newServerAiStep() @@ -1829,40 +2002,42 @@ void LivingEntity::newServerAiStep() void LivingEntity::pushEntities() { - vector > *entities = level->getEntities(shared_from_this(), this->bb->grow(0.2f, 0, 0.2f)); - if (entities != nullptr && !entities->empty()) - { - for (auto& e : *entities) - { - if ( e && e->isPushable()) - e->push(shared_from_this()); - } - } + vector> *entities = level->getEntities(shared_from_this(), this->bb->grow(0.2f, 0, 0.2f)); + if (entities != nullptr && !entities->empty()) + { + for (auto &e : *entities) + { + if (e && e->isPushable()) + { + e->push(shared_from_this()); + } + } + } } void LivingEntity::doPush(shared_ptr e) { - e->push(shared_from_this()); + e->push(shared_from_this()); } void LivingEntity::rideTick() { - Entity::rideTick(); - oRun = run; - run = 0; - fallDistance = 0; + Entity::rideTick(); + oRun = run; + run = 0; + fallDistance = 0; } void LivingEntity::lerpTo(double x, double y, double z, float yRot, float xRot, int steps) { - heightOffset = 0; - lx = x; - ly = y; - lz = z; - lyr = yRot; - lxr = xRot; + heightOffset = 0; + lx = x; + ly = y; + lz = z; + lyr = yRot; + lxr = xRot; - lSteps = steps; + lSteps = steps; } void LivingEntity::serverAiMobStep() @@ -1871,158 +2046,164 @@ void LivingEntity::serverAiMobStep() void LivingEntity::serverAiStep() { - noActionTime++; + noActionTime++; } void LivingEntity::setJumping(bool jump) { - jumping = jump; + jumping = jump; } void LivingEntity::take(shared_ptr e, int orgCount) { - if (!e->removed && !level->isClientSide) - { - EntityTracker *entityTracker = static_cast(level)->getTracker(); - if ( e->instanceof(eTYPE_ITEMENTITY) ) - { - entityTracker->broadcast(e, std::make_shared(e->entityId, entityId)); - } - else if ( e->instanceof(eTYPE_ARROW) ) - { - entityTracker->broadcast(e, std::make_shared(e->entityId, entityId)); - } - else if ( e->instanceof(eTYPE_EXPERIENCEORB) ) - { - entityTracker->broadcast(e, std::make_shared(e->entityId, entityId)); - } - } + if (!e->removed && !level->isClientSide) + { + EntityTracker *entityTracker = static_cast(level)->getTracker(); + if (e->instanceof(eTYPE_ITEMENTITY)) + { + entityTracker->broadcast(e, std::make_shared(e->entityId, entityId)); + } + else if (e->instanceof(eTYPE_ARROW)) + { + entityTracker->broadcast(e, std::make_shared(e->entityId, entityId)); + } + else if (e->instanceof(eTYPE_EXPERIENCEORB)) + { + entityTracker->broadcast(e, std::make_shared(e->entityId, entityId)); + } + } } bool LivingEntity::canSee(shared_ptr target) { - HitResult *hres = level->clip(Vec3::newTemp(x, y + getHeadHeight(), z), Vec3::newTemp(target->x, target->y + target->getHeadHeight(), target->z)); - bool retVal = (hres == nullptr); - delete hres; - return retVal; + HitResult *hres = level->clip(Vec3::newTemp(x, y + getHeadHeight(), z), Vec3::newTemp(target->x, target->y + target->getHeadHeight(), target->z)); + bool retVal = (hres == nullptr); + delete hres; + return retVal; } Vec3 *LivingEntity::getLookAngle() { - return getViewVector(1); + return getViewVector(1); } Vec3 *LivingEntity::getViewVector(float a) { - if (a == 1) - { - float yCos = Mth::cos(-yRot * Mth::RAD_TO_GRAD - PI); - float ySin = Mth::sin(-yRot * Mth::RAD_TO_GRAD - PI); - float xCos = -Mth::cos(-xRot * Mth::RAD_TO_GRAD); - float xSin = Mth::sin(-xRot * Mth::RAD_TO_GRAD); + if (a == 1) + { + float yCos = Mth::cos(-yRot * Mth::RAD_TO_GRAD - PI); + float ySin = Mth::sin(-yRot * Mth::RAD_TO_GRAD - PI); + float xCos = -Mth::cos(-xRot * Mth::RAD_TO_GRAD); + float xSin = Mth::sin(-xRot * Mth::RAD_TO_GRAD); - return Vec3::newTemp(ySin * xCos, xSin, yCos * xCos); - } - float xRot = xRotO + (this->xRot - xRotO) * a; - float yRot = yRotO + (this->yRot - yRotO) * a; + return Vec3::newTemp(ySin * xCos, xSin, yCos * xCos); + } + float xRot = xRotO + (this->xRot - xRotO) * a; + float yRot = yRotO + (this->yRot - yRotO) * a; - float yCos = Mth::cos(-yRot * Mth::RAD_TO_GRAD - PI); - float ySin = Mth::sin(-yRot * Mth::RAD_TO_GRAD - PI); - float xCos = -Mth::cos(-xRot * Mth::RAD_TO_GRAD); - float xSin = Mth::sin(-xRot * Mth::RAD_TO_GRAD); + float yCos = Mth::cos(-yRot * Mth::RAD_TO_GRAD - PI); + float ySin = Mth::sin(-yRot * Mth::RAD_TO_GRAD - PI); + float xCos = -Mth::cos(-xRot * Mth::RAD_TO_GRAD); + float xSin = Mth::sin(-xRot * Mth::RAD_TO_GRAD); - return Vec3::newTemp(ySin * xCos, xSin, yCos * xCos); + return Vec3::newTemp(ySin * xCos, xSin, yCos * xCos); } float LivingEntity::getAttackAnim(float a) { - float diff = attackAnim - oAttackAnim; - if (diff < 0) diff += 1; - return oAttackAnim + diff * a; + float diff = attackAnim - oAttackAnim; + if (diff < 0) + { + diff += 1; + } + return oAttackAnim + diff * a; } Vec3 *LivingEntity::getPos(float a) { - if (a == 1) - { - return Vec3::newTemp(x, y, z); - } - double x = xo + (this->x - xo) * a; - double y = yo + (this->y - yo) * a; - double z = zo + (this->z - zo) * a; + if (a == 1) + { + return Vec3::newTemp(x, y, z); + } + double x = xo + (this->x - xo) * a; + double y = yo + (this->y - yo) * a; + double z = zo + (this->z - zo) * a; - return Vec3::newTemp(x, y, z); + return Vec3::newTemp(x, y, z); } HitResult *LivingEntity::pick(double range, float a) { - Vec3 *from = getPos(a); - Vec3 *b = getViewVector(a); - Vec3 *to = from->add(b->x * range, b->y * range, b->z * range); - return level->clip(from, to); + Vec3 *from = getPos(a); + Vec3 *b = getViewVector(a); + Vec3 *to = from->add(b->x * range, b->y * range, b->z * range); + return level->clip(from, to); } bool LivingEntity::isEffectiveAi() { - return !level->isClientSide; + return !level->isClientSide; } bool LivingEntity::isPickable() { - return !removed; + return !removed; } bool LivingEntity::isPushable() { - return !removed; + return !removed; } float LivingEntity::getHeadHeight() { - return bbHeight * 0.85f; + return bbHeight * 0.85f; } void LivingEntity::markHurt() { - hurtMarked = random->nextDouble() >= getAttribute(SharedMonsterAttributes::KNOCKBACK_RESISTANCE)->getValue(); + hurtMarked = random->nextDouble() >= getAttribute(SharedMonsterAttributes::KNOCKBACK_RESISTANCE)->getValue(); } float LivingEntity::getYHeadRot() { - return yHeadRot; + return yHeadRot; } void LivingEntity::setYHeadRot(float yHeadRot) { - this->yHeadRot = yHeadRot; + this->yHeadRot = yHeadRot; } float LivingEntity::getAbsorptionAmount() { - return absorptionAmount; + return absorptionAmount; } void LivingEntity::setAbsorptionAmount(float absorptionAmount) { - if (absorptionAmount < 0) absorptionAmount = 0; - this->absorptionAmount = absorptionAmount; + if (absorptionAmount < 0) + { + absorptionAmount = 0; + } + this->absorptionAmount = absorptionAmount; } Team *LivingEntity::getTeam() { - return nullptr; + return nullptr; } bool LivingEntity::isAlliedTo(shared_ptr other) { - return isAlliedTo(other->getTeam()); + return isAlliedTo(other->getTeam()); } bool LivingEntity::isAlliedTo(Team *other) { - if (getTeam() != nullptr) - { - return getTeam()->isAlliedTo(other); - } - return false; + if (getTeam() != nullptr) + { + return getTeam()->isAlliedTo(other); + } + return false; } diff --git a/Minecraft.World/TrapDoorTile.cpp b/Minecraft.World/TrapDoorTile.cpp index 69ccfd5119..abd2925615 100644 --- a/Minecraft.World/TrapDoorTile.cpp +++ b/Minecraft.World/TrapDoorTile.cpp @@ -1,215 +1,299 @@ -#include "stdafx.h" -#include "net.minecraft.world.level.h" +#include "TrapDoorTile.h" +#include "net.minecraft.h" #include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.level.h" #include "net.minecraft.world.level.tile.h" -#include "net.minecraft.h" -#include "TrapDoorTile.h" +#include "stdafx.h" -TrapDoorTile::TrapDoorTile(int id, Material *material) : Tile(id, material,isSolidRender()) +TrapDoorTile::TrapDoorTile(int id, Material *material) : Tile(id, material, isSolidRender()) { - float r = 0.5f; - float h = 1.0f; - setShape(0.5f - r, 0, 0.5f - r, 0.5f + r, h, 0.5f + r); + float r = 0.5f; + float h = 1.0f; + setShape(0.5f - r, 0, 0.5f - r, 0.5f + r, h, 0.5f + r); } bool TrapDoorTile::blocksLight() { - return false; + return false; } - bool TrapDoorTile::isSolidRender(bool isServerLevel) { - return false; + return false; } - bool TrapDoorTile::isCubeShaped() { - return false; + return false; } bool TrapDoorTile::isPathfindable(LevelSource *level, int x, int y, int z) { - return !isOpen(level->getData(x, y, z)); + return !isOpen(level->getData(x, y, z)); } int TrapDoorTile::getRenderShape() { - return Tile::SHAPE_BLOCK; + return Tile::SHAPE_BLOCK; } - AABB *TrapDoorTile::getTileAABB(Level *level, int x, int y, int z) { - updateShape(level, x, y, z); - return Tile::getTileAABB(level, x, y, z); + updateShape(level, x, y, z); + return Tile::getTileAABB(level, x, y, z); } - AABB *TrapDoorTile::getAABB(Level *level, int x, int y, int z) { - updateShape(level, x, y, z); - return Tile::getAABB(level, x, y, z); + updateShape(level, x, y, z); + return Tile::getAABB(level, x, y, z); } - void TrapDoorTile::updateShape(LevelSource *level, int x, int y, int z, int forceData, shared_ptr forceEntity) // 4J added forceData, forceEntity param { - setShape(level->getData(x, y, z)); + setShape(level->getData(x, y, z)); } - void TrapDoorTile::updateDefaultShape() { - float r = 3 / 16.0f; - setShape(0, 0.5f - r / 2, 0, 1, 0.5f + r / 2, 1); + float r = 3 / 16.0f; + setShape(0, 0.5f - r / 2, 0, 1, 0.5f + r / 2, 1); } - void TrapDoorTile::setShape(int data) { - float r = 3 / 16.0f; - if ((data & TOP_MASK) != 0) - { - setShape(0, 1 - r, 0, 1, 1, 1); - } - else - { - setShape(0, 0, 0, 1, r, 1); - } - if (isOpen(data)) - { - if ((data & 3) == 0) setShape(0, 0, 1 - r, 1, 1, 1); - if ((data & 3) == 1) setShape(0, 0, 0, 1, 1, r); - if ((data & 3) == 2) setShape(1 - r, 0, 0, 1, 1, 1); - if ((data & 3) == 3) setShape(0, 0, 0, r, 1, 1); - } + float r = 3 / 16.0f; + if ((data & TOP_MASK) != 0) + { + setShape(0, 1 - r, 0, 1, 1, 1); + } + else + { + setShape(0, 0, 0, 1, r, 1); + } + if (isOpen(data)) + { + if ((data & 3) == 0) + { + setShape(0, 0, 1 - r, 1, 1, 1); + } + if ((data & 3) == 1) + { + setShape(0, 0, 0, 1, 1, r); + } + if ((data & 3) == 2) + { + setShape(1 - r, 0, 0, 1, 1, 1); + } + if ((data & 3) == 3) + { + setShape(0, 0, 0, r, 1, 1); + } + } } - void TrapDoorTile::attack(Level *level, int x, int y, int z, shared_ptr player) { - //use(level, x, y, z, player, 0, 0, 0, 0); + // use(level, x, y, z, player, 0, 0, 0, 0); } // 4J-PB - Adding a TestUse for tooltip display bool TrapDoorTile::TestUse() { - return true; + return true; } -bool TrapDoorTile::use(Level *level, int x, int y, int z, shared_ptr player, int clickedFace, float clickX, float clickY, float clickZ, bool soundOnly/*=false*/) // 4J added soundOnly param +bool TrapDoorTile::use(Level *level, int x, int y, int z, shared_ptr player, int clickedFace, float clickX, float clickY, float clickZ, bool soundOnly /*=false*/) // 4J added soundOnly param { - if (material == Material::metal) return true; - - if (soundOnly) - { - // 4J - added - just do enough to play the sound - level->levelEvent(player, LevelEvent::SOUND_OPEN_DOOR, x, y, z, 0); - return false; - } - - int dir = level->getData(x, y, z); - level->setData(x, y, z, dir ^ 4, Tile::UPDATE_CLIENTS); - - level->levelEvent(player, LevelEvent::SOUND_OPEN_DOOR, x, y, z, 0); - return true; + if (material == Material::metal) + { + return true; + } + + if (soundOnly) + { + // 4J - added - just do enough to play the sound + level->levelEvent(player, LevelEvent::SOUND_OPEN_DOOR, x, y, z, 0); + return false; + } + + int dir = level->getData(x, y, z); + level->setData(x, y, z, dir ^ 4, Tile::UPDATE_CLIENTS); + + level->levelEvent(player, LevelEvent::SOUND_OPEN_DOOR, x, y, z, 0); + return true; } - void TrapDoorTile::setOpen(Level *level, int x, int y, int z, bool shouldOpen) { - int dir = level->getData(x, y, z); + int dir = level->getData(x, y, z); - bool wasOpen = (dir & 4) > 0; - if (wasOpen == shouldOpen) return; + bool wasOpen = (dir & 4) > 0; + if (wasOpen == shouldOpen) + { + return; + } - level->setData(x, y, z, dir ^ 4, Tile::UPDATE_CLIENTS); + level->setData(x, y, z, dir ^ 4, Tile::UPDATE_CLIENTS); - level->levelEvent(nullptr, LevelEvent::SOUND_OPEN_DOOR, x, y, z, 0); + level->levelEvent(nullptr, LevelEvent::SOUND_OPEN_DOOR, x, y, z, 0); } void TrapDoorTile::neighborChanged(Level *level, int x, int y, int z, int type) { - if (level->isClientSide) return; - - int data = level->getData(x, y, z); - int xt = x; - int zt = z; - if ((data & 3) == 0) zt++; - if ((data & 3) == 1) zt--; - if ((data & 3) == 2) xt++; - if ((data & 3) == 3) xt--; - - - if (!attachesTo(level->getTile(xt, y, zt))) - { - level->removeTile(x, y, z); - spawnResources(level, x, y, z, data, 0); - } - - bool signal = level->hasNeighborSignal(x, y, z); - if( signal || ((type > 0 && Tile::tiles[type]->isSignalSource())) ) - { - setOpen(level, x, y, z, signal); - } + if (level->isClientSide) + { + return; + } + + int data = level->getData(x, y, z); + int xt = x; + int zt = z; + if ((data & 3) == 0) + { + zt++; + } + if ((data & 3) == 1) + { + zt--; + } + if ((data & 3) == 2) + { + xt++; + } + if ((data & 3) == 3) + { + xt--; + } + + // if (!attachesTo(level->getTile(xt, y, zt))) + // { + // level->removeTile(x, y, z); + // spawnResources(level, x, y, z, data, 0); + // } + + bool signal = level->hasNeighborSignal(x, y, z); + if (signal || ((type > 0 && Tile::tiles[type]->isSignalSource()))) + { + setOpen(level, x, y, z, signal); + } } HitResult *TrapDoorTile::clip(Level *level, int xt, int yt, int zt, Vec3 *a, Vec3 *b) { - updateShape(level, xt, yt, zt); - return Tile::clip(level, xt, yt, zt, a, b); + updateShape(level, xt, yt, zt); + return Tile::clip(level, xt, yt, zt, a, b); } int TrapDoorTile::getDir(int dir) { - if ((dir & 4) == 0) - { - return ((dir - 1) & 3); - } - else - { - return (dir & 3); - } + if ((dir & 4) == 0) + { + return ((dir - 1) & 3); + } + else + { + return (dir & 3); + } } int TrapDoorTile::getPlacedOnFaceDataValue(Level *level, int x, int y, int z, int face, float clickX, float clickY, float clickZ, int itemValue) { - int dir = 0; - if (face == 2) dir = 0; - if (face == 3) dir = 1; - if (face == 4) dir = 2; - if (face == 5) dir = 3; - if (face != Facing::UP && face != Facing::DOWN && clickY > 0.5f) dir |= TOP_MASK; - return dir; + int dir = 0; + + if (face == Facing::NORTH) + { + dir = 0; + } + if (face == Facing::SOUTH) + { + dir = 1; + } + if (face == Facing::WEST) + { + dir = 2; + } + if (face == Facing::EAST) + { + dir = 3; + } + + if (face == Facing::DOWN) + { + dir |= TOP_MASK; + } + else if (face == Facing::UP) + { + dir |= 0; + } + else + { + dir |= (clickY > 0.5f ? TOP_MASK : 0); + } + + return dir; } -bool TrapDoorTile::mayPlace(Level *level, int x, int y, int z, int face) +void TrapDoorTile::setPlacedBy(Level *level, int x, int y, int z, shared_ptr by, shared_ptr itemInstance) { - if (face == 0) return false; - if (face == 1) return false; - if (face == 2) z++; - if (face == 3) z--; - if (face == 4) x++; - if (face == 5) x--; + int data = level->getData(x, y, z); + int dir = data & 7; + int flip = data & TOP_MASK; + + // We only need to apply player yaw rotation when the trapdoor is placed + // on floor or ceiling (i.e. when it would have been face UP or DOWN). + // Because its allowed placement without support, we detect this case when the initial + // direction is still the default (0) and it's not a clear wall placement. + + if (dir == 0) + { + int i = Mth::floor((by->yRot * 4.0f / 360.0f) + 0.5f) & 0x3; + if (i == 0) + { + dir = 0; + } + if (i == 1) + { + dir = 3; + } + if (i == 2) + { + dir = 1; + } + if (i == 3) + { + dir = 2; + } + level->setData(x, y, z, dir | flip, Tile::UPDATE_CLIENTS); + } +} - return attachesTo(level->getTile(x, y, z)); +bool TrapDoorTile::mayPlace(Level *level, int x, int y, int z, int face) +{ + // if (face == 0) return false; + // if (face == 1) return false; + // if (face == 2) z++; + // if (face == 3) z--; + // if (face == 4) x++; + // if (face == 5) x--; + // + // return attachesTo(level->getTile(x, y, z)); + return true; } bool TrapDoorTile::isOpen(int data) { - return (data & 4) != 0; + return (data & 4) != 0; } bool TrapDoorTile::attachesTo(int id) { - if (id <= 0) - { - return false; - } - Tile *tile = Tile::tiles[id]; + if (id <= 0) + { + return false; + } + Tile *tile = Tile::tiles[id]; - return tile != nullptr && (tile->material->isSolidBlocking() && tile->isCubeShaped()) || tile == Tile::glowstone || (dynamic_cast(tile) != nullptr) || (dynamic_cast(tile) != nullptr); -} \ No newline at end of file + return tile != nullptr && (tile->material->isSolidBlocking() && tile->isCubeShaped()) || tile == Tile::glowstone || (dynamic_cast(tile) != nullptr) || (dynamic_cast(tile) != nullptr); +} diff --git a/Minecraft.World/TrapDoorTile.h b/Minecraft.World/TrapDoorTile.h index 19cec15057..cc4071237f 100644 --- a/Minecraft.World/TrapDoorTile.h +++ b/Minecraft.World/TrapDoorTile.h @@ -7,81 +7,81 @@ class HitResult; class TrapDoorTile : public Tile { - friend class Tile; -private: - static const int TOP_MASK = 0x8; + friend class Tile; -protected: - TrapDoorTile(int id, Material *material); + private: + static const int TOP_MASK = 0x8; -/* - * public int getTexture(int face, int data) { if (face == 0 || face == 1) - * return tex; int dir = getDir(data); if ((dir == 0 || dir == 2) ^ (face <= 3)) - * { return tex; } int tt = (dir / 2 + ((face & 1) ^ dir)); tt += ((data & 4) / - * 4); int texture = tex - (data & 8) * 2; if ((tt & 1) != 0) { texture = - * -texture; } // if (getDir(data)==0 // tt-=((face+data&3)&1)^((data&4)>>2); - * return texture; } - */ + protected: + TrapDoorTile(int id, Material *material); -public: - bool blocksLight(); + /* + * public int getTexture(int face, int data) { if (face == 0 || face == 1) + * return tex; int dir = getDir(data); if ((dir == 0 || dir == 2) ^ (face <= 3)) + * { return tex; } int tt = (dir / 2 + ((face & 1) ^ dir)); tt += ((data & 4) / + * 4); int texture = tex - (data & 8) * 2; if ((tt & 1) != 0) { texture = + * -texture; } // if (getDir(data)==0 // tt-=((face+data&3)&1)^((data&4)>>2); + * return texture; } + */ -public: - bool isSolidRender(bool isServerLevel = false); + public: + bool blocksLight(); -public: - bool isCubeShaped(); - bool isPathfindable(LevelSource *level, int x, int y, int z); + public: + bool isSolidRender(bool isServerLevel = false); -public: - int getRenderShape(); + public: + bool isCubeShaped(); + bool isPathfindable(LevelSource *level, int x, int y, int z); -public: - AABB *getTileAABB(Level *level, int x, int y, int z); + public: + int getRenderShape(); -public: - AABB *getAABB(Level *level, int x, int y, int z); + public: + AABB *getTileAABB(Level *level, int x, int y, int z); -public: - void updateShape(LevelSource *level, int x, int y, int z, int forceData = -1, shared_ptr forceEntity = shared_ptr()); // 4J added forceData, forceEntity param + public: + AABB *getAABB(Level *level, int x, int y, int z); -public: - void updateDefaultShape(); + public: + void updateShape(LevelSource *level, int x, int y, int z, int forceData = -1, shared_ptr forceEntity = shared_ptr()); // 4J added forceData, forceEntity param -public: - using Tile::setShape; - void setShape(int data); + public: + void updateDefaultShape(); -public: - void attack(Level *level, int x, int y, int z, shared_ptr player); + public: + using Tile::setShape; + void setShape(int data); -public: - virtual bool TestUse(); - bool use(Level *level, int x, int y, int z, shared_ptr player, int clickedFace, float clickX, float clickY, float clickZ, bool soundOnly = false); // 4J added soundOnly param + public: + void attack(Level *level, int x, int y, int z, shared_ptr player); -public: - void setOpen(Level *level, int x, int y, int z, bool shouldOpen); + public: + virtual bool TestUse(); + bool use(Level *level, int x, int y, int z, shared_ptr player, int clickedFace, float clickX, float clickY, float clickZ, bool soundOnly = false); // 4J added soundOnly param + public: + void setOpen(Level *level, int x, int y, int z, bool shouldOpen); -public: - void neighborChanged(Level *level, int x, int y, int z, int type); + public: + void neighborChanged(Level *level, int x, int y, int z, int type); + public: + HitResult *clip(Level *level, int xt, int yt, int zt, Vec3 *a, Vec3 *b); -public: - HitResult *clip(Level *level, int xt, int yt, int zt, Vec3 *a, Vec3 *b); + public: + int getDir(int dir); -public: - int getDir(int dir); + public: + int getPlacedOnFaceDataValue(Level *level, int x, int y, int z, int face, float clickX, float clickY, float clickZ, int itemValue); + virtual void setPlacedBy(Level *level, int x, int y, int z, shared_ptr by, shared_ptr itemInstance); -public: - int getPlacedOnFaceDataValue(Level *level, int x, int y, int z, int face, float clickX, float clickY, float clickZ, int itemValue); + public: + bool mayPlace(Level *level, int x, int y, int z, int face); -public: - bool mayPlace(Level *level, int x, int y, int z, int face); + public: + static bool isOpen(int data); -public: - static bool isOpen(int data); - -private: - static bool attachesTo(int id); -}; \ No newline at end of file + private: + static bool attachesTo(int id); +};