1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-12-22 22:13:35 +02:00

Switching costume

This commit is contained in:
SoundSSGood 2024-04-19 17:14:41 +03:00
parent 25dea7e364
commit ce9d2d8ab8
17 changed files with 154 additions and 115 deletions

View File

@ -195,8 +195,6 @@ void CCallback::scrollBackpackArtifacts(ObjectInstanceID hero, bool left)
void CCallback::manageHeroCostume(ObjectInstanceID hero, size_t costumeIndex, bool saveCostume)
{
assert(costumeIndex < GameConstants::HERO_COSTUMES_ARTIFACTS);
ManageEquippedArtifacts mea(hero, costumeIndex, saveCostume);
sendRequest(&mea);
}

View File

@ -314,12 +314,20 @@ CKeyShortcut::CKeyShortcut(EShortcut key)
{
}
CKeyShortcut::CKeyShortcut(const EShortcut & key, const KeyPressedFunctor & keyPressedCallback)
: CKeyShortcut(key)
{
this->keyPressedCallback = keyPressedCallback;
}
void CKeyShortcut::keyPressed(EShortcut key)
{
if( assignedKey == key && assignedKey != EShortcut::NONE && !shortcutPressed)
{
shortcutPressed = true;
clickPressed(GH.getCursorPosition());
if(keyPressedCallback)
keyPressedCallback();
}
}

View File

@ -133,14 +133,19 @@ public:
/// Classes wanting use it should have it as one of their base classes
class CKeyShortcut : public virtual CIntObject
{
bool shortcutPressed;
public:
using KeyPressedFunctor = std::function<void()>;
EShortcut assignedKey;
CKeyShortcut();
CKeyShortcut(EShortcut key);
CKeyShortcut(const EShortcut & key, const KeyPressedFunctor & keyPressedCallback);
void keyPressed(EShortcut key) override;
void keyReleased(EShortcut key) override;
private:
bool shortcutPressed;
KeyPressedFunctor keyPressedCallback;
};
class WindowBase : public CIntObject

View File

@ -18,7 +18,7 @@
#include "../../CCallback.h"
#include "../../lib/networkPacks/ArtifactLocation.h"
CArtifactsOfHeroMain::CArtifactsOfHeroMain(const Point & position, bool costumesEnabled)
CArtifactsOfHeroMain::CArtifactsOfHeroMain(const Point & position)
{
init(
std::bind(&CArtifactsOfHeroBase::clickPrassedArtPlace, this, _1, _2),
@ -26,21 +26,6 @@ CArtifactsOfHeroMain::CArtifactsOfHeroMain(const Point & position, bool costumes
position,
std::bind(&CArtifactsOfHeroBase::scrollBackpack, this, _1));
addGestureCallback(std::bind(&CArtifactsOfHeroBase::gestureArtPlace, this, _1, _2));
if(costumesEnabled)
{
size_t costumeIndex = 0;
for(const auto & hotkey : hotkeys)
{
auto keyProc = costumesSwitchers.emplace_back(std::make_shared<CKeyShortcutWrapper>(hotkey,
[this, hotkey, costumeIndex]()
{
CArtifactsOfHeroMain::onCostumeSelect(costumeIndex);
}));
keyProc->addUsedEvents(AEventsReceiver::KEYBOARD);
costumeIndex++;
}
}
}
CArtifactsOfHeroMain::~CArtifactsOfHeroMain()
@ -48,19 +33,17 @@ CArtifactsOfHeroMain::~CArtifactsOfHeroMain()
CArtifactsOfHeroBase::putBackPickedArtifact();
}
void CArtifactsOfHeroMain::onCostumeSelect(const size_t costumeIndex)
void CArtifactsOfHeroMain::enableArtifactsCostumeSwitcher()
{
LOCPLINT->cb->manageHeroCostume(getHero()->id, costumeIndex, GH.isKeyboardCtrlDown());
}
CArtifactsOfHeroMain::CKeyShortcutWrapper::CKeyShortcutWrapper(const EShortcut & key, const KeyPressedFunctor & onCostumeSelect)
: CKeyShortcut(key)
, onCostumeSelect(onCostumeSelect)
{
}
void CArtifactsOfHeroMain::CKeyShortcutWrapper::clickPressed(const Point & cursorPosition)
{
if(onCostumeSelect)
onCostumeSelect();
size_t costumeIdx = 0;
for(const auto & hotkey : costumesSwitcherHotkeys)
{
auto keyProc = costumesSwitcherProcessors.emplace_back(std::make_shared<CKeyShortcut>(hotkey,
[this, costumeIdx]()
{
LOCPLINT->cb->manageHeroCostume(getHero()->id, costumeIdx, GH.isKeyboardCtrlDown());
}));
keyProc->addUsedEvents(AEventsReceiver::KEYBOARD);
costumeIdx++;
}
}

