1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-02-03 13:01:33 +02:00

Merge pull request #4186 from SoundSSGood/end-of-battle-artifacts-transfer

End of battle artifacts transfer
This commit is contained in:
Ivan Savenko 2024-07-16 12:45:13 +03:00 committed by GitHub
commit ad9750ed3e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
30 changed files with 454 additions and 457 deletions

View File

@ -173,9 +173,9 @@ bool CCallback::swapArtifacts(const ArtifactLocation &l1, const ArtifactLocation
* @param assembleTo If assemble is true, this represents the artifact ID of the combination
* artifact to assemble to. Otherwise it's not used.
*/
void CCallback::assembleArtifacts(const CGHeroInstance * hero, ArtifactPosition artifactSlot, bool assemble, ArtifactID assembleTo)
void CCallback::assembleArtifacts(const ObjectInstanceID & heroID, ArtifactPosition artifactSlot, bool assemble, ArtifactID assembleTo)
{
AssembleArtifacts aa(hero->id, artifactSlot, assemble, assembleTo);
AssembleArtifacts aa(heroID, artifactSlot, assemble, assembleTo);
sendRequest(&aa);
}

View File

@ -93,7 +93,7 @@ public:
virtual bool swapArtifacts(const ArtifactLocation &l1, const ArtifactLocation &l2)=0;
virtual void scrollBackpackArtifacts(ObjectInstanceID hero, bool left) = 0;
virtual void manageHeroCostume(ObjectInstanceID hero, size_t costumeIndex, bool saveCostume) = 0;
virtual void assembleArtifacts(const CGHeroInstance * hero, ArtifactPosition artifactSlot, bool assemble, ArtifactID assembleTo)=0;
virtual void assembleArtifacts(const ObjectInstanceID & heroID, ArtifactPosition artifactSlot, bool assemble, ArtifactID assembleTo)=0;
virtual void eraseArtifactByClient(const ArtifactLocation & al)=0;
virtual bool dismissCreature(const CArmedInstance *obj, SlotID stackPos)=0;
virtual void endTurn()=0;
@ -176,7 +176,7 @@ public:
int bulkMergeStacks(ObjectInstanceID armyId, SlotID srcSlot) override;
bool dismissHero(const CGHeroInstance * hero) override;
bool swapArtifacts(const ArtifactLocation &l1, const ArtifactLocation &l2) override;
void assembleArtifacts(const CGHeroInstance * hero, ArtifactPosition artifactSlot, bool assemble, ArtifactID assembleTo) override;
void assembleArtifacts(const ObjectInstanceID & heroID, ArtifactPosition artifactSlot, bool assemble, ArtifactID assembleTo) override;
void bulkMoveArtifacts(ObjectInstanceID srcHero, ObjectInstanceID dstHero, bool swap, bool equipped = true, bool backpack = true) override;
void scrollBackpackArtifacts(ObjectInstanceID hero, bool left) override;
void manageHeroCostume(ObjectInstanceID hero, size_t costumeIdx, bool saveCostume) override;

View File

@ -0,0 +1,166 @@
/*
* ArtifactsUIController.cpp, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#include "StdInc.h"
#include "ArtifactsUIController.h"
#include "CGameInfo.h"
#include "CPlayerInterface.h"
#include "../CCallback.h"
#include "../lib/ArtifactUtils.h"
#include "../lib/CGeneralTextHandler.h"
#include "../lib/mapObjects/CGHeroInstance.h"
#include "gui/CGuiHandler.h"
#include "gui/WindowHandler.h"
#include "widgets/CComponent.h"
#include "windows/CWindowWithArtifacts.h"
ArtifactsUIController::ArtifactsUIController()
{
numOfMovedArts = 0;
}
bool ArtifactsUIController::askToAssemble(const ArtifactLocation & al, const bool onlyEquipped, const bool checkIgnored)
{
if(auto hero = LOCPLINT->cb->getHero(al.artHolder))
{
if(hero->getArt(al.slot) == nullptr)
{
logGlobal->error("artifact location %d points to nothing", al.slot.num);
return false;
}
return askToAssemble(hero, al.slot, onlyEquipped, checkIgnored);
}
return false;
}
bool ArtifactsUIController::askToAssemble(const CGHeroInstance * hero, const ArtifactPosition & slot,
const bool onlyEquipped, const bool checkIgnored)
{
assert(hero);
const auto art = hero->getArt(slot);
assert(art);
if(hero->tempOwner != LOCPLINT->playerID)
return false;
if(numOfArtsAskAssembleSession != 0)
numOfArtsAskAssembleSession--;
auto assemblyPossibilities = ArtifactUtils::assemblyPossibilities(hero, art->getTypeId(), onlyEquipped);
if(!assemblyPossibilities.empty())
{
auto askThread = new boost::thread([this, hero, art, slot, assemblyPossibilities, checkIgnored]() -> void
{
boost::mutex::scoped_lock askLock(askAssembleArtifactMutex);
for(const auto combinedArt : assemblyPossibilities)
{
boost::mutex::scoped_lock interfaceLock(GH.interfaceMutex);
if(checkIgnored)
{
if(vstd::contains(ignoredArtifacts, combinedArt->getId()))
continue;
ignoredArtifacts.emplace(combinedArt->getId());
}
bool assembleConfirmed = false;
MetaString message = MetaString::createFromTextID(art->artType->getDescriptionTextID());
message.appendEOL();
message.appendEOL();
message.appendRawString(CGI->generaltexth->allTexts[732]); // You possess all of the components needed to assemble the
message.replaceName(ArtifactID(combinedArt->getId()));
LOCPLINT->showYesNoDialog(message.toString(), [&assembleConfirmed, hero, slot, combinedArt]()
{
assembleConfirmed = true;
LOCPLINT->cb.get()->assembleArtifacts(hero->id, slot, true, combinedArt->getId());
}, nullptr, {std::make_shared<CComponent>(ComponentType::ARTIFACT, combinedArt->getId())});
LOCPLINT->waitWhileDialog();
if(assembleConfirmed)
break;
}
});
askThread->detach();
return true;
}
return false;
}
bool ArtifactsUIController::askToDisassemble(const CGHeroInstance * hero, const ArtifactPosition & slot)
{
assert(hero);
const auto art = hero->getArt(slot);
assert(art);
if(hero->tempOwner != LOCPLINT->playerID)
return false;
if(art->isCombined())
{
if(ArtifactUtils::isSlotBackpack(slot) && !ArtifactUtils::isBackpackFreeSlots(hero, art->artType->getConstituents().size() - 1))
return false;
MetaString message = MetaString::createFromTextID(art->artType->getDescriptionTextID());
message.appendEOL();
message.appendEOL();
message.appendRawString(CGI->generaltexth->allTexts[733]); // Do you wish to disassemble this artifact?
LOCPLINT->showYesNoDialog(message.toString(), [hero, slot]()
{
LOCPLINT->cb->assembleArtifacts(hero->id, slot, false, ArtifactID());
}, nullptr);
return true;
}
return false;
}
void ArtifactsUIController::artifactRemoved()
{
for(const auto & artWin : GH.windows().findWindows<CWindowWithArtifacts>())
artWin->update();
LOCPLINT->waitWhileDialog();
}
void ArtifactsUIController::artifactMoved()
{
// If a bulk transfer has arrived, then redrawing only the last art movement.
if(numOfMovedArts != 0)
numOfMovedArts--;
if(numOfMovedArts == 0)
for(const auto & artWin : GH.windows().findWindows<CWindowWithArtifacts>())
{
artWin->update();
}
LOCPLINT->waitWhileDialog();
}
void ArtifactsUIController::bulkArtMovementStart(size_t totalNumOfArts, size_t possibleAssemblyNumOfArts)
{
assert(totalNumOfArts >= possibleAssemblyNumOfArts);
numOfMovedArts = totalNumOfArts;
if(numOfArtsAskAssembleSession == 0)
{
// Do not start the next session until the previous one is finished
numOfArtsAskAssembleSession = possibleAssemblyNumOfArts;
ignoredArtifacts.clear();
}
}
void ArtifactsUIController::artifactAssembled()
{
for(const auto & artWin : GH.windows().findWindows<CWindowWithArtifacts>())
artWin->update();
}
void ArtifactsUIController::artifactDisassembled()
{
for(const auto & artWin : GH.windows().findWindows<CWindowWithArtifacts>())
artWin->update();
}

View File

@ -0,0 +1,42 @@
/*
* ArtifactsUIController.h, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#pragma once
#include "../lib/constants/EntityIdentifiers.h"
#include "../lib/networkPacks/ArtifactLocation.h"
VCMI_LIB_NAMESPACE_BEGIN
class CGHeroInstance;
VCMI_LIB_NAMESPACE_END
class ArtifactsUIController
{
size_t numOfMovedArts;
size_t numOfArtsAskAssembleSession;
std::set<ArtifactID> ignoredArtifacts;
boost::mutex askAssembleArtifactMutex;
public:
ArtifactsUIController();
bool askToAssemble(const ArtifactLocation & al, const bool onlyEquipped = false, const bool checkIgnored = false);
bool askToAssemble(const CGHeroInstance * hero, const ArtifactPosition & slot, const bool onlyEquipped = false,
const bool checkIgnored = false);
bool askToDisassemble(const CGHeroInstance * hero, const ArtifactPosition & slot);
void artifactRemoved();
void artifactMoved();
void bulkArtMovementStart(size_t totalNumOfArts, size_t possibleAssemblyNumOfArts);
void artifactAssembled();
void artifactDisassembled();
};

View File

@ -168,6 +168,7 @@ set(client_SRCS
windows/settings/BattleOptionsTab.cpp
windows/settings/AdventureOptionsTab.cpp
ArtifactsUIController.cpp
CGameInfo.cpp
CMT.cpp
CPlayerInterface.cpp
@ -371,6 +372,7 @@ set(client_HEADERS
windows/settings/BattleOptionsTab.h
windows/settings/AdventureOptionsTab.h
ArtifactsUIController.h
CGameInfo.h
CMT.h
CPlayerInterface.h

View File

@ -66,7 +66,6 @@
#include "../CCallback.h"
#include "../lib/CArtHandler.h"
#include "../lib/CConfigHandler.h"
#include "../lib/CGeneralTextHandler.h"
#include "../lib/CHeroHandler.h"
@ -132,7 +131,9 @@ struct HeroObjectRetriever
CPlayerInterface::CPlayerInterface(PlayerColor Player):
localState(std::make_unique<PlayerLocalState>(*this)),
movementController(std::make_unique<HeroMovementController>())
movementController(std::make_unique<HeroMovementController>()),
artifactController(std::make_unique<ArtifactsUIController>())
{
logGlobal->trace("\tHuman player interface for player %s being constructed", Player.toString());
GH.defActionsDef = 0;
@ -148,7 +149,6 @@ CPlayerInterface::CPlayerInterface(PlayerColor Player):
isAutoFightOn = false;
isAutoFightEndBattle = false;
ignoreEvents = false;
numOfMovedArts = 0;
}
CPlayerInterface::~CPlayerInterface()
@ -1252,35 +1252,6 @@ void CPlayerInterface::showGarrisonDialog( const CArmedInstance *up, const CGHer
GH.windows().pushWindow(cgw);
}
/**
* Shows the dialog that appears when right-clicking an artifact that can be assembled
* into a combinational one on an artifact screen. Does not require the combination of
* artifacts to be legal.
*/
void CPlayerInterface::showArtifactAssemblyDialog(const Artifact * artifact, const Artifact * assembledArtifact, CFunctionList<void()> onYes)
{
std::string text = artifact->getDescriptionTranslated();
text += "\n\n";
std::vector<std::shared_ptr<CComponent>> scs;
if(assembledArtifact)
{
// You possess all of the components to...
text += boost::str(boost::format(CGI->generaltexth->allTexts[732]) % assembledArtifact->getNameTranslated());
// Picture of assembled artifact at bottom.
auto sc = std::make_shared<CComponent>(ComponentType::ARTIFACT, assembledArtifact->getId());
scs.push_back(sc);
}
else
{
// Do you wish to disassemble this artifact?
text += CGI->generaltexth->allTexts[733];
}
showYesNoDialog(text, onYes, nullptr, scs);
}
void CPlayerInterface::requestRealized( PackageApplied *pa )
{
if(pa->packType == CTypeList::getInstance().getTypeID<MoveHero>(nullptr))
@ -1738,17 +1709,7 @@ void CPlayerInterface::showShipyardDialogOrProblemPopup(const IShipyard *obj)
void CPlayerInterface::askToAssembleArtifact(const ArtifactLocation &al)
{
if(auto hero = cb->getHero(al.artHolder))
{
auto art = hero->getArt(al.slot);
if(art == nullptr)
{
logGlobal->error("artifact location %d points to nothing",
al.slot.num);
return;
}
ArtifactUtilsClient::askToAssemble(hero, al.slot);
}
artifactController->askToAssemble(al, true, true);
}
void CPlayerInterface::artifactPut(const ArtifactLocation &al)
@ -1761,55 +1722,33 @@ void CPlayerInterface::artifactRemoved(const ArtifactLocation &al)
{
EVENT_HANDLER_CALLED_BY_CLIENT;
adventureInt->onHeroChanged(cb->getHero(al.artHolder));
for(auto artWin : GH.windows().findWindows<CWindowWithArtifacts>())
artWin->artifactRemoved(al);
waitWhileDialog();
artifactController->artifactRemoved();
}
void CPlayerInterface::artifactMoved(const ArtifactLocation &src, const ArtifactLocation &dst)
{
EVENT_HANDLER_CALLED_BY_CLIENT;
adventureInt->onHeroChanged(cb->getHero(dst.artHolder));
// If a bulk transfer has arrived, then redrawing only the last art movement.
if(numOfMovedArts != 0)
numOfMovedArts--;
for(auto artWin : GH.windows().findWindows<CWindowWithArtifacts>())
{
artWin->artifactMoved(src, dst);
if(numOfMovedArts == 0)
{
artWin->update();
artWin->redraw();
}
}
waitWhileDialog();
artifactController->artifactMoved();
}
void CPlayerInterface::bulkArtMovementStart(size_t numOfArts)
void CPlayerInterface::bulkArtMovementStart(size_t totalNumOfArts, size_t possibleAssemblyNumOfArts)
{
numOfMovedArts = numOfArts;
artifactController->bulkArtMovementStart(totalNumOfArts, possibleAssemblyNumOfArts);
}
void CPlayerInterface::artifactAssembled(const ArtifactLocation &al)
{
EVENT_HANDLER_CALLED_BY_CLIENT;
adventureInt->onHeroChanged(cb->getHero(al.artHolder));
for(auto artWin : GH.windows().findWindows<CWindowWithArtifacts>())
artWin->artifactAssembled(al);
artifactController->artifactAssembled();
}
void CPlayerInterface::artifactDisassembled(const ArtifactLocation &al)
{
EVENT_HANDLER_CALLED_BY_CLIENT;
adventureInt->onHeroChanged(cb->getHero(al.artHolder));
for(auto artWin : GH.windows().findWindows<CWindowWithArtifacts>())
artWin->artifactDisassembled(al);
artifactController->artifactDisassembled();
}
void CPlayerInterface::waitForAllDialogs()

View File

@ -9,6 +9,8 @@
*/
#pragma once
#include "ArtifactsUIController.h"
#include "../lib/FunctionList.h"
#include "../lib/CGameInterface.h"
#include "gui/CIntObject.h"
@ -16,9 +18,7 @@
VCMI_LIB_NAMESPACE_BEGIN
class Artifact;
struct TryMoveHero;
class CGHeroInstance;
class CStack;
class CCreature;
struct CGPath;
@ -59,14 +59,13 @@ namespace boost
class CPlayerInterface : public CGameInterface, public IUpdateable
{
bool ignoreEvents;
size_t numOfMovedArts;
int autosaveCount;
std::list<std::shared_ptr<CInfoWindow>> dialogs; //queue of dialogs awaiting to be shown (not currently shown!)
std::unique_ptr<HeroMovementController> movementController;
public: // TODO: make private
std::unique_ptr<ArtifactsUIController> artifactController;
std::shared_ptr<Environment> env;
std::unique_ptr<PlayerLocalState> localState;
@ -98,7 +97,7 @@ protected: // Call-ins from server, should not be called directly, but only via
void artifactPut(const ArtifactLocation &al) override;
void artifactRemoved(const ArtifactLocation &al) override;
void artifactMoved(const ArtifactLocation &src, const ArtifactLocation &dst) override;
void bulkArtMovementStart(size_t numOfArts) override;
void bulkArtMovementStart(size_t totalNumOfArts, size_t possibleAssemblyNumOfArts) override;
void artifactAssembled(const ArtifactLocation &al) override;
void askToAssembleArtifact(const ArtifactLocation & dst) override;
void artifactDisassembled(const ArtifactLocation &al) override;
@ -180,7 +179,6 @@ public: // public interface for use by client via LOCPLINT access
void showShipyardDialog(const IShipyard *obj) override; //obj may be town or shipyard;
void showHeroExchange(ObjectInstanceID hero1, ObjectInstanceID hero2);
void showArtifactAssemblyDialog(const Artifact * artifact, const Artifact * assembledArtifact, CFunctionList<void()> onYes);
void waitWhileDialog();
void waitForAllDialogs();
void openTownWindow(const CGTownInstance * town); //shows townscreen

View File

@ -48,7 +48,6 @@ public:
void visitBulkSmartRebalanceStacks(BulkSmartRebalanceStacks & pack) override;
void visitPutArtifact(PutArtifact & pack) override;
void visitEraseArtifact(EraseArtifact & pack) override;
void visitMoveArtifact(MoveArtifact & pack) override;
void visitBulkMoveArtifacts(BulkMoveArtifacts & pack) override;
void visitAssembledArtifact(AssembledArtifact & pack) override;
void visitDisassembledArtifact(DisassembledArtifact & pack) override;

View File

@ -296,45 +296,45 @@ void ApplyClientNetPackVisitor::visitEraseArtifact(EraseArtifact & pack)
callInterfaceIfPresent(cl, cl.getOwner(pack.al.artHolder), &IGameEventsReceiver::artifactRemoved, pack.al);
}
void ApplyClientNetPackVisitor::visitMoveArtifact(MoveArtifact & pack)
{
auto moveArtifact = [this, &pack](PlayerColor player) -> void
{
callInterfaceIfPresent(cl, player, &IGameEventsReceiver::artifactMoved, pack.src, pack.dst);
if(pack.askAssemble)
callInterfaceIfPresent(cl, player, &IGameEventsReceiver::askToAssembleArtifact, pack.dst);
};
moveArtifact(pack.interfaceOwner);
if(pack.interfaceOwner != cl.getOwner(pack.dst.artHolder))
moveArtifact(cl.getOwner(pack.dst.artHolder));
cl.invalidatePaths(); // hero might have equipped/unequipped Angel Wings
}
void ApplyClientNetPackVisitor::visitBulkMoveArtifacts(BulkMoveArtifacts & pack)
{
auto applyMove = [this, &pack](std::vector<BulkMoveArtifacts::LinkedSlots> & artsPack) -> void
const auto dstOwner = cl.getOwner(pack.dstArtHolder);
const auto applyMove = [this, &pack, dstOwner](std::vector<BulkMoveArtifacts::LinkedSlots> & artsPack)
{
for(auto & slotToMove : artsPack)
for(const auto & slotToMove : artsPack)
{
auto srcLoc = ArtifactLocation(pack.srcArtHolder, slotToMove.srcPos);
auto dstLoc = ArtifactLocation(pack.dstArtHolder, slotToMove.dstPos);
MoveArtifact ma(pack.interfaceOwner, srcLoc, dstLoc, pack.askAssemble);
visitMoveArtifact(ma);
const auto srcLoc = ArtifactLocation(pack.srcArtHolder, slotToMove.srcPos);
const auto dstLoc = ArtifactLocation(pack.dstArtHolder, slotToMove.dstPos);
callInterfaceIfPresent(cl, pack.interfaceOwner, &IGameEventsReceiver::artifactMoved, srcLoc, dstLoc);
if(slotToMove.askAssemble)
callInterfaceIfPresent(cl, pack.interfaceOwner, &IGameEventsReceiver::askToAssembleArtifact, dstLoc);
if(pack.interfaceOwner != dstOwner)
callInterfaceIfPresent(cl, dstOwner, &IGameEventsReceiver::artifactMoved, srcLoc, dstLoc);
cl.invalidatePaths(); // hero might have equipped/unequipped Angel Wings
}
};
auto srcOwner = cl.getOwner(pack.srcArtHolder);
auto dstOwner = cl.getOwner(pack.dstArtHolder);
size_t possibleAssemblyNumOfArts = 0;
const auto calcPossibleAssemblyNumOfArts = [&possibleAssemblyNumOfArts](const auto & slotToMove)
{
if(slotToMove.askAssemble)
possibleAssemblyNumOfArts++;
};
std::for_each(pack.artsPack0.cbegin(), pack.artsPack0.cend(), calcPossibleAssemblyNumOfArts);
std::for_each(pack.artsPack1.cbegin(), pack.artsPack1.cend(), calcPossibleAssemblyNumOfArts);
// Begin a session of bulk movement of arts. It is not necessary but useful for the client optimization.
callInterfaceIfPresent(cl, srcOwner, &IGameEventsReceiver::bulkArtMovementStart, pack.artsPack0.size() + pack.artsPack1.size());
if(srcOwner != dstOwner)
callInterfaceIfPresent(cl, dstOwner, &IGameEventsReceiver::bulkArtMovementStart, pack.artsPack0.size() + pack.artsPack1.size());
callInterfaceIfPresent(cl, pack.interfaceOwner, &IGameEventsReceiver::bulkArtMovementStart,
pack.artsPack0.size() + pack.artsPack1.size(), possibleAssemblyNumOfArts);
if(pack.interfaceOwner != dstOwner)
callInterfaceIfPresent(cl, dstOwner, &IGameEventsReceiver::bulkArtMovementStart,
pack.artsPack0.size() + pack.artsPack1.size(), possibleAssemblyNumOfArts);
applyMove(pack.artsPack0);
if(pack.swap)
if(!pack.artsPack1.empty())
applyMove(pack.artsPack1);
}

View File

@ -245,59 +245,3 @@ void CArtPlace::addCombinedArtInfo(const std::map<const ArtifactID, std::vector<
text += info.toString();
}
}
bool ArtifactUtilsClient::askToAssemble(const CGHeroInstance * hero, const ArtifactPosition & slot)
{
assert(hero);
const auto art = hero->getArt(slot);
assert(art);
if(hero->tempOwner != LOCPLINT->playerID)
return false;
auto assemblyPossibilities = ArtifactUtils::assemblyPossibilities(hero, art->getTypeId());
if(!assemblyPossibilities.empty())
{
auto askThread = new boost::thread([hero, art, slot, assemblyPossibilities]() -> void
{
boost::mutex::scoped_lock interfaceLock(GH.interfaceMutex);
for(const auto combinedArt : assemblyPossibilities)
{
bool assembleConfirmed = false;
CFunctionList<void()> onYesHandlers([&assembleConfirmed]() -> void {assembleConfirmed = true; });
onYesHandlers += std::bind(&CCallback::assembleArtifacts, LOCPLINT->cb.get(), hero, slot, true, combinedArt->getId());
LOCPLINT->showArtifactAssemblyDialog(art->artType, combinedArt, onYesHandlers);
LOCPLINT->waitWhileDialog();
if(assembleConfirmed)
break;
}
});
askThread->detach();
return true;
}
return false;
}
bool ArtifactUtilsClient::askToDisassemble(const CGHeroInstance * hero, const ArtifactPosition & slot)
{
assert(hero);
const auto art = hero->getArt(slot);
assert(art);
if(hero->tempOwner != LOCPLINT->playerID)
return false;
if(art->isCombined())
{
if(ArtifactUtils::isSlotBackpack(slot) && !ArtifactUtils::isBackpackFreeSlots(hero, art->artType->getConstituents().size() - 1))
return false;
LOCPLINT->showArtifactAssemblyDialog(
art->artType,
nullptr,
std::bind(&CCallback::assembleArtifacts, LOCPLINT->cb.get(), hero, slot, false, ArtifactID()));
return true;
}
return false;
}

View File

@ -59,9 +59,3 @@ public:
void clickPressed(const Point & cursorPosition) override;
void showPopupWindow(const Point & cursorPosition) override;
};
namespace ArtifactUtilsClient
{
bool askToAssemble(const CGHeroInstance * hero, const ArtifactPosition & slot);
bool askToDisassemble(const CGHeroInstance * hero, const ArtifactPosition & slot);
}

