diff --git a/CCallback.cpp b/CCallback.cpp index a386d5efc..01bad1e34 100644 --- a/CCallback.cpp +++ b/CCallback.cpp @@ -433,6 +433,24 @@ bool CCallback::swapArtifacts(const CGHeroInstance * hero1, ui16 pos1, const CGH return true; } +/** + * Assembles or disassembles a combination artifact. + * @param hero Hero holding the artifact(s). + * @param artifactSlot The worn slot ID of the combination- or constituent artifact. + * @param assemble True for assembly operation, false for disassembly. + * @param assembleTo If assemble is true, this represents the artifact ID of the combination + * artifact to assemble to. Otherwise it's not used. + */ +bool CCallback::assembleArtifacts (const CGHeroInstance * hero, ui16 artifactSlot, bool assemble, ui32 assembleTo) +{ + if (player != hero->tempOwner) + return false; + + AssembleArtifacts aa(hero->id, artifactSlot, assemble, assembleTo); + sendRequest(&aa); + return true; +} + bool CCallback::buildBuilding(const CGTownInstance *town, si32 buildingID) { CGTownInstance * t = const_cast(town); diff --git a/CCallback.h b/CCallback.h index 95ca43963..cb2e78c57 100644 --- a/CCallback.h +++ b/CCallback.h @@ -89,6 +89,7 @@ public: virtual int mergeStacks(const CArmedInstance *s1, const CArmedInstance *s2, int p1, int p2)=0;//joins first stack tothe second (creatures must be same type) virtual int splitStack(const CArmedInstance *s1, const CArmedInstance *s2, int p1, int p2, int val)=0;//split creatures from the first stack virtual bool swapArtifacts(const CGHeroInstance * hero1, ui16 pos1, const CGHeroInstance * hero2, ui16 pos2)=0; //swaps artifacts between two given heroes + virtual bool assembleArtifacts(const CGHeroInstance * hero, ui16 artifactSlot, bool assemble, ui32 assembleTo)=0; virtual bool dismissCreature(const CArmedInstance *obj, int stackPos)=0; virtual void endTurn()=0; virtual void buyArtifact(const CGHeroInstance *hero, int aid)=0; //used to buy artifacts in towns (including spell book in the guild and war machines in blacksmith) @@ -207,7 +208,7 @@ public: int splitStack(const CArmedInstance *s1, const CArmedInstance *s2, int p1, int p2, int val); bool dismissHero(const CGHeroInstance * hero); bool swapArtifacts(const CGHeroInstance * hero1, ui16 pos1, const CGHeroInstance * hero2, ui16 pos2); - bool setArtifact(const CGHeroInstance * hero, ui16 pos, int artID); + bool assembleArtifacts(const CGHeroInstance * hero, ui16 artifactSlot, bool assemble, ui32 assembleTo); bool buildBuilding(const CGTownInstance *town, si32 buildingID); void recruitCreatures(const CGObjectInstance *obj, ui32 ID, ui32 amount); bool dismissCreature(const CArmedInstance *obj, int stackPos); diff --git a/client/GUIClasses.cpp b/client/GUIClasses.cpp index 1e4144e65..aacbb5e98 100644 --- a/client/GUIClasses.cpp +++ b/client/GUIClasses.cpp @@ -27,6 +27,7 @@ #include "../lib/map.h" #include "../mapHandler.h" #include "../timeHandler.h" +#include #include #include #include @@ -3689,8 +3690,23 @@ void CArtPlace::clickLeft(tribool down, bool previousState) void CArtPlace::clickRight(tribool down, bool previousState) { - if(!locked() && text.size()) //if there is no description or it's a lock, do nothing ;] + if(ourArt && !locked() && text.size()) { //if there is no description or it's a lock, do nothing ;] LRClickableAreaWTextComp::clickRight(down, previousState); + + /*if (ourArt->constituentOf != NULL) { + BOOST_FOREACH(ui32 combination, *ourArt->constituentOf) { + if (ourArt->canBeAssembledTo(ourOwner->curHero->artifWorn, combination)) { + LOCPLINT->cb->assembleArtifacts(ourOwner->curHero, slotID, true, combination); + return; + } + } + } + + if (ourArt->constituents != NULL) { + LOCPLINT->cb->assembleArtifacts(ourOwner->curHero, slotID, false, 0); + return; + }*/ + } } /** diff --git a/hch/CArtHandler.cpp b/hch/CArtHandler.cpp index 264aff6f6..e6d7949b6 100644 --- a/hch/CArtHandler.cpp +++ b/hch/CArtHandler.cpp @@ -100,6 +100,29 @@ bool CArtifact::fitsAt (const std::map &artifWorn, ui16 slotID) cons return true; } +bool CArtifact::canBeAssembledTo (const std::map &artifWorn, ui32 artifactID) const +{ + if (constituentOf == NULL || !vstd::contains(*constituentOf, artifactID)) + return false; + + const CArtifact &artifact = VLC->arth->artifacts[artifactID]; + assert(artifact.constituents); + + BOOST_FOREACH(ui32 constituentID, *artifact.constituents) { + bool found = false; + for (std::map::const_iterator it = artifWorn.begin(); it != artifWorn.end(); ++it) { + if (it->second == constituentID) { + found = true; + break; + } + } + if (!found) + return false; + } + + return true; +} + CArtHandler::CArtHandler() { VLC->arth = this; @@ -108,6 +131,15 @@ CArtHandler::CArtHandler() for (ui32 i = 3; i <= 6; i++) bigArtifacts.insert(i); } + +CArtHandler::~CArtHandler() +{ + for (std::vector::iterator it = artifacts.begin(); it != artifacts.end(); ++it) { + delete it->constituents; + delete it->constituentOf; + } +} + void CArtHandler::loadArtifacts(bool onlyTxt) { std::vector slots; @@ -145,6 +177,7 @@ void CArtHandler::loadArtifacts(bool onlyTxt) desc = desc.substr(1,desc.size()-2); // Fill in information about combined artifacts. Should perhaps be moved to a config file? + nart.constituentOf = NULL; switch (nart.id) { case 129: // Angelic Alliance nart.constituents = new std::vector(); @@ -219,6 +252,17 @@ void CArtHandler::loadArtifacts(bool onlyTxt) sortArts(); if(!onlyTxt) addBonuses(); + + // Populate reverse mappings of combinational artifacts. + BOOST_FOREACH(CArtifact artifact, artifacts) { + if (artifact.constituents != NULL) { + BOOST_FOREACH(ui32 constituentID, *artifact.constituents) { + if (artifacts[constituentID].constituentOf == NULL) + artifacts[constituentID].constituentOf = new std::vector(); + artifacts[constituentID].constituentOf->push_back(artifact.id); + } + } + } } int CArtHandler::convertMachineID(int id, bool creToArt ) @@ -507,6 +551,7 @@ void CArtHandler::addBonuses() giveArtBonus(134, HeroBonus::LEVEL_SPELL_IMMUNITY, 4); //Titan's Thunder + // should also add a permanent spell book, somehow. ART_ATTACK_AND_DEFENSE(135, +9); ART_POWER_AND_KNOWLEDGE(135, +8); giveArtBonus(135, HeroBonus::SPELL, 3, 57); @@ -612,7 +657,6 @@ void CArtHandler::unequipArtifact (std::map &artifWorn, ui16 slotID) } } } - } } } diff --git a/hch/CArtHandler.h b/hch/CArtHandler.h index 0ab3d7457..f46f3f148 100644 --- a/hch/CArtHandler.h +++ b/hch/CArtHandler.h @@ -27,17 +27,19 @@ public: const std::string &Description() const; //getter bool isBig () const; bool fitsAt (const std::map &artifWorn, ui16 slot) const; + bool canBeAssembledTo (const std::map &artifWorn, ui32 artifactID) const; ui32 price; std::vector possibleSlots; //ids of slots where artifact can be placed std::vector * constituents; // Artifacts IDs a combined artifact consists of, or NULL. + std::vector * constituentOf; // Reverse map of constituents. EartClass aClass; ui32 id; std::list bonuses; //bonuses given by artifact template void serialize(Handler &h, const int version) { - h & name & description & price & possibleSlots & constituents & aClass & id & bonuses; + h & name & description & price & possibleSlots & constituents & constituentOf & aClass & id & bonuses; } }; @@ -58,6 +60,7 @@ public: void unequipArtifact (std::map &artifWorn, ui16 slotID); static int convertMachineID(int id, bool creToArt); CArtHandler(); + ~CArtHandler(); template void serialize(Handler &h, const int version) { diff --git a/lib/NetPacks.h b/lib/NetPacks.h index 7262b5354..57f7b9279 100644 --- a/lib/NetPacks.h +++ b/lib/NetPacks.h @@ -1284,6 +1284,23 @@ struct ExchangeArtifacts : public CPackForServer } }; +struct AssembleArtifacts : public CPackForServer +{ + AssembleArtifacts(){}; + AssembleArtifacts(si32 _heroID, ui16 _artifactSlot, bool _assemble, ui32 _assembleTo) + : heroID(_heroID), artifactSlot(_artifactSlot), assemble(_assemble), assembleTo(_assembleTo){}; + si32 heroID; + ui16 artifactSlot; + bool assemble; // True to assemble artifact, false to disassemble. + ui32 assembleTo; // Artifact to assemble into. + + bool applyGh(CGameHandler *gh); + template void serialize(Handler &h, const int version) + { + h & heroID & artifactSlot & assemble & assembleTo; + } +}; + struct BuyArtifact : public CPackForServer { BuyArtifact(){}; diff --git a/lib/RegisterTypes.cpp b/lib/RegisterTypes.cpp index fbc0263c5..401e828dd 100644 --- a/lib/RegisterTypes.cpp +++ b/lib/RegisterTypes.cpp @@ -143,6 +143,7 @@ void registerTypes3(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/server/CGameHandler.cpp b/server/CGameHandler.cpp index 39fcab578..f39d128fa 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -2625,6 +2625,85 @@ bool CGameHandler::swapArtifacts(si32 srcHeroID, si32 destHeroID, ui16 srcSlot, return true; } +/** + * Assembles or disassembles a combination artifact. + * @param heroID ID of hero holding the artifact(s). + * @param artifactSlot The worn slot ID of the combination- or constituent artifact. + * @param assemble True for assembly operation, false for disassembly. + * @param assembleTo If assemble is true, this represents the artifact ID of the combination + * artifact to assemble to. Otherwise it's not used. + */ +bool CGameHandler::assembleArtifacts (si32 heroID, ui16 artifactSlot, bool assemble, ui32 assembleTo) +{ + if (artifactSlot < 0 || artifactSlot > 18) { + complain("Illegal artifact slot."); + return false; + } + + CGHeroInstance *hero = gs->getHero(heroID); + const CArtifact *destArtifact = hero->getArt(artifactSlot); + + SetHeroArtifacts sha; + sha.hid = heroID; + sha.artifacts = hero->artifacts; + sha.artifWorn = hero->artifWorn; + + if (assemble) { + if (VLC->arth->artifacts.size() >= assembleTo) { + complain("Illegal artifact to assemble to."); + return false; + } + + if (!destArtifact->canBeAssembledTo(hero->artifWorn, assembleTo)) { + complain("Artifact cannot be assembled."); + return false; + } + + const CArtifact &artifact = VLC->arth->artifacts[assembleTo]; + + if (artifact.constituents == NULL) { + complain("Not a combinational artifact."); + return false; + } + + bool destConsumed = false; // Determines which constituent that will be counted for together with the artifact. + BOOST_FOREACH(ui32 constituentID, *artifact.constituents) { + bool found = false; + for (std::map::iterator it = sha.artifWorn.begin(); it != sha.artifWorn.end(); ++it) { + if (it->second == constituentID) { + if (!destConsumed && vstd::contains(artifact.possibleSlots, it->first)) { + it->second = assembleTo; + destConsumed = true; + } else { + it->second = 145; + } + found = true; + break; + } + } + if (!found) { + complain("Constituent missing."); + return false; + } + } + } else { + BOOST_FOREACH(ui32 constituentID, *destArtifact->constituents) { + const CArtifact &constituent = VLC->arth->artifacts[constituentID]; + + BOOST_REVERSE_FOREACH(ui16 slotID, constituent.possibleSlots) { + if (sha.artifWorn.find(slotID) != sha.artifWorn.end()) { + if (sha.artifWorn[slotID] == 145 || slotID == artifactSlot) + sha.artifWorn[slotID] = constituentID; + } + } + } + } + + sendAndApply(&sha); + + return true; +} + bool CGameHandler::buyArtifact( ui32 hid, si32 aid ) { CGHeroInstance *hero = gs->getHero(hid); diff --git a/server/CGameHandler.h b/server/CGameHandler.h index f106b6293..86d1ec3f0 100644 --- a/server/CGameHandler.h +++ b/server/CGameHandler.h @@ -163,6 +163,7 @@ public: bool buildBoat( ui32 objid ); bool setFormation( si32 hid, ui8 formation ); bool tradeResources( ui32 val, ui8 player, ui32 id1, ui32 id2 ); + bool assembleArtifacts (si32 heroID, ui16 artifactSlot, bool assemble, ui32 assembleTo); bool buyArtifact( ui32 hid, si32 aid ); bool swapArtifacts(si32 srcHeroID, si32 destHeroID, ui16 srcSlot, ui16 destSlot); bool garrisonSwap(si32 tid); diff --git a/server/NetPacksServer.cpp b/server/NetPacksServer.cpp index 6d39fbc99..83c67ca5b 100644 --- a/server/NetPacksServer.cpp +++ b/server/NetPacksServer.cpp @@ -98,6 +98,12 @@ bool ExchangeArtifacts::applyGh( CGameHandler *gh ) return gh->swapArtifacts(hid1,hid2,slot1,slot2); } +bool AssembleArtifacts::applyGh( CGameHandler *gh ) +{ + ERROR_IF_NOT_OWNS(heroID); + return gh->assembleArtifacts(heroID, artifactSlot, assemble, assembleTo); +} + bool BuyArtifact::applyGh( CGameHandler *gh ) { ERROR_IF_NOT_OWNS(hid);