Переглянути джерело

Some work on artifact assembly.

OnionKnight 15 роки тому
батько
коміт
ab75f4b1f8

+ 18 - 0
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<CGTownInstance *>(town);

+ 2 - 1
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);

+ 17 - 1
client/GUIClasses.cpp

@@ -27,6 +27,7 @@
 #include "../lib/map.h"
 #include "../mapHandler.h"
 #include "../timeHandler.h"
+#include <boost/foreach.hpp>
 #include <boost/lexical_cast.hpp>
 #include <boost/format.hpp>
 #include <boost/algorithm/string.hpp>
@@ -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;
+		}*/
+	}
 }
 
 /**

+ 45 - 1
hch/CArtHandler.cpp

@@ -100,6 +100,29 @@ bool CArtifact::fitsAt (const std::map<ui16, ui32> &artifWorn, ui16 slotID) cons
 	return true;
 }
 
+bool CArtifact::canBeAssembledTo (const std::map<ui16, ui32> &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<ui16, ui32>::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<CArtifact>::iterator it = artifacts.begin(); it != artifacts.end(); ++it) {
+		delete it->constituents;
+		delete it->constituentOf;
+	}
+}
+
 void CArtHandler::loadArtifacts(bool onlyTxt)
 {
 	std::vector<ui16> 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<ui32>();
@@ -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<ui32>();
+				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<ui16, ui32> &artifWorn, ui16 slotID)
 					}
 				}
 			}
-
 		}
 	}
 }

+ 4 - 1
hch/CArtHandler.h

@@ -27,17 +27,19 @@ public:
 	const std::string &Description() const; //getter
 	bool isBig () const;
 	bool fitsAt (const std::map<ui16, ui32> &artifWorn, ui16 slot) const;
+	bool canBeAssembledTo (const std::map<ui16, ui32> &artifWorn, ui32 artifactID) const;
 
 	ui32 price;
 	std::vector<ui16> possibleSlots; //ids of slots where artifact can be placed
 	std::vector<ui32> * constituents; // Artifacts IDs a combined artifact consists of, or NULL.
+	std::vector<ui32> * constituentOf; // Reverse map of constituents.
 	EartClass aClass;
 	ui32 id;
 	std::list<HeroBonus> bonuses; //bonuses given by artifact
 
 	template <typename Handler> 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<ui16, ui32> &artifWorn, ui16 slotID);
 	static int convertMachineID(int id, bool creToArt);
 	CArtHandler();
+	~CArtHandler();
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{

+ 17 - 0
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 <typename Handler> void serialize(Handler &h, const int version)
+	{
+		h & heroID & artifactSlot & assemble & assembleTo;
+	}
+};
+
 struct BuyArtifact : public CPackForServer
 {
 	BuyArtifact(){};

+ 1 - 0
lib/RegisterTypes.cpp

@@ -143,6 +143,7 @@ void registerTypes3(Serializer &s)
 	s.template registerType<UpgradeCreature>();
 	s.template registerType<GarrisonHeroSwap>();
 	s.template registerType<ExchangeArtifacts>();
+	s.template registerType<AssembleArtifacts>();
 	s.template registerType<BuyArtifact>();
 	s.template registerType<TradeOnMarketplace>();
 	s.template registerType<SetFormation>();

+ 79 - 0
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<ui16, ui32>::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);

+ 1 - 0
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);

+ 6 - 0
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);