mirror of
https://github.com/vcmi/vcmi.git
synced 2024-12-24 22:14:36 +02:00
commit
33f8f6dc53
@ -586,11 +586,18 @@ void AIGateway::heroGotLevel(const CGHeroInstance * hero, PrimarySkill pskill, s
|
||||
|
||||
requestActionASAP([=]()
|
||||
{
|
||||
int sel = 0;
|
||||
|
||||
if(hPtr.validAndSet())
|
||||
{
|
||||
std::unique_lock<std::mutex> lockGuard(nullkiller->aiStateMutex);
|
||||
|
||||
nullkiller->heroManager->update();
|
||||
answerQuery(queryID, nullkiller->heroManager->selectBestSkill(hPtr, skills));
|
||||
|
||||
sel = nullkiller->heroManager->selectBestSkill(hPtr, skills);
|
||||
}
|
||||
|
||||
answerQuery(queryID, sel);
|
||||
});
|
||||
}
|
||||
|
||||
@ -661,6 +668,9 @@ void AIGateway::showBlockingDialog(const std::string & text, const std::vector<C
|
||||
if(selection) //select from multiple components -> take the last one (they're indexed [1-size])
|
||||
sel = components.size();
|
||||
|
||||
{
|
||||
std::unique_lock<std::mutex> mxLock(nullkiller->aiStateMutex);
|
||||
|
||||
// TODO: Find better way to understand it is Chest of Treasures
|
||||
if(hero.validAndSet()
|
||||
&& components.size() == 2
|
||||
@ -670,6 +680,7 @@ void AIGateway::showBlockingDialog(const std::string & text, const std::vector<C
|
||||
{
|
||||
sel = 1; // for now lets pick gold from a chest.
|
||||
}
|
||||
}
|
||||
|
||||
answerQuery(askID, sel);
|
||||
});
|
||||
@ -859,6 +870,8 @@ void AIGateway::performObjectInteraction(const CGObjectInstance * obj, HeroPtr h
|
||||
{
|
||||
makePossibleUpgrades(h.get());
|
||||
|
||||
std::unique_lock<std::mutex> lockGuard(nullkiller->aiStateMutex);
|
||||
|
||||
if(!h->visitedTown->garrisonHero || !nullkiller->isHeroLocked(h->visitedTown->garrisonHero))
|
||||
moveCreaturesToHero(h->visitedTown);
|
||||
|
||||
|
@ -115,6 +115,8 @@ Goals::TTask Nullkiller::choseBestTask(Goals::TSubgoal behavior, int decompositi
|
||||
|
||||
void Nullkiller::resetAiState()
|
||||
{
|
||||
std::unique_lock<std::mutex> lockGuard(aiStateMutex);
|
||||
|
||||
lockedResources = TResources();
|
||||
scanDepth = ScanDepth::MAIN_FULL;
|
||||
playerID = ai->playerID;
|
||||
@ -127,6 +129,8 @@ void Nullkiller::updateAiState(int pass, bool fast)
|
||||
{
|
||||
boost::this_thread::interruption_point();
|
||||
|
||||
std::unique_lock<std::mutex> lockGuard(aiStateMutex);
|
||||
|
||||
auto start = std::chrono::high_resolution_clock::now();
|
||||
|
||||
activeHero = nullptr;
|
||||
|
@ -73,6 +73,7 @@ public:
|
||||
std::unique_ptr<ArmyFormation> armyFormation;
|
||||
PlayerColor playerID;
|
||||
std::shared_ptr<CCallback> cb;
|
||||
std::mutex aiStateMutex;
|
||||
|
||||
Nullkiller();
|
||||
void init(std::shared_ptr<CCallback> cb, PlayerColor playerID);
|
||||
|
@ -137,6 +137,18 @@ TResources getCreatureBankResources(const CGObjectInstance * target, const CGHer
|
||||
return sum > 1 ? result / sum : result;
|
||||
}
|
||||
|
||||
uint64_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];
|
||||
|
||||
return res[EGameResID::GOLD] + 100 * nonGoldResources;
|
||||
}
|
||||
|
||||
uint64_t getCreatureBankArmyReward(const CGObjectInstance * target, const CGHeroInstance * hero)
|
||||
{
|
||||
auto objectInfo = target->getObjectHandler()->getObjectInfo(target->appearance);
|
||||
@ -491,7 +503,7 @@ float RewardEvaluator::getStrategicalValue(const CGObjectInstance * target) cons
|
||||
//Evaluate resources used for construction. Gold is evaluated separately.
|
||||
if (it->resType != EGameResID::GOLD)
|
||||
{
|
||||
sum += 0.1f * getResourceRequirementStrength(it->resType);
|
||||
sum += 0.1f * it->resVal * getResourceRequirementStrength(it->resType);
|
||||
}
|
||||
}
|
||||
return sum;
|
||||
@ -529,6 +541,9 @@ float RewardEvaluator::getStrategicalValue(const CGObjectInstance * target) cons
|
||||
? getEnemyHeroStrategicalValue(dynamic_cast<const CGHeroInstance *>(target))
|
||||
: 0;
|
||||
|
||||
case Obj::KEYMASTER:
|
||||
return 0.6f;
|
||||
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
@ -588,6 +603,8 @@ float RewardEvaluator::getSkillReward(const CGObjectInstance * target, const CGH
|
||||
case Obj::PANDORAS_BOX:
|
||||
//Can contains experience, spells, or skills (only on custom maps)
|
||||
return 2.5f;
|
||||
case Obj::PYRAMID:
|
||||
return 3.0f;
|
||||
case Obj::HERO:
|
||||
return ai->cb->getPlayerRelations(target->tempOwner, ai->playerID) == PlayerRelations::ENEMIES
|
||||
? enemyHeroEliminationSkillRewardRatio * dynamic_cast<const CGHeroInstance *>(target)->level
|
||||
@ -660,7 +677,7 @@ int32_t RewardEvaluator::getGoldReward(const CGObjectInstance * target, const CG
|
||||
case Obj::WAGON:
|
||||
return 100;
|
||||
case Obj::CREATURE_BANK:
|
||||
return getCreatureBankResources(target, hero)[EGameResID::GOLD];
|
||||
return getResourcesGoldReward(getCreatureBankResources(target, hero));
|
||||
case Obj::CRYPT:
|
||||
case Obj::DERELICT_SHIP:
|
||||
return 3000;
|
||||
|
@ -234,6 +234,7 @@ void AINodeStorage::resetTile(const int3 & coord, EPathfindingLayer layer, EPath
|
||||
heroNode.specialAction.reset();
|
||||
heroNode.armyLoss = 0;
|
||||
heroNode.chainOther = nullptr;
|
||||
heroNode.dayFlags = DayFlags::NONE;
|
||||
heroNode.update(coord, layer, accessibility);
|
||||
}
|
||||
}
|
||||
@ -295,6 +296,11 @@ void AINodeStorage::commit(
|
||||
{
|
||||
commitedTiles.insert(destination->coord);
|
||||
}
|
||||
|
||||
if(destination->turns == source->turns)
|
||||
{
|
||||
destination->dayFlags = source->dayFlags;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<CGPathNode *> AINodeStorage::calculateNeighbours(
|
||||
|
@ -41,11 +41,19 @@ namespace AIPathfinding
|
||||
const int CHAIN_MAX_DEPTH = 4;
|
||||
}
|
||||
|
||||
enum DayFlags : ui8
|
||||
{
|
||||
NONE = 0,
|
||||
FLY_CAST = 1,
|
||||
WATER_WALK_CAST = 2
|
||||
};
|
||||
|
||||
struct AIPathNode : public CGPathNode
|
||||
{
|
||||
uint64_t danger;
|
||||
uint64_t armyLoss;
|
||||
int32_t manaCost;
|
||||
int16_t manaCost;
|
||||
DayFlags dayFlags;
|
||||
const AIPathNode * chainOther;
|
||||
std::shared_ptr<const SpecialAction> specialAction;
|
||||
const ChainActor * actor;
|
||||
|
@ -22,18 +22,18 @@ namespace NKAI
|
||||
|
||||
namespace AIPathfinding
|
||||
{
|
||||
AdventureCastAction::AdventureCastAction(SpellID spellToCast, const CGHeroInstance * hero)
|
||||
:spellToCast(spellToCast), hero(hero)
|
||||
AdventureCastAction::AdventureCastAction(SpellID spellToCast, const CGHeroInstance * hero, DayFlags flagsToAdd)
|
||||
:spellToCast(spellToCast), hero(hero), flagsToAdd(flagsToAdd)
|
||||
{
|
||||
manaCost = hero->getSpellCost(spellToCast.toSpell());
|
||||
}
|
||||
|
||||
WaterWalkingAction::WaterWalkingAction(const CGHeroInstance * hero)
|
||||
:AdventureCastAction(SpellID::WATER_WALK, hero)
|
||||
:AdventureCastAction(SpellID::WATER_WALK, hero, DayFlags::WATER_WALK_CAST)
|
||||
{ }
|
||||
|
||||
AirWalkingAction::AirWalkingAction(const CGHeroInstance * hero)
|
||||
: AdventureCastAction(SpellID::FLY, hero)
|
||||
: AdventureCastAction(SpellID::FLY, hero, DayFlags::FLY_CAST)
|
||||
{
|
||||
}
|
||||
|
||||
@ -41,11 +41,12 @@ namespace AIPathfinding
|
||||
const CGHeroInstance * hero,
|
||||
CDestinationNodeInfo & destination,
|
||||
const PathNodeInfo & source,
|
||||
AIPathNode * dstMode,
|
||||
AIPathNode * dstNode,
|
||||
const AIPathNode * srcNode) const
|
||||
{
|
||||
dstMode->manaCost = srcNode->manaCost + manaCost;
|
||||
dstMode->theNodeBefore = source.node;
|
||||
dstNode->manaCost = srcNode->manaCost + manaCost;
|
||||
dstNode->theNodeBefore = source.node;
|
||||
dstNode->dayFlags = static_cast<DayFlags>(dstNode->dayFlags | flagsToAdd);
|
||||
}
|
||||
|
||||
void AdventureCastAction::execute(const CGHeroInstance * hero) const
|
||||
|
@ -24,9 +24,10 @@ namespace AIPathfinding
|
||||
SpellID spellToCast;
|
||||
const CGHeroInstance * hero;
|
||||
int manaCost;
|
||||
DayFlags flagsToAdd;
|
||||
|
||||
public:
|
||||
AdventureCastAction(SpellID spellToCast, const CGHeroInstance * hero);
|
||||
AdventureCastAction(SpellID spellToCast, const CGHeroInstance * hero, DayFlags flagsToAdd = DayFlags::NONE);
|
||||
|
||||
virtual void execute(const CGHeroInstance * hero) const override;
|
||||
|
||||
|
@ -61,6 +61,12 @@ namespace AIPathfinding
|
||||
|
||||
if(source.node->layer == EPathfindingLayer::LAND && destination.node->layer == EPathfindingLayer::WATER)
|
||||
{
|
||||
if(nodeStorage->getAINode(source.node)->dayFlags & DayFlags::WATER_WALK_CAST)
|
||||
{
|
||||
destination.blocked = false;
|
||||
return;
|
||||
}
|
||||
|
||||
auto action = waterWalkingActions.find(nodeStorage->getHero(source.node));
|
||||
|
||||
if(action != waterWalkingActions.end() && tryUseSpecialAction(destination, source, action->second, EPathNodeAction::NORMAL))
|
||||
@ -73,6 +79,12 @@ namespace AIPathfinding
|
||||
|
||||
if(source.node->layer == EPathfindingLayer::LAND && destination.node->layer == EPathfindingLayer::AIR)
|
||||
{
|
||||
if(nodeStorage->getAINode(source.node)->dayFlags & DayFlags::FLY_CAST)
|
||||
{
|
||||
destination.blocked = false;
|
||||
return;
|
||||
}
|
||||
|
||||
auto action = airWalkingActions.find(nodeStorage->getHero(source.node));
|
||||
|
||||
if(action != airWalkingActions.end() && tryUseSpecialAction(destination, source, action->second, EPathNodeAction::NORMAL))
|
||||
|
@ -462,9 +462,9 @@ static void mainLoop()
|
||||
{
|
||||
if(CSH->client)
|
||||
CSH->endGameplay();
|
||||
}
|
||||
|
||||
GH.windows().clear();
|
||||
}
|
||||
|
||||
CMM.reset();
|
||||
|
||||
@ -524,25 +524,24 @@ void handleQuit(bool ask)
|
||||
// FIXME: avoids crash if player attempts to close game while opening is still playing
|
||||
// use cursor handler as indicator that loading is not done yet
|
||||
// proper solution would be to abort init thread (or wait for it to finish)
|
||||
if(!ask)
|
||||
{
|
||||
quitApplication();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!CCS->curh)
|
||||
{
|
||||
quitRequestedDuringOpeningPlayback = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if(ask)
|
||||
{
|
||||
CCS->curh->set(Cursor::Map::POINTER);
|
||||
|
||||
if (LOCPLINT)
|
||||
LOCPLINT->showYesNoDialog(CGI->generaltexth->allTexts[69], quitApplication, nullptr);
|
||||
else
|
||||
CInfoWindow::showYesNoDialog(CGI->generaltexth->allTexts[69], {}, quitApplication, {}, PlayerColor(1));
|
||||
}
|
||||
else
|
||||
{
|
||||
quitApplication();
|
||||
}
|
||||
}
|
||||
|
||||
void handleFatalError(const std::string & message, bool terminate)
|
||||
|
@ -617,6 +617,8 @@ bool CServerHandler::validateGameStart(bool allowOnlyAI) const
|
||||
void CServerHandler::sendStartGame(bool allowOnlyAI) const
|
||||
{
|
||||
verifyStateBeforeStart(allowOnlyAI ? true : settings["session"]["onlyai"].Bool());
|
||||
|
||||
if(!settings["session"]["headless"].Bool())
|
||||
GH.windows().createAndPushWindow<CLoadingScreen>();
|
||||
|
||||
LobbyStartGame lsg;
|
||||
|
@ -411,7 +411,7 @@ void CClient::initPlayerEnvironments()
|
||||
hasHumanPlayer = true;
|
||||
}
|
||||
|
||||
if(!hasHumanPlayer)
|
||||
if(!hasHumanPlayer && !settings["session"]["headless"].Bool())
|
||||
{
|
||||
Settings session = settings.write["session"];
|
||||
session["spectate"].Bool() = true;
|
||||
@ -436,7 +436,7 @@ void CClient::initPlayerInterfaces()
|
||||
if(!vstd::contains(playerint, color))
|
||||
{
|
||||
logNetwork->info("Preparing interface for player %s", color.toString());
|
||||
if(playerInfo.second.isControlledByAI())
|
||||
if(playerInfo.second.isControlledByAI() || settings["session"]["onlyai"].Bool())
|
||||
{
|
||||
bool alliedToHuman = false;
|
||||
for(auto & allyInfo : gs->scenarioOps->playerInfos)
|
||||
|
@ -157,6 +157,9 @@ void ApplyClientNetPackVisitor::visitSetMana(SetMana & pack)
|
||||
const CGHeroInstance *h = cl.getHero(pack.hid);
|
||||
callInterfaceIfPresent(cl, h->tempOwner, &IGameEventsReceiver::heroManaPointsChanged, h);
|
||||
|
||||
if(settings["session"]["headless"].Bool())
|
||||
return;
|
||||
|
||||
for (auto window : GH.windows().findWindows<BattleWindow>())
|
||||
window->heroManaPointsChanged(h);
|
||||
}
|
||||
@ -467,6 +470,7 @@ void ApplyFirstClientNetPackVisitor::visitRemoveObject(RemoveObject & pack)
|
||||
i->second->objectRemoved(o, pack.initiator);
|
||||
}
|
||||
|
||||
if(CGI->mh)
|
||||
CGI->mh->waitForOngoingAnimations();
|
||||
}
|
||||
|
||||
@ -553,9 +557,11 @@ void ApplyClientNetPackVisitor::visitNewStructures(NewStructures & pack)
|
||||
}
|
||||
|
||||
// invalidate section of map view with our object and force an update
|
||||
if(CGI->mh)
|
||||
{
|
||||
CGI->mh->onObjectInstantRemove(town, town->getOwner());
|
||||
CGI->mh->onObjectInstantAdd(town, town->getOwner());
|
||||
|
||||
}
|
||||
}
|
||||
void ApplyClientNetPackVisitor::visitRazeStructures(RazeStructures & pack)
|
||||
{
|
||||
@ -566,8 +572,11 @@ void ApplyClientNetPackVisitor::visitRazeStructures(RazeStructures & pack)
|
||||
}
|
||||
|
||||
// invalidate section of map view with our object and force an update
|
||||
if(CGI->mh)
|
||||
{
|
||||
CGI->mh->onObjectInstantRemove(town, town->getOwner());
|
||||
CGI->mh->onObjectInstantAdd(town, town->getOwner());
|
||||
}
|
||||
}
|
||||
|
||||
void ApplyClientNetPackVisitor::visitSetAvailableCreatures(SetAvailableCreatures & pack)
|
||||
@ -651,7 +660,7 @@ void ApplyFirstClientNetPackVisitor::visitSetObjectProperty(SetObjectProperty &
|
||||
}
|
||||
|
||||
// invalidate section of map view with our object and force an update with new flag color
|
||||
if (pack.what == ObjProperty::OWNER)
|
||||
if (pack.what == ObjProperty::OWNER && CGI->mh)
|
||||
{
|
||||
auto object = gs.getObjInstance(pack.id);
|
||||
CGI->mh->onObjectInstantRemove(object, object->getOwner());
|
||||
@ -668,7 +677,7 @@ void ApplyClientNetPackVisitor::visitSetObjectProperty(SetObjectProperty & pack)
|
||||
}
|
||||
|
||||
// invalidate section of map view with our object and force an update with new flag color
|
||||
if (pack.what == ObjProperty::OWNER)
|
||||
if (pack.what == ObjProperty::OWNER && CGI->mh)
|
||||
{
|
||||
auto object = gs.getObjInstance(pack.id);
|
||||
CGI->mh->onObjectInstantAdd(object, object->getOwner());
|
||||
@ -1023,6 +1032,8 @@ void ApplyClientNetPackVisitor::visitNewObject(NewObject & pack)
|
||||
if(gs.isVisible(obj, i->first))
|
||||
i->second->newObject(obj);
|
||||
}
|
||||
|
||||
if(CGI->mh)
|
||||
CGI->mh->waitForOngoingAnimations();
|
||||
}
|
||||
|
||||
|
@ -249,9 +249,7 @@ PathfinderBlockingRule::BlockingReason MovementAfterDestinationRule::getBlocking
|
||||
}
|
||||
|
||||
case EPathNodeAction::BLOCKING_VISIT:
|
||||
return destination.guarded
|
||||
? BlockingReason::DESTINATION_GUARDED
|
||||
: BlockingReason::DESTINATION_BLOCKVIS;
|
||||
return BlockingReason::DESTINATION_BLOCKVIS;
|
||||
|
||||
case EPathNodeAction::NORMAL:
|
||||
return BlockingReason::NONE;
|
||||
|
Loading…
Reference in New Issue
Block a user