* Initial commit.

:)
This commit is contained in:
iProgramInCpp
2023-07-30 22:22:02 +03:00
commit 9642818a88
613 changed files with 151754 additions and 0 deletions

View File

@@ -0,0 +1,995 @@
#include "Entity.hpp"
#include "Player.hpp"
#include "Level.hpp"
int Entity::entityCounter;
Random Entity::sharedRandom;
Entity::Entity(Level* pLevel)
{
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);
auto 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);
auto 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);
auto pCubes = m_pLevel->getCubes(this, scanAABB);
float newY = y;
for (const auto &aabb : (*pCubes))
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 (const auto& aabb : (*pCubes))
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 (const auto& aabb : (*pCubes))
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 (const auto& aabb : (*pCubes))
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 (const auto& aabb : (*pCubes))
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 (const auto& aabb : (*pCubes))
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;
auto 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::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->field_11)
goto label_4;
}
else
{
field_D4 = false;
if (m_pLevel->field_11)
{
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, true);
}
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);
auto 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);
auto 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;
push(-x4, 0.0f, -x4);
bud->push(x4, 0.0f, x4);
}
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);
auto 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;
}

View File

@@ -0,0 +1,191 @@
#pragma once
#include "AABB.hpp"
#include "Vec3.hpp"
#include "Material.hpp"
#include "Tile.hpp"
#include "ItemInstance.hpp"
class Level;
class Player;
class ItemInstance;
enum eEntityRenderType
{
RENDER_NONE,
RENDER_TNT,
RENDER_HUMANOID,
RENDER_ITEM,
RENDER_CAMERA,
// custom
RENDER_FALLING_TILE = 50,
};
struct EntityPos
{
Vec3 m_pos;
float m_yaw = 0, m_pitch = 0;
bool m_bHasRot = false, m_bHasPos = false;
EntityPos() {};
EntityPos(const Vec3& pos)
{
m_pos = pos;
m_bHasPos = true;
}
EntityPos(float yaw, float pitch)
{
m_yaw = yaw;
m_pitch = pitch;
m_bHasRot = true;
}
EntityPos(const Vec3& pos, float yaw, float pitch)
{
m_pos = pos;
m_yaw = yaw;
m_pitch = pitch;
m_bHasPos = true;
m_bHasRot = true;
}
};
class Entity
{
public:
Entity(Level*);
virtual ~Entity();
virtual void setLevel(Level*);
virtual void removed();
virtual void setPos(float x, float y, float z);
virtual void remove();
virtual int move(float x, float y, float z);
virtual void moveTo(float x, float y, float z, float yaw, float pitch);
virtual void absMoveTo(float x, float y, float z, float yaw, float pitch);
virtual void moveRelative(float x, float z, float y);
virtual void lerpTo(float x, float y, float z, float yaw, float pitch, int i);
virtual void lerpMotion(float x, float y, float z);
virtual void turn(float yaw, float pitch);
virtual void interpolateTurn(float yaw, float pitch);
virtual void tick();
virtual void baseTick();
virtual bool intersects(float minX, float minY, float minZ, float maxX, float maxY, float maxZ);
virtual bool isFree(float offX, float offY, float offZ);
virtual bool isFree(float offX, float offY, float offZ, float expand);
virtual bool isInWall();
virtual bool isInWater();
virtual bool isInLava();
virtual bool isUnderLiquid(Material*);
virtual float getHeadHeight();
virtual float getShadowHeightOffs();
virtual float getBrightness(float f);
virtual float distanceTo(Entity*);
virtual float distanceToSqr(float x, float y, float z);
virtual float distanceTo(float x, float y, float z);
virtual float distanceToSqr(Entity*);
virtual int interactPreventDefault();
virtual bool interact(Player*);
virtual void playerTouch(Player*);
virtual void push(Entity*);
virtual void push(float x, float y, float z);
virtual bool isPickable();
virtual bool isPushable();
virtual bool isShootable();
virtual bool isSneaking();
virtual bool isAlive();
virtual bool isOnFire();
virtual bool isPlayer();
virtual bool isCreativeModeAllowed();
virtual bool shouldRender(Vec3& camPos);
virtual bool shouldRenderAtSqrDistance(float distSqr);
virtual bool hurt(Entity*, int);
virtual void animateHurt();
virtual float getPickRadius();
// virtual void spawnAtLocation(ItemInstance*, float);
// virtual void spawnAtLocation(int, int);
// virtual void spawnAtLocation(int, int, float);
virtual void awardKillScore(Entity* pKilled, int score);
virtual void setEquippedSlot(int, int, int);
virtual void setRot(float yaw, float pitch);
virtual void setSize(float rad, float height);
virtual void setPos(EntityPos*);
virtual void resetPos();
virtual void outOfWorld();
virtual void checkFallDamage(float f, bool b);
virtual void causeFallDamage(float f);
virtual void markHurt();
virtual void burn(int);
virtual void lavaHurt();
int hashCode();
bool operator==(const Entity& other) const;
float distanceToSqr_inline(float x, float y, float z) const
{
return
(m_pos.x - x) * (m_pos.x - x) +
(m_pos.y - y) * (m_pos.y - y) +
(m_pos.z - z) * (m_pos.z - z);
}
public:
static int entityCounter;
static Random sharedRandom;
Vec3 m_pos;
bool m_bInAChunk = false;
int m_chunkX = 0;
int m_chunkY = 0;
int m_chunkZ = 0;
int field_20 = 0;
int field_24 = 0;
int field_28 = 0;
int m_EntityID;
float field_30 = 1.0f;
uint8_t field_34 = 0;
Level* m_pLevel = nullptr;
Vec3 field_3C;
Vec3 m_vel;
float m_yaw = 0.0f;
float m_pitch = 0.0f;
//maybe these are the actual m_yaw and m_pitch, and
//the one I annotated are the destination yaw and pitch.
//interpolateTurn doesn't modify them, so I highly suspect
//this to be the case.
float field_5C = 0.0f;
float field_60 = 0.0f;
AABB m_hitbox;
bool field_7C = false;
bool field_7D = false;
bool field_7E = false;
bool field_7F = false;
bool m_bHurt = false;
uint8_t field_81 = 1;
bool m_bRemoved = false;
float field_84 = 0.0f;
float field_88 = 0.6f;
float field_8C = 1.8f;
float field_90 = 0.0f;
float field_94 = 0.0f;
Vec3 field_98;
float field_A4 = 0.0f;
float field_A8 = 0.0f;
bool m_bNoCollision = false;
float field_B0 = 0.0f;
int field_B4 = 0;
int field_B8 = 0;
int field_BC = 300;
int field_C0 = 0;
int field_C4;
int field_C8 = 0; // @NOTE: Render type? (eEntityRenderType)
float m_distanceFallen = 0.0f;
int field_D0 = 300;
uint8_t field_D4 = 0;
bool field_D5 = false;
bool field_D6 = true;
int field_D8 = 1;
};

View File

@@ -0,0 +1,84 @@
#include "FallingTile.hpp"
#include "Level.hpp"
FallingTile::FallingTile(Level* level) : Entity(level)
{
}
FallingTile::FallingTile(Level* level, float x, float y, float z, int id) : Entity(level)
{
m_id = id;
field_34 = 1;
setSize(0.98f, 0.98f);
field_84 = field_8C * 0.5f;
setPos(x, y, z);
field_3C.x = x;
field_3C.y = y;
field_3C.z = z;
m_vel.x = 0.0f;
m_vel.y = 0.0f;
m_vel.z = 0.0f;
#if defined(ENH_ALLOW_SAND_GRAVITY)
field_C8 = RENDER_FALLING_TILE;
#endif
}
float FallingTile::getShadowHeightOffs()
{
return 0.0f;
}
bool FallingTile::isPickable()
{
return !m_bRemoved;
}
void FallingTile::tick()
{
if (!m_id)
remove();
field_3C = m_pos;
field_E0++;
m_vel.y -= 0.04f;
move(m_vel.x, m_vel.y, m_vel.z);
m_vel *= 0.98f;
int tileX = Mth::floor(m_pos.x);
int tileY = Mth::floor(m_pos.y);
int tileZ = Mth::floor(m_pos.z);
// if we're inside one of our own tiles, clear it.
// Assumes we started there
if (m_pLevel->getTile(tileX, tileY, tileZ) == m_id)
m_pLevel->setTile(tileX, tileY, tileZ, TILE_AIR);
if (!field_7C)
{
if (field_E0 > 100 && !m_pLevel->field_11)
remove();
return;
}
m_vel.x *= 0.7f;
m_vel.z *= 0.7f;
m_vel.y *= -0.5f;
remove();
if (m_pLevel->mayPlace(m_id, tileX, tileY, tileZ, true))
{
m_pLevel->setTile(tileX, tileY, tileZ, m_id);
}
else
{
// @TODO: spawn resources?
}
}
Level* FallingTile::getLevel()
{
return m_pLevel;
}

