/********************************************************************
	Minecraft: Pocket Edition - Decompilation Project
	Copyright (C) 2023 iProgramInCpp

	The following code is licensed under the BSD 1 clause license.
	SPDX-License-Identifier: BSD-1-Clause
 ********************************************************************/

#include "Entity.hpp"
#include "Player.hpp"
#include "world/level/Level.hpp"

int Entity::entityCounter;
Random Entity::sharedRandom;

void Entity::_init()
{
	m_bInAChunk = false;
	m_chunkX = 0;
	m_chunkY = 0;
	m_chunkZ = 0;
	field_20 = 0;
	field_24 = 0;
	field_28 = 0;
	field_30 = 1.0f;
	field_34 = 0;
	m_pLevel = nullptr;
	m_yaw = 0.0f;
	m_pitch = 0.0f;
	field_5C = 0.0f;
	field_60 = 0.0f;
	field_7C = false;
	field_7D = false;
	field_7E = false;
	field_7F = false;
	m_bHurt = false;
	field_81 = 1;
	m_bRemoved = false;
	field_84 = 0.0f;
	field_88 = 0.6f;
	field_8C = 1.8f;
	field_90 = 0.0f;
	field_94 = 0.0f;
	field_A4 = 0.0f;
	field_A8 = 0.0f;
	m_bNoCollision = false;
	field_B0 = 0.0f;
	field_B4 = 0;
	field_B8 = 0;
	field_BC = 300;
	field_C0 = 0;
	field_C8 = 0;  // @NOTE: Render type? (eEntityRenderType)
	m_distanceFallen = 0.0f;
	field_D0 = 300;
	field_D4 = 0;
	field_D5 = false;
	field_D6 = true;
	field_D8 = 1;
}

Entity::Entity(Level* pLevel)
{
	_init();

	m_pLevel = pLevel;
	m_EntityID = ++entityCounter;
	setPos(0, 0, 0);
}

Entity::~Entity()
{
}

void Entity::setLevel(Level* pLvl)
{
	m_pLevel = pLvl;
}

void Entity::removed()
{
	m_bRemoved = true;
}

void Entity::setPos(float x, float y, float z)
{
	m_pos = Vec3(x, y, z);

	float halfSize = field_88 / 2;
	float lowY = y - field_84 + field_A4;

	m_hitbox = AABB(
		x - halfSize,
		lowY,
		z - halfSize,
		x + halfSize,
		lowY + field_8C,
		z + halfSize);
}

void Entity::remove()
{
	m_bRemoved = true;
}

