diff --git a/CMakeLists.txt b/CMakeLists.txt index a8c5310..0dd4454 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.22) project(GGP_Exam VERSION 1.0.0) -set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) if(NOT CMAKE_BUILD_TYPE) diff --git a/_DEMO_DEBUG/GPP_Plugin_d.dll b/_DEMO_DEBUG/GPP_Plugin_d.dll index 7c40a11..42d6a36 100644 Binary files a/_DEMO_DEBUG/GPP_Plugin_d.dll and b/_DEMO_DEBUG/GPP_Plugin_d.dll differ diff --git a/_DEMO_DEBUG/GPP_Plugin_d.pdb b/_DEMO_DEBUG/GPP_Plugin_d.pdb index 06ecf8b..e90e0fb 100644 Binary files a/_DEMO_DEBUG/GPP_Plugin_d.pdb and b/_DEMO_DEBUG/GPP_Plugin_d.pdb differ diff --git a/_DEMO_DEBUG/imgui.ini b/_DEMO_DEBUG/imgui.ini index bd31cb6..d90e1e6 100644 --- a/_DEMO_DEBUG/imgui.ini +++ b/_DEMO_DEBUG/imgui.ini @@ -579,8 +579,8 @@ Size=240,520 Collapsed=0 [WORLD INFO [305 FPS]] -Pos=651,10 -Size=240,431 +Pos=710,10 +Size=240,520 Collapsed=0 [WORLD INFO [291 FPS]] @@ -984,8 +984,8 @@ Size=240,520 Collapsed=0 [WORLD INFO [284 FPS]] -Pos=651,10 -Size=240,431 +Pos=710,10 +Size=240,520 Collapsed=0 [WORLD INFO [275 FPS]] @@ -1024,7 +1024,7 @@ Size=240,520 Collapsed=0 [*GAME OVER*] -Pos=360,150 +Pos=390,195 Size=180,150 Collapsed=0 @@ -1139,8 +1139,8 @@ Size=240,431 Collapsed=0 [WORLD INFO [352 FPS]] -Pos=651,10 -Size=240,431 +Pos=710,10 +Size=240,520 Collapsed=0 [WORLD INFO [336 FPS]] @@ -1763,3 +1763,213 @@ Pos=710,10 Size=240,520 Collapsed=0 +[WORLD INFO [739 FPS]] +Pos=710,10 +Size=240,520 +Collapsed=0 + +[WORLD INFO [615 FPS]] +Pos=710,10 +Size=240,520 +Collapsed=0 + +[WORLD INFO [592 FPS]] +Pos=710,10 +Size=240,520 +Collapsed=0 + +[WORLD INFO [573 FPS]] +Pos=710,10 +Size=240,520 +Collapsed=0 + +[WORLD INFO [531 FPS]] +Pos=710,10 +Size=240,520 +Collapsed=0 + +[WORLD INFO [491 FPS]] +Pos=710,10 +Size=240,520 +Collapsed=0 + +[WORLD INFO [463 FPS]] +Pos=710,10 +Size=240,520 +Collapsed=0 + +[WORLD INFO [435 FPS]] +Pos=710,10 +Size=240,520 +Collapsed=0 + +[WORLD INFO [410 FPS]] +Pos=710,10 +Size=240,520 +Collapsed=0 + +[WORLD INFO [388 FPS]] +Pos=710,10 +Size=240,520 +Collapsed=0 + +[WORLD INFO [368 FPS]] +Pos=710,10 +Size=240,520 +Collapsed=0 + +[WORLD INFO [292 FPS]] +Pos=710,10 +Size=240,520 +Collapsed=0 + +[WORLD INFO [779 FPS]] +Pos=710,10 +Size=240,520 +Collapsed=0 + +[WORLD INFO [633 FPS]] +Pos=710,10 +Size=240,520 +Collapsed=0 + +[WORLD INFO [609 FPS]] +Pos=710,10 +Size=240,520 +Collapsed=0 + +[WORLD INFO [583 FPS]] +Pos=710,10 +Size=240,520 +Collapsed=0 + +[WORLD INFO [538 FPS]] +Pos=710,10 +Size=240,520 +Collapsed=0 + +[WORLD INFO [468 FPS]] +Pos=710,10 +Size=240,520 +Collapsed=0 + +[WORLD INFO [441 FPS]] +Pos=710,10 +Size=240,520 +Collapsed=0 + +[WORLD INFO [395 FPS]] +Pos=710,10 +Size=240,520 +Collapsed=0 + +[WORLD INFO [374 FPS]] +Pos=710,10 +Size=240,520 +Collapsed=0 + +[WORLD INFO [339 FPS]] +Pos=710,10 +Size=240,520 +Collapsed=0 + +[WORLD INFO [808 FPS]] +Pos=710,10 +Size=240,520 +Collapsed=0 + +[WORLD INFO [673 FPS]] +Pos=710,10 +Size=240,520 +Collapsed=0 + +[WORLD INFO [646 FPS]] +Pos=710,10 +Size=240,520 +Collapsed=0 + +[WORLD INFO [620 FPS]] +Pos=710,10 +Size=240,520 +Collapsed=0 + +[WORLD INFO [570 FPS]] +Pos=710,10 +Size=240,520 +Collapsed=0 + +[WORLD INFO [465 FPS]] +Pos=710,10 +Size=240,520 +Collapsed=0 + +[WORLD INFO [436 FPS]] +Pos=710,10 +Size=240,520 +Collapsed=0 + +[WORLD INFO [412 FPS]] +Pos=710,10 +Size=240,520 +Collapsed=0 + +[WORLD INFO [389 FPS]] +Pos=710,10 +Size=240,520 +Collapsed=0 + +[WORLD INFO [370 FPS]] +Pos=710,10 +Size=240,520 +Collapsed=0 + +[WORLD INFO [781 FPS]] +Pos=710,10 +Size=240,520 +Collapsed=0 + +[WORLD INFO [632 FPS]] +Pos=710,10 +Size=240,520 +Collapsed=0 + +[WORLD INFO [572 FPS]] +Pos=710,10 +Size=240,520 +Collapsed=0 + +[WORLD INFO [494 FPS]] +Pos=710,10 +Size=240,520 +Collapsed=0 + +[WORLD INFO [433 FPS]] +Pos=710,10 +Size=240,520 +Collapsed=0 + +[WORLD INFO [387 FPS]] +Pos=710,10 +Size=240,520 +Collapsed=0 + +[WORLD INFO [765 FPS]] +Pos=710,10 +Size=240,520 +Collapsed=0 + +[WORLD INFO [472 FPS]] +Pos=710,10 +Size=240,520 +Collapsed=0 + +[WORLD INFO [458 FPS]] +Pos=710,10 +Size=240,520 +Collapsed=0 + +[WORLD INFO [422 FPS]] +Pos=710,10 +Size=240,520 +Collapsed=0 + diff --git a/project/Behaviour.cpp b/project/Behaviour.cpp index 0d2857c..ee737f2 100644 --- a/project/Behaviour.cpp +++ b/project/Behaviour.cpp @@ -2,31 +2,45 @@ #include "Behaviour.h" #include "BehaviourTree.h" #include +#include "Thinker.h" + +#define TO_RAD(i) i * (M_PI / 180) + +static int randNumRange(int minRange, int maxRange) { + std::random_device rd; + std::mt19937 seed(rd()); + std::uniform_int_distribution<> range(minRange, maxRange); + + return range(seed); +} namespace BT_Action { - BT::State FindAHouse(Blackboard* blackboardPtr) { - IExamInterface* interfacePtr{}; - blackboardPtr->GetData("Interface", interfacePtr); - - const Elite::Vector2 worldDimensions{ interfacePtr->World_GetInfo().Dimensions }; - - std::random_device rd; // obtain a random number from hardware - std::mt19937 seed(rd()); // seed the generator - std::uniform_int_distribution<> range(-worldDimensions.x, worldDimensions.x); // define the range - - const Elite::Vector2 randomLocation(range(seed), range(seed)); - const Elite::Vector2 target = interfacePtr->NavMesh_GetClosestPathPoint(randomLocation); - - if (randomLocation != target) { - blackboardPtr->ChangeData("Target", randomLocation); - - std::cout << "Data send " << randomLocation << "\n"; + BT::State SetTimer(Blackboard* blackboardPtr, const std::string& timerName, bool doOnce) { + bool didOnce{}; + blackboardPtr->GetData("Timer" + timerName + "DoOnce", didOnce); + if (doOnce && didOnce) return BT::State::Success; - } - return BT::State::Failure; + if (doOnce) + blackboardPtr->ChangeData("Timer" + timerName + "DoOnce", true); + + blackboardPtr->ChangeData("Timer" + timerName, std::chrono::steady_clock::now()); + + return BT::State::Success; + } + + BT::State UnlockTimer(Blackboard* blackboardPtr, const std::string& timerName) { + blackboardPtr->ChangeData("TimerLock" + timerName, false); + + return BT::State::Success; + } + + BT::State LockTimer(Blackboard* blackboardPtr, const std::string& timerName) { + blackboardPtr->ChangeData("TimerLock" + timerName, true); + + return BT::State::Success; } BT::State GoToTarget(Blackboard* blackboardPtr) { @@ -34,11 +48,22 @@ namespace BT_Action Elite::Vector2 target{}; SteeringPlugin_Output steering{}; + std::chrono::steady_clock::time_point timer{}; + float maxTime{}; + bool doOnce{}; + blackboardPtr->GetData("Interface", interfacePtr); blackboardPtr->GetData("Target", target); blackboardPtr->GetData("Steering", steering); - std::cout << "target received " << target << "\n"; + blackboardPtr->GetData("FailSafe", timer); + blackboardPtr->GetData("MaxFailSafe", maxTime); + blackboardPtr->GetData("FailSafeDoOnce", doOnce); + + if (!doOnce) { + blackboardPtr->ChangeData("FailSafe", std::chrono::steady_clock::now()); + blackboardPtr->ChangeData("FailSafeDoOnce", true); + } const auto agentInfo = interfacePtr->Agent_GetInfo(); @@ -46,12 +71,18 @@ namespace BT_Action steering.LinearVelocity = nextTargetPos - agentInfo.Position; steering.LinearVelocity.Normalize(); - steering.LinearVelocity *= agentInfo.MaxLinearSpeed; + steering.LinearVelocity *= agentInfo.MaxLinearSpeed * 100; - if (Distance(nextTargetPos, agentInfo.Position) < 2.f) { - steering.LinearVelocity = Elite::ZeroVector2; + const std::chrono::steady_clock::time_point currentTime{ std::chrono::steady_clock::now() }; + const std::chrono::duration elapsedSec{ currentTime - timer }; - std::cout << "target reached at " << target << "\n"; + if (elapsedSec.count() > maxTime) { + blackboardPtr->ChangeData("FailSafeDoOnce", false); + return BT::State::Success; + } + + if (Distance(target, agentInfo.Position) < 2.f) { + blackboardPtr->ChangeData("FailSafeDoOnce", false); return BT::State::Success; } @@ -60,4 +91,434 @@ namespace BT_Action return BT::State::Running; } -} \ No newline at end of file + + BT::State EnableSpin(Blackboard* blackboardPtr) { + blackboardPtr->ChangeData("Spin", true); + + return BT::State::Success; + } + + BT::State DisableSpin(Blackboard* blackboardPtr) { + blackboardPtr->ChangeData("Spin", false); + + return BT::State::Success; + } + + BT::State FindClosestEdge(Blackboard* blackboardPtr, int degree) { + IExamInterface* interfacePtr{}; + blackboardPtr->GetData("Interface", interfacePtr); + + const Elite::Vector2 playerPos{ interfacePtr->Agent_GetInfo().Position }; + + constexpr float offset{ 3.f }; + const Elite::Vector2 center{ interfacePtr->GetPurgeZonesInFOV()[0].Center }; + const float radius{ interfacePtr->GetPurgeZonesInFOV()[0].Radius + offset }; + + float closestTarget{ FLT_MAX }; + Elite::Vector2 finalTarget{}; + + constexpr int circleDegrees{ 360 }; + + if (degree > circleDegrees) { + degree = circleDegrees; + } + for (int i = 0; i <= circleDegrees; i += degree) { + const Elite::Vector2 pointOnCircle{ center.x + radius * std::cosf(TO_RAD(i)), center.y + radius * std::sinf(TO_RAD(i)) }; + + const float targetDistance{ playerPos.DistanceSquared(pointOnCircle) }; + + if (closestTarget > targetDistance) { + closestTarget = targetDistance; + finalTarget = pointOnCircle; + } + } + + blackboardPtr->ChangeData("Target", finalTarget); + + return BT::State::Success; + } + + BT::State SetItemAsTarget(Blackboard* blackboardPtr) { + IExamInterface* interfacePtr{}; + blackboardPtr->GetData("Interface", interfacePtr); + + if (interfacePtr->GetItemsInFOV().capacity() == 0) { + return BT::State::Failure; + } + + ItemInfo targetItem{}; + float closestItem{ FLT_MAX }; + + for (const auto item : interfacePtr->GetItemsInFOV()) { + const float itemDistance{ interfacePtr->Agent_GetInfo().Position.DistanceSquared(item.Location) }; + + if (closestItem < itemDistance) + continue; + + closestItem = itemDistance; + targetItem = item; + } + + blackboardPtr->ChangeData("TargetItem", targetItem); + blackboardPtr->ChangeData("Target", targetItem.Location); + + return BT::State::Success; + } + + BT::State DestroyItemOnFloor(Blackboard* blackboardPtr) { + IExamInterface* interfacePtr{}; + ItemInfo targetItem{}; + + blackboardPtr->GetData("Interface", interfacePtr); + blackboardPtr->GetData("TargetItem", targetItem); + + if (interfacePtr->DestroyItem(targetItem)) + return BT::State::Success; + + return BT::State::Failure; + } + + BT::State PickUpItem(Blackboard* blackboardPtr) { + IExamInterface* interfacePtr{}; + Thinker* thinkerPtr{}; + ItemInfo targetItem{}; + int freeSlot{}; + + blackboardPtr->GetData("Interface", interfacePtr); + blackboardPtr->GetData("Brain", thinkerPtr); + blackboardPtr->GetData("TargetItem", targetItem); + blackboardPtr->GetData("NextFreeSlot", freeSlot); + + if (interfacePtr->GrabItem(targetItem)) { + interfacePtr->Inventory_AddItem(freeSlot, targetItem); + + blackboardPtr->ChangeData("NextFreeSlot", thinkerPtr->AddItemToMemory(targetItem)); + + return BT::State::Success; + } + + return BT::State::Failure; + } + + BT::State SwapItem(Blackboard* blackboardPtr) { + IExamInterface* interfacePtr{}; + Thinker* thinkerPtr{}; + ItemInfo targetItem{}; + + blackboardPtr->GetData("Interface", interfacePtr); + blackboardPtr->GetData("Brain", thinkerPtr); + blackboardPtr->GetData("TargetItem", targetItem); + + if (interfacePtr->GrabItem(targetItem)) { + const int slot = thinkerPtr->FindEmptyValue(targetItem); + interfacePtr->Inventory_RemoveItem(slot); + interfacePtr->Inventory_AddItem(slot, targetItem); + + return BT::State::Success; + } + + return BT::State::Failure; + } + + BT::State CheckItem(Blackboard* blackboardPtr) { + IExamInterface* interfacePtr{}; + Thinker* thinkerPtr{}; + ItemInfo targetItem{}; + + blackboardPtr->GetData("Interface", interfacePtr); + blackboardPtr->GetData("Brain", thinkerPtr); + blackboardPtr->GetData("TargetItem", targetItem); + + const int slotIndex{ thinkerPtr->CheckItem(targetItem) }; + + if (slotIndex == interfacePtr->Inventory_GetCapacity() - 1) { + if (targetItem.Type == eItemType::SHOTGUN || targetItem.Type == eItemType::PISTOL) { + interfacePtr->DestroyItem(targetItem); + return BT::State::Success; + } + + if (interfacePtr->GrabItem(targetItem)) { + interfacePtr->Inventory_AddItem(slotIndex, targetItem); + interfacePtr->Inventory_UseItem(slotIndex); + interfacePtr->Inventory_RemoveItem(slotIndex); + + return BT::State::Success; + } + } + else { + if (!(targetItem.Type == eItemType::SHOTGUN || targetItem.Type == eItemType::PISTOL)) { + interfacePtr->Inventory_UseItem(slotIndex); + } + + if (interfacePtr->GrabItem(targetItem)) { + interfacePtr->Inventory_RemoveItem(slotIndex); + interfacePtr->Inventory_AddItem(slotIndex, targetItem); + return BT::State::Success; + } + } + + return BT::State::Failure; + } + + BT::State UseItem(Blackboard* blackboardPtr, eItemType type) { + IExamInterface* interfacePtr{}; + Thinker* thinkerPtr{}; + + blackboardPtr->GetData("Interface", interfacePtr); + blackboardPtr->GetData("Brain", thinkerPtr); + + const auto item = thinkerPtr->FindLeastValueItem(type); + + if (item->ItemInfo.Value <= 0) + return BT::State::Failure; + + if (interfacePtr->Inventory_UseItem(item->invIndex)) { + item->ItemInfo.Value = 0; + return BT::State::Success; + } + + return BT::State::Failure; + } + + BT::State TryFindHouse(Blackboard* blackboardPtr, float searchRadius, int degree) { + IExamInterface* interfacePtr{}; + Thinker* thinkerPtr{}; + + blackboardPtr->GetData("Interface", interfacePtr); + blackboardPtr->GetData("Brain", thinkerPtr); + + blackboardPtr->ChangeData("TargetHouse", HouseInfo{}); + + const Elite::Vector2 playerPos{ interfacePtr->Agent_GetInfo().Position }; + + float closestTarget{ FLT_MAX }; + Elite::Vector2 finalTarget{}; + + constexpr int circleDegrees{ 360 }; + + if (degree > circleDegrees) { + degree = circleDegrees; + } + + for (int i = 0; i <= circleDegrees; i += degree) { + const Elite::Vector2 pointOnCircle{ playerPos.x + searchRadius * std::cosf(TO_RAD(i)), playerPos.y + searchRadius * std::sinf(TO_RAD(i)) }; + const Elite::Vector2 target = interfacePtr->NavMesh_GetClosestPathPoint(pointOnCircle); + + if (pointOnCircle != target) { + constexpr float houseOffset{ 5.f }; + if (thinkerPtr->CheckIfTargetIsExplored(target, houseOffset)) + continue; + + const float targetDistance{ playerPos.DistanceSquared(target) }; + + if (closestTarget > targetDistance) { + closestTarget = targetDistance; + finalTarget = target; + } + } + } + + if (finalTarget == Elite::Vector2{}) { + finalTarget = Elite::Vector2(randNumRange(-searchRadius, searchRadius), + randNumRange(-searchRadius, searchRadius)); + } + + blackboardPtr->ChangeData("Target", finalTarget); + + return BT::State::Success; + } + + BT::State GetHouseAsTarget(Blackboard* blackboardPtr, float maxTravelDistance) { + IExamInterface* interfacePtr{}; + Thinker* thinkerPtr{}; + + blackboardPtr->GetData("Interface", interfacePtr); + blackboardPtr->GetData("Brain", thinkerPtr); + + const HouseInfo targetHouse{ thinkerPtr->CheckHouseValidTarget(interfacePtr->Agent_GetInfo().Position, maxTravelDistance) }; + + if (targetHouse.Size.x <= 0) { + return BT::State::Failure; + } + + blackboardPtr->ChangeData("Target", targetHouse.Center); + blackboardPtr->ChangeData("TargetHouse", targetHouse); + + return BT::State::Success; + } + + BT::State CheckHouses(Blackboard* blackboardPtr) { + Thinker* thinkerPtr{}; + IExamInterface* interfacePtr{}; + + blackboardPtr->GetData("Brain", thinkerPtr); + blackboardPtr->GetData("Interface", interfacePtr); + + if (thinkerPtr->CheckHousesForMemory(interfacePtr->GetHousesInFOV())) { + return BT::State::Success; + } + + return BT::State::Failure; + } + + BT::State SetExpireDate(Blackboard* blackboardPtr) { + Thinker* thinkerPtr{}; + HouseInfo targetHouse{}; + + blackboardPtr->GetData("Brain", thinkerPtr); + blackboardPtr->GetData("TargetHouse", targetHouse); + + thinkerPtr->SetTargetHouseExpireDate(targetHouse); + + return BT::State::Success; + } + + BT::State GetInsideTarget(Blackboard* blackboardPtr, float offset) { + HouseInfo targetHouse{}; + blackboardPtr->GetData("TargetHouse", targetHouse); + + const Elite::Vector2 houseCenter{ targetHouse.Center }; + const Elite::Vector2 houseSize{ targetHouse.Size }; + + const Elite::Vector2 targetLocation(randNumRange(int(houseCenter.x - houseSize.x / 2 + offset), int(houseCenter.x + houseSize.x / 2 - offset)), + randNumRange(int(houseCenter.y - houseSize.y / 2 + offset), int(houseCenter.y + houseSize.y / 2 - offset))); + + blackboardPtr->ChangeData("Target", targetLocation); + + return BT::State::Success; + } +} + +namespace BT_Condition +{ + bool CheckTimerLock(Blackboard* blackboardPtr, const std::string& timerName) { + bool lock{}; + blackboardPtr->GetData(timerName + "TimerLock", lock); + + return !lock; + } + + bool CheckTimer(Blackboard* blackboardPtr, const std::string& timerName, bool doOnce) { + std::chrono::steady_clock::time_point timer{}; + float maxTime{}; + + blackboardPtr->GetData("Timer" + timerName, timer); + blackboardPtr->GetData("MaxTime" + timerName, maxTime); + + const std::chrono::steady_clock::time_point currentTime{ std::chrono::steady_clock::now() }; + const std::chrono::duration elapsedSec{ currentTime - timer }; + + if (elapsedSec.count() > maxTime) { + if (doOnce) + blackboardPtr->ChangeData("Timer" + timerName + "DoOnce", false); + + return true; + } + + return false; + } + + bool SeePurgeZone(Blackboard* blackboardPtr) { + IExamInterface* interfacePtr{}; + blackboardPtr->GetData("Interface", interfacePtr); + + return interfacePtr->GetPurgeZonesInFOV().capacity() > 0; + } + + bool ItemInInv(Blackboard* blackboardPtr, eItemType type) { + Thinker* thinkerPtr{}; + blackboardPtr->GetData("Brain", thinkerPtr); + + return thinkerPtr->IsItemInInv(type); + } + + bool HpUnderThreshold(Blackboard* blackboardPtr, float threshold) { + IExamInterface* interfacePtr{}; + blackboardPtr->GetData("Interface", interfacePtr); + + return interfacePtr->Agent_GetInfo().Health <= threshold; + } + + bool CheckMinNeededEnergy(Blackboard* blackboardPtr) { + IExamInterface* interfacePtr{}; + Thinker* thinkerPtr{}; + + blackboardPtr->GetData("Interface", interfacePtr); + blackboardPtr->GetData("Brain", thinkerPtr); + + const auto item = thinkerPtr->FindLeastValueItem(eItemType::FOOD); + + return interfacePtr->Agent_GetInfo().Energy <= 10.f - item->ItemInfo.Value; + } + + bool SeeItem(Blackboard* blackboardPtr) { + IExamInterface* interfacePtr{}; + blackboardPtr->GetData("Interface", interfacePtr); + + return interfacePtr->GetItemsInFOV().capacity() > 0; + } + + bool IsTypeOfItem(Blackboard* blackboardPtr, eItemType type) { + ItemInfo targetItem{}; + blackboardPtr->GetData("TargetItem", targetItem); + + return targetItem.Type == type; + } + + bool InvIsNotFull(Blackboard* blackboardPtr) { + Thinker* thinkerPtr{}; + blackboardPtr->GetData("Brain", thinkerPtr); + return thinkerPtr->IsInvNotFull(); + } + + bool EmptyValue(Blackboard* blackboardPtr) { + Thinker* thinkerPtr{}; + blackboardPtr->GetData("Brain", thinkerPtr); + + if (thinkerPtr->EmptyValue()) { + std::cout << "empty item\n"; + } + + return thinkerPtr->EmptyValue(); + } + + bool InvIsFull(Blackboard* blackboardPtr) { + return !InvIsNotFull(blackboardPtr); + } + + bool InsideTargetHouse(Blackboard* blackboardPtr) { + IExamInterface* interfacePtr{}; + Thinker* thinkerPtr{}; + HouseInfo targetHouse{}; + + blackboardPtr->GetData("Interface", interfacePtr); + blackboardPtr->GetData("Brain", thinkerPtr); + blackboardPtr->GetData("TargetHouse", targetHouse); + + if (interfacePtr->Agent_GetInfo().IsInHouse) { + return thinkerPtr->CheckIfTargetIsInside(targetHouse, interfacePtr->Agent_GetInfo().Position); + } + return false; + } + + bool SeeHouse(Blackboard* blackboardPtr) { + IExamInterface* interfacePtr{}; + blackboardPtr->GetData("Interface", interfacePtr); + + return interfacePtr->GetHousesInFOV().capacity() > 0; + } + + bool NewHouse(Blackboard* blackboardPtr) { + Thinker* thinkerPtr{}; + blackboardPtr->GetData("Brain", thinkerPtr); + + return thinkerPtr->NewHouseToExplore(); + } + + bool ReExploreHouse(Blackboard* blackboardPtr) { + Thinker* thinkerPtr{}; + blackboardPtr->GetData("Brain", thinkerPtr); + + return thinkerPtr->HouseToReExplore(); + } +} diff --git a/project/Behaviour.h b/project/Behaviour.h index 4c28968..5a40cfb 100644 --- a/project/Behaviour.h +++ b/project/Behaviour.h @@ -1,4 +1,5 @@ #pragma once +#include namespace BT { @@ -9,10 +10,52 @@ class Blackboard; namespace BT_Action { - BT::State FindAHouse(Blackboard* blackboardPtr); + BT::State SetTimer(Blackboard* blackboardPtr, const std::string& timerName, bool doOnce); + BT::State UnlockTimer(Blackboard* blackboardPtr, const std::string& timerName); + BT::State LockTimer(Blackboard* blackboardPtr, const std::string& timerName); + BT::State GoToTarget(Blackboard* blackboardPtr); + BT::State EnableSpin(Blackboard* blackboardPtr); + BT::State DisableSpin(Blackboard* blackboardPtr); + + BT::State FindClosestEdge(Blackboard* blackboardPtr, int degree); + + BT::State UseItem(Blackboard* blackboardPtr, eItemType type); + + BT::State SetItemAsTarget(Blackboard* blackboardPtr); + BT::State DestroyItemOnFloor(Blackboard* blackboardPtr); + BT::State PickUpItem(Blackboard* blackboardPtr); + BT::State SwapItem(Blackboard* blackboardPtr); + BT::State CheckItem(Blackboard* blackboardPtr); + + BT::State TryFindHouse(Blackboard* blackboardPtr, float searchRadius, int degree); + BT::State GetHouseAsTarget(Blackboard* blackboardPtr, float maxTravelDistance); + BT::State CheckHouses(Blackboard* blackboardPtr); + BT::State SetExpireDate(Blackboard* blackboardPtr); + BT::State GetInsideTarget(Blackboard* blackboardPtr, float offset); + } -namespace BT_Conditions +namespace BT_Condition { + bool CheckTimerLock(Blackboard* blackboardPtr, const std::string& timerName); + bool CheckTimer(Blackboard* blackboardPtr, const std::string& timerName, bool doOnce); + + bool SeePurgeZone(Blackboard* blackboardPtr); + + bool ItemInInv(Blackboard* blackboardPtr, eItemType type); + bool HpUnderThreshold(Blackboard* blackboardPtr, float threshold); + bool CheckMinNeededEnergy(Blackboard* blackboardPtr); + + bool SeeItem(Blackboard* blackboardPtr); + bool IsTypeOfItem(Blackboard* blackboardPtr, eItemType type); + bool InvIsNotFull(Blackboard* blackboardPtr); + bool EmptyValue(Blackboard* blackboardPtr); + bool InvIsFull(Blackboard* blackboardPtr); + + bool InsideTargetHouse(Blackboard* blackboardPtr); + bool SeeHouse(Blackboard* blackboardPtr); + bool NewHouse(Blackboard* blackboardPtr); + bool ReExploreHouse(Blackboard* blackboardPtr); } + diff --git a/project/BehaviourTree.cpp b/project/BehaviourTree.cpp index 36adf19..b7d5a93 100644 --- a/project/BehaviourTree.cpp +++ b/project/BehaviourTree.cpp @@ -116,7 +116,7 @@ State Action::Execute(Blackboard* blackboardPtr) { BehaviorTree::~BehaviorTree() { SAFE_DELETE(m_RootBehaviorPtr) - SAFE_DELETE(m_BlackboardPtr) //Takes ownership of passed blackboard! + SAFE_DELETE(m_BlackboardPtr) //Takes ownership of passed blackboard! } void BehaviorTree::Update() { diff --git a/project/BehaviourTree.h b/project/BehaviourTree.h index a6c9e48..223b642 100644 --- a/project/BehaviourTree.h +++ b/project/BehaviourTree.h @@ -1,6 +1,7 @@ #include #include "Blackboard.h" + namespace BT { enum class State @@ -87,7 +88,9 @@ namespace BT class Action final : public IBehavior { public: - explicit Action(std::function fp) : m_ActionPtr(std::move(fp)) {} + explicit Action(std::function fp) + : m_ActionPtr(std::move(fp)) { + } State Execute(Blackboard* blackboardPtr) override; private: diff --git a/project/BigThink.cpp b/project/BigThink.cpp new file mode 100644 index 0000000..b3092c9 --- /dev/null +++ b/project/BigThink.cpp @@ -0,0 +1,152 @@ +#include "stdafx.h" +#include "BigThink.h" + +#include "Behaviour.h" +#include "BehaviourTree.h" + +namespace BigThink +{ + constexpr float HpThreshold{ 8.f }; // TODO: Make this a parameter + + BT::Sequence* PurgeZoneHandling() { + constexpr int searchDegree{ 45 }; + return + new BT::Sequence({ + new BT::Conditional(BT_Condition::SeePurgeZone), + new BT::Action(std::bind(BT_Action::FindClosestEdge, std::placeholders::_1, searchDegree)), + new BT::Action(BT_Action::GoToTarget) + }); + } + + BT::Selector* ItemHandling() { + return + new BT::Selector({ + new BT::Sequence({ + new BT::Conditional(std::bind(BT_Condition::ItemInInv, std::placeholders::_1, eItemType::MEDKIT)), + new BT::Conditional(std::bind(BT_Condition::HpUnderThreshold, std::placeholders::_1, HpThreshold)), + new BT::Action(std::bind(BT_Action::UseItem, std::placeholders::_1, eItemType::MEDKIT)) + }), + new BT::Sequence({ + new BT::Conditional(std::bind(BT_Condition::ItemInInv, std::placeholders::_1, eItemType::FOOD)), + new BT::Conditional(BT_Condition::CheckMinNeededEnergy), + new BT::Action(std::bind(BT_Action::UseItem, std::placeholders::_1, eItemType::FOOD)) + }) + }); + } + + BT::PartialSequence* PickUpHandling() { + return + new BT::PartialSequence({ + new BT::Conditional(BT_Condition::SeeItem), + new BT::Action(BT_Action::DisableSpin), + new BT::Action(BT_Action::SetItemAsTarget), + new BT::Action(BT_Action::GoToTarget), + new BT::Selector({ + new BT::Sequence({ + new BT::Conditional(std::bind(BT_Condition::IsTypeOfItem, std::placeholders::_1, eItemType::GARBAGE)), + new BT::Action(BT_Action::DestroyItemOnFloor) + }), + new BT::Sequence({ + new BT::Conditional(BT_Condition::EmptyValue), + new BT::Action(BT_Action::SwapItem) + }), + new BT::Sequence({ + new BT::Conditional(BT_Condition::InvIsNotFull), + new BT::Action(BT_Action::PickUpItem) + }), + new BT::Sequence({ + new BT::Conditional(BT_Condition::InvIsFull), + new BT::Selector({ + new BT::Sequence({ + new BT::Conditional(std::bind(BT_Condition::IsTypeOfItem, std::placeholders::_1, eItemType::FOOD)), + new BT::Action(BT_Action::CheckItem) + }), + new BT::Sequence({ + new BT::Conditional(std::bind(BT_Condition::IsTypeOfItem, std::placeholders::_1, eItemType::MEDKIT)), + new BT::Action(BT_Action::CheckItem) + }), + new BT::Sequence({ + new BT::Conditional(std::bind(BT_Condition::IsTypeOfItem, std::placeholders::_1, eItemType::SHOTGUN)), + new BT::Action(BT_Action::CheckItem) + }), + new BT::Sequence({ + new BT::Conditional(std::bind(BT_Condition::IsTypeOfItem, std::placeholders::_1, eItemType::PISTOL)), + new BT::Action(BT_Action::CheckItem) + }) + }) + }) + }) + }); + } + + BT::Selector* HouseHandling() { + constexpr float maxTravelDistance{ 100.f }; + constexpr int searchRadius{ 300 }; + constexpr int searchDegree{ 45 }; //TODO + constexpr float InsideOffset{ 5.f }; + + const std::string BeforeLeavingTimer{ "BeforeLeaving" }; + constexpr bool BeforeLeavingDoOnce{ true }; + + return + new BT::Selector({ + new BT::PartialSequence({ + new BT::Conditional(BT_Condition::InsideTargetHouse), + new BT::Action(BT_Action::SetExpireDate), + new BT::Action(std::bind(BT_Action::SetTimer, std::placeholders::_1, BeforeLeavingTimer, BeforeLeavingDoOnce)), + new BT::Selector({ + new BT::PartialSequence({ + new BT::Conditional(std::bind(BT_Condition::CheckTimer, std::placeholders::_1, BeforeLeavingTimer, BeforeLeavingDoOnce)), + new BT::Selector({ + new BT::PartialSequence({ + new BT::Conditional(BT_Condition::NewHouse), + new BT::Action(std::bind(BT_Action::GetHouseAsTarget, std::placeholders::_1, maxTravelDistance)), + new BT::Action(BT_Action::EnableSpin), + new BT::Action(BT_Action::GoToTarget), + }), + new BT::PartialSequence({ + new BT::Conditional(BT_Condition::ReExploreHouse), + new BT::Action(std::bind(BT_Action::GetHouseAsTarget, std::placeholders::_1, maxTravelDistance)), + new BT::Action(BT_Action::EnableSpin), + new BT::Action(BT_Action::GoToTarget), + }), + new BT::PartialSequence({ + new BT::Action(std::bind(BT_Action::TryFindHouse, std::placeholders::_1, searchRadius, searchDegree)), + new BT::Action(BT_Action::EnableSpin), + new BT::Action(BT_Action::GoToTarget) + }) + }), + }), + new BT::PartialSequence({ + new BT::Action(std::bind(BT_Action::GetInsideTarget, std::placeholders::_1, InsideOffset)), + new BT::Action(BT_Action::EnableSpin), + new BT::Action(BT_Action::GoToTarget) + }), + }) + }), + new BT::Sequence({ + new BT::Conditional(BT_Condition::SeeHouse), + new BT::Action(BT_Action::CheckHouses) + }), + new BT::Selector({ + new BT::PartialSequence({ + new BT::Conditional(BT_Condition::NewHouse), + new BT::Action(std::bind(BT_Action::GetHouseAsTarget, std::placeholders::_1, maxTravelDistance)), + new BT::Action(BT_Action::EnableSpin), + new BT::Action(BT_Action::GoToTarget), + }), + new BT::PartialSequence({ + new BT::Conditional(BT_Condition::ReExploreHouse), + new BT::Action(std::bind(BT_Action::GetHouseAsTarget, std::placeholders::_1, maxTravelDistance)), + new BT::Action(BT_Action::EnableSpin), + new BT::Action(BT_Action::GoToTarget), + }) + }), + new BT::PartialSequence({ + new BT::Action(std::bind(BT_Action::TryFindHouse, std::placeholders::_1, searchRadius, searchDegree)), + new BT::Action(BT_Action::EnableSpin), + new BT::Action(BT_Action::GoToTarget) + }) + }); + } +} diff --git a/project/BigThink.h b/project/BigThink.h new file mode 100644 index 0000000..cab09db --- /dev/null +++ b/project/BigThink.h @@ -0,0 +1,17 @@ +#pragma once +#include "stdafx.h" + +namespace BT +{ + class PartialSequence; + class Sequence; + class Selector; +} + +namespace BigThink +{ + BT::Sequence* PurgeZoneHandling(); + BT::Selector* ItemHandling(); + BT::PartialSequence* PickUpHandling(); + BT::Selector* HouseHandling(); +} diff --git a/project/CMakeLists.txt b/project/CMakeLists.txt index 301d8ce..b74ff2b 100644 --- a/project/CMakeLists.txt +++ b/project/CMakeLists.txt @@ -3,7 +3,17 @@ # ADD NEW .cpp FILES HERE add_library(Exam_Plugin SHARED "stdafx.cpp" - "SurvivalAgentPlugin.cpp" "BehaviourTree.cpp" "BehaviourTree.h" "BlackBoard.h" "Thinker.h" "Thinker.cpp" "Behaviour.h" "Behaviour.cpp") + "SurvivalAgentPlugin.cpp" + "BehaviourTree.cpp" + "BehaviourTree.h" + "BlackBoard.h" + "Thinker.h" + "Thinker.cpp" + "Behaviour.h" + "Behaviour.cpp" + "BigThink.h" + "BigThink.cpp" +) target_link_libraries(Exam_Plugin PUBLIC ${EXAM_LIB_DEBUG}) target_include_directories(Exam_Plugin PUBLIC ${EXAM_INCLUDE_DIR}) diff --git a/project/SurvivalAgentPlugin.cpp b/project/SurvivalAgentPlugin.cpp index 90817f0..8383bd5 100644 --- a/project/SurvivalAgentPlugin.cpp +++ b/project/SurvivalAgentPlugin.cpp @@ -1,9 +1,10 @@ #include "stdafx.h" #include "SurvivalAgentPlugin.h" +#include "BehaviourTree.h" #include "IExamInterface.h" #include "Blackboard.h" -#include "BehaviourTree.h" -#include "Behaviour.h" +#include "Thinker.h" +#include "BigThink.h" //Called only once, during initialization void SurvivalAgentPlugin::Initialize(IBaseInterface* pInterface, PluginInfo& info) @@ -18,34 +19,61 @@ void SurvivalAgentPlugin::Initialize(IBaseInterface* pInterface, PluginInfo& inf info.Student_Class = "2DAE11"; info.LB_Password = "ILikeCuteCats!";//Don't use a real password! This is only to prevent other students from overwriting your highscore! - Blackboard* blackboard = CreateBlackboard(); - m_BehaviourTree = new BT::BehaviorTree(blackboard, + m_Thinker = new Thinker(); + + constexpr float closeRadius{ 50.f }; + const float farRadius{ m_pInterface->World_GetInfo().Dimensions.x }; + + const std::string searchTimer{ "Search" }; + + Blackboard* blackboardPtr{ CreateBlackboard() }; + m_BehaviourTree = new BT::BehaviorTree(blackboardPtr, new BT::Selector({ - new BT::PartialSequence({ - new BT::Action(BT_Action::FindAHouse), - new BT::Action(BT_Action::GoToTarget) - }) - }) - ); + BigThink::PurgeZoneHandling(), + BigThink::ItemHandling(), + BigThink::PickUpHandling(), + BigThink::HouseHandling() + })); + + } Blackboard* SurvivalAgentPlugin::CreateBlackboard() { - Blackboard* blackboard = new Blackboard(); - blackboard->AddData("Interface", m_pInterface); - blackboard->AddData("Steering", SteeringPlugin_Output{}); - blackboard->AddData("Target", m_Target); - return blackboard; + + Blackboard* blackboardPtr = new Blackboard(); + + blackboardPtr->AddData("Brain", m_Thinker); + blackboardPtr->AddData("Interface", m_pInterface); + blackboardPtr->AddData("Steering", SteeringPlugin_Output{}); + blackboardPtr->AddData("Target", m_Target); + blackboardPtr->AddData("Spin", false); + blackboardPtr->AddData("FailSafe", std::chrono::steady_clock::time_point{}); + blackboardPtr->AddData("MaxFailSafe", 2.f); + blackboardPtr->AddData("FailSafeDoOnce", false); + + blackboardPtr->AddData("TargetItem", ItemInfo{}); + blackboardPtr->AddData("NextFreeSlot", 0); + + blackboardPtr->AddData("TargetHouse", HouseInfo{}); + blackboardPtr->AddData("TimerBeforeLeaving", std::chrono::steady_clock::time_point{}); + blackboardPtr->AddData("TimerBeforeLeavingDoOnce", false); + blackboardPtr->AddData("MaxTimeBeforeLeaving", 5.f); + + return blackboardPtr; + + } void SurvivalAgentPlugin::UpdateBlackboard(const SteeringPlugin_Output& steering) { Blackboard* blackboard{ m_BehaviourTree->GetBlackboard() }; - //blackboard->ChangeData("playerPos", m_pInterface->Agent_GetInfo().Position); blackboard->ChangeData("Steering", steering); + blackboard->GetData("Target", m_Target); } SurvivalAgentPlugin::~SurvivalAgentPlugin() { SAFE_DELETE(m_BehaviourTree); + SAFE_DELETE(m_Thinker); } //Called only once @@ -80,8 +108,6 @@ void SurvivalAgentPlugin::InitGameDebugParams(GameDebugParams& params) params.Seed = 0; //-1 = don't set seed. Any other number = fixed seed //TIP: use Seed = int(time(nullptr)) for pure randomness } -//Only Active in DEBUG Mode -//(=Use only for Debug Purposes) void SurvivalAgentPlugin::Update_Debug(float dt) { //Demo Event Code @@ -151,21 +177,32 @@ void SurvivalAgentPlugin::Update_Debug(float dt) SteeringPlugin_Output SurvivalAgentPlugin::UpdateSteering(float dt) { auto steering = SteeringPlugin_Output(); - - //Use the Interface (IAssignmentInterface) to 'interface' with the AI_Framework - auto agentInfo = m_pInterface->Agent_GetInfo(); + bool spin{}; UpdateBlackboard(steering); m_BehaviourTree->Update(); m_BehaviourTree->GetBlackboard()->GetData("Steering", steering); + m_BehaviourTree->GetBlackboard()->GetData("Spin", spin); - steering.AngularVelocity = m_AngSpeed; - steering.AutoOrient = false; + if (spin) { + steering.AutoOrient = false; + steering.AngularVelocity = 3.14f; + } + if (m_GrabItem) { + ItemInfo item; + + m_BehaviourTree->GetBlackboard()->GetData("TargetItem", item); + + if (m_pInterface->GrabItem(item)) { + m_pInterface->Inventory_AddItem(m_InventorySlot, item); + } + } return steering; + } //This function should only be used for rendering debug elements diff --git a/project/SurvivalAgentPlugin.h b/project/SurvivalAgentPlugin.h index 124de63..9752c23 100644 --- a/project/SurvivalAgentPlugin.h +++ b/project/SurvivalAgentPlugin.h @@ -2,15 +2,18 @@ #include "IExamPlugin.h" #include "Exam_HelperStructs.h" -namespace BT{ +namespace BT +{ class BehaviorTree; } class IBaseInterface; class IExamInterface; class Blackboard; +class Thinker; -class SurvivalAgentPlugin final :public IExamPlugin + +class SurvivalAgentPlugin final : public IExamPlugin { public: SurvivalAgentPlugin() = default; @@ -43,6 +46,7 @@ private: Blackboard* CreateBlackboard(); void UpdateBlackboard(const SteeringPlugin_Output& steering); BT::BehaviorTree* m_BehaviourTree = nullptr; + Thinker* m_Thinker = nullptr; }; //ENTRY diff --git a/project/Thinker.cpp b/project/Thinker.cpp index c0e98fc..a0279ea 100644 --- a/project/Thinker.cpp +++ b/project/Thinker.cpp @@ -2,20 +2,191 @@ #include "Thinker.h" #include -void Thinker::CheckIfNewHouse(const HouseInfo& newHouse) { - auto it = std::find_if(m_HousesMemory.begin(), m_HousesMemory.end(), - [&newHouse](const HouseMemory& house) { - return house.info.Center == newHouse.Center; +std::vector::iterator Thinker::FindLeastValueItem(const eItemType& itemType) { + std::ranges::partition(m_ItemMemory, + [itemType](const ItemMemory& memory)->bool { return memory.ItemInfo.Type == itemType; }); + + const auto minItem = + std::ranges::min_element(m_ItemMemory, + [itemType](const ItemMemory& lhs, const ItemMemory& rhs)->bool { + if (lhs.ItemInfo.Type == itemType && rhs.ItemInfo.Type == itemType) { + return lhs.ItemInfo.Value < rhs.ItemInfo.Value; + } + return false; }); - if (it != m_HousesMemory.end()) { - //it->lastSaw = std::chrono::steady_clock::now(); - //it->newHouse = false; - return; + return minItem; +} + +bool Thinker::IsInvNotFull() const { + return m_ItemMemory.capacity() < m_MaxStorageSlots; +} + +bool Thinker::IsItemInInv(const eItemType& itemType) { + return std::any_of(std::begin(m_ItemMemory), std::end(m_ItemMemory), + [itemType](const ItemMemory& memory)->bool { return memory.ItemInfo.Type == itemType; }); +} + +bool Thinker::EmptyValue() { + return std::any_of(std::begin(m_ItemMemory), std::end(m_ItemMemory), + [](const ItemMemory& memory)->bool { return memory.ItemInfo.Value <= 0; }); +} + +int Thinker::FindEmptyValue(const ItemInfo& item) { + const auto foundItem = std::find_if(std::begin(m_ItemMemory), std::end(m_ItemMemory), + [item](const ItemMemory& memory)->bool { return memory.ItemInfo.Type == item.Type; }); + if (foundItem != std::end(m_ItemMemory)) { + return foundItem->invIndex; + } + return -1; +} + +int Thinker::AddItemToMemory(const ItemInfo& item) { + if (m_ItemMemory.capacity() >= m_MaxStorageSlots) + return -1; + ItemMemory newItem{}; + newItem.ItemInfo = item; + newItem.invIndex = static_cast(m_ItemMemory.size()); + m_ItemMemory.push_back(newItem); + return newItem.invIndex; +} + +int Thinker::CheckItem(const ItemInfo& item) { + if (std::ranges::any_of(m_ItemMemory, + [item](const ItemMemory& memory)->bool { return memory.ItemInfo.Type == item.Type; })) { + const auto minItem{ FindLeastValueItem(item.Type) }; + + if (minItem->ItemInfo.Value <= item.Value) { + minItem->ItemInfo = item; + return minItem->invIndex; + } + else { + return m_MaxStorageSlots - 1; + } + } + else { + std::ranges::sort(m_ItemMemory, + [](const ItemMemory& lhs, const ItemMemory& rhs)->bool { return lhs.ItemInfo.Type < rhs.ItemInfo.Type; }); + + const auto duplicate = + std::ranges::adjacent_find(m_ItemMemory, + [](const ItemMemory& lhs, const ItemMemory& rhs)->bool { return lhs.ItemInfo.Type == rhs.ItemInfo.Type; }); + + const auto minItem = + std::min_element(duplicate, duplicate + 1, + [](const ItemMemory& lhs, const ItemMemory& rhs)->bool { return lhs.ItemInfo.Value < rhs.ItemInfo.Value; }); + + minItem->ItemInfo = item; + return minItem->invIndex; + } +} + +bool Thinker::CheckIfTargetIsInside(const HouseInfo& targetHouse, Elite::Vector2 playerPos) { + const float houseOffset{ 5.f }; + const Elite::Vector2 houseCenter{ targetHouse.Center }; + return (playerPos.x >= houseCenter.x - houseOffset && playerPos.x <= houseCenter.x + houseOffset && + playerPos.y >= houseCenter.y - houseOffset && playerPos.y <= houseCenter.y + houseOffset); +} + +bool Thinker::CheckIfTargetIsExplored(Elite::Vector2 target, float offset) const { + return std::any_of(std::begin(m_HousesMemory), std::end(m_HousesMemory), + [target, offset](const HouseMemory& memory)->bool { + return memory.info.Center.x >= target.x - offset && memory.info.Center.x <= target.x + offset && + memory.info.Center.y >= target.y - offset && memory.info.Center.y <= target.y + offset; + }); +} + + +bool Thinker::NewHouseToExplore() { + if (m_HousesMemory.capacity() != 0) { + if (std::ranges::any_of(m_HousesMemory, + [](const HouseMemory& houseMemory)->bool { return houseMemory.newHouse == true; })) { + return true; + } + } + return false; +} + +bool Thinker::HouseToReExplore() { + if (m_HousesMemory.capacity() != 0) { + const std::chrono::steady_clock::time_point currentTime{ std::chrono::steady_clock::now() }; + + if (std::ranges::any_of(m_HousesMemory, + [=](const HouseMemory& houseMemory)->bool { + const std::chrono::duration elapsedSec{ currentTime - houseMemory.lastSaw }; + return elapsedSec.count() >= m_MaxWaitTimer; + })) { + return true; + } + } + return false; +} + +void Thinker::SetTargetHouseExpireDate(const HouseInfo& targetHouse) { + assert(m_HousesMemory.capacity() != 0); + + const auto foundHouse = FindHouseInMemory(targetHouse); + + foundHouse->lastSaw = std::chrono::steady_clock::now(); + foundHouse->newHouse = false; +} + +HouseInfo Thinker::CheckHouseValidTarget(Elite::Vector2 playerPos, float maxRadius) const { + HouseInfo targetHouse{}; + float closestHouse{ FLT_MAX }; + + for (auto house : m_HousesMemory) { + const float houseDistance{ house.info.Center.DistanceSquared(playerPos) }; + + if (houseDistance > maxRadius * maxRadius) + continue; + + if (closestHouse < houseDistance) + continue; + + if (house.newHouse == true) { + targetHouse = house.info; + closestHouse = houseDistance; + } + else { + const std::chrono::steady_clock::time_point currentTime{ std::chrono::steady_clock::now() }; + const std::chrono::duration elapsedSec{ currentTime - house.lastSaw }; + + if (elapsedSec.count() < m_MaxWaitTimer) + continue; + + targetHouse = house.info; + closestHouse = houseDistance; + } } - HouseMemory houseToMemory{}; - houseToMemory.info = newHouse; - - m_HousesMemory.push_back(houseToMemory); + return targetHouse; +} + +bool Thinker::CheckHousesForMemory(const std::vector& FOVHouses) { + bool result{}; + + for (auto& newHouse : FOVHouses) { + if (m_HousesMemory.capacity() != 0) { + if (std::ranges::any_of(m_HousesMemory, + [newHouse](const HouseMemory& houseMemory)->bool { return houseMemory.info.Center == newHouse.Center; })) { + continue; + } + } + + HouseMemory houseToMemory{}; + houseToMemory.info = newHouse; + + m_HousesMemory.push_back(houseToMemory); + + result = true; + } + + return result; +} + +std::vector::iterator Thinker::FindHouseInMemory(const HouseInfo& targetHouse) { + return + std::ranges::find_if(m_HousesMemory, + [targetHouse](const HouseMemory& houseMemory)->bool { return houseMemory.info.Center == targetHouse.Center; }); } diff --git a/project/Thinker.h b/project/Thinker.h index 6a1e0f7..ef7af50 100644 --- a/project/Thinker.h +++ b/project/Thinker.h @@ -11,6 +11,13 @@ public: HouseInfo info; std::chrono::steady_clock::time_point lastSaw; }; + + struct ItemMemory + { + ItemInfo ItemInfo; + int invIndex; + }; + Thinker() = default; ~Thinker() = default; @@ -19,7 +26,35 @@ public: Thinker(Thinker&&) = default; Thinker& operator=(Thinker&&) = default; - void CheckIfNewHouse(const HouseInfo& newHouse); + /*bool NewHouseToExplore(); + bool HouseToReExplore(); + void SetTargetHouseExpireDate(const HouseInfo& targetHouse); + HouseInfo CheckHouseValidTarget(Elite::Vector2 playerPos, float maxRadius) const; + bool CheckHousesForMemory(const std::vector& FOVHouses);*/ + + std::vector::iterator FindLeastValueItem(const eItemType& itemType); + bool IsInvNotFull() const; + bool IsItemInInv(const eItemType& itemType); + bool EmptyValue(); + int FindEmptyValue(const ItemInfo& item); + int AddItemToMemory(const ItemInfo& item); + int CheckItem(const ItemInfo& item); + + bool CheckIfTargetIsInside(const HouseInfo& targetHouse, Elite::Vector2 playerPos); + bool CheckIfTargetIsExplored(Elite::Vector2 target, float offset) const; + bool NewHouseToExplore(); + bool HouseToReExplore(); + void SetTargetHouseExpireDate(const HouseInfo& targetHouse); + HouseInfo CheckHouseValidTarget(Elite::Vector2 playerPos, float maxRadius) const; + bool CheckHousesForMemory(const std::vector& FOVHouses); + + private: std::vector m_HousesMemory{}; + const float m_MaxWaitTimer{ 360.f }; + + std::vector m_ItemMemory{}; + const size_t m_MaxStorageSlots{ 5 }; + + std::vector::iterator FindHouseInMemory(const HouseInfo& targetHouse); };