1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-11-24 08:32:34 +02:00

Merge pull request #4087 from IvanSavenko/stabilization

[1.5.3] Stabilization
This commit is contained in:
Ivan Savenko 2024-06-05 23:25:00 +03:00 committed by GitHub
commit 381171f897
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
23 changed files with 112 additions and 91 deletions

View File

@ -403,7 +403,7 @@ void Nullkiller::makeTurn()
if(selectedTasks.empty())
{
return;
selectedTasks.push_back(taskptr(Goals::Invalid()));
}
bool hasAnySuccess = false;
@ -456,7 +456,7 @@ void Nullkiller::makeTurn()
scanDepth = ScanDepth::ALL_FULL;
useHeroChain = false;
hasAnySuccess = true;
break;;
break;
}
logAi->trace("Goal %s has too low priority. It is not worth doing it.", taskDescription);

View File

@ -26,9 +26,9 @@ bool ExploreNeighbourTile::operator==(const ExploreNeighbourTile & other) const
void ExploreNeighbourTile::accept(AIGateway * ai)
{
ExplorationHelper h(hero, ai->nullkiller.get());
ExplorationHelper h(hero, ai->nullkiller.get(), true);
for(int i = 0; i < tilesToExplore && hero->movementPointsRemaining() > 0; i++)
for(int i = 0; i < tilesToExplore && ai->myCb->getObj(hero->id, false) && hero->movementPointsRemaining() > 0; i++)
{
int3 pos = hero->visitablePos();
float value = 0;
@ -54,7 +54,14 @@ void ExploreNeighbourTile::accept(AIGateway * ai)
}
});
if(!target.valid() || !ai->moveHeroToTile(target, hero))
if(!target.valid())
{
return;
}
auto danger = ai->nullkiller->pathfinder->getStorage()->evaluateDanger(target, hero, true);
if(danger > 0 || !ai->moveHeroToTile(target, hero))
{
return;
}

View File