int Entity::move(float x, float y, float z)
{
	float x_1 = x, z_1 = z;

	if (m_bNoCollision)
	{
		// just move it. Don't perform any kind of collision
		m_hitbox.min.x += x;
		m_hitbox.max.x += x;
		m_hitbox.min.y += y;
		m_hitbox.max.y += y;
		m_hitbox.min.z += z;
		m_hitbox.max.z += z;

		m_pos.x = (m_hitbox.max.x + m_hitbox.min.x) / 2;
		m_pos.z = (m_hitbox.max.z + m_hitbox.min.z) / 2;
		m_pos.y = m_hitbox.min.y + field_84 - field_A4;

		return 1300;
	}

	//@TODO: untangle the control flow

	float x1, x2, x3, x4, x5, x6;
	float x7, x8, x9, x10, x11, x12, x20;
	float x_2, z_2, x_3, z_3;
	float oldX, oldZ;
	bool b1, b2, b3, b4, b5, b6;
	AABB hitold;

    oldX = m_pos.x; oldZ = m_pos.z;

	x7 = m_hitbox.max.z;
	x8 = m_hitbox.max.y;
	x9 = m_hitbox.max.x;
	x10 = m_hitbox.min.z;
	x11 = m_hitbox.min.y;
	x12 = m_hitbox.min.x;

	x1 = m_hitbox.max.z;
	x2 = m_hitbox.max.y;
	x3 = m_hitbox.max.x;
	x4 = m_hitbox.min.z;
	x5 = m_hitbox.min.y;
	x6 = m_hitbox.min.x;

	if (!field_7C)
	{
	label_4:

		z_2 = z_1;
		b1 = x_1 < 0.0f;
		b2 = x_1 > 0.0f;
		x_2 = x_1;
		b3 = z_1 < 0.0f;
		b4 = z_1 > 0.0f;
		b5 = false;
		goto label_5;
	}

	if (!isSneaking())
		goto label_4;

	if (x_1 == 0.0f)
	{
		x_2 = x_1;
		b1 = x_1 < 0.0f;
		b2 = x_1 > 0.0f;
	}
	else
	{
		x_2 = x_1;
		do
		{
			AABB aabb = m_hitbox;
			aabb.move(x_1, -1.0f, 0);

			AABBVector* cubes = m_pLevel->getCubes(this, aabb);

			if (cubes->size())
				break;

			if (x_1 < 0.05f && x_1 >= -0.05f)
			{
				x_2 = 0.0f;
				x_1 = 0.0f;
				break;
			}

			// @BUG: See the z_1 part
			if (x_1 <= 0.0f)
				x_1 = x_1 + 0.05f;
			else
				x_1 = x_1 - 0.05f;

			x_2 = x_1;
		} while (x_1 != 0.0f);

		b1 = x_1 < 0.0f;
		b2 = x_1 > 0.0f;
	}

	if (z_1 == 0.0f)
	{
		z_2 = z_1;
		b3 = z_1 < 0.0f;
		b4 = z_1 > 0.0f;
	}
	else
	{
		z_2 = z_1;
		do
		{
			AABB aabb = m_hitbox;
			aabb.move(0, -1.0f, z_1);

			AABBVector* cubes = m_pLevel->getCubes(this, aabb);

			if (cubes->size())
				break;

			if (z_1 < 0.05f && z_1 >= -0.05f)
			{
				z_2 = 0.0f;
				z_1 = 0.0f;
				break;
			}

			//@BUG: wouldn't this loop forever? Since if z_1 == 0.025f, it'd oscillate between -0.025f and +0.025f...
			if (z_1 <= 0.0f)
				z_1 = z_1 + 0.05f;
			else
				z_1 = z_1 - 0.05f;
		} while (z_1 != 0.0f);
		b3 = z_1 < 0.0f;
		b4 = z_1 > 0.0f;
	}

	b5 = true;

label_5:
	if (b1) x6 += x_1;
	if (b2) x3 += x_1;
	if (y < 0.0f) x5 += y;
	if (y > 0.0f) x2 += y;
	if (b3) x4 += z;
	if (b4) x1 += z;

	AABB scanAABB(x6, x5, x4, x3, x2, x1);
	AABBVector* pCubes = m_pLevel->getCubes(this, scanAABB);

	float newY = y;
	for (int i = 0; i < pCubes->size(); i++)
	{
		const AABB& aabb = pCubes->at(i);
		newY = aabb.clipYCollide(m_hitbox, newY);
	}

	m_hitbox.move(0, newY, 0);

	if (!field_81 && newY != y)
	{
		z_1 = 0.0f;
		x_1 = 0.0f;
		newY = 0.0f;
	}

	if (field_7C)
	{
		b6 = true;
	}
	else
	{
		b6 = y < 0.0f;
		if (newY == y)
			b6 = 0;
	}

	for (int i = 0; i < pCubes->size(); i++)
	{
		const AABB& aabb = pCubes->at(i);
		x_1 = aabb.clipXCollide(m_hitbox, x_1);
	}

	m_hitbox.move(x_1, 0, 0);

	if (!field_81 && x_1 != x_2)
	{
		z_1 = 0.0f;
		x_1 = 0.0f;
		newY = 0.0f;
	}

	for (int i = 0; i < pCubes->size(); i++)
	{
		const AABB& aabb = pCubes->at(i);
		z_1 = aabb.clipZCollide(m_hitbox, z_1);
	}

	m_hitbox.move(0, 0, z_1);

	if (!field_81 && z_1 != z_2)
	{
		z_1 = 0.0f;
		x_1 = 0.0f;
		newY = 0.0f;
	}

	x20 = field_A8;
	if (x20 <= 0.0f || !b6)
	{
	label_44:
		z_3 = z_1;
		x_3 = x_1;
		goto label_45;
	}

    if (field_A4 >= 0.05f || (x_2 == x_1 && z_2 == z_1))
		goto label_44;

	// oh come on, undoing all our work??
	hitold = m_hitbox;
	m_hitbox = AABB(x12, x11, x10, x9, x8, x7);

	if (b1) x12 += x_2;
	if (b2) x9 += x_2;
	x8 += x20; //@BUG: missing if (x20 > 0) check?
	if (x20 < 0.0f) x11 += x20;
	if (b3) x10 += z_2;
	if (b4) x7 += z_2;

	{
		AABB scanAABB2(x12, x11, x10, x9, x8, x7);

		//@BUG: useless copy
		//@BUG: this doesn't actually copy anything
		*pCubes = *m_pLevel->getCubes(this, scanAABB2);
	}

	for (int i = 0; i < pCubes->size(); i++)
	{
		const AABB& aabb = pCubes->at(i);
		x20 = aabb.clipYCollide(m_hitbox, x20);
	}

	m_hitbox.move(0, x20, 0);

	if (field_81 || x20 == y)
	{
		z_3 = z_2;
		x_3 = x_2;
	}
	else
	{
		x20 = 0.0f;
		z_3 = 0.0f;
		x_3 = 0.0f;
	}

	for (int i = 0; i < pCubes->size(); i++)
	{
		const AABB& aabb = pCubes->at(i);
		x_3 = aabb.clipXCollide(m_hitbox, x_3);
	}

	m_hitbox.move(x_3, 0, 0);

	if (!field_81 && x_2 != x_3)
	{
		x20 = 0.0f;
		z_3 = 0.0f;
		x_3 = 0.0f;
	}

	for (int i = 0; i < pCubes->size(); i++)
	{
		const AABB& aabb = pCubes->at(i);
		z_3 = aabb.clipZCollide(m_hitbox, z_3);
	}

	m_hitbox.move(0, 0, z_3);

	if (!field_81 && z_2 != z_3)
	{
		x20 = 0.0f;
		z_3 = 0.0f;
		x_3 = 0.0f;
	}

	// if we moved more this time than before?? no clue wtf this is about...
	if (z_1 * z_1 + x_1 * x_1 < z_3 * z_3 + x_3 * x_3)
	{
		field_A4 += 0.5f;
		newY = x20;
	}
	else
	{
		// don't get the rationale behind this at all...
		m_hitbox = hitold;
		z_3 = z_1;
		x_3 = x_1;
	}

label_45:
	m_pos.x = (m_hitbox.min.x + m_hitbox.max.x) / 2;
	m_pos.z = (m_hitbox.min.z + m_hitbox.max.z) / 2;
	m_pos.y = m_hitbox.min.y - field_A4 + field_84;

	field_7D = x_2 != x_3 || z_2 != z_3;
	field_7F = field_7D || newY != y;
	field_7C = y < 0.0f && newY != y;
	field_7E = newY != y;

	checkFallDamage(newY, field_7C);

	if (x_2 != x_3) m_vel.x = 0.0;
	if (newY != y)  m_vel.y = 0.0;
	if (z_2 != z_3) m_vel.z = 0.0;

	if (!b5)
	{
		float diffX = (m_pos.x - oldX), diffZ = (m_pos.z - oldZ);
		field_94 += Mth::sqrt(diffZ * diffZ + diffX * diffX) * 0.6f;

		int tileX = Mth::floor(m_pos.x);
		int tileY = Mth::floor(m_pos.y - 0.2f - field_84);
		int tileZ = Mth::floor(m_pos.z);

		TileID tileID = m_pLevel->getTile(tileX, tileY, tileZ);

		if (tileID > 0 && field_94 > float(field_D8))
		{
			++field_D8;
			bool bPlaySound = true;

			const Tile::SoundType *sound = Tile::tiles[tileID]->m_pSound;
			if (m_pLevel->getTile(tileX, tileY + 1, tileZ) == Tile::topSnow->m_ID)
				sound = Tile::topSnow->m_pSound;
			else if (Tile::tiles[tileID]->m_pMaterial->isLiquid())
				bPlaySound = false;

			if (bPlaySound)
				m_pLevel->playSound(this, "step." + sound->m_name, sound->field_18 * 0.15f, sound->field_1C);

			Tile::tiles[tileID]->stepOn(m_pLevel, tileX, tileY, tileZ, this);
		}
	}

	// Check whether the entity is inside of any tiles.

	int minX = Mth::floor(m_hitbox.min.x), minY = Mth::floor(m_hitbox.min.y), minZ = Mth::floor(m_hitbox.min.z),
		maxX = Mth::floor(m_hitbox.max.x), maxY = Mth::floor(m_hitbox.max.y), maxZ = Mth::floor(m_hitbox.max.z);

	if (m_pLevel->hasChunksAt(minX, minY, minZ, maxX, maxY, maxZ))
	{
		for (int x = minX; x <= maxX; x++)
			for (int y = minY; y <= maxY; y++)
				for (int z = minZ; z <= maxZ; z++)
				{
					TileID tileID = m_pLevel->getTile(x, y, z);
					if (tileID > 0)
						Tile::tiles[tileID]->entityInside(m_pLevel, x, y, z, this);
				}
	}

	field_A4 *= 0.4f;

	bool bIsInWater = isInWater();

	if (m_pLevel->containsFireTile(m_hitbox))
	{
		burn(true);

		if (bIsInWater)
		{
		label_76:
			if (field_C0 > 0)
				field_C0 = -field_C4;

			return 1300;
		}

		field_C0++;
		if (field_C0 == 0)
			field_C0 = 300;
	}
	else
	{
		if (field_C0 <= 0)
			field_C0 = -field_C4;

		if (bIsInWater)
			goto label_76;
	}

	return 1300;
}