View File

@ -176,9 +176,9 @@ void CArtifactsOfHeroQuickBackpack::setHero(const CGHeroInstance * hero)
initAOHbackpack(requiredSlots, false);
auto artPlace = backpack.begin();
for(auto & art : filteredArts)
setSlotData(*artPlace++, curHero->getSlotByInstance(art.second));
setSlotData(*artPlace++, curHero->getArtPos(art.second));
for(auto & art : filteredScrolls)
setSlotData(*artPlace++, curHero->getSlotByInstance(art.second));
setSlotData(*artPlace++, curHero->getArtPos(art.second));
}
}

View File

@ -146,7 +146,7 @@ void CAltarArtifacts::updateAltarSlots()
for(auto & tradeSlot : tradeSlotsMapNewArts)
{
assert(tradeSlot.first->id == -1);
assert(altarArtifacts->getSlotByInstance(tradeSlot.second) != ArtifactPosition::PRE_FIRST);
assert(altarArtifacts->getArtPos(tradeSlot.second) != ArtifactPosition::PRE_FIRST);
tradeSlot.first->setID(tradeSlot.second->getTypeId().num);
tradeSlot.first->subtitle->setText(std::to_string(calcExpCost(tradeSlot.second->getTypeId())));
}
@ -221,7 +221,7 @@ void CAltarArtifacts::onSlotClickPressed(const std::shared_ptr<CTradeableItem> &
else if(altarSlot->id != -1)
{
assert(tradeSlotsMap.at(altarSlot));
const auto slot = altarArtifacts->getSlotByInstance(tradeSlotsMap.at(altarSlot));
const auto slot = altarArtifacts->getArtPos(tradeSlotsMap.at(altarSlot));
assert(slot != ArtifactPosition::PRE_FIRST);
LOCPLINT->cb->swapArtifacts(ArtifactLocation(heroArts->altarId, slot),
ArtifactLocation(hero->id, GH.isKeyboardCtrlDown() ? ArtifactPosition::FIRST_AVAILABLE : ArtifactPosition::TRANSITION_POS));

View File

@ -117,9 +117,9 @@ void CWindowWithArtifacts::showArtifactAssembling(const CArtifactsOfHeroBase & a
{
if(artsInst.getArt(artPlace.slot))
{
if(ArtifactUtilsClient::askToDisassemble(artsInst.getHero(), artPlace.slot))
if(LOCPLINT->artifactController->askToDisassemble(artsInst.getHero(), artPlace.slot))
return;
if(ArtifactUtilsClient::askToAssemble(artsInst.getHero(), artPlace.slot))
if(LOCPLINT->artifactController->askToAssemble(artsInst.getHero(), artPlace.slot))
return;
if(artPlace.text.size())
artPlace.LRClickableAreaWTextComp::showPopupWindow(cursorPosition);
@ -166,14 +166,13 @@ void CWindowWithArtifacts::enableKeyboardShortcuts() const
artSet->enableKeyboardShortcuts();
}
void CWindowWithArtifacts::artifactRemoved(const ArtifactLocation & artLoc)
{
update();
}
void CWindowWithArtifacts::artifactMoved(const ArtifactLocation & srcLoc, const ArtifactLocation & destLoc)
void CWindowWithArtifacts::update()
{
for(const auto & artSet : artSets)
{
artSet->updateWornSlots();
artSet->updateBackpackSlots();
if(const auto pickedArtInst = getPickedArtifact())
{
markPossibleSlots();
@ -184,30 +183,12 @@ void CWindowWithArtifacts::artifactMoved(const ArtifactLocation & srcLoc, const
artSet->unmarkSlots();
CCS->curh->dragAndDropCursor(nullptr);
}
}
void CWindowWithArtifacts::artifactDisassembled(const ArtifactLocation & artLoc)
{
update();
}
void CWindowWithArtifacts::artifactAssembled(const ArtifactLocation & artLoc)
{
markPossibleSlots();
update();
}
void CWindowWithArtifacts::update()
{
for(const auto & artSet : artSets)
{
artSet->updateWornSlots();
artSet->updateBackpackSlots();
// Make sure the status bar is updated so it does not display old text
if(auto artPlace = artSet->getArtPlace(GH.getCursorPosition()))
artPlace->hover(true);
}
redraw();
}
void CWindowWithArtifacts::markPossibleSlots() const

View File

@ -37,10 +37,6 @@ public:
void deactivate() override;
void enableKeyboardShortcuts() const;
virtual void artifactRemoved(const ArtifactLocation & artLoc);
virtual void artifactMoved(const ArtifactLocation & srcLoc, const ArtifactLocation & destLoc);
virtual void artifactDisassembled(const ArtifactLocation & artLoc);
virtual void artifactAssembled(const ArtifactLocation & artLoc);
virtual void update();
protected:

View File

@ -196,7 +196,7 @@ DLL_LINKAGE bool ArtifactUtils::isBackpackFreeSlots(const CArtifactSet * target,
}
DLL_LINKAGE std::vector<const CArtifact*> ArtifactUtils::assemblyPossibilities(
const CArtifactSet * artSet, const ArtifactID & aid)
const CArtifactSet * artSet, const ArtifactID & aid, const bool onlyEquiped)
{
std::vector<const CArtifact*> arts;
const auto * art = aid.toArtifact();
@ -210,7 +210,7 @@ DLL_LINKAGE std::vector<const CArtifact*> ArtifactUtils::assemblyPossibilities(
for(const auto constituent : artifact->getConstituents()) //check if all constituents are available
{
if(!artSet->hasArt(constituent->getId(), false, false, false))
if(!artSet->hasArt(constituent->getId(), onlyEquiped, false, false))
{
possible = false;
break;

View File

@ -39,7 +39,7 @@ namespace ArtifactUtils
DLL_LINKAGE bool isSlotBackpack(const ArtifactPosition & slot);
DLL_LINKAGE bool isSlotEquipment(const ArtifactPosition & slot);
DLL_LINKAGE bool isBackpackFreeSlots(const CArtifactSet * target, const size_t reqSlots = 1);
DLL_LINKAGE std::vector<const CArtifact*> assemblyPossibilities(const CArtifactSet * artSet, const ArtifactID & aid);
DLL_LINKAGE std::vector<const CArtifact*> assemblyPossibilities(const CArtifactSet * artSet, const ArtifactID & aid, const bool onlyEquiped = false);
DLL_LINKAGE CArtifactInstance * createScroll(const SpellID & sid);
DLL_LINKAGE CArtifactInstance * createNewArtifactInstance(const CArtifact * art);
DLL_LINKAGE CArtifactInstance * createNewArtifactInstance(const ArtifactID & aid);

View File

@ -741,19 +741,6 @@ std::vector<ArtifactPosition> CArtifactSet::getBackpackArtPositions(const Artifa
return result;
}
ArtifactPosition CArtifactSet::getArtPos(const CArtifactInstance *art) const
{
for(auto i : artifactsWorn)
if(i.second.artifact == art)
return i.first;
for(int i = 0; i < artifactsInBackpack.size(); i++)
if(artifactsInBackpack[i].artifact == art)
return ArtifactPosition::BACKPACK_START + i;
return ArtifactPosition::PRE_FIRST;
}
const CArtifactInstance * CArtifactSet::getArtByInstanceId(const ArtifactInstanceID & artInstId) const
{
for(auto i : artifactsWorn)
@ -767,7 +754,7 @@ const CArtifactInstance * CArtifactSet::getArtByInstanceId(const ArtifactInstanc
return nullptr;
}
const ArtifactPosition CArtifactSet::getSlotByInstance(const CArtifactInstance * artInst) const
const ArtifactPosition CArtifactSet::getArtPos(const CArtifactInstance * artInst) const
{
if(artInst)
{
@ -1079,8 +1066,8 @@ void CArtifactSet::serializeJsonSlot(JsonSerializeFormat & handler, const Artifa
}
}
CArtifactFittingSet::CArtifactFittingSet(ArtBearer::ArtBearer Bearer):
Bearer(Bearer)
CArtifactFittingSet::CArtifactFittingSet(ArtBearer::ArtBearer bearer)
: bearer(bearer)
{
}
@ -1094,7 +1081,7 @@ CArtifactFittingSet::CArtifactFittingSet(const CArtifactSet & artSet)
ArtBearer::ArtBearer CArtifactFittingSet::bearerType() const
{
return this->Bearer;
return this->bearer;
}
VCMI_LIB_NAMESPACE_END

View File

@ -207,11 +207,10 @@ public:
/// Looks for equipped artifact with given ID and returns its slot ID or -1 if none
/// (if more than one such artifact lower ID is returned)
ArtifactPosition getArtPos(const ArtifactID & aid, bool onlyWorn = true, bool allowLocked = true) const;
ArtifactPosition getArtPos(const CArtifactInstance *art) const;
const ArtifactPosition getArtPos(const CArtifactInstance * art) const;
std::vector<ArtifactPosition> getAllArtPositions(const ArtifactID & aid, bool onlyWorn, bool allowLocked, bool getAll) const;
std::vector<ArtifactPosition> getBackpackArtPositions(const ArtifactID & aid) const;
const CArtifactInstance * getArtByInstanceId(const ArtifactInstanceID & artInstId) const;
const ArtifactPosition getSlotByInstance(const CArtifactInstance * artInst) const;
/// Search for constituents of assemblies in backpack which do not have an ArtifactPosition
const CArtifactInstance * getHiddenArt(const ArtifactID & aid) const;
const CArtifactInstance * getAssemblyByConstituent(const ArtifactID & aid) const;
@ -255,7 +254,7 @@ public:
ArtBearer::ArtBearer bearerType() const override;
protected:
ArtBearer::ArtBearer Bearer;
ArtBearer::ArtBearer bearer;
};
VCMI_LIB_NAMESPACE_END

View File

@ -284,6 +284,10 @@ CArtifactSet * CNonConstInfoCallback::getArtSet(const ArtifactLocation & loc)
return hero;
}
}
else if(auto army = getArmyInstance(loc.artHolder))
{
return army->getStackPtr(loc.creature.value());
}
else if(auto market = dynamic_cast<CGArtifactsAltar*>(getObjInstance(loc.artHolder)))
{
return market;

View File

@ -90,7 +90,7 @@ public:
virtual void artifactAssembled(const ArtifactLocation &al){};
virtual void artifactDisassembled(const ArtifactLocation &al){};
virtual void artifactMoved(const ArtifactLocation &src, const ArtifactLocation &dst){};
virtual void bulkArtMovementStart(size_t numOfArts) {};
virtual void bulkArtMovementStart(size_t totalNumOfArts, size_t possibleAssemblyNumOfArts) {};
virtual void askToAssembleArtifact(const ArtifactLocation & dst) {};
virtual void heroVisit(const CGHeroInstance *visitor, const CGObjectInstance *visitedObj, bool start){};

View File

@ -49,7 +49,7 @@ private:
REPLACE_TEXTID_STRING,
REPLACE_NUMBER,
REPLACE_POSITIVE_NUMBER,
APPEND_EOL
APPEND_EOL
};
std::vector<EMessage> message;

View File

@ -54,6 +54,7 @@ namespace GameConstants
constexpr int ALTAR_ARTIFACTS_SLOTS = 22;
constexpr int TOURNAMENT_RULES_DD_MAP_TILES_THRESHOLD = 144*144*2; //map tiles count threshold for 2 dimension door casts with tournament rules
constexpr int KINGDOM_WINDOW_HEROES_SLOTS = 4;
constexpr int INFO_WINDOW_ARTIFACTS_MAX_ITEMS = 14;
}
VCMI_LIB_NAMESPACE_END

View File

@ -37,6 +37,12 @@ struct ArtifactLocation
, creature(creatureSlot)
{
}
ArtifactLocation(const ObjectInstanceID id, const std::optional<SlotID> creatureSlot, const ArtifactPosition & slot)
: artHolder(id)
, slot(slot)
, creature(creatureSlot)
{
}
template <typename Handler> void serialize(Handler & h)
{

View File

@ -78,7 +78,6 @@ public:
virtual void visitBulkSmartRebalanceStacks(BulkSmartRebalanceStacks & pack) {}
virtual void visitPutArtifact(PutArtifact & pack) {}
virtual void visitEraseArtifact(EraseArtifact & pack) {}
virtual void visitMoveArtifact(MoveArtifact & pack) {}
virtual void visitBulkMoveArtifacts(BulkMoveArtifacts & pack) {}
virtual void visitAssembledArtifact(AssembledArtifact & pack) {}
virtual void visitDisassembledArtifact(DisassembledArtifact & pack) {}

View File

@ -338,11 +338,6 @@ void EraseArtifact::visitTyped(ICPackVisitor & visitor)
visitor.visitEraseArtifact(*this);
}
void MoveArtifact::visitTyped(ICPackVisitor & visitor)
{
visitor.visitMoveArtifact(*this);
}
void BulkMoveArtifacts::visitTyped(ICPackVisitor & visitor)
{
visitor.visitBulkMoveArtifacts(*this);
@ -1794,17 +1789,6 @@ void EraseArtifact::applyGs(CGameState *gs)
art->removeFrom(*artSet, al.slot);
}
void MoveArtifact::applyGs(CGameState * gs)
{
auto srcHero = gs->getArtSet(src);
auto dstHero = gs->getArtSet(dst);
assert(srcHero);
assert(dstHero);
auto art = srcHero->getArt(src.slot);
assert(art && art->canBePutAt(dstHero, dst.slot));
art->move(*srcHero, src.slot, *dstHero, dst.slot);
}
void BulkMoveArtifacts::applyGs(CGameState * gs)
{
const auto bulkArtsRemove = [](std::vector<LinkedSlots> & artsPack, CArtifactSet & artSet)
@ -1861,7 +1845,7 @@ void AssembledArtifact::applyGs(CGameState *gs)
return art->getId() == builtArt->getId();
}));
const auto transformedArtSlot = hero->getSlotByInstance(transformedArt);
const auto transformedArtSlot = hero->getArtPos(transformedArt);
auto * combinedArt = new CArtifactInstance(builtArt);
gs->map->addNewArtifactInstance(combinedArt);

View File

@ -1025,47 +1025,26 @@ struct DLL_LINKAGE EraseArtifact : CArtifactOperationPack
}
};
struct DLL_LINKAGE MoveArtifact : CArtifactOperationPack
{
MoveArtifact() = default;
MoveArtifact(const PlayerColor & interfaceOwner, const ArtifactLocation & src, const ArtifactLocation & dst, bool askAssemble = true)
: interfaceOwner(interfaceOwner), src(src), dst(dst), askAssemble(askAssemble)
{
}
PlayerColor interfaceOwner;
ArtifactLocation src;
ArtifactLocation dst;
bool askAssemble = true;
void applyGs(CGameState * gs);
void visitTyped(ICPackVisitor & visitor) override;
template <typename Handler> void serialize(Handler & h)
{
h & interfaceOwner;
h & src;
h & dst;
h & askAssemble;
}
};
struct DLL_LINKAGE BulkMoveArtifacts : CArtifactOperationPack
{
struct LinkedSlots
{
ArtifactPosition srcPos;
ArtifactPosition dstPos;
bool askAssemble;
LinkedSlots() = default;
LinkedSlots(const ArtifactPosition & srcPos, const ArtifactPosition & dstPos)
LinkedSlots(const ArtifactPosition & srcPos, const ArtifactPosition & dstPos, bool askAssemble = false)
: srcPos(srcPos)
, dstPos(dstPos)
, askAssemble(askAssemble)
{
}
template <typename Handler> void serialize(Handler & h)
{
h & srcPos;
h & dstPos;
h & askAssemble;
}
};
@ -1079,8 +1058,6 @@ struct DLL_LINKAGE BulkMoveArtifacts : CArtifactOperationPack
: interfaceOwner(PlayerColor::NEUTRAL)
, srcArtHolder(ObjectInstanceID::NONE)
, dstArtHolder(ObjectInstanceID::NONE)
, swap(false)
, askAssemble(false)
, srcCreature(std::nullopt)
, dstCreature(std::nullopt)
{
@ -1089,8 +1066,6 @@ struct DLL_LINKAGE BulkMoveArtifacts : CArtifactOperationPack
: interfaceOwner(interfaceOwner)
, srcArtHolder(srcArtHolder)
, dstArtHolder(dstArtHolder)
, swap(swap)
, askAssemble(false)
, srcCreature(std::nullopt)
, dstCreature(std::nullopt)
{
@ -1100,8 +1075,6 @@ struct DLL_LINKAGE BulkMoveArtifacts : CArtifactOperationPack
std::vector<LinkedSlots> artsPack0;
std::vector<LinkedSlots> artsPack1;
bool swap;
bool askAssemble;
void visitTyped(ICPackVisitor & visitor) override;
@ -1114,8 +1087,6 @@ struct DLL_LINKAGE BulkMoveArtifacts : CArtifactOperationPack
h & dstArtHolder;
h & srcCreature;
h & dstCreature;
h & swap;
h & askAssemble;
}
};

View File

@ -112,7 +112,6 @@ void registerTypesClientPacks(Serializer &s)
s.template registerType<CPackForClient, CArtifactOperationPack>();
s.template registerType<CArtifactOperationPack, PutArtifact>();
s.template registerType<CArtifactOperationPack, EraseArtifact>();
s.template registerType<CArtifactOperationPack, MoveArtifact>();
s.template registerType<CArtifactOperationPack, AssembledArtifact>();
s.template registerType<CArtifactOperationPack, DisassembledArtifact>();
s.template registerType<CArtifactOperationPack, BulkMoveArtifacts>();

View File

@ -2779,7 +2779,6 @@ bool CGameHandler::moveArtifact(const PlayerColor & player, const ArtifactLocati
// Previous artifact must be swapped
COMPLAIN_RET_FALSE_IF(!dstArtifact->canBePutAt(srcArtSet, src.slot, true), "Cannot swap artifacts!");
ma.artsPack1.push_back(BulkMoveArtifacts::LinkedSlots(dstSlot, src.slot));
ma.swap = true;
}
auto hero = getHero(dst.artHolder);
@ -2788,7 +2787,7 @@ bool CGameHandler::moveArtifact(const PlayerColor & player, const ArtifactLocati
ma.artsPack0.push_back(BulkMoveArtifacts::LinkedSlots(src.slot, dstSlot));
if(src.artHolder != dst.artHolder)
ma.askAssemble = true;
ma.artsPack0.back().askAssemble = true;
sendAndApply(&ma);
return true;
}
@ -2896,7 +2895,7 @@ bool CGameHandler::bulkMoveArtifacts(const PlayerColor & player, ObjectInstanceI
bool CGameHandler::scrollBackpackArtifacts(const PlayerColor & player, const ObjectInstanceID heroID, bool left)
{
auto artSet = getArtSet(heroID);
const auto artSet = getArtSet(heroID);
COMPLAIN_RET_FALSE_IF(artSet == nullptr, "scrollBackpackArtifacts: wrong hero's ID");
BulkMoveArtifacts bma(player, heroID, heroID, false);
@ -2961,7 +2960,7 @@ bool CGameHandler::switchArtifactsCostume(const PlayerColor & player, const Obje
{
bma.artsPack0.emplace_back(BulkMoveArtifacts::LinkedSlots
{
artSet->getSlotByInstance(artFittingSet.getArt(availableArts.front())),
artSet->getArtPos(artFittingSet.getArt(availableArts.front())),
artPos.first
});
artFittingSet.removeArtifact(availableArts.front());
@ -3926,7 +3925,7 @@ bool CGameHandler::sacrificeArtifact(const IMarket * m, const CGHeroInstance * h
int expToGive;
m->getOffer(art->getTypeId(), 0, dmp, expToGive, EMarketMode::ARTIFACT_EXP);
expSum += expToGive;
removeArtifact(ArtifactLocation(altarObj->id, altarObj->getSlotByInstance(art)));
removeArtifact(ArtifactLocation(altarObj->id, altarObj->getArtPos(art)));
}
else
{

View File

@ -323,176 +323,9 @@ void BattleResultProcessor::endBattleConfirm(const CBattleInfoCallback & battle)
CasualtiesAfterBattle cab1(battle, BattleSide::ATTACKER);
CasualtiesAfterBattle cab2(battle, BattleSide::DEFENDER);
ChangeSpells cs; //for Eagle Eye
if(!finishingBattle->isDraw() && finishingBattle->winnerHero)
{
if (int eagleEyeLevel = finishingBattle->winnerHero->valOfBonuses(BonusType::LEARN_BATTLE_SPELL_LEVEL_LIMIT))
{
double eagleEyeChance = finishingBattle->winnerHero->valOfBonuses(BonusType::LEARN_BATTLE_SPELL_CHANCE);
for(auto & spellId : battle.getBattle()->getUsedSpells(battle.otherSide(battleResult->winner)))
{
auto spell = spellId.toEntity(VLC->spells());
if(spell && spell->getLevel() <= eagleEyeLevel && !finishingBattle->winnerHero->spellbookContainsSpell(spell->getId()) && gameHandler->getRandomGenerator().nextInt(99) < eagleEyeChance)
cs.spells.insert(spell->getId());
}
}
}
std::vector<const CArtifactInstance *> arts; //display them in window
if(result == EBattleResult::NORMAL && !finishingBattle->isDraw() && finishingBattle->winnerHero)
{
auto sendMoveArtifact = [&](const CArtifactInstance *art, MoveArtifact *ma)
{
const auto slot = ArtifactUtils::getArtAnyPosition(finishingBattle->winnerHero, art->getTypeId());
if(slot != ArtifactPosition::PRE_FIRST)
{
arts.push_back(art);
ma->dst = ArtifactLocation(finishingBattle->winnerHero->id, slot);
if(ArtifactUtils::isSlotBackpack(slot))
ma->askAssemble = false;
gameHandler->sendAndApply(ma);
}
};
if (finishingBattle->loserHero)
{
//TODO: wrap it into a function, somehow (std::variant -_-)
auto artifactsWorn = finishingBattle->loserHero->artifactsWorn;
for (auto artSlot : artifactsWorn)
{
MoveArtifact ma;
ma.src = ArtifactLocation(finishingBattle->loserHero->id, artSlot.first);
const CArtifactInstance * art = finishingBattle->loserHero->getArt(artSlot.first);
if (art && !art->artType->isBig() &&
art->artType->getId() != ArtifactID::SPELLBOOK)
// don't move war machines or locked arts (spellbook)
{
sendMoveArtifact(art, &ma);
}
}
for(int slotNumber = finishingBattle->loserHero->artifactsInBackpack.size() - 1; slotNumber >= 0; slotNumber--)
{
//we assume that no big artifacts can be found
MoveArtifact ma;
ma.src = ArtifactLocation(finishingBattle->loserHero->id,
ArtifactPosition(ArtifactPosition::BACKPACK_START + slotNumber)); //backpack automatically shifts arts to beginning
const CArtifactInstance * art = finishingBattle->loserHero->getArt(ArtifactPosition::BACKPACK_START + slotNumber);
if (art->artType->getId() != ArtifactID::GRAIL) //grail may not be won
{
sendMoveArtifact(art, &ma);
}
}
if (finishingBattle->loserHero->commander) //TODO: what if commanders belong to no hero?
{
artifactsWorn = finishingBattle->loserHero->commander->artifactsWorn;
for (auto artSlot : artifactsWorn)
{
MoveArtifact ma;
ma.src = ArtifactLocation(finishingBattle->loserHero->id, artSlot.first);
ma.src.creature = finishingBattle->loserHero->findStack(finishingBattle->loserHero->commander);
const auto art = finishingBattle->loserHero->commander->getArt(artSlot.first);
if (art && !art->artType->isBig())
{
sendMoveArtifact(art, &ma);
}
}
}
}
auto loser = battle.otherSide(battleResult->winner);
for (auto armySlot : battle.battleGetArmyObject(loser)->stacks)
{
auto artifactsWorn = armySlot.second->artifactsWorn;
for(const auto & artSlot : artifactsWorn)
{
MoveArtifact ma;
ma.src = ArtifactLocation(finishingBattle->loserHero->id, artSlot.first);
ma.src.creature = finishingBattle->loserHero->findStack(finishingBattle->loserHero->commander);
const auto art = finishingBattle->loserHero->commander->getArt(artSlot.first);
if (art && !art->artType->isBig())
{
sendMoveArtifact(art, &ma);
}
}
}
}
if (arts.size()) //display loot
{
InfoWindow iw;
iw.player = finishingBattle->winnerHero->tempOwner;
iw.text.appendLocalString (EMetaText::GENERAL_TXT, 30); //You have captured enemy artifact
for (auto art : arts) //TODO; separate function to display loot for various objects?
{
if (art->artType->getId() == ArtifactID::SPELL_SCROLL)
iw.components.emplace_back(ComponentType::SPELL_SCROLL, art->getScrollSpellID());
else
iw.components.emplace_back(ComponentType::ARTIFACT, art->artType->getId());
if (iw.components.size() >= 14)
{
gameHandler->sendAndApply(&iw);
iw.components.clear();
}
}
if (iw.components.size())
{
gameHandler->sendAndApply(&iw);
}
}
//Eagle Eye secondary skill handling
if (!cs.spells.empty())
{
cs.learn = 1;
cs.hid = finishingBattle->winnerHero->id;
InfoWindow iw;
iw.player = finishingBattle->winnerHero->tempOwner;
iw.text.appendLocalString(EMetaText::GENERAL_TXT, 221); //Through eagle-eyed observation, %s is able to learn %s
iw.text.replaceRawString(finishingBattle->winnerHero->getNameTranslated());
std::ostringstream names;
for (int i = 0; i < cs.spells.size(); i++)
{
names << "%s";
if (i < cs.spells.size() - 2)
names << ", ";
else if (i < cs.spells.size() - 1)
names << "%s";
}
names << ".";
iw.text.replaceRawString(names.str());
auto it = cs.spells.begin();
for (int i = 0; i < cs.spells.size(); i++, it++)
{
iw.text.replaceName(*it);
if (i == cs.spells.size() - 2) //we just added pre-last name
iw.text.replaceLocalString(EMetaText::GENERAL_TXT, 141); // " and "
iw.components.emplace_back(ComponentType::SPELL, *it);
}
gameHandler->sendAndApply(&iw);
gameHandler->sendAndApply(&cs);
}
cab1.updateArmy(gameHandler);
cab2.updateArmy(gameHandler); //take casualties after battle is deleted
if(finishingBattle->loserHero) //remove beaten hero
{
RemoveObject ro(finishingBattle->loserHero->id, finishingBattle->victor);
gameHandler->sendAndApply(&ro);
}
if(finishingBattle->isDraw() && finishingBattle->winnerHero) //for draw case both heroes should be removed
{
RemoveObject ro(finishingBattle->winnerHero->id, finishingBattle->loser);
gameHandler->sendAndApply(&ro);
}
if(battleResult->winner == BattleSide::DEFENDER
&& finishingBattle->winnerHero
&& finishingBattle->winnerHero->visitedTown
@ -505,6 +338,160 @@ void BattleResultProcessor::endBattleConfirm(const CBattleInfoCallback & battle)
if(!finishingBattle->isDraw() && battleResult->exp[finishingBattle->winnerSide] && finishingBattle->winnerHero)
gameHandler->giveExperience(finishingBattle->winnerHero, battleResult->exp[finishingBattle->winnerSide]);
// Eagle Eye handling
if(!finishingBattle->isDraw() && finishingBattle->winnerHero)
{
ChangeSpells spells;
if(auto eagleEyeLevel = finishingBattle->winnerHero->valOfBonuses(BonusType::LEARN_BATTLE_SPELL_LEVEL_LIMIT))
{
auto eagleEyeChance = finishingBattle->winnerHero->valOfBonuses(BonusType::LEARN_BATTLE_SPELL_CHANCE);
for(auto & spellId : battle.getBattle()->getUsedSpells(battle.otherSide(battleResult->winner)))
{
auto spell = spellId.toEntity(VLC->spells());
if(spell
&& spell->getLevel() <= eagleEyeLevel
&& !finishingBattle->winnerHero->spellbookContainsSpell(spell->getId())
&& gameHandler->getRandomGenerator().nextInt(99) < eagleEyeChance)
{
spells.spells.insert(spell->getId());
}
}
}
if(!spells.spells.empty())
{
spells.learn = 1;
spells.hid = finishingBattle->winnerHero->id;
InfoWindow iw;
iw.player = finishingBattle->winnerHero->tempOwner;
iw.text.appendLocalString(EMetaText::GENERAL_TXT, 221); //Through eagle-eyed observation, %s is able to learn %s
iw.text.replaceRawString(finishingBattle->winnerHero->getNameTranslated());
std::ostringstream names;
for(int i = 0; i < spells.spells.size(); i++)
{
names << "%s";
if(i < spells.spells.size() - 2)
names << ", ";
else if(i < spells.spells.size() - 1)
names << "%s";
}
names << ".";
iw.text.replaceRawString(names.str());
auto it = spells.spells.begin();
for(int i = 0; i < spells.spells.size(); i++, it++)
{
iw.text.replaceName(*it);
if(i == spells.spells.size() - 2) //we just added pre-last name
iw.text.replaceLocalString(EMetaText::GENERAL_TXT, 141); // " and "
iw.components.emplace_back(ComponentType::SPELL, *it);
}
gameHandler->sendAndApply(&iw);
gameHandler->sendAndApply(&spells);
}
}
// Artifacts handling
if(result == EBattleResult::NORMAL && !finishingBattle->isDraw() && finishingBattle->winnerHero)
{
std::vector<const CArtifactInstance*> arts; // display them in window
CArtifactFittingSet artFittingSet(*finishingBattle->winnerHero);
const auto addArtifactToTransfer = [&artFittingSet, &arts](BulkMoveArtifacts & pack, const ArtifactPosition & srcSlot, const CArtifactInstance * art)
{
assert(art);
const auto dstSlot = ArtifactUtils::getArtAnyPosition(&artFittingSet, art->getTypeId());
if(dstSlot != ArtifactPosition::PRE_FIRST)
{
pack.artsPack0.emplace_back(BulkMoveArtifacts::LinkedSlots(srcSlot, dstSlot));
if(ArtifactUtils::isSlotEquipment(dstSlot))
pack.artsPack0.back().askAssemble = true;
arts.emplace_back(art);
artFittingSet.putArtifact(dstSlot, const_cast<CArtifactInstance*>(art));
}
};
const auto sendArtifacts = [this](BulkMoveArtifacts & bma)
{
if(!bma.artsPack0.empty())
gameHandler->sendAndApply(&bma);
};
BulkMoveArtifacts packHero(finishingBattle->winnerHero->getOwner(), ObjectInstanceID::NONE, finishingBattle->winnerHero->id, false);
if(finishingBattle->loserHero)
{
packHero.srcArtHolder = finishingBattle->loserHero->id;
for(const auto & artSlot : finishingBattle->loserHero->artifactsWorn)
{
if(ArtifactUtils::isArtRemovable(artSlot))
addArtifactToTransfer(packHero, artSlot.first, artSlot.second.getArt());
}
for(const auto & artSlot : finishingBattle->loserHero->artifactsInBackpack)
{
if(const auto art = artSlot.getArt(); art->getTypeId() != ArtifactID::GRAIL)
addArtifactToTransfer(packHero, finishingBattle->loserHero->getArtPos(art), art);
}
if(finishingBattle->loserHero->commander)
{
BulkMoveArtifacts packCommander(finishingBattle->winnerHero->getOwner(), finishingBattle->loserHero->id, finishingBattle->winnerHero->id, false);
packCommander.srcCreature = finishingBattle->loserHero->findStack(finishingBattle->loserHero->commander);
for(const auto & artSlot : finishingBattle->loserHero->commander->artifactsWorn)
addArtifactToTransfer(packCommander, artSlot.first, artSlot.second.getArt());
sendArtifacts(packCommander);
}
}
auto armyObj = battle.battleGetArmyObject(battle.otherSide(battleResult->winner));
for(const auto & armySlot : armyObj->stacks)
{
BulkMoveArtifacts packsArmy(finishingBattle->winnerHero->getOwner(), finishingBattle->loserHero->id, finishingBattle->winnerHero->id, false);
packsArmy.srcArtHolder = armyObj->id;
packsArmy.srcCreature = armySlot.first;
for(const auto & artSlot : armySlot.second->artifactsWorn)
addArtifactToTransfer(packsArmy, artSlot.first, armySlot.second->getArt(artSlot.first));
sendArtifacts(packsArmy);
}
// Display loot
if(!arts.empty())
{
InfoWindow iw;
iw.player = finishingBattle->winnerHero->tempOwner;
iw.text.appendLocalString(EMetaText::GENERAL_TXT, 30); //You have captured enemy artifact
for(const auto art : arts) //TODO; separate function to display loot for various objects?
{
if(art->isScroll())
iw.components.emplace_back(ComponentType::SPELL_SCROLL, art->getScrollSpellID());
else
iw.components.emplace_back(ComponentType::ARTIFACT, art->getTypeId());
if(iw.components.size() >= GameConstants::INFO_WINDOW_ARTIFACTS_MAX_ITEMS)
{
gameHandler->sendAndApply(&iw);
iw.components.clear();
}
}
gameHandler->sendAndApply(&iw);
}
if(!packHero.artsPack0.empty())
sendArtifacts(packHero);
}
// Remove beaten hero
if(finishingBattle->loserHero)
{
RemoveObject ro(finishingBattle->loserHero->id, finishingBattle->victor);
gameHandler->sendAndApply(&ro);
}
// For draw case both heroes should be removed
if(finishingBattle->isDraw() && finishingBattle->winnerHero)
{
RemoveObject ro(finishingBattle->winnerHero->id, finishingBattle->loser);
gameHandler->sendAndApply(&ro);
}
BattleResultAccepted raccepted;
raccepted.battleID = battle.getBattle()->getBattleID();
raccepted.heroResult[0].army = const_cast<CArmedInstance*>(battle.battleGetArmyObject(BattleSide::ATTACKER));
@ -513,7 +500,7 @@ void BattleResultProcessor::endBattleConfirm(const CBattleInfoCallback & battle)
raccepted.heroResult[1].hero = const_cast<CGHeroInstance*>(battle.battleGetFightingHero(BattleSide::DEFENDER));
raccepted.heroResult[0].exp = battleResult->exp[0];
raccepted.heroResult[1].exp = battleResult->exp[1];
raccepted.winnerSide = finishingBattle->winnerSide;
raccepted.winnerSide = finishingBattle->winnerSide;
gameHandler->sendAndApply(&raccepted);
gameHandler->queries->popIfTop(battleQuery);