mirror of
https://github.com/vcmi/vcmi.git
synced 2025-03-19 21:10:12 +02:00
Merge branch 'battle-dialog' into battle-dialog2
# Conflicts: # AI/BattleAI/BattleAI.h # AI/StupidAI/StupidAI.h # client/CPlayerInterface.cpp # client/CPlayerInterface.h # client/Client.cpp # client/NetPacksClient.cpp # client/battle/CBattleInterface.cpp # client/battle/CBattleInterface.h # client/battle/CBattleInterfaceClasses.cpp # client/battle/CBattleInterfaceClasses.h # config/schemas/settings.json # lib/CGameInterface.h # lib/IGameEventsReceiver.h # lib/NetPacks.h # lib/NetPacksLib.cpp # lib/battle/BattleInfo.cpp # server/CGameHandler.cpp # server/CQuery.h
This commit is contained in:
commit
ece8a2a41f
@ -182,7 +182,7 @@
|
|||||||
"type" : "object",
|
"type" : "object",
|
||||||
"additionalProperties" : false,
|
"additionalProperties" : false,
|
||||||
"default": {},
|
"default": {},
|
||||||
"required" : [ "heroMoveTime", "enemyMoveTime", "scrollSpeedPixels", "heroReminder", "quickCombat", "objectAnimation", "terrainAnimation" ],
|
"required" : [ "heroMoveTime", "enemyMoveTime", "scrollSpeedPixels", "heroReminder", "quickCombat", "objectAnimation", "terrainAnimation", "alwaysSkipCombat" ],
|
||||||
"properties" : {
|
"properties" : {
|
||||||
"heroMoveTime" : {
|
"heroMoveTime" : {
|
||||||
"type" : "number",
|
"type" : "number",
|
||||||
@ -211,6 +211,10 @@
|
|||||||
"terrainAnimation" : {
|
"terrainAnimation" : {
|
||||||
"type" : "boolean",
|
"type" : "boolean",
|
||||||
"default" : true
|
"default" : true
|
||||||
|
},
|
||||||
|
"alwaysSkipCombat" : {
|
||||||
|
"type" : "boolean",
|
||||||
|
"default" : false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -205,6 +205,7 @@ BattleInfo * BattleInfo::setupBattle(const int3 & tile, TerrainId terrain, const
|
|||||||
curB->battlefieldType = battlefieldType;
|
curB->battlefieldType = battlefieldType;
|
||||||
curB->round = -2;
|
curB->round = -2;
|
||||||
curB->activeStack = -1;
|
curB->activeStack = -1;
|
||||||
|
curB->creatureBank = creatureBank;
|
||||||
|
|
||||||
if(town)
|
if(town)
|
||||||
{
|
{
|
||||||
|
@ -614,44 +614,56 @@ void CGameHandler::endBattle(int3 tile, const CGHeroInstance * heroAttacker, con
|
|||||||
if(heroDefender)
|
if(heroDefender)
|
||||||
battleResult.data->exp[1] = heroDefender->calculateXp(battleResult.data->exp[1]);
|
battleResult.data->exp[1] = heroDefender->calculateXp(battleResult.data->exp[1]);
|
||||||
|
|
||||||
const CArmedInstance *bEndArmy1 = gs->curB->sides.at(0).armyObject;
|
auto battleQuery = std::dynamic_pointer_cast<CBattleQuery>(queries.topQuery(gs->curB->sides[0].color));
|
||||||
const CArmedInstance *bEndArmy2 = gs->curB->sides.at(1).armyObject;
|
|
||||||
const BattleResult::EResult result = battleResult.get()->result;
|
|
||||||
|
|
||||||
auto findBattleQuery = [this]() -> std::shared_ptr<CBattleQuery>
|
|
||||||
{
|
|
||||||
for (auto &q : queries.allQueries())
|
|
||||||
{
|
|
||||||
if (auto bq = std::dynamic_pointer_cast<CBattleQuery>(q))
|
|
||||||
if (bq->bi == gs->curB)
|
|
||||||
return bq;
|
|
||||||
}
|
|
||||||
return std::shared_ptr<CBattleQuery>();
|
|
||||||
};
|
|
||||||
|
|
||||||
auto battleQuery = findBattleQuery();
|
|
||||||
if (!battleQuery)
|
if (!battleQuery)
|
||||||
{
|
{
|
||||||
logGlobal->error("Cannot find battle query!");
|
logGlobal->error("Cannot find battle query!");
|
||||||
|
complain("Player " + boost::lexical_cast<std::string>(gs->curB->sides[0].color) + " has no battle query at the top!");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
if (battleQuery != queries.topQuery(gs->curB->sides[0].color))
|
|
||||||
complain("Player " + boost::lexical_cast<std::string>(gs->curB->sides[0].color) + " although in battle has no battle query at the top!");
|
|
||||||
|
|
||||||
battleQuery->result = boost::make_optional(*battleResult.data);
|
battleQuery->result = boost::make_optional(*battleResult.data);
|
||||||
|
|
||||||
|
//set same battle result for all queries
|
||||||
|
for(auto q : queries.allQueries())
|
||||||
|
{
|
||||||
|
auto otherBattleQuery = std::dynamic_pointer_cast<CBattleQuery>(q);
|
||||||
|
if(otherBattleQuery && otherBattleQuery->bi == battleQuery->bi)
|
||||||
|
otherBattleQuery->result = battleQuery->result;
|
||||||
|
}
|
||||||
|
|
||||||
//Check how many battle queries were created (number of players blocked by battle)
|
//Check how many battle queries were created (number of players blocked by battle)
|
||||||
const int queriedPlayers = battleQuery ? (int)boost::count(queries.allQueries(), battleQuery) : 0;
|
const int queriedPlayers = battleQuery ? (int)boost::count(queries.allQueries(), battleQuery) : 0;
|
||||||
finishingBattle = std::make_unique<FinishingBattleHelper>(battleQuery, queriedPlayers);
|
finishingBattle = make_unique<FinishingBattleHelper>(battleQuery, queriedPlayers);
|
||||||
|
|
||||||
CasualtiesAfterBattle cab1(bEndArmy1, gs->curB), cab2(bEndArmy2, gs->curB); //calculate casualties before deleting battle
|
auto battleDialogQuery = std::make_shared<CBattleDialogQuery>(this, gs->curB);
|
||||||
|
battleResult.data->queryID = battleDialogQuery->queryID;
|
||||||
|
queries.addQuery(battleDialogQuery);
|
||||||
|
sendAndApply(battleResult.data); //after this point casualties objects are destroyed
|
||||||
|
}
|
||||||
|
|
||||||
|
void CGameHandler::endBattleConfirm(const BattleInfo * battleInfo)
|
||||||
|
{
|
||||||
|
auto battleQuery = std::dynamic_pointer_cast<CBattleQuery>(queries.topQuery(battleInfo->sides.at(0).color));
|
||||||
|
if(!battleQuery)
|
||||||
|
{
|
||||||
|
logGlobal->trace("No battle query, battle end was confirmed by another player");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const CArmedInstance *bEndArmy1 = battleInfo->sides.at(0).armyObject;
|
||||||
|
const CArmedInstance *bEndArmy2 = battleInfo->sides.at(1).armyObject;
|
||||||
|
const BattleResult::EResult result = battleResult.get()->result;
|
||||||
|
|
||||||
|
CasualtiesAfterBattle cab1(bEndArmy1, battleInfo), cab2(bEndArmy2, battleInfo); //calculate casualties before deleting battle
|
||||||
ChangeSpells cs; //for Eagle Eye
|
ChangeSpells cs; //for Eagle Eye
|
||||||
|
|
||||||
if (finishingBattle->winnerHero)
|
if(!finishingBattle->isDraw() && finishingBattle->winnerHero)
|
||||||
{
|
{
|
||||||
if (int eagleEyeLevel = finishingBattle->winnerHero->valOfBonuses(Bonus::LEARN_BATTLE_SPELL_LEVEL_LIMIT, -1))
|
if (int eagleEyeLevel = finishingBattle->winnerHero->valOfBonuses(Bonus::LEARN_BATTLE_SPELL_LEVEL_LIMIT, -1))
|
||||||
{
|
{
|
||||||
double eagleEyeChance = finishingBattle->winnerHero->valOfBonuses(Bonus::LEARN_BATTLE_SPELL_CHANCE, 0);
|
double eagleEyeChance = finishingBattle->winnerHero->valOfBonuses(Bonus::LEARN_BATTLE_SPELL_CHANCE, 0);
|
||||||
for(auto & spellId : gs->curB->sides.at(!battleResult.data->winner).usedSpellsHistory)
|
for(auto & spellId : battleInfo->sides.at(!battleResult.data->winner).usedSpellsHistory)
|
||||||
{
|
{
|
||||||
auto spell = spellId.toSpell(VLC->spells());
|
auto spell = spellId.toSpell(VLC->spells());
|
||||||
if(spell && spell->getLevel() <= eagleEyeLevel && !finishingBattle->winnerHero->spellbookContainsSpell(spell->getId()) && getRandomGenerator().nextInt(99) < eagleEyeChance)
|
if(spell && spell->getLevel() <= eagleEyeLevel && !finishingBattle->winnerHero->spellbookContainsSpell(spell->getId()) && getRandomGenerator().nextInt(99) < eagleEyeChance)
|
||||||
@ -661,7 +673,7 @@ void CGameHandler::endBattle(int3 tile, const CGHeroInstance * heroAttacker, con
|
|||||||
}
|
}
|
||||||
std::vector<const CArtifactInstance *> arts; //display them in window
|
std::vector<const CArtifactInstance *> arts; //display them in window
|
||||||
|
|
||||||
if (result == BattleResult::NORMAL && finishingBattle->winnerHero)
|
if(result == BattleResult::NORMAL && !finishingBattle->isDraw() && finishingBattle->winnerHero)
|
||||||
{
|
{
|
||||||
auto sendMoveArtifact = [&](const CArtifactInstance *art, MoveArtifact *ma)
|
auto sendMoveArtifact = [&](const CArtifactInstance *art, MoveArtifact *ma)
|
||||||
{
|
{
|
||||||
@ -697,7 +709,7 @@ void CGameHandler::endBattle(int3 tile, const CGHeroInstance * heroAttacker, con
|
|||||||
//we assume that no big artifacts can be found
|
//we assume that no big artifacts can be found
|
||||||
MoveArtifact ma;
|
MoveArtifact ma;
|
||||||
ma.src = ArtifactLocation(finishingBattle->loserHero,
|
ma.src = ArtifactLocation(finishingBattle->loserHero,
|
||||||
ArtifactPosition(GameConstants::BACKPACK_START)); //backpack automatically shifts arts to beginning
|
ArtifactPosition(GameConstants::BACKPACK_START)); //backpack automatically shifts arts to beginning
|
||||||
const CArtifactInstance * art = ma.src.getArt();
|
const CArtifactInstance * art = ma.src.getArt();
|
||||||
if (art->artType->getId() != ArtifactID::GRAIL) //grail may not be won
|
if (art->artType->getId() != ArtifactID::GRAIL) //grail may not be won
|
||||||
{
|
{
|
||||||
@ -719,7 +731,7 @@ void CGameHandler::endBattle(int3 tile, const CGHeroInstance * heroAttacker, con
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (auto armySlot : gs->curB->battleGetArmyObject(!battleResult.data->winner)->stacks)
|
for (auto armySlot : battleInfo->sides.at(!battleResult.data->winner).armyObject->stacks)
|
||||||
{
|
{
|
||||||
auto artifactsWorn = armySlot.second->artifactsWorn;
|
auto artifactsWorn = armySlot.second->artifactsWorn;
|
||||||
for (auto artSlot : artifactsWorn)
|
for (auto artSlot : artifactsWorn)
|
||||||
@ -734,7 +746,6 @@ void CGameHandler::endBattle(int3 tile, const CGHeroInstance * heroAttacker, con
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sendAndApply(battleResult.data); //after this point casualties objects are destroyed
|
|
||||||
|
|
||||||
if (arts.size()) //display loot
|
if (arts.size()) //display loot
|
||||||
{
|
{
|
||||||
@ -797,35 +808,40 @@ void CGameHandler::endBattle(int3 tile, const CGHeroInstance * heroAttacker, con
|
|||||||
cab1.updateArmy(this);
|
cab1.updateArmy(this);
|
||||||
cab2.updateArmy(this); //take casualties after battle is deleted
|
cab2.updateArmy(this); //take casualties after battle is deleted
|
||||||
|
|
||||||
if(battleResult.data->winner != BattleSide::ATTACKER && heroAttacker) //remove beaten Attacker
|
if(finishingBattle->loserHero) //remove beaten hero
|
||||||
{
|
{
|
||||||
RemoveObject ro(heroAttacker->id);
|
RemoveObject ro(finishingBattle->loserHero->id);
|
||||||
sendAndApply(&ro);
|
sendAndApply(&ro);
|
||||||
}
|
}
|
||||||
|
if(finishingBattle->isDraw() && finishingBattle->winnerHero) //for draw case both heroes should be removed
|
||||||
if(battleResult.data->winner != BattleSide::DEFENDER && heroDefender) //remove beaten Defender
|
|
||||||
{
|
{
|
||||||
RemoveObject ro(heroDefender->id);
|
RemoveObject ro(finishingBattle->winnerHero->id);
|
||||||
sendAndApply(&ro);
|
sendAndApply(&ro);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(battleResult.data->winner == BattleSide::DEFENDER
|
if(battleResult.data->winner == BattleSide::DEFENDER
|
||||||
&& heroDefender
|
&& finishingBattle->winnerHero
|
||||||
&& heroDefender->visitedTown
|
&& finishingBattle->winnerHero->visitedTown
|
||||||
&& !heroDefender->inTownGarrison
|
&& !finishingBattle->winnerHero->inTownGarrison
|
||||||
&& heroDefender->visitedTown->garrisonHero == heroDefender)
|
&& finishingBattle->winnerHero->visitedTown->garrisonHero == finishingBattle->winnerHero)
|
||||||
{
|
{
|
||||||
swapGarrisonOnSiege(heroDefender->visitedTown->id); //return defending visitor from garrison to its rightful place
|
swapGarrisonOnSiege(finishingBattle->winnerHero->visitedTown->id); //return defending visitor from garrison to its rightful place
|
||||||
}
|
}
|
||||||
//give exp
|
//give exp
|
||||||
if(battleResult.data->exp[0] && heroAttacker && battleResult.get()->winner == BattleSide::ATTACKER)
|
if(!finishingBattle->isDraw() && battleResult.data->exp[finishingBattle->winnerSide] && finishingBattle->winnerHero)
|
||||||
changePrimSkill(heroAttacker, PrimarySkill::EXPERIENCE, battleResult.data->exp[0]);
|
changePrimSkill(finishingBattle->winnerHero, PrimarySkill::EXPERIENCE, battleResult.data->exp[finishingBattle->winnerSide]);
|
||||||
else if(battleResult.data->exp[1] && heroDefender && battleResult.get()->winner == BattleSide::DEFENDER)
|
|
||||||
changePrimSkill(heroDefender, PrimarySkill::EXPERIENCE, battleResult.data->exp[1]);
|
BattleResultAccepted raccepted;
|
||||||
|
raccepted.army1 = const_cast<CArmedInstance*>(bEndArmy1);
|
||||||
|
raccepted.army2 = const_cast<CArmedInstance*>(bEndArmy2);
|
||||||
|
raccepted.hero1 = const_cast<CGHeroInstance*>(battleInfo->sides.at(0).hero);
|
||||||
|
raccepted.hero2 = const_cast<CGHeroInstance*>(battleInfo->sides.at(1).hero);
|
||||||
|
raccepted.exp[0] = battleResult.data->exp[0];
|
||||||
|
raccepted.exp[1] = battleResult.data->exp[1];
|
||||||
|
sendAndApply(&raccepted);
|
||||||
|
|
||||||
queries.popIfTop(battleQuery);
|
queries.popIfTop(battleQuery);
|
||||||
|
//--> continuation (battleAfterLevelUp) occurs after level-up queries are handled or on removing query
|
||||||
//--> continuation (battleAfterLevelUp) occurs after level-up queries are handled or on removing query (above)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CGameHandler::battleAfterLevelUp(const BattleResult &result)
|
void CGameHandler::battleAfterLevelUp(const BattleResult &result)
|
||||||
|
@ -409,6 +409,28 @@ void CBattleDialogQuery::onRemoval(PlayerColor color)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CBattleDialogQuery::CBattleDialogQuery(CGameHandler * owner, const BattleInfo * Bi):
|
||||||
|
CDialogQuery(owner)
|
||||||
|
{
|
||||||
|
bi = Bi;
|
||||||
|
|
||||||
|
for(auto & side : bi->sides)
|
||||||
|
addPlayer(side.color);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CBattleDialogQuery::onRemoval(PlayerColor color)
|
||||||
|
{
|
||||||
|
assert(answer);
|
||||||
|
if(*answer == 1)
|
||||||
|
{
|
||||||
|
gh->startBattlePrimary(bi->sides[0].armyObject, bi->sides[1].armyObject, bi->tile, bi->sides[0].hero, bi->sides[1].hero, bi->creatureBank, bi->town);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
gh->endBattleConfirm(bi);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void CBlockingDialogQuery::notifyObjectAboutRemoval(const CObjectVisitQuery & objectVisit) const
|
void CBlockingDialogQuery::notifyObjectAboutRemoval(const CObjectVisitQuery & objectVisit) const
|
||||||
{
|
{
|
||||||
assert(answer);
|
assert(answer);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user