View File

@@ -0,0 +1,21 @@
#pragma once
#include "Entity.hpp"
class FallingTile : public Entity
{
public:
FallingTile(Level*);
FallingTile(Level*, float x, float y, float z, int id);
float getShadowHeightOffs() override;
bool isPickable() override;
void tick() override;
Level* getLevel();
public:
int m_id;
int field_E0 = 0;
};

View File

@@ -0,0 +1,166 @@
#include "ItemEntity.hpp"
#include "Level.hpp"
ItemEntity::ItemEntity(Level* level) : Entity(level)
#ifndef ORIGINAL_CODE
, m__itemInstance(0, 0, 0)
#endif
{
// @NOTE: not setting render type
field_E8 = 2 * float(M_PI) * Mth::random();
setSize(0.25f, 0.25f);
field_84 = field_8C * 0.5f;
}
ItemEntity::ItemEntity(Level* level, float x, float y, float z, ItemInstance* itemInstance) :
Entity(level)
#ifndef ORIGINAL_CODE
, m__itemInstance(0, 0, 0)
#endif
{
field_C8 = RENDER_ITEM;
field_E8 = 2 * float(M_PI) * Mth::random();
setSize(0.25f, 0.25f);
field_84 = field_8C * 0.5f;
setPos(x, y, z);
// @BUG: Keeping a pointer to the original ItemInstance may end up storing an invalid one
#ifdef ORIGINAL_CODE
m_pItemInstance = itemInstance;
#else
// copy it
m__itemInstance = *itemInstance;
#endif
m_yaw = 360.0f * Mth::random();
m_vel.y = 0.2f;
m_vel.x = Mth::random() * 0.2f - 0.1f;
m_vel.z = Mth::random() * 0.2f - 0.1f;
}
void ItemEntity::burn(int damage)
{
hurt(nullptr, damage);
}
bool ItemEntity::hurt(Entity* pCulprit, int damage)
{
markHurt();
m_health -= damage;
if (m_health < 0)
remove();
return false;
}
bool ItemEntity::isInWater()
{
return m_pLevel->checkAndHandleWater(m_hitbox, Material::water, this);
}
void ItemEntity::playerTouch(Player* player)
{
// Here, this would give the item to the player, and remove the item entity.
#ifdef TEST_DROPPED_ITEMS
if (field_E4 != 0)
return;
// No inventory to add an item to. Just set the amount to 0
m_pItemInstance->m_amount = 0;
// No "random.pop" sound.
if (m_pItemInstance->m_amount <= 0)
remove();
#endif
}
void ItemEntity::tick()
{
Entity::tick();
if (field_E4 > 0)
field_E4--;
field_3C = m_pos;
m_vel.y -= 0.04f;
if (m_pLevel->getMaterial(Mth::floor(m_pos.x), Mth::floor(m_pos.y), Mth::floor(m_pos.z)) == Material::lava)
{
// give it a small bounce upwards
m_vel.y = 0.2f;
m_vel.x = 0.2f * (sharedRandom.nextFloat() - sharedRandom.nextFloat());
m_vel.z = 0.2f * (sharedRandom.nextFloat() - sharedRandom.nextFloat());
}
checkInTile(m_pos.x, m_pos.y, m_pos.z);
move(m_vel.x, m_vel.y, m_vel.z);
float dragFactor = 0.98f;
if (field_7C)
{
dragFactor = 0.588f;
TileID tile = m_pLevel->getTile(Mth::floor(m_pos.x), Mth::floor(m_hitbox.min.y) - 1, Mth::floor(m_pos.z));
if (tile > 0)
dragFactor = Tile::tiles[tile]->field_30 * 0.98f;
}
m_vel.x *= dragFactor;
m_vel.z *= dragFactor;
m_vel.y *= 0.98f;
if (field_7C)
m_vel.y *= -0.5f;
field_EC++;
field_E0++;
//despawn after 5 minutes
if (field_E0 >= 6000)
remove();
}
void ItemEntity::checkInTile(float x, float y, float z)
{
int xfl = Mth::floor(x);
int yfl = Mth::floor(y);
int zfl = Mth::floor(z);
if (!Tile::solid[m_pLevel->getTile(xfl, yfl, zfl)])
return;
bool solidXN = Tile::solid[m_pLevel->getTile(xfl - 1, yfl, zfl)];
bool solidXP = Tile::solid[m_pLevel->getTile(xfl + 1, yfl, zfl)];
bool solidYN = Tile::solid[m_pLevel->getTile(xfl, yfl - 1, zfl)];
bool solidYP = Tile::solid[m_pLevel->getTile(xfl, yfl + 1, zfl)];
bool solidZN = Tile::solid[m_pLevel->getTile(xfl, yfl, zfl - 1)];
bool solidZP = Tile::solid[m_pLevel->getTile(xfl, yfl, zfl + 1)];
float mindist = 9999.0f;
int mindir = -1;
float xdiff = x - float(xfl);
float ydiff = y - float(yfl);
float zdiff = z - float(zfl);
if (!solidXN && xdiff < mindist) mindist = xdiff, mindir = 0;
if (!solidXP && 1.0f - xdiff < mindist) mindist = 1.0f - xdiff, mindir = 1;
if (!solidYN && ydiff < mindist) mindist = ydiff, mindir = 2;
if (!solidYP && 1.0f - ydiff < mindist) mindist = 1.0f - ydiff, mindir = 3;
if (!solidZN && zdiff < mindist) mindist = zdiff, mindir = 4;
if (!solidZP && 1.0f - zdiff < mindist) mindist = 1.0f - zdiff, mindir = 5;
// the -1 case will be handled accordingly
float force = 0.1f + 0.2f * sharedRandom.nextFloat();
switch (mindir)
{
case 0: m_vel.x = -force; break;
case 1: m_vel.x = force; break;
case 2: m_vel.y = -force; break;
case 3: m_vel.y = force; break;
case 4: m_vel.z = -force; break;
case 5: m_vel.z = force; break;
}
}

View File

@@ -0,0 +1,35 @@
#pragma once
#include "Entity.hpp"
class ItemEntity : public Entity
{
public:
ItemEntity(Level* level);
ItemEntity(Level* level, float x, float y, float z, ItemInstance* itemInstance);
void burn(int damage) override;
bool hurt(Entity* pCulprit, int damage) override;
bool isInWater() override;
void playerTouch(Player*) override;
void tick() override;
void checkInTile(float x, float y, float z);
public:
// @NOTE: The original code keeps a pointer to something which is not duplicated with a new(), nor does it delete() the instance.
// So either it's leaked, or the code will use invalid memory.
#ifdef ORIGINAL_CODE
ItemInstance* m_pItemInstance;
#else
ItemInstance m__itemInstance;
ItemInstance* m_pItemInstance = &m__itemInstance;
#endif
int field_E0 = 0;
int field_E4 = 0;
float field_E8 = 0.0f;
int field_EC = 0;
int m_health = 5;
};

View File

