Files
Loki Rautio 087b7e7abf Revert "Project modernization (#630)"
This code was not tested and breaks in Release builds, reverting to restore
functionality of the nightly. All in-game menus do not work and generating
a world crashes.

This reverts commit a9be52c41a.
2026-03-07 21:12:22 -06:00

224 lines
6.5 KiB
C++

#include "stdafx.h"
#include "net.minecraft.world.effect.h"
#include "net.minecraft.world.damagesource.h"
#include "net.minecraft.world.entity.h"
#include "net.minecraft.world.entity.ai.attributes.h"
#include "net.minecraft.world.entity.ai.goal.h"
#include "net.minecraft.world.entity.ai.goal.target.h"
#include "net.minecraft.world.entity.monster.h"
#include "net.minecraft.world.entity.projectile.h"
#include "net.minecraft.world.item.h"
#include "net.minecraft.world.item.alchemy.h"
#include "net.minecraft.world.level.h"
#include "net.minecraft.world.phys.h"
#include "Witch.h"
AttributeModifier *Witch::SPEED_MODIFIER_DRINKING = (new AttributeModifier(eModifierId_MOB_WITCH_DRINKSPEED, -0.25f, AttributeModifier::OPERATION_ADDITION))->setSerialize(false);
const int Witch::DEATH_LOOT[Witch::DEATH_LOOT_COUNT] = {
Item::yellowDust_Id, Item::sugar_Id, Item::redStone_Id, Item::spiderEye_Id, Item::glassBottle_Id, Item::gunpowder_Id, Item::stick_Id, Item::stick_Id,
};
Witch::Witch(Level *level) : Monster(level)
{
// 4J Stu - This function call had to be moved here from the Entity ctor to ensure that
// the derived version of the function is called
defineSynchedData();
registerAttributes();
setHealth(getMaxHealth());
usingTime = 0;
goalSelector.addGoal(1, new FloatGoal(this));
goalSelector.addGoal(2, new RangedAttackGoal(this, this, 1.0, SharedConstants::TICKS_PER_SECOND * 3, 10));
goalSelector.addGoal(2, new RandomStrollGoal(this, 1.0));
goalSelector.addGoal(3, new LookAtPlayerGoal(this, typeid(Player), 8));
goalSelector.addGoal(3, new RandomLookAroundGoal(this));
targetSelector.addGoal(1, new HurtByTargetGoal(this, false));
targetSelector.addGoal(2, new NearestAttackableTargetGoal(this, typeid(Player), 0, true));
}
void Witch::defineSynchedData()
{
Monster::defineSynchedData();
getEntityData()->define(DATA_USING_ITEM, (byte) 0);
}
int Witch::getAmbientSound()
{
return eSoundType_MOB_WITCH_IDLE; //"mob.witch.idle";
}
int Witch::getHurtSound()
{
return eSoundType_MOB_WITCH_HURT; //"mob.witch.hurt";
}
int Witch::getDeathSound()
{
return eSoundType_MOB_WITCH_DEATH; //"mob.witch.death";
}
void Witch::setUsingItem(bool isUsing)
{
getEntityData()->set(DATA_USING_ITEM, isUsing ? (byte) 1 : (byte) 0);
}
bool Witch::isUsingItem()
{
return getEntityData()->getByte(DATA_USING_ITEM) == 1;
}
void Witch::registerAttributes()
{
Monster::registerAttributes();
getAttribute(SharedMonsterAttributes::MAX_HEALTH)->setBaseValue(26);
getAttribute(SharedMonsterAttributes::MOVEMENT_SPEED)->setBaseValue(0.25f);
}
bool Witch::useNewAi()
{
return true;
}
void Witch::aiStep()
{
if (!level->isClientSide)
{
if (isUsingItem())
{
if (usingTime-- <= 0)
{
setUsingItem(false);
shared_ptr<ItemInstance> item = getCarriedItem();
setEquippedSlot(SLOT_WEAPON, nullptr);
if (item != NULL && item->id == Item::potion_Id)
{
vector<MobEffectInstance *> *effects = Item::potion->getMobEffects(item);
if (effects != NULL)
{
for(auto& effect : *effects)
{
addEffect(new MobEffectInstance(effect));
}
}
delete effects;
}
getAttribute(SharedMonsterAttributes::MOVEMENT_SPEED)->removeModifier(SPEED_MODIFIER_DRINKING);
}
}
else
{
int potion = -1;
if (random->nextFloat() < 0.15f && isOnFire() && !hasEffect(MobEffect::fireResistance))
{
potion = PotionBrewing::POTION_ID_FIRE_RESISTANCE;
}
else if (random->nextFloat() < 0.05f && getHealth() < getMaxHealth())
{
potion = PotionBrewing::POTION_ID_HEAL;
}
else if (random->nextFloat() < 0.25f && getTarget() != NULL && !hasEffect(MobEffect::movementSpeed) && getTarget()->distanceToSqr(shared_from_this()) > 11 * 11)
{
potion = PotionBrewing::POTION_ID_SWIFTNESS;
}
else if (random->nextFloat() < 0.25f && getTarget() != NULL && !hasEffect(MobEffect::movementSpeed) && getTarget()->distanceToSqr(shared_from_this()) > 11 * 11)
{
potion = PotionBrewing::POTION_ID_SWIFTNESS;
}
if (potion > -1)
{
setEquippedSlot(SLOT_WEAPON, shared_ptr<ItemInstance>( new ItemInstance(Item::potion, 1, potion)) );
usingTime = getCarriedItem()->getUseDuration();
setUsingItem(true);
AttributeInstance *speed = getAttribute(SharedMonsterAttributes::MOVEMENT_SPEED);
speed->removeModifier(SPEED_MODIFIER_DRINKING);
speed->addModifier(new AttributeModifier(*SPEED_MODIFIER_DRINKING));
}
}
if (random->nextFloat() < 0.00075f)
{
level->broadcastEntityEvent(shared_from_this(), EntityEvent::WITCH_HAT_MAGIC);
}
}
Monster::aiStep();
}
void Witch::handleEntityEvent(byte id)
{
if (id == EntityEvent::WITCH_HAT_MAGIC)
{
for (int i = 0; i < random->nextInt(35) + 10; i++)
{
level->addParticle(eParticleType_witchMagic, x + random->nextGaussian() * .13f, bb->y1 + 0.5f + random->nextGaussian() * .13f, z + random->nextGaussian() * .13f, 0, 0, 0);
}
}
else
{
Monster::handleEntityEvent(id);
}
}
float Witch::getDamageAfterMagicAbsorb(DamageSource *damageSource, float damage)
{
damage = Monster::getDamageAfterMagicAbsorb(damageSource, damage);
if (damageSource->getEntity() == shared_from_this()) damage = 0;
if (damageSource->isMagic()) damage *= 0.15;
return damage;
}
void Witch::dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel)
{
int passes = random->nextInt(3) + 1;
for (int pass = 0; pass < passes; pass++)
{
int count = random->nextInt(3);
int type = DEATH_LOOT[random->nextInt(DEATH_LOOT_COUNT)];
if (playerBonusLevel > 0) count += random->nextInt(playerBonusLevel + 1);
for (int i = 0; i < count; i++)
{
spawnAtLocation(type, 1);
}
}
}
void Witch::performRangedAttack(shared_ptr<LivingEntity> target, float power)
{
if (isUsingItem()) return;
shared_ptr<ThrownPotion> potion = shared_ptr<ThrownPotion>( new ThrownPotion(level, dynamic_pointer_cast<LivingEntity>(shared_from_this()), PotionBrewing::POTION_ID_SPLASH_DAMAGE) );
potion->xRot -= -20;
double xd = (target->x + target->xd) - x;
double yd = (target->y + target->getHeadHeight() - 1.1f) - y;
double zd = (target->z + target->zd) - z;
float dist = Mth::sqrt(xd * xd + zd * zd);
if (dist >= 8 && !target->hasEffect(MobEffect::movementSlowdown))
{
potion->setPotionValue(PotionBrewing::POTION_ID_SPLASH_SLOWNESS);
}
else if (target->getHealth() >= 8 && !target->hasEffect(MobEffect::poison))
{
potion->setPotionValue(PotionBrewing::POTION_ID_SPLASH_POISON);
}
else if (dist <= 3 && !target->hasEffect(MobEffect::weakness) && random->nextFloat() < 0.25f)
{
potion->setPotionValue(PotionBrewing::POTION_ID_SPLASH_WEAKNESS);
}
potion->shoot(xd, yd + dist * 0.2f, zd, 0.75f, 8);
level->addEntity(potion);
}