void Entity::moveTo(float x, float y, float z, float yaw, float pitch)
{
	y += field_84;

	m_pos.x = x;
	m_yaw = yaw;
	field_3C.x = x;
	field_98.x = x;

	m_pos.z = z;
	m_pitch = pitch;
	field_3C.z = z;
	field_98.z = z;

	m_pos.y = y;
	field_3C.y = y;
	field_98.y = y;

	setPos(x, y, z);
}

void Entity::absMoveTo(float x, float y, float z, float yaw, float pitch)
{
	field_A4 = 0.0f;

	field_5C = yaw;
	m_yaw = yaw;
	m_pitch = pitch;
	field_60 = pitch;

	m_pos.x = x;
	field_3C.x = x;
	m_pos.y = y;
	field_3C.y = y;
	m_pos.z = z;
	field_3C.z = z;

	//@WHAT: (yaw - yaw) == 0
	if (yaw - yaw < -180.0f)
		field_5C = yaw + 360.0f;
	if (yaw - yaw >= 180.0f)
		field_5C = field_5C - 360.0f;

	setPos(this->m_pos.x, this->m_pos.y, this->m_pos.z);
	return setRot(yaw, pitch);
}

void Entity::moveRelative(float x, float z, float y)
{
	float d = sqrtf(x * x + z * z);
	if (d < 0.01f) return;
	if (d < 1.0f)
		d = 1.0f;

	y /= d;
	x *= y;
	z *= y;

	float yaw = m_yaw * float(M_PI);
	float syaw = sinf(yaw / 180.0f);
	float cyaw = cosf(yaw / 180.0f);

	m_vel.x += x * cyaw - z * syaw;
	m_vel.z += x * syaw + z * cyaw;
}

