mfeat: added early access elytra in creative for bug testing/accuracy comparison
TODO: replace temporary atlas, Properly implement 3d skulls
This commit is contained in:
Tranqlmao 2026-04-24 20:00:35 -04:00
parent 7db8860ea4
commit 4b6e9f8eb2
31 changed files with 889 additions and 97 deletions

View file

@ -607,7 +607,91 @@ void SoundEngine::play(int iSound, float x, float y, float z, float volume, floa
/////////////////////////////////////////////
//
// playUI
//
// startElytraSound / stopElytraSound
// Manages a single persistent looping sound for elytra gliding.
// Call startElytraSound every tick while gliding (it no-ops if already running,
// just updates volume). Call stopElytraSound when gliding ends.
//
// IMPORTANT: m_elytraLoopingSound is NOT added to m_activeSounds.
// The tick() cleanup loop deletes sounds where is_playing()==false.
// A looping sound briefly reports is_playing()==false at the loop point,
// which would cause tick() to free it and leave m_elytraLoopingSound dangling.
//
/////////////////////////////////////////////
void SoundEngine::startElytraSound(float x, float y, float z, float volume, float pitch)
{
// If already initialized just update volume and pitch - never reinitialize mid-flight.
if (m_elytraLoopingSound != nullptr)
{
float finalVolume = volume * m_MasterEffectsVolume * SFX_VOLUME_MULTIPLIER;
if (finalVolume > SFX_MAX_GAIN) finalVolume = SFX_MAX_GAIN;
ma_sound_set_volume(&m_elytraLoopingSound->sound, finalVolume);
ma_sound_set_pitch(&m_elytraLoopingSound->sound, pitch);
return;
}
// Resolve file path using the same logic as play().
wstring name = wchSoundNames[eSoundType_ITEM_ELYTRA_FLYING];
char* soundName = ConvertSoundPathToName(name);
char basePath[256];
sprintf_s(basePath, "Windows64Media/Sound/Minecraft/%s", soundName);
const char* extensions[] = { ".ogg", ".wav", ".mp3" };
char finalPath[256] = {};
bool found = false;
for (auto& ext : extensions)
{
char candidate[256];
sprintf_s(candidate, "%s%s", basePath, ext);
DWORD attr = GetFileAttributesA(candidate);
if (attr != INVALID_FILE_ATTRIBUTES && !(attr & FILE_ATTRIBUTE_DIRECTORY))
{
sprintf_s(finalPath, "%s", candidate);
found = true;
break;
}
}
if (!found) return;
MiniAudioSound* s = new MiniAudioSound();
memset(&s->info, 0, sizeof(AUDIO_INFO));
s->info.volume = volume; s->info.pitch = pitch;
s->info.bIs3D = false;
s->info.iSound = eSoundType_ITEM_ELYTRA_FLYING + eSFX_MAX;
// Synchronous load so the sound is immediately ready - no ASYNC gap.
if (ma_sound_init_from_file(&m_engine, finalPath, 0,
nullptr, nullptr, &s->sound) != MA_SUCCESS)
{
delete s;
return;
}
ma_sound_set_spatialization_enabled(&s->sound, MA_FALSE);
ma_sound_set_looping(&s->sound, MA_TRUE);
float finalVolume = volume * m_MasterEffectsVolume * SFX_VOLUME_MULTIPLIER;
if (finalVolume > SFX_MAX_GAIN) finalVolume = SFX_MAX_GAIN;
ma_sound_set_volume(&s->sound, finalVolume);
ma_sound_set_pitch(&s->sound, pitch);
ma_sound_start(&s->sound);
// NOT added to m_activeSounds - tick() cleanup would delete it at loop boundaries.
m_elytraLoopingSound = s;
}
void SoundEngine::stopElytraSound()
{
if (m_elytraLoopingSound == nullptr) return;
ma_sound_stop(&m_elytraLoopingSound->sound);
ma_sound_uninit(&m_elytraLoopingSound->sound);
delete m_elytraLoopingSound;
m_elytraLoopingSound = nullptr;
}
/////////////////////////////////////////////
// playUI
//
/////////////////////////////////////////////
void SoundEngine::playUI(int iSound, float volume, float pitch)

View file

@ -127,6 +127,8 @@ public:
void GetSoundName(char *szSoundName,int iSound);
#endif
void play(int iSound, float x, float y, float z, float volume, float pitch) override;
void startElytraSound(float x, float y, float z, float volume, float pitch);
void stopElytraSound();
void playStreaming(const wstring& name, float x, float y , float z, float volume, float pitch, bool bMusicDelay=true) override;
void playUI(int iSound, float volume, float pitch) override;
void playMusicTick() override;
@ -159,6 +161,9 @@ private:
int GetRandomishTrack(int iStart,int iEnd);
MiniAudioSound* m_elytraLoopingSound = nullptr;
ma_engine m_engine;
ma_engine_config m_engineConfig;
ma_sound m_musicStream;

View file

@ -272,6 +272,8 @@ const WCHAR *ConsoleSoundEngine::wchSoundNames[eSoundType_MAX]=
L"item.armor.equip_generic6",
L"damage.critical", //eSoundType_DAMAGE_CRITICAL,
L"item.elytra.flying" // eSoundType_ITEM_ELYTRA_FLYING
};

View file

@ -1273,6 +1273,10 @@ void IUIScene_AbstractContainerMenu::onMouseTick()
case Item::chestplate_gold_Id:
case Item::leggings_gold_Id:
case Item::boots_gold_Id:
case Item::elytra_Id:
buttonY=eToolTipQuickMoveArmor;
break;

View file

@ -383,6 +383,7 @@ void IUIScene_CreativeMenu::staticCtor()
ITEM(Item::minecart_tnt_Id)
ITEM(Item::saddle_Id)
ITEM(Item::boat_Id)
ITEM(Item::elytra_Id)
// Miscellaneous
DEF(eCreativeInventory_Misc)

Binary file not shown.

After

Width:  |  Height:  |  Size: 684 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 112 KiB

After

Width:  |  Height:  |  Size: 111 KiB

View file

@ -189,7 +189,9 @@ EntityRenderDispatcher::EntityRenderDispatcher()
it.second->init(this);
}
isGuiRender = false; // 4J added
isGuiRender = false; // 4J added
isInventoryRender = false;
}
EntityRenderer *EntityRenderDispatcher::getRenderer(eINSTANCEOF e)

View file

@ -32,6 +32,8 @@ public:
float playerRotX;
Options *options;
bool isGuiRender; // 4J added
bool isInventoryRender;
double xPlayer, yPlayer, zPlayer;

View file

