#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> PackDistributedTargetGoal::getPackmates( double range) { vector> result; wstring ownerUUID = tameAnimal->getOwnerUUID(); if (ownerUUID.empty()) return result; // Ищем всех волков в радиусе vector>* 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 = dynamic_pointer_cast(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>& packmates, shared_ptr target) { if (target == nullptr) return 0; int count = 0; for (auto& packmate : packmates) { shared_ptr packTarget = dynamic_pointer_cast( dynamic_pointer_cast(packmate) != nullptr ? dynamic_pointer_cast(packmate)->getTarget() : nullptr); if (packTarget != nullptr && packTarget.get() == target.get()) count++; } return count; } // --------------------------------------------------------------------------- shared_ptr PackDistributedTargetGoal::pickBestTarget( const vector>* candidates, const vector>& packmates, bool respectLimit) { shared_ptr best = nullptr; double bestScore = DBL_MAX; shared_ptr owner = dynamic_pointer_cast(tameAnimal->getOwner()); for (auto& e : *candidates) { shared_ptr le = dynamic_pointer_cast(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>* candidates = mob->level->getEntitiesOfClass( typeid(Monster), mob->bb->grow(range, 4.0, range), nullptr); if (candidates == nullptr || candidates->empty()) { delete candidates; return false; } vector> packmates = getPackmates(range); // Фаза 1: ищем цель с учётом лимита MAX_WOLVES_PER_TARGET shared_ptr target = pickBestTarget(candidates, packmates, true); // Фаза 2: если все цели заняты сверх лимита — игнорируем лимит, // чтобы волк не простаивал if (target == nullptr) target = pickBestTarget(candidates, packmates, false); delete candidates; if (target == nullptr) return false; assignedTarget = weak_ptr(target); return true; } // --------------------------------------------------------------------------- bool PackDistributedTargetGoal::canContinueToUse() { // Базовые проверки TargetGoal (цель жива, в радиусе follow_range и т.д.) if (!TargetGoal::canContinueToUse()) return false; // Не удаляться от хозяина дальше поводка shared_ptr owner = tameAnimal->getOwner(); if (owner == nullptr) return false; return mob->distanceToSqr(owner) <= maxChaseDistFromOwner * maxChaseDistFromOwner; } // --------------------------------------------------------------------------- void PackDistributedTargetGoal::start() { shared_ptr target = assignedTarget.lock(); mob->setTarget(target); tameAnimal->onTameAggroStarted(target); TargetGoal::start(); }