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

ArtifactsUIController class

This commit is contained in:
SoundSSGood 2024-06-22 19:29:39 +03:00
parent f87762bc96
commit ef1fbffad4
15 changed files with 212 additions and 169 deletions

View File

@ -0,0 +1,142 @@
/*
* 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"
bool ArtifactsUIController::askToAssemble(const CGHeroInstance * hero, const ArtifactPosition & slot, std::set<ArtifactID> * ignoredArtifacts)
{
assert(hero);
const auto art = hero->getArt(slot);
assert(art);
if(hero->tempOwner != LOCPLINT->playerID)
return false;
auto assemblyPossibilities = ArtifactUtils::assemblyPossibilities(hero, art->getTypeId(), true);
if(!assemblyPossibilities.empty())
{
auto askThread = new boost::thread([this, hero, art, slot, assemblyPossibilities, ignoredArtifacts]() -> void
{
boost::mutex::scoped_lock askLock(askAssembleArtifactsMutex);
for(const auto combinedArt : assemblyPossibilities)
{
boost::mutex::scoped_lock interfaceLock(GH.interfaceMutex);
if(ignoredArtifacts && vstd::contains(*ignoredArtifacts, combinedArt->getId()))
continue;
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, slot, true, combinedArt->getId());
}, nullptr, {std::make_shared<CComponent>(ComponentType::ARTIFACT, combinedArt->getId())});
LOCPLINT->waitWhileDialog();
if(ignoredArtifacts)
ignoredArtifacts->emplace(combinedArt->getId());
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, slot, false, ArtifactID());
}, nullptr);
return true;
}
return false;
}
void ArtifactsUIController::artifactRemoved()
{
for(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--;
for(auto & artWin : GH.windows().findWindows<CWindowWithArtifacts>())
if(numOfMovedArts == 0)
{
artWin->update();
artWin->redraw();
}
LOCPLINT->waitWhileDialog();
}
void ArtifactsUIController::bulkArtMovementStart(size_t numOfArts)
{
numOfMovedArts = numOfArts;
if(numOfArtsAskAssembleSession == 0)
{
// Do not start the next session until the previous one is finished
numOfArtsAskAssembleSession = numOfArts; // TODO this is wrong
ignoredArtifacts.clear();
}
}
void ArtifactsUIController::artifactAssembled()
{
for(auto & artWin : GH.windows().findWindows<CWindowWithArtifacts>())
artWin->update();
}
void ArtifactsUIController::artifactDisassembled()
{
for(auto & artWin : GH.windows().findWindows<CWindowWithArtifacts>())
artWin->update();
}

View File

@ -0,0 +1,38 @@
/*
* 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"
VCMI_LIB_NAMESPACE_BEGIN
class CGHeroInstance;
VCMI_LIB_NAMESPACE_END
class ArtifactsUIController
{
public:
size_t numOfMovedArts;
size_t numOfArtsAskAssembleSession;
std::set<ArtifactID> ignoredArtifacts;
boost::mutex askAssembleArtifactsMutex;
bool askToAssemble(const CGHeroInstance * hero, const ArtifactPosition & slot, std::set<ArtifactID> * ignoredArtifacts = nullptr);
bool askToDisassemble(const CGHeroInstance * hero, const ArtifactPosition & slot);
void artifactRemoved();
void artifactMoved();
void bulkArtMovementStart(size_t numOfArts);
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

@ -1251,35 +1251,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))
@ -1739,14 +1710,14 @@ void CPlayerInterface::askToAssembleArtifact(const ArtifactLocation &al)
{
if(auto hero = cb->getHero(al.artHolder))
{
auto art = hero->getArt(al.slot);
if(art == nullptr)
if(hero->getArt(al.slot) == nullptr)
{
logGlobal->error("artifact location %d points to nothing",
al.slot.num);
logGlobal->error("artifact location %d points to nothing", al.slot.num);
return;
}
ArtifactUtilsClient::askToAssemble(hero, al.slot);
askToAssemble(hero, al.slot, &ignoredArtifacts);
if(numOfArtsAskAssembleSession != 0)
numOfArtsAskAssembleSession--;
}
}
@ -1760,55 +1731,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();
ArtifactsUIController::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();
ArtifactsUIController::artifactMoved();
}
void CPlayerInterface::bulkArtMovementStart(size_t numOfArts)
{
numOfMovedArts = numOfArts;
ArtifactsUIController::bulkArtMovementStart(numOfArts);
}
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);
ArtifactsUIController::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);
ArtifactsUIController::artifactDisassembled();
}
void CPlayerInterface::waitForAllDialogs()

View File

@ -12,6 +12,7 @@
#include "../lib/FunctionList.h"
#include "../lib/CGameInterface.h"
#include "gui/CIntObject.h"
#include "ArtifactsUIController.h"
VCMI_LIB_NAMESPACE_BEGIN
@ -56,11 +57,9 @@ namespace boost
}
/// Central class for managing user interface logic
class CPlayerInterface : public CGameInterface, public IUpdateable
class CPlayerInterface : public CGameInterface, public IUpdateable, public ArtifactsUIController
{
bool ignoreEvents;
size_t numOfMovedArts;
int autosaveCount;
std::list<std::shared_ptr<CInfoWindow>> dialogs; //queue of dialogs awaiting to be shown (not currently shown!)
@ -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

@ -307,7 +307,7 @@ void ApplyClientNetPackVisitor::visitBulkMoveArtifacts(BulkMoveArtifacts & pack)
const auto dstLoc = ArtifactLocation(pack.dstArtHolder, slotToMove.dstPos);
callInterfaceIfPresent(cl, pack.interfaceOwner, &IGameEventsReceiver::artifactMoved, srcLoc, dstLoc);
if(pack.askAssemble)
if(slotToMove.askAssemble)
callInterfaceIfPresent(cl, pack.interfaceOwner, &IGameEventsReceiver::askToAssembleArtifact, dstLoc);
if(pack.interfaceOwner != dstOwner)
callInterfaceIfPresent(cl, dstOwner, &IGameEventsReceiver::artifactMoved, srcLoc, dstLoc);

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)
{
LOCPLINT->waitWhileDialog();
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);
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

@ -117,9 +117,9 @@ void CWindowWithArtifacts::showArtifactAssembling(const CArtifactsOfHeroBase & a
{
if(artsInst.getArt(artPlace.slot))
{
if(ArtifactUtilsClient::askToDisassemble(artsInst.getHero(), artPlace.slot))
if(LOCPLINT->askToDisassemble(artsInst.getHero(), artPlace.slot))
return;
if(ArtifactUtilsClient::askToAssemble(artsInst.getHero(), artPlace.slot))
if(LOCPLINT->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,25 +183,6 @@ 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()))

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

@ -1031,17 +1031,20 @@ struct DLL_LINKAGE BulkMoveArtifacts : CArtifactOperationPack
{
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;
}
};
@ -1056,7 +1059,6 @@ struct DLL_LINKAGE BulkMoveArtifacts : CArtifactOperationPack
, srcArtHolder(ObjectInstanceID::NONE)
, dstArtHolder(ObjectInstanceID::NONE)
, swap(false)
, askAssemble(false)
, srcCreature(std::nullopt)
, dstCreature(std::nullopt)
{
@ -1066,7 +1068,6 @@ struct DLL_LINKAGE BulkMoveArtifacts : CArtifactOperationPack
, srcArtHolder(srcArtHolder)
, dstArtHolder(dstArtHolder)
, swap(swap)
, askAssemble(false)
, srcCreature(std::nullopt)
, dstCreature(std::nullopt)
{
@ -1077,7 +1078,6 @@ struct DLL_LINKAGE BulkMoveArtifacts : CArtifactOperationPack
std::vector<LinkedSlots> artsPack0;
std::vector<LinkedSlots> artsPack1;
bool swap;
bool askAssemble;
void visitTyped(ICPackVisitor & visitor) override;
@ -1091,7 +1091,6 @@ struct DLL_LINKAGE BulkMoveArtifacts : CArtifactOperationPack
h & srcCreature;
h & dstCreature;
h & swap;
h & askAssemble;
}
};

View File

@ -2783,7 +2783,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;
}

View File

@ -407,6 +407,8 @@ void BattleResultProcessor::endBattleConfirm(const CBattleInfoCallback & battle)
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));
}
@ -421,7 +423,6 @@ void BattleResultProcessor::endBattleConfirm(const CBattleInfoCallback & battle)
if(finishingBattle->loserHero)
{
packHero.srcArtHolder = finishingBattle->loserHero->id;
packHero.askAssemble = true;
for(const auto & artSlot : finishingBattle->loserHero->artifactsWorn)
{
if(ArtifactUtils::isArtRemovable(artSlot))