@ -24,8 +24,8 @@ namespace NKAI
using namespace Goals;
ExplorationHelper::ExplorationHelper(const CGHeroInstance * hero, const Nullkiller * ai)
:ai(ai), cbp(ai->cb.get()), hero(hero)
ExplorationHelper::ExplorationHelper(const CGHeroInstance * hero, const Nullkiller * ai, bool useCPathfinderAccessibility)
:ai(ai), cbp(ai->cb.get()), hero(hero), useCPathfinderAccessibility(useCPathfinderAccessibility)
{
ts = cbp->getPlayerTeam(ai->playerID);
sightRadius = hero->getSightRadius();
@ -104,7 +104,7 @@ bool ExplorationHelper::scanMap()
if(!bestGoal->invalid())
{
return false;
return true;
}
allowDeadEndCancellation = false;
@ -222,7 +222,9 @@ bool ExplorationHelper::hasReachableNeighbor(const int3 & pos) const
int3 tile = pos + dir;
if(cbp->isInTheMap(tile))
{
auto isAccessible = ai->pathfinder->isTileAccessible(hero, tile);
auto isAccessible = useCPathfinderAccessibility
? ai->cb->getPathsInfo(hero)->getPathInfo(tile)->reachable()
: ai->pathfinder->isTileAccessible(hero, tile);
if(isAccessible)
return true;

View File

@ -34,9 +34,10 @@ private:
const TeamState * ts;
int3 ourPos;
bool allowDeadEndCancellation;
bool useCPathfinderAccessibility;
public:
ExplorationHelper(const CGHeroInstance * hero, const Nullkiller * ai);
ExplorationHelper(const CGHeroInstance * hero, const Nullkiller * ai, bool useCPathfinderAccessibility = false);
Goals::TSubgoal makeComposition() const;
bool scanSector(int scanRadius);
bool scanMap();

View File

@ -10,7 +10,7 @@ android {
applicationId "is.xyz.vcmi"
minSdk 19
targetSdk 33
versionCode 1520
versionCode 1521
versionName "1.5.2"
setProperty("archivesBaseName", "vcmi")
}

View File

@ -244,7 +244,7 @@ void CPlayerInterface::performAutosave()
int txtlen = TextOperations::getUnicodeCharactersCount(name);
TextOperations::trimRightUnicode(name, std::max(0, txtlen - 15));
std::string forbiddenChars("\\/:?\"<>| ");
std::string forbiddenChars("\\/:*?\"<>| ");
std::replace_if(name.begin(), name.end(), [&](char c) { return std::string::npos != forbiddenChars.find(c); }, '_' );
prefix = name + "_" + cb->getStartInfo()->startTimeIso8601 + "/";

View File

@ -643,11 +643,15 @@ void AdventureMapInterface::onTileHovered(const int3 &targetPosition)
objRelations = LOCPLINT->cb->getPlayerRelations(LOCPLINT->playerID, objAtTile->tempOwner);
std::string text = LOCPLINT->localState->getCurrentHero() ? objAtTile->getHoverText(LOCPLINT->localState->getCurrentHero()) : objAtTile->getHoverText(LOCPLINT->playerID);
boost::replace_all(text,"\n"," ");
if (GH.isKeyboardShiftDown())
text.append(" (" + std::to_string(targetPosition.x) + ", " + std::to_string(targetPosition.y) + ")");
GH.statusbar()->write(text);
}
else if(isTargetPositionVisible)
{
std::string tileTooltipText = CGI->mh->getTerrainDescr(targetPosition, false);
if (GH.isKeyboardShiftDown())
tileTooltipText.append(" (" + std::to_string(targetPosition.x) + ", " + std::to_string(targetPosition.y) + ")");
GH.statusbar()->write(tileTooltipText);
}

View File

@ -763,8 +763,8 @@ void BattleWindow::blockUI(bool on)
setShortcutBlocked(EShortcut::BATTLE_SELECT_ACTION, on || owner.tacticsMode);
setShortcutBlocked(EShortcut::BATTLE_AUTOCOMBAT, (settings["battle"]["endWithAutocombat"].Bool() && onlyOnePlayerHuman) ? on || owner.tacticsMode || owner.actionsController->spellcastingModeActive() : owner.actionsController->spellcastingModeActive());
setShortcutBlocked(EShortcut::BATTLE_END_WITH_AUTOCOMBAT, on || owner.tacticsMode || !onlyOnePlayerHuman || owner.actionsController->spellcastingModeActive());
setShortcutBlocked(EShortcut::BATTLE_TACTICS_END, on && owner.tacticsMode);
setShortcutBlocked(EShortcut::BATTLE_TACTICS_NEXT, on && owner.tacticsMode);
setShortcutBlocked(EShortcut::BATTLE_TACTICS_END, on || !owner.tacticsMode);
setShortcutBlocked(EShortcut::BATTLE_TACTICS_NEXT, on || !owner.tacticsMode);
setShortcutBlocked(EShortcut::BATTLE_CONSOLE_DOWN, on && !owner.tacticsMode);
setShortcutBlocked(EShortcut::BATTLE_CONSOLE_UP, on && !owner.tacticsMode);
}

View File

@ -542,42 +542,34 @@ void CGarrisonInt::addSplitBtn(std::shared_ptr<CButton> button)
void CGarrisonInt::createSlots()
{
availableSlots.clear();
int distance = interx + (smallIcons ? 32 : 58);
for(auto i : { EGarrisonType::UPPER, EGarrisonType::LOWER })
{
Point offset = garOffset * static_cast<int>(i);
std::vector<std::shared_ptr<CGarrisonSlot>> garrisonSlots;
garrisonSlots.resize(7);
if(army(i))
{
for(auto & elem : army(i)->Slots())
{
garrisonSlots[elem.first.getNum()] = std::make_shared<CGarrisonSlot>(this, offset.x + (elem.first.getNum()*distance), offset.y, elem.first, i, elem.second);
}
}
for(int j = 0; j < 7; j++)
{
if(!garrisonSlots[j])
garrisonSlots[j] = std::make_shared<CGarrisonSlot>(this, offset.x + (j*distance), offset.y, SlotID(j), i, nullptr);
Point position(offset.x + (j*distance), offset.y);
if(layout == ESlotsLayout::TWO_ROWS && j >= 4)
{
garrisonSlots[j]->moveBy(Point(-126, 37));
position += Point(-126, 37);
}
else if(layout == ESlotsLayout::REVERSED_TWO_ROWS)
{
if(j >= 3)
{
garrisonSlots[j]->moveBy(Point(-90, 49));
position += Point(-90, 49);
}
else
{
garrisonSlots[j]->moveBy(Point(36, 0));
position += Point(36, 0);
}
}
SlotID slot(j);
availableSlots.push_back(std::make_shared<CGarrisonSlot>(this, position.x, position.y, slot, i, army(i) ? army(i)->getStackPtr(slot) : nullptr));
}
vstd::concatenate(availableSlots, garrisonSlots);
}
}