@ -47,6 +47,8 @@
#include "../Minecraft.World/compression.h"
#include "PS3/PS3Extras/ShutdownManager.h"
#include "BossMobGuiInfo.h"
#include "../Minecraft.World/LivingEntity.h"
#include "TexturePackRepository.h"
#include "TexturePack.h"
@ -96,6 +98,10 @@ GameRenderer::GameRenderer(Minecraft *mc)
fovOffsetO = 0;
cameraRoll = 0;
cameraRollO = 0;
m_yBob = 0.0f;
m_yBobO = 0.0f;
m_elytaCamShift = 0.0f;
for( int i = 0; i < 4; i++ )
{
fov[i] = 0.0f;
@ -197,6 +203,20 @@ void GameRenderer::tick(bool first) // 4J - add bFirst
fovOffsetO = fovOffset;
cameraRollO = cameraRoll;
m_yBobO = m_yBob;
{
shared_ptr<Player> yBobPlayer = dynamic_pointer_cast<Player>(mc->cameraTargetPlayer);
float target = 0.0f;
if (yBobPlayer != nullptr && !yBobPlayer->onGround
&& !(yBobPlayer->abilities.mayfly && yBobPlayer->abilities.flying))
{
target = (float)(atan(-yBobPlayer->yd * 0.2) * 15.0);
}
m_yBob += (target - m_yBob) * 0.8f;
}
if (mc->options->smoothCamera)
{
// update player view in tick() instead of render() to maintain
@ -455,6 +475,11 @@ void GameRenderer::bobView(float a)
glRotatef((float) Mth::sin(b * PI) * bob * 3, 0, 0, 1);
glRotatef((float) abs(Mth::cos(b * PI - 0.2f) * bob) * 5, 1, 0, 0);
glRotatef((float) tilt, 1, 0, 0);
float yBobAngle = m_yBobO + (m_yBob - m_yBobO) * a;
if (fabsf(yBobAngle) > 0.001f)
glRotatef(yBobAngle, 1, 0, 0);
}
void GameRenderer::moveCameraToPlayer(float a)
@ -468,7 +493,71 @@ void GameRenderer::moveCameraToPlayer(float a)
double z = player->zo + (player->z - player->zo) * a;
glRotatef(cameraRollO + (cameraRoll - cameraRollO) * a, 0, 0, 1);
{
shared_ptr<Player> elytraPlayer = dynamic_pointer_cast<Player>(player);
bool elytraFlying = (elytraPlayer != nullptr && elytraPlayer->isElytraFlying());
float targetShift = elytraFlying ? -1.22f : 0.0f;
m_elytaCamShift += (targetShift - m_elytaCamShift) * 0.15f;
if (fabsf(m_elytaCamShift) < 0.001f) m_elytaCamShift = 0.0f;
y += m_elytaCamShift;
}
if (localplayer != nullptr && !player->isSleeping())
{
shared_ptr<Player> rollPlayer = dynamic_pointer_cast<Player>(player);
if (rollPlayer != nullptr && rollPlayer->isElytraFlying())
{
double velX = rollPlayer->xd;
double velZ = rollPlayer->zd;
double horizVelSq = velX * velX + velZ * velZ;
Vec3* look = rollPlayer->getLookAngle();
double horizLookSq = look->x * look->x + look->z * look->z;
float targetRoll = 0.0f;
if (horizVelSq > 1.0e-6 && horizLookSq > 1.0e-6)
{
double dot = velX * look->x + velZ * look->z;
double cosAngle = dot / sqrt(horizVelSq * horizLookSq);
if (cosAngle > 1.0) cosAngle = 1.0;
if (cosAngle < -1.0) cosAngle = -1.0;
double angle = acos(cosAngle) / 2.5;
if (angle > PI / 8.0) angle = PI / 8.0;
double cross = velX * look->z - velZ * look->x;
float sign = (cross < 0.0) ? -1.0f : 1.0f;
targetRoll = sign * (float)(angle * (180.0 / PI));
}
cameraRoll += (targetRoll - cameraRoll) * 0.1f;
}
else
{
if (fabsf(cameraRoll) > 0.01f)
{
cameraRoll += (0.0f - cameraRoll) * 0.15f;
if (fabsf(cameraRoll) < 0.01f) cameraRoll = 0.0f;
}
}
if (fabsf(cameraRoll) > 0.01f)
glRotatef(cameraRoll, 0, 0, 1);
}
if (player->isSleeping())
{
@ -574,10 +663,12 @@ void GameRenderer::moveCameraToPlayer(float a)
}
}
glTranslatef(0, heightOffset, 0);
glTranslatef(0, heightOffset - m_elytaCamShift, 0);
x = player->xo + (player->x - player->xo) * a;
y = player->yo + (player->y - player->yo) * a - heightOffset;
y = player->yo + (player->y - player->yo) * a - heightOffset + m_elytaCamShift;
z = player->zo + (player->z - player->zo) * a;
isInClouds = mc->levelRenderer->isInCloud(x, y, z, a);

View file

@ -64,6 +64,13 @@ private:
float cameraRoll;
float cameraRollO;
float m_yBob;
float m_yBobO;
float m_elytaCamShift;
// 4J - changes brought forward from 1.8.2
static const int NUM_LIGHT_TEXTURES = 4;// * 3;
int lightTexture[NUM_LIGHT_TEXTURES]; // 4J - changed so that we have one lightTexture per level, to support split screen

View file

