From b691c43c44ff180d10e7d4a9afc83b98551ff586 Mon Sep 17 00:00:00 2001 From: daoge_cmd <3523206925@qq.com> Date: Sun, 1 Mar 2026 12:16:08 +0800 Subject: Initial commit --- Minecraft.World/Animal.cpp | 458 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 458 insertions(+) create mode 100644 Minecraft.World/Animal.cpp (limited to 'Minecraft.World/Animal.cpp') diff --git a/Minecraft.World/Animal.cpp b/Minecraft.World/Animal.cpp new file mode 100644 index 00000000..45fc304f --- /dev/null +++ b/Minecraft.World/Animal.cpp @@ -0,0 +1,458 @@ +#include "stdafx.h" +#include "com.mojang.nbt.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.world.phys.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.storage.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.entity.projectile.h" +#include "net.minecraft.world.damagesource.h" +#include "Random.h" +#include "Animal.h" + + + +Animal::Animal(Level *level) : AgableMob( level ) +{ +// inLove = 0; // 4J removed - now synched data + loveTime = 0; + loveCause = shared_ptr(); + + setDespawnProtected(); +} + +void Animal::defineSynchedData() +{ + AgableMob::defineSynchedData(); + + entityData->define(DATA_IN_LOVE, (int)0); // 4J added +} + +void Animal::serverAiMobStep() +{ + if (getAge() != 0) setInLoveValue(0); + AgableMob::serverAiMobStep(); +} + + +void Animal::aiStep() +{ + AgableMob::aiStep(); + + if (getAge() != 0) setInLoveValue(0); + + if (getInLoveValue() > 0) + { + setInLoveValue(getInLoveValue()-1); + if (getInLoveValue() % 10 == 0) + { + double xa = random->nextGaussian() * 0.02; + double ya = random->nextGaussian() * 0.02; + double za = random->nextGaussian() * 0.02; + level->addParticle(eParticleType_heart, x + random->nextFloat() * bbWidth * 2 - bbWidth, y + .5f + random->nextFloat() * bbHeight, z + random->nextFloat() * bbWidth * 2 - bbWidth, xa, ya, za); + } + } + else + { + loveTime = 0; + } + + updateDespawnProtectedState(); // 4J added +} + +void Animal::checkHurtTarget(shared_ptr target, float d) +{ + if (dynamic_pointer_cast(target) != NULL) + { + if (d < 3) + { + double xd = target->x - x; + double zd = target->z - z; + yRot = (float) (atan2(zd, xd) * 180 / PI) - 90; + + holdGround = true; + } + + shared_ptr p = dynamic_pointer_cast(target); + if (p->getSelectedItem() != NULL && this->isFood(p->getSelectedItem())) + { + } + else + { + attackTarget = nullptr; + } + + } + else if (dynamic_pointer_cast(target) != NULL) + { + shared_ptr a = dynamic_pointer_cast(target); + if (getAge() > 0 && a->getAge() < 0) + { + if (d < 2.5) + { + holdGround = true; + } + } + else if (getInLoveValue() > 0 && a->getInLoveValue() > 0) + { + if (a->attackTarget == NULL) a->attackTarget = shared_from_this(); + + if (a->attackTarget == shared_from_this() && d < 3.5) + { + a->setInLoveValue(a->getInLoveValue()+1); + setInLoveValue(getInLoveValue()+1); + loveTime++; + if (loveTime % 4 == 0) + { + level->addParticle(eParticleType_heart, x + random->nextFloat() * bbWidth * 2 - bbWidth, y + .5f + random->nextFloat() * bbHeight, z + random->nextFloat() * bbWidth * 2 - bbWidth, 0, 0, 0); + } + + if (loveTime == 20 * 3) breedWith(a); + } + else loveTime = 0; + } + else + { + loveTime = 0; + attackTarget = nullptr; + } + + } +} + + +void Animal::breedWith(shared_ptr target) +{ + shared_ptr offspring = getBreedOffspring(target); + + setInLoveValue(0); + loveTime = 0; + attackTarget = nullptr; + target->attackTarget = nullptr; + target->loveTime = 0; + target->setInLoveValue(0); + + // 4J - we have offspring of NULL returned when we have hit our limits of spawning any particular type of animal. In these cases try and do everything we can apart from actually + // spawning the entity. + if (offspring != NULL) + { + // Only want to set the age to this +ve value if something is actually spawned, as during this period the animal will attempt to follow offspring and ignore players. + setAge(5 * 60 * 20); + target->setAge(5 * 60 * 20); + + offspring->setAge(-20 * 60 * 20); + offspring->moveTo(x, y, z, yRot, xRot); + offspring->setDespawnProtected(); + for (int i = 0; i < 7; i++) + { + double xa = random->nextGaussian() * 0.02; + double ya = random->nextGaussian() * 0.02; + double za = random->nextGaussian() * 0.02; + level->addParticle(eParticleType_heart, x + random->nextFloat() * bbWidth * 2 - bbWidth, y + .5f + random->nextFloat() * bbHeight, z + random->nextFloat() * bbWidth * 2 - bbWidth, xa, ya, za); + } + level->addEntity(offspring); + + level->addEntity( shared_ptr( new ExperienceOrb(level, x, y, z, random->nextInt(4) + 1) ) ); + } + + setDespawnProtected(); +} + +float Animal::getWalkTargetValue(int x, int y, int z) +{ + if (level->getTile(x, y - 1, z) == Tile::grass_Id) return 10; + return level->getBrightness(x, y, z) - 0.5f; +} + +bool Animal::hurt(DamageSource *dmgSource, int dmg) +{ + if (dynamic_cast(dmgSource) != NULL) + { + shared_ptr source = dmgSource->getDirectEntity(); + + if (dynamic_pointer_cast(source) != NULL && !dynamic_pointer_cast(source)->isAllowedToAttackAnimals() ) + { + return false; + } + + if (source != NULL && source->GetType() == eTYPE_ARROW) + { + shared_ptr arrow = dynamic_pointer_cast(source); + if (dynamic_pointer_cast(arrow->owner) != NULL && ! dynamic_pointer_cast(arrow->owner)->isAllowedToAttackAnimals() ) + { + return false; + } + } + } + + fleeTime = 20 * 3; + attackTarget = nullptr; + setInLoveValue(0); + + return AgableMob::hurt(dmgSource, dmg); +} + +void Animal::addAdditonalSaveData(CompoundTag *tag) +{ + AgableMob::addAdditonalSaveData(tag); + tag->putInt(L"InLove", getInLoveValue()); +} + +void Animal::readAdditionalSaveData(CompoundTag *tag) +{ + AgableMob::readAdditionalSaveData(tag); + setInLoveValue(tag->getInt(L"InLove")); + setDespawnProtected(); +} + +shared_ptr Animal::findAttackTarget() +{ + if (fleeTime > 0) return nullptr; + + float r = 8; + if (getInLoveValue() > 0) + { + vector > *others = level->getEntitiesOfClass(typeid(*this), bb->grow(r, r, r)); + //for (int i = 0; i < others->size(); i++) + for(AUTO_VAR(it, others->begin()); it != others->end(); ++it) + { + shared_ptr p = dynamic_pointer_cast(*it); + if (p != shared_from_this() && p->getInLoveValue() > 0) + { + delete others; + return p; + } + } + delete others; + } + else + { + if (getAge() == 0) + { + vector > *players = level->getEntitiesOfClass(typeid(Player), bb->grow(r, r, r)); + //for (int i = 0; i < players.size(); i++) + for(AUTO_VAR(it, players->begin()); it != players->end(); ++it) + { + setDespawnProtected(); + + shared_ptr p = dynamic_pointer_cast(*it); + if (p->getSelectedItem() != NULL && this->isFood(p->getSelectedItem())) + { + delete players; + return p; + } + } + delete players; + } + else if (getAge() > 0) + { + vector > *others = level->getEntitiesOfClass(typeid(*this), bb->grow(r, r, r)); + //for (int i = 0; i < others.size(); i++) + for(AUTO_VAR(it, others->begin()); it != others->end(); ++it) + { + shared_ptr p = dynamic_pointer_cast(*it); + if (p != shared_from_this() && p->getAge() < 0) + { + delete others; + return p; + } + } + delete others; + } + } + return nullptr; +} + +bool Animal::canSpawn() +{ + int xt = Mth::floor(x); + int yt = Mth::floor(bb->y0); + int zt = Mth::floor(z); + return level->getTile(xt, yt - 1, zt) == Tile::grass_Id && level->getDaytimeRawBrightness(xt, yt, zt) > 8 && AgableMob::canSpawn(); +} + +int Animal::getAmbientSoundInterval() +{ + return 20 * 6; +} + +bool Animal::removeWhenFarAway() +{ + return !isDespawnProtected(); // 4J changed - was false +} + +int Animal::getExperienceReward(shared_ptr killedBy) +{ + return 1 + level->random->nextInt(3); +} + +bool Animal::isFood(shared_ptr itemInstance) +{ + return itemInstance->id == Item::wheat_Id; +} + +bool Animal::interact(shared_ptr player) +{ + shared_ptr item = player->inventory->getSelected(); + if (item != NULL && isFood(item) && getAge() == 0) + { + if (!player->abilities.instabuild) + { + item->count--; + if (item->count <= 0) + { + player->inventory->setItem(player->inventory->selected, nullptr); + } + } + + + // 4J-PB - If we can't produce another animal through breeding because of the spawn limits, display a message here + if(!level->isClientSide) + { + switch(GetType()) + { + case eTYPE_CHICKEN: + if( !level->canCreateMore(eTYPE_CHICKEN, Level::eSpawnType_Breed) ) + { + player->displayClientMessage(IDS_MAX_CHICKENS_BRED ); + return false; + } + break; + case eTYPE_WOLF: + if( !level->canCreateMore(eTYPE_WOLF, Level::eSpawnType_Breed) ) + { + player->displayClientMessage(IDS_MAX_WOLVES_BRED ); + return false; + } + break; + case eTYPE_MUSHROOMCOW: + if( !level->canCreateMore(eTYPE_MUSHROOMCOW, Level::eSpawnType_Breed) ) + { + player->displayClientMessage(IDS_MAX_MUSHROOMCOWS_BRED ); + return false; + } + break; + default: + if((GetType() & eTYPE_ANIMALS_SPAWN_LIMIT_CHECK) == eTYPE_ANIMALS_SPAWN_LIMIT_CHECK) + { + if( !level->canCreateMore(GetType(), Level::eSpawnType_Breed) ) + { + player->displayClientMessage(IDS_MAX_PIGS_SHEEP_COWS_CATS_BRED ); + + return false; + } + } + else if( (GetType() & eTYPE_MONSTER) == eTYPE_MONSTER) + { + + } + break; + } + setInLove(player); + } + + + attackTarget = nullptr; + for (int i = 0; i < 7; i++) + { + double xa = random->nextGaussian() * 0.02; + double ya = random->nextGaussian() * 0.02; + double za = random->nextGaussian() * 0.02; + level->addParticle(eParticleType_heart, x + random->nextFloat() * bbWidth * 2 - bbWidth, y + .5f + random->nextFloat() * bbHeight, z + random->nextFloat() * bbWidth * 2 - bbWidth, xa, ya, za); + } + + return true; + } + return AgableMob::interact(player); +} + +// 4J added +int Animal::getInLoveValue() +{ + return entityData->getInteger(DATA_IN_LOVE); +} + +void Animal::setInLoveValue(int value) +{ + entityData->set(DATA_IN_LOVE, value); +} + +// 4J added +void Animal::setInLove(shared_ptr player) +{ + loveCause = player; + setInLoveValue(20*30); +} + +shared_ptr Animal::getLoveCause() +{ + return loveCause.lock(); +} + +bool Animal::isInLove() +{ + return entityData->getInteger(DATA_IN_LOVE) > 0; +} + +void Animal::resetLove() { + entityData->set(DATA_IN_LOVE, 0); +} + +bool Animal::canMate(shared_ptr partner) +{ + if (partner == shared_from_this()) return false; + if (typeid(*partner) != typeid(*this)) return false; + return isInLove() && partner->isInLove(); +} + +void Animal::updateDespawnProtectedState() +{ + if( level->isClientSide ) return; + + if( m_isDespawnProtected ) + { + int xt = Mth::floor(x); + int zt = Mth::floor(z); + + if ( xt > m_maxWanderX ) m_maxWanderX = xt; + if ( xt < m_minWanderX ) m_minWanderX = xt; + if ( zt > m_maxWanderZ ) m_maxWanderZ = zt; + if ( zt < m_minWanderZ ) m_minWanderZ = zt; + + if( ( ( m_maxWanderX - m_minWanderX ) > MAX_WANDER_DISTANCE ) || + ( ( m_maxWanderZ - m_minWanderZ ) > MAX_WANDER_DISTANCE ) ) + { +// printf("Unprotecting : %d to %d, %d to %d\n", m_minWanderX, m_maxWanderX, m_minWanderZ, m_maxWanderZ ); + m_isDespawnProtected = false; + } + +/* + if( isExtraWanderingEnabled() ) + { + printf("%d: %d %d, %d\n",entityId,m_maxWanderX - m_minWanderX, m_maxWanderZ - m_minWanderZ, getWanderingQuadrant()); + } + */ + } +} + +bool Animal::isDespawnProtected() +{ + return m_isDespawnProtected; +} + +void Animal::setDespawnProtected() +{ + if( level && level->isClientSide ) return; + + int xt = Mth::floor(x); + int zt = Mth::floor(z); + + m_minWanderX = xt; + m_maxWanderX = xt; + m_minWanderZ = zt; + m_maxWanderZ = zt; + + m_isDespawnProtected = true; +} -- cgit v1.2.3