mirror of
				https://github.com/vcmi/vcmi.git
				synced 2025-10-31 00:07:39 +02:00 
			
		
		
		
	Merge pull request #4989 from IvanSavenko/ai_fix
Fixes for unfinished items from AI pull request review
This commit is contained in:
		| @@ -731,55 +731,6 @@ bool BattleEvaluator::attemptCastingSpell(const CStack * activeStack) | ||||
|  | ||||
| 					ps.value = scoreEvaluator.evaluateExchange(updatedAttack, cachedAttack.turn, *targets, innerCache, state); | ||||
| 				} | ||||
| 				//! Some units may be dead alltogether. So if they existed before but not now, we know they were killed by the spell | ||||
| 				for (const auto& unit : all) | ||||
| 				{ | ||||
| 					if (!unit->isValidTarget()) | ||||
| 						continue; | ||||
| 					bool isDead = true; | ||||
| 					for (const auto& remainingUnit : allUnits) | ||||
| 					{ | ||||
| 						if (remainingUnit->unitId() == unit->unitId()) | ||||
| 							isDead = false; | ||||
| 					} | ||||
| 					if (isDead) | ||||
| 					{ | ||||
| 						auto newHealth = 0; | ||||
| 						auto oldHealth = vstd::find_or(healthOfStack, unit->unitId(), 0); | ||||
| 						if (oldHealth != newHealth) | ||||
| 						{ | ||||
| 							auto damage = std::abs(oldHealth - newHealth); | ||||
| 							auto originalDefender = cb->getBattle(battleID)->battleGetUnitByID(unit->unitId()); | ||||
| 							auto dpsReduce = AttackPossibility::calculateDamageReduce( | ||||
| 								nullptr, | ||||
| 								originalDefender && originalDefender->alive() ? originalDefender : unit, | ||||
| 								damage, | ||||
| 								innerCache, | ||||
| 								state); | ||||
| 							auto ourUnit = unit->unitSide() == side ? 1 : -1; | ||||
| 							auto goodEffect = newHealth > oldHealth ? 1 : -1; | ||||
| 							if (ourUnit * goodEffect == 1) | ||||
| 							{ | ||||
| 								if (ourUnit && goodEffect && (unit->isClone() || unit->isGhost())) | ||||
| 									continue; | ||||
| 								ps.value += dpsReduce * scoreEvaluator.getPositiveEffectMultiplier(); | ||||
| 							} | ||||
| 							else | ||||
| 								ps.value -= dpsReduce * scoreEvaluator.getNegativeEffectMultiplier(); | ||||
| #if BATTLE_TRACE_LEVEL >= 1 | ||||
| 							logAi->trace( | ||||
| 								"Spell %s to %d affects %s (%d), dps: %2f oldHealth: %d newHealth: %d", | ||||
| 								ps.spell->getNameTranslated(), | ||||
| 								ps.dest.at(0).hexValue.hex, | ||||
| 								unit->creatureId().toCreature()->getNameSingularTranslated(), | ||||
| 								unit->getCount(), | ||||
| 								dpsReduce, | ||||
| 								oldHealth, | ||||
| 								newHealth); | ||||
| #endif | ||||
| 						} | ||||
| 					} | ||||
| 				} | ||||
| 				for(const auto & unit : allUnits) | ||||
| 				{ | ||||
| 					if(!unit->isValidTarget(true)) | ||||
|   | ||||
| @@ -28,6 +28,8 @@ namespace NKAI | ||||
| 		scoutHeroTurnDistanceLimit(5), | ||||
| 		maxGoldPressure(0.3f),  | ||||
| 		maxpass(10), | ||||
| 		pathfinderBucketsCount(1), | ||||
| 		pathfinderBucketSize(32), | ||||
| 		allowObjectGraph(true), | ||||
| 		useTroopsFromGarrisons(false), | ||||
| 		openMap(true), | ||||
| @@ -35,49 +37,16 @@ namespace NKAI | ||||
| 	{ | ||||
| 		JsonNode node = JsonUtils::assembleFromFiles("config/ai/nkai/nkai-settings"); | ||||
|  | ||||
| 		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()["maxGoldPressure"].isNumber()) | ||||
| 		{ | ||||
| 			maxGoldPressure = node.Struct()["maxGoldPressure"].Float(); | ||||
| 		} | ||||
|  | ||||
| 		if(!node.Struct()["allowObjectGraph"].isNull()) | ||||
| 		{ | ||||
| 			allowObjectGraph = node.Struct()["allowObjectGraph"].Bool(); | ||||
| 		} | ||||
|  | ||||
| 		if(!node.Struct()["openMap"].isNull()) | ||||
| 		{ | ||||
| 			openMap = node.Struct()["openMap"].Bool(); | ||||
| 		} | ||||
|  | ||||
| 		if (!node.Struct()["useFuzzy"].isNull()) | ||||
| 		{ | ||||
| 			useFuzzy = node.Struct()["useFuzzy"].Bool(); | ||||
| 		} | ||||
|  | ||||
| 		if(!node.Struct()["useTroopsFromGarrisons"].isNull()) | ||||
| 		{ | ||||
| 			useTroopsFromGarrisons = node.Struct()["useTroopsFromGarrisons"].Bool(); | ||||
| 		} | ||||
| 		maxRoamingHeroes = node["maxRoamingHeroes"].Integer(); | ||||
| 		mainHeroTurnDistanceLimit = node["mainHeroTurnDistanceLimit"].Integer(); | ||||
| 		scoutHeroTurnDistanceLimit = node["scoutHeroTurnDistanceLimit"].Integer(); | ||||
| 		maxpass = node["maxpass"].Integer(); | ||||
| 		pathfinderBucketsCount = node["pathfinderBucketsCount"].Integer(); | ||||
| 		pathfinderBucketSize = node["pathfinderBucketSize"].Integer(); | ||||
| 		maxGoldPressure = node["maxGoldPressure"].Float(); | ||||
| 		allowObjectGraph = node["allowObjectGraph"].Bool(); | ||||
| 		openMap = node["openMap"].Bool(); | ||||
| 		useFuzzy = node["useFuzzy"].Bool(); | ||||
| 		useTroopsFromGarrisons = node["useTroopsFromGarrisons"].Bool(); | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -25,6 +25,8 @@ namespace NKAI | ||||
| 		int mainHeroTurnDistanceLimit; | ||||
| 		int scoutHeroTurnDistanceLimit; | ||||
| 		int maxpass; | ||||
| 		int pathfinderBucketsCount; | ||||
| 		int pathfinderBucketSize; | ||||
| 		float maxGoldPressure; | ||||
| 		bool allowObjectGraph; | ||||
| 		bool useTroopsFromGarrisons; | ||||
| @@ -39,6 +41,8 @@ namespace NKAI | ||||
| 		int getMaxRoamingHeroes() const { return maxRoamingHeroes; } | ||||
| 		int getMainHeroTurnDistanceLimit() const { return mainHeroTurnDistanceLimit; } | ||||
| 		int getScoutHeroTurnDistanceLimit() const { return scoutHeroTurnDistanceLimit; } | ||||
| 		int getPathfinderBucketsCount() const { return pathfinderBucketsCount; } | ||||
| 		int getPathfinderBucketSize() const { return pathfinderBucketSize; } | ||||
| 		bool isObjectGraphAllowed() const { return allowObjectGraph; } | ||||
| 		bool isGarrisonTroopsUsageAllowed() const { return useTroopsFromGarrisons; } | ||||
| 		bool isOpenMap() const { return openMap; } | ||||
|   | ||||
| @@ -39,17 +39,17 @@ const uint64_t CHAIN_MAX_DEPTH = 4; | ||||
|  | ||||
| const bool DO_NOT_SAVE_TO_COMMITTED_TILES = false; | ||||
|  | ||||
| AISharedStorage::AISharedStorage(int3 sizes) | ||||
| AISharedStorage::AISharedStorage(int3 sizes, int numChains) | ||||
| { | ||||
| 	if(!shared){ | ||||
| 		shared.reset(new boost::multi_array<AIPathNode, 4>( | ||||
| 			boost::extents[sizes.z][sizes.x][sizes.y][AIPathfinding::NUM_CHAINS])); | ||||
| 			boost::extents[sizes.z][sizes.x][sizes.y][numChains])); | ||||
|  | ||||
| 		nodes = shared; | ||||
|  | ||||
| 		foreach_tile_pos([&](const int3 & pos) | ||||
| 			{ | ||||
| 				for(auto i = 0; i < AIPathfinding::NUM_CHAINS; i++) | ||||
| 				for(auto i = 0; i < numChains; i++) | ||||
| 				{ | ||||
| 					auto & node = get(pos)[i]; | ||||
| 						 | ||||
| @@ -92,8 +92,18 @@ void AIPathNode::addSpecialAction(std::shared_ptr<const SpecialAction> action) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| int AINodeStorage::getBucketCount() const | ||||
| { | ||||
| 	return ai->settings->getPathfinderBucketsCount(); | ||||
| } | ||||
|  | ||||
| int AINodeStorage::getBucketSize() const | ||||
| { | ||||
| 	return ai->settings->getPathfinderBucketSize(); | ||||
| } | ||||
|  | ||||
| AINodeStorage::AINodeStorage(const Nullkiller * ai, const int3 & Sizes) | ||||
| 	: sizes(Sizes), ai(ai), cb(ai->cb.get()), nodes(Sizes) | ||||
| 	: sizes(Sizes), ai(ai), cb(ai->cb.get()), nodes(Sizes, ai->settings->getPathfinderBucketSize() * ai->settings->getPathfinderBucketsCount()) | ||||
| { | ||||
| 	accessibility = std::make_unique<boost::multi_array<EPathAccessibility, 4>>( | ||||
| 		boost::extents[sizes.z][sizes.x][sizes.y][EPathfindingLayer::NUM_LAYERS]); | ||||
| @@ -169,8 +179,8 @@ std::optional<AIPathNode *> AINodeStorage::getOrCreateNode( | ||||
| 	const EPathfindingLayer layer,  | ||||
| 	const ChainActor * actor) | ||||
| { | ||||
| 	int bucketIndex = ((uintptr_t)actor + static_cast<uint32_t>(layer)) % AIPathfinding::BUCKET_COUNT; | ||||
| 	int bucketOffset = bucketIndex * AIPathfinding::BUCKET_SIZE; | ||||
| 	int bucketIndex = ((uintptr_t)actor + static_cast<uint32_t>(layer)) % ai->settings->getPathfinderBucketsCount(); | ||||
| 	int bucketOffset = bucketIndex * ai->settings->getPathfinderBucketSize(); | ||||
| 	auto chains = nodes.get(pos); | ||||
|  | ||||
| 	if(blocked(pos, layer)) | ||||
| @@ -178,7 +188,7 @@ std::optional<AIPathNode *> AINodeStorage::getOrCreateNode( | ||||
| 		return std::nullopt; | ||||
| 	} | ||||
|  | ||||
| 	for(auto i = AIPathfinding::BUCKET_SIZE - 1; i >= 0; i--) | ||||
| 	for(auto i = ai->settings->getPathfinderBucketSize() - 1; i >= 0; i--) | ||||
| 	{ | ||||
| 		AIPathNode & node = chains[i + bucketOffset]; | ||||
|  | ||||
| @@ -486,8 +496,8 @@ public: | ||||
| 		AINodeStorage & storage, const std::vector<int3> & tiles, uint64_t chainMask, int heroChainTurn) | ||||
| 		:existingChains(), newChains(), delayedWork(), storage(storage), chainMask(chainMask), heroChainTurn(heroChainTurn), heroChain(), tiles(tiles) | ||||
| 	{ | ||||
| 		existingChains.reserve(AIPathfinding::NUM_CHAINS); | ||||
| 		newChains.reserve(AIPathfinding::NUM_CHAINS); | ||||
| 		existingChains.reserve(storage.getBucketCount() * storage.getBucketSize()); | ||||
| 		newChains.reserve(storage.getBucketCount() * storage.getBucketSize()); | ||||
| 	} | ||||
|  | ||||
| 	void execute(const tbb::blocked_range<size_t>& r) | ||||
|   | ||||
| @@ -29,9 +29,6 @@ namespace NKAI | ||||
| { | ||||
| namespace AIPathfinding | ||||
| { | ||||
| 	const int BUCKET_COUNT = 1; | ||||
| 	const int BUCKET_SIZE = 32; | ||||
| 	const int NUM_CHAINS = BUCKET_COUNT * BUCKET_SIZE; | ||||
| 	const int CHAIN_MAX_DEPTH = 4; | ||||
| } | ||||
|  | ||||
| @@ -157,7 +154,7 @@ public: | ||||
| 	static boost::mutex locker; | ||||
| 	static uint32_t version; | ||||
|  | ||||
| 	AISharedStorage(int3 mapSize); | ||||
| 	AISharedStorage(int3 sizes, int numChains); | ||||
| 	~AISharedStorage(); | ||||
|  | ||||
| 	STRONG_INLINE | ||||
| @@ -197,6 +194,9 @@ public: | ||||
| 	bool selectFirstActor(); | ||||
| 	bool selectNextActor(); | ||||
|  | ||||
| 	int getBucketCount() const; | ||||
| 	int getBucketSize() const; | ||||
|  | ||||
| 	std::vector<CGPathNode *> getInitialNodes() override; | ||||
|  | ||||
| 	virtual void calculateNeighbours( | ||||
| @@ -298,7 +298,7 @@ public: | ||||
|  | ||||
| 	inline int getBucket(const ChainActor * actor) const | ||||
| 	{ | ||||
| 		return ((uintptr_t)actor * 395) % AIPathfinding::BUCKET_COUNT; | ||||
| 		return ((uintptr_t)actor * 395) % getBucketCount(); | ||||
| 	} | ||||
|  | ||||
| 	void calculateTownPortalTeleportations(std::vector<CGPathNode *> & neighbours); | ||||
|   | ||||
| @@ -1112,6 +1112,6 @@ void SelectionTab::ListItem::updateItem(std::shared_ptr<ElementInfo> info, bool | ||||
| 		labelName->alignment = ETextAlignment::CENTER; | ||||
| 		labelName->moveTo(Point(pos.x + LABEL_POS_X, labelName->pos.y)); | ||||
| 	} | ||||
| 	labelName->setText(info->getNameForList()); | ||||
| 	labelName->setText(info->name); | ||||
| 	labelName->setColor(color); | ||||
| } | ||||
|   | ||||
| @@ -6,5 +6,8 @@ | ||||
| 	"maxGoldPressure" : 0.3, | ||||
| 	"useTroopsFromGarrisons" : true, | ||||
| 	"openMap": true, | ||||
| 	"allowObjectGraph": false | ||||
| 	"allowObjectGraph": false, | ||||
| 	"pathfinderBucketsCount" : 1, // old value: 3, | ||||
| 	"pathfinderBucketSize" : 32, // old value: 7, | ||||
| 	"useFuzzy" : false | ||||
| } | ||||
| @@ -488,6 +488,20 @@ | ||||
| 			// if enabled flying will work like in original game, otherwise nerf similar to HotA flying is applied | ||||
| 			"originalFlyRules" : true | ||||
| 		}, | ||||
| 		 | ||||
| 		"resources" : { | ||||
| 			// H3 mechanics - AI receives bonus (or malus, on easy) to his resource income | ||||
| 			// AI will receive specified values as percentage of his weekly income | ||||
| 			// So, "gems" : 200 will give AI player 200% of his daily income of gems over week, or, in other words, | ||||
| 			// giving AI player 2 additional gems per week for every owned Gem Pond | ||||
| 			"weeklyBonusesAI" : { | ||||
| 				"pawn"  : { "gold" : -175 }, | ||||
| 				"knight": {}, | ||||
| 				"rook"  : {}, | ||||
| 				"queen" : { "wood" : 275 , "mercury" : 100, "ore" : 275, "sulfur" : 100, "crystal" : 100, "gems" : 100, "gold" : 175}, | ||||
| 				"king"  : { "wood" : 375 , "mercury" : 200, "ore" : 375, "sulfur" : 200, "crystal" : 200, "gems" : 200, "gold" : 350} | ||||
| 			} | ||||
| 		}, | ||||
|  | ||||
| 		"spells": | ||||
| 		{ | ||||
|   | ||||
| @@ -133,6 +133,14 @@ | ||||
| 				"originalFlyRules" :        { "type" : "boolean" } | ||||
| 			} | ||||
| 		}, | ||||
| 		"resources": { | ||||
| 			"type" : "object", | ||||
| 			"additionalProperties" : false, | ||||
| 			"properties" : { | ||||
| 				"weeklyBonusesAI" : { "type" : "object" } | ||||
| 			} | ||||
| 		}, | ||||
|  | ||||
| 		"spells": { | ||||
| 			"type" : "object", | ||||
| 			"additionalProperties" : false, | ||||
|   | ||||
| @@ -89,6 +89,7 @@ const std::vector<GameSettings::SettingOption> GameSettings::settingProperties = | ||||
| 		{EGameSettings::PATHFINDER_USE_MONOLITH_ONE_WAY_UNIQUE,           "pathfinder", "useMonolithOneWayUnique"             }, | ||||
| 		{EGameSettings::PATHFINDER_USE_MONOLITH_TWO_WAY,                  "pathfinder", "useMonolithTwoWay"                   }, | ||||
| 		{EGameSettings::PATHFINDER_USE_WHIRLPOOL,                         "pathfinder", "useWhirlpool"                        }, | ||||
| 		{EGameSettings::RESOURCES_WEEKLY_BONUSES_AI,                      "resources", "weeklyBonusesAI"                      }, | ||||
| 		{EGameSettings::TEXTS_ARTIFACT,                                   "textData",  "artifact"                             }, | ||||
| 		{EGameSettings::TEXTS_CREATURE,                                   "textData",  "creature"                             }, | ||||
| 		{EGameSettings::TEXTS_FACTION,                                    "textData",  "faction"                              }, | ||||
|   | ||||
| @@ -67,6 +67,7 @@ enum class EGameSettings | ||||
| 	PATHFINDER_USE_MONOLITH_ONE_WAY_UNIQUE, | ||||
| 	PATHFINDER_USE_MONOLITH_TWO_WAY, | ||||
| 	PATHFINDER_USE_WHIRLPOOL, | ||||
| 	RESOURCES_WEEKLY_BONUSES_AI, | ||||
| 	TEXTS_ARTIFACT, | ||||
| 	TEXTS_CREATURE, | ||||
| 	TEXTS_FACTION, | ||||
|   | ||||
| @@ -18,6 +18,7 @@ | ||||
| #include "../../lib/IGameSettings.h" | ||||
| #include "../../lib/StartInfo.h" | ||||
| #include "../../lib/TerrainHandler.h" | ||||
| #include "../../lib/constants/StringConstants.h" | ||||
| #include "../../lib/entities/building/CBuilding.h" | ||||
| #include "../../lib/entities/faction/CTownHandler.h" | ||||
| #include "../../lib/gameState/CGameState.h" | ||||
| @@ -240,46 +241,22 @@ ResourceSet NewTurnProcessor::generatePlayerIncome(PlayerColor playerID, bool ne | ||||
| 	if (!state.isHuman()) | ||||
| 	{ | ||||
| 		// Initialize bonuses for different resources | ||||
| 		std::array<int, GameResID::COUNT> weeklyBonuses = {}; | ||||
|  | ||||
| 		// Calculate weekly bonuses based on difficulty | ||||
| 		if (gameHandler->gameState()->getStartInfo()->difficulty == 0) | ||||
| 		{ | ||||
| 			weeklyBonuses[EGameResID::GOLD] = static_cast<int>(std::round(incomeHandicapped[EGameResID::GOLD] * (0.75 - 1) * 7)); | ||||
| 		} | ||||
| 		else if (gameHandler->gameState()->getStartInfo()->difficulty == 3) | ||||
| 		{ | ||||
| 			weeklyBonuses[EGameResID::GOLD] = static_cast<int>(std::round(incomeHandicapped[EGameResID::GOLD] * 0.25 * 7)); | ||||
| 			weeklyBonuses[EGameResID::WOOD] = static_cast<int>(std::round(incomeHandicapped[EGameResID::WOOD] * 0.39 * 7)); | ||||
| 			weeklyBonuses[EGameResID::ORE] = static_cast<int>(std::round(incomeHandicapped[EGameResID::ORE] * 0.39 * 7)); | ||||
| 			weeklyBonuses[EGameResID::MERCURY] = static_cast<int>(std::round(incomeHandicapped[EGameResID::MERCURY] * 0.14 * 7)); | ||||
| 			weeklyBonuses[EGameResID::CRYSTAL] = static_cast<int>(std::round(incomeHandicapped[EGameResID::CRYSTAL] * 0.14 * 7)); | ||||
| 			weeklyBonuses[EGameResID::SULFUR] = static_cast<int>(std::round(incomeHandicapped[EGameResID::SULFUR] * 0.14 * 7)); | ||||
| 			weeklyBonuses[EGameResID::GEMS] = static_cast<int>(std::round(incomeHandicapped[EGameResID::GEMS] * 0.14 * 7)); | ||||
| 		} | ||||
| 		else if (gameHandler->gameState()->getStartInfo()->difficulty == 4) | ||||
| 		{ | ||||
| 			weeklyBonuses[EGameResID::GOLD] = static_cast<int>(std::round(incomeHandicapped[EGameResID::GOLD] * 0.5 * 7)); | ||||
| 			weeklyBonuses[EGameResID::WOOD] = static_cast<int>(std::round(incomeHandicapped[EGameResID::WOOD] * 0.53 * 7)); | ||||
| 			weeklyBonuses[EGameResID::ORE] = static_cast<int>(std::round(incomeHandicapped[EGameResID::ORE] * 0.53 * 7)); | ||||
| 			weeklyBonuses[EGameResID::MERCURY] = static_cast<int>(std::round(incomeHandicapped[EGameResID::MERCURY] * 0.28 * 7)); | ||||
| 			weeklyBonuses[EGameResID::CRYSTAL] = static_cast<int>(std::round(incomeHandicapped[EGameResID::CRYSTAL] * 0.28 * 7)); | ||||
| 			weeklyBonuses[EGameResID::SULFUR] = static_cast<int>(std::round(incomeHandicapped[EGameResID::SULFUR] * 0.28 * 7)); | ||||
| 			weeklyBonuses[EGameResID::GEMS] = static_cast<int>(std::round(incomeHandicapped[EGameResID::GEMS] * 0.28 * 7)); | ||||
| 		} | ||||
| 		int difficultyIndex = gameHandler->gameState()->getStartInfo()->difficulty; | ||||
| 		const std::string & difficultyName = GameConstants::DIFFICULTY_NAMES[difficultyIndex]; | ||||
| 		const JsonNode & weeklyBonusesConfig = gameHandler->gameState()->getSettings().getValue(EGameSettings::RESOURCES_WEEKLY_BONUSES_AI); | ||||
| 		const JsonNode & difficultyConfig = weeklyBonusesConfig[difficultyName]; | ||||
|  | ||||
| 		// Distribute weekly bonuses over 7 days, depending on the current day of the week | ||||
| 		for (int i = 0; i < GameResID::COUNT; ++i) | ||||
| 		for (GameResID i : GameResID::ALL_RESOURCES()) | ||||
| 		{ | ||||
| 			int dailyBonus = weeklyBonuses[i] / 7; | ||||
| 			int remainderBonus = weeklyBonuses[i] % 7; | ||||
|  | ||||
| 			// Apply the daily bonus for each day, and distribute the remainder accordingly | ||||
| 			incomeHandicapped[static_cast<GameResID>(i)] += dailyBonus; | ||||
| 			if (gameHandler->gameState()->getDate(Date::DAY_OF_WEEK) - 1 < remainderBonus) | ||||
| 			{ | ||||
| 				incomeHandicapped[static_cast<GameResID>(i)] += 1; | ||||
| 			} | ||||
| 			const std::string & name = GameConstants::RESOURCE_NAMES[i]; | ||||
| 			int weeklyBonus = difficultyConfig[name].Integer(); | ||||
| 			int dayOfWeek = gameHandler->gameState()->getDate(Date::DAY_OF_WEEK); | ||||
| 			int dailyIncome = incomeHandicapped[i]; | ||||
| 			int amountTillToday = dailyIncome * weeklyBonus * (dayOfWeek-1) / 7 / 100; | ||||
| 			int amountAfterToday = dailyIncome * weeklyBonus * dayOfWeek / 7 / 100; | ||||
| 			int dailyBonusToday = amountAfterToday - amountTillToday; | ||||
| 			incomeHandicapped[static_cast<GameResID>(i)] += dailyBonusToday; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
|   | ||||
 Submodule test/googletest updated: b796f7d446...b514bdc898
									
								
							
		Reference in New Issue
	
	Block a user