void Entity::lerpTo(float x, float y, float z, float yaw, float pitch, int p)
{
	setPos(x, y, z);
	setRot(yaw, pitch);
}

void Entity::lerpMotion(float x, float y, float z)
{
	m_vel = Vec3(x, y, z);
}

void Entity::turn(float yaw, float pitch)
{
	float y_old = m_yaw;
	float p_old = m_pitch;

	m_yaw += yaw * 0.15f;
	m_pitch -= pitch * 0.15f;

	// can't rotate more than facing fully up or fully down
	if (m_pitch < -90.0f)
		m_pitch = -90.0f;
	if (m_pitch > 90.0f)
		m_pitch = 90.0f;

	field_5C += m_yaw - y_old;
	field_60 += m_pitch - p_old;
}

void Entity::reset()
{
	// TODO is this it
	field_98 = field_3C = m_pos;
	field_5C = m_yaw;
	field_60 = m_pitch;
	m_bRemoved = false;
	m_distanceFallen = 0.0f;
	field_D5 = false;
	field_C0 = 0;
}

void Entity::interpolateTurn(float yaw, float pitch)
{
	m_yaw += yaw * 0.15f;
	m_pitch -= pitch * 0.15f;

	// can't rotate more than facing fully up or fully down
	if (m_pitch < -90.0f)
		m_pitch = -90.0f;
	if (m_pitch > 90.0f)
		m_pitch = 90.0f;
}