View File

@ -46,9 +46,7 @@ static bool isQuickExchangeLayoutAvailable()
CExchangeWindow::CExchangeWindow(ObjectInstanceID hero1, ObjectInstanceID hero2, QueryID queryID)
: CWindowObject(PLAYER_COLORED | BORDERED, ImagePath::builtin(isQuickExchangeLayoutAvailable() ? QUICK_EXCHANGE_BG : "TRADE2")),
controller(hero1, hero2),
moveStackLeftButtons(),
moveStackRightButtons()
controller(hero1, hero2)
{
const bool qeLayout = isQuickExchangeLayoutAvailable();
@ -193,54 +191,52 @@ CExchangeWindow::CExchangeWindow(ObjectInstanceID hero1, ObjectInstanceID hero2,
if(qeLayout)
{
moveAllGarrButtonLeft = std::make_shared<CButton>(Point(325, 118), AnimationPath::builtin("quick-exchange/armRight.DEF"), CButton::tooltip(CGI->generaltexth->qeModCommands[1]),
[this](){ this->moveUnitsShortcut(false); });
exchangeGarrButton = std::make_shared<CButton>(Point(377, 118), AnimationPath::builtin("quick-exchange/swapAll.DEF"), CButton::tooltip(CGI->generaltexth->qeModCommands[2]),
[this](){ controller.swapArmy(); });
moveAllGarrButtonRight = std::make_shared<CButton>(Point(425, 118), AnimationPath::builtin("quick-exchange/armLeft.DEF"), CButton::tooltip(CGI->generaltexth->qeModCommands[1]),
[this](){ this->moveUnitsShortcut(true); });
moveArtifactsButtonLeft = std::make_shared<CButton>(Point(325, 154), AnimationPath::builtin("quick-exchange/artRight.DEF"), CButton::tooltip(CGI->generaltexth->qeModCommands[3]),
[this](){ this->moveArtifactsCallback(false);});
exchangeArtifactsButton = std::make_shared<CButton>(Point(377, 154), AnimationPath::builtin("quick-exchange/swapAll.DEF"), CButton::tooltip(CGI->generaltexth->qeModCommands[4]),
[this](){ this->swapArtifactsCallback(); });
moveArtifactsButtonRight = std::make_shared<CButton>(Point(425, 154), AnimationPath::builtin("quick-exchange/artLeft.DEF"), CButton::tooltip(CGI->generaltexth->qeModCommands[3]),
[this](){ this->moveArtifactsCallback(true);});
buttonMoveUnitsFromLeftToRight = std::make_shared<CButton>(Point(325, 118), AnimationPath::builtin("quick-exchange/armRight.DEF"), CButton::tooltip(CGI->generaltexth->qeModCommands[1]), [this](){ this->moveUnitsShortcut(true); });
buttonMoveUnitsFromRightToLeft = std::make_shared<CButton>(Point(425, 118), AnimationPath::builtin("quick-exchange/armLeft.DEF"), CButton::tooltip(CGI->generaltexth->qeModCommands[1]), [this](){ this->moveUnitsShortcut(false); });
buttonMoveArtifactsFromLeftToRight = std::make_shared<CButton>(Point(325, 154), AnimationPath::builtin("quick-exchange/artRight.DEF"), CButton::tooltip(CGI->generaltexth->qeModCommands[3]), [this](){ this->moveArtifactsCallback(true);});
buttonMoveArtifactsFromRightToLeft = std::make_shared<CButton>(Point(425, 154), AnimationPath::builtin("quick-exchange/artLeft.DEF"), CButton::tooltip(CGI->generaltexth->qeModCommands[3]), [this](){ this->moveArtifactsCallback(false);});
backpackButtonLeft = std::make_shared<CButton>(Point(325, 518), AnimationPath::builtin("heroBackpack"), CButton::tooltipLocalized("vcmi.heroWindow.openBackpack"),
exchangeUnitsButton = std::make_shared<CButton>(Point(377, 118), AnimationPath::builtin("quick-exchange/swapAll.DEF"), CButton::tooltip(CGI->generaltexth->qeModCommands[2]), [this](){ controller.swapArmy(); });
exchangeArtifactsButton = std::make_shared<CButton>(Point(377, 154), AnimationPath::builtin("quick-exchange/swapAll.DEF"), CButton::tooltip(CGI->generaltexth->qeModCommands[4]), [this](){ this->swapArtifactsCallback(); });
backpackButtonLeft = std::make_shared<CButton>(Point(325, 518), AnimationPath::builtin("heroBackpack"), CButton::tooltipLocalized("vcmi.heroWindow.openBackpack"),
[this](){ this->backpackShortcut(true); });
backpackButtonRight = std::make_shared<CButton>(Point(419, 518), AnimationPath::builtin("heroBackpack"), CButton::tooltipLocalized("vcmi.heroWindow.openBackpack"),
backpackButtonRight = std::make_shared<CButton>(Point(419, 518), AnimationPath::builtin("heroBackpack"), CButton::tooltipLocalized("vcmi.heroWindow.openBackpack"),
[this](){ this->backpackShortcut(false); });
backpackButtonLeft->setOverlay(std::make_shared<CPicture>(ImagePath::builtin("heroWindow/backpackButtonIcon")));
backpackButtonRight->setOverlay(std::make_shared<CPicture>(ImagePath::builtin("heroWindow/backpackButtonIcon")));
auto leftHeroBlock = heroInst[0]->tempOwner != LOCPLINT->cb->getPlayerID();
auto rightHeroBlock = heroInst[1]->tempOwner != LOCPLINT->cb->getPlayerID();
moveAllGarrButtonLeft->block(leftHeroBlock);
exchangeGarrButton->block(leftHeroBlock || rightHeroBlock);
moveAllGarrButtonRight->block(rightHeroBlock);
moveArtifactsButtonLeft->block(leftHeroBlock);
buttonMoveUnitsFromLeftToRight->block(leftHeroBlock);
buttonMoveUnitsFromRightToLeft->block(rightHeroBlock);
buttonMoveArtifactsFromLeftToRight->block(leftHeroBlock);
buttonMoveArtifactsFromRightToLeft->block(rightHeroBlock);
exchangeUnitsButton->block(leftHeroBlock || rightHeroBlock);
exchangeArtifactsButton->block(leftHeroBlock || rightHeroBlock);
moveArtifactsButtonRight->block(rightHeroBlock);
backpackButtonLeft->block(leftHeroBlock);
backpackButtonRight->block(rightHeroBlock);
for(int i = 0; i < GameConstants::ARMY_SIZE; i++)
{
moveStackLeftButtons.push_back(
moveUnitFromRightToLeftButtons.push_back(
std::make_shared<CButton>(
Point(484 + 35 * i, 154),
AnimationPath::builtin("quick-exchange/unitLeft.DEF"),
CButton::tooltip(CGI->generaltexth->qeModCommands[1]),
std::bind(&CExchangeController::moveStack, &controller, false, SlotID(i))));
moveStackLeftButtons.back()->block(leftHeroBlock);
moveUnitFromRightToLeftButtons.back()->block(leftHeroBlock);
moveStackRightButtons.push_back(
moveUnitFromLeftToRightButtons.push_back(
std::make_shared<CButton>(
Point(66 + 35 * i, 154),
AnimationPath::builtin("quick-exchange/unitRight.DEF"),
CButton::tooltip(CGI->generaltexth->qeModCommands[1]),
std::bind(&CExchangeController::moveStack, &controller, true, SlotID(i))));
moveStackLeftButtons.back()->block(rightHeroBlock);
moveUnitFromLeftToRightButtons.back()->block(rightHeroBlock);
}
}

View File

@ -41,14 +41,16 @@ class CExchangeWindow : public CStatusbarWindow, public IGarrisonHolder, public
std::array<std::shared_ptr<CButton>, 2> questlogButton;
std::shared_ptr<CGarrisonInt> garr;
std::shared_ptr<CButton> moveAllGarrButtonLeft;
std::shared_ptr<CButton> exchangeGarrButton;
std::shared_ptr<CButton> moveAllGarrButtonRight;
std::shared_ptr<CButton> moveArtifactsButtonLeft;
std::shared_ptr<CButton> buttonMoveUnitsFromLeftToRight;
std::shared_ptr<CButton> buttonMoveUnitsFromRightToLeft;
std::shared_ptr<CButton> buttonMoveArtifactsFromLeftToRight;
std::shared_ptr<CButton> buttonMoveArtifactsFromRightToLeft;
std::shared_ptr<CButton> exchangeUnitsButton;
std::shared_ptr<CButton> exchangeArtifactsButton;
std::shared_ptr<CButton> moveArtifactsButtonRight;
std::vector<std::shared_ptr<CButton>> moveStackLeftButtons;
std::vector<std::shared_ptr<CButton>> moveStackRightButtons;
std::vector<std::shared_ptr<CButton>> moveUnitFromLeftToRightButtons;
std::vector<std::shared_ptr<CButton>> moveUnitFromRightToLeftButtons;
std::shared_ptr<CButton> backpackButtonLeft;
std::shared_ptr<CButton> backpackButtonRight;
CExchangeController controller;

View File

@ -434,14 +434,15 @@ CLevelWindow::CLevelWindow(const CGHeroInstance * hero, PrimarySkill pskill, std
skillValue = std::make_shared<CLabel>(192, 253, FONT_MEDIUM, ETextAlignment::CENTER, Colors::WHITE, CGI->generaltexth->primarySkillNames[static_cast<int>(pskill)] + " +1");
}
CLevelWindow::~CLevelWindow()
void CLevelWindow::close()
{
//FIXME: call callback if there was nothing to select?
if (box && box->selectedIndex() != -1)
cb(box->selectedIndex());
LOCPLINT->showingDialog->setFree();
CWindowObject::close();
}
CTavernWindow::CTavernWindow(const CGObjectInstance * TavernObj, const std::function<void()> & onWindowClosed)

View File

@ -150,7 +150,8 @@ class CLevelWindow : public CWindowObject
public:
CLevelWindow(const CGHeroInstance *hero, PrimarySkill pskill, std::vector<SecondarySkill> &skills, std::function<void(ui32)> callback);
~CLevelWindow();
void close() override;
};
/// Town portal, castle gate window

View File

@ -646,10 +646,15 @@ CCreature * CCreatureHandler::loadFromJson(const std::string & scope, const Json
registerObject(scope, type_name, extraName.String(), cre->getIndex());
}
if (!cre->special &&
!CResourceHandler::get()->existsResource(cre->animDefName) &&
!CResourceHandler::get()->existsResource(cre->animDefName.addPrefix("SPRITES/")))
throw ModLoadingException(scope, "creature " + cre->getJsonKey() + " has no combat animation but is not marked as special!" );
JsonNode advMapFile = node["graphics"]["map"];
JsonNode advMapMask = node["graphics"]["mapMask"];
VLC->identifiers()->requestIdentifier(scope, "object", "monster", [cre, scope, advMapFile, advMapMask](si32 index)
VLC->identifiers()->requestIdentifier(scope, "object", "monster", [cre, scope, advMapFile, advMapMask](si32 monsterIndex)
{
JsonNode conf;
conf.setModScope(scope);
@ -672,7 +677,7 @@ CCreature * CCreatureHandler::loadFromJson(const std::string & scope, const Json
if (VLC->objtypeh->getHandlerFor(Obj::MONSTER, cre->getId().num)->getTemplates().empty())
{
if (!cre->special)
throw DataLoadingException("Mod " + scope + " is corrupted! Please disable or reinstall this mod. Reason: creature " + cre->getJsonKey() + " has no adventure map animation but is not marked as special!" );
throw ModLoadingException(scope, "creature " + cre->getJsonKey() + " has no adventure map animation but is not marked as special!" );
VLC->objtypeh->removeSubObject(Obj::MONSTER, cre->getId().num);
}
@ -737,7 +742,6 @@ void CCreatureHandler::loadCrExpMod()
}
}
void CCreatureHandler::loadCrExpBon(CBonusSystemNode & globalEffects)
{
if (VLC->settings()->getBoolean(EGameSettings::MODULE_STACK_EXPERIENCE)) //reading default stack experience bonuses

View File

@ -218,8 +218,7 @@ void CHeroClass::serializeJson(JsonSerializeFormat & handler)
CHeroClass::CHeroClass():
faction(0),
affinity(0),
defaultTavernChance(0),
commander(nullptr)
defaultTavernChance(0)
{
}
@ -302,7 +301,7 @@ CHeroClass * CHeroClassHandler::loadFromJson(const std::string & scope, const Js
VLC->identifiers()->requestIdentifier ("creature", node["commander"],
[=](si32 commanderID)
{
heroClass->commander = CreatureID(commanderID).toCreature();
heroClass->commander = CreatureID(commanderID);
});
heroClass->defaultTavernChance = static_cast<ui32>(node["defaultTavern"].Float());

View File

@ -121,7 +121,7 @@ public:
// resulting chance = sqrt(town.chance * heroClass.chance)
ui32 defaultTavernChance;
const CCreature * commander;
CreatureID commander;
std::vector<int> primarySkillInitial; // initial primary skills
std::vector<int> primarySkillLowLevel; // probability (%) of getting point of primary skill when getting level

View File

@ -14,3 +14,11 @@ class DLL_LINKAGE DataLoadingException: public std::runtime_error
public:
using std::runtime_error::runtime_error;
};
class DLL_LINKAGE ModLoadingException: public DataLoadingException
{
public:
ModLoadingException(const std::string & modName, const std::string & reason)
: DataLoadingException("Mod " + modName + " is corrupted! Please disable or reinstall this mod. Reason: " + reason)
{}
};

View File

@ -1499,8 +1499,8 @@ bool CGameState::checkForVictory(const PlayerColor & player, const EventConditio
case EventCondition::TRANSPORT:
{
const auto * t = getTown(condition.objectID);
return (t->visitingHero && t->visitingHero->hasArt(condition.objectType.as<ArtifactID>())) ||
(t->garrisonHero && t->garrisonHero->hasArt(condition.objectType.as<ArtifactID>()));
return (t->visitingHero && t->visitingHero->getOwner() == player && t->visitingHero->hasArt(condition.objectType.as<ArtifactID>())) ||
(t->garrisonHero && t->garrisonHero->getOwner() == player && t->garrisonHero->hasArt(condition.objectType.as<ArtifactID>()));
}
case EventCondition::DAYS_PASSED:
{

View File

@ -402,9 +402,9 @@ void CGHeroInstance::initHero(CRandomGenerator & rand)
addNewBonus(bonus);
}
if (VLC->settings()->getBoolean(EGameSettings::MODULE_COMMANDERS) && !commander)
if (VLC->settings()->getBoolean(EGameSettings::MODULE_COMMANDERS) && !commander && type->heroClass->commander.hasValue())
{
commander = new CCommanderInstance(type->heroClass->commander->getId());
commander = new CCommanderInstance(type->heroClass->commander);
commander->setArmyObj (castToArmyObj()); //TODO: separate function for setting commanders
commander->giveStackExp (exp); //after our exp is set
}

View File

@ -1104,7 +1104,11 @@ bool CGameHandler::moveHero(ObjectInstanceID hid, int3 dst, EMovementMode moveme
objectToVisit = t.visitableObjects.back();
if (isInTheMap(guardPos))
guardian = getTile(guardPos)->visitableObjects.back();
{
for (auto const & object : getTile(guardPos)->visitableObjects)
if (object->ID == MapObjectID::MONSTER) // exclude other objects, such as hero flying above monster
guardian = object;
}
const bool embarking = !h->boat && objectToVisit && objectToVisit->ID == Obj::BOAT;
const bool disembarking = h->boat

View File

@ -165,6 +165,9 @@ void CVCMIServer::onNewConnection(const std::shared_ptr<INetworkConnection> & co
void CVCMIServer::onPacketReceived(const std::shared_ptr<INetworkConnection> & connection, const std::vector<std::byte> & message)
{
std::shared_ptr<CConnection> c = findConnection(connection);
if (c == nullptr)
throw std::out_of_range("Unknown connection received in CVCMIServer::findConnection");
auto pack = c->retrievePack(message);
pack->c = c;
CVCMIServerPackVisitor visitor(*this, this->gh);
@ -197,7 +200,7 @@ std::shared_ptr<CConnection> CVCMIServer::findConnection(const std::shared_ptr<I
return gameConnection;
}
throw std::runtime_error("Unknown connection received in CVCMIServer::findConnection");
return nullptr;
}
bool CVCMIServer::wasStartedByClient() const
@ -342,6 +345,9 @@ void CVCMIServer::onDisconnected(const std::shared_ptr<INetworkConnection> & con
logNetwork->error("Network error receiving a pack. Connection has been closed");
std::shared_ptr<CConnection> c = findConnection(connection);
if (!c)
return; // player have already disconnected via clientDisconnected call
vstd::erase(activeConnections, c);
if(activeConnections.empty() || hostClientId == c->connectionID)

View File

@ -27,7 +27,7 @@ void CBattleQuery::notifyObjectAboutRemoval(const CObjectVisitQuery & objectVisi
{
assert(result);
if(result && !isAiVsHuman)
if(result)
objectVisit.visitedObject->battleFinished(objectVisit.visitingHero, *result);
}
@ -38,8 +38,6 @@ CBattleQuery::CBattleQuery(CGameHandler * owner, const IBattleInfo * bi):
belligerents[0] = bi->getSideArmy(0);
belligerents[1] = bi->getSideArmy(1);
isAiVsHuman = bi->getSidePlayer(1).isValidPlayer() && gh->getPlayerState(bi->getSidePlayer(0))->isHuman() != gh->getPlayerState(bi->getSidePlayer(1))->isHuman();
addPlayer(bi->getSidePlayer(0));
addPlayer(bi->getSidePlayer(1));
}
@ -89,7 +87,9 @@ CBattleDialogQuery::CBattleDialogQuery(CGameHandler * owner, const IBattleInfo *
void CBattleDialogQuery::onRemoval(PlayerColor color)
{
if (!gh->getPlayerState(color)->isHuman())
// answer to this query was already processed when handling 1st player
// this removal call for 2nd player which can be safely ignored
if (resultProcessed)
return;
assert(answer);
@ -108,13 +108,7 @@ void CBattleDialogQuery::onRemoval(PlayerColor color)
}
else
{
auto hero = bi->getSideHero(BattleSide::ATTACKER);
auto visitingObj = bi->getDefendedTown() ? bi->getDefendedTown() : gh->getVisitingObject(hero);
bool isAiVsHuman = bi->getSidePlayer(1).isValidPlayer() && gh->getPlayerState(bi->getSidePlayer(0))->isHuman() != gh->getPlayerState(bi->getSidePlayer(1))->isHuman();
gh->battles->endBattleConfirm(bi->getBattleID());
if(visitingObj && result && isAiVsHuman)
visitingObj->battleFinished(hero, *result);
}
resultProcessed = true;
}

View File

@ -23,7 +23,6 @@ public:
std::array<const CArmedInstance *,2> belligerents;
std::array<int, 2> initialHeroMana;
bool isAiVsHuman;
BattleID battleID;
std::optional<BattleResult> result;
@ -37,6 +36,7 @@ public:
class CBattleDialogQuery : public CDialogQuery
{
bool resultProcessed = false;
public:
CBattleDialogQuery(CGameHandler * owner, const IBattleInfo * Bi, std::optional<BattleResult> Br);