Files
2026-05-03 17:59:57 +03:00

627 lines
14 KiB
C++

#include "stdafx.h"
#include "com.mojang.nbt.h"
#include "net.minecraft.world.level.tile.h"
#include "net.minecraft.world.phys.h"
#include "net.minecraft.world.level.h"
#include "net.minecraft.world.item.h"
#include "net.minecraft.world.entity.h"
#include "net.minecraft.world.entity.player.h"
#include "net.minecraft.world.entity.ai.attributes.h"
#include "net.minecraft.world.entity.monster.h"
#include "net.minecraft.world.damagesource.h"
#include "SharedConstants.h"
#include "Guardian.h"
#include "../Minecraft.Client/Textures.h"
#include "../Minecraft.World/Mth.h"
#include "../Minecraft.World/net.minecraft.world.entity.animal.h"
#include "MobEffect.h"
#include "MobEffectInstance.h"
void Guardian::_init()
{
clientSideTailAnimation = 0.0f;
clientSideTailAnimationO = 0.0f;
clientSideSpikesAnimation = 0.0f;
clientSideSpikesAnimationO = 0.0f;
clientSideAttackTime = 0.0f;
clientSideTouchedGround = false;
xBodyRot = xBodyRotO = 0.0f;
zBodyRot = zBodyRotO = 0.0f;
tx = ty = tz = 0.0f;
attackTimer = 0;
}
Guardian::Guardian(Level *level) : WaterAnimal(level)
{
this->defineSynchedData();
registerAttributes();
setHealth(getMaxHealth());
_init();
if (isElder()) {
this->setSize(1.9975f, 1.9975f);
}
else {
this->setSize(0.85f, 0.85f);
}
}
void Guardian::registerAttributes()
{
WaterAnimal::registerAttributes();
getAttributes()->registerAttribute(SharedMonsterAttributes::ATTACK_DAMAGE);
getAttribute(SharedMonsterAttributes::ATTACK_DAMAGE)->setBaseValue(6.0);
getAttribute(SharedMonsterAttributes::MOVEMENT_SPEED)->setBaseValue(0.5);
getAttribute(SharedMonsterAttributes::FOLLOW_RANGE)->setBaseValue(16.0);
getAttribute(SharedMonsterAttributes::MAX_HEALTH)->setBaseValue(30.0);
}
void Guardian::defineSynchedData()
{
WaterAnimal::defineSynchedData();
entityData->define(16, (byte)0);
entityData->define(17, 0);
}
// NBT
void Guardian::readAdditionalSaveData(CompoundTag *tag)
{
WaterAnimal::readAdditionalSaveData(tag);
setElder(tag->getBoolean(L"Elder"));
}
void Guardian::addAdditonalSaveData(CompoundTag *tag)
{
WaterAnimal::addAdditonalSaveData(tag);
tag->putBoolean(L"Elder", isElder());
}
bool Guardian::isElder()
{
return (entityData->getByte(16) & 4) != 0;
}
void Guardian::setElder(bool elder)
{
byte flags = entityData->getByte(16);
if (elder)
{
entityData->set(16, (byte)(flags | 4));
this->setSize(1.9975f, 1.9975f);
getAttribute(SharedMonsterAttributes::MOVEMENT_SPEED)->setBaseValue(0.30000001192092896);
getAttribute(SharedMonsterAttributes::ATTACK_DAMAGE)->setBaseValue(8.0);
getAttribute(SharedMonsterAttributes::MAX_HEALTH)->setBaseValue(80.0);
setHealth(getMaxHealth());
}
else
{
entityData->set(16, (byte)(flags & ~4));
this->setSize(0.85f, 0.85f);
}
}
void Guardian::setElderClient()
{
setElder(true);
clientSideSpikesAnimation = clientSideSpikesAnimationO = 1.0f;
}
bool Guardian::isMoving()
{
return (entityData->getByte(16) & 2) != 0;
}
void Guardian::setMoving(bool moving)
{
byte flags = entityData->getByte(16);
if (moving)
entityData->set(16, (byte)(flags | 2));
else
entityData->set(16, (byte)(flags & ~2));
}
int Guardian::getTargetedEntityId()
{
return entityData->getInteger(17);
}
void Guardian::setTargetedEntityId(int id)
{
entityData->set(17, id);
entityData->markDirty(17);
}
bool Guardian::hasTargetedEntity()
{
return entityData->getInteger(17) != 0;
}
shared_ptr<LivingEntity> Guardian::getTargetedEntity()
{
if (!hasTargetedEntity())
return nullptr;
if (level->isClientSide)
{
if (targetedEntity != nullptr)
return targetedEntity;
shared_ptr<Entity> entity = level->getEntity(getTargetedEntityId());
if (entity != nullptr && dynamic_pointer_cast<LivingEntity>(entity))
{
targetedEntity = dynamic_pointer_cast<LivingEntity>(entity);
return targetedEntity;
}
return nullptr;
}
shared_ptr<Entity> e = level->getEntity(getTargetedEntityId());
return dynamic_pointer_cast<LivingEntity>(e);
}
int Guardian::getAttackDuration()
{
// Java func_175464_ck
return isElder() ? 60 : 80;
}
float Guardian::getAttackAnimationScale(float partialTicks)
{
return (clientSideAttackTime + partialTicks) / (float)getAttackDuration();
}
// Java func_175471_a
float Guardian::getTailAnimation(float partialTicks)
{
return clientSideSpikesAnimationO + (clientSideSpikesAnimation - clientSideSpikesAnimationO) * partialTicks;
}
// Java func_175469_o
float Guardian::getSpikesAnimation(float partialTicks)
{
return isMoving() ? 1.0f : 0.0f;
}
void Guardian::dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel)
{
//loot (prismarine, sponge)
}
float Guardian::getEyeHeight()
{
return bbHeight * 0.5f;
}
bool Guardian::isInWater()
{
return level->checkAndHandleWater(bb->grow(0, -0.6f, 0), Material::water, shared_from_this());
}
float Guardian::rotlerp(float a, float b, float max)
{
float f = Mth::wrapDegrees(b - a);
if (f > max) f = max;
if (f < -max) f = -max;
return a + f;
}
void Guardian::lookAt(shared_ptr<Entity> e, float yMax, float xMax)
{
double d0 = e->x - x;
double d1 = e->z - z;
double d2;
if (dynamic_pointer_cast<LivingEntity>(e))
{
shared_ptr<LivingEntity> living = dynamic_pointer_cast<LivingEntity>(e);
d2 = living->y + living->getEyeHeight() - (y + getEyeHeight());
}
else
{
d2 = (e->bb->y0 + e->bb->y1) / 2.0 - (y + getEyeHeight());
}
double d3 = sqrt(d0 * d0 + d1 * d1);
float f = (float)(atan2(d1, d0) * 180.0 / PI) - 90.0f;
float f1 = (float)(-(atan2(d2, d3) * 180.0 / PI));
xRot = rotlerp(xRot, f1, xMax);
yRot = rotlerp(yRot, f, yMax);
}
void Guardian::serverAiStep()
{
WaterAnimal::serverAiStep();
if (isElder() && !level->isClientSide)
{
bool periodicTick = ((tickCount + entityId) % 1200 == 0);
auto& players = level->players;
for (int i = 0; i < (int)players.size(); i++)
{
shared_ptr<Player> player = players[i];
if (!player) continue;
if (distanceToSqr(player) >= 2500.0) continue;
if (player->abilities.invulnerable) continue;
MobEffectInstance* existing = player->getEffect(MobEffect::digSlowdown);
bool noEffect = (existing == nullptr);
bool weakEffect = (existing != nullptr && existing->getAmplifier() < 2);
bool shortEffect= (existing != nullptr && existing->getDuration() < 1200);
bool applyNow = noEffect || weakEffect || (periodicTick && shortEffect);
if (applyNow)
{
player->addEffect(new MobEffectInstance(MobEffect::digSlowdown->id, 6000, 2));
level->broadcastEntityEvent(shared_from_this(), (byte)8);
playCurseSound();
}
}
}
if (!hasTargetedEntity())
{
shared_ptr<Player> nearest = level->getNearestPlayer(x, y, z, 16.0);
if (nearest != nullptr && canSee(nearest) && !nearest->abilities.invulnerable)
{
setTargetedEntityId(nearest->entityId);
attackTimer = 0;
}
}
shared_ptr<LivingEntity> target = getTargetedEntity();
if (target != nullptr)
{
if (!target->isAlive() || distanceToSqr(target) > 256.0 || !canSee(target))
{
setTargetedEntityId(0);
target = nullptr;
}
}
if (target != nullptr)
{
lookAt(target, 90.0f, 90.0f);
setMoving(false);
tx = ty = tz = 0.0f;
attackTimer++;
if (attackTimer == 1)
{
level->broadcastEntityEvent(shared_from_this(), 21);
playAttackSound();
}
else if (attackTimer >= getAttackDuration())
{
float dmg = 1.0f;
if (isElder()) dmg += 2.0f;
target->hurt(DamageSource::mobAttack(dynamic_pointer_cast<LivingEntity>(shared_from_this())), dmg);
target->hurt(DamageSource::magic,
(float)getAttribute(SharedMonsterAttributes::ATTACK_DAMAGE)->getValue());
setTargetedEntityId(0);
attackTimer = 0;
}
}
else
{
attackTimer = 0;
setTargetedEntityId(0);
noActionTime++;
if (noActionTime > 100 || random->nextInt(50) == 0 ||
(tx == 0.0f && ty == 0.0f && tz == 0.0f))
{
float angle = random->nextFloat() * (float)PI * 2.0f;
tx = Mth::cos(angle) * 0.2f;
ty = -0.1f + random->nextFloat() * 0.2f;
tz = Mth::sin(angle) * 0.2f;
noActionTime = 0;
}
setMoving(true);
}
if (isMoving())
{
xd += (tx - xd) * 0.1f;
yd += (ty - yd) * 0.1f;
zd += (tz - zd) * 0.1f;
double horizontalMovement = sqrt(xd * xd + zd * zd);
yBodyRot += ((-static_cast<float>(atan2(xd, zd)) * 180.0f / (float)PI) - yBodyRot) * 0.1f;
yRot = yBodyRot;
zBodyRot = zBodyRot + (float)PI * 0.05f;
xBodyRot += ((-static_cast<float>(atan2(horizontalMovement, yd)) * 180.0f / (float)PI) - xBodyRot) * 0.1f;
}
}
void Guardian::aiStep()
{
if (level->isClientSide)
{
updateSize(isElder());
clientSideTailAnimationO = clientSideTailAnimation;
if (!isInWater() && onGround)
{
if (clientSideTouchedGround)
{
playFlopSound();
clientSideTouchedGround = false;
}
}
if (!isInWater())
{
clientSideTailAnimation = 2.0f;
clientSideTouchedGround = yd < 0.0 &&
level->hasChunkAt(Mth::floor(x), Mth::floor(y - 1), Mth::floor(z));
}
else if (isMoving())
{
if (clientSideTailAnimation < 0.5f)
clientSideTailAnimation = 4.0f;
else
clientSideTailAnimation += (0.5f - clientSideTailAnimation) * 0.1f;
}
else
{
clientSideTailAnimation += (0.125f - clientSideTailAnimation) * 0.2f;
}
clientSideSpikesAnimationO = clientSideSpikesAnimation;
clientSideSpikesAnimation += clientSideTailAnimation;
if (hasTargetedEntity() && clientSideAttackTime < (float)getAttackDuration())
clientSideAttackTime++;
else if (!hasTargetedEntity())
clientSideAttackTime = 0.0f;
}
if (isInWater())
{
setAirSupply(300);
}
else if (onGround)
{
yd += 0.5;
xd += (random->nextFloat() * 2.0f - 1.0f) * 0.4f;
zd += (random->nextFloat() * 2.0f - 1.0f) * 0.4f;
yRot = random->nextFloat() * 360.0f;
onGround = false;
hasImpulse = true;
}
if (hasTargetedEntity())
yRot = yHeadRot;
xBodyRotO = xBodyRot;
zBodyRotO = zBodyRot;
WaterAnimal::aiStep();
}
void Guardian::updateSize(bool elder)
{
if (elder)
this->setSize(1.9975f, 1.9975f);
else
setSize(0.85f, 0.85f);
}
void Guardian::travel(float xa, float ya)
{
if (level->isClientSide)
{
if (isInWater())
{
move(xd, yd, zd);
xd *= 0.9f;
yd *= 0.9f;
zd *= 0.9f;
}
else
{
WaterAnimal::travel(xa, ya);
}
}
else
{
if (isInWater())
{
moveRelative(xa, ya, 0.1f);
move(xd, yd, zd);
xd *= 0.9f;
yd *= 0.9f;
zd *= 0.9f;
}
else
{
WaterAnimal::travel(xa, ya);
}
}
}
MobGroupData *Guardian::finalizeMobSpawn(MobGroupData *groupData, int extraData)
{
WaterAnimal::finalizeMobSpawn(groupData, extraData);
if (extraData == 1)
setElder(true);
return groupData;
}
void Guardian::handleEntityEvent(byte eventId)
{
if (eventId == 8 && level->isClientSide && isElder())
{
shared_ptr<Player> localPlayer = level->getNearestPlayer(x, y, z, 64.0);
if (localPlayer != nullptr)
{
level->addParticle(
eParticleType_mobAppearance,
localPlayer->x,
localPlayer->bb->y0,
localPlayer->z,
0.0, 0.0, 0.0);
}
}
WaterAnimal::handleEntityEvent(eventId);
}
int Guardian::getAmbientSound()
{
if (!isInWater())
{
if (isElder())
{
return eSoundType_MOB_ELDER_GUARDIAN_IDLE;
}
else
{
return eSoundType_MOB_GUARDIAN_LAND_IDLE;
}
}
return -1;
}
int Guardian::getHurtSound()
{
if (!isInWater())
{
if (isElder())
{
return eSoundType_MOB_ELDER_GUARDIAN_HIT;
}
else
{
return eSoundType_MOB_GUARDIAN_LAND_HIT;
}
}
else
{
if (isElder())
{
return eSoundType_MOB_ELDER_GUARDIAN_HIT;
}
else
{
return eSoundType_MOB_GUARDIAN_HIT;
}
}
}
int Guardian::getDeathSound()
{
if (!isInWater())
{
if (isElder())
return eSoundType_MOB_ELDER_GUARDIAN_DEATH;
else
return eSoundType_MOB_GUARDIAN_LAND_DEATH;
}
else
{
if (isElder())
return eSoundType_MOB_ELDER_GUARDIAN_DEATH;
else
return eSoundType_MOB_GUARDIAN_DEATH;
}
}
bool Guardian::makeStepSound()
{
return false;
}
void Guardian::playFlopSound()
{
level->playSound(x, y, z, eSoundType_MOB_GUARDIAN_FLOP, getSoundVolume(), 1.0f);
}
void Guardian::playAttackSound()
{
level->playSound(x, y, z, eSoundType_MOB_GUARDIAN_ATTACK_LOOP, getSoundVolume(), 1.0f);
}
void Guardian::playCurseSound()
{
level->playSound(x, y, z, eSoundType_MOB_ELDER_GUARDIAN_CURSE, getSoundVolume(), 1.0f);
}
float Guardian::getSoundVolume()
{
return 1.0f;
}
int Guardian::getDeathLoot()
{
return 0;
}