Files
MinecraftConsoles/Minecraft.World/Boat.cpp
2026-03-02 17:37:16 +07:00

520 lines
11 KiB
C++

#include "stdafx.h"
#include "com.mojang.nbt.h"
#include "net.minecraft.world.entity.h"
#include "net.minecraft.world.entity.player.h"
#include "net.minecraft.world.item.h"
#include "net.minecraft.world.level.h"
#include "net.minecraft.world.level.material.h"
#include "net.minecraft.world.level.tile.h"
#include "net.minecraft.world.phys.h"
#include "net.minecraft.world.damagesource.h"
#include "Boat.h"
const double Boat::MAX_SPEED = 0.35;
const double Boat::MAX_COLLISION_SPEED = MAX_SPEED * 0.75;
const double Boat::MIN_ACCELERATION = 0.07;
const double Boat::MAX_ACCELERATION = 0.35;
// 4J - added for common ctor code
void Boat::_init()
{
doLerp = true;
acceleration = MIN_ACCELERATION;
lSteps = 0;
lx = ly = lz = lyr = lxr = 0.0;
lxd = lyd = lzd = 0.0;
blocksBuilding = true;
setSize(1.5f, 0.6f);
heightOffset = bbHeight / 2.0f;
// 4J Stu - This function call had to be moved here from the Entity ctor to ensure that
// the derived version of the function is called
this->defineSynchedData();
}
Boat::Boat(Level *level) : Entity( level )
{
_init();
}
bool Boat::makeStepSound()
{
return false;
}
void Boat::defineSynchedData()
{
entityData->define(DATA_ID_HURT, 0);
entityData->define(DATA_ID_HURTDIR, 1);
entityData->define(DATA_ID_DAMAGE, 0);
}
AABB *Boat::getCollideAgainstBox(shared_ptr<Entity> entity)
{
return entity->bb;
}
AABB *Boat::getCollideBox()
{
return bb;
}
bool Boat::isPushable()
{
return true;
}
Boat::Boat(Level *level, double x, double y, double z) : Entity( level )
{
_init();
setPos(x, y + heightOffset, z);
xd = 0;
yd = 0;
zd = 0;
xo = x;
yo = y;
zo = z;
}
double Boat::getRideHeight()
{
return bbHeight * 0.0f - 0.3f;
}
bool Boat::hurt(DamageSource *source, int hurtDamage)
{
if (level->isClientSide || removed) return true;
// 4J-JEV: Fix for #88212,
// Untrusted players shouldn't be able to damage minecarts or boats.
if (dynamic_cast<EntityDamageSource *>(source) != NULL)
{
shared_ptr<Entity> attacker = source->getDirectEntity();
if (dynamic_pointer_cast<Player>(attacker) != NULL &&
!dynamic_pointer_cast<Player>(attacker)->isAllowedToHurtEntity( shared_from_this() ))
return false;
}
setHurtDir(-getHurtDir());
setHurtTime(10);
// 4J Stu - If someone is riding in this, then it can tick multiple times which causes the damage to
// decrease too quickly. So just make the damage a bit higher to start with for similar behaviour
// to an unridden one. Only do this change if the riding player is attacking it.
if( rider.lock() != NULL && rider.lock() == source->getEntity() ) hurtDamage += 1;
setDamage(getDamage() + hurtDamage * 10);
markHurt();
// 4J Stu - Brought froward from 12w36 to fix #46611 - TU5: Gameplay: Minecarts and boat requires more hits than one to be destroyed in creative mode
shared_ptr<Player> player = dynamic_pointer_cast<Player>(source->getEntity());
if (player != NULL && player->abilities.instabuild) setDamage(100);
if (getDamage() > 20 * 2)
{
if (rider.lock() != NULL) rider.lock()->ride( shared_from_this() );
spawnAtLocation(Item::boat_Id, 1, 0);
remove();
}
return true;
}
void Boat::animateHurt()
{
setHurtDir(-getHurtDir());
setHurtTime(10);
setDamage(getDamage() * 11);
}
bool Boat::isPickable()
{
return !removed;
}
void Boat::lerpTo(double x, double y, double z, float yRot, float xRot, int steps)
{
if (doLerp)
{
lSteps = steps + 5;
}
else
{
double xdiff = x - this->x;
double ydiff = y - this->y;
double zdiff = z - this->z;
double diff = xdiff * xdiff + ydiff * ydiff + zdiff * zdiff;
if (diff > 1)
{
lSteps = 3;
}
else
{
return;
}
}
lx = x;
ly = y;
lz = z;
lyr = yRot;
lxr = xRot;
this->xd = lxd;
this->yd = lyd;
this->zd = lzd;
}
void Boat::lerpMotion(double xd, double yd, double zd)
{
lxd = this->xd = xd;
lyd = this->yd = yd;
lzd = this->zd = zd;
}
void Boat::tick()
{
Entity::tick();
if (getHurtTime() > 0) setHurtTime(getHurtTime() - 1);
if (getDamage() > 0) setDamage(getDamage() - 1);
xo = x;
yo = y;
zo = z;
int steps = 5;
double waterPercentage = 0;
for (int i = 0; i < steps; i++)
{
double y0 = bb->y0 + (bb->y1 - bb->y0) * (i + 0) / steps - 2 / 16.0f;
double y1 = bb->y0 + (bb->y1 - bb->y0) * (i + 1) / steps - 2 / 16.0f;
AABB *bb2 = AABB::newTemp(bb->x0, y0, bb->z0, bb->x1, y1, bb->z1);
if (level->containsLiquid(bb2, Material::water))
{
waterPercentage += 1.0 / steps;
}
}
double lastSpeed = sqrt(xd * xd + zd * zd);
if (lastSpeed > MAX_COLLISION_SPEED)
{
double xa = cos(yRot * PI / 180);
double za = sin(yRot * PI / 180);
for (int i = 0; i < 1 + lastSpeed * 60; i++)
{
double side = (random->nextFloat() * 2 - 1);
double side2 = (random->nextInt(2) * 2 - 1) * 0.7;
if (random->nextBoolean())
{
double xx = x - xa * side * 0.8 + za * side2;
double zz = z - za * side * 0.8 - xa * side2;
level->addParticle(eParticleType_splash, xx, y - 2 / 16.0f, zz, +xd, yd, +zd);
}
else
{
double xx = x + xa + za * side * 0.7;
double zz = z + za - xa * side * 0.7;
level->addParticle(eParticleType_splash, xx, y - 2 / 16.0f, zz, +xd, yd, +zd);
}
}
}
if (level->isClientSide && doLerp)
{
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);
yRot += (float) ( (yrd) / lSteps );
xRot += (float) ( (lxr - xRot) / lSteps );
lSteps--;
this->setPos(xt, yt, zt);
this->setRot(yRot, xRot);
}
else
{
#if 1
// Original
//double xt = x + xd;
//double yt = y + yd;
//double zt = z + zd;
//this->setPos(xt, yt, zt);
// 4J Stu - Fix for various boat bugs, ensure that we check collision on client-side movement
this->move(xd,yd,zd);
if (onGround)
{
xd *= 0.5f;
yd *= 0.5f;
zd *= 0.5f;
}
xd *= 0.99f;
yd *= 0.95f;
zd *= 0.99f;
#else
// 4J Stu - Fix for #8280 - Gameplay : Boats behave erratically when exited next to land.
// The client shouldn't change the position of the boat
double xt = x;// + xd;
double yt = y + yd;
double zt = z;// + zd;
this->setPos(xt, yt, zt);
// 4J Stu - Fix for #9579 - GAMEPLAY: Boats with a player in them slowly sink under the water over time, and with no player in them they float into the sky.
// Just make the boats bob up and down rather than any other client-side movement when not receiving packets from server
if (waterPercentage < 1)
{
double bob = waterPercentage * 2 - 1;
yd += 0.04f * bob;
}
else
{
if (yd < 0) yd /= 2;
yd += 0.007f;
}
//if (onGround)
//{
xd *= 0.5f;
yd *= 0.5f;
zd *= 0.5f;
//}
//xd *= 0.99f;
//yd *= 0.95f;
//zd *= 0.99f;
#endif
}
return;
}
if (waterPercentage < 1)
{
double bob = waterPercentage * 2 - 1;
yd += 0.04f * bob;
}
else
{
if (yd < 0) yd /= 2;
yd += 0.007f;
}
if (rider.lock() != NULL)
{
xd += rider.lock()->xd * acceleration;
zd += rider.lock()->zd * acceleration;
}
double curSpeed = sqrt(xd * xd + zd * zd);
if (curSpeed > MAX_SPEED)
{
double ratio = MAX_SPEED / curSpeed;
xd *= ratio;
zd *= ratio;
curSpeed = MAX_SPEED;
}
if (curSpeed > lastSpeed && acceleration < MAX_ACCELERATION)
{
acceleration += (MAX_ACCELERATION - acceleration) / 35;
if (acceleration > MAX_ACCELERATION) acceleration = MAX_ACCELERATION;
}
else
{
acceleration -= (acceleration - MIN_ACCELERATION) / 35;
if (acceleration < MIN_ACCELERATION) acceleration = MIN_ACCELERATION;
}
if (onGround)
{
xd *= 0.5f;
yd *= 0.5f;
zd *= 0.5f;
}
move(xd, yd, zd);
if ((horizontalCollision && lastSpeed > 0.20))
{
if (!level->isClientSide)
{
remove();
for (int i = 0; i < 3; i++)
{
spawnAtLocation(Tile::wood_Id, 1, 0);
}
for (int i = 0; i < 2; i++)
{
spawnAtLocation(Item::stick->id, 1, 0);
}
}
}
else
{
xd *= 0.99f;
yd *= 0.95f;
zd *= 0.99f;
}
xRot = 0;
double yRotT = yRot;
double xDiff = xo - x;
double zDiff = zo - z;
if (xDiff * xDiff + zDiff * zDiff > 0.001)
{
yRotT = (float) (atan2(zDiff, xDiff) * 180 / PI);
}
double rotDiff = Mth::wrapDegrees(yRotT - yRot);
if (rotDiff > 20) rotDiff = 20;
if (rotDiff < -20) rotDiff = -20;
yRot += (float) rotDiff;
setRot(yRot, xRot);
if(level->isClientSide) return;
vector<shared_ptr<Entity> > *entities = level->getEntities(shared_from_this(), this->bb->grow(0.2f, 0, 0.2f));
if (entities != NULL && !entities->empty())
{
AUTO_VAR(itEnd, entities->end());
for (AUTO_VAR(it, entities->begin()); it != itEnd; it++)
{
shared_ptr<Entity> e = (*it); // entities->at(i);
if (e != rider.lock() && e->isPushable() && e->GetType() == eTYPE_BOAT)
{
e->push(shared_from_this());
}
}
}
for (int i = 0; i < 4; i++)
{
int xx = Mth::floor(x + ((i % 2) - 0.5) * 0.8);
int zz = Mth::floor(z + ((i / 2) - 0.5) * 0.8);
for (int j = 0; j < 2; j++)
{
int yy = Mth::floor(y) + j;
int tile = level->getTile(xx, yy, zz);
int data = level->getData(xx, yy, zz);
if (tile == Tile::topSnow_Id)
{
level->setTile(xx, yy, zz, 0);
}
else if (tile == Tile::waterLily_Id)
{
Tile::waterLily->spawnResources(level, xx, yy, zz, data, 0.3f, 0);
level->setTile(xx, yy, zz, 0);
}
}
}
if (rider.lock() != NULL)
{
if (rider.lock()->removed) rider = weak_ptr<Entity>();
}
}
void Boat::positionRider()
{
if (rider.lock() == NULL) return;
double xa = cos(yRot * PI / 180) * 0.4;
double za = sin(yRot * PI / 180) * 0.4;
rider.lock()->setPos(x + xa, y + getRideHeight() + rider.lock()->getRidingHeight(), z + za);
}
void Boat::addAdditonalSaveData(CompoundTag *base)
{
}
void Boat::readAdditionalSaveData(CompoundTag *base)
{
}
float Boat::getShadowHeightOffs()
{
return 0;
}
wstring Boat::getName()
{
return L"Boat";
}
bool Boat::interact(shared_ptr<Player> player)
{
if (rider.lock() != NULL && dynamic_pointer_cast<Player>(rider.lock())!=NULL && rider.lock() != player) return true;
if (!level->isClientSide)
{
// 4J HEG - Fixed issue with player not being able to dismount boat (issue #4446)
player->ride( rider.lock() == player ? nullptr : shared_from_this() );
}
return true;
}
void Boat::setDamage(int damage)
{
entityData->set(DATA_ID_DAMAGE, damage);
}
int Boat::getDamage()
{
return entityData->getInteger(DATA_ID_DAMAGE);
}
void Boat::setHurtTime(int hurtTime)
{
entityData->set(DATA_ID_HURT, hurtTime);
}
int Boat::getHurtTime()
{
return entityData->getInteger(DATA_ID_HURT);
}
void Boat::setHurtDir(int hurtDir)
{
entityData->set(DATA_ID_HURTDIR, hurtDir);
}
int Boat::getHurtDir()
{
return entityData->getInteger(DATA_ID_HURTDIR);
}
bool Boat::getDoLerp()
{
return doLerp;
}
void Boat::setDoLerp(bool doLerp)
{
this->doLerp = doLerp;
}