void Entity::tick()
{
	baseTick();
}

void Entity::baseTick()
{
	//@TODO: untangle the gotos

	field_90 = field_94;
	field_3C = m_pos;
	field_B4++;
	field_5C = m_yaw;
	field_60 = m_pitch;
	if (isInWater())
	{
		if (!field_D4 && !field_D6)
		{
			float dist = sqrtf(m_vel.y * m_vel.y + m_vel.x * m_vel.x * 0.2f + m_vel.z * m_vel.z * 0.2f) * 0.2f;
			if (dist > 1.0f)
				dist = 1.0f;

			m_pLevel->playSound(this, "random.splash", dist, 1.0f + 0.4f * (sharedRandom.nextFloat() - sharedRandom.nextFloat()));

			float f1 = floorf(m_hitbox.min.y);

			for (int i = 0; float(i) < field_88 * 20.0f + 1.0f; i++)
			{
				m_pLevel->addParticle(
					"bubble",
					m_pos.x + field_88 * (sharedRandom.nextFloat() * 2.0f - 1.0f),
					f1 + 1.0f,
					m_pos.z + field_88 * (sharedRandom.nextFloat() * 2.0f - 1.0f),
					m_vel.x,
					m_vel.y - 0.2f * sharedRandom.nextFloat(),
					m_vel.z
				);
			}

			for (int i = 0; float(i) < field_88 * 20.0f + 1.0f; i++)
			{
				m_pLevel->addParticle(
					"splash",
					m_pos.x + field_88 * (sharedRandom.nextFloat() * 2.0f - 1.0f),
					f1 + 1.0f,
					m_pos.z + field_88 * (sharedRandom.nextFloat() * 2.0f - 1.0f),
					m_vel.x,
					m_vel.y - 0.2f * sharedRandom.nextFloat(),
					m_vel.z
				);
			}
		}

		field_D4 = true;
		field_C0 = 0;
		m_distanceFallen = 0;

		if (m_pLevel->m_bIsMultiplayer)
			goto label_4;
	}
	else
	{
		field_D4 = false;

		if (m_pLevel->m_bIsMultiplayer)
		{
		label_4:
			field_C0 = 0;
			goto label_5;
		}
	}

	if (field_C0 <= 0)
	{
	label_5:
		if (!isInLava())
			goto label_6;
		goto label_15;
	}

	if (field_D5)
	{
		field_C0 -= 4;
		if (field_C0 < 0)
			field_C0 = 0;
		goto label_5;
	}

	if (field_C0 % 20 == 0)
	{
		hurt(nullptr, 1);
	}

	field_C0--;

	if (isInLava())
	{
	label_15:
		lavaHurt();
	}

label_6:
	if (m_pos.y < -64.0f)
		outOfWorld();

	field_D6 = false;
}

bool Entity::intersects(float minX, float minY, float minZ, float maxX, float maxY, float maxZ)
{
	AABB aabb(minX, minY, minZ, maxX, maxY, maxZ);
	return aabb.intersect(m_hitbox);
}

bool Entity::isFree(float offX, float offY, float offZ)
{
	AABB aabb = m_hitbox;
	aabb.move(offX, offY, offZ);

	AABBVector* pCubes = m_pLevel->getCubes(this, aabb);
	if (!pCubes)
		return false;

	return !m_pLevel->containsAnyLiquid(aabb);
}

