aboutsummaryrefslogtreecommitdiff
path: root/Minecraft.World/Zombie.cpp
diff options
context:
space:
mode:
authordaoge <3523206925@qq.com>2026-03-03 03:04:10 +0800
committerGitHub <noreply@github.com>2026-03-03 03:04:10 +0800
commitb3feddfef372618c8a9d7a0abcaf18cfad866c18 (patch)
tree267761c3bb39241ba5c347bfbe2254d06686e287 /Minecraft.World/Zombie.cpp
parent84c31a2331f7a0ec85b9d438992e244f60e5020f (diff)
feat: TU19 (Dec 2014) Features & Content (#155)
* try to resolve merge conflict * feat: TU19 (Dec 2014) Features & Content (#32) * December 2014 files * Working release build * Fix compilation issues * Add sound to Windows64Media * Add DLC content and force Tutorial DLC * Revert "Add DLC content and force Tutorial DLC" This reverts commit 97a43994725008e35fceb984d5549df9c8cea470. * Disable broken light packing * Disable breakpoint during DLC texture map load Allows DLC loading but the DLC textures are still broken * Fix post build not working * ... * fix vs2022 build * fix cmake build --------- Co-authored-by: Loki <lokirautio@gmail.com>
Diffstat (limited to 'Minecraft.World/Zombie.cpp')
-rw-r--r--Minecraft.World/Zombie.cpp310
1 files changed, 210 insertions, 100 deletions
diff --git a/Minecraft.World/Zombie.cpp b/Minecraft.World/Zombie.cpp
index b167635f..df9d5c6b 100644
--- a/Minecraft.World/Zombie.cpp
+++ b/Minecraft.World/Zombie.cpp
@@ -3,81 +3,81 @@
#include "net.minecraft.world.level.tile.h"
#include "net.minecraft.world.h"
#include "net.minecraft.world.item.h"
+#include "net.minecraft.world.damagesource.h"
#include "net.minecraft.world.effect.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.ai.navigation.h"
+#include "net.minecraft.world.entity.monster.h"
#include "net.minecraft.world.entity.npc.h"
#include "net.minecraft.world.entity.player.h"
#include "Zombie.h"
#include "GenericStats.h"
#include "..\Minecraft.Client\Textures.h"
#include "net.minecraft.world.entity.h"
+#include "JavaMath.h"
#include "SoundTypes.h"
+ Attribute *Zombie::SPAWN_REINFORCEMENTS_CHANCE = (new RangedAttribute(eAttributeId_ZOMBIE_SPAWNREINFORCEMENTS, 0, 0, 1));
+ AttributeModifier *Zombie::SPEED_MODIFIER_BABY = new AttributeModifier(eModifierId_MOB_ZOMBIE_BABYSPEED, 0.5f, AttributeModifier::OPERATION_MULTIPLY_BASE);
+
+const float Zombie::ZOMBIE_LEADER_CHANCE = 0.05f;
+
+
Zombie::Zombie(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
this->defineSynchedData();
-
- // 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
- health = getMaxHealth();
-
- this->textureIdx = TN_MOB_ZOMBIE; // 4J was L"/mob/zombie.png";
- runSpeed = 0.23f;
- attackDamage = 4;
+ registerAttributes();
+ setHealth(getMaxHealth());
villagerConversionTime = 0;
-
- registeredBBWidth = -1;
- registeredBBHeight = 0;
-
- setSize(bbWidth, bbHeight);
getNavigation()->setCanOpenDoors(true);
goalSelector.addGoal(0, new FloatGoal(this));
goalSelector.addGoal(1, new BreakDoorGoal(this));
- goalSelector.addGoal(2, new MeleeAttackGoal(this, eTYPE_PLAYER, runSpeed, false));
- goalSelector.addGoal(3, new MeleeAttackGoal(this, eTYPE_VILLAGER, runSpeed, true));
- goalSelector.addGoal(4, new MoveTowardsRestrictionGoal(this, runSpeed));
- goalSelector.addGoal(5, new MoveThroughVillageGoal(this, runSpeed, false));
- goalSelector.addGoal(6, new RandomStrollGoal(this, runSpeed));
+ goalSelector.addGoal(2, new MeleeAttackGoal(this, eTYPE_PLAYER, 1.0, false));
+ goalSelector.addGoal(3, new MeleeAttackGoal(this, eTYPE_VILLAGER, 1.0, true));
+ goalSelector.addGoal(4, new MoveTowardsRestrictionGoal(this, 1.0));
+ goalSelector.addGoal(5, new MoveThroughVillageGoal(this, 1.0, false));
+ goalSelector.addGoal(6, new RandomStrollGoal(this, 1.0));
goalSelector.addGoal(7, new LookAtPlayerGoal(this, typeid(Player), 8));
goalSelector.addGoal(7, new RandomLookAroundGoal(this));
- targetSelector.addGoal(1, new HurtByTargetGoal(this, false));
- targetSelector.addGoal(2, new NearestAttackableTargetGoal(this, typeid(Player), 16, 0, true));
- targetSelector.addGoal(2, new NearestAttackableTargetGoal(this, typeid(Villager), 16, 0, false));
+ targetSelector.addGoal(1, new HurtByTargetGoal(this, true));
+ targetSelector.addGoal(2, new NearestAttackableTargetGoal(this, typeid(Player), 0, true));
+ targetSelector.addGoal(2, new NearestAttackableTargetGoal(this, typeid(Villager), 0, false));
}
-void Zombie::defineSynchedData()
+void Zombie::registerAttributes()
{
- Monster::defineSynchedData();
+ Monster::registerAttributes();
- getEntityData()->define(DATA_BABY_ID, (byte) 0);
- getEntityData()->define(DATA_VILLAGER_ID, (byte) 0);
- getEntityData()->define(DATA_CONVERTING_ID, (byte) 0);
-}
+ // 4J Stu - Don't make it so far!
+ //getAttribute(SharedMonsterAttributes::FOLLOW_RANGE)->setBaseValue(40);
-float Zombie::getWalkingSpeedModifier()
-{
- return Monster::getWalkingSpeedModifier() * (isBaby() ? 1.5f : 1.0f);
-}
+ getAttribute(SharedMonsterAttributes::MOVEMENT_SPEED)->setBaseValue(0.23f);
+ getAttribute(SharedMonsterAttributes::ATTACK_DAMAGE)->setBaseValue(3);
-int Zombie::getTexture()
-{
- return isVillager() ? TN_MOB_ZOMBIE_VILLAGER : TN_MOB_ZOMBIE;
+ getAttributes()->registerAttribute(SPAWN_REINFORCEMENTS_CHANCE)->setBaseValue(random->nextDouble() * 0.10f);
}
-int Zombie::getMaxHealth()
+void Zombie::defineSynchedData()
{
- return 20;
+ Monster::defineSynchedData();
+
+ getEntityData()->define(DATA_BABY_ID, (byte) 0);
+ getEntityData()->define(DATA_VILLAGER_ID, (byte) 0);
+ getEntityData()->define(DATA_CONVERTING_ID, (byte) 0);
}
int Zombie::getArmorValue()
{
- return 2;
+ int value = Monster::getArmorValue() + 2;
+ if (value > 20) value = 20;
+ return value;
}
bool Zombie::useNewAi()
@@ -92,8 +92,17 @@ bool Zombie::isBaby()
void Zombie::setBaby(bool baby)
{
- getEntityData()->set(DATA_BABY_ID, (byte) 1);
- updateSize(isBaby());
+ getEntityData()->set(DATA_BABY_ID, (byte) (baby ? 1 : 0));
+
+ if (level != NULL && !level->isClientSide)
+ {
+ AttributeInstance *speed = getAttribute(SharedMonsterAttributes::MOVEMENT_SPEED);
+ speed->removeModifier(SPEED_MODIFIER_BABY);
+ if (baby)
+ {
+ speed->addModifier(new AttributeModifier(*SPEED_MODIFIER_BABY));
+ }
+ }
}
bool Zombie::isVillager()
@@ -108,21 +117,83 @@ void Zombie::setVillager(bool villager)
void Zombie::aiStep()
{
- if(level->isClientSide)
- {
- updateSize(isBaby());
- }
- else if (level->isDay() && !level->isClientSide && !isBaby())
+ if (level->isDay() && !level->isClientSide && !isBaby())
{
float br = getBrightness(1);
- if (br > 0.5f && random->nextFloat() * 30 < (br - 0.4f) * 2 && level->canSeeSky( Mth::floor(x), Mth::floor(y), Mth::floor(z)))
+ if (br > 0.5f && random->nextFloat() * 30 < (br - 0.4f) * 2 && level->canSeeSky(Mth::floor(x), (int)floor( y + 0.5 ), Mth::floor(z)))
{
- setOnFire(8);
+ bool burn = true;
+
+ shared_ptr<ItemInstance> helmet = getCarried(SLOT_HELM);
+ if (helmet != NULL)
+ {
+ if (helmet->isDamageableItem())
+ {
+ helmet->setAuxValue(helmet->getDamageValue() + random->nextInt(2));
+ if (helmet->getDamageValue() >= helmet->getMaxDamage())
+ {
+ breakItem(helmet);
+ setEquippedSlot(SLOT_HELM, nullptr);
+ }
+ }
+
+ burn = false;
+ }
+
+ if (burn)
+ {
+ setOnFire(8);
+ }
}
}
Monster::aiStep();
}
+bool Zombie::hurt(DamageSource *source, float dmg)
+{
+ if (Monster::hurt(source, dmg))
+ {
+ shared_ptr<LivingEntity> target = getTarget();
+ if ( (target == NULL) && getAttackTarget() != NULL && getAttackTarget()->instanceof(eTYPE_LIVINGENTITY) ) target = dynamic_pointer_cast<LivingEntity>( getAttackTarget() );
+ if ( (target == NULL) && source->getEntity() != NULL && source->getEntity()->instanceof(eTYPE_LIVINGENTITY) ) target = dynamic_pointer_cast<LivingEntity>( source->getEntity() );
+
+ if ( (target != NULL) && level->difficulty >= Difficulty::HARD && random->nextFloat() < getAttribute(SPAWN_REINFORCEMENTS_CHANCE)->getValue())
+ {
+ int x = Mth::floor(this->x);
+ int y = Mth::floor(this->y);
+ int z = Mth::floor(this->z);
+ shared_ptr<Zombie> reinforcement = shared_ptr<Zombie>( new Zombie(level) );
+
+ for (int i = 0; i < REINFORCEMENT_ATTEMPTS; i++)
+ {
+ int xt = x + Mth::nextInt(random, REINFORCEMENT_RANGE_MIN, REINFORCEMENT_RANGE_MAX) * Mth::nextInt(random, -1, 1);
+ int yt = y + Mth::nextInt(random, REINFORCEMENT_RANGE_MIN, REINFORCEMENT_RANGE_MAX) * Mth::nextInt(random, -1, 1);
+ int zt = z + Mth::nextInt(random, REINFORCEMENT_RANGE_MIN, REINFORCEMENT_RANGE_MAX) * Mth::nextInt(random, -1, 1);
+
+ if (level->isTopSolidBlocking(xt, yt - 1, zt) && level->getRawBrightness(xt, yt, zt) < 10)
+ {
+ reinforcement->setPos(xt, yt, zt);
+
+ if (level->isUnobstructed(reinforcement->bb) && level->getCubes(reinforcement, reinforcement->bb)->empty() && !level->containsAnyLiquid(reinforcement->bb))
+ {
+ level->addEntity(reinforcement);
+ reinforcement->setTarget(target);
+ reinforcement->finalizeMobSpawn(NULL);
+
+ getAttribute(SPAWN_REINFORCEMENTS_CHANCE)->addModifier(new AttributeModifier(-0.05f, AttributeModifier::OPERATION_ADDITION));
+ reinforcement->getAttribute(SPAWN_REINFORCEMENTS_CHANCE)->addModifier(new AttributeModifier(-0.05f, AttributeModifier::OPERATION_ADDITION));
+ break;
+ }
+ }
+ }
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
void Zombie::tick()
{
if (!level->isClientSide && isConverting())
@@ -140,6 +211,21 @@ void Zombie::tick()
Monster::tick();
}
+bool Zombie::doHurtTarget(shared_ptr<Entity> target)
+{
+ bool result = Monster::doHurtTarget(target);
+
+ if (result)
+ {
+ if (getCarriedItem() == NULL && isOnFire() && random->nextFloat() < level->difficulty * 0.3f)
+ {
+ target->setOnFire(2 * level->difficulty);
+ }
+ }
+
+ return result;
+}
+
int Zombie::getAmbientSound()
{
return eSoundType_MOB_ZOMBIE_AMBIENT;
@@ -160,6 +246,11 @@ int Zombie::getDeathLoot()
return Item::rotten_flesh_Id;
}
+void Zombie::playStepSound(int xt, int yt, int zt, int t)
+{
+ playSound(eSoundType_MOB_ZOMBIE_STEP, 0.15f, 1);
+}
+
MobType Zombie::getMobType()
{
return UNDEAD;
@@ -169,18 +260,6 @@ void Zombie::dropRareDeathLoot(int rareLootLevel)
{
switch (random->nextInt(3))
{
-/* case 0:
- spawnAtLocation(Item::sword_iron_Id, 1);
- break;
- case 1:
- spawnAtLocation(Item::helmet_iron_Id, 1);
- break;
- case 2:
- spawnAtLocation(Item::ironIngot_Id, 1);
- break;
- case 3:
- spawnAtLocation(Item::shovel_iron_Id, 1);
- break;*/
case 0:
spawnAtLocation(Item::ironIngot_Id, 1);
break;
@@ -193,6 +272,24 @@ void Zombie::dropRareDeathLoot(int rareLootLevel)
}
}
+void Zombie::populateDefaultEquipmentSlots()
+{
+ Monster::populateDefaultEquipmentSlots();
+
+ if (random->nextFloat() < (level->difficulty == Difficulty::HARD ? 0.05f : 0.01f))
+ {
+ int rand = random->nextInt(3);
+ if (rand == 0)
+ {
+ setEquippedSlot(SLOT_WEAPON, shared_ptr<ItemInstance>( new ItemInstance(Item::sword_iron)) );
+ }
+ else
+ {
+ setEquippedSlot(SLOT_WEAPON, shared_ptr<ItemInstance>( new ItemInstance(Item::shovel_iron)) );
+ }
+ }
+}
+
void Zombie::addAdditonalSaveData(CompoundTag *tag)
{
Monster::addAdditonalSaveData(tag);
@@ -211,19 +308,18 @@ void Zombie::readAdditionalSaveData(CompoundTag *tag)
if (tag->contains(L"ConversionTime") && tag->getInt(L"ConversionTime") > -1) startConverting(tag->getInt(L"ConversionTime"));
}
-void Zombie::killed(shared_ptr<Mob> mob)
+void Zombie::killed(shared_ptr<LivingEntity> mob)
{
Monster::killed(mob);
- if (level->difficulty >= Difficulty::NORMAL && ((mob->GetType() & eTYPE_VILLAGER) == eTYPE_VILLAGER))
+ if ( level->difficulty >= Difficulty::NORMAL && (mob->GetType() == eTYPE_VILLAGER) ) // 4J-JEV: Villager isn't a non-terminal class, no need to instanceof.
{
- if( !level->canCreateMore( GetType(), Level::eSpawnType_Egg) ) return;
if (level->difficulty == Difficulty::NORMAL && random->nextBoolean()) return;
shared_ptr<Zombie> zombie = shared_ptr<Zombie>(new Zombie(level));
zombie->copyPosition(mob);
level->removeEntity(mob);
- zombie->finalizeMobSpawn();
+ zombie->finalizeMobSpawn(NULL);
zombie->setVillager(true);
if (mob->isBaby()) zombie->setBaby(true);
level->addEntity(zombie);
@@ -232,38 +328,64 @@ void Zombie::killed(shared_ptr<Mob> mob)
}
}
-void Zombie::finalizeMobSpawn()
+MobGroupData *Zombie::finalizeMobSpawn(MobGroupData *groupData, int extraData /*= 0*/) // 4J Added extraData param
{
- // 4J Stu - TODO TU15
-#if 0
- canPickUpLoot = random->nextFloat() < CAN_PICK_UP_LOOT_CHANCES[level->difficulty];
-#endif
+ groupData = Monster::finalizeMobSpawn(groupData);
+ float difficulty = level->getDifficulty(x, y, z);
+
+ setCanPickUpLoot(random->nextFloat() < MAX_PICKUP_LOOT_CHANCE * difficulty);
+
+ if (groupData == NULL)
+ {
+ groupData = new ZombieGroupData(level->random->nextFloat() < 0.05f, level->random->nextFloat() < 0.05f);
+ }
- if (level->random->nextFloat() < 0.05f)
+ if ( dynamic_cast<ZombieGroupData *>( groupData ) != NULL)
{
- setVillager(true);
+ ZombieGroupData *zombieData = (ZombieGroupData *) groupData;
+
+ if (zombieData->isVillager)
+ {
+ setVillager(true);
+ }
+
+ if (zombieData->isBaby)
+ {
+ setBaby(true);
+ }
}
- // 4J Stu - TODO TU15
-#if 0
populateDefaultEquipmentSlots();
populateDefaultEquipmentEnchantments();
- if (getCarried(SLOT_HELM) == null)
+ if (getCarried(SLOT_HELM) == NULL)
{
- Calendar cal = level.getCalendar();
-
- if (cal.get(Calendar.MONTH) + 1 == 10 && cal.get(Calendar.DAY_OF_MONTH) == 31 && random.nextFloat() < 0.25f) {
+ // [EB]: We have this code in quite some places, shouldn't we set
+ // something like this globally?
+ if (Calendar::GetMonth() + 1 == 10 && Calendar::GetDayOfMonth() == 31 && random->nextFloat() < 0.25f)
+ {
// Halloween! OooOOo! 25% of all skeletons/zombies can wear
// pumpkins on their heads.
- setEquippedSlot(SLOT_HELM, new ItemInstance(random.nextFloat() < 0.1f ? Tile.litPumpkin : Tile.pumpkin));
+ setEquippedSlot(SLOT_HELM, shared_ptr<ItemInstance>( new ItemInstance(random->nextFloat() < 0.1f ? Tile::litPumpkin : Tile::pumpkin) ));
dropChances[SLOT_HELM] = 0;
}
}
-#endif
+
+ getAttribute(SharedMonsterAttributes::KNOCKBACK_RESISTANCE)->addModifier(new AttributeModifier(random->nextDouble() * 0.05f, AttributeModifier::OPERATION_ADDITION));
+
+ // 4J Stu - Take this out, it's not good and nobody will notice. Also not great for performance.
+ //getAttribute(SharedMonsterAttributes::FOLLOW_RANGE)->addModifier(new AttributeModifier(random->nextDouble() * 1.50f, AttributeModifier::OPERATION_MULTIPLY_TOTAL));
+
+ if (random->nextFloat() < difficulty * ZOMBIE_LEADER_CHANCE)
+ {
+ getAttribute(SPAWN_REINFORCEMENTS_CHANCE)->addModifier(new AttributeModifier(random->nextDouble() * 0.25f + 0.50f, AttributeModifier::OPERATION_ADDITION));
+ getAttribute(SharedMonsterAttributes::MAX_HEALTH)->addModifier(new AttributeModifier(random->nextDouble() * 3.0f + 1.0f, AttributeModifier::OPERATION_MULTIPLY_TOTAL));
+ }
+
+ return groupData;
}
-bool Zombie::interact(shared_ptr<Player> player)
+bool Zombie::mobInteract(shared_ptr<Player> player)
{
shared_ptr<ItemInstance> item = player->getSelectedItem();
@@ -304,7 +426,7 @@ void Zombie::handleEntityEvent(byte id)
{
if (id == EntityEvent::ZOMBIE_CONVERTING)
{
- level->playLocalSound(x + 0.5f, y + 0.5f, z + 0.5f, eSoundType_MOB_ZOMBIE_REMEDY, 1 + random->nextFloat(), random->nextFloat() * 0.7f + 0.3f);//, false);
+ level->playLocalSound(x + 0.5f, y + 0.5f, z + 0.5f, eSoundType_MOB_ZOMBIE_REMEDY, 1 + random->nextFloat(), random->nextFloat() * 0.7f + 0.3f, false);
}
else
{
@@ -312,6 +434,11 @@ void Zombie::handleEntityEvent(byte id)
}
}
+bool Zombie::removeWhenFarAway()
+{
+ return !isConverting();
+}
+
bool Zombie::isConverting()
{
return getEntityData()->getByte(DATA_CONVERTING_ID) == (byte) 1;
@@ -321,7 +448,7 @@ void Zombie::finishConversion()
{
shared_ptr<Villager> villager = shared_ptr<Villager>(new Villager(level));
villager->copyPosition(shared_from_this());
- villager->finalizeMobSpawn();
+ villager->finalizeMobSpawn(NULL);
villager->setRewardPlayersInVillage();
if (isBaby()) villager->setAge(-20 * 60 * 20);
level->removeEntity(shared_from_this());
@@ -359,25 +486,8 @@ int Zombie::getConversionProgress()
return amount;
}
-void Zombie::updateSize(bool isBaby)
+Zombie::ZombieGroupData::ZombieGroupData(bool baby, bool villager)
{
- internalSetSize(isBaby ? .5f : 1.0f);
-}
-
-void Zombie::setSize(float w, float h)
-{
- bool inited = registeredBBWidth > 0;
-
- registeredBBWidth = w;
- registeredBBHeight = h;
-
- if (!inited)
- {
- internalSetSize(1.0f);
- }
-}
-
-void Zombie::internalSetSize(float scale)
-{
- PathfinderMob::setSize(registeredBBWidth * scale, registeredBBHeight * scale);
-}
+ isBaby = baby;
+ isVillager = villager;
+} \ No newline at end of file