@@ -0,0 +1,206 @@
#include "LocalPlayer.hpp"
#include "Minecraft.hpp"
int dword_250ADC, dword_250AE0;
LocalPlayer::LocalPlayer(Minecraft* pMinecraft, Level* pLevel, User* pUser, int i) : Player(pLevel)
{
m_pMinecraft = pMinecraft;
m_name = pUser->field_0;
field_BC4 = i;
field_C38 = m_pInventory->getSelectedItemId();
}
LocalPlayer::~LocalPlayer()
{
if (m_pKeyboardInput)
delete m_pKeyboardInput;
}
void LocalPlayer::aiStep()
{
m_pKeyboardInput->tick(/* this */);
if (m_pKeyboardInput->m_bSneakButton && field_A4 < 0.2f)
{
field_A4 = 0.2f;
}
Mob::aiStep();
Player::aiStep();
}
void LocalPlayer::animateRespawn()
{
}
void LocalPlayer::calculateFlight(float x, float y, float z)
{
float f1 = m_pMinecraft->m_options.field_244;
float x1 = f1 * x;
float z1 = f1 * z;
float y1 = 0.0f;
if (Keyboard::_states[AKEYCODE_BUTTON_R1] == 1)
y1 = f1 * 0.2f;
if (Keyboard::_states[AKEYCODE_BUTTON_L1] == 1)
y1 = f1 * -0.2f;
field_BFC += x1;
float f2 = m_pMinecraft->m_options.field_8 * 0.35f;
float f3 = f2 * (field_BFC - field_C00);
float f4 = field_C04 + 0.5f * (f3 - field_C04);
field_C04 = f4;
if ((f3 <= 0.0f || f3 <= f4) && (f3 >= 0.0f || f3 >= f4))
f4 = f3;
field_C00 += f4;
field_BF0 = f4 * 10.0f;
field_C08 += y1;
float f5 = f2 * (field_C08 - field_C0C);
float f6 = field_C10 + 0.5f * (f5 - field_C10);
field_C10 = f6;
if ((f5 <= 0.0f || f5 <= f6) && (f5 >= 0.0f || f5 >= f6))
f6 = f5;
field_C0C += f6;
field_BF4 = f6 * 10.0f;
field_C14 += z1;
float f7 = f2 * (field_C14 - field_C18);
float f8 = field_C1C + 0.5f * (f7 - field_C1C);
field_C1C = f8;
if ((f7 <= 0.0f || f7 <= f8) && (f7 >= 0.0f || f7 >= f8))
f8 = f7;
field_C18 += f8;
field_BF8 = f8 * 10.0f;
}
void LocalPlayer::closeContainer()
{
Player::closeContainer();
m_pMinecraft->setScreen(nullptr);
}
bool LocalPlayer::isSneaking()
{
return m_pKeyboardInput->m_bSneakButton;
}
int LocalPlayer::move(float x, float y, float z)
{
int result = 0;
LocalPlayer* pLP = m_pMinecraft->m_pLocalPlayer;
if (Minecraft::DEADMAU5_CAMERA_CHEATS && pLP == this && m_pMinecraft->m_options.m_bFlyCheat)
{
//@HUH: Using m_pMinecraft->m_pLocalPlayer instead of this, even though they're the same
pLP->m_bNoCollision = true;
float field_94_old = field_94;
pLP->calculateFlight(x, y, z);
pLP->m_distanceFallen = 0.0f;
pLP->m_vel.y = 0.0f;
// This looks very funny.
result = pLP->Entity::move(field_BF0, field_BF4, field_BF8);
pLP->field_7C = true;
field_94 = field_94_old;
}
else
{
#ifndef ORIGINAL_CODE
// @BUG: In the original Minecraft, you can't stop flying! If you do, you'll just fall through to the bottom of the world. :(
pLP->m_bNoCollision = false;
#endif
// autojump stuff
if (m_nAutoJumpFrames > 0)
{
m_nAutoJumpFrames--;
m_pKeyboardInput->m_bJumpButton = true;
}
float posX = m_pos.x;
float posY = m_pos.y;
result = Entity::move(x, y, z);
//@BUG: backing up posZ too late
float posZ = m_pos.z;
if (m_nAutoJumpFrames <= 0)
{
if (Mth::floor(posX * 2) == Mth::floor(m_pos.x * 2) &&
Mth::floor(posY * 2) == Mth::floor(m_pos.y * 2) &&
Mth::floor(posZ * 2) == Mth::floor(m_pos.z * 2))
return result;
float dist = Mth::sqrt(x * x + z * z);
int x1 = Mth::floor(x / dist + m_pos.x);
int z1 = Mth::floor(z / dist + m_pos.z);
TileID tileOnTop = m_pLevel->getTile(x1, int(m_pos.y - 1.0f), z1);
// not standing on top of a tile?
if (!m_pLevel->isSolidTile(x1, int(m_pos.y - 1.0f), z1))
return 0;
// aren't inside of a tile right now
if (m_pLevel->isSolidTile(x1, int(m_pos.y), z1))
return 0;
// don't have anything on top of us
if (m_pLevel->isSolidTile(x1, int(m_pos.y + 1.0f), z1))
return 1;
// are we trying to walk into stairs or a slab?
if (tileOnTop != Tile::stairs_stone->m_ID && tileOnTop != Tile::stairs_wood->m_ID && tileOnTop != Tile::stoneSlabHalf->m_ID)
// Nope, we're walking towards a full block. Trigger an auto jump.
m_nAutoJumpFrames = 1;
}
}
return result;
}
void LocalPlayer::tick()
{
Player::tick();
if (m_pMinecraft->isOnline())
{
if (fabsf(m_pos.x - field_C24) > 0.1f ||
fabsf(m_pos.y - field_C28) > 0.01f ||
fabsf(m_pos.z - field_C2C) > 0.1f ||
fabsf(field_C30 - m_pitch) > 1.0f ||
fabsf(field_C34 - m_yaw) > 1.0f)
{
m_pMinecraft->m_pRakNetInstance->send(new MovePlayerPacket(m_EntityID, m_pos.x, m_pos.y - field_84, m_pos.z, m_pitch, m_yaw));
field_C24 = m_pos.x;
field_C28 = m_pos.y;
field_C2C = m_pos.z;
field_C30 = m_pitch;
field_C34 = m_yaw;
}
if (field_C38 != m_pInventory->getSelectedItemId())
{
field_C38 = m_pInventory->getSelectedItemId();
m_pMinecraft->m_pRakNetInstance->send(new PlayerEquipmentPacket(m_EntityID, field_C38));
}
}
}
void LocalPlayer::updateAi()
{
Player::updateAi();
field_B00 = m_pKeyboardInput->m_horzInput;
field_B04 = m_pKeyboardInput->m_vertInput;
field_B0C = m_pKeyboardInput->m_bJumpButton || m_nAutoJumpFrames > 0;
}

View File

@@ -0,0 +1,52 @@
#pragma once
#include "Player.hpp"
#include "User.hpp"
#include "KeyboardInput.hpp"
class Minecraft;
class LocalPlayer : public Player
{
public:
LocalPlayer(Minecraft*, Level*, User*, int);
virtual ~LocalPlayer();
// TODO: void addAdditonalSaveData();
virtual void animateRespawn() override;
virtual void aiStep() override;
virtual bool isSneaking() override;
virtual int move(float x, float y, float z) override;
virtual void tick() override;
virtual void updateAi() override;
void calculateFlight(float, float, float);
void closeContainer(); //@HUH: oddly enough not a virtual/override
public:
int field_BEC = 0;
float field_BF0 = 0.0f;
float field_BF4 = 0.0f;
float field_BF8 = 0.0f;
float field_BFC = 0.0f;
float field_C00 = 0.0f;
float field_C04 = 0.0f;
float field_C08 = 0.0f;
float field_C0C = 0.0f;
float field_C10 = 0.0f;
float field_C14 = 0.0f;
float field_C18 = 0.0f;
float field_C1C = 0.0f;
int m_nAutoJumpFrames = 0;
// multiplayer related
float field_C24 = 0.0f;
float field_C28 = 0.0f;
float field_C2C = 0.0f;
float field_C30 = 0.0f;
float field_C34 = 0.0f;
// multiplayer related -- end
int field_C38 = 0;
Minecraft* m_pMinecraft;
KeyboardInput* m_pKeyboardInput = nullptr;
};

731
source/World/Entity/Mob.cpp Normal file
View File