bool Entity::isFree(float offX, float offY, float offZ, float expand)
{
	AABB aabb = m_hitbox;
	aabb.move(offX, offY, offZ);
	aabb.grow(expand, expand, expand);

	AABBVector* pCubes = m_pLevel->getCubes(this, aabb);
	if (!pCubes)
		return false;

	return !m_pLevel->containsAnyLiquid(aabb);
}

bool Entity::isInWall()
{
	return m_pLevel->isSolidTile(Mth::floor(m_pos.x), Mth::floor(m_pos.y + getHeadHeight()), Mth::floor(m_pos.z));
}

bool Entity::isInWater()
{
	AABB aabb = m_hitbox;
	aabb.grow(0, -0.4f, 0);
	return m_pLevel->checkAndHandleWater(aabb, Material::water, this);
}

bool Entity::isInLava()
{
	AABB aabb = m_hitbox;
	aabb.grow(-0.1f, -0.4f, -0.1f);
	return m_pLevel->containsMaterial(aabb, Material::lava);
}

bool Entity::isUnderLiquid(Material* pMtl)
{
	int tileX = Mth::floor(m_pos.x);
	int tileY = Mth::floor(m_pos.y + getHeadHeight());
	int tileZ = Mth::floor(m_pos.z);

	Tile* pTile = Tile::tiles[m_pLevel->getTile(tileX, tileY, tileZ)];
	if (!pTile || pTile->m_pMaterial != pMtl)
		return false;

	int data = m_pLevel->getData(tileX, tileY, tileZ);
	int level = data <= 7 ? data + 1 : 1;

	return float(tileY) < float(tileY + 1) - (float(level) / 9.0f - 0.11111f);
}

float Entity::getHeadHeight()
{
	return 0.0f;
}

float Entity::getShadowHeightOffs()
{
	return field_8C / 2.0f;
}

float Entity::getBrightness(float f)
{
	int tileX = Mth::floor(m_pos.x);
	int tileY = Mth::floor(m_pos.y - field_84 + (m_hitbox.max.y - m_hitbox.min.y) * 0.66f);
	int tileZ = Mth::floor(m_pos.z);

	int minX = Mth::floor(m_hitbox.min.x);
	int minY = Mth::floor(m_hitbox.min.y);
	int minZ = Mth::floor(m_hitbox.min.z);
	int maxX = Mth::floor(m_hitbox.max.x);
	int maxY = Mth::floor(m_hitbox.max.y);
	int maxZ = Mth::floor(m_hitbox.max.z);

	if (!m_pLevel->hasChunksAt(minX, minY, minZ, maxX, maxY, maxZ))
		return 0;

	return m_pLevel->getBrightness(tileX, tileY, tileZ);
}

float Entity::distanceTo(Entity* pEnt)
{
	return distanceTo(pEnt->m_pos.x, pEnt->m_pos.y, pEnt->m_pos.z);
}

float Entity::distanceTo(float x, float y, float z)
{
	return sqrtf(distanceToSqr(x, y, z));
}

float Entity::distanceToSqr(Entity* pEnt)
{
	return distanceToSqr(pEnt->m_pos.x, pEnt->m_pos.y, pEnt->m_pos.z);
}

float Entity::distanceToSqr(float x, float y, float z)
{
	float diffX = m_pos.x - x;
	float diffY = m_pos.y - y;
	float diffZ = m_pos.z - z;

	return diffX * diffX + diffY * diffY + diffZ * diffZ;
}

int Entity::interactPreventDefault()
{
	return 0;
}

bool Entity::interact(Player* player)
{
	return false;
}

void Entity::playerTouch(Player* player)
{

}

void Entity::push(Entity* bud)
{
	float diffX = bud->m_pos.x - m_pos.x;
	float diffZ = bud->m_pos.z - m_pos.z;
	float maxDiff = Mth::absMax(diffX, diffZ);

	if (maxDiff < 0.01f) return;

	float x1 = sqrtf(maxDiff);
	float x2 = 1.0f / x1;
	if (x2 > 1.0f)
		x2 = 1.0f;
	float x3 = 1.0f - this->field_B0;
	float x4 = x3 * diffX / x1 * x2 * 0.05f;
	float x5 = x3 * diffZ / x1 * x2 * 0.05f;

	push(-x4, 0.0f, -x5);
	bud->push(x4, 0.0f, x5);
}

