From 25dea7e3645fd9b1f2e77991044647811e99857b Mon Sep 17 00:00:00 2001 From: SoundSSGood <87084363+SoundSSGood@users.noreply.github.com> Date: Sat, 30 Mar 2024 17:18:50 +0200 Subject: [PATCH] Saving costume --- CCallback.cpp | 8 ++++ CCallback.h | 2 + client/gui/Shortcut.h | 10 +++++ client/gui/ShortcutHandler.cpp | 10 +++++ client/widgets/CArtifactsOfHeroMain.cpp | 36 +++++++++++++++++- client/widgets/CArtifactsOfHeroMain.h | 39 +++++++++++++++++--- client/windows/CHeroWindow.cpp | 2 +- config/shortcutsConfig.json | 12 +++++- lib/CPlayerState.h | 2 + lib/networkPacks/NetPackVisitor.h | 2 + lib/networkPacks/NetPacksLib.cpp | 19 ++++++++++ lib/networkPacks/PacksForClient.h | 24 ++++++++++++ lib/networkPacks/PacksForServer.h | 25 +++++++++++++ lib/registerTypes/RegisterTypesClientPacks.h | 1 + lib/registerTypes/RegisterTypesServerPacks.h | 3 +- server/CGameHandler.cpp | 18 +++++++++ server/CGameHandler.h | 1 + server/NetPacksServer.cpp | 8 ++++ server/ServerNetPackVisitors.h | 1 + 19 files changed, 213 insertions(+), 10 deletions(-) diff --git a/CCallback.cpp b/CCallback.cpp index d78c0a660..5f4edfd50 100644 --- a/CCallback.cpp +++ b/CCallback.cpp @@ -193,6 +193,14 @@ void CCallback::scrollBackpackArtifacts(ObjectInstanceID hero, bool left) sendRequest(&mba); } +void CCallback::manageHeroCostume(ObjectInstanceID hero, size_t costumeIndex, bool saveCostume) +{ + assert(costumeIndex < GameConstants::HERO_COSTUMES_ARTIFACTS); + + ManageEquippedArtifacts mea(hero, costumeIndex, saveCostume); + sendRequest(&mea); +} + void CCallback::eraseArtifactByClient(const ArtifactLocation & al) { EraseArtifactByClient ea(al); diff --git a/CCallback.h b/CCallback.h index 9c559e768..17cbf12fb 100644 --- a/CCallback.h +++ b/CCallback.h @@ -92,6 +92,7 @@ public: //virtual bool swapArtifacts(const CGHeroInstance * hero1, ui16 pos1, const CGHeroInstance * hero2, ui16 pos2)=0; //swaps artifacts between two given heroes 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 eraseArtifactByClient(const ArtifactLocation & al)=0; virtual bool dismissCreature(const CArmedInstance *obj, SlotID stackPos)=0; @@ -178,6 +179,7 @@ public: void assembleArtifacts(const CGHeroInstance * hero, 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; void eraseArtifactByClient(const ArtifactLocation & al) override; bool buildBuilding(const CGTownInstance *town, BuildingID buildingID) override; void recruitCreatures(const CGDwelling * obj, const CArmedInstance * dst, CreatureID ID, ui32 amount, si32 level=-1) override; diff --git a/client/gui/Shortcut.h b/client/gui/Shortcut.h index 016251e57..fa3d3b720 100644 --- a/client/gui/Shortcut.h +++ b/client/gui/Shortcut.h @@ -158,6 +158,16 @@ enum class EShortcut HERO_TIGHT_FORMATION, HERO_TOGGLE_TACTICS, // b HERO_BACKPACK, + HERO_COSTUME_0, + HERO_COSTUME_1, + HERO_COSTUME_2, + HERO_COSTUME_3, + HERO_COSTUME_4, + HERO_COSTUME_5, + HERO_COSTUME_6, + HERO_COSTUME_7, + HERO_COSTUME_8, + HERO_COSTUME_9, // Spellbook screen SPELLBOOK_TAB_ADVENTURE, diff --git a/client/gui/ShortcutHandler.cpp b/client/gui/ShortcutHandler.cpp index 604c9d4d4..3b17f4ae6 100644 --- a/client/gui/ShortcutHandler.cpp +++ b/client/gui/ShortcutHandler.cpp @@ -180,6 +180,16 @@ EShortcut ShortcutHandler::findShortcut(const std::string & identifier ) const {"heroLooseFormation", EShortcut::HERO_LOOSE_FORMATION }, {"heroTightFormation", EShortcut::HERO_TIGHT_FORMATION }, {"heroToggleTactics", EShortcut::HERO_TOGGLE_TACTICS }, + {"heroCostume0", EShortcut::HERO_COSTUME_0 }, + {"heroCostume1", EShortcut::HERO_COSTUME_1 }, + {"heroCostume2", EShortcut::HERO_COSTUME_2 }, + {"heroCostume3", EShortcut::HERO_COSTUME_3 }, + {"heroCostume4", EShortcut::HERO_COSTUME_4 }, + {"heroCostume5", EShortcut::HERO_COSTUME_5 }, + {"heroCostume6", EShortcut::HERO_COSTUME_6 }, + {"heroCostume7", EShortcut::HERO_COSTUME_7 }, + {"heroCostume8", EShortcut::HERO_COSTUME_8 }, + {"heroCostume9", EShortcut::HERO_COSTUME_9 }, {"spellbookTabAdventure", EShortcut::SPELLBOOK_TAB_ADVENTURE }, {"spellbookTabCombat", EShortcut::SPELLBOOK_TAB_COMBAT } }; diff --git a/client/widgets/CArtifactsOfHeroMain.cpp b/client/widgets/CArtifactsOfHeroMain.cpp index 3bc53e444..62e98db03 100644 --- a/client/widgets/CArtifactsOfHeroMain.cpp +++ b/client/widgets/CArtifactsOfHeroMain.cpp @@ -10,13 +10,15 @@ #include "StdInc.h" #include "CArtifactsOfHeroMain.h" +#include "../gui/CGuiHandler.h" + #include "../CPlayerInterface.h" #include "../../lib/mapObjects/CGHeroInstance.h" #include "../../CCallback.h" #include "../../lib/networkPacks/ArtifactLocation.h" -CArtifactsOfHeroMain::CArtifactsOfHeroMain(const Point & position) +CArtifactsOfHeroMain::CArtifactsOfHeroMain(const Point & position, bool costumesEnabled) { init( std::bind(&CArtifactsOfHeroBase::clickPrassedArtPlace, this, _1, _2), @@ -24,9 +26,41 @@ CArtifactsOfHeroMain::CArtifactsOfHeroMain(const Point & position) 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(hotkey, + [this, hotkey, costumeIndex]() + { + CArtifactsOfHeroMain::onCostumeSelect(costumeIndex); + })); + keyProc->addUsedEvents(AEventsReceiver::KEYBOARD); + costumeIndex++; + } + } } CArtifactsOfHeroMain::~CArtifactsOfHeroMain() { CArtifactsOfHeroBase::putBackPickedArtifact(); } + +void CArtifactsOfHeroMain::onCostumeSelect(const size_t costumeIndex) +{ + 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(); +} diff --git a/client/widgets/CArtifactsOfHeroMain.h b/client/widgets/CArtifactsOfHeroMain.h index e41e51899..31f157274 100644 --- a/client/widgets/CArtifactsOfHeroMain.h +++ b/client/widgets/CArtifactsOfHeroMain.h @@ -11,15 +11,42 @@ #include "CArtifactsOfHeroBase.h" -VCMI_LIB_NAMESPACE_BEGIN - -struct ArtifactLocation; - -VCMI_LIB_NAMESPACE_END +#include "../gui/Shortcut.h" class CArtifactsOfHeroMain : public CArtifactsOfHeroBase { public: - CArtifactsOfHeroMain(const Point & position); + CArtifactsOfHeroMain(const Point & position, bool costumesEnabled = false); ~CArtifactsOfHeroMain() override; + +private: + // TODO may be removed if CKeyShortcut supports callbacks + class CKeyShortcutWrapper : public CKeyShortcut + { + public: + using KeyPressedFunctor = std::function; + + CKeyShortcutWrapper(const EShortcut & key, const KeyPressedFunctor & onCostumeSelect); + void clickPressed(const Point & cursorPosition) override; + + private: + KeyPressedFunctor onCostumeSelect; + }; + + const std::array hotkeys = + { + EShortcut::HERO_COSTUME_0, + EShortcut::HERO_COSTUME_1, + EShortcut::HERO_COSTUME_2, + EShortcut::HERO_COSTUME_3, + EShortcut::HERO_COSTUME_4, + EShortcut::HERO_COSTUME_5, + EShortcut::HERO_COSTUME_6, + EShortcut::HERO_COSTUME_7, + EShortcut::HERO_COSTUME_8, + EShortcut::HERO_COSTUME_9 + }; + std::vector> costumesSwitchers; + + void onCostumeSelect(const size_t costumeIndex); }; diff --git a/client/windows/CHeroWindow.cpp b/client/windows/CHeroWindow.cpp index 080e9e09a..8b5219cd7 100644 --- a/client/windows/CHeroWindow.cpp +++ b/client/windows/CHeroWindow.cpp @@ -218,7 +218,7 @@ void CHeroWindow::update(const CGHeroInstance * hero, bool redrawNeeded) } if(!arts) { - arts = std::make_shared(Point(-65, -8)); + arts = std::make_shared(Point(-65, -8), true); arts->setHero(curHero); addSetAndCallbacks(arts); } diff --git a/config/shortcutsConfig.json b/config/shortcutsConfig.json index 630478f60..2954bcf56 100644 --- a/config/shortcutsConfig.json +++ b/config/shortcutsConfig.json @@ -125,6 +125,16 @@ "heroTightFormation": "T", "heroToggleTactics": "B", "spellbookTabAdventure": "A", - "spellbookTabCombat": "C" + "spellbookTabCombat": "C", + "heroCostume0": "0", + "heroCostume1": "1", + "heroCostume2": "2", + "heroCostume3": "3", + "heroCostume4": "4", + "heroCostume5": "5", + "heroCostume6": "6", + "heroCostume7": "7", + "heroCostume8": "8", + "heroCostume9": "9" } } diff --git a/lib/CPlayerState.h b/lib/CPlayerState.h index 0afeec09a..4dde7d650 100644 --- a/lib/CPlayerState.h +++ b/lib/CPlayerState.h @@ -63,6 +63,7 @@ public: std::vector > dwellings; //used for town growth std::vector quests; //store info about all received quests std::vector battleBonuses; //additional bonuses to be added during battle with neutrals + std::map> costumesArtifacts; bool cheated; bool enteredWinningCheatCode, enteredLosingCheatCode; //if true, this player has entered cheat codes for loss / victory @@ -111,6 +112,7 @@ public: h & daysWithoutCastle; h & cheated; h & battleBonuses; + h & costumesArtifacts; h & enteredLosingCheatCode; h & enteredWinningCheatCode; h & static_cast(*this); diff --git a/lib/networkPacks/NetPackVisitor.h b/lib/networkPacks/NetPackVisitor.h index a36e0d703..f2495a452 100644 --- a/lib/networkPacks/NetPackVisitor.h +++ b/lib/networkPacks/NetPackVisitor.h @@ -87,6 +87,7 @@ public: virtual void visitInfoWindow(InfoWindow & pack) {} virtual void visitSetObjectProperty(SetObjectProperty & pack) {} virtual void visitChangeObjectVisitors(ChangeObjectVisitors & pack) {} + virtual void visitChangeArtifactsCostume(ChangeArtifactsCostume & pack) {} virtual void visitHeroLevelUp(HeroLevelUp & pack) {} virtual void visitCommanderLevelUp(CommanderLevelUp & pack) {} virtual void visitBlockingDialog(BlockingDialog & pack) {} @@ -132,6 +133,7 @@ public: virtual void visitExchangeArtifacts(ExchangeArtifacts & pack) {} virtual void visitBulkExchangeArtifacts(BulkExchangeArtifacts & pack) {} virtual void visitManageBackpackArtifacts(ManageBackpackArtifacts & pack) {} + virtual void visitManageEquippedArtifacts(ManageEquippedArtifacts & pack) {} virtual void visitAssembleArtifacts(AssembleArtifacts & pack) {} virtual void visitEraseArtifactByClient(EraseArtifactByClient & pack) {} virtual void visitBuyArtifact(BuyArtifact & pack) {} diff --git a/lib/networkPacks/NetPacksLib.cpp b/lib/networkPacks/NetPacksLib.cpp index 29844ed50..c39d8c76c 100644 --- a/lib/networkPacks/NetPacksLib.cpp +++ b/lib/networkPacks/NetPacksLib.cpp @@ -383,6 +383,11 @@ void ChangeObjectVisitors::visitTyped(ICPackVisitor & visitor) visitor.visitChangeObjectVisitors(*this); } +void ChangeArtifactsCostume::visitTyped(ICPackVisitor & visitor) +{ + visitor.visitChangeArtifactsCostume(*this); +} + void HeroLevelUp::visitTyped(ICPackVisitor & visitor) { visitor.visitHeroLevelUp(*this); @@ -613,6 +618,11 @@ void ManageBackpackArtifacts::visitTyped(ICPackVisitor & visitor) visitor.visitManageBackpackArtifacts(*this); } +void ManageEquippedArtifacts::visitTyped(ICPackVisitor & visitor) +{ + visitor.visitManageEquippedArtifacts(*this); +} + void AssembleArtifacts::visitTyped(ICPackVisitor & visitor) { visitor.visitAssembleArtifacts(*this); @@ -1062,6 +1072,15 @@ 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()) + costume->second = costumeSet; + else + allCostumes.emplace(costumeIdx, costumeSet); +} + void PlayerEndsGame::applyGs(CGameState * gs) const { PlayerState *p = gs->getPlayerState(player); diff --git a/lib/networkPacks/PacksForClient.h b/lib/networkPacks/PacksForClient.h index a287af945..8ece152dc 100644 --- a/lib/networkPacks/PacksForClient.h +++ b/lib/networkPacks/PacksForClient.h @@ -1288,6 +1288,30 @@ struct DLL_LINKAGE ChangeObjectVisitors : public CPackForClient } }; +struct DLL_LINKAGE ChangeArtifactsCostume : public CPackForClient +{ + std::map costumeSet; + size_t costumeIdx; + const PlayerColor player; + + void applyGs(CGameState * gs) const; + void visitTyped(ICPackVisitor & visitor) override; + + ChangeArtifactsCostume() = default; + ChangeArtifactsCostume(const PlayerColor & player, const size_t costumeIdx) + : costumeIdx(costumeIdx) + , player(player) + { + } + + template void serialize(Handler & h) + { + h & costumeSet; + h & costumeIdx; + h & player; + } +}; + struct DLL_LINKAGE HeroLevelUp : public Query { PlayerColor player; diff --git a/lib/networkPacks/PacksForServer.h b/lib/networkPacks/PacksForServer.h index 19f0b72fd..be4e71249 100644 --- a/lib/networkPacks/PacksForServer.h +++ b/lib/networkPacks/PacksForServer.h @@ -429,6 +429,31 @@ struct DLL_LINKAGE ManageBackpackArtifacts : public CPackForServer } }; +struct DLL_LINKAGE ManageEquippedArtifacts : public CPackForServer +{ + ManageEquippedArtifacts() = default; + ManageEquippedArtifacts(const ObjectInstanceID & artHolder, const size_t costumeIdx, bool saveCostume = false) + : artHolder(artHolder) + , costumeIdx(costumeIdx) + , saveCostume(saveCostume) + { + } + + ObjectInstanceID artHolder; + size_t costumeIdx; + bool saveCostume; + + void visitTyped(ICPackVisitor & visitor) override; + + template void serialize(Handler & h) + { + h & static_cast(*this); + h & artHolder; + h & costumeIdx; + h & saveCostume; + } +}; + struct DLL_LINKAGE AssembleArtifacts : public CPackForServer { AssembleArtifacts() = default; diff --git a/lib/registerTypes/RegisterTypesClientPacks.h b/lib/registerTypes/RegisterTypesClientPacks.h index 5ba7c3165..9bb4fb30d 100644 --- a/lib/registerTypes/RegisterTypesClientPacks.h +++ b/lib/registerTypes/RegisterTypesClientPacks.h @@ -67,6 +67,7 @@ void registerTypesClientPacks(Serializer &s) s.template registerType(); s.template registerType(); s.template registerType(); + s.template registerType(); s.template registerType(); s.template registerType(); s.template registerType(); diff --git a/lib/registerTypes/RegisterTypesServerPacks.h b/lib/registerTypes/RegisterTypesServerPacks.h index b0c3ae181..de13eae8e 100644 --- a/lib/registerTypes/RegisterTypesServerPacks.h +++ b/lib/registerTypes/RegisterTypesServerPacks.h @@ -49,7 +49,8 @@ void registerTypesServerPacks(Serializer &s) s.template registerType(); s.template registerType(); s.template registerType(); - s.template registerType < CPackForServer, ManageBackpackArtifacts>(); + s.template registerType(); + s.template registerType(); s.template registerType(); s.template registerType(); } diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index a11532013..c8899c065 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -2892,6 +2892,24 @@ bool CGameHandler::scrollBackpackArtifacts(const PlayerColor & player, const Obj return true; } +bool CGameHandler::saveArtifactsCostume(const PlayerColor & player, const ObjectInstanceID heroID, size_t costumeIdx) +{ + 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()) + { + if(const auto & slotInfo = artSet->getSlot(slot)) + if(!slotInfo->locked) + costume.costumeSet.emplace(slot, slotInfo->getArt()->getTypeId()); + } + + sendAndApply(&costume); + return true; +} + /** * Assembles or disassembles a combination artifact. * @param heroID ID of hero holding the artifact(s). diff --git a/server/CGameHandler.h b/server/CGameHandler.h index 80c3ab3ae..8de08316b 100644 --- a/server/CGameHandler.h +++ b/server/CGameHandler.h @@ -130,6 +130,7 @@ public: bool moveArtifact(const PlayerColor & player, const ArtifactLocation & src, const ArtifactLocation & dst) override; 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 eraseArtifactByClient(const ArtifactLocation & al); void synchronizeArtifactHandlerLists(); diff --git a/server/NetPacksServer.cpp b/server/NetPacksServer.cpp index 9e9d7e7f5..e35049fda 100644 --- a/server/NetPacksServer.cpp +++ b/server/NetPacksServer.cpp @@ -179,6 +179,14 @@ void ApplyGhNetPackVisitor::visitManageBackpackArtifacts(ManageBackpackArtifacts } } +void ApplyGhNetPackVisitor::visitManageEquippedArtifacts(ManageEquippedArtifacts & pack) +{ + gh.throwIfWrongOwner(&pack, pack.artHolder); + if(pack.saveCostume) + gh.saveArtifactsCostume(pack.player, pack.artHolder, pack.costumeIdx); + result = true; +} + void ApplyGhNetPackVisitor::visitAssembleArtifacts(AssembleArtifacts & pack) { gh.throwIfWrongOwner(&pack, pack.heroID); diff --git a/server/ServerNetPackVisitors.h b/server/ServerNetPackVisitors.h index a56395d7e..568c8322a 100644 --- a/server/ServerNetPackVisitors.h +++ b/server/ServerNetPackVisitors.h @@ -47,6 +47,7 @@ public: void visitExchangeArtifacts(ExchangeArtifacts & pack) override; void visitBulkExchangeArtifacts(BulkExchangeArtifacts & pack) override; void visitManageBackpackArtifacts(ManageBackpackArtifacts & pack) override; + void visitManageEquippedArtifacts(ManageEquippedArtifacts & pack) override; void visitAssembleArtifacts(AssembleArtifacts & pack) override; void visitEraseArtifactByClient(EraseArtifactByClient & pack) override; void visitBuyArtifact(BuyArtifact & pack) override;