浏览代码

NKAI: moddable configuration

Andrii Danylchenko 1 年之前
父节点
当前提交
35429eab52

+ 2 - 2
AI/Nullkiller/AIGateway.cpp

@@ -677,9 +677,9 @@ void AIGateway::showBlockingDialog(const std::string & text, const std::vector<C
 					&& components.size() == 2
 					&& components.front().type == ComponentType::RESOURCE
 					&& (nullkiller->heroManager->getHeroRole(hero) != HeroRole::MAIN
-						|| nullkiller->buildAnalyzer->getGoldPreasure() > MAX_GOLD_PEASURE))
+						|| nullkiller->buildAnalyzer->isGoldPreasureHigh()))
 				{
-					sel = 1; // for now lets pick gold from a chest.
+					sel = 1;
 				}
 		}
 

+ 1 - 1
AI/Nullkiller/AIUtility.cpp

@@ -437,7 +437,7 @@ bool shouldVisit(const Nullkiller * ai, const CGHeroInstance * h, const CGObject
 	case Obj::MAGIC_WELL:
 		return h->mana < h->manaLimit();
 	case Obj::PRISON:
-		return ai->cb->getHeroesInfo().size() < VLC->settings()->getInteger(EGameSettings::HEROES_PER_PLAYER_ON_MAP_CAP);
+		return !ai->heroManager->heroCapReached();
 	case Obj::TAVERN:
 	case Obj::EYE_OF_MAGI:
 	case Obj::BOAT:

+ 5 - 0
AI/Nullkiller/Analyzers/BuildAnalyzer.cpp

@@ -120,6 +120,11 @@ TResources BuildAnalyzer::getTotalResourcesRequired() const
 	return result;
 }
 
+bool BuildAnalyzer::isGoldPreasureHigh() const
+{
+	return goldPreasure > ai->settings->getMaxGoldPreasure();
+}
+
 void BuildAnalyzer::update()
 {
 	logAi->trace("Start analysing build");

+ 1 - 0
AI/Nullkiller/Analyzers/BuildAnalyzer.h

@@ -96,6 +96,7 @@ public:
 	const std::vector<TownDevelopmentInfo> & getDevelopmentInfo() const { return developmentInfos; }
 	TResources getDailyIncome() const { return dailyIncome; }
 	float getGoldPreasure() const { return goldPreasure; }
+	bool isGoldPreasureHigh() const;
 	bool hasAnyBuilding(int32_t alignment, BuildingID bid) const;
 
 private:

+ 1 - 0
AI/Nullkiller/Analyzers/HeroManager.cpp

@@ -187,6 +187,7 @@ bool HeroManager::heroCapReached() const
 	int heroCount = cb->getHeroCount(ai->playerID, includeGarnisoned);
 
 	return heroCount >= ALLOWED_ROAMING_HEROES
+		|| heroCount >= ai->settings->getMaxRoamingHeroes()
 		|| heroCount >= VLC->settings()->getInteger(EGameSettings::HEROES_PER_PLAYER_ON_MAP_CAP)
 		|| heroCount >= VLC->settings()->getInteger(EGameSettings::HEROES_PER_PLAYER_TOTAL_CAP);
 }

+ 2 - 2
AI/Nullkiller/Behaviors/BuildingBehavior.cpp

@@ -47,13 +47,13 @@ Goals::TGoalVec BuildingBehavior::decompose() const
 		totalDevelopmentCost.toString());
 
 	auto & developmentInfos = ai->nullkiller->buildAnalyzer->getDevelopmentInfo();