void Entity::push(float x, float y, float z)
{
	m_vel.x += x;
	m_vel.y += y;
	m_vel.z += z;
}

bool Entity::isPickable()
{
	return false;
}

bool Entity::isPushable()
{
	return false;
}

bool Entity::isShootable()
{
	return false;
}

bool Entity::isSneaking()
{
	return false;
}

bool Entity::isAlive()
{
	return m_bRemoved;
}

bool Entity::isOnFire()
{
	return field_C0 > 0;
}

bool Entity::isPlayer()
{
	return false;
}

bool Entity::isCreativeModeAllowed()
{
	return false;
}

bool Entity::shouldRender(Vec3& camPos)
{
	return shouldRenderAtSqrDistance(distanceToSqr(camPos.x, camPos.y, camPos.z));
}

bool Entity::shouldRenderAtSqrDistance(float distSqr)
{
	float maxDist = (field_30 * 64.0f) * (((m_hitbox.max.z - m_hitbox.min.z) + (m_hitbox.max.x - m_hitbox.min.x) + (m_hitbox.max.y - m_hitbox.min.y)) / 3.0f);

	return maxDist * maxDist > distSqr;
}

bool Entity::hurt(Entity* pAttacker, int damage)
{
	markHurt();
	return 0;
}

void Entity::animateHurt()
{

}

float Entity::getPickRadius()
{
	return 0.1f;
}


void Entity::awardKillScore(Entity* pKilled, int score)
{

}

void Entity::setEquippedSlot(int a, int b, int c)
{

}

void Entity::setRot(float yaw, float pitch)
{
	m_yaw = yaw;
	m_pitch = pitch;
}

void Entity::setSize(float rad, float height)
{
	field_88 = rad;
	field_8C = height;
}

void Entity::setPos(EntityPos* pPos)
{
	if (pPos->m_bHasPos)
		setPos(pPos->m_pos.x, pPos->m_pos.y, pPos->m_pos.z);
	else
		setPos(m_pos.x, m_pos.y, m_pos.z);

	if (pPos->m_bHasRot)
		setRot(pPos->m_yaw, pPos->m_pitch);
	else
		setRot(m_yaw, m_pitch);
}

void Entity::resetPos()
{
	if (!m_pLevel)
		// No level?  Fine
		return;

	if (m_pos.y <= 0.0f)
		// It's in the void.  It's probably not inside of any tiles.
		return;

	// Push the entity up while it's inside solid tiles.
	while (true)
	{
		setPos(m_pos.x, m_pos.y, m_pos.z);

		AABBVector* pCubes = m_pLevel->getCubes(this, m_hitbox);

		// if we aren't inside any tiles, great!
		if (!pCubes->size())
			break;

		m_pos.y += 1.0f;
	}

	m_vel.x = 0.0f;
	m_vel.y = 0.0f;
	m_vel.z = 0.0f;
	m_pitch = 0.0f;
}

void Entity::outOfWorld()
{
	remove();
}

void Entity::checkFallDamage(float fDeltaY, bool bHitGround)
{
	if (bHitGround)
	{
		if (m_distanceFallen > 0.0f)
		{
			causeFallDamage(m_distanceFallen);
			m_distanceFallen = 0.0f;
		}
	}
	else if (fDeltaY < 0.0f)
	{
		m_distanceFallen -= fDeltaY;
	}
}

void Entity::causeFallDamage(float f)
{
	// stub
}

void Entity::markHurt()
{
	m_bHurt = true;
}

void Entity::burn(int x)
{
	if (!field_D5)
		hurt(nullptr, 4);
}

void Entity::lavaHurt()
{
	if (!field_D5)
	{
		hurt(nullptr, 4);
		field_C0 = 600;
	}
}

int Entity::hashCode()
{
	return m_EntityID;
}

bool Entity::operator==(const Entity& other) const
{
	return m_EntityID == other.m_EntityID;
}

bool Entity::isLocalPlayer()
{
	return false;
}
