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="BiomeOverrideLayer.h" />
|
||||||
<ClInclude Include="BiomeSource.h" />
|
<ClInclude Include="BiomeSource.h" />
|
||||||
<ClInclude Include="BirchFeature.h" />
|
<ClInclude Include="BirchFeature.h" />
|
||||||
|
<ClInclude Include="BirchForestBiome.h" />
|
||||||
<ClInclude Include="Blaze.h" />
|
<ClInclude Include="Blaze.h" />
|
||||||
<ClInclude Include="BlockDestructionProgress.h" />
|
<ClInclude Include="BlockDestructionProgress.h" />
|
||||||
<ClInclude Include="BlockGenMethods.h" />
|
<ClInclude Include="BlockGenMethods.h" />
|
||||||
|
|
@ -2468,6 +2469,8 @@
|
||||||
<ClInclude Include="ComparatorTileEntity.h" />
|
<ClInclude Include="ComparatorTileEntity.h" />
|
||||||
<ClInclude Include="DaylightDetectorTile.h" />
|
<ClInclude Include="DaylightDetectorTile.h" />
|
||||||
<ClInclude Include="DaylightDetectorTileEntity.h" />
|
<ClInclude Include="DaylightDetectorTileEntity.h" />
|
||||||
|
<ClInclude Include="PrioritizeOwnerThreatGoal.h" />
|
||||||
|
<ClInclude Include="TameAnimalDefendGoal.h" />
|
||||||
<ClInclude Include="DropperTile.h" />
|
<ClInclude Include="DropperTile.h" />
|
||||||
<ClInclude Include="DropperTileEntity.h" />
|
<ClInclude Include="DropperTileEntity.h" />
|
||||||
<ClInclude Include="DummyCriteria.h" />
|
<ClInclude Include="DummyCriteria.h" />
|
||||||
|
|
@ -2847,6 +2850,8 @@
|
||||||
<ClInclude Include="Witch.h" />
|
<ClInclude Include="Witch.h" />
|
||||||
<ClInclude Include="WitherBoss.h" />
|
<ClInclude Include="WitherBoss.h" />
|
||||||
<ClInclude Include="WitherSkull.h" />
|
<ClInclude Include="WitherSkull.h" />
|
||||||
|
<ClInclude Include="WolfPackDistributedTargetGoal.h" />
|
||||||
|
<ClInclude Include="WolfRetreatGoal.h" />
|
||||||
<ClInclude Include="WoodButtonTile.h" />
|
<ClInclude Include="WoodButtonTile.h" />
|
||||||
<ClInclude Include="WoodSlabTile.h" />
|
<ClInclude Include="WoodSlabTile.h" />
|
||||||
<ClInclude Include="WoodTile.h" />
|
<ClInclude Include="WoodTile.h" />
|
||||||
|
|
@ -3519,6 +3524,7 @@
|
||||||
<ClCompile Include="BiomeOverrideLayer.cpp" />
|
<ClCompile Include="BiomeOverrideLayer.cpp" />
|
||||||
<ClCompile Include="BiomeSource.cpp" />
|
<ClCompile Include="BiomeSource.cpp" />
|
||||||
<ClCompile Include="BirchFeature.cpp" />
|
<ClCompile Include="BirchFeature.cpp" />
|
||||||
|
<ClCompile Include="BirchForestBiome.cpp" />
|
||||||
<ClCompile Include="Blaze.cpp" />
|
<ClCompile Include="Blaze.cpp" />
|
||||||
<ClCompile Include="BlockDestructionProgress.cpp" />
|
<ClCompile Include="BlockDestructionProgress.cpp" />
|
||||||
<ClCompile Include="BlockGenMethods.cpp" />
|
<ClCompile Include="BlockGenMethods.cpp" />
|
||||||
|
|
@ -3540,6 +3546,8 @@
|
||||||
<ClCompile Include="ComparatorTileEntity.cpp" />
|
<ClCompile Include="ComparatorTileEntity.cpp" />
|
||||||
<ClCompile Include="DaylightDetectorTile.cpp" />
|
<ClCompile Include="DaylightDetectorTile.cpp" />
|
||||||
<ClCompile Include="DaylightDetectorTileEntity.cpp" />
|
<ClCompile Include="DaylightDetectorTileEntity.cpp" />
|
||||||
|
<ClCompile Include="PrioritizeOwnerThreatGoal.cpp" />
|
||||||
|
<ClCompile Include="TameAnimalDefendGoal.cpp" />
|
||||||
<ClCompile Include="DropperTile.cpp" />
|
<ClCompile Include="DropperTile.cpp" />
|
||||||
<ClCompile Include="DropperTileEntity.cpp" />
|
<ClCompile Include="DropperTileEntity.cpp" />
|
||||||
<ClCompile Include="DummyCriteria.cpp" />
|
<ClCompile Include="DummyCriteria.cpp" />
|
||||||
|
|
@ -3649,6 +3657,8 @@
|
||||||
<ClCompile Include="StructureFeatureSavedData.cpp" />
|
<ClCompile Include="StructureFeatureSavedData.cpp" />
|
||||||
<ClCompile Include="Team.cpp" />
|
<ClCompile Include="Team.cpp" />
|
||||||
<ClCompile Include="WeightedPressurePlateTile.cpp" />
|
<ClCompile Include="WeightedPressurePlateTile.cpp" />
|
||||||
|
<ClCompile Include="WolfPackDistributedTargetGoal.cpp" />
|
||||||
|
<ClCompile Include="WolfRetreatGoal.cpp" />
|
||||||
<ClCompile Include="WoodButtonTile.cpp" />
|
<ClCompile Include="WoodButtonTile.cpp" />
|
||||||
<ClCompile Include="WoolTileItem.cpp" />
|
<ClCompile Include="WoolTileItem.cpp" />
|
||||||
<ClCompile Include="CoalItem.cpp" />
|
<ClCompile Include="CoalItem.cpp" />
|
||||||
|
|
|
||||||
|
|
@ -3203,6 +3203,21 @@
|
||||||
<ClInclude Include="LightSourceTile.h">
|
<ClInclude Include="LightSourceTile.h">
|
||||||
<Filter>net\minecraft\world\level\tile</Filter>
|
<Filter>net\minecraft\world\level\tile</Filter>
|
||||||
</ClInclude>
|
</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>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClCompile Include="stdafx.cpp">
|
<ClCompile Include="stdafx.cpp">
|
||||||
|
|
@ -5638,5 +5653,20 @@
|
||||||
<ClCompile Include="LightSourceTile.cpp">
|
<ClCompile Include="LightSourceTile.cpp">
|
||||||
<Filter>net\minecraft\world\level\tile</Filter>
|
<Filter>net\minecraft\world\level\tile</Filter>
|
||||||
</ClCompile>
|
</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>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|
@ -32,5 +32,13 @@ void OwnerHurtByTargetGoal::start()
|
||||||
timestamp = owner->getLastHurtByMobTimestamp();
|
timestamp = owner->getLastHurtByMobTimestamp();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tameAnimal->onTameAggroStarted(owner->getLastHurtByMob());
|
||||||
|
|
||||||
TargetGoal::start();
|
TargetGoal::start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void OwnerHurtByTargetGoal::stop()
|
||||||
|
{
|
||||||
|
tameAnimal->onTameAggroStopped();
|
||||||
|
TargetGoal::stop();
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,4 +16,5 @@ public:
|
||||||
|
|
||||||
bool canUse();
|
bool canUse();
|
||||||
void start();
|
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 void setOwnerUUID(const wstring &name);
|
||||||
virtual shared_ptr<Entity> getOwner();
|
virtual shared_ptr<Entity> getOwner();
|
||||||
virtual SitGoal *getSitGoal();
|
virtual SitGoal *getSitGoal();
|
||||||
|
virtual void onTameAggroStarted(shared_ptr<LivingEntity> target) {}
|
||||||
|
virtual void onTameAggroStopped() {}
|
||||||
bool wantsToAttack(shared_ptr<LivingEntity> target, shared_ptr<LivingEntity> owner);
|
bool wantsToAttack(shared_ptr<LivingEntity> target, shared_ptr<LivingEntity> owner);
|
||||||
Team *getTeam();
|
Team *getTeam();
|
||||||
bool isAlliedTo(shared_ptr<LivingEntity> other);
|
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 "Wolf.h"
|
||||||
#include "..\Minecraft.Client\Textures.h"
|
#include "..\Minecraft.Client\Textures.h"
|
||||||
#include "SoundTypes.h"
|
#include "SoundTypes.h"
|
||||||
|
#include "PrimedTnt.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -39,20 +40,28 @@ Wolf::Wolf(Level *level) : TamableAnimal( level )
|
||||||
|
|
||||||
getNavigation()->setAvoidWater(true);
|
getNavigation()->setAvoidWater(true);
|
||||||
goalSelector.addGoal(1, new FloatGoal(this));
|
goalSelector.addGoal(1, new FloatGoal(this));
|
||||||
goalSelector.addGoal(2, sitGoal, false);
|
goalSelector.addGoal(2, new WolfRetreatGoal(this, 0.25f, 1.5f));
|
||||||
goalSelector.addGoal(3, new LeapAtTargetGoal(this, 0.4));
|
goalSelector.addGoal(3, new AvoidPlayerGoal(this, typeid(Creeper), 12, 1.25, 1.25));
|
||||||
goalSelector.addGoal(4, new MeleeAttackGoal(this, 1.0, true));
|
goalSelector.addGoal(4, new AvoidPlayerGoal(this, typeid(PrimedTnt), 12, 1.25, 1.25));
|
||||||
goalSelector.addGoal(5, new FollowOwnerGoal(this, 1.0, 10, 2));
|
goalSelector.addGoal(5, sitGoal, false);
|
||||||
goalSelector.addGoal(6, new BreedGoal(this, 1.0));
|
goalSelector.addGoal(6, new LeapAtTargetGoal(this, 0.4));
|
||||||
goalSelector.addGoal(7, new RandomStrollGoal(this, 1.0));
|
goalSelector.addGoal(7, new MeleeAttackGoal(this, 1.0, true));
|
||||||
goalSelector.addGoal(8, new BegGoal(this, 8));
|
goalSelector.addGoal(8, new FollowOwnerGoal(this, 1.0, 10, 2));
|
||||||
goalSelector.addGoal(9, new LookAtPlayerGoal(this, typeid(Player), 8));
|
goalSelector.addGoal(9, new BreedGoal(this, 1.0));
|
||||||
goalSelector.addGoal(9, new RandomLookAroundGoal(this));
|
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(1, new HurtByTargetGoal(this, true));
|
||||||
targetSelector.addGoal(2, new OwnerHurtTargetGoal(this));
|
targetSelector.addGoal(2, new OwnerHurtByTargetGoal(this));
|
||||||
targetSelector.addGoal(3, new HurtByTargetGoal(this, true));
|
targetSelector.addGoal(3, new PrioritizeOwnerThreatGoal(this));
|
||||||
targetSelector.addGoal(4, new NonTameRandomTargetGoal(this, typeid(Sheep), 200, false));
|
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
|
setTame(false); // Initialize health
|
||||||
}
|
}
|
||||||
|
|
@ -81,14 +90,20 @@ bool Wolf::useNewAi()
|
||||||
void Wolf::setTarget(shared_ptr<LivingEntity> target)
|
void Wolf::setTarget(shared_ptr<LivingEntity> target)
|
||||||
{
|
{
|
||||||
TamableAnimal::setTarget(target);
|
TamableAnimal::setTarget(target);
|
||||||
if ( target == nullptr )
|
if (target == nullptr)
|
||||||
{
|
|
||||||
setAngry(false);
|
setAngry(false);
|
||||||
}
|
else
|
||||||
else if(!isTame())
|
|
||||||
{
|
|
||||||
setAngry(true);
|
setAngry(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Wolf::onTameAggroStarted(shared_ptr<LivingEntity> target)
|
||||||
|
{
|
||||||
|
setAngry(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Wolf::onTameAggroStopped()
|
||||||
|
{
|
||||||
|
setAngry(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Wolf::serverAiMobStep()
|
void Wolf::serverAiMobStep()
|
||||||
|
|
@ -474,8 +489,7 @@ bool Wolf::isFood(shared_ptr<ItemInstance> item)
|
||||||
|
|
||||||
int Wolf::getMaxSpawnClusterSize()
|
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 8;
|
||||||
return 4;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Wolf::isAngry()
|
bool Wolf::isAngry()
|
||||||
|
|
|
||||||
|
|
@ -76,6 +76,9 @@ public:
|
||||||
// For tooltips
|
// For tooltips
|
||||||
int GetSynchedHealth();
|
int GetSynchedHealth();
|
||||||
|
|
||||||
|
virtual void onTameAggroStarted(shared_ptr<LivingEntity> target) override;
|
||||||
|
virtual void onTameAggroStopped() override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual shared_ptr<AgableMob> getBreedOffspring(shared_ptr<AgableMob> target);
|
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;
|
||||||
|
};
|
||||||
|
|
@ -7,3 +7,7 @@
|
||||||
#include "NonTameRandomTargetGoal.h"
|
#include "NonTameRandomTargetGoal.h"
|
||||||
#include "OwnerHurtByTargetGoal.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