@@ -0,0 +1,731 @@
#include "Mob.hpp"
#include "Level.hpp"
Mob::Mob(Level* pLevel) : Entity(pLevel)
{
m_texture = "/mob/char.png";
m_class = "";
field_34 = 1;
field_E4 = (Mth::random() + 1.0f) * 0.01f;
setPos(m_pos.x, m_pos.y, m_pos.z);
field_E0 = Mth::random() * 12398.0f;
m_yaw = float(Mth::random() * M_PI);
field_A8 = 0.5f;
}
Mob::~Mob()
{
}
void Mob::lerpTo(float x, float y, float z, float yaw, float pitch, int i)
{
field_B70 = x;
field_B74 = y + field_84;
field_B78 = z;
field_B7C = yaw;
field_B80 = pitch;
field_B6C = i;
}
void Mob::tick()
{
superTick();
if (field_B6C > 0)
{
float xPos = m_pos.x + (field_B70 - m_pos.x) / field_B6C;
float yPos = m_pos.y + (field_B74 - m_pos.y) / field_B6C;
float zPos = m_pos.z + (field_B78 - m_pos.z) / field_B6C;
float ang = field_B7C - m_yaw;
while (ang < -180.0f) ang += 360.0f;
while (ang >= 180.0f) ang -= 360.0f;
m_yaw += ang / float(field_B6C);
m_pitch += (field_B80 - m_pitch) / float(field_B6C);
setPos(xPos, yPos, zPos);
setRot(m_yaw, m_pitch);
field_B6C--;
}
aiStep();
updateWalkAnim();
//@TODO: unf*ck this variable mess
float deltaX, deltaZ, dist, x1, x2, x3, x4, x5, x6, x7, field_E8_2, field_E8_new, v36;
bool angleOOB = false;
deltaX = m_pos.x - field_3C.x;
deltaZ = m_pos.z - field_3C.z;
dist = Mth::sqrt(deltaZ * deltaZ + deltaX * deltaX);
field_E8_2 = field_E8;
x1 = field_E8_2;
field_B4C = field_B50;
if (dist > 0.05f)
{
x2 = dist * 3.0f;
v36 = Mth::atan2(deltaZ, deltaX);
x3 = 1.0f;
x1 = ((v36 * 180.0f) / float(M_PI)) - 90.0f;
field_E8_2 = this->field_E8;
}
else
{
x3 = 0.0f;
x2 = 0.0f;
}
x4 = field_F8;
if (x4 <= 0.0f)
x4 = m_yaw;
else
x4 = x1 = m_yaw;
if (!field_7C)
x3 = 0.0f;
field_B50 += (x3 - field_B50) * 0.3f;
x5 = x1 - field_E8_2;
while (x5 < -180.0f)
x5 += 360.0f;
while (x5 >= 180.0f)
x5 -= 360.0f;
field_E8_new = field_E8_2 + (float)(x5 * 0.3);
this->field_E8 = field_E8_new;
x6 = x4 - field_E8_new;
while (x6 < -180.0f)
x6 += 360.0f;
while (x6 >= 180.0f)
x6 -= 360.0f;
angleOOB = x6 < -90.0f || x6 >= 90.0f;
if (x6 < -75.0f)
{
x7 = x4 + 75.0f;
x6 = -75.0f;
field_E8 = x4 + 75.0f;
goto LABEL_30;
}
if (x6 >= 75.0f)
{
x7 = x4 - 75.0f;
x6 = 75.0f;
field_E8 = x4 - 75.0f;
LABEL_30:
field_E8 = x7 + (x6 * 0.2f);
goto LABEL_31;
}
x7 = x4 - x6;
field_E8 = x4 - x6;
if (x6 * x6 > 2500.0f)
goto LABEL_30;
LABEL_31:
if (angleOOB)
x2 = -x2;
while (x4 - field_5C < -180.0f)
field_5C -= 360.0f;
while (x4 - field_5C >= 180.0f)
field_5C += 360.0f;
while (field_E8 - field_EC < -180.0f)
field_EC -= 360.0f;
while (field_E8 - field_EC >= 180.0f)
field_EC += 360.0f;
while (m_pitch - field_60 < -180.0f);
field_60 -= 360.0f;
while (m_pitch - field_60 >= 180.0f)
field_60 += 360.0f;
field_B54 += x2;
}
void Mob::baseTick()
{
field_F4 = field_F8;
Entity::baseTick();
if (isAlive() && isInWall())
hurt(nullptr, 1);
if (isAlive() && isUnderLiquid(Material::water) && !isWaterMob())
{
field_BC--;
if (field_BC == -20)
{
field_BC = 0;
for (int i = 0; i < 8; i++)
{
m_pLevel->addParticle("bubble",
m_pos.x + m_random.nextFloat() - m_random.nextFloat(),
m_pos.y + m_random.nextFloat() - m_random.nextFloat(),
m_pos.z + m_random.nextFloat() - m_random.nextFloat(),
m_vel.x, m_vel.y, m_vel.z
);
}
hurt(nullptr, 2);
}
}
else
{
field_BC = field_D0;
}
field_118 = field_11C;
if (field_114 > 0) field_114--;
if (field_104 > 0) field_104--;
if (field_B8 > 0) field_B8--;
if (m_health <= 0)
{
field_110++;
if (field_110 > 20)
{
beforeRemove();
remove();
for (int i = 0; i < 20; i++)
{
m_pLevel->addParticle("explode",
m_pos.x + 2 * field_88 * m_random.nextFloat() - field_88,
m_pos.y + field_8C * m_random.nextFloat(),
m_pos.z + 2 * field_88 * m_random.nextFloat() - field_88,
0.02f * (m_random.nextFloat() * 2 - 1) * (m_random.nextFloat() * 2 - 1),
0.02f * (m_random.nextFloat() * 2 - 1) * (m_random.nextFloat() * 2 - 1),
0.02f * (m_random.nextFloat() * 2 - 1) * (m_random.nextFloat() * 2 - 1)
);
}
}
}
field_B58 = field_B54;
field_EC = field_E8;
field_5C = m_yaw;
field_60 = m_pitch;
}
float Mob::getHeadHeight()
{
return 0.85f * field_8C;
}
bool Mob::isPickable()
{
return !m_bRemoved;
}
bool Mob::isPushable()
{
return !m_bRemoved;
}
bool Mob::isShootable()
{
return true;
}
bool Mob::isAlive()
{
if (m_bRemoved)
return false;
return m_health >= 0;
}
bool Mob::hurt(Entity *pAttacker, int damage)
{
if (m_pLevel->field_11)
return false;
field_AFC = m_pLevel->field_11;
if (m_health <= 0)
return false;
field_12C = 1.5f;
if (float(field_B8) <= float(field_DC) * 0.5f)
{
field_100 = m_health;
field_B8 = field_DC;
field_B84 = damage;
actuallyHurt(damage);
field_108 = 10;
field_104 = 10;
}
else
{
if (field_B84 >= damage)
return 0;
actuallyHurt(damage - field_B84);
field_B84 = damage;
}
field_10C = 0;
if (m_health <= 0)
die(pAttacker);
return true;
}
void Mob::animateHurt()
{
field_108 = 10;
field_10C = 0;
field_104 = 10;
}
void Mob::setSize(float rad, float height)
{
Entity::setSize(rad, height);
}
void Mob::outOfWorld()
{
hurt(nullptr, 4);
}
void Mob::causeFallDamage(float level)
{
int x = int(ceilf(level - 3));
if (x > 0)
{
hurt(nullptr, 3);
//@HUH: useless call to getTile? or could this be a return value of some sort
//Entity::causeFallDamage returns nothing though, so....
m_pLevel->getTile(Mth::floor(m_pos.x), Mth::floor(m_pos.y - 0.2f - field_84), Mth::floor(m_pos.z));
}
}
void Mob::knockback(Entity* pEnt, int a, float x, float z)
{
float power = Mth::invSqrt(x * x + z * z);
m_vel.x = m_vel.x * 0.5f - x * power * 0.4f;
m_vel.y = m_vel.y * 0.5f + 0.4f;
m_vel.z = m_vel.z * 0.5f - z * power * 0.4f;
//@BUG: maybe this is meant to behave differently.
// Unless the player is falling, the Y vel will always be 0.4.
if (m_vel.y > 0.4f)
m_vel.y = 0.4f;
}
bool Mob::onLadder()
{
int tileX = Mth::floor(m_pos.x);
int tileZ = Mth::floor(m_pos.z);
int tileY = Mth::floor(m_hitbox.min.y);
//@INFO: Pre Beta 1.5 stair behaviour
return
m_pLevel->getTile(tileX, tileY + 0, tileZ) == Tile::ladder->m_ID ||
m_pLevel->getTile(tileX, tileY + 1, tileZ) == Tile::ladder->m_ID;
}
void Mob::spawnAnim()
{
}
std::string Mob::getTexture()
{
return m_texture;
}
void Mob::playAmbientSound()
{
}
int Mob::getAmbientSoundInterval()
{
return 80;
}
void Mob::heal(int health)
{
if (m_health <= 0)
return;
m_health += health;
if (m_health > C_MAX_MOB_HEALTH)
m_health = C_MAX_MOB_HEALTH;
field_B8 = field_DC / 2;
}
HitResult Mob::pick(float f1, float f2)
{
Vec3 pos = getPos(f2);
Vec3 view = getViewVector(f2);
Vec3 limit = pos + view * f1;
return m_pLevel->clip(pos, limit);
}
void Mob::travel(float a2, float a3)
{
float x1, x2, dragFactor, oldYPos = m_pos.y;
if (isInWater())
{
moveRelative(a2, a3, 0.02f);
move(m_vel.x, m_vel.y, m_vel.z);
x1 = 0.8f;
goto label_3;
}
if (isInLava())
{
moveRelative(a2, a3, 0.02f);
move(m_vel.x, m_vel.y, m_vel.z);
x1 = 0.5f;
label_3:
m_vel.y = m_vel.y * x1 - 0.02f;
m_vel.x *= x1;
m_vel.z *= x1;
if (field_7D)
{
if (isFree(m_vel.x, m_vel.y + 0.6f - m_pos.y + oldYPos, m_vel.z))
m_vel.y = 0.3f;
}
return;
}
if (!field_7C)
{
x2 = 0.02f;
}
else
{
float _x1;
int tileX = Mth::floor(m_pos.x);
int tileY = Mth::floor(m_hitbox.min.y);
int tileZ = Mth::floor(m_pos.z);
TileID tile = m_pLevel->getTile(tileX, tileY - 1, tileZ);
if (tile <= 0)
_x1 = 0.546f;
else
_x1 = Tile::tiles[tile]->field_30 * 0.91f;
assert(_x1 != 0.0f);
x2 = (0.16277f / (_x1 * _x1 * _x1)) * 0.1f;
}
moveRelative(a2, a3, x2);
if (!field_7C)
{
dragFactor = 0.91f;
}
else
{
//@HUH: repeated code. Could be an inlined function?
int tileX = Mth::floor(m_pos.x);
int tileY = Mth::floor(m_hitbox.min.y);
int tileZ = Mth::floor(m_pos.z);
TileID tile = m_pLevel->getTile(tileX, tileY - 1, tileZ);
if (tile <= 0)
dragFactor = 0.546f;
else
dragFactor = Tile::tiles[tile]->field_30 * 0.91f;
}
if (onLadder())
{
m_distanceFallen = 0.0f;
if (m_vel.y < -0.15f)
m_vel.y = -0.15f;
if (isSneaking() && m_vel.y < 0)
m_vel.y = 0;
}
move(m_vel.x, m_vel.y, m_vel.z);
if (field_7D && onLadder())
m_vel.y = 0.2f;
m_vel.y = (m_vel.y - 0.08f) * 0.98f; // gravity?
// drag
m_vel.x *= dragFactor;
m_vel.z *= dragFactor;
}
void Mob::die(Entity* pCulprit)
{
if (pCulprit && field_B48 > 0)
pCulprit->awardKillScore(pCulprit, field_B48);
field_B69 = true;
if (!m_pLevel->field_11)
dropDeathLoot();
}
bool Mob::canSee(Entity* pEnt)
{
Vec3 v1 = m_pos;
v1.y += getHeadHeight();
Vec3 v2 = pEnt->m_pos;
v2.y += pEnt->getHeadHeight();
return m_pLevel->clip(v1, v2).m_hitType == HitResult::NONE;
}
void Mob::updateWalkAnim()
{
field_128 = field_12C;
float diffX = m_pos.x - field_3C.x;
float diffZ = m_pos.z - field_3C.z;
float spd = 4.0f * Mth::sqrt(diffX * diffX + diffZ * diffZ);
if (spd > 1.0f)
spd = 1.0f;
field_12C += (spd - field_12C) * 0.4f;
field_130 += field_12C;
}
void Mob::aiStep()
{
if (isImmobile())
{
field_B0C = 0;
field_B00 = 0.0f;
field_B04 = 0.0f;
field_B08 = 0.0f;
}
else if (!field_F0)
{
updateAi();
}
bool bIsInWater = isInWater(), bIsInLava = isInLava();
if (field_B0C)
{
if (bIsInWater || bIsInLava)
m_vel.y += 0.04f;
else if (field_7C)
jumpFromGround();
}
field_B00 *= 0.98f;
field_B04 *= 0.98f;
field_B08 *= 0.9f;
travel(field_B00, field_B04);
AABB aabb = m_hitbox;
aabb.grow(0.2f, 0.2f, 0.2f);
auto pEnts = m_pLevel->getEntities(this, aabb);
for (auto pEnt : *pEnts)
{
if (pEnt->isPushable())
pEnt->push(this);
}
}
bool Mob::isWaterMob()
{
return false;
}
void Mob::superTick()
{
Entity::tick();
}
void Mob::lookAt(Entity* pEnt, float a3, float a4)
{
float diffX = pEnt->m_pos.x - m_pos.x;
float diffZ = pEnt->m_pos.z - m_pos.z;
float x1 = atan2f(diffZ, diffX);
float x2 = atan2f((pEnt->m_hitbox.min.y + pEnt->m_hitbox.max.y) / 2 - m_pos.y - getHeadHeight(), Mth::sqrt(diffX * diffX + diffZ * diffZ));
m_pitch = -rotlerp(m_pitch, x2 * 180.0f / float(M_PI), a4);
m_yaw = rotlerp(m_yaw, x1 * 180.0f / float(M_PI) - 90.0f, a3);
}
bool Mob::isLookingAtAnEntity()
{
return m_pEntLookedAt != nullptr;
}
Entity* Mob::getLookingAt()
{
return m_pEntLookedAt;
}
void Mob::beforeRemove()
{
}
bool Mob::canSpawn()
{
return m_pLevel->getCubes(this, m_hitbox)->empty();
}
float Mob::getAttackAnim(float f)
{
float x = field_F8 - field_F4;
if (x < 0.0f)
x += 1.0f;
return field_F4 + (x * f);
}
Vec3 Mob::getPos(float f)
{
if (f == 1.0f)
return m_pos;
return Vec3(
Lerp(field_3C.x, m_pos.x, f),
Lerp(field_3C.y, m_pos.y, f),
Lerp(field_3C.z, m_pos.z, f)
);
}
Vec3 Mob::getLookAngle(float f)
{
return getViewVector(1.0f);
}
Vec3 Mob::getViewVector(float f)
{
constexpr float C_180_OVER_PI = 0.017453f;
constexpr float C_PI = 3.1416f;
if (f == 1.0)
{
float x1 = Mth::cos(-(m_yaw * C_180_OVER_PI) - C_PI);
float x2 = Mth::sin(-(m_yaw * C_180_OVER_PI) - C_PI);
float x3 = -Mth::cos(-(m_pitch * C_180_OVER_PI));
return Vec3(x1 * x3, Mth::sin(-(m_pitch * C_180_OVER_PI)), x2 * x3);
}
float x1 = field_60 + (m_pitch - field_60) * f;
float x2 = -((field_5C + (m_yaw - field_5C) * f) * C_180_OVER_PI) - C_PI;
float x3 = Mth::cos(x2);
float x4 = Mth::sin(x2);
float x5 = -(x1 * C_180_OVER_PI);
float x6 = -Mth::cos(x5);
return Vec3(x4 * x6, Mth::sin(x5), x3 * x6);
}
int Mob::getMaxSpawnClusterSize()
{
return 4;
}
void Mob::actuallyHurt(int damage)
{
}
bool Mob::removeWhenFarAway()
{
return false;
}
int Mob::getDeathLoot()
{
return 0;
}
void Mob::dropDeathLoot()
{
}
bool Mob::isImmobile()
{
return m_health <= 0;
}
void Mob::jumpFromGround()
{
m_vel.y = 0.42f;
}
void Mob::updateAi()
{
// TODO
}
int Mob::getMaxHeadXRot()
{
return 10;
}
float Mob::getSoundVolume()
{
return 1.0f;
}
std::string Mob::getAmbientSound()
{
return "";
}
std::string Mob::getHurtSound()
{
return "random.hurt";
}
std::string Mob::getDeathSound()
{
return "random.hurt";
}
void Mob::defineSynchedData()
{
}
float Mob::rotlerp(float a2, float a3, float a4)
{
float ang = a3 - a2;
while (ang < -180.0f) ang += 360.0f;
while (ang >= 180.0f) ang -= 360.0f;
if (ang > a4)
ang = a4;
if (ang < -a4)
ang = -a4;
return a2 + ang;
}

