mirror of
https://github.com/smartcmd/MinecraftConsoles.git
synced 2026-04-25 08:27:28 +00:00
commit b40530fa5e12bdd0e2d03686b111964f9c5b3359
Author: Langtanium <94726057+Langtanium@users.noreply.github.com>
Date: Wed Apr 15 19:59:46 2026 -0700
Implemented skin offsets in UI
Added code to render skin offsets in the skin select UI.
commit a8384d984089b989a162550705dee7a505412e22
Author: Langtanium <94726057+Langtanium@users.noreply.github.com>
Date: Wed Apr 15 19:38:08 2026 -0700
Partially implemented offsets
Added code that visually shifts the player's model parts, but only in game not in the skin select UI.
commit 875100cf9afe7df258dc653a4b33857d3cd285c1
Author: Langtanium <94726057+Langtanium@users.noreply.github.com>
Date: Wed Apr 15 16:48:03 2026 -0700
Minor change
Simplified redundant conditions in HumanoidModel.cpp
commit 96f683d1fb93d09a49987460ef698e0d125c2c93
Merge: db685a74 24c74aa2
Author: Langtanium <94726057+Langtanium@users.noreply.github.com>
Date: Tue Apr 14 16:37:30 2026 -0700
Merge branch 'feat/64x64-skins' into feat/skin-offsets
commit db685a74f34d02cc83e33ab97c5a7ad9a62ef023
Author: Langtanium <94726057+Langtanium@users.noreply.github.com>
Date: Tue Apr 14 15:35:38 2026 -0700
Fixed skin offset data
Fixed skin offsets so they now return the actual data instead of the defaults, added a few minor tweaks, and added code in PlayerRenderer.cpp to access offsets (Can read the offsets but can not apply them).
commit aa769d54adb8f5bf96c75d10766422d23cc9129a
Author: Langtanium <94726057+Langtanium@users.noreply.github.com>
Date: Sat Apr 11 19:36:52 2026 -0700
Fixed crashes
Fixed code for offsets preventing crashes. The amount of offsets is correctly obtain, but lacks the actual data.
commit f18ac12cc072db74ed9b8da937e00c2e15f889a0
Merge: 8e76763a fd2fd659
Author: Langtanium <94726057+Langtanium@users.noreply.github.com>
Date: Fri Apr 10 16:06:57 2026 -0700
Merge branch 'feat/64x64-skins' into feat/skin-offsets
commit 8e76763a3ddeaff943243fd0469d6c4a7b12b6c1
Author: Langtanium <94726057+Langtanium@users.noreply.github.com>
Date: Tue Apr 7 16:50:43 2026 -0700
Made more changes
Made more changes in files to support skin offsets. The game still crashes when trying to load skins.
commit 1a8f3532979033717044e74e6ceb766e23e35032
Merge: a1d9ae59 bb5fa506
Author: Langtanium <94726057+Langtanium@users.noreply.github.com>
Date: Tue Apr 7 13:12:39 2026 -0700
Merge branch 'feat/64x64-skins' into feat/skin-offsets
commit a1d9ae591ac27117626aac708a4204a9e2451684
Author: Langtanium <94726057+Langtanium@users.noreply.github.com>
Date: Fri Apr 3 21:50:42 2026 -0700
Added small additions
Added more code referencing skin offsets. Still doesn't work correctly.
commit d28a751d9ca0527854e40be82e7841a650c2f189
Merge: 3888de7a 8bf03435
Author: Langtanium <94726057+Langtanium@users.noreply.github.com>
Date: Thu Apr 2 17:09:08 2026 -0700
Merge branch 'smartcmd:main' into feat/skin-offsets
commit 3888de7ab401d9562b7b922eca8b898aa73ba024
Author: Langtanium <94726057+Langtanium@users.noreply.github.com>
Date: Thu Apr 2 17:07:48 2026 -0700
Added code for skin offsets
Added code to the file which have the functionality to get skin boxes and duplicated the functionality for skin offsets. The code causes the game to crash when switching to third person. The error occurs with the skin offsets returning as an empty class object.
2146 lines
48 KiB
C++
2146 lines
48 KiB
C++
#include "stdafx.h"
|
|
#include "com.mojang.nbt.h"
|
|
#include "net.minecraft.world.item.h"
|
|
#include "net.minecraft.world.item.enchantment.h"
|
|
#include "net.minecraft.world.level.h"
|
|
#include "net.minecraft.world.level.dimension.h"
|
|
#include "net.minecraft.world.level.tile.h"
|
|
#include "net.minecraft.world.phys.h"
|
|
#include "net.minecraft.world.entity.item.h"
|
|
#include "net.minecraft.world.level.material.h"
|
|
#include "net.minecraft.world.damagesource.h"
|
|
#include "SynchedEntityData.h"
|
|
#include "EntityIO.h"
|
|
#include "SharedConstants.h"
|
|
|
|
#include "ParticleTypes.h"
|
|
|
|
#include "EntityPos.h"
|
|
#include "Entity.h"
|
|
#include "SoundTypes.h"
|
|
#include "..\minecraft.Client\HumanoidModel.h"
|
|
#include "..\Minecraft.Client\MinecraftServer.h"
|
|
#include "..\Minecraft.Client\MultiPlayerLevel.h"
|
|
#include "..\Minecraft.Client\MultiplayerLocalPlayer.h"
|
|
#include "..\Minecraft.Client\ServerLevel.h"
|
|
#include "..\Minecraft.Client\PlayerList.h"
|
|
|
|
const wstring Entity::RIDING_TAG = L"Riding";
|
|
|
|
int Entity::entityCounter = 2048; // 4J - changed initialiser to 2048, as we are using range 0 - 2047 as special unique smaller ids for things that need network tracked
|
|
DWORD Entity::tlsIdx = TlsAlloc();
|
|
|
|
// 4J - added getSmallId & freeSmallId methods
|
|
unsigned int Entity::entityIdUsedFlags[2048/32] = {0};
|
|
unsigned int Entity::entityIdWanderFlags[2048/32] = {0};
|
|
unsigned int Entity::entityIdRemovingFlags[2048/32] = {0};
|
|
int Entity::extraWanderIds[EXTRA_WANDER_MAX] = {0};
|
|
int Entity::extraWanderTicks = 0;
|
|
int Entity::extraWanderCount = 0;
|
|
|
|
int Entity::getSmallId()
|
|
{
|
|
unsigned int *puiUsedFlags = entityIdUsedFlags;
|
|
unsigned int *puiRemovedFlags = nullptr;
|
|
|
|
// If we are the server (we should be, if we are allocating small Ids), then check with the server if there are any small Ids which are
|
|
// still in the ServerPlayer's vectors of entities to be removed - these are used to gather up a set of entities into one network packet
|
|
// for final notification to the client that the entities are removed. We can't go re-using these small Ids yet, as otherwise we will
|
|
// potentially end up telling the client that the entity has been removed After we have already re-used its Id and created a new entity.
|
|
// This ends up with newly created client-side entities being removed by accident, causing invisible mobs.
|
|
if( ((size_t)TlsGetValue(tlsIdx) != 0 ) )
|
|
{
|
|
MinecraftServer *server = MinecraftServer::getInstance();
|
|
if( server )
|
|
{
|
|
// In some attempt to optimise this, flagEntitiesToBeRemoved most of the time shouldn't do anything at all, and in this case it
|
|
// doesn't even memset the entityIdRemovingFlags array, so we shouldn't use it if the return value is false.
|
|
bool removedFound = server->flagEntitiesToBeRemoved(entityIdRemovingFlags);
|
|
if( removedFound )
|
|
{
|
|
// Has set up the entityIdRemovingFlags vector in this case, so we should check against this when allocating new ids
|
|
// app.DebugPrintf("getSmallId: Removed entities found\n");
|
|
puiRemovedFlags = entityIdRemovingFlags;
|
|
}
|
|
}
|
|
}
|
|
|
|
for( int i = 0; i < (2048 / 32 ); i++ )
|
|
{
|
|
unsigned int uiFlags = *puiUsedFlags;
|
|
if( uiFlags != 0xffffffff )
|
|
{
|
|
unsigned int uiMask = 0x80000000;
|
|
for( int j = 0; j < 32; j++ )
|
|
{
|
|
// See comments above - now checking (if required) that these aren't newly removed entities that the clients still haven't been told about,
|
|
// so we don't reuse those ids before we should.
|
|
if( puiRemovedFlags )
|
|
{
|
|
if( puiRemovedFlags[i] & uiMask )
|
|
{
|
|
// app.DebugPrintf("Avoiding using ID %d (0x%x)\n", i * 32 + j,puiRemovedFlags[i]);
|
|
uiMask >>= 1;
|
|
continue;
|
|
}
|
|
}
|
|
if( ( uiFlags & uiMask ) == 0 )
|
|
{
|
|
uiFlags |= uiMask;
|
|
*puiUsedFlags = uiFlags;
|
|
return i * 32 + j;
|
|
}
|
|
uiMask >>= 1;
|
|
}
|
|
}
|
|
puiUsedFlags++;
|
|
}
|
|
|
|
#ifdef MINECRAFT_SERVER_BUILD
|
|
// in mc server dedi, a server with 8+ playerrs can cause this to go wack
|
|
int fallbackId = Entity::entityCounter++;
|
|
|
|
if (entityCounter == 0x7ffffff)
|
|
{
|
|
entityCounter = 2048;
|
|
}
|
|
return fallbackId;
|
|
#else
|
|
app.DebugPrintf("Out of small entity Ids... possible leak?\n");
|
|
__debugbreak();
|
|
return -1;
|
|
#endif
|
|
}
|
|
|
|
void Entity::countFlagsForPIX()
|
|
{
|
|
int freecount = 0;
|
|
unsigned int *puiUsedFlags = entityIdUsedFlags;
|
|
for( int i = 0; i < (2048 / 32 ); i++ )
|
|
{
|
|
unsigned int uiFlags = *puiUsedFlags;
|
|
if( uiFlags != 0xffffffff )
|
|
{
|
|
unsigned int uiMask = 0x80000000;
|
|
for( int j = 0; j < 32; j++ )
|
|
{
|
|
if( ( uiFlags & uiMask ) == 0 )
|
|
{
|
|
freecount++;
|
|
}
|
|
uiMask >>= 1;
|
|
}
|
|
}
|
|
puiUsedFlags++;
|
|
}
|
|
PIXAddNamedCounter(freecount,"Small Ids free");
|
|
PIXAddNamedCounter(2048 - freecount,"Small Ids used");
|
|
}
|
|
|
|
void Entity::resetSmallId()
|
|
{
|
|
freeSmallId(entityId);
|
|
if( ((size_t)TlsGetValue(tlsIdx) != 0 ) )
|
|
{
|
|
entityId = getSmallId();
|
|
}
|
|
}
|
|
|
|
void Entity::freeSmallId(int index)
|
|
{
|
|
if( ( (size_t)TlsGetValue(tlsIdx) ) == 0 ) return; // Don't do anything with small ids if this isn't the server thread
|
|
if( index >= 2048 ) return; // Don't do anything if this isn't a short id
|
|
|
|
unsigned int i = index / 32;
|
|
unsigned int j = index % 32;
|
|
unsigned int uiMask = ~(0x80000000 >> j);
|
|
|
|
entityIdUsedFlags[i] &= uiMask;
|
|
entityIdWanderFlags[i] &= uiMask;
|
|
}
|
|
|
|
void Entity::useSmallIds()
|
|
{
|
|
TlsSetValue(tlsIdx,(LPVOID)1);
|
|
}
|
|
|
|
// Things also added here to be able to manage the concept of a number of extra "wandering" entities - normally path finding entities aren't allowed to
|
|
// randomly wander about once they are a certain distance away from any player, but we want to be able to (in a controlled fashion) allow some to be able
|
|
// to move so that we can determine whether they have been enclosed in some kind of farm, and so be able to better determine what shouldn't or shouldn't be despawned.
|
|
|
|
// Let the management system here know whether or not to consider this particular entity for some extra wandering
|
|
void Entity::considerForExtraWandering(bool enable)
|
|
{
|
|
if( ( (size_t)TlsGetValue(tlsIdx) ) == 0 ) return; // Don't do anything with small ids if this isn't the server thread
|
|
if( entityId >= 2048 ) return; // Don't do anything if this isn't a short id
|
|
|
|
unsigned int i = entityId / 32;
|
|
unsigned int j = entityId % 32;
|
|
if( enable )
|
|
{
|
|
unsigned int uiMask = 0x80000000 >> j;
|
|
entityIdWanderFlags[i] |= uiMask;
|
|
}
|
|
else
|
|
{
|
|
unsigned int uiMask = ~(0x80000000 >> j);
|
|
entityIdWanderFlags[i] &= uiMask;
|
|
}
|
|
}
|
|
|
|
// Should this entity do wandering in addition to what the java code would have done?
|
|
bool Entity::isExtraWanderingEnabled()
|
|
{
|
|
if( ( (size_t)TlsGetValue(tlsIdx) ) == 0 ) return false; // Don't do anything with small ids if this isn't the server thread
|
|
if( entityId >= 2048 ) return false; // Don't do anything if this isn't a short id
|
|
|
|
for( int i = 0; i < extraWanderCount; i++ )
|
|
{
|
|
if( extraWanderIds[i] == entityId ) return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
// Returns a quadrant of direction that a given entity should be moved in - this is to stop the randomness of the wandering/strolling from just making the entity double back
|
|
// on itself and thus making the determination of whether the entity has been enclosed take longer than it needs to. This function returns a quadrant from 0 to 3
|
|
// that should be consistent within one period of an entity being considered for extra wandering, but should potentially vary between tries and between different entities.
|
|
int Entity::getWanderingQuadrant()
|
|
{
|
|
return ( entityId + ( extraWanderTicks / EXTRA_WANDER_TICKS ) ) & 3;
|
|
}
|
|
|
|
// Every EXTRA_WANDER_TICKS ticks, attempt to find EXTRA_WANDER_MAX entity Ids from those that have been flagged as ones that should be considered for
|
|
// extra wandering
|
|
void Entity::tickExtraWandering()
|
|
{
|
|
extraWanderTicks++;
|
|
// Time to move onto some new entities?
|
|
|
|
if( ( extraWanderTicks % EXTRA_WANDER_TICKS == 0 ) )
|
|
{
|
|
// printf("Updating extras: ");
|
|
// Start from the next Id after the one that we last found, or zero if we didn't find anything last time
|
|
int entityId = 0;
|
|
if( extraWanderCount )
|
|
{
|
|
entityId = ( extraWanderIds[ extraWanderCount - 1 ] + 1 ) % 2048;
|
|
}
|
|
|
|
extraWanderCount = 0;
|
|
|
|
for( int k = 0; ( k < 2048 ) && ( extraWanderCount < EXTRA_WANDER_MAX); k++ )
|
|
{
|
|
unsigned int i = entityId / 32;
|
|
unsigned int j = entityId % 32;
|
|
unsigned int uiMask = 0x80000000 >> j;
|
|
|
|
if( entityIdWanderFlags[i] & uiMask )
|
|
{
|
|
extraWanderIds[ extraWanderCount++ ] = entityId;
|
|
// printf("%d, ", entityId);
|
|
}
|
|
|
|
entityId = ( entityId + 1 ) % 2048;
|
|
}
|
|
// printf("\n");
|
|
}
|
|
}
|
|
|
|
// 4J - added for common ctor code
|
|
// Do all the default initialisations done in the java class
|
|
void Entity::_init(bool useSmallId, Level *level)
|
|
{
|
|
// 4J - changed to assign two different types of ids. A range from 0-2047 is used for things that we'll be wanting to identify over the network,
|
|
// so we should only need 11 bits rather than 32 to uniquely identify them. The rest of the range is used for anything we don't need to track like this,
|
|
// currently particles. We only ever want to allocate this type of id from the server thread, so using thread local storage to isolate this.
|
|
if( useSmallId && ((size_t)TlsGetValue(tlsIdx) != 0 ) )
|
|
{
|
|
entityId = getSmallId();
|
|
}
|
|
else
|
|
{
|
|
entityId = Entity::entityCounter++;
|
|
if(entityCounter == 0x7ffffff ) entityCounter = 2048;
|
|
}
|
|
|
|
viewScale = 1.0;
|
|
|
|
blocksBuilding = false;
|
|
rider = weak_ptr<Entity>();
|
|
riding = nullptr;
|
|
forcedLoading = false;
|
|
|
|
//level = nullptr; // Level is assigned to in the original c_tor code
|
|
xo = yo = zo = 0.0;
|
|
x = y = z = 0.0;
|
|
xd = yd = zd = 0.0;
|
|
yRot = xRot = 0.0f;
|
|
yRotO = xRotO = 0.0f;
|
|
bb = AABB::newPermanent(0, 0, 0, 0, 0, 0); // 4J Was final
|
|
onGround = false;
|
|
horizontalCollision = verticalCollision = false;
|
|
collision = false;
|
|
hurtMarked = false;
|
|
isStuckInWeb = false;
|
|
|
|
slide = true;
|
|
removed = false;
|
|
heightOffset = 0 / 16.0f;
|
|
|
|
bbWidth = 0.6f;
|
|
bbHeight = 1.8f;
|
|
|
|
walkDistO = 0;
|
|
walkDist = 0;
|
|
moveDist = 0.0f;
|
|
|
|
fallDistance = 0;
|
|
|
|
nextStep = 1;
|
|
|
|
xOld = yOld = zOld = 0.0;
|
|
ySlideOffset = 0;
|
|
footSize = 0.0f;
|
|
noPhysics = false;
|
|
pushthrough = 0.0f;
|
|
|
|
random = new Random();
|
|
|
|
tickCount = 0;
|
|
flameTime = 1;
|
|
|
|
onFire = 0;
|
|
wasInWater = false;
|
|
|
|
|
|
invulnerableTime = 0;
|
|
|
|
firstTick = true;
|
|
|
|
fireImmune = false;
|
|
|
|
// values that need to be sent to clients in SMP
|
|
if( useSmallId )
|
|
{
|
|
entityData = std::make_shared<SynchedEntityData>();
|
|
}
|
|
else
|
|
{
|
|
entityData = nullptr;
|
|
}
|
|
|
|
xRideRotA = yRideRotA = 0.0;
|
|
inChunk = false;
|
|
xChunk = yChunk = zChunk = 0;
|
|
xp = yp = zp = 0;
|
|
xRotp = yRotp = 0;
|
|
noCulling = false;
|
|
|
|
hasImpulse = false;
|
|
|
|
changingDimensionDelay = 0;
|
|
isInsidePortal = false;
|
|
portalTime = 0;
|
|
dimension = 0;
|
|
portalEntranceDir = 0;
|
|
invulnerable = false;
|
|
if( useSmallId )
|
|
{
|
|
uuid = L"ent" + Mth::createInsecureUUID(random);
|
|
}
|
|
|
|
// 4J Added
|
|
m_ignoreVerticalCollisions = false;
|
|
m_uiAnimOverrideBitmask = 0L;
|
|
m_ignorePortal = false;
|
|
}
|
|
|
|
Entity::Entity(Level *level, bool useSmallId) // 4J - added useSmallId parameter
|
|
{
|
|
MemSect(16);
|
|
_init(useSmallId, level);
|
|
MemSect(0);
|
|
|
|
this->level = level;
|
|
// resetPos();
|
|
setPos(0, 0, 0);
|
|
|
|
if (level != nullptr)
|
|
{
|
|
dimension = level->dimension->id;
|
|
}
|
|
|
|
if( entityData )
|
|
{
|
|
entityData->define(DATA_SHARED_FLAGS_ID, static_cast<byte>(0));
|
|
entityData->define(DATA_AIR_SUPPLY_ID, TOTAL_AIR_SUPPLY); // 4J Stu - Brought forward from 1.2.3 to fix 38654 - Gameplay: Player will take damage when air bubbles are present if resuming game from load/autosave underwater.
|
|
}
|
|
|
|
// 4J Stu - We cannot call virtual functions in ctors, as at this point the object
|
|
// is of type Entity and not a derived class
|
|
//this->defineSynchedData();
|
|
}
|
|
|
|
Entity::~Entity()
|
|
{
|
|
freeSmallId(entityId);
|
|
delete random;
|
|
delete bb;
|
|
}
|
|
|
|
shared_ptr<SynchedEntityData> Entity::getEntityData()
|
|
{
|
|
return entityData;
|
|
}
|
|
|
|
/*
|
|
public bool equals(Object obj) {
|
|
if (obj instanceof Entity) {
|
|
return ((Entity) obj).entityId == entityId;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public int hashCode() {
|
|
return entityId;
|
|
}
|
|
*/
|
|
|
|
void Entity::resetPos()
|
|
{
|
|
if (level == nullptr) return;
|
|
|
|
shared_ptr<Entity> sharedThis = shared_from_this();
|
|
while (true && y > 0)
|
|
{
|
|
setPos(x, y, z);
|
|
if (level->getCubes(sharedThis, bb)->empty()) break;
|
|
y += 1;
|
|
}
|
|
|
|
xd = yd = zd = 0;
|
|
xRot = 0;
|
|
}
|
|
|
|
void Entity::remove()
|
|
{
|
|
removed = true;
|
|
}
|
|
|
|
|
|
void Entity::setSize(float w, float h)
|
|
{
|
|
if (w != bbWidth || h != bbHeight)
|
|
{
|
|
float oldW = bbWidth;
|
|
|
|
bbWidth = w;
|
|
bbHeight = h;
|
|
|
|
bb->x1 = bb->x0 + bbWidth;
|
|
bb->z1 = bb->z0 + bbWidth;
|
|
bb->y1 = bb->y0 + bbHeight;
|
|
|
|
if (bbWidth > oldW && !firstTick && !level->isClientSide)
|
|
{
|
|
move(oldW - bbWidth, 0, oldW - bbWidth);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Entity::setPos(EntityPos *pos)
|
|
{
|
|
if (pos->move) setPos(pos->x, pos->y, pos->z);
|
|
else setPos(x, y, z);
|
|
|
|
if (pos->rot) setRot(pos->yRot, pos->xRot);
|
|
else setRot(yRot, xRot);
|
|
}
|
|
|
|
void Entity::setRot(float yRot, float xRot)
|
|
{
|
|
/* JAVA:
|
|
this->yRot = yRot % 360.0f;
|
|
this->xRot = xRot % 360.0f;
|
|
|
|
C++ Cannot do mod of non-integral type
|
|
*/
|
|
|
|
while( yRot >= 360.0f )
|
|
yRot -= 360.0f;
|
|
while( yRot < 0 )
|
|
yRot += 360.0f;
|
|
while( xRot >= 360.0f )
|
|
xRot -= 360.0f;
|
|
|
|
this->yRot = yRot;
|
|
this->xRot = xRot;
|
|
}
|
|
|
|
|
|
void Entity::setPos(double x, double y, double z)
|
|
{
|
|
this->x = x;
|
|
this->y = y;
|
|
this->z = z;
|
|
float w = bbWidth / 2;
|
|
float h = bbHeight;
|
|
bb->set(x - w, y - heightOffset + ySlideOffset, z - w, x + w, y - heightOffset + ySlideOffset + h, z + w);
|
|
}
|
|
|
|
void Entity::turn(float xo, float yo)
|
|
{
|
|
float xRotOld = xRot;
|
|
float yRotOld = yRot;
|
|
|
|
yRot += xo * 0.15f;
|
|
xRot -= yo * 0.15f;
|
|
if (xRot < -90) xRot = -90;
|
|
if (xRot > 90) xRot = 90;
|
|
|
|
xRotO += xRot - xRotOld;
|
|
yRotO += yRot - yRotOld;
|
|
}
|
|
|
|
void Entity::interpolateTurn(float xo, float yo)
|
|
{
|
|
yRot += xo * 0.15f;
|
|
xRot -= yo * 0.15f;
|
|
if (xRot < -90) xRot = -90;
|
|
if (xRot > 90) xRot = 90;
|
|
}
|
|
|
|
void Entity::tick()
|
|
{
|
|
baseTick();
|
|
}
|
|
|
|
void Entity::baseTick()
|
|
{
|
|
// 4J Stu - Not needed
|
|
//util.Timer.push("entityBaseTick");
|
|
|
|
if (riding != nullptr && riding->removed)
|
|
{
|
|
riding = nullptr;
|
|
}
|
|
|
|
walkDistO = walkDist;
|
|
xo = x;
|
|
yo = y;
|
|
zo = z;
|
|
xRotO = xRot;
|
|
yRotO = yRot;
|
|
|
|
if (!level->isClientSide) // 4J Stu - Don't need this && level instanceof ServerLevel)
|
|
{
|
|
if(!m_ignorePortal) // 4J Added
|
|
{
|
|
MinecraftServer *server = dynamic_cast<ServerLevel *>(level)->getServer();
|
|
int waitTime = getPortalWaitTime();
|
|
|
|
if (isInsidePortal)
|
|
{
|
|
if (server->isNetherEnabled())
|
|
{
|
|
if (riding == nullptr)
|
|
{
|
|
if (portalTime++ >= waitTime)
|
|
{
|
|
portalTime = waitTime;
|
|
changingDimensionDelay = getDimensionChangingDelay();
|
|
|
|
int targetDimension;
|
|
|
|
if (level->dimension->id == -1)
|
|
{
|
|
targetDimension = 0;
|
|
}
|
|
else
|
|
{
|
|
targetDimension = -1;
|
|
}
|
|
|
|
changeDimension(targetDimension);
|
|
}
|
|
}
|
|
isInsidePortal = false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (portalTime > 0) portalTime -= 4;
|
|
if (portalTime < 0) portalTime = 0;
|
|
}
|
|
if (changingDimensionDelay > 0) changingDimensionDelay--;
|
|
}
|
|
}
|
|
|
|
if (isSprinting() && !isInWater() && canCreateParticles())
|
|
{
|
|
int xt = Mth::floor(x);
|
|
int yt = Mth::floor(y - 0.2f - heightOffset);
|
|
int zt = Mth::floor(z);
|
|
int t = level->getTile(xt, yt, zt);
|
|
int d = level->getData(xt, yt, zt);
|
|
if (t > 0)
|
|
{
|
|
level->addParticle(PARTICLE_TILECRACK(t,d), x + (random->nextFloat() - 0.5) * bbWidth, bb->y0 + 0.1, z + (random->nextFloat() - 0.5) * bbWidth, -xd * 4, 1.5, -zd * 4);
|
|
}
|
|
}
|
|
|
|
updateInWaterState();
|
|
|
|
if (level->isClientSide)
|
|
{
|
|
onFire = 0;
|
|
}
|
|
else
|
|
{
|
|
if (onFire > 0)
|
|
{
|
|
if (fireImmune)
|
|
{
|
|
onFire -= 4;
|
|
if (onFire < 0) onFire = 0;
|
|
}
|
|
else
|
|
{
|
|
if (onFire % 20 == 0)
|
|
{
|
|
hurt(DamageSource::onFire, 1);
|
|
}
|
|
onFire--;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (isInLava())
|
|
{
|
|
lavaHurt();
|
|
fallDistance *= .5f;
|
|
}
|
|
|
|
if (y < -64)
|
|
{
|
|
outOfWorld();
|
|
}
|
|
|
|
if (!level->isClientSide)
|
|
{
|
|
setSharedFlag(FLAG_ONFIRE, onFire > 0);
|
|
}
|
|
|
|
firstTick = false;
|
|
|
|
// 4J Stu - Unused
|
|
//util.Timer.pop();
|
|
}
|
|
|
|
int Entity::getPortalWaitTime()
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
void Entity::lavaHurt()
|
|
{
|
|
if (fireImmune)
|
|
{
|
|
}
|
|
else
|
|
{
|
|
hurt(DamageSource::lava, 4);
|
|
setOnFire(15);
|
|
}
|
|
}
|
|
|
|
void Entity::setOnFire(int numberOfSeconds)
|
|
{
|
|
int newValue = numberOfSeconds * SharedConstants::TICKS_PER_SECOND;
|
|
newValue = ProtectionEnchantment::getFireAfterDampener(shared_from_this(), newValue);
|
|
if (onFire < newValue)
|
|
{
|
|
onFire = newValue;
|
|
}
|
|
}
|
|
|
|
void Entity::clearFire()
|
|
{
|
|
onFire = 0;
|
|
}
|
|
|
|
void Entity::outOfWorld()
|
|
{
|
|
remove();
|
|
}
|
|
|
|
|
|
bool Entity::isFree(float xa, float ya, float za, float grow)
|
|
{
|
|
AABB *box = bb->grow(grow, grow, grow)->cloneMove(xa, ya, za);
|
|
AABBList *aABBs = level->getCubes(shared_from_this(), box);
|
|
if (!aABBs->empty()) return false;
|
|
if (level->containsAnyLiquid(box)) return false;
|
|
return true;
|
|
}
|
|
|
|
bool Entity::isFree(double xa, double ya, double za)
|
|
{
|
|
AABB *box = bb->cloneMove(xa, ya, za);
|
|
AABBList *aABBs = level->getCubes(shared_from_this(), box);
|
|
if (!aABBs->empty()) return false;
|
|
if (level->containsAnyLiquid(box)) return false;
|
|
return true;
|
|
}
|
|
|
|
void Entity::move(double xa, double ya, double za, bool noEntityCubes) // 4J - added noEntityCubes parameter
|
|
{
|
|
if (noPhysics)
|
|
{
|
|
bb->move(xa, ya, za);
|
|
x = (bb->x0 + bb->x1) / 2.0f;
|
|
y = bb->y0 + heightOffset - ySlideOffset;
|
|
z = (bb->z0 + bb->z1) / 2.0f;
|
|
return;
|
|
}
|
|
|
|
ySlideOffset *= 0.4f;
|
|
|
|
double xo = x;
|
|
double yo = y;
|
|
double zo = z;
|
|
|
|
if (isStuckInWeb)
|
|
{
|
|
isStuckInWeb = false;
|
|
|
|
xa *= 0.25f;
|
|
ya *= 0.05f;
|
|
za *= 0.25f;
|
|
xd = 0.0f;
|
|
yd = 0.0f;
|
|
zd = 0.0f;
|
|
}
|
|
|
|
double xaOrg = xa;
|
|
double yaOrg = ya;
|
|
double zaOrg = za;
|
|
|
|
AABB *bbOrg = bb->copy();
|
|
|
|
bool isPlayerSneaking = onGround && isSneaking() && instanceof(eTYPE_PLAYER);
|
|
|
|
if (isPlayerSneaking)
|
|
{
|
|
double d = 0.05;
|
|
while (xa != 0 && level->getCubes(shared_from_this(), bb->cloneMove(xa, -1.0, 0))->empty())
|
|
{
|
|
if (xa < d && xa >= -d) xa = 0;
|
|
else if (xa > 0) xa -= d;
|
|
else xa += d;
|
|
xaOrg = xa;
|
|
}
|
|
while (za != 0 && level->getCubes(shared_from_this(), bb->cloneMove(0, -1.0, za))->empty())
|
|
{
|
|
if (za < d && za >= -d) za = 0;
|
|
else if (za > 0) za -= d;
|
|
else za += d;
|
|
zaOrg = za;
|
|
}
|
|
while (xa != 0 && za != 0 && level->getCubes(shared_from_this(), bb->cloneMove(xa, -1.0, za))->empty())
|
|
{
|
|
if (xa < d && xa >= -d) xa = 0;
|
|
else if (xa > 0) xa -= d;
|
|
else xa += d;
|
|
if (za < d && za >= -d) za = 0;
|
|
else if (za > 0) za -= d;
|
|
else za += d;
|
|
xaOrg = xa;
|
|
zaOrg = za;
|
|
}
|
|
}
|
|
|
|
AABBList *aABBs = level->getCubes(shared_from_this(), bb->expand(xa, ya, za), noEntityCubes, true);
|
|
|
|
|
|
// 4J Stu - Particles (and possibly other entities) don't have xChunk and zChunk set, so calculate the chunk instead
|
|
int xc = Mth::floor(x / 16);
|
|
int zc = Mth::floor(z / 16);
|
|
if(!level->isClientSide || level->reallyHasChunk(xc, zc))
|
|
{
|
|
// 4J Stu - It's horrible that the client is doing any movement at all! But if we don't have the chunk
|
|
// data then all the collision info will be incorrect as well
|
|
for ( auto& it : *aABBs )
|
|
ya = it->clipYCollide(bb, ya);
|
|
bb->move(0, ya, 0);
|
|
}
|
|
|
|
if (!slide && yaOrg != ya)
|
|
{
|
|
xa = ya = za = 0;
|
|
}
|
|
|
|
bool og = onGround || (yaOrg != ya && yaOrg < 0);
|
|
|
|
for ( auto& it : *aABBs )
|
|
xa = it->clipXCollide(bb, xa);
|
|
|
|
bb->move(xa, 0, 0);
|
|
|
|
if (!slide && xaOrg != xa)
|
|
{
|
|
xa = ya = za = 0;
|
|
}
|
|
|
|
for ( auto& it : *aABBs )
|
|
za = it->clipZCollide(bb, za);
|
|
bb->move(0, 0, za);
|
|
|
|
if (!slide && zaOrg != za)
|
|
{
|
|
xa = ya = za = 0;
|
|
}
|
|
|
|
if (footSize > 0 && og && (isPlayerSneaking || ySlideOffset < 0.05f) && ((xaOrg != xa) || (zaOrg != za)))
|
|
{
|
|
double xaN = xa;
|
|
double yaN = ya;
|
|
double zaN = za;
|
|
|
|
xa = xaOrg;
|
|
ya = footSize;
|
|
za = zaOrg;
|
|
|
|
AABB *normal = bb->copy();
|
|
bb->set(bbOrg);
|
|
// 4J - added extra expand, as if we don't move up by footSize by hitting a block above us, then overall we could be trying to move as much as footSize downwards,
|
|
// so we'd better include cubes under our feet in this list of things we might possibly collide with
|
|
aABBs = level->getCubes(shared_from_this(), bb->expand(xa, ya, za)->expand(0,-ya,0),false,true);
|
|
|
|
if(!level->isClientSide || level->reallyHasChunk(xc, zc))
|
|
{
|
|
// 4J Stu - It's horrible that the client is doing any movement at all! But if we don't have the chunk
|
|
// data then all the collision info will be incorrect as well
|
|
for ( auto& it : *aABBs )
|
|
ya = it->clipYCollide(bb, ya);
|
|
bb->move(0, ya, 0);
|
|
}
|
|
|
|
if (!slide && yaOrg != ya)
|
|
{
|
|
xa = ya = za = 0;
|
|
}
|
|
|
|
|
|
for ( auto& it : *aABBs )
|
|
xa = it->clipXCollide(bb, xa);
|
|
bb->move(xa, 0, 0);
|
|
|
|
if (!slide && xaOrg != xa)
|
|
{
|
|
xa = ya = za = 0;
|
|
}
|
|
|
|
for ( auto& it : *aABBs )
|
|
za = it->clipZCollide(bb, za);
|
|
bb->move(0, 0, za);
|
|
|
|
if (!slide && zaOrg != za)
|
|
{
|
|
xa = ya = za = 0;
|
|
}
|
|
|
|
|
|
if (!slide && yaOrg != ya)
|
|
{
|
|
xa = ya = za = 0;
|
|
}
|
|
else
|
|
{
|
|
ya = -footSize;
|
|
// LAND FIRST, then x and z
|
|
for ( auto& it : *aABBs )
|
|
ya = it->clipYCollide(bb, ya);
|
|
bb->move(0, ya, 0);
|
|
}
|
|
|
|
if (xaN * xaN + zaN * zaN >= xa * xa + za * za)
|
|
{
|
|
xa = xaN;
|
|
ya = yaN;
|
|
za = zaN;
|
|
bb->set(normal);
|
|
}
|
|
}
|
|
|
|
|
|
x = (bb->x0 + bb->x1) / 2.0f;
|
|
y = bb->y0 + heightOffset - ySlideOffset;
|
|
z = (bb->z0 + bb->z1) / 2.0f;
|
|
|
|
horizontalCollision = (xaOrg != xa) || (zaOrg != za);
|
|
verticalCollision = !m_ignoreVerticalCollisions && (yaOrg != ya);
|
|
onGround = !m_ignoreVerticalCollisions && yaOrg != ya && yaOrg < 0;
|
|
collision = horizontalCollision || verticalCollision;
|
|
checkFallDamage(ya, onGround);
|
|
|
|
if (xaOrg != xa) xd = 0;
|
|
if (yaOrg != ya) yd = 0;
|
|
if (zaOrg != za) zd = 0;
|
|
|
|
double xm = x - xo;
|
|
double ym = y - yo;
|
|
double zm = z - zo;
|
|
|
|
|
|
if (makeStepSound() && !isPlayerSneaking && riding == nullptr)
|
|
{
|
|
int xt = Mth::floor(x);
|
|
int yt = Mth::floor(y - 0.2f - heightOffset);
|
|
int zt = Mth::floor(z);
|
|
int t = level->getTile(xt, yt, zt);
|
|
if (t == 0)
|
|
{
|
|
int renderShape = level->getTileRenderShape(xt, yt - 1, zt);
|
|
if (renderShape == Tile::SHAPE_FENCE || renderShape == Tile::SHAPE_WALL || renderShape == Tile::SHAPE_FENCE_GATE)
|
|
{
|
|
t = level->getTile(xt, yt - 1, zt);
|
|
}
|
|
}
|
|
if (t != Tile::ladder_Id)
|
|
{
|
|
ym = 0;
|
|
}
|
|
|
|
walkDist += Mth::sqrt(xm * xm + zm * zm) * 0.6;
|
|
moveDist += Mth::sqrt(xm * xm + ym * ym + zm * zm) * 0.6;
|
|
|
|
if (moveDist > nextStep && t > 0)
|
|
{
|
|
nextStep = static_cast<int>(moveDist) + 1;
|
|
if (isInWater())
|
|
{
|
|
float speed = Mth::sqrt(xd * xd * 0.2f + yd * yd + zd * zd * 0.2f) * 0.35f;
|
|
if (speed > 1) speed = 1;
|
|
playSound(eSoundType_LIQUID_SWIM, speed, 1 + (random->nextFloat() - random->nextFloat()) * 0.4f);
|
|
}
|
|
playStepSound(xt, yt, zt, t);
|
|
Tile::tiles[t]->stepOn(level, xt, yt, zt, shared_from_this());
|
|
}
|
|
}
|
|
|
|
checkInsideTiles();
|
|
|
|
|
|
bool water = isInWaterOrRain();
|
|
if (level->containsFireTile(bb->shrink(0.001, 0.001, 0.001)))
|
|
{
|
|
burn(1);
|
|
if (!water)
|
|
{
|
|
onFire++;
|
|
if (onFire == 0) setOnFire(8);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (onFire <= 0)
|
|
{
|
|
onFire = -flameTime;
|
|
}
|
|
}
|
|
|
|
if (water && onFire > 0)
|
|
{
|
|
playSound(eSoundType_RANDOM_FIZZ, 0.7f, 1.6f + (random->nextFloat() - random->nextFloat()) * 0.4f);
|
|
onFire = -flameTime;
|
|
}
|
|
}
|
|
|
|
void Entity::checkInsideTiles()
|
|
{
|
|
int x0 = Mth::floor(bb->x0 + 0.001);
|
|
int y0 = Mth::floor(bb->y0 + 0.001);
|
|
int z0 = Mth::floor(bb->z0 + 0.001);
|
|
int x1 = Mth::floor(bb->x1 - 0.001);
|
|
int y1 = Mth::floor(bb->y1 - 0.001);
|
|
int z1 = Mth::floor(bb->z1 - 0.001);
|
|
|
|
if (level->hasChunksAt(x0, y0, z0, x1, y1, z1))
|
|
{
|
|
for (int x = x0; x <= x1; x++)
|
|
for (int y = y0; y <= y1; y++)
|
|
for (int z = z0; z <= z1; z++)
|
|
{
|
|
int t = level->getTile(x, y, z);
|
|
if (t > 0)
|
|
{
|
|
Tile::tiles[t]->entityInside(level, x, y, z, shared_from_this());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void Entity::playStepSound(int xt, int yt, int zt, int t)
|
|
{
|
|
const Tile::SoundType *soundType = Tile::tiles[t]->soundType;
|
|
MemSect(31);
|
|
|
|
if (GetType() == eTYPE_PLAYER)
|
|
{
|
|
// should we turn off step sounds?
|
|
unsigned int uiAnimOverrideBitmask=getAnimOverrideBitmask(); // this is masked for custom anim off, and force anim
|
|
|
|
if(( uiAnimOverrideBitmask& (1<<HumanoidModel::eAnim_NoLegAnim))!=0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
}
|
|
if (level->getTile(xt, yt + 1, zt) == Tile::topSnow_Id)
|
|
{
|
|
soundType = Tile::topSnow->soundType;
|
|
playSound(soundType->getStepSound(), soundType->getVolume() * 0.15f, soundType->getPitch());
|
|
}
|
|
else if (!Tile::tiles[t]->material->isLiquid())
|
|
{
|
|
playSound(soundType->getStepSound(), soundType->getVolume() * 0.15f, soundType->getPitch());
|
|
}
|
|
|
|
MemSect(0);
|
|
}
|
|
|
|
void Entity::playSound(int iSound, float volume, float pitch)
|
|
{
|
|
level->playEntitySound(shared_from_this(), iSound, volume, pitch);
|
|
}
|
|
|
|
bool Entity::makeStepSound()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
void Entity::checkFallDamage(double ya, bool onGround)
|
|
{
|
|
if (onGround)
|
|
{
|
|
if (fallDistance > 0)
|
|
{
|
|
causeFallDamage(fallDistance);
|
|
fallDistance = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (ya < 0) fallDistance -= static_cast<float>(ya);
|
|
}
|
|
}
|
|
|
|
AABB *Entity::getCollideBox()
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
void Entity::burn(int dmg)
|
|
{
|
|
if (!fireImmune)
|
|
{
|
|
hurt(DamageSource::inFire, dmg);
|
|
}
|
|
}
|
|
|
|
bool Entity::isFireImmune()
|
|
{
|
|
return fireImmune;
|
|
}
|
|
|
|
void Entity::causeFallDamage(float distance)
|
|
{
|
|
if (rider.lock() != nullptr) rider.lock()->causeFallDamage(distance);
|
|
}
|
|
|
|
|
|
bool Entity::isInWaterOrRain()
|
|
{
|
|
return wasInWater || (level->isRainingAt( Mth::floor(x), Mth::floor(y), Mth::floor(z)) || level->isRainingAt(Mth::floor(x), Mth::floor(y + bbHeight), Mth::floor(z)));
|
|
}
|
|
|
|
bool Entity::isInWater()
|
|
{
|
|
return wasInWater;
|
|
}
|
|
|
|
bool Entity::updateInWaterState()
|
|
{
|
|
if(level->checkAndHandleWater(bb->grow(0, -0.4f, 0)->shrink(0.001, 0.001, 0.001), Material::water, shared_from_this()))
|
|
{
|
|
if (!wasInWater && !firstTick && canCreateParticles())
|
|
{
|
|
float speed = Mth::sqrt(xd * xd * 0.2f + yd * yd + zd * zd * 0.2f) * 0.2f;
|
|
if (speed > 1) speed = 1;
|
|
MemSect(31);
|
|
playSound(eSoundType_RANDOM_SPLASH, speed, 1 + (random->nextFloat() - random->nextFloat()) * 0.4f);
|
|
MemSect(0);
|
|
float yt = static_cast<float>(Mth::floor(bb->y0));
|
|
for (int i = 0; i < 1 + bbWidth * 20; i++)
|
|
{
|
|
float xo = (random->nextFloat() * 2 - 1) * bbWidth;
|
|
float zo = (random->nextFloat() * 2 - 1) * bbWidth;
|
|
level->addParticle(eParticleType_bubble, x + xo, yt + 1, z + zo, xd, yd - random->nextFloat() * 0.2f, zd);
|
|
}
|
|
for (int i = 0; i < 1 + bbWidth * 20; i++)
|
|
{
|
|
float xo = (random->nextFloat() * 2 - 1) * bbWidth;
|
|
float zo = (random->nextFloat() * 2 - 1) * bbWidth;
|
|
level->addParticle(eParticleType_splash, x + xo, yt + 1, z + zo, xd, yd, zd);
|
|
}
|
|
}
|
|
fallDistance = 0;
|
|
wasInWater = true;
|
|
onFire = 0;
|
|
}
|
|
else
|
|
{
|
|
wasInWater = false;
|
|
}
|
|
return wasInWater;
|
|
}
|
|
|
|
bool Entity::isUnderLiquid(Material *material)
|
|
{
|
|
double yp = y + getHeadHeight();
|
|
int xt = Mth::floor(x);
|
|
int yt = Mth::floor(yp); // 4J - this used to be a nested pair of floors for some reason
|
|
int zt = Mth::floor(z);
|
|
int t = level->getTile(xt, yt, zt);
|
|
if (t != 0 && Tile::tiles[t]->material == material) {
|
|
float hh = LiquidTile::getHeight(level->getData(xt, yt, zt)) - 1 / 9.0f;
|
|
float h = yt + 1 - hh;
|
|
return yp < h;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
float Entity::getHeadHeight()
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
bool Entity::isInLava()
|
|
{
|
|
return level->containsMaterial(bb->grow(-0.1f, -0.4f, -0.1f), Material::lava);
|
|
}
|
|
|
|
void Entity::moveRelative(float xa, float za, float speed)
|
|
{
|
|
float dist = xa * xa + za * za;
|
|
if (dist < 0.01f * 0.01f) return;
|
|
|
|
dist = sqrt(dist);
|
|
if (dist < 1) dist = 1;
|
|
dist = speed / dist;
|
|
xa *= dist;
|
|
za *= dist;
|
|
|
|
float sinVar = Mth::sin(yRot * PI / 180);
|
|
float cosVar = Mth::cos(yRot * PI / 180);
|
|
|
|
xd += xa * cosVar - za * sinVar;
|
|
zd += za * cosVar + xa * sinVar;
|
|
}
|
|
|
|
// 4J - change brought forward from 1.8.2
|
|
int Entity::getLightColor(float a)
|
|
{
|
|
int xTile = Mth::floor(x);
|
|
int zTile = Mth::floor(z);
|
|
|
|
if (level->hasChunkAt(xTile, 0, zTile))
|
|
{
|
|
double hh = (bb->y1 - bb->y0) * 0.66;
|
|
int yTile = Mth::floor(y - heightOffset + hh);
|
|
return level->getLightColor(xTile, yTile, zTile, 0);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// 4J - changes brought forward from 1.8.2
|
|
float Entity::getBrightness(float a)
|
|
{
|
|
int xTile = Mth::floor(x);
|
|
int zTile = Mth::floor(z);
|
|
if (level->hasChunkAt(xTile, 0, zTile))
|
|
{
|
|
double hh = (bb->y1 - bb->y0) * 0.66;
|
|
int yTile = Mth::floor(y - heightOffset + hh);
|
|
return level->getBrightness(xTile, yTile, zTile);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void Entity::setLevel(Level *level)
|
|
{
|
|
this->level = level;
|
|
}
|
|
|
|
void Entity::absMoveTo(double x, double y, double z, float yRot, float xRot)
|
|
{
|
|
xo = this->x = x;
|
|
yo = this->y = y;
|
|
zo = this->z = z;
|
|
yRotO = this->yRot = yRot;
|
|
xRotO = this->xRot = xRot;
|
|
ySlideOffset = 0;
|
|
|
|
double yRotDiff = yRotO - yRot;
|
|
if (yRotDiff < -180) yRotO += 360;
|
|
if (yRotDiff >= 180) yRotO -= 360;
|
|
setPos(this->x, this->y, this->z);
|
|
setRot(yRot, xRot);
|
|
}
|
|
|
|
void Entity::moveTo(double x, double y, double z, float yRot, float xRot)
|
|
{
|
|
xOld = xo = this->x = x;
|
|
yOld = yo = this->y = y + heightOffset;
|
|
zOld = zo = this->z = z;
|
|
this->yRot = yRot;
|
|
this->xRot = xRot;
|
|
setPos(this->x, this->y, this->z);
|
|
}
|
|
|
|
float Entity::distanceTo(shared_ptr<Entity> e)
|
|
{
|
|
float xd = static_cast<float>(x - e->x);
|
|
float yd = static_cast<float>(y - e->y);
|
|
float zd = static_cast<float>(z - e->z);
|
|
return sqrt(xd * xd + yd * yd + zd * zd);
|
|
}
|
|
|
|
double Entity::distanceToSqr(double x2, double y2, double z2)
|
|
{
|
|
double xd = (x - x2);
|
|
double yd = (y - y2);
|
|
double zd = (z - z2);
|
|
return xd * xd + yd * yd + zd * zd;
|
|
}
|
|
|
|
double Entity::distanceTo(double x2, double y2, double z2)
|
|
{
|
|
double xd = (x - x2);
|
|
double yd = (y - y2);
|
|
double zd = (z - z2);
|
|
return sqrt(xd * xd + yd * yd + zd * zd);
|
|
}
|
|
|
|
double Entity::distanceToSqr(shared_ptr<Entity> e)
|
|
{
|
|
double xd = x - e->x;
|
|
double yd = y - e->y;
|
|
double zd = z - e->z;
|
|
return xd * xd + yd * yd + zd * zd;
|
|
}
|
|
|
|
void Entity::playerTouch(shared_ptr<Player> player)
|
|
{
|
|
}
|
|
|
|
void Entity::push(shared_ptr<Entity> e)
|
|
{
|
|
if (e->rider.lock().get() == this || e->riding.get() == this) return;
|
|
|
|
double xa = e->x - x;
|
|
double za = e->z - z;
|
|
|
|
double dd = Mth::asbMax(xa, za);
|
|
|
|
if (dd >= 0.01f)
|
|
{
|
|
dd = sqrt(dd);
|
|
xa /= dd;
|
|
za /= dd;
|
|
|
|
double pow = 1 / dd;
|
|
if (pow > 1) pow = 1;
|
|
xa *= pow;
|
|
za *= pow;
|
|
|
|
xa *= 0.05f;
|
|
za *= 0.05f;
|
|
|
|
xa *= 1 - pushthrough;
|
|
za *= 1 - pushthrough;
|
|
|
|
push(-xa, 0, -za);
|
|
e->push(xa, 0, za);
|
|
}
|
|
}
|
|
|
|
void Entity::push(double xa, double ya, double za)
|
|
{
|
|
xd += xa;
|
|
yd += ya;
|
|
zd += za;
|
|
hasImpulse = true;
|
|
}
|
|
|
|
void Entity::markHurt()
|
|
{
|
|
hurtMarked = true;
|
|
}
|
|
|
|
bool Entity::hurt(DamageSource *source, float damage)
|
|
{
|
|
if(isInvulnerable()) return false;
|
|
markHurt();
|
|
return false;
|
|
}
|
|
|
|
bool Entity::intersects(double x0, double y0, double z0, double x1, double y1, double z1)
|
|
{
|
|
return bb->intersects(x0, y0, z0, x1, y1, z1);
|
|
}
|
|
|
|
bool Entity::isPickable()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool Entity::isPushable()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool Entity::isShootable()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
void Entity::awardKillScore(shared_ptr<Entity> victim, int score)
|
|
{
|
|
}
|
|
|
|
bool Entity::shouldRender(Vec3 *c)
|
|
{
|
|
double xd = x - c->x;
|
|
double yd = y - c->y;
|
|
double zd = z - c->z;
|
|
double distance = xd * xd + yd * yd + zd * zd;
|
|
return shouldRenderAtSqrDistance(distance);
|
|
}
|
|
|
|
bool Entity::shouldRenderAtSqrDistance(double distance)
|
|
{
|
|
double size = bb->getSize();
|
|
size *= 64.0f * viewScale;
|
|
return distance < size * size;
|
|
}
|
|
|
|
bool Entity::isCreativeModeAllowed()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool Entity::saveAsMount(CompoundTag *entityTag)
|
|
{
|
|
wstring id = getEncodeId();
|
|
if (removed || id.empty() )
|
|
{
|
|
return false;
|
|
}
|
|
// TODO Is this fine to be casting to a non-const char pointer?
|
|
entityTag->putString(L"id", id );
|
|
saveWithoutId(entityTag);
|
|
return true;
|
|
}
|
|
|
|
bool Entity::save(CompoundTag *entityTag)
|
|
{
|
|
wstring id = getEncodeId();
|
|
if (removed || id.empty() || (rider.lock() != nullptr) )
|
|
{
|
|
return false;
|
|
}
|
|
// TODO Is this fine to be casting to a non-const char pointer?
|
|
entityTag->putString(L"id", id );
|
|
saveWithoutId(entityTag);
|
|
return true;
|
|
}
|
|
|
|
void Entity::saveWithoutId(CompoundTag *entityTag)
|
|
{
|
|
entityTag->put(L"Pos", newDoubleList(3, x, y + ySlideOffset, z));
|
|
entityTag->put(L"Motion", newDoubleList(3, xd, yd, zd));
|
|
entityTag->put(L"Rotation", newFloatList(2, yRot, xRot));
|
|
|
|
entityTag->putFloat(L"FallDistance", fallDistance);
|
|
entityTag->putShort(L"Fire", static_cast<short>(onFire));
|
|
entityTag->putShort(L"Air", static_cast<short>(getAirSupply()));
|
|
entityTag->putBoolean(L"OnGround", onGround);
|
|
entityTag->putInt(L"Dimension", dimension);
|
|
entityTag->putBoolean(L"Invulnerable", invulnerable);
|
|
entityTag->putInt(L"PortalCooldown", changingDimensionDelay);
|
|
|
|
entityTag->putString(L"UUID", uuid);
|
|
|
|
addAdditonalSaveData(entityTag);
|
|
|
|
if (riding != nullptr)
|
|
{
|
|
CompoundTag *ridingTag = new CompoundTag(RIDING_TAG);
|
|
if (riding->saveAsMount(ridingTag))
|
|
{
|
|
entityTag->put(L"Riding", ridingTag);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Entity::load(CompoundTag *tag)
|
|
{
|
|
ListTag<DoubleTag> *pos = (ListTag<DoubleTag> *) tag->getList(L"Pos");
|
|
ListTag<DoubleTag> *motion = (ListTag<DoubleTag> *) tag->getList(L"Motion");
|
|
ListTag<FloatTag> *rotation = (ListTag<FloatTag> *) tag->getList(L"Rotation");
|
|
|
|
xd = motion->get(0)->data;
|
|
yd = motion->get(1)->data;
|
|
zd = motion->get(2)->data;
|
|
|
|
if (abs(xd) > 10.0)
|
|
{
|
|
xd = 0;
|
|
}
|
|
if (abs(yd) > 10.0)
|
|
{
|
|
yd = 0;
|
|
}
|
|
if (abs(zd) > 10.0)
|
|
{
|
|
zd = 0;
|
|
}
|
|
|
|
xo = xOld = x = pos->get(0)->data;
|
|
yo = yOld = y = pos->get(1)->data;
|
|
zo = zOld = z = pos->get(2)->data;
|
|
|
|
yRotO = yRot = rotation->get(0)->data;
|
|
xRotO = xRot = rotation->get(1)->data;
|
|
|
|
fallDistance = tag->getFloat(L"FallDistance");
|
|
onFire = tag->getShort(L"Fire");
|
|
setAirSupply(tag->getShort(L"Air"));
|
|
onGround = tag->getBoolean(L"OnGround");
|
|
dimension = tag->getInt(L"Dimension");
|
|
invulnerable = tag->getBoolean(L"Invulnerable");
|
|
changingDimensionDelay = tag->getInt(L"PortalCooldown");
|
|
|
|
if (tag->contains(L"UUID"))
|
|
{
|
|
uuid = tag->getString(L"UUID");
|
|
}
|
|
|
|
setPos(x, y, z);
|
|
setRot(yRot, xRot);
|
|
|
|
readAdditionalSaveData(tag);
|
|
|
|
// set position again because bb size may have changed
|
|
if (repositionEntityAfterLoad()) setPos(x, y, z);
|
|
}
|
|
|
|
bool Entity::repositionEntityAfterLoad()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
const wstring Entity::getEncodeId()
|
|
{
|
|
return EntityIO::getEncodeId( shared_from_this() );
|
|
}
|
|
|
|
/**
|
|
* Called after load() has finished and the entity has been added to the
|
|
* world
|
|
*/
|
|
void Entity::onLoadedFromSave()
|
|
{
|
|
|
|
}
|
|
|
|
template<typename ...Args>
|
|
ListTag<DoubleTag> *Entity::newDoubleList(unsigned int, double firstValue, Args... args)
|
|
{
|
|
ListTag<DoubleTag> *res = new ListTag<DoubleTag>();
|
|
|
|
// Add the first parameter to the ListTag
|
|
res->add( new DoubleTag(L"", firstValue ) );
|
|
|
|
// use pre-C++17 fold trick (TODO: once we drop C++14 support, use C++14 fold expression)
|
|
using expander = int[];
|
|
(void)expander{0, (res->add(new DoubleTag(L"", static_cast<double>(args))), 0)...};
|
|
|
|
return res;
|
|
}
|
|
|
|
ListTag<FloatTag> *Entity::newFloatList(unsigned int number, float firstValue, float secondValue)
|
|
{
|
|
ListTag<FloatTag> *res = new ListTag<FloatTag>();
|
|
|
|
// Add the first parameter to the ListTag
|
|
res->add( new FloatTag( L"", firstValue ) );
|
|
|
|
// TODO - 4J Stu For some reason the va_list wasn't working correctly here
|
|
// We only make a list of two floats so just overriding and not using va_list
|
|
res->add( new FloatTag( L"", secondValue ) );
|
|
|
|
/*
|
|
va_list vl;
|
|
va_start(vl,firstValue);
|
|
|
|
float val;
|
|
|
|
for (unsigned int i = 1; i < number; i++)
|
|
{
|
|
val = va_arg(vl,float);
|
|
res->add(new FloatTag(val));
|
|
}
|
|
va_end(vl);
|
|
*/
|
|
return res;
|
|
}
|
|
|
|
float Entity::getShadowHeightOffs()
|
|
{
|
|
return bbHeight / 2;
|
|
}
|
|
|
|
shared_ptr<ItemEntity> Entity::spawnAtLocation(int resource, int count)
|
|
{
|
|
return spawnAtLocation(resource, count, 0);
|
|
}
|
|
|
|
shared_ptr<ItemEntity> Entity::spawnAtLocation(int resource, int count, float yOffs)
|
|
{
|
|
return spawnAtLocation(std::make_shared<ItemInstance>(resource, count, 0), yOffs);
|
|
}
|
|
|
|
shared_ptr<ItemEntity> Entity::spawnAtLocation(shared_ptr<ItemInstance> itemInstance, float yOffs)
|
|
{
|
|
if (itemInstance->count == 0)
|
|
{
|
|
return nullptr;
|
|
}
|
|
shared_ptr<ItemEntity> ie = std::make_shared<ItemEntity>(level, x, y + yOffs, z, itemInstance);
|
|
ie->throwTime = 10;
|
|
level->addEntity(ie);
|
|
return ie;
|
|
}
|
|
|
|
bool Entity::isAlive()
|
|
{
|
|
return !removed;
|
|
}
|
|
|
|
bool Entity::isInWall()
|
|
{
|
|
for (int i = 0; i < 8; i++)
|
|
{
|
|
float xo = ((i >> 0) % 2 - 0.5f) * bbWidth * 0.8f;
|
|
float yo = ((i >> 1) % 2 - 0.5f) * 0.1f;
|
|
float zo = ((i >> 2) % 2 - 0.5f) * bbWidth * 0.8f;
|
|
int xt = Mth::floor(x + xo);
|
|
int yt = Mth::floor(y + getHeadHeight() + yo);
|
|
int zt = Mth::floor(z + zo);
|
|
if (level->isSolidBlockingTile(xt, yt, zt))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool Entity::interact(shared_ptr<Player> player)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
AABB *Entity::getCollideAgainstBox(shared_ptr<Entity> entity)
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
void Entity::rideTick()
|
|
{
|
|
if (riding->removed)
|
|
{
|
|
riding = nullptr;
|
|
return;
|
|
}
|
|
xd = yd = zd = 0;
|
|
tick();
|
|
|
|
if (riding == nullptr) return;
|
|
|
|
// Sets riders old&new position to it's mount's old&new position (plus the ride y-seperatation).
|
|
riding->positionRider();
|
|
|
|
yRideRotA += (riding->yRot - riding->yRotO);
|
|
xRideRotA += (riding->xRot - riding->xRotO);
|
|
|
|
// Wrap rotation angles.
|
|
while (yRideRotA >= 180) yRideRotA -= 360;
|
|
while (yRideRotA < -180) yRideRotA += 360;
|
|
while (xRideRotA >= 180) xRideRotA -= 360;
|
|
while (xRideRotA < -180) xRideRotA += 360;
|
|
|
|
double yra = yRideRotA * 0.5;
|
|
double xra = xRideRotA * 0.5;
|
|
|
|
// Cap rotation speed.
|
|
float max = 10;
|
|
if (yra > max) yra = max;
|
|
if (yra < -max) yra = -max;
|
|
if (xra > max) xra = max;
|
|
if (xra < -max) xra = -max;
|
|
|
|
yRideRotA -= yra;
|
|
xRideRotA -= xra;
|
|
|
|
// jeb: This caused the crosshair to "drift" while riding horses. For now I've just disabled it,
|
|
// because I can't figure out what it's needed for. Riding boats and minecarts seem unaffected...
|
|
// yRot += yra;
|
|
// xRot += xra;
|
|
}
|
|
|
|
void Entity::positionRider()
|
|
{
|
|
shared_ptr<Entity> lockedRider = rider.lock();
|
|
if( lockedRider == nullptr)
|
|
{
|
|
return;
|
|
}
|
|
lockedRider->setPos(x, y + getRideHeight() + lockedRider->getRidingHeight(), z);
|
|
}
|
|
|
|
double Entity::getRidingHeight()
|
|
{
|
|
return heightOffset;
|
|
}
|
|
|
|
double Entity::getRideHeight()
|
|
{
|
|
return bbHeight * .75;
|
|
}
|
|
|
|
void Entity::ride(shared_ptr<Entity> e)
|
|
{
|
|
xRideRotA = 0;
|
|
yRideRotA = 0;
|
|
|
|
if (e == nullptr)
|
|
{
|
|
if (riding != nullptr)
|
|
{
|
|
// 4J Stu - Position should already be updated before the SetEntityLinkPacket comes in
|
|
if(!level->isClientSide) moveTo(riding->x, riding->bb->y0 + riding->bbHeight, riding->z, yRot, xRot);
|
|
riding->rider = weak_ptr<Entity>();
|
|
}
|
|
riding = nullptr;
|
|
return;
|
|
}
|
|
if (riding != nullptr)
|
|
{
|
|
riding->rider = weak_ptr<Entity>();
|
|
}
|
|
riding = e;
|
|
e->rider = shared_from_this();
|
|
}
|
|
|
|
void Entity::lerpTo(double x, double y, double z, float yRot, float xRot, int steps)
|
|
{
|
|
setPos(x, y, z);
|
|
setRot(yRot, xRot);
|
|
|
|
// 4J - don't know what this special y collision is specifically for, but its definitely bad news
|
|
// for arrows as they are actually Meant to intersect the geometry they land in slightly.
|
|
if( GetType() != eTYPE_ARROW )
|
|
{
|
|
AABBList *collisions = level->getCubes(shared_from_this(), bb->shrink(1 / 32.0, 0, 1 / 32.0));
|
|
if ( collisions && !collisions->empty())
|
|
{
|
|
double yTop = 0;
|
|
for ( const AABB *ab : *collisions )
|
|
{
|
|
if ( ab && ab->y1 > yTop) yTop = ab->y1;
|
|
}
|
|
|
|
y += yTop - bb->y0;
|
|
setPos(x, y, z);
|
|
}
|
|
}
|
|
}
|
|
|
|
float Entity::getPickRadius()
|
|
{
|
|
return 0.1f;
|
|
}
|
|
|
|
Vec3 *Entity::getLookAngle()
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
void Entity::handleInsidePortal()
|
|
{
|
|
if (changingDimensionDelay > 0)
|
|
{
|
|
changingDimensionDelay = getDimensionChangingDelay();
|
|
return;
|
|
}
|
|
|
|
double xd = xo - x;
|
|
double zd = zo - z;
|
|
|
|
if (!level->isClientSide && !isInsidePortal)
|
|
{
|
|
portalEntranceDir = Direction::getDirection(xd, zd);
|
|
}
|
|
|
|
isInsidePortal = true;
|
|
}
|
|
|
|
int Entity::getDimensionChangingDelay()
|
|
{
|
|
return SharedConstants::TICKS_PER_SECOND * 45;
|
|
}
|
|
|
|
void Entity::lerpMotion(double xd, double yd, double zd)
|
|
{
|
|
this->xd = xd;
|
|
this->yd = yd;
|
|
this->zd = zd;
|
|
}
|
|
|
|
void Entity::handleEntityEvent(byte eventId)
|
|
{
|
|
}
|
|
|
|
void Entity::animateHurt()
|
|
{
|
|
}
|
|
|
|
ItemInstanceArray Entity::getEquipmentSlots() // ItemInstance[]
|
|
{
|
|
return ItemInstanceArray(); // Default ctor creates nullptr internal array
|
|
}
|
|
|
|
// 4J Stu - Brought forward change from 1.3 to fix #64688 - Customer Encountered: TU7: Content: Art: Aura of enchanted item is not displayed for other players in online game
|
|
void Entity::setEquippedSlot(int slot, shared_ptr<ItemInstance> item)
|
|
{
|
|
}
|
|
|
|
bool Entity::isOnFire()
|
|
{
|
|
return !fireImmune && (onFire > 0 || getSharedFlag(FLAG_ONFIRE));
|
|
}
|
|
|
|
bool Entity::isRiding()
|
|
{
|
|
return riding != nullptr;
|
|
}
|
|
|
|
bool Entity::isSneaking()
|
|
{
|
|
return getSharedFlag(FLAG_SNEAKING);
|
|
}
|
|
|
|
void Entity::setSneaking(bool value)
|
|
{
|
|
setSharedFlag(FLAG_SNEAKING, value);
|
|
}
|
|
|
|
bool Entity::isIdle()
|
|
{
|
|
return getSharedFlag(FLAG_IDLEANIM);
|
|
}
|
|
|
|
void Entity::setIsIdle(bool value)
|
|
{
|
|
setSharedFlag(FLAG_IDLEANIM, value);
|
|
}
|
|
|
|
bool Entity::isSprinting()
|
|
{
|
|
return getSharedFlag(FLAG_SPRINTING);
|
|
}
|
|
|
|
void Entity::setSprinting(bool value)
|
|
{
|
|
setSharedFlag(FLAG_SPRINTING, value);
|
|
}
|
|
|
|
bool Entity::isInvisible()
|
|
{
|
|
return getSharedFlag(FLAG_INVISIBLE);
|
|
}
|
|
|
|
bool Entity::isInvisibleTo(shared_ptr<Player> plr)
|
|
{
|
|
return isInvisible();
|
|
}
|
|
|
|
void Entity::setInvisible(bool value)
|
|
{
|
|
setSharedFlag(FLAG_INVISIBLE, value);
|
|
}
|
|
|
|
bool Entity::isWeakened()
|
|
{
|
|
return getSharedFlag(FLAG_EFFECT_WEAKENED);
|
|
}
|
|
|
|
void Entity::setWeakened(bool value)
|
|
{
|
|
setSharedFlag(FLAG_EFFECT_WEAKENED, value);
|
|
}
|
|
|
|
bool Entity::isUsingItemFlag()
|
|
{
|
|
return getSharedFlag(FLAG_USING_ITEM);
|
|
}
|
|
|
|
void Entity::setUsingItemFlag(bool value)
|
|
{
|
|
setSharedFlag(FLAG_USING_ITEM, value);
|
|
}
|
|
|
|
bool Entity::getSharedFlag(int flag)
|
|
{
|
|
if( entityData )
|
|
{
|
|
return (entityData->getByte(DATA_SHARED_FLAGS_ID) & (1 << flag)) != 0;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void Entity::setSharedFlag(int flag, bool value)
|
|
{
|
|
if( entityData )
|
|
{
|
|
byte currentValue = entityData->getByte(DATA_SHARED_FLAGS_ID);
|
|
if (value)
|
|
{
|
|
entityData->set(DATA_SHARED_FLAGS_ID, static_cast<byte>(currentValue | (1 << flag)));
|
|
}
|
|
else
|
|
{
|
|
entityData->set(DATA_SHARED_FLAGS_ID, static_cast<byte>(currentValue & ~(1 << flag)));
|
|
}
|
|
}
|
|
}
|
|
|
|
// 4J Stu - Brought forward from 1.2.3 to fix 38654 - Gameplay: Player will take damage when air bubbles are present if resuming game from load/autosave underwater.
|
|
int Entity::getAirSupply()
|
|
{
|
|
return entityData->getShort(DATA_AIR_SUPPLY_ID);
|
|
}
|
|
|
|
// 4J Stu - Brought forward from 1.2.3 to fix 38654 - Gameplay: Player will take damage when air bubbles are present if resuming game from load/autosave underwater.
|
|
void Entity::setAirSupply(int supply)
|
|
{
|
|
entityData->set(DATA_AIR_SUPPLY_ID, static_cast<short>(supply));
|
|
}
|
|
|
|
void Entity::thunderHit(const LightningBolt *lightningBolt)
|
|
{
|
|
burn(5);
|
|
onFire++;
|
|
if (onFire == 0) setOnFire(8);
|
|
}
|
|
|
|
void Entity::killed(shared_ptr<LivingEntity> mob)
|
|
{
|
|
}
|
|
|
|
bool Entity::checkInTile(double x, double y, double z)
|
|
{
|
|
int xTile = Mth::floor(x);
|
|
int yTile = Mth::floor(y);
|
|
int zTile = Mth::floor(z);
|
|
|
|
double xd = x - (xTile);
|
|
double yd = y - (yTile);
|
|
double zd = z - (zTile);
|
|
|
|
vector<AABB *> *cubes = level->getTileCubes(bb);
|
|
if ( (cubes && !cubes->empty()) || level->isFullAABBTile(xTile, yTile, zTile))
|
|
{
|
|
bool west = !level->isFullAABBTile(xTile - 1, yTile, zTile);
|
|
bool east = !level->isFullAABBTile(xTile + 1, yTile, zTile);
|
|
bool down = !level->isFullAABBTile(xTile, yTile - 1, zTile);
|
|
bool up = !level->isFullAABBTile(xTile, yTile + 1, zTile);
|
|
bool north = !level->isFullAABBTile(xTile, yTile, zTile - 1);
|
|
bool south = !level->isFullAABBTile(xTile, yTile, zTile + 1);
|
|
|
|
int dir = 3;
|
|
double closest = 9999;
|
|
if (west && xd < closest)
|
|
{
|
|
closest = xd;
|
|
dir = 0;
|
|
}
|
|
if (east && 1 - xd < closest)
|
|
{
|
|
closest = 1 - xd;
|
|
dir = 1;
|
|
}
|
|
if (up && 1 - yd < closest)
|
|
{
|
|
closest = 1 - yd;
|
|
dir = 3;
|
|
}
|
|
if (north && zd < closest)
|
|
{
|
|
closest = zd;
|
|
dir = 4;
|
|
}
|
|
if (south && 1 - zd < closest)
|
|
{
|
|
closest = 1 - zd;
|
|
dir = 5;
|
|
}
|
|
|
|
float speed = random->nextFloat() * 0.2f + 0.1f;
|
|
if (dir == 0) this->xd = -speed;
|
|
if (dir == 1) this->xd = +speed;
|
|
|
|
if (dir == 2) this->yd = -speed;
|
|
if (dir == 3) this->yd = +speed;
|
|
|
|
if (dir == 4) this->zd = -speed;
|
|
if (dir == 5) this->zd = +speed;
|
|
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void Entity::makeStuckInWeb()
|
|
{
|
|
isStuckInWeb = true;
|
|
fallDistance = 0;
|
|
}
|
|
|
|
wstring Entity::getAName()
|
|
{
|
|
#ifdef _DEBUG
|
|
wstring id = EntityIO::getEncodeId(shared_from_this());
|
|
if (id.empty()) id = L"generic";
|
|
return L"entity." + id + std::to_wstring(entityId);
|
|
#else
|
|
return L"";
|
|
#endif
|
|
}
|
|
|
|
vector<shared_ptr<Entity> > *Entity::getSubEntities()
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
bool Entity::is(shared_ptr<Entity> other)
|
|
{
|
|
return shared_from_this() == other;
|
|
}
|
|
|
|
float Entity::getYHeadRot()
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
void Entity::setYHeadRot(float yHeadRot)
|
|
{
|
|
}
|
|
|
|
bool Entity::isAttackable()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
bool Entity::skipAttackInteraction(shared_ptr<Entity> source)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool Entity::isInvulnerable()
|
|
{
|
|
return invulnerable;
|
|
}
|
|
|
|
void Entity::copyPosition(shared_ptr<Entity> target)
|
|
{
|
|
moveTo(target->x, target->y, target->z, target->yRot, target->xRot);
|
|
}
|
|
|
|
void Entity::restoreFrom(shared_ptr<Entity> oldEntity, bool teleporting)
|
|
{
|
|
CompoundTag *tag = new CompoundTag();
|
|
oldEntity->saveWithoutId(tag);
|
|
load(tag);
|
|
delete tag;
|
|
changingDimensionDelay = oldEntity->changingDimensionDelay;
|
|
portalEntranceDir = oldEntity->portalEntranceDir;
|
|
}
|
|
|
|
void Entity::changeDimension(int i)
|
|
{
|
|
if (level->isClientSide || removed) return;
|
|
|
|
MinecraftServer *server = MinecraftServer::getInstance();
|
|
int lastDimension = dimension;
|
|
ServerLevel *oldLevel = server->getLevel(lastDimension);
|
|
ServerLevel *newLevel = server->getLevel(i);
|
|
|
|
if (lastDimension == 1 && i == 1)
|
|
{
|
|
newLevel = server->getLevel(0);
|
|
}
|
|
|
|
// 4J: Restrictions on what can go through
|
|
{
|
|
// 4J: Some things should just be destroyed when they hit a portal
|
|
if (instanceof(eTYPE_FALLINGTILE))
|
|
{
|
|
removed = true;
|
|
return;
|
|
}
|
|
|
|
// 4J: Check server level entity limit (arrows, item entities, experience orbs, etc)
|
|
if (newLevel->atEntityLimit(shared_from_this())) return;
|
|
|
|
// 4J: Check level limit on living entities, minecarts and boats
|
|
if (!instanceof(eTYPE_PLAYER) && !newLevel->canCreateMore(GetType(), Level::eSpawnType_Portal)) return;
|
|
}
|
|
|
|
// 4J: Definitely sending, set dimension now
|
|
dimension = newLevel->dimension->id;
|
|
|
|
level->removeEntity(shared_from_this());
|
|
removed = false;
|
|
|
|
server->getPlayers()->repositionAcrossDimension(shared_from_this(), lastDimension, oldLevel, newLevel);
|
|
shared_ptr<Entity> newEntity = EntityIO::newEntity(EntityIO::getEncodeId(shared_from_this()), newLevel);
|
|
|
|
if (newEntity != nullptr)
|
|
{
|
|
newEntity->restoreFrom(shared_from_this(), true);
|
|
|
|
if (lastDimension == 1 && i == 1)
|
|
{
|
|
Pos *spawnPos = newLevel->getSharedSpawnPos();
|
|
spawnPos->y = level->getTopSolidBlock(spawnPos->x, spawnPos->z);
|
|
newEntity->moveTo(spawnPos->x, spawnPos->y, spawnPos->z, newEntity->yRot, newEntity->xRot);
|
|
delete spawnPos;
|
|
}
|
|
|
|
newLevel->addEntity(newEntity);
|
|
}
|
|
|
|
removed = true;
|
|
|
|
oldLevel->resetEmptyTime();
|
|
newLevel->resetEmptyTime();
|
|
}
|
|
|
|
float Entity::getTileExplosionResistance(Explosion *explosion, Level *level, int x, int y, int z, Tile *tile)
|
|
{
|
|
return tile->getExplosionResistance(shared_from_this());
|
|
}
|
|
|
|
bool Entity::shouldTileExplode(Explosion *explosion, Level *level, int x, int y, int z, int id, float power)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
int Entity::getMaxFallDistance()
|
|
{
|
|
return 3;
|
|
}
|
|
|
|
int Entity::getPortalEntranceDir()
|
|
{
|
|
return portalEntranceDir;
|
|
}
|
|
|
|
bool Entity::isIgnoringTileTriggers()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool Entity::displayFireAnimation()
|
|
{
|
|
return isOnFire();
|
|
}
|
|
|
|
void Entity::setUUID(const wstring &UUID)
|
|
{
|
|
uuid = UUID;
|
|
}
|
|
|
|
wstring Entity::getUUID()
|
|
{
|
|
return uuid;
|
|
}
|
|
|
|
bool Entity::isPushedByWater()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
wstring Entity::getDisplayName()
|
|
{
|
|
return getAName();
|
|
}
|
|
|
|
// 4J: Added to retrieve name that should be sent in ChatPackets (important on Xbox One for players)
|
|
wstring Entity::getNetworkName()
|
|
{
|
|
return getDisplayName();
|
|
}
|
|
|
|
void Entity::setAnimOverrideBitmask(unsigned int uiBitmask)
|
|
{
|
|
m_uiAnimOverrideBitmask=uiBitmask;
|
|
app.DebugPrintf("!!! Setting anim override bitmask to %d\n",uiBitmask);
|
|
}
|
|
unsigned int Entity::getAnimOverrideBitmask()
|
|
{
|
|
if(app.GetGameSettings(eGameSetting_CustomSkinAnim)==0 )
|
|
{
|
|
// We have a force animation for some skins (claptrap)
|
|
// 4J-PB - treat all the eAnim_Disable flags as a force anim
|
|
unsigned int uiIgnoreUserCustomSkinAnimSettingMask=(1<<HumanoidModel::eAnim_ForceAnim) |
|
|
(1<<HumanoidModel::eAnim_DisableRenderArm0) |
|
|
(1<<HumanoidModel::eAnim_DisableRenderArm1) |
|
|
(1<<HumanoidModel::eAnim_DisableRenderTorso) |
|
|
(1<<HumanoidModel::eAnim_DisableRenderLeg0) |
|
|
(1<<HumanoidModel::eAnim_DisableRenderLeg1) |
|
|
(1<<HumanoidModel::eAnim_DisableRenderHair) |
|
|
(1<<HumanoidModel::eAnim_DisableRenderSleeve1) |
|
|
(1<<HumanoidModel::eAnim_DisableRenderSleeve0) |
|
|
(1<<HumanoidModel::eAnim_DisableRenderPants1) |
|
|
(1<<HumanoidModel::eAnim_DisableRenderPants0) |
|
|
(1<<HumanoidModel::eAnim_DisableRenderJacket);
|
|
|
|
if((m_uiAnimOverrideBitmask & HumanoidModel::m_staticBitmaskIgnorePlayerCustomAnimSetting)!=0)
|
|
{
|
|
return m_uiAnimOverrideBitmask;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
return m_uiAnimOverrideBitmask;
|
|
} |