@ -88,6 +88,17 @@ void HumanoidModel::_init(float g, float yOffset, int texWidth, int texHeight, b
cloak = new ModelPart(this, 0, 0);
cloak->addHumanoidBox(-5, -0, -1, 10, 16, 1, g); // Cloak
elytraRight = new ModelPart(this, 22, 0);
elytraRight->addHumanoidBox(-10.0f, 0.0f, 0.0f, 10, 20, 2, 0.0f);
elytraRight->setPos(5.0f, 0.0f + yOffset, 0.0f); // Wing Left
elytraLeft = new ModelPart(this, 22, 0);
elytraLeft->bMirror = true;
elytraLeft->addHumanoidBox(0.0f, 0.0f, 0.0f, 10, 20, 2, 0.0f);
elytraLeft->setPos(-5.0f, 0.0f + yOffset, 0.0f); // Wing Right
ear = new ModelPart(this, 24, 0);
ear->addHumanoidBox(-3, -6, -1, 6, 6, 1, g); // Ear
@ -186,6 +197,8 @@ void HumanoidModel::_init(float g, float yOffset, int texWidth, int texHeight, b
// 4J added - compile now to avoid random performance hit first time cubes are rendered
// 4J Stu - Not just performance, but alpha+depth tests don't work right unless we compile here
cloak->compile(1.0f/16.0f);
elytraLeft->compile(1.0f / 16.0f);
elytraRight->compile(1.0f / 16.0f);
ear->compile(1.0f/16.0f);
head->compile(1.0f/16.0f);
body->compile(1.0f/16.0f);
@ -211,6 +224,9 @@ void HumanoidModel::_init(float g, float yOffset, int texWidth, int texHeight, b
sneaking=false;
idle=false;
bowAndArrow=false;
elytraFlying = false;
elytraCrouching = false;
// 4J added
eating = false;
@ -881,6 +897,57 @@ void HumanoidModel::setupAnim(float time, float r, float bob, float yRot, float
}
}
}
if (elytraFlying)
{
if (elytraCrouching)
{
arm0->xRot = PI; arm0->yRot = 0.0f; arm0->zRot = 0.0f; arm0->y = 2.0f;
if (sleeve0) { sleeve0->xRot = PI; sleeve0->yRot = 0.0f; sleeve0->zRot = 0.0f; sleeve0->y = 2.0f; }
arm1->xRot = 0.0f; arm1->yRot = 0.0f; arm1->zRot = 0.0f; arm1->y = 2.0f;
if (sleeve1) { sleeve1->xRot = 0.0f; sleeve1->yRot = 0.0f; sleeve1->zRot = 0.0f; sleeve1->y = 2.0f; }
leg0->xRot = 0.0f; leg0->yRot = 0.0f; leg0->zRot = 0.0f;
leg1->xRot = 0.0f; leg1->yRot = 0.0f; leg1->zRot = 0.0f;
if (pants0) { pants0->xRot = 0.0f; pants0->yRot = 0.0f; pants0->zRot = 0.0f; }
if (pants1) { pants1->xRot = 0.0f; pants1->yRot = 0.0f; pants1->zRot = 0.0f; }
}
else
{
float elytraTime = (float)(entity->tickCount) * 0.3f;
float spd2 = (float)(entity->xd * entity->xd + entity->yd * entity->yd + entity->zd * entity->zd);
float fDamp = spd2 / 0.2f;
fDamp = fDamp * fDamp * fDamp;
if (fDamp < 1.0f) fDamp = 1.0f;
float armAmp = 2.0f * r * 0.5f / fDamp;
float legAmp = 1.4f * r / fDamp;
arm0->xRot = Mth::cos(elytraTime + PI) * armAmp;
arm0->yRot = 0.0f; arm0->zRot = 0.0f; arm0->y = 2.0f;
if (sleeve0) { sleeve0->xRot = arm0->xRot; sleeve0->yRot = 0.0f; sleeve0->zRot = 0.0f; sleeve0->y = 2.0f; }
arm1->xRot = Mth::cos(elytraTime) * armAmp;
arm1->yRot = 0.0f; arm1->zRot = 0.0f; arm1->y = 2.0f;
if (sleeve1) { sleeve1->xRot = arm1->xRot; sleeve1->yRot = 0.0f; sleeve1->zRot = 0.0f; sleeve1->y = 2.0f; }
leg0->xRot = Mth::cos(elytraTime) * legAmp;
leg0->yRot = 0.0f; leg0->zRot = 0.0f;
leg1->xRot = Mth::cos(elytraTime + PI) * legAmp;
leg1->yRot = 0.0f; leg1->zRot = 0.0f;
if (pants0) { pants0->xRot = leg0->xRot; pants0->yRot = 0.0f; pants0->zRot = 0.0f; }
if (pants1) { pants1->xRot = leg1->xRot; pants1->yRot = 0.0f; pants1->zRot = 0.0f; }
}
body->xRot = 0.0f;
body->z = 0.0f;
head->xRot = -(float)(PI / 4.0f);
hair->xRot = head->xRot;
}
}
void HumanoidModel::renderHair(float scale,bool usecompiled)
@ -903,6 +970,11 @@ void HumanoidModel::renderCloak(float scale,bool usecompiled)
{
cloak->render(scale,usecompiled);
}
void HumanoidModel::renderElytra(float scale, bool usecompiled)
{
elytraRight->render(scale, usecompiled);
elytraLeft->render(scale, usecompiled);
}
void HumanoidModel::render(HumanoidModel *model, float scale, bool usecompiled)
{

View file

@ -3,30 +3,32 @@
class HumanoidModel : public Model
{
public:
ModelPart *head, *hair, *body, *jacket, *arm0, *sleeve0, *arm1, *sleeve1, *leg0, *pants0, *leg1, *pants1, *ear, *cloak;
ModelPart* head, * hair, * body, * jacket, * arm0, * sleeve0, * arm1, * sleeve1, * leg0, * pants0, * leg1, * pants1, * ear, * cloak;
ModelPart* elytraLeft, * elytraRight;
int holdingLeftHand;
int holdingRightHand;
bool idle;
bool sneaking;
bool bowAndArrow;
bool eating;
float eating_t;
float eating_swing;
unsigned int m_uiAnimOverrideBitmask;
bool eating;
float eating_t;
float eating_swing;
bool elytraFlying;
bool elytraCrouching;
unsigned int m_uiAnimOverrideBitmask;
float m_fYOffset;
enum animbits
{
eAnim_ArmsDown = 0,
eAnim_ArmsOutFront,
eAnim_NoLegAnim,
eAnim_HasIdle,
eAnim_ForceAnim,
eAnim_ForceAnim,
eAnim_SingleLegs,
eAnim_SingleArms,
eAnim_StatueOfLiberty,
eAnim_DontRenderArmour,
eAnim_NoBobbing,
eAnim_StatueOfLiberty,
eAnim_DontRenderArmour,
eAnim_NoBobbing,
eAnim_DisableRenderHead,
eAnim_DisableRenderArm0,
eAnim_DisableRenderArm1,
@ -42,6 +44,7 @@ public:
eAnim_DisableRenderPants1
};
static const unsigned int m_staticBitmaskIgnorePlayerCustomAnimSetting =
(1 << HumanoidModel::eAnim_ForceAnim) |
(1 << HumanoidModel::eAnim_DisableRenderArm0) |
@ -75,6 +78,8 @@ public:
void renderHair(float scale, bool usecompiled);
void renderEars(float scale, bool usecompiled);
void renderCloak(float scale, bool usecompiled);
void renderElytra(float scale, bool usecompiled);
void render(HumanoidModel* model, float scale, bool usecompiled);
ModelPart* AddOrRetrievePart(SKIN_BOX* pBox);

View file

@ -11,6 +11,8 @@
#include "MultiPlayerLocalPlayer.h"
#include "Minimap.h"
#include "MultiPlayerLevel.h"
#include "SkullTileRenderer.h"
#include "../Minecraft.World/Facing.h"
#include "../Minecraft.World/net.minecraft.world.item.h"
#include "../Minecraft.World/net.minecraft.world.level.tile.h"
#include "../Minecraft.World/net.minecraft.world.entity.h"
@ -237,6 +239,17 @@ void ItemInHandRenderer::renderItem(shared_ptr<LivingEntity> mob, shared_ptr<Ite
}
glPushMatrix();
/*if (item->id == Item::skull_Id && SkullTileRenderer::instance != nullptr)
{
wstring extra = L"";
if (item->hasTag() && item->getTag()->contains(L"SkullOwner"))
extra = item->getTag()->getString(L"SkullOwner");
SkullTileRenderer::instance->renderSkull(-0.5f, 0.0f, -0.5f, Facing::UP, 0.0f, item->getAuxValue(), extra);
glPopMatrix();
return;
}*/
Tile *tile = Tile::tiles[item->id];
if (item->getIconType() == Icon::TYPE_TERRAIN && tile != nullptr && TileRenderer::canRender(tile->getRenderShape()))
{
@ -680,6 +693,16 @@ void ItemInHandRenderer::render(float a)
renderItem(player, item, 1, false);
}
//else if (item->id == Item::skull_Id && SkullTileRenderer::instance != nullptr)
//{
// wstring extra = L"";
// if (item->hasTag() && item->getTag()->contains(L"SkullOwner"))
// extra = item->getTag()->getString(L"SkullOwner");
// glEnable(GL_RESCALE_NORMAL);
// glScalef(2.0f, 2.0f, 2.0f);
// SkullTileRenderer::instance->renderSkull(-0.5f, 0.0f, -0.5f, Facing::UP, 0.0f, item->getAuxValue(), extra);
// glDisable(GL_RESCALE_NORMAL);
//}
else
{
renderItem(player, item, 0, false);

View file

@ -67,6 +67,9 @@ LocalPlayer::LocalPlayer(Minecraft *minecraft, Level *level, User *user, int dim
sprintTriggerTime = 0;
sprintTriggerRegisteredReturn = false;
twoJumpsRegistered = false;
m_elytraCancelPressCount = 0;
m_elytraCancelWindow = 0;
m_elytraSoundTicks = 0;
sprintTime = 0;
m_uiInactiveTicks=0;
portalTime = 0.0f;
@ -331,6 +334,58 @@ void LocalPlayer::aiStep()
}
if (isElytraFlying())
{
if (m_elytraCancelWindow > 0) m_elytraCancelWindow--;
if (!wasJumping && input->jumping)
{
m_elytraCancelPressCount++;
if (m_elytraCancelPressCount == 1)
{
m_elytraCancelWindow = 15;
}
else if (m_elytraCancelPressCount >= 2 && m_elytraCancelWindow > 0)
{
setElytraFlying(false);
jumpTriggerTime = 10;
m_elytraCancelPressCount = 0;
m_elytraCancelWindow = 0;
}
}
if (m_elytraCancelWindow == 0 && m_elytraCancelPressCount > 0)
m_elytraCancelPressCount = 0;
m_elytraSoundTicks++;
float vol = 0.0f;
float pitch = 1.0f;
if (m_elytraSoundTicks >= 20)
{
float totalSpeed = (float)Mth::sqrt(xd * xd + yd * yd + zd * zd);
float f1 = totalSpeed / 2.0f;
float speedVol = f1 * f1;
if (speedVol > 1.0f) speedVol = 1.0f;
float fadeFactor = (m_elytraSoundTicks < 40)
? (float)(m_elytraSoundTicks - 20) / 20.0f
: 1.0f;
vol = speedVol * fadeFactor;
if (vol > 0.8f)
pitch = 1.0f + (vol - 0.8f);
}
if (vol > 0.01f)
minecraft->soundEngine->startElytraSound((float)x, (float)y, (float)z, vol, pitch);
}
else
{
m_elytraCancelPressCount = 0;
m_elytraCancelWindow = 0;
m_elytraSoundTicks = 0;
minecraft->soundEngine->stopElytraSound();
}
if (abilities.flying)
{
// yd = 0;

View file

@ -31,6 +31,10 @@ protected:
int sprintTriggerTime;
bool sprintTriggerRegisteredReturn; // 4J added
bool twoJumpsRegistered; // 4J added
int m_elytraCancelPressCount;
int m_elytraCancelWindow;
int m_elytraSoundTicks;
unsigned int m_uiInactiveTicks; // To measure time for idle anims

View file

@ -288,14 +288,21 @@ void PlayerRenderer::render(shared_ptr<Entity> _mob, double x, double y, double
{
armorParts1->eating = armorParts2->eating = resModel->eating = false;
}
// Suppress crouch pose while elytra flying (superman pose is applied instead)
bool effectiveSneaking = mob->isSneaking() && !mob->isElytraFlying();
armorParts1->sneaking = armorParts2->sneaking = resModel->sneaking = effectiveSneaking;
// Signal models: elytraFlying suppresses walk/bob; elytraCrouching adds superman arms
// Suppress elytra pose in the inventory screen (isInventoryRender=true during that pass).
bool elytraFlying = mob->isElytraFlying() && !EntityRenderDispatcher::instance->isInventoryRender;
bool elytraCrouch = elytraFlying && mob->isSneaking();
armorParts1->elytraFlying = armorParts2->elytraFlying = resModel->elytraFlying = elytraFlying;
armorParts1->elytraCrouching = armorParts2->elytraCrouching = resModel->elytraCrouching = elytraCrouch;
armorParts1->sneaking = armorParts2->sneaking = resModel->sneaking = mob->isSneaking();
double yp = y - mob->heightOffset;
if (mob->isSneaking())
if (mob->isSneaking() && !mob->instanceof(eTYPE_LOCALPLAYER))
{
yp -= 2 / 16.0f;
}
if (mob->getAnimOverrideBitmask() & (1 << HumanoidModel::eAnim_SmallModel))
{
if (mob->isRiding())
@ -357,6 +364,8 @@ void PlayerRenderer::render(shared_ptr<Entity> _mob, double x, double y, double
}
armorParts1->bowAndArrow = armorParts2->bowAndArrow = resModel->bowAndArrow = false;
armorParts1->sneaking = armorParts2->sneaking = resModel->sneaking = false;
armorParts1->elytraFlying = armorParts2->elytraFlying = resModel->elytraFlying = false;
armorParts1->elytraCrouching = armorParts2->elytraCrouching = resModel->elytraCrouching = false;
armorParts1->holdingRightHand = armorParts2->holdingRightHand = resModel->holdingRightHand = 0;
}
@ -497,8 +506,6 @@ void PlayerRenderer::additionalRendering(shared_ptr<LivingEntity> _mob, float a)
// 4J Stu - Fix for sprint-flying causing the cape to rotate up by 180 degrees or more
float xRot = 6.0f + lean / 2 + flap;
if (xRot > 64.0f) xRot = 64.0f;
glRotatef(xRot, 1, 0, 0);
glRotatef(lean2 / 2, 0, 0, 1);
glRotatef(-lean2 / 2, 0, 1, 0);
@ -507,101 +514,182 @@ void PlayerRenderer::additionalRendering(shared_ptr<LivingEntity> _mob, float a)
glPopMatrix();
}
shared_ptr<ItemInstance> item = mob->inventory->getSelected();
if (item != nullptr)
{
glPushMatrix();
resModel->arm0->translateTo(1 / 16.0f);
glTranslatef(-1 / 16.0f, 7 / 16.0f, 1 / 16.0f);
shared_ptr<ItemInstance> chestItem = mob->inventory->armor[LivingEntity::SLOT_CHEST - 1];
if (chestItem != nullptr && dynamic_cast<ElytraItem*>(chestItem->getItem()) != nullptr && !mob->isInvisible())
{
static ResourceLocation elytraTexture(L"item/elytra.png");
bindTexture(&elytraTexture);
if (mob->fishing != nullptr)
{
item = std::make_shared<ItemInstance>(Item::stick);
}
float brightness2 = SharedConstants::TEXTURE_LIGHTING ? 1 : mob->getBrightness(a);
glColor3f(brightness2, brightness2, brightness2);
UseAnim anim = UseAnim_none;//null;
if (mob->getUseItemDuration() > 0)
{
anim = item->getUseAnimation();
}
if (item->id < 256 && TileRenderer::canRender(Tile::tiles[item->id]->getRenderShape()))
{
float s = 8 / 16.0f;
glTranslatef(-0 / 16.0f, 3 / 16.0f, -5 / 16.0f);
s *= 0.75f;
glRotatef(20, 1, 0, 0);
glRotatef(45, 0, 1, 0);
glScalef(-s, -s, s);
}
else if (item->id == Item::bow->id)
{
float s = 10 / 16.0f;
glTranslatef(0 / 16.0f, 2 / 16.0f, 5 / 16.0f);
glRotatef(-20, 0, 1, 0);
glScalef(s, -s, s);
glRotatef(-100, 1, 0, 0);
glRotatef(45, 0, 1, 0);
}
else if (Item::items[item->id]->isHandEquipped())
{
float s = 10 / 16.0f;
if (Item::items[item->id]->isMirroredArt())
float wf = 0.2617994f;
float wf1 = -0.2617994f;
float wf2 = resModel->body->y;
float wf3 = 0.0f;
if (mob->isElytraFlying() && !EntityRenderDispatcher::instance->isInventoryRender)
{
glRotatef(180, 0, 0, 1);
glTranslatef(0, -2 / 16.0f, 0);
float f4 = 1.0f;
if (mob->yd < 0.0)
{
double speed = sqrt(mob->xd * mob->xd + mob->yd * mob->yd + mob->zd * mob->zd);
if (speed > 0.0)
{
double normY = mob->yd / speed;
f4 = 1.0f - (float)pow(-normY, 1.5);
if (f4 < 0.0f) f4 = 0.0f;
if (f4 > 1.0f) f4 = 1.0f;
}
}
wf = f4 * 0.34906584f + (1.0f - f4) * wf;
wf1 = f4 * (-(float)(PI / 2.0)) + (1.0f - f4) * wf1;
}
else if (mob->isSneaking())
{
wf = (float)(PI * 2.0 / 9.0);
wf1 = -(float)(PI / 4.0);
wf2 = 0.0f;
wf3 = 0.08726646f;
}
if (EntityRenderDispatcher::instance->isInventoryRender)
{
mob->rotateElytraX = wf;
mob->rotateElytraY = wf3;
mob->rotateElytraZ = wf1;
}
else
{
mob->rotateElytraX += (wf - mob->rotateElytraX) * 0.3f;
mob->rotateElytraY += (wf3 - mob->rotateElytraY) * 0.3f;
mob->rotateElytraZ += (wf1 - mob->rotateElytraZ) * 0.3f;
}
humanoidModel->elytraRight->y = wf2;
humanoidModel->elytraRight->xRot = mob->rotateElytraX;
humanoidModel->elytraRight->yRot = mob->rotateElytraY;
humanoidModel->elytraRight->zRot = mob->rotateElytraZ;
humanoidModel->elytraLeft->y = wf2;
humanoidModel->elytraLeft->xRot = mob->rotateElytraX;
humanoidModel->elytraLeft->yRot = -mob->rotateElytraY;
humanoidModel->elytraLeft->zRot = -mob->rotateElytraZ;
glPushMatrix();
glTranslatef(0, 0.0f, (2.0f + 0.125f) / 16.0f);
humanoidModel->renderElytra(1 / 16.0f, true);
glPopMatrix();
}
shared_ptr<ItemInstance> item = mob->inventory->getSelected();
if (item != nullptr)
{
glPushMatrix();
resModel->arm0->translateTo(1 / 16.0f);
glTranslatef(-1 / 16.0f, 7 / 16.0f, 1 / 16.0f);
if (mob->fishing != nullptr)
{
item = std::make_shared<ItemInstance>(Item::stick);
}
UseAnim anim = UseAnim_none;//null;
if (mob->getUseItemDuration() > 0)
{
if (anim == UseAnim_block)
anim = item->getUseAnimation();
}
if (item->id < 256 && TileRenderer::canRender(Tile::tiles[item->id]->getRenderShape()))
{
float s = 8 / 16.0f;
glTranslatef(-0 / 16.0f, 3 / 16.0f, -5 / 16.0f);
s *= 0.75f;
glRotatef(20, 1, 0, 0);
glRotatef(45, 0, 1, 0);
glScalef(-s, -s, s);
}
else if (item->id == Item::bow->id)
{
float s = 10 / 16.0f;
glTranslatef(0 / 16.0f, 2 / 16.0f, 5 / 16.0f);
glRotatef(-20, 0, 1, 0);
glScalef(s, -s, s);
glRotatef(-100, 1, 0, 0);
glRotatef(45, 0, 1, 0);
}
else if (Item::items[item->id]->isHandEquipped())
{
float s = 10 / 16.0f;
if (Item::items[item->id]->isMirroredArt())
{
glTranslatef(0.05f, 0, -0.1f);
glRotatef(-50, 0, 1, 0);
glRotatef(-10, 1, 0, 0);
glRotatef(-60, 0, 0, 1);
glRotatef(180, 0, 0, 1);
glTranslatef(0, -2 / 16.0f, 0);
}
if (mob->getUseItemDuration() > 0)
{
if (anim == UseAnim_block)
{
glTranslatef(0.05f, 0, -0.1f);
glRotatef(-50, 0, 1, 0);
glRotatef(-10, 1, 0, 0);
glRotatef(-60, 0, 0, 1);
}
}
glTranslatef(0, 3 / 16.0f, 0);
glScalef(s, -s, s);
glRotatef(-100, 1, 0, 0);
glRotatef(45, 0, 1, 0);
}
else if (item->id == Item::skull_Id)
{
float s = 0.5f;
glTranslatef(0, -3 / 16.0f, -4 / 16.0f);
glRotatef(45, 1, 0, 0);
glRotatef(45, 0, 1, 0);
glScalef(-s, -s, s);
}
else
{
float s = 6 / 16.0f;
glTranslatef(+4 / 16.0f, +3 / 16.0f, -3 / 16.0f);
glScalef(s, s, s);
glRotatef(60, 0, 0, 1);
glRotatef(-90, 1, 0, 0);
glRotatef(20, 0, 0, 1);
}
if (item->getItem()->hasMultipleSpriteLayers())
{
for (int layer = 0; layer <= 1; layer++)
{
int col = item->getItem()->getColor(item, layer);
float red = ((col >> 16) & 0xff) / 255.0f;
float g = ((col >> 8) & 0xff) / 255.0f;
float b = ((col) & 0xff) / 255.0f;
glColor4f(red, g, b, 1);
this->entityRenderDispatcher->itemInHandRenderer->renderItem(mob, item, layer, false);
}
}
glTranslatef(0, 3 / 16.0f, 0);
glScalef(s, -s, s);
glRotatef(-100, 1, 0, 0);
glRotatef(45, 0, 1, 0);
}
else
{
float s = 6 / 16.0f;
glTranslatef(+4 / 16.0f, +3 / 16.0f, -3 / 16.0f);
glScalef(s, s, s);
glRotatef(60, 0, 0, 1);
glRotatef(-90, 1, 0, 0);
glRotatef(20, 0, 0, 1);
}
if (item->getItem()->hasMultipleSpriteLayers())
{
for (int layer = 0; layer <= 1; layer++)
else
{
int col = item->getItem()->getColor(item, layer);
int col = item->getItem()->getColor(item, 0);
float red = ((col >> 16) & 0xff) / 255.0f;
float g = ((col >> 8) & 0xff) / 255.0f;
float b = ((col) & 0xff) / 255.0f;
glColor4f(red, g, b, 1);
this->entityRenderDispatcher->itemInHandRenderer->renderItem(mob, item, layer, false);
this->entityRenderDispatcher->itemInHandRenderer->renderItem(mob, item, 0);
}
}
else
{
int col = item->getItem()->getColor(item, 0);
float red = ((col >> 16) & 0xff) / 255.0f;
float g = ((col >> 8) & 0xff) / 255.0f;
float b = ((col) & 0xff) / 255.0f;
glColor4f(red, g, b, 1);
this->entityRenderDispatcher->itemInHandRenderer->renderItem(mob, item, 0);
glPopMatrix();
}
glPopMatrix();
}
}
@ -746,10 +834,34 @@ void PlayerRenderer::setupRotations(shared_ptr<LivingEntity> _mob, float bob, fl
glRotatef(getFlipDegrees(mob), 0, 0, 1);
glRotatef(270, 0, 1, 0);
}
else
else if (mob->isElytraFlying() && !EntityRenderDispatcher::instance->isInventoryRender)
{
LivingEntityRenderer::setupRotations(mob, bob, bodyRot, a);
float f = (float)mob->ticksElytraFlying + a;
float f1 = Mth::clamp(f * f / 100.0f, 0.0f, 1.0f);
glRotatef(f1 * (-90.0f - mob->xRot), 1.0f, 0.0f, 0.0f);
Vec3* look = mob->getLookAngle();
double d0 = mob->xd * mob->xd + mob->zd * mob->zd;
double d1 = look->x * look->x + look->z * look->z;
if (d0 > 0.0 && d1 > 0.0)
{
double d2 = (mob->xd * look->x + mob->zd * look->z) / (sqrt(d0) * sqrt(d1));
if (d2 > 1.0) d2 = 1.0;
if (d2 < -1.0) d2 = -1.0;
double d3 = mob->xd * look->z - mob->zd * look->x;
float sign = (d3 >= 0.0) ? 1.0f : -1.0f;
glRotatef(sign * (float)(acos(d2) * 180.0 / PI), 0.0f, 1.0f, 0.0f);
}
}
else
{
LivingEntityRenderer::setupRotations(mob, bob, bodyRot, a);
}
}
// 4J Added override to stop rendering shadow if player is invisible

View file

@ -570,6 +570,8 @@ void PreStitchedTextureMap::loadUVs()
ADD_ICON(14, 6, L"rabbitHide")
ADD_ICON(13, 14, L"prismarineCrystal");
ADD_ICON(13, 13, L"prismarineShard");
ADD_ICON(13, 12, L"elytra");
ADD_ICON(13, 11, L"broken_elytra");
ADD_ICON_WITH_NAME(14, 7, L"compassP0", L"compass") // 4J Added
ADD_ICON_WITH_NAME(14, 8, L"compassP1", L"compass") // 4J Added

View file

@ -9393,4 +9393,7 @@ All Ender Chests in a world are linked. Items placed into an Ender Chest are acc
<data name="IDS_TILE_STONESLAB_REDSAND">
<value>Red Sandstone Slab</value>
</data>
<data name="IDS_ITEM_ELYTRA">
<value>Elytra</value>
</data>
</root>

View file

@ -92,6 +92,8 @@ ArmorRecipes::_eArmorType ArmorRecipes::GetArmorType(int iId)
case Item::chestplate_iron_Id:
case Item::chestplate_diamond_Id:
case Item::chestplate_gold_Id:
case Item::elytra_Id:
return eArmorType_Chestplate;
break;

View file

@ -31,6 +31,11 @@ bool ArmorSlot::mayPlace(shared_ptr<ItemInstance> item)
{
return slotNum == 0;
}
if (item->getItem()->id == Item::elytra_Id)
{
return slotNum == ArmorItem::SLOT_TORSO;
}
return false;
}

View file

@ -0,0 +1,92 @@
#include "stdafx.h"
#include "net.minecraft.world.h"
#include "net.minecraft.world.level.h"
#include "net.minecraft.world.entity.h"
#include "net.minecraft.world.entity.player.h"
#include "net.minecraft.world.item.h"
#include "ElytraItem.h"
// Internal item ID: 187 → public item ID: 443 (256 + 187)
ElytraItem::ElytraItem() : Item(187)
{
maxStackSize = 1;
setMaxDamage(432);
}
bool ElytraItem::isFlyEnabled(shared_ptr<ItemInstance> item)
{
return item->getDamageValue() < item->getMaxDamage() - 1;
}
bool ElytraItem::TestUse(shared_ptr<ItemInstance> instance, Level* level, shared_ptr<Player> player)
{
return true;
}
shared_ptr<ItemInstance> ElytraItem::use(shared_ptr<ItemInstance> instance, Level* level, shared_ptr<Player> player)
{
// Elytra equips to the chest slot (SLOT_CHEST = 3, armor array index = SLOT_CHEST - 1 = 2)
const int chestSlot = LivingEntity::SLOT_CHEST - 1;
ItemInstance copy = *instance->copy_not_shared();
if (!player->abilities.instabuild)
{
if (player->inventory->armor[chestSlot] == nullptr)
{
player->inventory->armor[chestSlot] = make_shared<ItemInstance>(copy);
player->inventory->removeItemNoUpdate(player->inventory->selected);
instance->count = 0;
}
else
{
player->inventory->setItem(player->inventory->selected, player->inventory->armor[chestSlot]);
player->inventory->armor[chestSlot] = make_shared<ItemInstance>(copy);
}
}
else
{
if (player->inventory->armor[chestSlot] == nullptr)
{
player->inventory->armor[chestSlot] = make_shared<ItemInstance>(copy);
}
else
{
player->inventory->setItem(player->inventory->selected, player->inventory->armor[chestSlot]);
player->inventory->armor[chestSlot] = make_shared<ItemInstance>(copy);
}
}
// Play cloth armor equip sound (range 194199)
player->playSound(194, 0.5f, 1.0f);
return instance;
}
bool ElytraItem::isValidRepairItem(shared_ptr<ItemInstance> source, shared_ptr<ItemInstance> repairItem)
{
return repairItem->id == Item::leather_Id;
}
void ElytraItem::registerIcons(IconRegister* iconRegister)
{
Item::registerIcons(iconRegister);
m_brokenElytraIcon = iconRegister->registerIcon(L"broken_elytra");
}
Icon* ElytraItem::getLayerIcon(int auxValue, int spriteLayer)
{
// auxValue may be the damage value in certain render paths
if (auxValue < getMaxDamage() - 1)
return icon;
else
return m_brokenElytraIcon;
}
Icon* ElytraItem::getIcon(int auxValue)
{
if (auxValue < getMaxDamage() - 1)
return icon;
else
return m_brokenElytraIcon;
}

View file

@ -0,0 +1,20 @@
#pragma once
#include "Item.h"
class ElytraItem : public Item
{
public:
ElytraItem();
static bool isFlyEnabled(shared_ptr<ItemInstance> item);
virtual bool TestUse(shared_ptr<ItemInstance> instance, Level* level, shared_ptr<Player> player) override;
virtual shared_ptr<ItemInstance> use(shared_ptr<ItemInstance> instance, Level* level, shared_ptr<Player> player) override;
virtual bool isValidRepairItem(shared_ptr<ItemInstance> source, shared_ptr<ItemInstance> repairItem) override;
virtual void registerIcons(IconRegister* iconRegister) override;
virtual Icon* getLayerIcon(int auxValue, int spriteLayer) override;
virtual Icon* getIcon(int auxValue) override;
Icon* m_brokenElytraIcon = nullptr;
};

View file

@ -17,6 +17,7 @@
#include "Item.h"
#include "HangingEntityItem.h"
#include "HtmlString.h"
#include "ElytraItem.h"
typedef Item::Tier _Tier;
@ -271,6 +272,8 @@ Item* Item::rabbitStew = nullptr;
Item* Item::prismarine_crystal = nullptr;
Item* Item::prismarine_shard = nullptr;
Item* Item::elytra = nullptr;
void Item::staticCtor()
{
@ -537,9 +540,11 @@ void Item::staticCtor()
Item::armor_stand = (new ArmorStandItem(160)) ->setBaseItemTypeAndMaterial(eBaseItemType_HangingItem,eMaterial_cloth)->setIconName(L"armorStand")->setDescriptionId(IDS_ITEM_ARMOR_STAND)->setUseDescriptionId(IDS_DESC_ARMOR_STAND);
Item::prismarine_crystal = (new Item(154))->setIconName(L"prismarineCrystal")->setDescriptionId(IDS_ITEM_PRISMARINE_CRYSTAL)->setUseDescriptionId(IDS_ITEM_PRISMARINE_CRYSTAL_DESC);
Item::prismarine_shard = (new Item(153))->setIconName(L"prismarineShard")->setDescriptionId(IDS_ITEM_PRISMARINE_SHARD)->setUseDescriptionId(IDS_ITEM_PRISMARINE_SHARD_DESC);
Item::elytra = (new ElytraItem())->setBaseItemTypeAndMaterial(eBaseItemType_chestplate, eMaterial_cloth)->setIconName(L"elytra")->setDescriptionId(IDS_ITEM_ELYTRA)->setUseDescriptionId(IDS_ITEM_ELYTRA);
}
// 4J Stu - We need to do this after the staticCtor AND after staticCtors for other class
// eg Recipes
void Item::staticInit()

View file

@ -436,6 +436,7 @@ public:
static Item* prismarine_crystal;
static Item* prismarine_shard;
static Item* elytra;
static const int shovel_iron_Id = 256;
@ -667,6 +668,8 @@ public:
static const int door_acacia_Id = 430;
static const int door_dark_Id = 431;
static const int elytra_Id = 443;
//TU31

View file

@ -42,6 +42,7 @@
#include "../Minecraft.Client/LocalPlayer.h"
#include "../Minecraft.Client/HumanoidModel.h"
#include "SoundTypes.h"
#include "ElytraItem.h"
@ -83,6 +84,14 @@ void Player::_init()
m_uiDebugOptions=0L;
jumpTriggerTime = 0;
ticksElytraFlying = 0;
rotateElytraX = 0.2617994f;
rotateElytraY = 0.0f;
rotateElytraZ = -0.2617994f;
m_elytraImpactYd = 0.0f;
m_wasElytraFlying = false;
m_elytraFallProtectTicks = 0;
takeXpDelay = 0;
experienceLevel = totalExperience = 0;
experienceProgress = 0.0f;
@ -1000,6 +1009,8 @@ void Player::serverAiStep()
void Player::aiStep()
{
if (jumpTriggerTime > 0) jumpTriggerTime--;
if (m_elytraFallProtectTicks > 0) m_elytraFallProtectTicks--;
if (level->difficulty == Difficulty::PEACEFUL && getHealth() < getMaxHealth() && level->getGameRules()->getBoolean(GameRules::RULE_NATURAL_REGENERATION))
{
@ -1008,8 +1019,43 @@ void Player::aiStep()
inventory->tick();
oBob = bob;
if (jumping && !onGround && yd < 0.0 && !isElytraFlying() && !abilities.flying && jumpTriggerTime == 0)
{
shared_ptr<ItemInstance> chestItem = inventory->armor[LivingEntity::SLOT_CHEST - 1];
if (chestItem != nullptr && chestItem->getItem() != nullptr)
{
ElytraItem* elytra = dynamic_cast<ElytraItem*>(chestItem->getItem());
if (elytra != nullptr && ElytraItem::isFlyEnabled(chestItem))
setElytraFlying(true);
}
}
LivingEntity::aiStep();
if (isElytraFlying() && !onGround)
{
ticksElytraFlying++;
if (!level->isClientSide && ticksElytraFlying % 20 == 0)
{
shared_ptr<ItemInstance> chestItem = inventory->armor[LivingEntity::SLOT_CHEST - 1];
if (chestItem != nullptr && dynamic_cast<ElytraItem*>(chestItem->getItem()) != nullptr)
{
chestItem->hurtAndBreak(1, dynamic_pointer_cast<LivingEntity>(shared_from_this()));
if (!ElytraItem::isFlyEnabled(chestItem))
setElytraFlying(false);
}
else
{
setElytraFlying(false);
}
}
}
AttributeInstance *speed = getAttribute(SharedMonsterAttributes::MOVEMENT_SPEED);
if (!level->isClientSide) speed->setBaseValue(abilities.getWalkingSpeed());
flyingSpeed = defaultFlySpeed;
@ -1389,6 +1435,8 @@ bool Player::hurt(DamageSource *source, float dmg)
if ( hasInvulnerablePrivilege() || (abilities.invulnerable && !source->isBypassInvul()) ) return false;
// 4J-JEV: Fix for PSVita: #3987 - [IN GAME] The user can take damage/die, when attempting to re-enter fly mode when falling from a height.
if ( source == DamageSource::fall && isAllowedToFly() && abilities.flying ) return false;
@ -2110,7 +2158,90 @@ void Player::travel(float xa, float ya)
{
double preX = x, preY = y, preZ = z;
if (abilities.flying && riding == nullptr)
m_elytraImpactYd = (float)yd;
if (isElytraFlying() && riding == nullptr)
{
double preHorizSpeed = Mth::sqrt(xd * xd + zd * zd);
if (yd > -0.5)
fallDistance = 1.0f;
Vec3* look = getLookAngle();
float pitchRad = xRot * (PI / 180.0f);
double horizLookLen = Mth::sqrt(look->x * look->x + look->z * look->z);
double horizSpeed = Mth::sqrt(xd * xd + zd * zd);
float cosPitch = Mth::cos(pitchRad);
cosPitch = cosPitch * cosPitch;
yd += -0.08 + (double)cosPitch * 0.06;
if (yd < 0.0 && horizLookLen > 0.0)
{
double thrust = yd * -0.1 * (double)cosPitch;
yd += thrust;
xd += look->x * thrust / horizLookLen;
zd += look->z * thrust / horizLookLen;
}
if (pitchRad < 0.0f && horizLookLen > 0.0)
{
double boost = horizSpeed * (double)(-Mth::sin(pitchRad)) * 0.04;
yd += boost * 3.2;
xd -= look->x * boost / horizLookLen;
zd -= look->z * boost / horizLookLen;
}
if (horizLookLen > 0.0)
{
xd += (look->x / horizLookLen * horizSpeed - xd) * 0.1;
zd += (look->z / horizLookLen * horizSpeed - zd) * 0.1;
}
// Air drag
xd *= 0.99;
yd *= 0.98;
zd *= 0.99;
if (jumping && abilities.instabuild)
yd += (double)abilities.getFlyingSpeed() * 3.0;
m_elytraImpactYd = (float)yd;
move(xd, yd, zd);
if (horizontalCollision && !verticalCollision)
{
double postHorizSpeed = Mth::sqrt(xd * xd + zd * zd);
double speedLost = preHorizSpeed - postHorizSpeed;
float damage = (float)(speedLost * 10.0 - 3.0);
if (damage > 0.0f)
onElytraKineticDamage(damage);
}
if (onGround)
{
bool canStandUp = true;
if (level != nullptr)
{
float halfW = bbWidth / 2.0f;
AABB* standingBB = AABB::newTemp(
x - halfW, bb->y0, z - halfW,
x + halfW, bb->y0 + 1.8, z + halfW);
AABBList* collisions = level->getCubes(shared_from_this(), standingBB, true);
canStandUp = collisions->empty();
}
if (canStandUp)
setElytraFlying(false);
}
}
else if (abilities.flying && riding == nullptr)
{
double ydo = yd;
float ofs = flyingSpeed;
@ -2278,6 +2409,13 @@ void Player::checkRidingStatistiscs(double dx, double dy, double dz)
}
}
}
void Player::checkFallDamage(double ya, bool onGround)
{
LivingEntity::checkFallDamage(ya, onGround);
}
void Player::causeFallDamage(float distance)
@ -2686,6 +2824,37 @@ bool Player::isCapeHidden()
{
return getPlayerFlag(FLAG_HIDE_CAPE);
}
bool Player::isElytraFlying()
{
return getPlayerFlag(FLAG_ELYTRA_FLYING);
}
void Player::onElytraKineticDamage(float damage)
{
hurt(DamageSource::fall, damage);
}
void Player::setElytraFlying(bool flying)
{
setPlayerFlag(FLAG_ELYTRA_FLYING, flying);
if (flying)
{
m_wasElytraFlying = false;
setSize(0.6f, 0.6f);
fallDistance = 0.0f;
}
else
{
ticksElytraFlying = 0;
m_wasElytraFlying = true;
m_elytraFallProtectTicks = 60;
setSize(0.6f, 1.8f);
}
}
bool Player::isPushedByWater()
{

View file

@ -61,6 +61,8 @@ private:
protected:
static const int FLAG_HIDE_CAPE = 1;
static const int FLAG_ELYTRA_FLYING = 2; // bit 2 of DATA_PLAYER_FLAGS_ID
public:
shared_ptr<Inventory> inventory;
@ -79,6 +81,20 @@ protected:
int jumpTriggerTime;
public:
int ticksElytraFlying;
float rotateElytraX;
float rotateElytraY;
float rotateElytraZ;
float m_elytraImpactYd;
bool m_wasElytraFlying;
int m_elytraFallProtectTicks;
bool isElytraFlying();
virtual void setElytraFlying(bool flying);
virtual void onElytraKineticDamage(float damage);
public:
BYTE userType;
float oBob, bob;
@ -351,6 +367,7 @@ private:
bool m_bAwardedOnARail;
protected:
virtual void checkFallDamage(double ya, bool onGround) override;
virtual void causeFallDamage(float distance);
public:

View file

@ -261,6 +261,8 @@ enum eSOUND_TYPE
eSoundType_ITEM_ARMOR_equipGeneric6,
eSoundType_DAMAGE_CRITICAL,
eSoundType_ITEM_ELYTRA_FLYING,
eSoundType_MAX
};

View file

@ -1077,6 +1077,8 @@ set(_MINECRAFT_WORLD_COMMON_NET_MINECRAFT_WORLD_ITEM
"${CMAKE_CURRENT_SOURCE_DIR}/DyePowderItem.h"
"${CMAKE_CURRENT_SOURCE_DIR}/EggItem.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/EggItem.h"
"${CMAKE_CURRENT_SOURCE_DIR}/ElytraItem.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/ElytraItem.h"
"${CMAKE_CURRENT_SOURCE_DIR}/EmptyMapItem.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/EmptyMapItem.h"
"${CMAKE_CURRENT_SOURCE_DIR}/EnchantedBookItem.cpp"

View file

@ -1,6 +1,7 @@
#pragma once
#include "ArmorItem.h"
#include "ElytraItem.h"
#include "BedItem.h"
#include "BoatItem.h"
#include "BowItem.h"