123
source/World/Entity/Mob.hpp Normal file
View File

@@ -0,0 +1,123 @@
#pragma once
#include "Entity.hpp"
#define C_MAX_MOB_HEALTH (20)
class Mob : public Entity
{
public:
Mob(Level* pLevel);
virtual ~Mob();
//overrides
virtual void lerpTo(float x, float y, float z, float yaw, float pitch, int i) override;
virtual void tick() override;
virtual void baseTick() override;
virtual float getHeadHeight() override;
virtual bool isPickable() override;
virtual bool isPushable() override;
virtual bool isShootable() override;
virtual bool isAlive() override;
virtual bool hurt(Entity*, int) override;
virtual void animateHurt() override;
virtual void setSize(float rad, float height) override;
virtual void outOfWorld() override;
virtual void causeFallDamage(float level) override;
//virtuals
virtual void knockback(Entity* pEnt, int a, float x, float z);
virtual void die(Entity* pCulprit);
virtual bool canSee(Entity* pEnt);
virtual bool onLadder();
virtual void spawnAnim();
virtual std::string getTexture();
virtual bool isWaterMob();
virtual void playAmbientSound();
virtual int getAmbientSoundInterval();
virtual void superTick();
virtual void heal(int health);
virtual HitResult pick(float, float);
virtual void travel(float, float);
virtual void updateWalkAnim();
virtual void aiStep();
//AddAdditonalSaveData TODO
//ReadAdditionalSaveData TODO
virtual void lookAt(Entity* pEnt, float, float);
virtual bool isLookingAtAnEntity();
virtual Entity* getLookingAt();
virtual void beforeRemove();
virtual bool canSpawn();
virtual float getAttackAnim(float f);
virtual Vec3 getPos(float f);
virtual Vec3 getLookAngle(float f);
virtual Vec3 getViewVector(float f);
virtual int getMaxSpawnClusterSize();
virtual void actuallyHurt(int damage);
virtual bool removeWhenFarAway();
virtual int getDeathLoot();
virtual void dropDeathLoot();
virtual bool isImmobile();
virtual void jumpFromGround();
virtual void updateAi();
virtual int getMaxHeadXRot();
virtual float getSoundVolume();
virtual std::string getAmbientSound();
virtual std::string getHurtSound();
virtual std::string getDeathSound();
virtual void defineSynchedData();
float rotlerp(float, float, float);
public:
int field_DC = 10;
float field_E0;
float field_E4;
float field_E8 = 0.0f;
float field_EC = 0.0f;
char field_F0 = 0;
float field_F4 = 0.0f;
float field_F8 = 0.0f;
int m_health = 10;
int field_100 = 20;
int field_104 = 0;
int field_108 = 0;
float field_10C = 0.0f;
int field_110 = 0;
int field_114 = 0;
float field_118 = 0.0f;
float field_11C = 0.0f;
int field_120 = 0;
int field_124 = 0;
float field_128 = 0.0f;
float field_12C = 0.0f;
float field_130 = 0.0f;
Random m_random;
int field_AFC = 0;
float field_B00 = 0.0f;
float field_B04 = 0.0f;
float field_B08 = 0.0f;
char field_B0C = 0;
int field_B10 = 0;
float field_B14 = 0.7f;
std::string m_texture;
std::string m_class;
int field_B48 = 0;
float field_B4C = 0.0f;
float field_B50 = 0.0f;
float field_B54 = 0.0f;
float field_B58 = 0.0f;
float field_B5C = 0.0f;
float field_B60 = 1.0f;
int field_B64 = 0;
char field_B68 = 1;
char field_B69 = 0;
int field_B6C = 0;
float field_B70 = 0.0f;
float field_B74 = 0.0f;
float field_B78 = 0.0f;
float field_B7C = 0.0f;
float field_B80 = 0.0f;
int field_B84 = 0;
Entity* m_pEntLookedAt = nullptr;
};

