diff options
| author | daoge <3523206925@qq.com> | 2026-03-03 03:04:10 +0800 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-03-03 03:04:10 +0800 |
| commit | b3feddfef372618c8a9d7a0abcaf18cfad866c18 (patch) | |
| tree | 267761c3bb39241ba5c347bfbe2254d06686e287 /Minecraft.World/Zombie.cpp | |
| parent | 84c31a2331f7a0ec85b9d438992e244f60e5020f (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.cpp | 310 |
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 |