-	auto goldPreasure = ai->nullkiller->buildAnalyzer->getGoldPreasure();
+	auto isGoldPreasureLow = !ai->nullkiller->buildAnalyzer->isGoldPreasureHigh();
 
 	for(auto & developmentInfo : developmentInfos)
 	{
 		for(auto & buildingInfo : developmentInfo.toBuild)
 		{
-			if(goldPreasure < MAX_GOLD_PEASURE || buildingInfo.dailyIncome[EGameResID::GOLD] > 0)
+			if(isGoldPreasureLow || buildingInfo.dailyIncome[EGameResID::GOLD] > 0)
 			{
 				if(buildingInfo.notEnoughRes)
 				{

+ 1 - 2
AI/Nullkiller/Behaviors/BuyArmyBehavior.cpp

@@ -46,8 +46,7 @@ Goals::TGoalVec BuyArmyBehavior::decompose() const
 
 		for(const CGHeroInstance * targetHero : heroes)
 		{
-			if(ai->nullkiller->buildAnalyzer->getGoldPreasure() > MAX_GOLD_PEASURE
-				&& !town->hasBuilt(BuildingID::CITY_HALL))
+			if(ai->nullkiller->buildAnalyzer->isGoldPreasureHigh()	&& !town->hasBuilt(BuildingID::CITY_HALL))
 			{
 				continue;
 			}

+ 3 - 3
AI/Nullkiller/Behaviors/GatherArmyBehavior.cpp

@@ -246,7 +246,7 @@ Goals::TGoalVec GatherArmyBehavior::upgradeArmy(const CGTownInstance * upgrader)
 	{
 		auto heroRole = ai->nullkiller->heroManager->getHeroRole(path.targetHero);
 
-		if(heroRole == HeroRole::MAIN && path.turn() < SCOUT_TURN_DISTANCE_LIMIT)
+		if(heroRole == HeroRole::MAIN && path.turn() < ai->nullkiller->settings->getScoutHeroTurnDistanceLimit())
 			hasMainAround = true;
 	}
 
@@ -335,7 +335,7 @@ Goals::TGoalVec GatherArmyBehavior::upgradeArmy(const CGTownInstance * upgrader)
 			if(!upgrade.upgradeValue
 				&& armyToGetOrBuy.upgradeValue > 20000
 				&& ai->nullkiller->heroManager->canRecruitHero(town)
-				&& path.turn() < SCOUT_TURN_DISTANCE_LIMIT)
+				&& path.turn() < ai->nullkiller->settings->getScoutHeroTurnDistanceLimit())
 			{
 				for(auto hero : cb->getAvailableHeroes(town))
 				{
@@ -344,7 +344,7 @@ Goals::TGoalVec GatherArmyBehavior::upgradeArmy(const CGTownInstance * upgrader)
 
 					if(scoutReinforcement >= armyToGetOrBuy.upgradeValue
 						&& ai->nullkiller->getFreeGold() >20000
-						&& ai->nullkiller->buildAnalyzer->getGoldPreasure() < MAX_GOLD_PEASURE)
+						&& !ai->nullkiller->buildAnalyzer->isGoldPreasureHigh())
 					{
 						Composition recruitHero;
 

+ 1 - 2
AI/Nullkiller/Behaviors/RecruitHeroBehavior.cpp

@@ -85,8 +85,7 @@ Goals::TGoalVec RecruitHeroBehavior::decompose() const
 				continue;
 
 			if(cb->getHeroesInfo().size() < cb->getTownsInfo().size() + 1
-				|| (ai->nullkiller->getFreeResources()[EGameResID::GOLD] > 10000
-					&& ai->nullkiller->buildAnalyzer->getGoldPreasure() < MAX_GOLD_PEASURE))
+				|| (ai->nullkiller->getFreeResources()[EGameResID::GOLD] > 10000 && !ai->nullkiller->buildAnalyzer->isGoldPreasureHigh()))
 			{
 				tasks.push_back(Goals::sptr(Goals::RecruitHero(town).setpriority(3)));
 			}

+ 2 - 0
AI/Nullkiller/CMakeLists.txt

@@ -17,6 +17,7 @@ set(Nullkiller_SRCS
 		AIUtility.cpp
 		Analyzers/ArmyManager.cpp
 		Analyzers/HeroManager.cpp
+		Engine/Settings.cpp
 		Engine/FuzzyEngines.cpp
 		Engine/FuzzyHelper.cpp
 		Engine/AIMemory.cpp
@@ -80,6 +81,7 @@ set(Nullkiller_HEADERS
 		AIUtility.h
 		Analyzers/ArmyManager.h
 		Analyzers/HeroManager.h
+		Engine/Settings.h
 		Engine/FuzzyEngines.h
 		Engine/FuzzyHelper.h
 		Engine/AIMemory.h

+ 9 - 13
AI/Nullkiller/Engine/Nullkiller.cpp

@@ -27,15 +27,11 @@ namespace NKAI
 
 using namespace Goals;
 
-#if NKAI_TRACE_LEVEL >= 1
-#define MAXPASS 1000000
-#else
-#define MAXPASS 30
-#endif
-
 Nullkiller::Nullkiller()
+	:activeHero(nullptr), scanDepth(ScanDepth::MAIN_FULL), useHeroChain(true)
 {
-	memory.reset(new AIMemory());
+	memory = std::make_unique<AIMemory>();
+	settings = std::make_unique<Settings>();
 }
 
 void Nullkiller::init(std::shared_ptr<CCallback> cb, PlayerColor playerID)
@@ -166,12 +162,12 @@ void Nullkiller::updateAiState(int pass, bool fast)
 
 		if(scanDepth == ScanDepth::SMALL)
 		{
-			cfg.mainTurnDistanceLimit = MAIN_TURN_DISTANCE_LIMIT;
+			cfg.mainTurnDistanceLimit = ai->nullkiller->settings->getMainHeroTurnDistanceLimit();
 		}
 
 		if(scanDepth != ScanDepth::ALL_FULL)
 		{
-			cfg.scoutTurnDistanceLimit = SCOUT_TURN_DISTANCE_LIMIT;
+			cfg.scoutTurnDistanceLimit = ai->nullkiller->settings->getScoutHeroTurnDistanceLimit();
 		}
 
 		boost::this_thread::interruption_point();
@@ -235,13 +231,13 @@ void Nullkiller::makeTurn()
 
 	resetAiState();
 
-	for(int i = 1; i <= MAXPASS; i++)
+	for(int i = 1; i <= settings->getMaxPass(); i++)
 	{
 		updateAiState(i);
 
 		Goals::TTask bestTask = taskptr(Goals::Invalid());
 
-		for(;i <= MAXPASS; i++)
+		for(;i <= settings->getMaxPass(); i++)
 		{
 			Goals::TTaskVec fastTasks = {
 				choseBestTask(sptr(BuyArmyBehavior()), 1),
@@ -328,9 +324,9 @@ void Nullkiller::makeTurn()
 
 		executeTask(bestTask);
 
-		if(i == MAXPASS)
+		if(i == settings->getMaxPass())
 		{
-			logAi->error("Goal %s exceeded maxpass. Terminating AI turn.", taskDescription);
+			logAi->warn("Goal %s exceeded maxpass. Terminating AI turn.", taskDescription);
 		}
 	}
 }

+ 2 - 1
AI/Nullkiller/Engine/Nullkiller.h

@@ -11,6 +11,7 @@
 
 #include "PriorityEvaluator.h"
 #include "FuzzyHelper.h"
+#include "Settings.h"
 #include "AIMemory.h"
 #include "DeepDecomposer.h"
 #include "../Analyzers/DangerHitMapAnalyzer.h"
@@ -23,7 +24,6 @@
 namespace NKAI
 {
 
-const float MAX_GOLD_PEASURE = 0.3f;
 const float MIN_PRIORITY = 0.01f;
 const float SMALL_SCAN_MIN_PRIORITY = 0.4f;
 
@@ -71,6 +71,7 @@ public:
 	std::unique_ptr<FuzzyHelper> dangerEvaluator;
 	std::unique_ptr<DeepDecomposer> decomposer;
 	std::unique_ptr<ArmyFormation> armyFormation;
+	std::unique_ptr<Settings> settings;
 	PlayerColor playerID;
 	std::shared_ptr<CCallback> cb;
 	std::mutex aiStateMutex;

+ 1 - 1
AI/Nullkiller/Engine/PriorityEvaluator.cpp

@@ -69,7 +69,7 @@ PriorityEvaluator::~PriorityEvaluator()
 
 void PriorityEvaluator::initVisitTile()
 {
-	auto file = CResourceHandler::get()->load(ResourcePath("config/ai/object-priorities.txt"))->readAll();
+	auto file = CResourceHandler::get()->load(ResourcePath("config/ai/nkai/object-priorities.txt"))->readAll();
 	std::string str = std::string((char *)file.first.get(), file.second);
 	engine = fl::FllImporter().fromString(str);
 	armyLossPersentageVariable = engine->getInputVariable("armyLoss");

+ 78 - 0
AI/Nullkiller/Engine/Settings.cpp

@@ -0,0 +1,78 @@
+/*
+* Settings.cpp, part of VCMI engine
+*
+* Authors: listed in file AUTHORS in main folder
+*
+* License: GNU General Public License v2.0 or later
+* Full text of license available in license.txt file, in main folder
+*
+*/
+#include "StdInc.h"
+#include <limits>
+
+#include "Settings.h"
+#include "../../../lib/mapObjectConstructors/AObjectTypeHandler.h"
+#include "../../../lib/mapObjectConstructors/CObjectClassesHandler.h"
+#include "../../../lib/mapObjectConstructors/CBankInstanceConstructor.h"
+#include "../../../lib/mapObjects/MapObjects.h"
+#include "../../../lib/modding/CModHandler.h"
+#include "../../../lib/VCMI_Lib.h"
+#include "../../../lib/filesystem/Filesystem.h"
+#include "../../../lib/json/JsonNode.h"
+
+namespace NKAI
+{
+	Settings::Settings()
+		: maxRoamingHeroes(8),
+		mainHeroTurnDistanceLimit(10),
+		scoutHeroTurnDistanceLimit(5),
+		maxGoldPreasure(0.3f), 
+		maxpass(30)
+	{
+		ResourcePath resource("config/ai/nkai/nkai-settings", EResType::JSON);
+
+		loadFromMod("core", resource);
+
+		for(const auto & modName : VLC->modh->getActiveMods())
+		{
+			if(CResourceHandler::get(modName)->existsResource(resource))
+				loadFromMod(modName, resource);
+		}
+	}
+
+	void Settings::loadFromMod(const std::string & modName, const ResourcePath & resource)
+	{
+		if(!CResourceHandler::get(modName)->existsResource(resource))
+		{
+			logGlobal->error("Failed to load font %s from mod %s", resource.getName(), modName);
+			return;
+		}
+
+	    JsonNode node(JsonPath::fromResource(resource), modName);
+		
+		if(node.Struct()["maxRoamingHeroes"].isNumber())
+		{
+			maxRoamingHeroes = node.Struct()["maxRoamingHeroes"].Integer();
+		}
+
+		if(node.Struct()["mainHeroTurnDistanceLimit"].isNumber())
+		{
+			mainHeroTurnDistanceLimit = node.Struct()["mainHeroTurnDistanceLimit"].Integer();
+		}
+
+		if(node.Struct()["scoutHeroTurnDistanceLimit"].isNumber())
+		{
+			scoutHeroTurnDistanceLimit = node.Struct()["scoutHeroTurnDistanceLimit"].Integer();
+		}
+
+		if(node.Struct()["maxpass"].isNumber())
+		{
+			maxpass = node.Struct()["maxpass"].Integer();
+		}
+
+		if(node.Struct()["maxGoldPreasure"].isNumber())
+		{
+			maxGoldPreasure = node.Struct()["maxGoldPreasure"].Float();
+		}
+	}
+}

+ 42 - 0
AI/Nullkiller/Engine/Settings.h

@@ -0,0 +1,42 @@
+/*
+* Settings.h, part of VCMI engine
+*
+* Authors: listed in file AUTHORS in main folder
+*
+* License: GNU General Public License v2.0 or later
+* Full text of license available in license.txt file, in main folder
+*
+*/
+#pragma once
+
+VCMI_LIB_NAMESPACE_BEGIN
+
+class JsonNode;
+class ResourcePath;
+
+VCMI_LIB_NAMESPACE_END
+
+namespace NKAI
+{
+	class Settings
+	{
+	private:
+		int maxRoamingHeroes;
+		int mainHeroTurnDistanceLimit;
+		int scoutHeroTurnDistanceLimit;
+		int maxpass;
+		float maxGoldPreasure;
+
+	public:
+		Settings();
+
+		int getMaxPass() const { return maxpass; }
+		float getMaxGoldPreasure() const { return maxGoldPreasure; }
+		int getMaxRoamingHeroes() const { return maxRoamingHeroes; }
+		int getMainHeroTurnDistanceLimit() const { return mainHeroTurnDistanceLimit; }
+		int getScoutHeroTurnDistanceLimit() const { return scoutHeroTurnDistanceLimit; }
+
+	private:
+		void loadFromMod(const std::string & modName, const ResourcePath & resource);
+	};
+}

+ 0 - 3
AI/Nullkiller/Pathfinding/AINodeStorage.h

@@ -24,9 +24,6 @@
 
 namespace NKAI
 {
-	const int SCOUT_TURN_DISTANCE_LIMIT = 5;
-	const int MAIN_TURN_DISTANCE_LIMIT = 10;
-
 namespace AIPathfinding
 {
 #ifdef ENVIRONMENT64

+ 7 - 0
config/ai/nkai/nkai-settings.json

@@ -0,0 +1,7 @@
+{
+	"maxRoamingHeroes" : 8,
+	"maxpass" : 30,
+	"mainHeroTurnDistanceLimit" : 10,
+	"scoutHeroTurnDistanceLimit" : 5,
+	"maxGoldPreasure" : 0.3
+}

+ 0 - 0
config/ai/object-priorities.txt → config/ai/nkai/object-priorities.txt