View File

@@ -0,0 +1,260 @@
#include "HumanoidModel.hpp"
#include "Minecraft.hpp"
HumanoidModel::HumanoidModel(float a, float b):
m_head(0, 0),
m_body(16, 16),
m_armL(40, 16),
m_armR(40, 16),
m_legL(0, 16),
m_legR(0, 16)
{
m_head.addBox(-4, -8, -4, 8, 8, 8, a);
m_head.setPos(0, b, 0);
m_body.addBox(-4, 0, -2, 8, 12, 4);
m_body.setPos(0, b, 0);
m_armL.addBox(-3, -2, -2, 4, 12, 4, a);
m_armL.setPos(-5, b + 2, 0);
m_armR.field_18 = true;
m_armR.addBox(-1, -2, -2, 4, 12, 4, a);
m_armR.setPos(5, b + 2, 0);
m_legL.addBox(-2, 0, -2, 4, 12, 4, a);
m_legL.setPos(-2, b + 12, 0);
m_legR.field_18 = true;
m_legR.addBox(-2, 0, -2, 4, 12, 4, a);
m_legR.setPos(2, b + 12, 0);
}
void HumanoidModel::_logGraphics()
{
Matrix m;
if (Minecraft::customDebugId == 1)
{
// @NOTE: I think most of this function was ifdef'd/commented out
m.fetchGL(GL_MODELVIEW_MATRIX);
}
}
void HumanoidModel::onGraphicsReset()
{
m_head.m_bCompiled = false;
m_body.m_bCompiled = false;
m_armL.m_bCompiled = false;
m_armR.m_bCompiled = false;
m_legL.m_bCompiled = false;
m_legR.m_bCompiled = false;
}
void HumanoidModel::render(float a, float b, float c, float d, float e, float f)
{
setupAnim(a, b, c, d, e, f);
m_head.render(f);
m_body.render(f);
m_armL.render(f);
m_armR.render(f);
m_legL.render(f);
m_legR.render(f);
_logGraphics();
}
void HumanoidModel::renderHorrible(float a, float b, float c, float d, float e, float f)
{
setupAnim(a, b, c, d, e, f);
m_head.renderHorrible(f);
m_body.renderHorrible(f);
m_armL.renderHorrible(f);
m_armR.renderHorrible(f);
m_legL.renderHorrible(f);
m_legR.renderHorrible(f);
_logGraphics();
}
void HumanoidModel::setupAnim(float a2, float a3, float a4, float a5, float a6, float a7)
{
//a6 = pitch, a5 = yaw-ish
m_head.field_10 = a5 * 0.017453f;
m_head.field_C = a6 * 0.017453f;
if (m_head.field_C < -1.0f)
m_head.field_C = -1.0f;
if (m_head.field_C > 1.0f)
m_head.field_C = 1.0f;
float v12 = (a2 * 0.6662f) + 3.1416f;
m_armL.field_C = (Mth::cos(v12) * 2.0f * a3) * 0.5f;
m_armR.field_C = Mth::cos(a2 * 0.6662f) * 2.0f * a3 * 0.5f; // @HUH: multiplying by 2 and also by 1/2
m_armL.field_14 = 0.0f;
m_armR.field_14 = 0.0f;
m_legL.field_C = (Mth::cos(a2 * 0.6662f) * 1.4f) * a3;
m_legR.field_C = (Mth::cos(v12) * 1.4f) * a3;
m_legL.field_10 = 0.0f;
m_legR.field_10 = 0.0f;
if (field_8)
{
float v15 = (3.1416f * -0.5f) * 0.4f;
m_armL.field_C += v15;
m_armR.field_C += v15;
float v16 = (3.1416f * -0.5f) * 0.8f;
m_legL.field_C = v16;
m_legR.field_C = v16;
m_legL.field_10 = (3.1416f * 0.5f) * 0.2f;
m_legR.field_10 = (3.1416f * -0.5f) * 0.2f;
}
if (field_10BC)
m_armR.field_C = ((3.1416f * 0.5f) * -0.2f) + (m_armR.field_C * 0.5f);
if (field_10BD)
m_armL.field_C = ((3.1416f * 0.5f) * -0.2f) + (m_armL.field_C * 0.5f);
m_armL.field_10 = 0.0f;
m_armR.field_10 = 0.0f;
if (field_4 > -9990.0f)
{
m_body.field_10 = Mth::sin(Mth::sqrt(field_4) * 3.1416f * 2.0f) * 0.2f;
m_armL.m_posZ = 5.0f * Mth::sin(m_body.field_10);
m_armL.m_posX = -5.0f * Mth::cos(m_body.field_10);
m_armR.m_posZ = -5.0f * Mth::sin(m_body.field_10);
m_armR.m_posX = 5.0f * Mth::cos(m_body.field_10);
m_armL.field_10 = m_armL.field_10 + m_body.field_10;
m_armR.field_10 = m_armR.field_10 + m_body.field_10;
float o = 1.0f - field_4;
m_armR.field_C += m_body.field_10;
m_armL.field_C -= -((m_head.field_C - 0.7f) * Mth::sin(3.1416f * field_4)) * 0.75f + Mth::sin((1.0f - o * o * o * o) * 3.1416f) * 1.2f;
m_armL.field_10 += m_body.field_10 * 2.0f;
m_armL.field_14 = Mth::sin(field_4 * 3.1416f) * -0.4f;
}
if (field_10BE) // sneaking?
{
m_body.field_C = 0.5f;
m_armL.field_C += 0.4f;
m_armR.field_C += 0.4f;
m_legL.m_posZ = 4.0f;
m_legR.m_posZ = 4.0f;
m_legL.m_posY = 9.0f;
m_legR.m_posY = 9.0f;
m_head.m_posY = 1.0f;
}
else
{
m_body.field_C = 0.0f;
m_legL.m_posZ = 0.0f;
m_head.m_posY = 0.0f;
m_legR.m_posZ = 0.0f;
m_legL.m_posY = 12.0f;
m_legR.m_posY = 12.0f;
}
m_armL.field_14 += Mth::cos(a4 * 0.09f) * 0.05f + 0.05f;
m_armR.field_14 -= Mth::cos(a4 * 0.09f) * 0.05f + 0.05f;
m_armL.field_C += Mth::sin(a4 * 0.067f) * 0.05f;
m_armR.field_C += Mth::sin(a4 * 0.067f) * -0.05f;
}
/* Keeping the original around just in case:
void HumanoidModel::setupAnim(float a2, float a3, float a4, float a5, float a6, float a7)
{
this->m_head.field_10 = a5 * 0.017453f;
float v11 = a6 * 0.017453f;
if ((a6 * 0.017453f) <= 1.0f)
{
if (v11 < -1.0f)
v11 = -1.0f;
}
else
{
v11 = 1.0f;
}
this->m_head.field_C = v11;
float v12 = (a2 * 0.6662f) + 3.1416f;
float v13 = Mth::cos(v12);
this->m_armL.field_C = ((v13 + v13) * a3) * 0.5f;
float v14 = Mth::cos(a2 * 0.6662f);
this->m_armR.field_C = ((v14 + v14) * a3) * 0.5f;
this->m_armL.field_14 = 0.0f;
this->m_armR.field_14 = 0.0f;
this->m_legL.field_C = (Mth::cos(a2 * 0.6662f) * 1.4f) * a3;
this->m_legR.field_C = (Mth::cos(v12) * 1.4f) * a3;
this->m_legL.field_10 = 0.0f;
this->m_legR.field_10 = 0.0f;
if (this->field_8)
{
float v15 = (3.1416f * -0.5f) * 0.4f;
this->m_armL.field_C = this->m_armL.field_C + v15;
this->m_armR.field_C = this->m_armR.field_C + v15;
float v16 = (3.1416f * -0.5f) * 0.8f;
this->m_legL.field_C = v16;
this->m_legR.field_C = v16;
this->m_legL.field_10 = (3.1416f * 0.5f) * 0.2f;
this->m_legR.field_10 = (3.1416f * -0.5f) * 0.2f;
}
if (this->field_10BC)
this->m_armR.field_C = ((3.1416f * 0.5f) * -0.2f) + (this->m_armR.field_C * 0.5f);
if (this->field_10BD)
this->m_armL.field_C = ((3.1416f * 0.5f) * -0.2f) + (this->m_armL.field_C * 0.5f);
this->m_armL.field_10 = 0.0f;
this->m_armR.field_10 = 0.0f;
if (field_4 > -9990.0f)
{
float v22 = Mth::sqrt(field_4);
float v23 = Mth::sin((v22 * 3.1416f) + (v22 * 3.1416f));
this->m_body.field_10 = v23 * 0.2f;
float v24 = Mth::sin(v23 * 0.2f) * 5.0f;
float bf10_1 = this->m_body.field_10;
this->m_armL.m_posZ = v24;
float v26 = Mth::cos(bf10_1) * -5.0f;
float bf10_2 = this->m_body.field_10;
this->m_armL.m_posX = v26;
float v28 = Mth::sin(bf10_2) * -5.0f;
float bf10_3 = this->m_body.field_10;
this->m_armR.m_posZ = v28;
this->m_armR.m_posX = Mth::cos(bf10_3) * 5.0f;
float bf10_4 = this->m_body.field_10;
this->m_armL.field_10 = this->m_armL.field_10 + bf10_4;
this->m_armR.field_10 = this->m_armR.field_10 + bf10_4;
float v31 = this->m_armR.field_C + bf10_4;
float v32 = 1.0f - this->field_4;
this->m_armR.field_C = v31;
float v33 = Mth::sin((1.0f - ((v32 * v32) * (v32 * v32))) * 3.1416f);
float v34 = Mth::sin(3.1416f * this->field_4);
float bf10_times_2 = this->m_body.field_10 + this->m_body.field_10;
this->m_armL.field_C = this->m_armL.field_C - ((-((this->m_head.field_C - 0.7f) * v34) * 0.75f) + (v33 * 1.2f));
float v36 = this->m_armL.field_10 + bf10_times_2;
float v37 = this->field_4 * 3.1416f;
this->m_armL.field_10 = v36;
this->m_armL.field_14 = Mth::sin(v37) * -0.4f;
}
if (this->field_10BE)
{
m_body.field_C = 0.5f;
m_armL.field_C += 0.4f;
m_armR.field_C += 0.4f;
m_legL.m_posZ = 4.0f;
m_legR.m_posZ = 4.0f;
m_legL.m_posY = 9.0f;
m_legR.m_posY = 9.0f;
m_head.m_posY = 1.0f;
}
else
{
m_body.field_C = 0.0f;
m_legL.m_posZ = 0.0f;
m_head.m_posY = 0.0f;
m_legR.m_posZ = 0.0f;
m_legL.m_posY = 12.0f;
m_legR.m_posY = 12.0f;
}
float v18 = this->m_armL.field_14;
this->m_armL.field_14 = v18 + ((Mth::cos(a4 * 0.09f) * 0.05f) + 0.05f);
float v19 = this->m_armR.field_14;
this->m_armR.field_14 = v19 - ((Mth::cos(a4 * 0.09f) * 0.05f) + 0.05f);
float v20 = this->m_armL.field_C;
this->m_armL.field_C = v20 + (Mth::sin(a4 * 0.067f) * 0.05f);
float v21 = this->m_armR.field_C;
this->m_armR.field_C = v21 + (Mth::sin(a4 * 0.067f) * -0.05f);
}
*/