View File

@ -16,24 +16,12 @@
class CArtifactsOfHeroMain : public CArtifactsOfHeroBase
{
public:
CArtifactsOfHeroMain(const Point & position, bool costumesEnabled = false);
CArtifactsOfHeroMain(const Point & position);
~CArtifactsOfHeroMain() override;
void enableArtifactsCostumeSwitcher();
private:
// TODO may be removed if CKeyShortcut supports callbacks
class CKeyShortcutWrapper : public CKeyShortcut
{
public:
using KeyPressedFunctor = std::function<void()>;
CKeyShortcutWrapper(const EShortcut & key, const KeyPressedFunctor & onCostumeSelect);
void clickPressed(const Point & cursorPosition) override;
private:
KeyPressedFunctor onCostumeSelect;
};
const std::array<EShortcut, GameConstants::HERO_COSTUMES_ARTIFACTS> hotkeys =
const std::vector<EShortcut> costumesSwitcherHotkeys =
{
EShortcut::HERO_COSTUME_0,
EShortcut::HERO_COSTUME_1,
@ -46,7 +34,5 @@ private:
EShortcut::HERO_COSTUME_8,
EShortcut::HERO_COSTUME_9
};
std::vector<std::shared_ptr<CKeyShortcutWrapper>> costumesSwitchers;
void onCostumeSelect(const size_t costumeIndex);
std::vector<std::shared_ptr<CKeyShortcut>> costumesSwitcherProcessors;
};

View File

@ -218,9 +218,10 @@ void CHeroWindow::update(const CGHeroInstance * hero, bool redrawNeeded)
}
if(!arts)
{
arts = std::make_shared<CArtifactsOfHeroMain>(Point(-65, -8), true);
arts = std::make_shared<CArtifactsOfHeroMain>(Point(-65, -8));
arts->setHero(curHero);
addSetAndCallbacks(arts);
enableArtifactsCostumeSwitcher();
}
int serial = LOCPLINT->cb->getHeroSerial(curHero, false);

View File

