1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-12-24 22:14:36 +02:00

Merge pull request #4110 from vcmi/rewardables

NKAI: rewardables
This commit is contained in:
Andrii Danylchenko 2024-07-14 09:02:37 +03:00 committed by GitHub
commit 6f5710e809
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 183 additions and 39 deletions

View File

@ -430,9 +430,16 @@ bool shouldVisit(const Nullkiller * ai, const CGHeroInstance * h, const CGObject
return false;
}
if(obj->wasVisited(h)) //it must pointer to hero instance, heroPtr calls function wasVisited(ui8 player);
if(obj->wasVisited(h))
return false;
auto rewardable = dynamic_cast<const Rewardable::Interface *>(obj);
if(rewardable && rewardable->getAvailableRewards(h, Rewardable::EEventType::EVENT_FIRST_VISIT).empty())
{
return false;
}
return true;
}

View File

@ -79,6 +79,15 @@ bool needToRecruitHero(const Nullkiller * ai, const CGTownInstance * startupTown
bool isGoldPile = dynamic_cast<const CGResource *>(obj)
&& dynamic_cast<const CGResource *>(obj)->resourceID() == EGameResID::GOLD;
auto rewardable = dynamic_cast<const Rewardable::Interface *>(obj);
if(rewardable)
{
for(auto & info : rewardable->configuration.info)
if(info.reward.resources[EGameResID::GOLD] > 0)
isGoldPile = true;
}
if(isGoldPile
|| obj->ID == Obj::TREASURE_CHEST
|| obj->ID == Obj::CAMPFIRE

View File

@ -137,16 +137,19 @@ TResources getCreatureBankResources(const CGObjectInstance * target, const CGHer
return sum > 1 ? result / sum : result;
}
uint64_t getResourcesGoldReward(const TResources & res)
int32_t getResourcesGoldReward(const TResources & res)
{
int nonGoldResources = res[EGameResID::GEMS]
+ res[EGameResID::SULFUR]
+ res[EGameResID::WOOD]
+ res[EGameResID::ORE]
+ res[EGameResID::CRYSTAL]
+ res[EGameResID::MERCURY];
int32_t result = 0;
return res[EGameResID::GOLD] + 100 * nonGoldResources;
for(EGameResID r = EGameResID(0); r < EGameResID::COUNT; r.advance(1))
{
if(res[r] > 0)
{
result += r == EGameResID::GOLD ? res[r] : res[r] * 100;
}
}
return result;
}
uint64_t getCreatureBankArmyReward(const CGObjectInstance * target, const CGHeroInstance * hero)
@ -250,9 +253,9 @@ int getDwellingArmyCost(const CGObjectInstance * target)
return cost;
}
static uint64_t evaluateArtifactArmyValue(const CArtifactInstance * art)
static uint64_t evaluateArtifactArmyValue(const CArtifact * art)
{
if(art->artType->getId() == ArtifactID::SPELL_SCROLL)
if(art->getId() == ArtifactID::SPELL_SCROLL)
return 1500;
auto statsValue =
@ -267,7 +270,7 @@ static uint64_t evaluateArtifactArmyValue(const CArtifactInstance * art)
auto classValue = 0;
switch(art->artType->aClass)
switch(art->aClass)
{
case CArtifact::EartClass::ART_MINOR:
classValue = 1000;
@ -315,7 +318,7 @@ uint64_t RewardEvaluator::getArmyReward(
case Obj::WARRIORS_TOMB:
return 1000;
case Obj::ARTIFACT:
return evaluateArtifactArmyValue(dynamic_cast<const CGArtifact *>(target)->storedArtifact);
return evaluateArtifactArmyValue(dynamic_cast<const CGArtifact *>(target)->storedArtifact->artType);
case Obj::DRAGON_UTOPIA:
return 10000;
case Obj::HERO:
@ -328,8 +331,46 @@ uint64_t RewardEvaluator::getArmyReward(
case Obj::MAGIC_SPRING:
return getManaRecoveryArmyReward(hero);
default:
return 0;
break;
}
auto rewardable = dynamic_cast<const Rewardable::Interface *>(target);
if(rewardable)
{
auto totalValue = 0;
for(int index : rewardable->getAvailableRewards(hero, Rewardable::EEventType::EVENT_FIRST_VISIT))
{
auto & info = rewardable->configuration.info[index];
auto rewardValue = 0;
if(!info.reward.artifacts.empty())
{
for(auto artID : info.reward.artifacts)
{
const CArtifact * art = dynamic_cast<const CArtifact *>(VLC->artifacts()->getById(artID));
rewardValue += evaluateArtifactArmyValue(art);
}
}
if(!info.reward.creatures.empty())
{
for(auto stackInfo : info.reward.creatures)
{
rewardValue += stackInfo.getType()->getAIValue() * stackInfo.getCount();
}
}
totalValue += rewardValue > 0 ? rewardValue / (info.reward.artifacts.size() + info.reward.creatures.size()) : 0;
}
return totalValue;
}
return 0;
}
uint64_t RewardEvaluator::getArmyGrowth(
@ -473,7 +514,24 @@ uint64_t RewardEvaluator::getManaRecoveryArmyReward(const CGHeroInstance * hero)
return ai->heroManager->getMagicStrength(hero) * 10000 * (1.0f - std::sqrt(static_cast<float>(hero->mana) / hero->manaLimit()));
}
float RewardEvaluator::getStrategicalValue(const CGObjectInstance * target) const
float RewardEvaluator::getResourceRequirementStrength(const TResources & res) const
{
float sum = 0.0f;
for(TResources::nziterator it(res); it.valid(); it++)
{
//Evaluate resources used for construction. Gold is evaluated separately.
if(it->resType != EGameResID::GOLD)
{
sum += 0.1f * it->resVal * getResourceRequirementStrength(it->resType)
+ 0.05f * it->resVal * getTotalResourceRequirementStrength(it->resType);
}
}
return sum;
}
float RewardEvaluator::getStrategicalValue(const CGObjectInstance * target, const CGHeroInstance * hero) const
{
if(!target)
return 0;
@ -491,24 +549,17 @@ float RewardEvaluator::getStrategicalValue(const CGObjectInstance * target) cons
case Obj::RESOURCE:
{
auto resource = dynamic_cast<const CGResource *>(target);
return resource->resourceID() == EGameResID::GOLD
? 0
: 0.2f * getTotalResourceRequirementStrength(resource->resourceID()) + 0.4f * getResourceRequirementStrength(resource->resourceID());
TResources res;
res[resource->resourceID()] = resource->amount;
return getResourceRequirementStrength(res);
}
case Obj::CREATURE_BANK:
{
auto resourceReward = getCreatureBankResources(target, nullptr);
float sum = 0.0f;
for (TResources::nziterator it (resourceReward); it.valid(); it++)
{
//Evaluate resources used for construction. Gold is evaluated separately.
if (it->resType != EGameResID::GOLD)
{
sum += 0.1f * it->resVal * getResourceRequirementStrength(it->resType);
}
}
return sum;
return getResourceRequirementStrength(resourceReward);
}
case Obj::TOWN:
@ -547,8 +598,24 @@ float RewardEvaluator::getStrategicalValue(const CGObjectInstance * target) cons
return 0.6f;
default:
return 0;
break;
}
auto rewardable = dynamic_cast<const Rewardable::Interface *>(target);
if(rewardable && hero)
{
auto resourceReward = 0.0f;
for(int index : rewardable->getAvailableRewards(hero, Rewardable::EEventType::EVENT_FIRST_VISIT))
{
resourceReward += getResourceRequirementStrength(rewardable->configuration.info[index].reward.resources);
}
return resourceReward;
}
return 0;
}
float RewardEvaluator::evaluateWitchHutSkillScore(const CGObjectInstance * hut, const CGHeroInstance * hero, HeroRole role) const
@ -593,11 +660,11 @@ float RewardEvaluator::getSkillReward(const CGObjectInstance * target, const CGH
case Obj::ARENA:
return 2;
case Obj::SHRINE_OF_MAGIC_INCANTATION:
return 0.2f;
return 0.25f;
case Obj::SHRINE_OF_MAGIC_GESTURE:
return 0.3f;
return 1.0f;
case Obj::SHRINE_OF_MAGIC_THOUGHT:
return 0.5f;
return 2.0f;
case Obj::LIBRARY_OF_ENLIGHTENMENT:
return 8;
case Obj::WITCH_HUT:
@ -606,14 +673,56 @@ float RewardEvaluator::getSkillReward(const CGObjectInstance * target, const CGH
//Can contains experience, spells, or skills (only on custom maps)
return 2.5f;
case Obj::PYRAMID:
return 3.0f;
return 6.0f;
case Obj::HERO:
return ai->cb->getPlayerRelations(target->tempOwner, ai->playerID) == PlayerRelations::ENEMIES
? enemyHeroEliminationSkillRewardRatio * dynamic_cast<const CGHeroInstance *>(target)->level
: 0;
default:
return 0;
break;
}
auto rewardable = dynamic_cast<const Rewardable::Interface *>(target);
if(rewardable)
{
auto totalValue = 0.0f;
for(int index : rewardable->getAvailableRewards(hero, Rewardable::EEventType::EVENT_FIRST_VISIT))
{
auto & info = rewardable->configuration.info[index];
auto rewardValue = 0.0f;
if(!info.reward.spells.empty())
{
for(auto spellID : info.reward.spells)
{
const spells::Spell * spell = VLC->spells()->getById(spellID);
if(hero->canLearnSpell(spell) && !hero->spellbookContainsSpell(spellID))
{
rewardValue += std::sqrt(spell->getLevel()) / 4.0f;
}
}
totalValue += rewardValue / info.reward.spells.size();
}
if(!info.reward.primary.empty())
{
for(auto value : info.reward.primary)
{
totalValue += value;
}
}
}
return totalValue;
}
return 0;
}
const HitMapInfo & RewardEvaluator::getEnemyHeroDanger(const int3 & tile, uint8_t turn) const
@ -697,8 +806,26 @@ int32_t RewardEvaluator::getGoldReward(const CGObjectInstance * target, const CG
? heroEliminationBonus + enemyArmyEliminationGoldRewardRatio * getArmyCost(dynamic_cast<const CGHeroInstance *>(target))
: 0;
default:
return 0;
break;
}
auto rewardable = dynamic_cast<const Rewardable::Interface *>(target);
if(rewardable)
{
auto goldReward = 0;
for(int index : rewardable->getAvailableRewards(hero, Rewardable::EEventType::EVENT_FIRST_VISIT))
{
auto & info = rewardable->configuration.info[index];
goldReward += getResourcesGoldReward(info.reward.resources);
}
return goldReward;
}
return 0;
}
class HeroExchangeEvaluator : public IEvaluationContextBuilder

View File

@ -39,7 +39,8 @@ public:
int getGoldCost(const CGObjectInstance * target, const CGHeroInstance * hero, const CCreatureSet * army) const;
float getEnemyHeroStrategicalValue(const CGHeroInstance * enemy) const;
float getResourceRequirementStrength(int resType) const;
float getStrategicalValue(const CGObjectInstance * target) const;
float getResourceRequirementStrength(const TResources & res) const;
float getStrategicalValue(const CGObjectInstance * target, const CGHeroInstance * hero = nullptr) const;
float getTotalResourceRequirementStrength(int resType) const;
float evaluateWitchHutSkillScore(const CGObjectInstance * hut, const CGHeroInstance * hero, HeroRole role) const;
float getSkillReward(const CGObjectInstance * target, const CGHeroInstance * hero, HeroRole role) const;

View File

@ -29,9 +29,6 @@ private:
protected:
/// filters list of visit info and returns rewards that can be granted to current hero
std::vector<ui32> getAvailableRewards(const CGHeroInstance * hero, Rewardable::EEventType event) const;
/// function that must be called if hero got level-up during grantReward call
virtual void grantRewardAfterLevelup(IGameCallback * cb, const Rewardable::VisitInfo & reward, const CArmedInstance * army, const CGHeroInstance * hero) const;
@ -39,6 +36,9 @@ protected:
virtual void grantRewardBeforeLevelup(IGameCallback * cb, const Rewardable::VisitInfo & reward, const CGHeroInstance * hero) const;
public:
/// filters list of visit info and returns rewards that can be granted to current hero
std::vector<ui32> getAvailableRewards(const CGHeroInstance * hero, Rewardable::EEventType event) const;
Rewardable::Configuration configuration;