View File

@@ -0,0 +1,24 @@
#pragma once
#include "Model.hpp"
class HumanoidModel : public Model
{
public:
HumanoidModel(float a, float b);
void _logGraphics();
// @TODO - No xrefs: void render(HumanoidModel* a, float f);
void onGraphicsReset() override;
void render(float, float, float, float, float, float) override;
void renderHorrible(float, float, float, float, float, float) override;
void setupAnim(float, float, float, float, float, float) override;
public:
// @TODO: swap armL and armR.. Steve punches with the right hand.
Cube m_head, m_body, m_armL, m_armR, m_legL, m_legR;
bool field_10BC = false;
bool field_10BD = false;
bool field_10BE = false;
};

View File

@@ -0,0 +1,26 @@
#include "Model.hpp"
void Model::onGraphicsReset()
{
}
void Model::prepareMobModel(Mob*, float, float, float)
{
}
void Model::render(float, float, float, float, float, float)
{
}
void Model::renderHorrible(float, float, float, float, float, float)
{
}
void Model::setupAnim(float, float, float, float, float, float)
{
}

View File

@@ -0,0 +1,18 @@
#pragma once
#include "Cube.hpp"
#include "Mob.hpp"
class Model
{
public:
virtual void onGraphicsReset();
virtual void prepareMobModel(Mob*, float, float, float);
virtual void render(float, float, float, float, float, float);
virtual void renderHorrible(float, float, float, float, float, float);
virtual void setupAnim(float, float, float, float, float, float);
public:
float field_4 = 0.0f;
bool field_8 = false;
};

View File

@@ -0,0 +1,299 @@
#include "Player.hpp"
#include "Level.hpp"
Player::Player(Level* pLevel) : Mob(pLevel)
{
field_C8 = 2;
m_pInventory = new Inventory(this);
field_84 = 1.62f;
Pos pos = m_pLevel->getSharedSpawnPos();
moveTo(float(pos.x) + 0.5f, float(pos.y) + 1.0f, float(pos.z) + 0.5f, 0.0f, 0.0f);
m_health = 20;
m_class = "humanoid";
m_texture = "mob/char.png";
field_C4 = 20;
field_B5C = 180.0f;
}
Player::~Player()
{
delete m_pInventory;
}
void Player::remove()
{
Entity::remove();
}
void Player::tick()
{
Mob::tick();
}
bool Player::isInWall()
{
return Entity::isInWall();
}
float Player::getHeadHeight()
{
return 0.12f; //@HUH: what?
}
bool Player::isShootable()
{
return true;
}
bool Player::isPlayer()
{
return true;
}
bool Player::isCreativeModeAllowed()
{
return true;
}
bool Player::hurt(Entity* pEnt, int damage)
{
//@HUH
return false;
}
void Player::awardKillScore(Entity* pKilled, int score)
{
m_score += score;
}
void Player::resetPos()
{
field_84 = 1.62f;
setSize(0.6f, 1.8f);
Entity::resetPos();
m_health = 20;
field_110 = 0;
}
void Player::die(Entity* pCulprit)
{
Mob::die(pCulprit);
setSize(0.2f, 0.2f);
setPos(m_pos.x, m_pos.y, m_pos.z); // update hitbox
m_vel.y = 0.1f;
if (pCulprit)
{
m_vel.x = -0.1f * Mth::cos(float((field_10C + m_yaw) * M_PI / 180.0));
m_vel.z = -0.1f * Mth::cos(float((field_10C + m_yaw) * M_PI / 180.0));
}
else
{
m_vel.x = 0;
m_vel.z = 0;
}
}
void Player::aiStep()
{
field_B9C = field_BA0;
float velLen = Mth::sqrt(m_vel.x * m_vel.x + m_vel.z * m_vel.z);
float velYAtan = Mth::atan(m_vel.y * -0.2f), x1 = 0.0f;
if (velLen > 0.1f)
velLen = 0.1f;
if (!field_7C)
{
if (m_health > 0)
{
velLen = 0;
x1 = velYAtan * 15.0f;
}
else
{
velLen = x1 = 0.0f;
}
}
if (m_health <= 0)
velLen = 0.0f;
field_BA0 += (velLen - field_BA0) * 0.4f;
field_11C += (x1 - field_11C) * 0.8f;
if (m_health <= 0)
return;
AABB scanAABB = m_hitbox;
scanAABB.grow(1, 1, 1);
auto pEnts = m_pLevel->getEntities(this, scanAABB);
for (auto pEnt : *pEnts)
{
if (pEnt->m_bRemoved)
continue;
touch(pEnt);
}
}
bool Player::isImmobile()
{
return m_health <= 0;
}
void Player::updateAi()
{
if (field_BA4)
{
field_BA8++;
if (field_BA8 > 7)
{
field_BA8 = 0;
field_BA4 = false;
}
}
else
{
field_BA8 = 0;
}
field_F8 = field_BA8 * 0.125f;
}
void Player::defineSynchedData()
{
Mob::defineSynchedData();
}
void Player::animateRespawn()
{
}
int Player::addResource(int x)
{
return 0;
}
void Player::animateRespawn(Player*, Level*)
{
}
void Player::attack(Entity* pEnt)
{
}
bool Player::canDestroy(Tile* pTile)
{
return true;
}
void Player::closeContainer()
{
}
void Player::displayClientMessage(const std::string& msg)
{
}
void Player::drop()
{
}
float Player::getDestroySpeed()
{
return 0.5f;
}
int Player::getInventorySlot(int x)
{
return 0;
}
Pos Player::getRespawnPosition()
{
return m_respawnPos;
}
int Player::getScore()
{
return m_score;
}
void Player::prepareCustomTextures()
{
}
void Player::respawn()
{
}
void Player::rideTick()
{
}
void Player::setDefaultHeadHeight()
{
field_84 = 1.62f;
}
void Player::setRespawnPos(Pos* pos)
{
if (!pos)
{
m_bHaveRespawnPos = false;
return;
}
m_bHaveRespawnPos = true;
m_respawnPos.x = pos->x;
m_respawnPos.y = pos->y;
// @BUG: no m_respawnPos.z = pos->z ??
}
void Player::startCrafting(int x, int y, int z)
{
}
void Player::swing()
{
LogMsg("Swing!");
field_BA8 = -1;
field_BA4 = true;
}
void Player::take(Entity* pEnt, int x)
{
}
void Player::touch(Entity* pEnt)
{
pEnt->playerTouch(this);
}
void Player::interact(Entity* pEnt)
{
pEnt->interact(this);
}

