mirror of
https://github.com/smartcmd/MinecraftConsoles.git
synced 2026-08-20 09:57:09 +00:00
Wolf AI improoves
This commit is contained in:
parent
f4db2495e8
commit
791f592cef
|
|
@ -2445,6 +2445,7 @@
|
|||
<ClInclude Include="BiomeOverrideLayer.h" />
|
||||
<ClInclude Include="BiomeSource.h" />
|
||||
<ClInclude Include="BirchFeature.h" />
|
||||
<ClInclude Include="BirchForestBiome.h" />
|
||||
<ClInclude Include="Blaze.h" />
|
||||
<ClInclude Include="BlockDestructionProgress.h" />
|
||||
<ClInclude Include="BlockGenMethods.h" />
|
||||
|
|
@ -2468,6 +2469,8 @@
|
|||
<ClInclude Include="ComparatorTileEntity.h" />
|
||||
<ClInclude Include="DaylightDetectorTile.h" />
|
||||
<ClInclude Include="DaylightDetectorTileEntity.h" />
|
||||
<ClInclude Include="PrioritizeOwnerThreatGoal.h" />
|
||||
<ClInclude Include="TameAnimalDefendGoal.h" />
|
||||
<ClInclude Include="DropperTile.h" />
|
||||
<ClInclude Include="DropperTileEntity.h" />
|
||||
<ClInclude Include="DummyCriteria.h" />
|
||||
|
|
@ -2847,6 +2850,8 @@
|
|||
<ClInclude Include="Witch.h" />
|
||||
<ClInclude Include="WitherBoss.h" />
|
||||
<ClInclude Include="WitherSkull.h" />
|
||||
<ClInclude Include="WolfPackDistributedTargetGoal.h" />
|
||||
<ClInclude Include="WolfRetreatGoal.h" />
|
||||
<ClInclude Include="WoodButtonTile.h" />
|
||||
<ClInclude Include="WoodSlabTile.h" />
|
||||
<ClInclude Include="WoodTile.h" />
|
||||
|
|
@ -3519,6 +3524,7 @@
|
|||
<ClCompile Include="BiomeOverrideLayer.cpp" />
|
||||
<ClCompile Include="BiomeSource.cpp" />
|
||||
<ClCompile Include="BirchFeature.cpp" />
|
||||
<ClCompile Include="BirchForestBiome.cpp" />
|
||||
<ClCompile Include="Blaze.cpp" />
|
||||
<ClCompile Include="BlockDestructionProgress.cpp" />
|
||||
<ClCompile Include="BlockGenMethods.cpp" />
|
||||
|
|
@ -3540,6 +3546,8 @@
|
|||
<ClCompile Include="ComparatorTileEntity.cpp" />
|
||||
<ClCompile Include="DaylightDetectorTile.cpp" />
|
||||
<ClCompile Include="DaylightDetectorTileEntity.cpp" />
|
||||
<ClCompile Include="PrioritizeOwnerThreatGoal.cpp" />
|
||||
<ClCompile Include="TameAnimalDefendGoal.cpp" />
|
||||
<ClCompile Include="DropperTile.cpp" />
|
||||
<ClCompile Include="DropperTileEntity.cpp" />
|
||||
<ClCompile Include="DummyCriteria.cpp" />
|
||||
|
|
@ -3649,6 +3657,8 @@
|
|||
<ClCompile Include="StructureFeatureSavedData.cpp" />
|
||||
<ClCompile Include="Team.cpp" />
|
||||
<ClCompile Include="WeightedPressurePlateTile.cpp" />
|
||||
<ClCompile Include="WolfPackDistributedTargetGoal.cpp" />
|
||||
<ClCompile Include="WolfRetreatGoal.cpp" />
|
||||
<ClCompile Include="WoodButtonTile.cpp" />
|
||||
<ClCompile Include="WoolTileItem.cpp" />
|
||||
<ClCompile Include="CoalItem.cpp" />
|
||||
|
|
|
|||
|
|
@ -3203,6 +3203,21 @@
|
|||
<ClInclude Include="LightSourceTile.h">
|
||||
<Filter>net\minecraft\world\level\tile</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="TameAnimalDefendGoal.h">
|
||||
<Filter>net\minecraft\world\entity\ai\goal\target</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="WolfRetreatGoal.h">
|
||||
<Filter>net\minecraft\world\entity\ai\goal\target</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="PrioritizeOwnerThreatGoal.h">
|
||||
<Filter>net\minecraft\world\entity\ai\goal\target</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="WolfPackDistributedTargetGoal.h">
|
||||
<Filter>net\minecraft\world\entity\ai\goal\target</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="BirchForestBiome.h">
|
||||
<Filter>net\minecraft\world\level\biome</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="stdafx.cpp">
|
||||
|
|
@ -5638,5 +5653,20 @@
|
|||
<ClCompile Include="LightSourceTile.cpp">
|
||||
<Filter>net\minecraft\world\level\tile</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="TameAnimalDefendGoal.cpp">
|
||||
<Filter>net\minecraft\world\entity\ai\goal\target</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="WolfRetreatGoal.cpp">
|
||||
<Filter>net\minecraft\world\entity\ai\goal\target</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="PrioritizeOwnerThreatGoal.cpp">
|
||||
<Filter>net\minecraft\world\entity\ai\goal\target</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="WolfPackDistributedTargetGoal.cpp">
|
||||
<Filter>net\minecraft\world\entity\ai\goal\target</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="BirchForestBiome.cpp">
|
||||
<Filter>net\minecraft\world\level\biome</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
|
@ -32,5 +32,13 @@ void OwnerHurtByTargetGoal::start()
|
|||
timestamp = owner->getLastHurtByMobTimestamp();
|
||||
}
|
||||
|
||||
tameAnimal->onTameAggroStarted(owner->getLastHurtByMob());
|
||||
|
||||
TargetGoal::start();
|
||||
}
|
||||
}
|
||||
|
||||
void OwnerHurtByTargetGoal::stop()
|
||||
{
|
||||
tameAnimal->onTameAggroStopped();
|
||||
TargetGoal::stop();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,4 +16,5 @@ public:
|
|||
|
||||
bool canUse();
|
||||
void start();
|
||||
void stop();
|
||||
};
|
||||
75
Minecraft.World/PrioritizeOwnerThreatGoal.cpp
Normal file
75
Minecraft.World/PrioritizeOwnerThreatGoal.cpp
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
#include "stdafx.h"
|
||||
#include "net.minecraft.world.entity.h"
|
||||
#include "net.minecraft.world.entity.animal.h"
|
||||
#include "PrioritizeOwnerThreatGoal.h"
|
||||
|
||||
PrioritizeOwnerThreatGoal::PrioritizeOwnerThreatGoal(TamableAnimal* tameAnimal)
|
||||
: TargetGoal(tameAnimal, false)
|
||||
{
|
||||
this->tameAnimal = tameAnimal;
|
||||
this->timestamp = 0;
|
||||
setRequiredControlFlags(TargetGoal::TargetFlag);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
bool PrioritizeOwnerThreatGoal::canUse()
|
||||
{
|
||||
// Только прирученный и не сидящий
|
||||
if (!tameAnimal->isTame()) return false;
|
||||
if (tameAnimal->isSitting()) return false;
|
||||
|
||||
// Этот goal — именно ПЕРЕКЛЮЧАТЕЛЬ, а не первичный выбор цели.
|
||||
// Если текущей цели нет — пусть этим занимаются
|
||||
// HurtByTargetGoal / TameAnimalDefendGoal
|
||||
shared_ptr<LivingEntity> currentTarget = mob->getTarget();
|
||||
if (currentTarget == nullptr || !currentTarget->isAlive()) return false;
|
||||
|
||||
// Нужен живой хозяин
|
||||
shared_ptr<LivingEntity> owner =
|
||||
dynamic_pointer_cast<LivingEntity>(tameAnimal->getOwner());
|
||||
if (owner == nullptr || !owner->isAlive()) return false;
|
||||
|
||||
// Кто последний ударил хозяина?
|
||||
shared_ptr<LivingEntity> ownerAttacker = owner->getLastHurtByMob();
|
||||
int ts = owner->getLastHurtByMobTimestamp();
|
||||
|
||||
// Ничего нового с момента последней обработки
|
||||
if (ts == timestamp) return false;
|
||||
|
||||
// Уже атакуем именно эту угрозу — незачем переключаться
|
||||
if (ownerAttacker == currentTarget) return false;
|
||||
|
||||
// Стандартные проверки: цель жива, не союзник, видна и т.д.
|
||||
if (!canAttack(ownerAttacker, false)) return false;
|
||||
|
||||
// Питомец-специфичный фильтр (Wolf: не атаковать Creeper,
|
||||
// питомцев того же хозяина, PvP-флаг и т.д.)
|
||||
if (!tameAnimal->wantsToAttack(ownerAttacker, owner)) return false;
|
||||
|
||||
// Переключаемся только если новая угроза ощутимо ближе к хозяину,
|
||||
// чем текущая цель — иначе не стоит бросать начатое
|
||||
double distToOwnerSq = owner->distanceToSqr(ownerAttacker);
|
||||
double currentDistToOwnerSq = owner->distanceToSqr(currentTarget);
|
||||
|
||||
if (distToOwnerSq >= currentDistToOwnerSq - SWITCH_DIST_ADVANTAGE_SQ)
|
||||
return false;
|
||||
|
||||
newThreat = weak_ptr<LivingEntity>(ownerAttacker);
|
||||
return true;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
void PrioritizeOwnerThreatGoal::start()
|
||||
{
|
||||
shared_ptr<LivingEntity> target = newThreat.lock();
|
||||
mob->setTarget(target);
|
||||
tameAnimal->onTameAggroStarted(target); // Wolf: setAngry(true)
|
||||
|
||||
// Запомнить timestamp чтобы не обрабатывать это событие повторно
|
||||
shared_ptr<LivingEntity> owner =
|
||||
dynamic_pointer_cast<LivingEntity>(tameAnimal->getOwner());
|
||||
if (owner != nullptr)
|
||||
timestamp = owner->getLastHurtByMobTimestamp();
|
||||
|
||||
TargetGoal::start();
|
||||
}
|
||||
31
Minecraft.World/PrioritizeOwnerThreatGoal.h
Normal file
31
Minecraft.World/PrioritizeOwnerThreatGoal.h
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
#pragma once
|
||||
|
||||
#include "TargetGoal.h"
|
||||
|
||||
class TamableAnimal;
|
||||
|
||||
// Если волк уже дерётся с мобом A, но хозяина начал бить моб B —
|
||||
// переключиться на B. Работает только при наличии активной цели,
|
||||
// иначе уступает TameAnimalDefendGoal и HurtByTargetGoal.
|
||||
class PrioritizeOwnerThreatGoal : public TargetGoal
|
||||
{
|
||||
private:
|
||||
TamableAnimal* tameAnimal;
|
||||
weak_ptr<LivingEntity> newThreat;
|
||||
|
||||
// Запоминаем timestamp последней обработанной атаки на хозяина,
|
||||
// чтобы не переключаться на одну и ту же угрозу дважды
|
||||
int timestamp;
|
||||
|
||||
// Минимальная разница дистанций (в блоках²), при которой
|
||||
// новая угроза считается "ближе" и стоит переключиться.
|
||||
// Защищает от дёрганья между двумя равноудалёнными целями.
|
||||
static constexpr double SWITCH_DIST_ADVANTAGE_SQ = 4.0 * 4.0;
|
||||
|
||||
public:
|
||||
PrioritizeOwnerThreatGoal(TamableAnimal* tameAnimal);
|
||||
|
||||
bool canUse() override;
|
||||
void start() override;
|
||||
};
|
||||
|
||||
|
|
@ -36,6 +36,8 @@ public:
|
|||
virtual void setOwnerUUID(const wstring &name);
|
||||
virtual shared_ptr<Entity> getOwner();
|
||||
virtual SitGoal *getSitGoal();
|
||||
virtual void onTameAggroStarted(shared_ptr<LivingEntity> target) {}
|
||||
virtual void onTameAggroStopped() {}
|
||||
bool wantsToAttack(shared_ptr<LivingEntity> target, shared_ptr<LivingEntity> owner);
|
||||
Team *getTeam();
|
||||
bool isAlliedTo(shared_ptr<LivingEntity> other);
|
||||
|
|
|
|||
88
Minecraft.World/TameAnimalDefendGoal.cpp
Normal file
88
Minecraft.World/TameAnimalDefendGoal.cpp
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
#include "stdafx.h"
|
||||
#include "net.minecraft.world.entity.h"
|
||||
#include "net.minecraft.world.entity.animal.h"
|
||||
#include "net.minecraft.world.entity.monster.h"
|
||||
#include "net.minecraft.world.phys.h"
|
||||
#include "TameAnimalDefendGoal.h"
|
||||
#include "Level.h"
|
||||
|
||||
TameAnimalDefendGoal::TameAnimalDefendGoal(TamableAnimal* tameAnimal, bool mustSee)
|
||||
: TargetGoal(tameAnimal, mustSee)
|
||||
{
|
||||
this->tameAnimal = tameAnimal;
|
||||
setRequiredControlFlags(TargetGoal::TargetFlag);
|
||||
}
|
||||
|
||||
bool TameAnimalDefendGoal::canUse()
|
||||
{
|
||||
// Òîëüêî ïðèðó÷åííûé è íå ñèäÿùèé ïèòîìåö èùåò óãðîçû ñàì
|
||||
if (!tameAnimal->isTame()) return false;
|
||||
if (tameAnimal->isSitting()) return false;
|
||||
|
||||
// Åñëè óæå åñòü öåëü — íå ïåðåáèâàåì (ïðèîðèòåò ó ðåàêòèâíûõ öåëåé)
|
||||
if (mob->getTarget() != nullptr && mob->getTarget()->isAlive()) return false;
|
||||
|
||||
shared_ptr<LivingEntity> owner =
|
||||
dynamic_pointer_cast<LivingEntity>(tameAnimal->getOwner());
|
||||
// owner ìîæåò áûòü nullptr (îôôëàéí-èãðîê) — äîïóñêàåì, íî wantsToAttack
|
||||
// íèæå îáðàáîòàåò ýòî êîððåêòíî
|
||||
|
||||
double range = getFollowDistance();
|
||||
|
||||
// Èùåì âñå Monster-ñóùíîñòè â ðàäèóñå
|
||||
vector<shared_ptr<Entity>>* candidates = mob->level->getEntitiesOfClass(
|
||||
typeid(Monster),
|
||||
mob->bb->grow(range, 4.0, range),
|
||||
nullptr // áåç äîïîëíèòåëüíîãî ôèëüòðà — canAttack ñäåëàåò çà íàñ
|
||||
);
|
||||
|
||||
if (candidates == nullptr || candidates->empty())
|
||||
{
|
||||
delete candidates;
|
||||
return false;
|
||||
}
|
||||
|
||||
shared_ptr<LivingEntity> best = nullptr;
|
||||
double bestSq = DBL_MAX;
|
||||
|
||||
for (auto& e : *candidates)
|
||||
{
|
||||
shared_ptr<LivingEntity> le = dynamic_pointer_cast<LivingEntity>(e);
|
||||
if (!le) continue;
|
||||
|
||||
// Ñòàíäàðòíàÿ ïðîâåðêà äèñòàíöèè, âèäèìîñòè, îãðàíè÷åíèé
|
||||
if (!canAttack(le, false)) continue;
|
||||
|
||||
// Ñïåöèôè÷íàÿ äëÿ ïèòîìöà ôèëüòðàöèÿ
|
||||
// (íå àòàêîâàòü êðèïåðà, ñâîèõ ïèòîìöåâ âëàäåëüöà è ò.ä.)
|
||||
if (owner != nullptr && !tameAnimal->wantsToAttack(le, owner)) continue;
|
||||
|
||||
double distSq = mob->distanceToSqr(le);
|
||||
if (distSq < bestSq)
|
||||
{
|
||||
bestSq = distSq;
|
||||
best = le;
|
||||
}
|
||||
}
|
||||
|
||||
delete candidates;
|
||||
|
||||
if (!best) return false;
|
||||
|
||||
nearestHostile = weak_ptr<LivingEntity>(best);
|
||||
return true;
|
||||
}
|
||||
|
||||
void TameAnimalDefendGoal::start()
|
||||
{
|
||||
shared_ptr<LivingEntity> target = nearestHostile.lock();
|
||||
mob->setTarget(target);
|
||||
tameAnimal->onTameAggroStarted(target);
|
||||
TargetGoal::start();
|
||||
}
|
||||
|
||||
void TameAnimalDefendGoal::stop()
|
||||
{
|
||||
tameAnimal->onTameAggroStopped();
|
||||
TargetGoal::stop();
|
||||
}
|
||||
22
Minecraft.World/TameAnimalDefendGoal.h
Normal file
22
Minecraft.World/TameAnimalDefendGoal.h
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
#pragma once
|
||||
|
||||
#include "TargetGoal.h"
|
||||
|
||||
class TamableAnimal;
|
||||
|
||||
// Ïðèðó÷åííûé ïèòîìåö ïðîàêòèâíî àòàêóåò áëèæàéøåãî âðàæäåáíîãî ìîáà â ðàäèóñå
|
||||
class TameAnimalDefendGoal : public TargetGoal
|
||||
{
|
||||
private:
|
||||
TamableAnimal* tameAnimal;
|
||||
weak_ptr<LivingEntity> nearestHostile;
|
||||
|
||||
public:
|
||||
// mustSee=true: âèäåòü öåëü ïåðåä àòàêîé
|
||||
// scanRadius: åñëè 0, èñïîëüçóåòñÿ FOLLOW_RANGE àòðèáóò
|
||||
TameAnimalDefendGoal(TamableAnimal* tameAnimal, bool mustSee);
|
||||
|
||||
bool canUse() override;
|
||||
void start() override;
|
||||
void stop() override;
|
||||
};
|
||||
|
|
@ -20,6 +20,7 @@
|
|||
#include "Wolf.h"
|
||||
#include "..\Minecraft.Client\Textures.h"
|
||||
#include "SoundTypes.h"
|
||||
#include "PrimedTnt.h"
|
||||
|
||||
|
||||
|
||||
|
|
@ -39,20 +40,28 @@ Wolf::Wolf(Level *level) : TamableAnimal( level )
|
|||
|
||||
getNavigation()->setAvoidWater(true);
|
||||
goalSelector.addGoal(1, new FloatGoal(this));
|
||||
goalSelector.addGoal(2, sitGoal, false);
|
||||
goalSelector.addGoal(3, new LeapAtTargetGoal(this, 0.4));
|
||||
goalSelector.addGoal(4, new MeleeAttackGoal(this, 1.0, true));
|
||||
goalSelector.addGoal(5, new FollowOwnerGoal(this, 1.0, 10, 2));
|
||||
goalSelector.addGoal(6, new BreedGoal(this, 1.0));
|
||||
goalSelector.addGoal(7, new RandomStrollGoal(this, 1.0));
|
||||
goalSelector.addGoal(8, new BegGoal(this, 8));
|
||||
goalSelector.addGoal(9, new LookAtPlayerGoal(this, typeid(Player), 8));
|
||||
goalSelector.addGoal(9, new RandomLookAroundGoal(this));
|
||||
goalSelector.addGoal(2, new WolfRetreatGoal(this, 0.25f, 1.5f));
|
||||
goalSelector.addGoal(3, new AvoidPlayerGoal(this, typeid(Creeper), 12, 1.25, 1.25));
|
||||
goalSelector.addGoal(4, new AvoidPlayerGoal(this, typeid(PrimedTnt), 12, 1.25, 1.25));
|
||||
goalSelector.addGoal(5, sitGoal, false);
|
||||
goalSelector.addGoal(6, new LeapAtTargetGoal(this, 0.4));
|
||||
goalSelector.addGoal(7, new MeleeAttackGoal(this, 1.0, true));
|
||||
goalSelector.addGoal(8, new FollowOwnerGoal(this, 1.0, 10, 2));
|
||||
goalSelector.addGoal(9, new BreedGoal(this, 1.0));
|
||||
goalSelector.addGoal(10, new RandomStrollGoal(this, 1.0));
|
||||
goalSelector.addGoal(11, new BegGoal(this, 8));
|
||||
goalSelector.addGoal(12, new LookAtPlayerGoal(this, typeid(Player), 8));
|
||||
goalSelector.addGoal(13, new LookAtPlayerGoal(this, typeid(Wolf), 8));
|
||||
goalSelector.addGoal(14, new RandomLookAroundGoal(this));
|
||||
|
||||
targetSelector.addGoal(1, new OwnerHurtByTargetGoal(this));
|
||||
targetSelector.addGoal(2, new OwnerHurtTargetGoal(this));
|
||||
targetSelector.addGoal(3, new HurtByTargetGoal(this, true));
|
||||
targetSelector.addGoal(4, new NonTameRandomTargetGoal(this, typeid(Sheep), 200, false));
|
||||
targetSelector.addGoal(1, new HurtByTargetGoal(this, true));
|
||||
targetSelector.addGoal(2, new OwnerHurtByTargetGoal(this));
|
||||
targetSelector.addGoal(3, new PrioritizeOwnerThreatGoal(this));
|
||||
targetSelector.addGoal(4, new OwnerHurtTargetGoal(this));
|
||||
targetSelector.addGoal(5, new PackDistributedTargetGoal(this, true, 20.0f));
|
||||
targetSelector.addGoal(6, new TameAnimalDefendGoal(this, true));
|
||||
targetSelector.addGoal(7, new NonTameRandomTargetGoal(this, typeid(Sheep), 200, false));
|
||||
targetSelector.addGoal(8, new NonTameRandomTargetGoal(this, typeid(Skeleton), 200, false));
|
||||
|
||||
setTame(false); // Initialize health
|
||||
}
|
||||
|
|
@ -81,14 +90,20 @@ bool Wolf::useNewAi()
|
|||
void Wolf::setTarget(shared_ptr<LivingEntity> target)
|
||||
{
|
||||
TamableAnimal::setTarget(target);
|
||||
if ( target == nullptr )
|
||||
{
|
||||
if (target == nullptr)
|
||||
setAngry(false);
|
||||
}
|
||||
else if(!isTame())
|
||||
{
|
||||
else
|
||||
setAngry(true);
|
||||
}
|
||||
}
|
||||
|
||||
void Wolf::onTameAggroStarted(shared_ptr<LivingEntity> target)
|
||||
{
|
||||
setAngry(true);
|
||||
}
|
||||
|
||||
void Wolf::onTameAggroStopped()
|
||||
{
|
||||
setAngry(false);
|
||||
}
|
||||
|
||||
void Wolf::serverAiMobStep()
|
||||
|
|
@ -474,8 +489,7 @@ bool Wolf::isFood(shared_ptr<ItemInstance> item)
|
|||
|
||||
int Wolf::getMaxSpawnClusterSize()
|
||||
{
|
||||
// 4J - changed - was 8 but we have a limit of only 8 wolves in the world so doesn't seem right potentially spawning them all in once cluster
|
||||
return 4;
|
||||
return 8;
|
||||
}
|
||||
|
||||
bool Wolf::isAngry()
|
||||
|
|
|
|||
|
|
@ -76,6 +76,9 @@ public:
|
|||
// For tooltips
|
||||
int GetSynchedHealth();
|
||||
|
||||
virtual void onTameAggroStarted(shared_ptr<LivingEntity> target) override;
|
||||
virtual void onTameAggroStopped() override;
|
||||
|
||||
protected:
|
||||
virtual shared_ptr<AgableMob> getBreedOffspring(shared_ptr<AgableMob> target);
|
||||
|
||||
|
|
|
|||
182
Minecraft.World/WolfPackDistributedTargetGoal.cpp
Normal file
182
Minecraft.World/WolfPackDistributedTargetGoal.cpp
Normal file
|
|
@ -0,0 +1,182 @@
|
|||
#include "stdafx.h"
|
||||
#include "net.minecraft.world.entity.h"
|
||||
#include "net.minecraft.world.entity.animal.h"
|
||||
#include "net.minecraft.world.entity.monster.h"
|
||||
#include "net.minecraft.world.phys.h"
|
||||
#include "Level.h"
|
||||
#include "Wolf.h"
|
||||
#include "WolfPackDistributedTargetGoal.h"
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
PackDistributedTargetGoal::PackDistributedTargetGoal(
|
||||
TamableAnimal* tameAnimal,
|
||||
bool mustSee,
|
||||
float maxChaseDistFromOwner)
|
||||
: TargetGoal(tameAnimal, mustSee)
|
||||
{
|
||||
this->tameAnimal = tameAnimal;
|
||||
this->maxChaseDistFromOwner = maxChaseDistFromOwner;
|
||||
setRequiredControlFlags(TargetGoal::TargetFlag);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
vector<shared_ptr<LivingEntity>> PackDistributedTargetGoal::getPackmates(
|
||||
double range)
|
||||
{
|
||||
vector<shared_ptr<LivingEntity>> result;
|
||||
|
||||
wstring ownerUUID = tameAnimal->getOwnerUUID();
|
||||
if (ownerUUID.empty()) return result;
|
||||
|
||||
// Ищем всех волков в радиусе
|
||||
vector<shared_ptr<Entity>>* nearby = mob->level->getEntitiesOfClass(
|
||||
typeid(Wolf),
|
||||
mob->bb->grow(range, 4.0, range),
|
||||
nullptr);
|
||||
|
||||
if (nearby == nullptr)
|
||||
return result;
|
||||
|
||||
for (auto& e : *nearby)
|
||||
{
|
||||
// Пропустить самого себя
|
||||
if (e.get() == mob) continue;
|
||||
|
||||
shared_ptr<Wolf> wolf = dynamic_pointer_cast<Wolf>(e);
|
||||
if (wolf == nullptr) continue;
|
||||
if (!wolf->isTame()) continue;
|
||||
|
||||
// Только волки того же хозяина
|
||||
if (wolf->getOwnerUUID() != ownerUUID) continue;
|
||||
|
||||
result.push_back(wolf);
|
||||
}
|
||||
|
||||
delete nearby;
|
||||
return result;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
int PackDistributedTargetGoal::countAssignments(
|
||||
const vector<shared_ptr<LivingEntity>>& packmates,
|
||||
shared_ptr<LivingEntity> target)
|
||||
{
|
||||
if (target == nullptr) return 0;
|
||||
|
||||
int count = 0;
|
||||
for (auto& packmate : packmates)
|
||||
{
|
||||
shared_ptr<LivingEntity> packTarget =
|
||||
dynamic_pointer_cast<LivingEntity>(
|
||||
dynamic_pointer_cast<Wolf>(packmate) != nullptr
|
||||
? dynamic_pointer_cast<Wolf>(packmate)->getTarget()
|
||||
: nullptr);
|
||||
|
||||
if (packTarget != nullptr && packTarget.get() == target.get())
|
||||
count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
shared_ptr<LivingEntity> PackDistributedTargetGoal::pickBestTarget(
|
||||
const vector<shared_ptr<Entity>>* candidates,
|
||||
const vector<shared_ptr<LivingEntity>>& packmates,
|
||||
bool respectLimit)
|
||||
{
|
||||
shared_ptr<LivingEntity> best = nullptr;
|
||||
double bestScore = DBL_MAX;
|
||||
|
||||
shared_ptr<LivingEntity> owner =
|
||||
dynamic_pointer_cast<LivingEntity>(tameAnimal->getOwner());
|
||||
|
||||
for (auto& e : *candidates)
|
||||
{
|
||||
shared_ptr<LivingEntity> le = dynamic_pointer_cast<LivingEntity>(e);
|
||||
if (le == nullptr) continue;
|
||||
if (!canAttack(le, false)) continue;
|
||||
if (owner != nullptr && !tameAnimal->wantsToAttack(le, owner)) continue;
|
||||
|
||||
int assigned = countAssignments(packmates, le);
|
||||
|
||||
// Фаза 1: уважаем жёсткий лимит
|
||||
if (respectLimit && assigned >= MAX_WOLVES_PER_TARGET)
|
||||
continue;
|
||||
|
||||
// score = штраф за назначенных волков + дистанция²
|
||||
// Это позволяет предпочесть менее занятую цель,
|
||||
// но при равном числе назначений — выбрать ближайшую
|
||||
double distSq = mob->distanceToSqr(le);
|
||||
double score = assigned * ASSIGNMENT_PENALTY_SQ + distSq;
|
||||
|
||||
if (score < bestScore)
|
||||
{
|
||||
bestScore = score;
|
||||
best = le;
|
||||
}
|
||||
}
|
||||
|
||||
return best;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
bool PackDistributedTargetGoal::canUse()
|
||||
{
|
||||
if (!tameAnimal->isTame()) return false;
|
||||
if (tameAnimal->isSitting()) return false;
|
||||
if (mob->getTarget() != nullptr && mob->getTarget()->isAlive())
|
||||
return false;
|
||||
|
||||
double range = getFollowDistance();
|
||||
|
||||
vector<shared_ptr<Entity>>* candidates = mob->level->getEntitiesOfClass(
|
||||
typeid(Monster),
|
||||
mob->bb->grow(range, 4.0, range),
|
||||
nullptr);
|
||||
|
||||
if (candidates == nullptr || candidates->empty())
|
||||
{
|
||||
delete candidates;
|
||||
return false;
|
||||
}
|
||||
|
||||
vector<shared_ptr<LivingEntity>> packmates = getPackmates(range);
|
||||
|
||||
// Фаза 1: ищем цель с учётом лимита MAX_WOLVES_PER_TARGET
|
||||
shared_ptr<LivingEntity> target = pickBestTarget(candidates, packmates, true);
|
||||
|
||||
// Фаза 2: если все цели заняты сверх лимита — игнорируем лимит,
|
||||
// чтобы волк не простаивал
|
||||
if (target == nullptr)
|
||||
target = pickBestTarget(candidates, packmates, false);
|
||||
|
||||
delete candidates;
|
||||
|
||||
if (target == nullptr) return false;
|
||||
|
||||
assignedTarget = weak_ptr<LivingEntity>(target);
|
||||
return true;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
bool PackDistributedTargetGoal::canContinueToUse()
|
||||
{
|
||||
// Базовые проверки TargetGoal (цель жива, в радиусе follow_range и т.д.)
|
||||
if (!TargetGoal::canContinueToUse()) return false;
|
||||
|
||||
// Не удаляться от хозяина дальше поводка
|
||||
shared_ptr<Entity> owner = tameAnimal->getOwner();
|
||||
if (owner == nullptr) return false;
|
||||
|
||||
return mob->distanceToSqr(owner) <=
|
||||
maxChaseDistFromOwner * maxChaseDistFromOwner;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
void PackDistributedTargetGoal::start()
|
||||
{
|
||||
shared_ptr<LivingEntity> target = assignedTarget.lock();
|
||||
mob->setTarget(target);
|
||||
tameAnimal->onTameAggroStarted(target);
|
||||
TargetGoal::start();
|
||||
}
|
||||
56
Minecraft.World/WolfPackDistributedTargetGoal.h
Normal file
56
Minecraft.World/WolfPackDistributedTargetGoal.h
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
#pragma once
|
||||
|
||||
#include "Goal.h"
|
||||
#include "TargetGoal.h"
|
||||
#include "LivingEntity.h"
|
||||
|
||||
class TamableAnimal;
|
||||
|
||||
// Заменяет TameAnimalDefendGoal для волков в стае.
|
||||
// Вместо того чтобы вся стая фокусировалась на одной цели,
|
||||
// каждый волк выбирает наименее "занятого" врага по формуле:
|
||||
// score = assignedWolves * ASSIGNMENT_PENALTY_SQ + distanceSq
|
||||
// Меньший score = более приоритетная цель.
|
||||
class PackDistributedTargetGoal : public TargetGoal
|
||||
{
|
||||
private:
|
||||
TamableAnimal* tameAnimal;
|
||||
weak_ptr<LivingEntity> assignedTarget;
|
||||
float maxChaseDistFromOwner;
|
||||
|
||||
// Штраф за каждого уже назначенного волка на цель.
|
||||
// Эквивалентен "виртуальному удалению" цели на 12 блоков за каждого волка.
|
||||
// Чем выше — тем равномернее распределение.
|
||||
// Чем ниже — тем сильнее влияет дистанция.
|
||||
static constexpr double ASSIGNMENT_PENALTY_SQ = 12.0 * 12.0;
|
||||
|
||||
// Жёсткий лимит волков на одну цель при наличии альтернативы.
|
||||
// Если все цели заняты сверх лимита — лимит игнорируется
|
||||
// (волки не простаивают).
|
||||
static constexpr int MAX_WOLVES_PER_TARGET = 2;
|
||||
|
||||
public:
|
||||
PackDistributedTargetGoal(TamableAnimal* tameAnimal,
|
||||
bool mustSee,
|
||||
float maxChaseDistFromOwner = 20.0f);
|
||||
|
||||
bool canUse() override;
|
||||
bool canContinueToUse() override;
|
||||
void start() override;
|
||||
|
||||
private:
|
||||
// Собрать всех прирученных волков того же хозяина в радиусе range
|
||||
vector<shared_ptr<LivingEntity>> getPackmates(double range);
|
||||
|
||||
// Посчитать сколько волков из packmates уже атакуют target
|
||||
int countAssignments(
|
||||
const vector<shared_ptr<LivingEntity>>& packmates,
|
||||
shared_ptr<LivingEntity> target);
|
||||
|
||||
// Выбрать лучшую цель из candidates с учётом распределения.
|
||||
// respectLimit: если true — не назначать на цели с >= MAX_WOLVES_PER_TARGET
|
||||
shared_ptr<LivingEntity> pickBestTarget(
|
||||
const vector<shared_ptr<Entity>>* candidates,
|
||||
const vector<shared_ptr<LivingEntity>>& packmates,
|
||||
bool respectLimit);
|
||||
};
|
||||
102
Minecraft.World/WolfRetreatGoal.cpp
Normal file
102
Minecraft.World/WolfRetreatGoal.cpp
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
#include "stdafx.h"
|
||||
#include "net.minecraft.world.entity.h"
|
||||
#include "net.minecraft.world.entity.animal.h"
|
||||
#include "net.minecraft.world.entity.ai.navigation.h"
|
||||
#include "WolfRetreatGoal.h"
|
||||
|
||||
WolfRetreatGoal::WolfRetreatGoal(TamableAnimal* tameAnimal,
|
||||
float retreatHealthFraction,
|
||||
float runSpeedMultiplier)
|
||||
{
|
||||
this->tameAnimal = tameAnimal;
|
||||
this->retreatHealthFraction = retreatHealthFraction;
|
||||
this->runSpeedMultiplier = runSpeedMultiplier;
|
||||
this->recalcPathTicks = 0;
|
||||
|
||||
// Öåëü èñïîëüçóåò ôëàã äâèæåíèÿ — áëîêèðóåò FollowOwnerGoal,
|
||||
// RandomStrollGoal è ò.ä. ïîêà àêòèâíà
|
||||
setRequiredControlFlags(0);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
bool WolfRetreatGoal::canUse()
|
||||
{
|
||||
// Òîëüêî ïðèðó÷åííûé è íå ñèäÿùèé
|
||||
if (!tameAnimal->isTame()) return false;
|
||||
if (tameAnimal->isSitting()) return false;
|
||||
|
||||
// Ïðîâåðÿåì ïîðîã çäîðîâüÿ
|
||||
float hpFraction = tameAnimal->getHealth() / tameAnimal->getMaxHealth();
|
||||
if (hpFraction > retreatHealthFraction) return false;
|
||||
|
||||
// Åñòü æèâîé õîçÿèí
|
||||
shared_ptr<Entity> owner = tameAnimal->getOwner();
|
||||
if (owner == nullptr) return false;
|
||||
|
||||
shared_ptr<LivingEntity> ownerLiving =
|
||||
dynamic_pointer_cast<LivingEntity>(owner);
|
||||
return ownerLiving != nullptr && ownerLiving->isAlive();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
bool WolfRetreatGoal::canContinueToUse()
|
||||
{
|
||||
// Ïðåêðàòèòü åñëè ñåë ïî êîìàíäå
|
||||
if (tameAnimal->isSitting()) return false;
|
||||
|
||||
// Ïðåêðàòèòü åñëè äîñòàòî÷íî âûëå÷èëñÿ (ñ ãèñòåðåçèñîì)
|
||||
float hpFraction = tameAnimal->getHealth() / tameAnimal->getMaxHealth();
|
||||
if (hpFraction >= retreatHealthFraction + HYSTERESIS) return false;
|
||||
|
||||
// Ïðåêðàòèòü åñëè õîçÿèí èñ÷åç èëè óìåð
|
||||
shared_ptr<Entity> owner = tameAnimal->getOwner();
|
||||
if (owner == nullptr) return false;
|
||||
shared_ptr<LivingEntity> ownerLiving =
|
||||
dynamic_pointer_cast<LivingEntity>(owner);
|
||||
if (ownerLiving == nullptr || !ownerLiving->isAlive()) return false;
|
||||
|
||||
// Ïðåêðàòèòü åñëè óæå äîáåæàëè äî õîçÿèíà
|
||||
if (tameAnimal->distanceToSqr(owner) <= SAFE_DIST_SQ) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
void WolfRetreatGoal::start()
|
||||
{
|
||||
recalcPathTicks = 0;
|
||||
|
||||
// Áðîñèòü òåêóùóþ áîåâóþ öåëü è ñáðîñèòü çëîñòü
|
||||
tameAnimal->setTarget(nullptr);
|
||||
tameAnimal->onTameAggroStopped(); // Wolf: setAngry(false)
|
||||
|
||||
// Ñðàçó äâèãàåìñÿ ê õîçÿèíó
|
||||
shared_ptr<Entity> owner = tameAnimal->getOwner();
|
||||
if (owner != nullptr)
|
||||
{
|
||||
tameAnimal->getNavigation()->moveTo(owner, runSpeedMultiplier);
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
void WolfRetreatGoal::stop()
|
||||
{
|
||||
tameAnimal->getNavigation()->stop();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
void WolfRetreatGoal::tick()
|
||||
{
|
||||
// Ïåðåñ÷èòûâàåì ïóòü íå êàæäûé òèê — õîçÿèí äâèæåòñÿ,
|
||||
// ïóòü óñòàðåâàåò
|
||||
if (--recalcPathTicks <= 0)
|
||||
{
|
||||
recalcPathTicks = PATH_RECALC_INTERVAL;
|
||||
|
||||
shared_ptr<Entity> owner = tameAnimal->getOwner();
|
||||
if (owner != nullptr)
|
||||
{
|
||||
tameAnimal->getNavigation()->moveTo(owner, runSpeedMultiplier);
|
||||
}
|
||||
}
|
||||
}
|
||||
41
Minecraft.World/WolfRetreatGoal.h
Normal file
41
Minecraft.World/WolfRetreatGoal.h
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
#pragma once
|
||||
|
||||
#include "Goal.h"
|
||||
|
||||
class TamableAnimal;
|
||||
|
||||
class WolfRetreatGoal : public Goal
|
||||
{
|
||||
private:
|
||||
TamableAnimal* tameAnimal;
|
||||
|
||||
// Ïîðîã HP (0.0–1.0), íèæå êîòîðîãî íà÷èíàåì îòñòóïàòü
|
||||
float retreatHealthFraction;
|
||||
|
||||
// Ìíîæèòåëü ñêîðîñòè ïðè îòñòóïëåíèè
|
||||
float runSpeedMultiplier;
|
||||
|
||||
// Ñ÷¸ò÷èê òèêîâ äëÿ ïåðåc÷¸òà ïóòè (íå êàæäûé òèê)
|
||||
int recalcPathTicks;
|
||||
|
||||
// Ãèñòåðåçèñ: âîññòàíîâèòüñÿ íóæíî ÷óòü âûøå ïîðîãà îòñòóïëåíèÿ,
|
||||
// ÷òîáû íå ä¸ðãàòüñÿ òóäà-îáðàòíî
|
||||
static constexpr float HYSTERESIS = 0.15f;
|
||||
|
||||
// Ðàññòîÿíèå äî õîçÿèíà, ïðè êîòîðîì ñ÷èòàåì "äîáåæàëè"
|
||||
static constexpr float SAFE_DIST_SQ = 4.0f * 4.0f;
|
||||
|
||||
// Ïåðåñ÷èòûâàòü ïóòü êàæäûå N òèêîâ
|
||||
static constexpr int PATH_RECALC_INTERVAL = 10;
|
||||
|
||||
public:
|
||||
WolfRetreatGoal(TamableAnimal* tameAnimal,
|
||||
float retreatHealthFraction,
|
||||
float runSpeedMultiplier = 1.5f);
|
||||
|
||||
bool canUse() override;
|
||||
bool canContinueToUse() override;
|
||||
void start() override;
|
||||
void stop() override;
|
||||
void tick() override;
|
||||
};
|
||||
|
|
@ -6,4 +6,8 @@
|
|||
#include "NearestAttackableTargetGoal.h"
|
||||
#include "NonTameRandomTargetGoal.h"
|
||||
#include "OwnerHurtByTargetGoal.h"
|
||||
#include "OwnerHurtTargetGoal.h"
|
||||
#include "OwnerHurtTargetGoal.h"
|
||||
#include "TameAnimalDefendGoal.h"
|
||||
#include "WolfRetreatGoal.h"
|
||||
#include "WolfPackDistributedTargetGoal.h"
|
||||
#include "PrioritizeOwnerThreatGoal.h"
|
||||
|
|
|
|||
Loading…
Reference in a new issue