@ -343,6 +343,20 @@ void CWindowWithArtifacts::deactivate()
CWindowObject::deactivate();
}
void CWindowWithArtifacts::enableArtifactsCostumeSwitcher()
{
for(auto artSet : artSets)
std::visit(
[](auto artSetWeak)
{
if constexpr(std::is_same_v<decltype(artSetWeak), std::weak_ptr<CArtifactsOfHeroMain>>)
{
const auto artSetPtr = artSetWeak.lock();
artSetPtr->enableArtifactsCostumeSwitcher();
}
}, artSet);
}
void CWindowWithArtifacts::artifactRemoved(const ArtifactLocation & artLoc)
{
update();

View File

@ -42,6 +42,7 @@ public:
void gestureArtPlaceHero(const CArtifactsOfHeroBase & artsInst, CArtPlace & artPlace, const Point & cursorPosition);
void activate() override;
void deactivate() override;
void enableArtifactsCostumeSwitcher();
virtual void artifactRemoved(const ArtifactLocation & artLoc);
virtual void artifactMoved(const ArtifactLocation & srcLoc, const ArtifactLocation & destLoc, bool withRedraw);

View File

@ -52,7 +52,7 @@ DLL_LINKAGE const std::vector<ArtifactPosition> & ArtifactUtils::unmovableSlots(
return positions;
}
DLL_LINKAGE const std::vector<ArtifactPosition> & ArtifactUtils::constituentWornSlots()
DLL_LINKAGE const std::vector<ArtifactPosition> & ArtifactUtils::commonWornSlots()
{
static const std::vector<ArtifactPosition> positions =
{

View File

@ -30,7 +30,7 @@ namespace ArtifactUtils
DLL_LINKAGE ArtifactPosition getArtBackpackPosition(const CArtifactSet * target, const ArtifactID & aid);
// TODO: Make this constexpr when the toolset is upgraded
DLL_LINKAGE const std::vector<ArtifactPosition> & unmovableSlots();
DLL_LINKAGE const std::vector<ArtifactPosition> & constituentWornSlots();
DLL_LINKAGE const std::vector<ArtifactPosition> & commonWornSlots();
DLL_LINKAGE const std::vector<ArtifactPosition> & allWornSlots();
DLL_LINKAGE const std::vector<ArtifactPosition> & commanderSlots();
DLL_LINKAGE bool isArtRemovable(const std::pair<ArtifactPosition, ArtSlotInfo> & slot);

View File

@ -1086,6 +1086,14 @@ CArtifactFittingSet::CArtifactFittingSet(ArtBearer::ArtBearer Bearer):
{
}
CArtifactFittingSet::CArtifactFittingSet(const CArtifactSet & artSet)
: CArtifactFittingSet(artSet.bearerType())
{
artifactsWorn = artSet.artifactsWorn;
artifactsInBackpack = artSet.artifactsInBackpack;
artifactsTransitionPos = artSet.artifactsTransitionPos;
}
ArtBearer::ArtBearer CArtifactFittingSet::bearerType() const
{
return this->Bearer;

View File

@ -249,6 +249,7 @@ class DLL_LINKAGE CArtifactFittingSet : public CArtifactSet
{
public:
CArtifactFittingSet(ArtBearer::ArtBearer Bearer);
CArtifactFittingSet(const CArtifactSet & artSet);
ArtBearer::ArtBearer bearerType() const override;
protected:

View File

@ -1075,7 +1075,7 @@ void ChangeObjectVisitors::applyGs(CGameState * gs) const
void ChangeArtifactsCostume::applyGs(CGameState * gs) const
{
auto & allCostumes = gs->getPlayerState(player)->costumesArtifacts;
if(auto & costume = allCostumes.find(costumeIdx); costume != allCostumes.end())
if(const auto & costume = allCostumes.find(costumeIdx); costume != allCostumes.end())
costume->second = costumeSet;
else
allCostumes.emplace(costumeIdx, costumeSet);
@ -1802,69 +1802,47 @@ void MoveArtifact::applyGs(CGameState * gs)
void BulkMoveArtifacts::applyGs(CGameState * gs)
{
enum class EBulkArtsOp
const auto bulkArtsRemove = [](std::vector<LinkedSlots> & artsPack, CArtifactSet & artSet)
{
BULK_MOVE,
BULK_REMOVE,
BULK_PUT
std::vector<ArtifactPosition> packToRemove;
for(const auto & slotsPair : artsPack)
packToRemove.push_back(slotsPair.srcPos);
std::sort(packToRemove.begin(), packToRemove.end(), [](const ArtifactPosition & slot0, const ArtifactPosition & slot1) -> bool
{
return slot0.num > slot1.num;
});
for(const auto & slot : packToRemove)
{
auto * art = artSet.getArt(slot);
assert(art);
art->removeFrom(artSet, slot);
}
};
auto bulkArtsOperation = [this, gs](std::vector<LinkedSlots> & artsPack,
CArtifactSet & artSet, EBulkArtsOp operation) -> void
const auto bulkArtsPut = [](std::vector<LinkedSlots> & artsPack, CArtifactSet & initArtSet, CArtifactSet & dstArtSet)
{
int numBackpackArtifactsMoved = 0;
for(auto & slot : artsPack)
for(const auto & slotsPair : artsPack)
{
// When an object gets removed from the backpack, the backpack shrinks
// so all the following indices will be affected. Thus, we need to update
// the subsequent artifact slots to account for that
auto srcPos = slot.srcPos;
if(ArtifactUtils::isSlotBackpack(srcPos) && (operation != EBulkArtsOp::BULK_PUT))
{
srcPos = ArtifactPosition(srcPos.num - numBackpackArtifactsMoved);
}
auto * art = artSet.getArt(srcPos);
auto * art = initArtSet.getArt(slotsPair.srcPos);
assert(art);
switch(operation)
{
case EBulkArtsOp::BULK_MOVE:
art->move(artSet, srcPos, *gs->getArtSet(ArtifactLocation(dstArtHolder, dstCreature)), slot.dstPos);
break;
case EBulkArtsOp::BULK_REMOVE:
art->removeFrom(artSet, srcPos);
break;
case EBulkArtsOp::BULK_PUT:
art->putAt(*gs->getArtSet(ArtifactLocation(srcArtHolder, srcCreature)), slot.dstPos);
break;
default:
break;
}
if(srcPos >= ArtifactPosition::BACKPACK_START)
{
numBackpackArtifactsMoved++;
}
art->putAt(dstArtSet, slotsPair.dstPos);
}
};
auto * leftSet = gs->getArtSet(ArtifactLocation(srcArtHolder, srcCreature));
if(swap)
{
// Swap
assert(leftSet);
auto * rightSet = gs->getArtSet(ArtifactLocation(dstArtHolder, dstCreature));
CArtifactFittingSet artFittingSet(leftSet->bearerType());
artFittingSet.artifactsWorn = rightSet->artifactsWorn;
artFittingSet.artifactsInBackpack = rightSet->artifactsInBackpack;
bulkArtsOperation(artsPack1, *rightSet, EBulkArtsOp::BULK_REMOVE);
bulkArtsOperation(artsPack0, *leftSet, EBulkArtsOp::BULK_MOVE);
bulkArtsOperation(artsPack1, artFittingSet, EBulkArtsOp::BULK_PUT);
}
else
assert(rightSet);
CArtifactFittingSet artInitialSetLeft(*leftSet);
bulkArtsRemove(artsPack0, *leftSet);
if(!artsPack1.empty())
{
bulkArtsOperation(artsPack0, *leftSet, EBulkArtsOp::BULK_MOVE);
CArtifactFittingSet artInitialSetRight(*rightSet);
bulkArtsRemove(artsPack1, *rightSet);
bulkArtsPut(artsPack1, artInitialSetRight, *leftSet);
}
bulkArtsPut(artsPack0, artInitialSetLeft, *rightSet);
}
void AssembledArtifact::applyGs(CGameState *gs)

View File

@ -1291,8 +1291,8 @@ struct DLL_LINKAGE ChangeObjectVisitors : public CPackForClient
struct DLL_LINKAGE ChangeArtifactsCostume : public CPackForClient
{
std::map<ArtifactPosition, ArtifactID> costumeSet;
size_t costumeIdx;
const PlayerColor player;
size_t costumeIdx = 0;
const PlayerColor player = PlayerColor::NEUTRAL;
void applyGs(CGameState * gs) const;
void visitTyped(ICPackVisitor & visitor) override;

View File

@ -2896,13 +2896,11 @@ bool CGameHandler::saveArtifactsCostume(const PlayerColor & player, const Object
{
auto artSet = getArtSet(heroID);
COMPLAIN_RET_FALSE_IF(artSet == nullptr, "saveArtifactsCostume: wrong hero's ID");
COMPLAIN_RET_FALSE_IF(costumeIdx >= GameConstants::HERO_COSTUMES_ARTIFACTS, "saveArtifactsCostume: wrong costume index");
ChangeArtifactsCostume costume(player, costumeIdx);
for(const auto & slot : ArtifactUtils::constituentWornSlots())
for(const auto & slot : ArtifactUtils::commonWornSlots())
{
if(const auto & slotInfo = artSet->getSlot(slot))
if(!slotInfo->locked)
if(const auto slotInfo = artSet->getSlot(slot); slotInfo != nullptr && !slotInfo->locked)
costume.costumeSet.emplace(slot, slotInfo->getArt()->getTypeId());
}
@ -2910,6 +2908,62 @@ bool CGameHandler::saveArtifactsCostume(const PlayerColor & player, const Object
return true;
}
bool CGameHandler::switchArtifactsCostume(const PlayerColor & player, const ObjectInstanceID heroID, size_t costumeIdx)
{
const auto artSet = getArtSet(heroID);
COMPLAIN_RET_FALSE_IF(artSet == nullptr, "switchArtifactsCostume: wrong hero's ID");
const auto playerState = getPlayerState(player);
COMPLAIN_RET_FALSE_IF(playerState == nullptr, "switchArtifactsCostume: wrong player");
if(auto costume = playerState->costumesArtifacts.find(costumeIdx); costume != playerState->costumesArtifacts.end())
{
CArtifactFittingSet artFittingSet(*artSet);
BulkMoveArtifacts bma(player, heroID, heroID, false);
auto costumeArtMap = costume->second;
auto estimateBackpackSize = artSet->artifactsInBackpack.size();
// First, find those artifacts that are already in place
for(const auto & slot : ArtifactUtils::commonWornSlots())
{
if(const auto * slotInfo = artFittingSet.getSlot(slot); slotInfo != nullptr && !slotInfo->locked)
if(const auto artPos = costumeArtMap.find(slot); artPos != costumeArtMap.end() && artPos->second == slotInfo->getArt()->getTypeId())
{
costumeArtMap.erase(artPos);
artFittingSet.removeArtifact(slot);
}
}
// Second, find the necessary artifacts for the costume
for(const auto & artPos : costumeArtMap)
{
if(const auto availableArts = artFittingSet.getAllArtPositions(artPos.second, false, false, false); !availableArts.empty())
{
bma.artsPack0.emplace_back(BulkMoveArtifacts::LinkedSlots
{
artSet->getSlotByInstance(artFittingSet.getArt(availableArts.front())),
artPos.first
});
artFittingSet.removeArtifact(availableArts.front());
if(ArtifactUtils::isSlotBackpack(availableArts.front()))
estimateBackpackSize--;
}
}
// Third, put unnecessary artifacts into backpack
for(const auto & slot : ArtifactUtils::commonWornSlots())
if(artFittingSet.getArt(slot))
{
bma.artsPack0.emplace_back(BulkMoveArtifacts::LinkedSlots{slot, ArtifactPosition::BACKPACK_START});
estimateBackpackSize++;
}
const auto backpackCap = VLC->settings()->getInteger(EGameSettings::HEROES_BACKPACK_CAP);
if((backpackCap < 0 || estimateBackpackSize <= backpackCap) && !bma.artsPack0.empty())
sendAndApply(&bma);
}
return true;
}
/**
* Assembles or disassembles a combination artifact.
* @param heroID ID of hero holding the artifact(s).

View File

@ -131,6 +131,7 @@ public:
bool bulkMoveArtifacts(const PlayerColor & player, ObjectInstanceID srcId, ObjectInstanceID dstId, bool swap, bool equipped, bool backpack);
bool scrollBackpackArtifacts(const PlayerColor & player, const ObjectInstanceID heroID, bool left);
bool saveArtifactsCostume(const PlayerColor & player, const ObjectInstanceID heroID, size_t costumeIdx);
bool switchArtifactsCostume(const PlayerColor & player, const ObjectInstanceID heroID, size_t costumeIdx);
bool eraseArtifactByClient(const ArtifactLocation & al);
void synchronizeArtifactHandlerLists();

View File

@ -183,8 +183,9 @@ void ApplyGhNetPackVisitor::visitManageEquippedArtifacts(ManageEquippedArtifacts
{
gh.throwIfWrongOwner(&pack, pack.artHolder);
if(pack.saveCostume)
gh.saveArtifactsCostume(pack.player, pack.artHolder, pack.costumeIdx);
result = true;
result = gh.saveArtifactsCostume(pack.player, pack.artHolder, pack.costumeIdx);
else
result = gh.switchArtifactsCostume(pack.player, pack.artHolder, pack.costumeIdx);
}
void ApplyGhNetPackVisitor::visitAssembleArtifacts(AssembleArtifacts & pack)