View File

@@ -0,0 +1,76 @@
#pragma once
#include "Mob.hpp"
#include "Inventory.hpp"
#include "RakNetTypes.h"
class Inventory; // in case we're included from Inventory.hpp
class Player : public Mob
{
public:
Player(Level*);
virtual ~Player();
virtual void remove() override;
virtual void tick() override;
virtual bool isInWall() override;
virtual float getHeadHeight() override;
virtual bool isShootable() override;
virtual bool isPlayer() override;
virtual bool isCreativeModeAllowed() override;
virtual bool hurt(Entity*, int) override;
virtual void awardKillScore(Entity* pKilled, int score) override;
virtual void resetPos() override;
virtual void die(Entity* pCulprit) override;
virtual void aiStep() override;
virtual bool isImmobile() override;
virtual void updateAi() override;
virtual void defineSynchedData() override;
virtual void animateRespawn();
int addResource(int);
void animateRespawn(Player*, Level*);
void attack(Entity* pEnt);
bool canDestroy(Tile*);
void closeContainer();
void displayClientMessage(const std::string& msg);
// TODO: void drop(ItemInstance*);
// TODO: void drop(ItemInstance*, bool);
void drop();
float getDestroySpeed();
int getInventorySlot(int x);
Pos getRespawnPosition();
int getScore();
void prepareCustomTextures();
// TODO: void reallyDrop(ItemEntity* pEnt);
void respawn();
void rideTick();
void setDefaultHeadHeight();
void setRespawnPos(Pos*);
void startCrafting(int, int, int);
void swing();
void take(Entity* pEnt, int x);
void touch(Entity* pEnt);
void interact(Entity* pEnt);
public:
//TODO
Inventory* m_pInventory = nullptr;
uint8_t field_B94 = 0;
int m_score = 0;
float field_B9C = 0.0f;
float field_BA0 = 0.0f;
bool field_BA4 = false;
int field_BA8 = 0;
std::string m_name = "";
int field_BC4 = 0;
RakNet::RakNetGUID m_guid;
//TODO
Pos m_respawnPos;
//TODO
bool m_bHaveRespawnPos = false;
//TODO
};

View File

@@ -0,0 +1,69 @@
#include "PrimedTnt.hpp"
#include "Level.hpp"
PrimedTnt::PrimedTnt(Level* level) : Entity(level)
{
m_fuseTimer = 0;
field_C8 = RENDER_TNT;
field_34 = 1;
setSize(0.98f, 0.98f);
field_84 = field_8C * 0.5f;
}
PrimedTnt::PrimedTnt(Level* level, float x, float y, float z) : PrimedTnt(level)
{
setPos(x, y, z);
float fAng = 0.017453f * 2.0f * float(M_PI) * Mth::random();
m_vel.x = -0.02f * Mth::sin(fAng);
m_vel.z = -0.02f * Mth::cos(fAng);
m_vel.y = 0.2f;
field_3C = m_pos;
m_fuseTimer = 80; // 4 secs
}
void PrimedTnt::explode()
{
// @NOTE: Not passing `this` as pointer to entity
m_pLevel->explode(nullptr, m_pos.x, m_pos.y, m_pos.z, 2.5f);
}
float PrimedTnt::getShadowHeightOffs()
{
return 0.0f;
}
bool PrimedTnt::isPickable()
{
return !m_bRemoved;
}
void PrimedTnt::tick()
{
field_3C = m_pos;
m_vel.y -= 0.04f;
move(m_vel.x, m_vel.y, m_vel.z);
m_vel *= 0.98f;
if (field_7C)
{
m_vel.x *= 0.7f;
m_vel.z *= 0.7f;
m_vel.y *= -0.5f;
}
m_fuseTimer--;
if (m_fuseTimer <= 0)
{
remove();
explode();
}
else
{
m_pLevel->addParticle("smoke", m_pos.x, m_pos.y + 0.5f, m_pos.z, 0.0f, 0.0f, 0.0f);
}
}

View File

@@ -0,0 +1,20 @@
#pragma once
#include "Entity.hpp"
class PrimedTnt : public Entity
{
public:
PrimedTnt(Level*);
PrimedTnt(Level*, float, float, float);
float getShadowHeightOffs() override;
bool isPickable() override;
void tick() override;
void explode();
public:
int m_fuseTimer;
};

View File

@@ -0,0 +1,94 @@
#include "TripodCamera.hpp"
#include "Player.hpp"
#include "Level.hpp"
TripodCamera::TripodCamera(Level* level, Player* player, float x, float y, float z) : Mob(level)
{
m_owner = player;
field_C8 = RENDER_CAMERA;
field_60 = m_pitch = player->m_pitch;
field_5C = m_yaw = player->m_yaw;
field_34 = 1;
setSize(1.0f, 1.5f);
field_84 = field_8C * 0.5f - 0.25f;
setPos(x, y, z);
field_3C.x = x;
field_3C.y = y;
field_3C.z = z;
}
void TripodCamera::defineSynchedData()
{
}
float TripodCamera::getShadowHeightOffs()
{
return 0.0f;
}
bool TripodCamera::interact(Player* player)
{
// @BUG-ish: No check for owner?
m_bActive = true;
return true;
}
int TripodCamera::interactPreventDefault()
{
return 1;
}
bool TripodCamera::isPickable()
{
return !m_bRemoved;
}
bool TripodCamera::isPushable()
{
return false;
}
void TripodCamera::tick()
{
field_3C = m_pos;
m_vel.y -= 0.04f;
move(m_vel.x, m_vel.y, m_vel.z);
m_vel *= 0.98f;
if (field_7C)
{
m_vel.x *= 0.7f;
m_vel.z *= 0.7f;
m_vel.y *= -0.5f;
}
if (!m_bActive)
return;
field_B90--;
if (field_B90 == 0)
{
remove();
return;
}
if (field_B90 == 8)
{
m_pLevel->takePicture(this, m_owner);
m_pLevel->addParticle("explode", m_pos.x, m_pos.y + 0.6f, m_pos.z, 0.0f, 0.0f, 0.0f);
m_pLevel->addParticle("explode", m_pos.x, m_pos.y + 0.8f, m_pos.z, 0.0f, 0.0f, 0.0f);
m_pLevel->addParticle("explode", m_pos.x, m_pos.y + 1.0f, m_pos.z, 0.0f, 0.0f, 0.0f);
return;
}
if (field_B90 > 8)
{
m_pLevel->addParticle("smoke", m_pos.x, m_pos.y + 1.0f, m_pos.z, 0.0f, 0.0f, 0.0f);
}
}

View File

@@ -0,0 +1,27 @@
#pragma once
#include "Mob.hpp"
class Level;
class Player;
class TripodCamera : public Mob
{
public:
TripodCamera(Level*, Player*, float, float, float);
void defineSynchedData() override;
float getShadowHeightOffs() override;
bool interact(Player* player) override;
int interactPreventDefault() override;
bool isPickable() override;
bool isPushable() override;
void tick() override;
public:
int field_B8C = 0;
int field_B90 = 80;
Player* m_owner = nullptr;
bool m_bActive = false;
};