| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451 | /* * CArtHandler.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 "CArtHandler.h"#include "filesystem/Filesystem.h"#include "CGeneralTextHandler.h"#include "VCMI_Lib.h"#include "CModHandler.h"#include "CCreatureHandler.h"#include "spells/CSpellHandler.h"#include "mapObjects/MapObjects.h"#include "NetPacksBase.h"#include "StringConstants.h"#include "CRandomGenerator.h"#include "mapObjects/CObjectClassesHandler.h"#include "mapping/CMap.h"#include "serializer/JsonSerializeFormat.h"// Note: list must match entries in ArtTraits.txt#define ART_POS_LIST    \	ART_POS(SPELLBOOK)  \	ART_POS(MACH4)      \	ART_POS(MACH3)      \	ART_POS(MACH2)      \	ART_POS(MACH1)      \	ART_POS(MISC5)      \	ART_POS(MISC4)      \	ART_POS(MISC3)      \	ART_POS(MISC2)      \	ART_POS(MISC1)      \	ART_POS(FEET)       \	ART_POS(LEFT_RING)  \	ART_POS(RIGHT_RING) \	ART_POS(TORSO)      \	ART_POS(LEFT_HAND)  \	ART_POS(RIGHT_HAND) \	ART_POS(NECK)       \	ART_POS(SHOULDERS)  \	ART_POS(HEAD)const std::string & CArtifact::Name() const{	return name;}const std::string & CArtifact::Description() const{	return description;}const std::string & CArtifact::EventText() const{	return eventText;}bool CArtifact::isBig() const{	return warMachine != CreatureID::NONE;}bool CArtifact::isTradable() const{	switch(id)	{	case ArtifactID::SPELLBOOK:	case ArtifactID::GRAIL:		return false;	default:		return !isBig();	}}CArtifact::CArtifact(){	setNodeType(ARTIFACT);	possibleSlots[ArtBearer::HERO]; //we want to generate map entry even if it will be empty	possibleSlots[ArtBearer::CREATURE]; //we want to generate map entry even if it will be empty	possibleSlots[ArtBearer::COMMANDER];	iconIndex = ArtifactID::NONE;	price = 0;	aClass = ART_SPECIAL;}CArtifact::~CArtifact(){}int CArtifact::getArtClassSerial() const{	if(id == ArtifactID::SPELL_SCROLL)		return 4;	switch(aClass)	{	case ART_TREASURE:		return 0;	case ART_MINOR:		return 1;	case ART_MAJOR:		return 2;	case ART_RELIC:		return 3;	case ART_SPECIAL:		return 5;	}	return -1;}std::string CArtifact::nodeName() const{	return "Artifact: " + Name();}void CArtifact::addNewBonus(const std::shared_ptr<Bonus>& b){	b->source = Bonus::ARTIFACT;	b->duration = Bonus::PERMANENT;	b->description = name;	CBonusSystemNode::addNewBonus(b);}void CArtifact::fillWarMachine(){	switch(id)	{	case ArtifactID::CATAPULT:		warMachine = CreatureID::CATAPULT;		break;	case ArtifactID::BALLISTA:		warMachine = CreatureID::BALLISTA;		break;	case ArtifactID::FIRST_AID_TENT:		warMachine = CreatureID::FIRST_AID_TENT;		break;	case ArtifactID::AMMO_CART:		warMachine = CreatureID::AMMO_CART;		break;	default:		warMachine = CreatureID::NONE;		break;	}}void CGrowingArtifact::levelUpArtifact (CArtifactInstance * art){	auto b = std::make_shared<Bonus>();	b->type = Bonus::LEVEL_COUNTER;	b->val = 1;	b->duration = Bonus::COMMANDER_KILLED;	art->accumulateBonus(b);	for (auto bonus : bonusesPerLevel)	{		if (art->valOfBonuses(Bonus::LEVEL_COUNTER) % bonus.first == 0) //every n levels		{			art->accumulateBonus(std::make_shared<Bonus>(bonus.second));		}	}	for (auto bonus : thresholdBonuses)	{		if (art->valOfBonuses(Bonus::LEVEL_COUNTER) == bonus.first) //every n levels		{			art->addNewBonus(std::make_shared<Bonus>(bonus.second));		}	}}CArtHandler::CArtHandler(){}CArtHandler::~CArtHandler(){	for(CArtifact * art : artifacts)		delete art;}std::vector<JsonNode> CArtHandler::loadLegacyData(size_t dataSize){	artifacts.resize(dataSize);	std::vector<JsonNode> h3Data;	h3Data.reserve(dataSize);	#define ART_POS(x) #x ,	const std::vector<std::string> artSlots = { ART_POS_LIST };	#undef ART_POS	static std::map<char, std::string> classes =		{{'S',"SPECIAL"}, {'T',"TREASURE"},{'N',"MINOR"},{'J',"MAJOR"},{'R',"RELIC"},};	CLegacyConfigParser parser("DATA/ARTRAITS.TXT");	CLegacyConfigParser events("DATA/ARTEVENT.TXT");	parser.endLine(); // header	parser.endLine();	for (size_t i = 0; i < dataSize; i++)	{		JsonNode artData;		artData["text"]["name"].String() = parser.readString();		artData["text"]["event"].String() = events.readString();		artData["value"].Float() = parser.readNumber();		for(auto & artSlot : artSlots)		{			if(parser.readString() == "x")			{				artData["slot"].Vector().push_back(JsonNode());				artData["slot"].Vector().back().String() = artSlot;			}		}		artData["class"].String() = classes[parser.readString()[0]];		artData["text"]["description"].String() = parser.readString();		parser.endLine();		events.endLine();		h3Data.push_back(artData);	}	return h3Data;}void CArtHandler::loadObject(std::string scope, std::string name, const JsonNode & data){	auto object = loadFromJson(data, normalizeIdentifier(scope, "core", name));	object->id = ArtifactID(artifacts.size());	object->iconIndex = object->id + 5;	artifacts.push_back(object);	VLC->modh->identifiers.requestIdentifier(scope, "object", "artifact", [=](si32 index)	{		JsonNode conf;		conf.setMeta(scope);		VLC->objtypeh->loadSubObject(object->identifier, conf, Obj::ARTIFACT, object->id.num);		if (!object->advMapDef.empty())		{			JsonNode templ;			templ.setMeta(scope);			templ["animation"].String() = object->advMapDef;			// add new template.			// Necessary for objects added via mods that don't have any templates in H3			VLC->objtypeh->getHandlerFor(Obj::ARTIFACT, object->id)->addTemplate(templ);		}		// object does not have any templates - this is not usable object (e.g. pseudo-art like lock)		if (VLC->objtypeh->getHandlerFor(Obj::ARTIFACT, object->id)->getTemplates().empty())			VLC->objtypeh->removeSubObject(Obj::ARTIFACT, object->id);	});	registerObject(scope, "artifact", name, object->id);}void CArtHandler::loadObject(std::string scope, std::string name, const JsonNode & data, size_t index){	auto object = loadFromJson(data, normalizeIdentifier(scope, "core", name));	object->id = ArtifactID(index);	object->iconIndex = object->id;	assert(artifacts[index] == nullptr); // ensure that this id was not loaded before	artifacts[index] = object;	VLC->modh->identifiers.requestIdentifier(scope, "object", "artifact", [=](si32 index)	{		JsonNode conf;		conf.setMeta(scope);		VLC->objtypeh->loadSubObject(object->identifier, conf, Obj::ARTIFACT, object->id.num);		if (!object->advMapDef.empty())		{			JsonNode templ;			templ.setMeta(scope);			templ["animation"].String() = object->advMapDef;			// add new template.			// Necessary for objects added via mods that don't have any templates in H3			VLC->objtypeh->getHandlerFor(Obj::ARTIFACT, object->id)->addTemplate(templ);		}		// object does not have any templates - this is not usable object (e.g. pseudo-art like lock)		if (VLC->objtypeh->getHandlerFor(Obj::ARTIFACT, object->id)->getTemplates().empty())			VLC->objtypeh->removeSubObject(Obj::ARTIFACT, object->id);	});	registerObject(scope, "artifact", name, object->id);}CArtifact * CArtHandler::loadFromJson(const JsonNode & node, const std::string & identifier){	CArtifact * art;	if (!VLC->modh->modules.COMMANDERS || node["growing"].isNull())		art = new CArtifact();	else	{		auto  growing = new CGrowingArtifact();		loadGrowingArt(growing, node);		art = growing;	}	art->identifier = identifier;	const JsonNode & text = node["text"];	art->name        = text["name"].String();	art->description = text["description"].String();	art->eventText   = text["event"].String();	const JsonNode & graphics = node["graphics"];	art->image = graphics["image"].String();	if (!graphics["large"].isNull())		art->large = graphics["large"].String();	else		art->large = art->image;	art->advMapDef = graphics["map"].String();	art->price = node["value"].Float();	loadSlots(art, node);	loadClass(art, node);	loadType(art, node);	loadComponents(art, node);	for (auto b : node["bonuses"].Vector())	{		auto bonus = JsonUtils::parseBonus(b);		art->addNewBonus(bonus);	}	const JsonNode & warMachine = node["warMachine"];	if(warMachine.getType() == JsonNode::JsonType::DATA_STRING && warMachine.String() != "")	{		VLC->modh->identifiers.requestIdentifier("creature", warMachine, [=](si32 id)		{			art->warMachine = CreatureID(id);			//this assumes that creature object is stored before registration			VLC->creh->creatures.at(id)->warMachine = art->id;		});	}	return art;}ArtifactPosition CArtHandler::stringToSlot(std::string slotName){#define ART_POS(x) { #x, ArtifactPosition::x },	static const std::map<std::string, ArtifactPosition> artifactPositionMap = { ART_POS_LIST };#undef ART_POS	auto it = artifactPositionMap.find (slotName);	if (it != artifactPositionMap.end())		return it->second;	logMod->warn("Warning! Artifact slot %s not recognized!", slotName);	return ArtifactPosition::PRE_FIRST;}void CArtHandler::addSlot(CArtifact * art, const std::string & slotID){	static const std::vector<ArtifactPosition> miscSlots =	{		ArtifactPosition::MISC1, ArtifactPosition::MISC2, ArtifactPosition::MISC3, ArtifactPosition::MISC4, ArtifactPosition::MISC5	};	static const std::vector<ArtifactPosition> ringSlots =	{		ArtifactPosition::LEFT_RING, ArtifactPosition::RIGHT_RING	};	if (slotID == "MISC")	{		vstd::concatenate(art->possibleSlots[ArtBearer::HERO], miscSlots);	}	else if (slotID == "RING")	{		vstd::concatenate(art->possibleSlots[ArtBearer::HERO], ringSlots);	}	else	{		auto slot = stringToSlot(slotID);		if (slot != ArtifactPosition::PRE_FIRST)			art->possibleSlots[ArtBearer::HERO].push_back (slot);	}}void CArtHandler::loadSlots(CArtifact * art, const JsonNode & node){	if (!node["slot"].isNull()) //we assume non-hero slots are irrelevant?	{		if (node["slot"].getType() == JsonNode::JsonType::DATA_STRING)			addSlot(art, node["slot"].String());		else		{			for (const JsonNode & slot : node["slot"].Vector())				addSlot(art, slot.String());		}	}}CArtifact::EartClass CArtHandler::stringToClass(std::string className){	static const std::map<std::string, CArtifact::EartClass> artifactClassMap =	{		{"TREASURE", CArtifact::ART_TREASURE},		{"MINOR", CArtifact::ART_MINOR},		{"MAJOR", CArtifact::ART_MAJOR},		{"RELIC", CArtifact::ART_RELIC},		{"SPECIAL", CArtifact::ART_SPECIAL}	};	auto it = artifactClassMap.find (className);	if (it != artifactClassMap.end())		return it->second;	logMod->warn("Warning! Artifact rarity %s not recognized!", className);	return CArtifact::ART_SPECIAL;}void CArtHandler::loadClass(CArtifact * art, const JsonNode & node){	art->aClass = stringToClass(node["class"].String());}void CArtHandler::loadType(CArtifact * art, const JsonNode & node){#define ART_BEARER(x) { #x, ArtBearer::x },	static const std::map<std::string, int> artifactBearerMap = { ART_BEARER_LIST };#undef ART_BEARER	for (const JsonNode & b : node["type"].Vector())	{		auto it = artifactBearerMap.find (b.String());		if (it != artifactBearerMap.end())		{			int bearerType = it->second;			switch (bearerType)			{				case ArtBearer::HERO://TODO: allow arts having several possible bearers					break;				case ArtBearer::COMMANDER:					makeItCommanderArt (art); //original artifacts should have only one bearer type					break;				case ArtBearer::CREATURE:					makeItCreatureArt (art);					break;			}		}		else			logMod->warn("Warning! Artifact type %s not recognized!", b.String());	}}void CArtHandler::loadComponents(CArtifact * art, const JsonNode & node){	if (!node["components"].isNull())	{		art->constituents.reset(new std::vector<CArtifact *>());		for (auto component : node["components"].Vector())		{			VLC->modh->identifiers.requestIdentifier("artifact", component, [=](si32 id)			{				// when this code is called both combinational art as well as component are loaded				// so it is safe to access any of them				art->constituents->push_back(VLC->arth->artifacts[id]);				VLC->arth->artifacts[id]->constituentOf.push_back(art);			});		}	}}void CArtHandler::loadGrowingArt(CGrowingArtifact * art, const JsonNode & node){	for (auto b : node["growing"]["bonusesPerLevel"].Vector())	{		art->bonusesPerLevel.push_back(std::pair <ui16, Bonus>(b["level"].Float(), Bonus()));		JsonUtils::parseBonus(b["bonus"], &art->bonusesPerLevel.back().second);	}	for (auto b : node["growing"]["thresholdBonuses"].Vector())	{		art->thresholdBonuses.push_back(std::pair <ui16, Bonus>(b["level"].Float(), Bonus()));		JsonUtils::parseBonus(b["bonus"], &art->thresholdBonuses.back().second);	}}ArtifactID CArtHandler::pickRandomArtifact(CRandomGenerator & rand, int flags, std::function<bool(ArtifactID)> accepts){	auto getAllowedArts = [&](std::vector<ConstTransitivePtr<CArtifact> > &out, std::vector<CArtifact*> *arts, CArtifact::EartClass flag)	{		if (arts->empty()) //restock available arts			fillList(*arts, flag);		for (auto & arts_i : *arts)		{			if (accepts(arts_i->id))			{				CArtifact *art = arts_i;				out.push_back(art);			}		}	};	auto getAllowed = [&](std::vector<ConstTransitivePtr<CArtifact> > &out)	{		if (flags & CArtifact::ART_TREASURE)			getAllowedArts (out, &treasures, CArtifact::ART_TREASURE);		if (flags & CArtifact::ART_MINOR)			getAllowedArts (out, &minors, CArtifact::ART_MINOR);		if (flags & CArtifact::ART_MAJOR)			getAllowedArts (out, &majors, CArtifact::ART_MAJOR);		if (flags & CArtifact::ART_RELIC)			getAllowedArts (out, &relics, CArtifact::ART_RELIC);		if (!out.size()) //no artifact of specified rarity, we need to take another one		{			getAllowedArts (out, &treasures, CArtifact::ART_TREASURE);			getAllowedArts (out, &minors, CArtifact::ART_MINOR);			getAllowedArts (out, &majors, CArtifact::ART_MAJOR);			getAllowedArts (out, &relics, CArtifact::ART_RELIC);		}		if (!out.size()) //no arts are available at all		{			out.resize (64);			std::fill_n (out.begin(), 64, artifacts[2]); //Give Grail - this can't be banned (hopefully)		}	};	std::vector<ConstTransitivePtr<CArtifact> > out;	getAllowed(out);	ArtifactID artID = (*RandomGeneratorUtil::nextItem(out, rand))->id;	erasePickedArt(artID);	return artID;}ArtifactID CArtHandler::pickRandomArtifact(CRandomGenerator & rand, std::function<bool(ArtifactID)> accepts){	return pickRandomArtifact(rand, 0xff, accepts);}ArtifactID CArtHandler::pickRandomArtifact(CRandomGenerator & rand, int flags){	return pickRandomArtifact(rand, flags, [](ArtifactID){ return true;});}std::shared_ptr<Bonus> createBonus(Bonus::BonusType type, int val, int subtype, Bonus::ValueType valType, std::shared_ptr<ILimiter> limiter = std::shared_ptr<ILimiter>(), int additionalInfo = 0){	auto added = std::make_shared<Bonus>(Bonus::PERMANENT,type,Bonus::ARTIFACT,val,-1,subtype);	added->additionalInfo = additionalInfo;	added->valType = valType;	added->limiter = limiter;	return added;}std::shared_ptr<Bonus> createBonus(Bonus::BonusType type, int val, int subtype, std::shared_ptr<IPropagator> propagator = std::shared_ptr<IPropagator>(), int additionalInfo = 0){	auto added = std::make_shared<Bonus>(Bonus::PERMANENT,type,Bonus::ARTIFACT,val,-1,subtype);	added->additionalInfo = additionalInfo;	added->valType = Bonus::BASE_NUMBER;	added->propagator = propagator;	return added;}void CArtHandler::giveArtBonus( ArtifactID aid, Bonus::BonusType type, int val, int subtype, Bonus::ValueType valType, std::shared_ptr<ILimiter> limiter, int additionalInfo){	giveArtBonus(aid, createBonus(type, val, subtype, valType, limiter, additionalInfo));}void CArtHandler::giveArtBonus(ArtifactID aid, Bonus::BonusType type, int val, int subtype, std::shared_ptr<IPropagator> propagator, int additionalInfo){	giveArtBonus(aid, createBonus(type, val, subtype, propagator, additionalInfo));}void CArtHandler::giveArtBonus(ArtifactID aid, std::shared_ptr<Bonus> bonus){	bonus->sid = aid;	if(bonus->subtype == Bonus::MORALE || bonus->type == Bonus::LUCK)		bonus->description = artifacts[aid]->Name()  + (bonus->val > 0 ? " +" : " ") + boost::lexical_cast<std::string>(bonus->val);	else		bonus->description = artifacts[aid]->Name();	artifacts[aid]->addNewBonus(bonus);}void CArtHandler::makeItCreatureArt(CArtifact * a, bool onlyCreature){	if (onlyCreature)	{		a->possibleSlots[ArtBearer::HERO].clear();		a->possibleSlots[ArtBearer::COMMANDER].clear();	}	a->possibleSlots[ArtBearer::CREATURE].push_back(ArtifactPosition::CREATURE_SLOT);}void CArtHandler::makeItCreatureArt(ArtifactID aid, bool onlyCreature){	CArtifact *a = artifacts[aid];	makeItCreatureArt (a, onlyCreature);}void CArtHandler::makeItCommanderArt(CArtifact * a, bool onlyCommander){	if (onlyCommander)	{		a->possibleSlots[ArtBearer::HERO].clear();		a->possibleSlots[ArtBearer::CREATURE].clear();	}	for (int i = ArtifactPosition::COMMANDER1; i <= ArtifactPosition::COMMANDER6; ++i)		a->possibleSlots[ArtBearer::COMMANDER].push_back(ArtifactPosition(i));}void CArtHandler::makeItCommanderArt(ArtifactID aid, bool onlyCommander){	CArtifact *a = artifacts[aid];	makeItCommanderArt (a, onlyCommander);}bool CArtHandler::legalArtifact(ArtifactID id){	auto art = artifacts[id];	//assert ( (!art->constituents) || art->constituents->size() ); //artifacts is not combined or has some components	return ((art->possibleSlots[ArtBearer::HERO].size() ||		(art->possibleSlots[ArtBearer::COMMANDER].size() && VLC->modh->modules.COMMANDERS) ||		(art->possibleSlots[ArtBearer::CREATURE].size() && VLC->modh->modules.STACK_ARTIFACT)) &&		!(art->constituents) && //no combo artifacts spawning		art->aClass >= CArtifact::ART_TREASURE &&		art->aClass <= CArtifact::ART_RELIC);}void CArtHandler::initAllowedArtifactsList(const std::vector<bool> &allowed){	allowedArtifacts.clear();	treasures.clear();	minors.clear();	majors.clear();	relics.clear();	for (ArtifactID i=ArtifactID::SPELLBOOK; i<ArtifactID::ART_SELECTION; i.advance(1))	{		//check artifacts allowed on a map		//TODO: This line will be different when custom map format is implemented		if (allowed[i] && legalArtifact(i))			allowedArtifacts.push_back(artifacts[i]);	}	for (ArtifactID i = ArtifactID::ART_SELECTION; i<ArtifactID(artifacts.size()); i.advance(1)) //try to allow all artifacts added by mods	{		if (legalArtifact(ArtifactID(i)))			allowedArtifacts.push_back(artifacts[i]);			 //keep im mind that artifact can be worn by more than one type of bearer	}}std::vector<bool> CArtHandler::getDefaultAllowed() const{	std::vector<bool> allowedArtifacts;	allowedArtifacts.resize(127, true);	allowedArtifacts.resize(141, false);	allowedArtifacts.resize(artifacts.size(), true);	return allowedArtifacts;}void CArtHandler::erasePickedArt(ArtifactID id){	CArtifact *art = artifacts[id];	if(auto artifactList = listFromClass(art->aClass))	{		if(artifactList->empty())			fillList(*artifactList, art->aClass);		auto itr = vstd::find(*artifactList, art);		if(itr != artifactList->end())		{			artifactList->erase(itr);		}		else			logMod->warn("Problem: cannot erase artifact %s from list, it was not present", art->Name());	}	else		logMod->warn("Problem: cannot find list for artifact %s, strange class. (special?)", art->Name());}boost::optional<std::vector<CArtifact*>&> CArtHandler::listFromClass( CArtifact::EartClass artifactClass ){	switch(artifactClass)	{	case CArtifact::ART_TREASURE:		return treasures;	case CArtifact::ART_MINOR:		return minors;	case CArtifact::ART_MAJOR:		return majors;	case CArtifact::ART_RELIC:		return relics;	default: //special artifacts should not be erased		return boost::optional<std::vector<CArtifact*>&>();	}}void CArtHandler::fillList( std::vector<CArtifact*> &listToBeFilled, CArtifact::EartClass artifactClass ){	assert(listToBeFilled.empty());	for (auto & elem : allowedArtifacts)	{		if (elem->aClass == artifactClass)			listToBeFilled.push_back(elem);	}}void CArtHandler::afterLoadFinalization(){	//All artifacts have their id, so we can properly update their bonuses' source ids.	for(auto &art : artifacts)	{		for(auto &bonus : art->getExportedBonusList())		{			assert(art == artifacts[art->id]);			assert(bonus->source == Bonus::ARTIFACT);			bonus->sid = art->id;		}	}	CBonusSystemNode::treeHasChanged();}CArtifactInstance::CArtifactInstance(){	init();}CArtifactInstance::CArtifactInstance( CArtifact *Art){	init();	setType(Art);}void CArtifactInstance::setType( CArtifact *Art ){	artType = Art;	attachTo(Art);}std::string CArtifactInstance::nodeName() const{	return "Artifact instance of " + (artType ? artType->Name() : std::string("uninitialized")) + " type";}CArtifactInstance * CArtifactInstance::createScroll( const CSpell *s){	return createScroll(s->id);}CArtifactInstance *CArtifactInstance::createScroll(SpellID sid){	auto ret = new CArtifactInstance(VLC->arth->artifacts[ArtifactID::SPELL_SCROLL]);	auto b = std::make_shared<Bonus>(Bonus::PERMANENT, Bonus::SPELL, Bonus::ARTIFACT_INSTANCE, -1, ArtifactID::SPELL_SCROLL, sid);	ret->addNewBonus(b);	return ret;}void CArtifactInstance::init(){	id = ArtifactInstanceID();	id = static_cast<ArtifactInstanceID>(ArtifactID::NONE); //to be randomized	setNodeType(ARTIFACT_INSTANCE);}std::string CArtifactInstance::getEffectiveDescription(	const CGHeroInstance *hero) const{	std::string text = artType->Description();	if (!vstd::contains(text, '{'))		text = '{' + artType->Name() + "}\n\n" + text; //workaround for new artifacts with single name, turns it to H3-style	if(artType->id == ArtifactID::SPELL_SCROLL)	{		// we expect scroll description to be like this: This scroll contains the [spell name] spell which is added into your spell book for as long as you carry the scroll.		// so we want to replace text in [...] with a spell name		// however other language versions don't have name placeholder at all, so we have to be careful		int spellID = getGivenSpellID();		size_t nameStart = text.find_first_of('[');		size_t nameEnd = text.find_first_of(']', nameStart);		if(spellID >= 0)		{			if(nameStart != std::string::npos  &&  nameEnd != std::string::npos)				text = text.replace(nameStart, nameEnd - nameStart + 1, VLC->spellh->objects[spellID]->name);		}	}	else if (hero && artType->constituentOf.size()) //display info about set	{		std::string artList;		auto combinedArt = artType->constituentOf[0];		text += "\n\n";		text += "{" + combinedArt->Name() + "}";		int wornArtifacts = 0;		for (auto a : *combinedArt->constituents) //TODO: can the artifact be a part of more than one set?		{			artList += "\n" + a->Name();			if (hero->hasArt(a->id, true))				wornArtifacts++;		}		text += " (" + boost::str(boost::format("%d") % wornArtifacts) +  " / " +			boost::str(boost::format("%d") % combinedArt->constituents->size()) + ")" + artList;		//TODO: fancy colors and fonts for this text	}	return text;}ArtifactPosition CArtifactInstance::firstAvailableSlot(const CArtifactSet *h) const{	for(auto slot : artType->possibleSlots.at(h->bearerType()))	{		if(canBePutAt(h, slot)) //if(artType->fitsAt(h->artifWorn, slot))		{			//we've found a free suitable slot.			return slot;		}	}	//if haven't find proper slot, use backpack	return firstBackpackSlot(h);}ArtifactPosition CArtifactInstance::firstBackpackSlot(const CArtifactSet *h) const{	if(!artType->isBig()) //discard big artifact		return ArtifactPosition(			GameConstants::BACKPACK_START + h->artifactsInBackpack.size());	return ArtifactPosition::PRE_FIRST;}bool CArtifactInstance::canBePutAt(const ArtifactLocation & al, bool assumeDestRemoved) const{	return canBePutAt(al.getHolderArtSet(), al.slot, assumeDestRemoved);}bool CArtifactInstance::canBePutAt(const CArtifactSet *artSet, ArtifactPosition slot, bool assumeDestRemoved) const{	if(slot >= GameConstants::BACKPACK_START)	{		if(artType->isBig())			return false;		//TODO backpack limit		return true;	} 	auto possibleSlots = artType->possibleSlots.find(artSet->bearerType()); 	if(possibleSlots == artType->possibleSlots.end()) 	{		logMod->warn("Warning: artifact %s doesn't have defined allowed slots for bearer of type %s", artType->Name(), artSet->bearerType());		return false;	}	if(!vstd::contains(possibleSlots->second, slot))		return false;	return artSet->isPositionFree(slot, assumeDestRemoved);}void CArtifactInstance::putAt(ArtifactLocation al){	assert(canBePutAt(al));	al.getHolderArtSet()->setNewArtSlot(al.slot, this, false);	if(al.slot < GameConstants::BACKPACK_START)		al.getHolderNode()->attachTo(this);}void CArtifactInstance::removeFrom(ArtifactLocation al){	assert(al.getHolderArtSet()->getArt(al.slot) == this);	al.getHolderArtSet()->eraseArtSlot(al.slot);	if(al.slot < GameConstants::BACKPACK_START)		al.getHolderNode()->detachFrom(this);	//TODO delete me?}bool CArtifactInstance::canBeDisassembled() const{	return bool(artType->constituents);}std::vector<const CArtifact *> CArtifactInstance::assemblyPossibilities(const CArtifactSet *h) const{	std::vector<const CArtifact *> ret;	if(artType->constituents) //combined artifact already: no combining of combined artifacts... for now.		return ret;	for(const CArtifact * artifact : artType->constituentOf)	{		assert(artifact->constituents);		bool possible = true;		for(const CArtifact * constituent : *artifact->constituents) //check if all constituents are available		{			if(!h->hasArt(constituent->id, true)) //constituent must be equipped			{				possible = false;				break;			}		}		if(possible)			ret.push_back(artifact);	}	return ret;}void CArtifactInstance::move(ArtifactLocation src, ArtifactLocation dst){	removeFrom(src);	putAt(dst);}CArtifactInstance * CArtifactInstance::createNewArtifactInstance(CArtifact *Art){	if(!Art->constituents)	{		auto  ret = new CArtifactInstance(Art);		if (dynamic_cast<CGrowingArtifact *>(Art))		{			auto bonus = std::make_shared<Bonus>();			bonus->type = Bonus::LEVEL_COUNTER;			bonus->val = 0;			ret->addNewBonus (bonus);		}		return ret;	}	else	{		auto  ret = new CCombinedArtifactInstance(Art);		ret->createConstituents();		return ret;	}}CArtifactInstance * CArtifactInstance::createNewArtifactInstance(int aid){	return createNewArtifactInstance(VLC->arth->artifacts[aid]);}CArtifactInstance * CArtifactInstance::createArtifact(CMap * map, int aid, int spellID){	CArtifactInstance * a = nullptr;	if(aid >= 0)	{		if(spellID < 0)		{			a = CArtifactInstance::createNewArtifactInstance(aid);		}		else		{			a = CArtifactInstance::createScroll(SpellID(spellID).toSpell());		}	}	else //FIXME: create combined artifact instance for random combined artifacts, just in case	{		a = new CArtifactInstance(); //random, empty	}	map->addNewArtifactInstance(a);	//TODO make it nicer	if(a->artType && (!!a->artType->constituents))	{		CCombinedArtifactInstance * comb = dynamic_cast<CCombinedArtifactInstance *>(a);		for(CCombinedArtifactInstance::ConstituentInfo & ci : comb->constituentsInfo)		{			map->addNewArtifactInstance(ci.art);		}	}	return a;}void CArtifactInstance::deserializationFix(){	setType(artType);}SpellID CArtifactInstance::getGivenSpellID() const{	const auto b = getBonusLocalFirst(Selector::type(Bonus::SPELL));	if(!b)	{		logMod->warn("Warning: %s doesn't bear any spell!", nodeName());		return SpellID::NONE;	}	return SpellID(b->subtype);}bool CArtifactInstance::isPart(const CArtifactInstance *supposedPart) const{	return supposedPart == this;}bool CCombinedArtifactInstance::canBePutAt(const CArtifactSet *artSet, ArtifactPosition slot, bool assumeDestRemoved) const{	bool canMainArtifactBePlaced = CArtifactInstance::canBePutAt(artSet, slot, assumeDestRemoved);	if(!canMainArtifactBePlaced)		return false; //no is no...	if(slot >= GameConstants::BACKPACK_START)		return true; //we can always remove combined art to the backapck	assert(artType->constituents);	std::vector<ConstituentInfo> constituentsToBePlaced = constituentsInfo; //we'll remove constituents from that list, as we find a suitable slot for them	//it may be that we picked a combined artifact in hero screen (though technically it's still there) to move it	//so we remove from the list all constituents that are already present on dst hero in the form of locks	for(const ConstituentInfo &constituent : constituentsInfo)	{		if(constituent.art == artSet->getArt(constituent.slot, false)) //no need to worry about locked constituent			constituentsToBePlaced -= constituent;	}	//we iterate over all active slots and check if constituents fits them	for (int i = 0; i < GameConstants::BACKPACK_START; i++)	{		for(auto art = constituentsToBePlaced.begin(); art != constituentsToBePlaced.end(); art++)		{			if(art->art->canBePutAt(artSet, ArtifactPosition(i), i == slot)) // i == al.slot because we can remove already worn artifact only from that slot  that is our main destination			{				constituentsToBePlaced.erase(art);				break;			}		}	}	return constituentsToBePlaced.empty();}bool CCombinedArtifactInstance::canBeDisassembled() const{	return true;}CCombinedArtifactInstance::CCombinedArtifactInstance(CArtifact *Art)	: CArtifactInstance(Art) //TODO: seems unued, but need to be written{}CCombinedArtifactInstance::CCombinedArtifactInstance(){}void CCombinedArtifactInstance::createConstituents(){	assert(artType);	assert(artType->constituents);	for(const CArtifact * art : *artType->constituents)	{		addAsConstituent(CArtifactInstance::createNewArtifactInstance(art->id), ArtifactPosition::PRE_FIRST);	}}void CCombinedArtifactInstance::addAsConstituent(CArtifactInstance *art, ArtifactPosition slot){	assert(vstd::contains(*artType->constituents, art->artType.get()));	assert(art->getParentNodes().size() == 1  &&  art->getParentNodes().front() == art->artType);	constituentsInfo.push_back(ConstituentInfo(art, slot));	attachTo(art);}void CCombinedArtifactInstance::putAt(ArtifactLocation al){	if(al.slot >= GameConstants::BACKPACK_START)	{		CArtifactInstance::putAt(al);		for(ConstituentInfo &ci : constituentsInfo)			ci.slot = ArtifactPosition::PRE_FIRST;	}	else	{		CArtifactInstance *mainConstituent = figureMainConstituent(al); //it'll be replaced with combined artifact, not a lock		CArtifactInstance::putAt(al); //puts combined art (this)		for(ConstituentInfo &ci : constituentsInfo)		{			if(ci.art != mainConstituent)			{				const ArtifactLocation suggestedPos(al.artHolder, ci.slot);				const bool inActiveSlot = vstd::isbetween(ci.slot, 0, GameConstants::BACKPACK_START);				const bool suggestedPosValid = ci.art->canBePutAt(suggestedPos);				ArtifactPosition pos = ArtifactPosition::PRE_FIRST;				if(inActiveSlot  &&  suggestedPosValid) //there is a valid suggestion where to place lock					pos = ci.slot;				else					ci.slot = pos = ci.art->firstAvailableSlot(al.getHolderArtSet());				assert(pos < GameConstants::BACKPACK_START);				al.getHolderArtSet()->setNewArtSlot(pos, ci.art, true); //sets as lock			}			else			{				ci.slot = ArtifactPosition::PRE_FIRST;			}		}	}}void CCombinedArtifactInstance::removeFrom(ArtifactLocation al){	if(al.slot >= GameConstants::BACKPACK_START)	{		CArtifactInstance::removeFrom(al);	}	else	{		for(ConstituentInfo &ci : constituentsInfo)		{			if(ci.slot >= 0)			{				al.getHolderArtSet()->eraseArtSlot(ci.slot);				ci.slot = ArtifactPosition::PRE_FIRST;			}			else			{				//main constituent				CArtifactInstance::removeFrom(al);			}		}	}}CArtifactInstance * CCombinedArtifactInstance::figureMainConstituent(const ArtifactLocation al){	CArtifactInstance *mainConstituent = nullptr; //it'll be replaced with combined artifact, not a lock	for(ConstituentInfo &ci : constituentsInfo)		if(ci.slot == al.slot)			mainConstituent = ci.art;	if(!mainConstituent)	{		for(ConstituentInfo &ci : constituentsInfo)		{			if(vstd::contains(ci.art->artType->possibleSlots[al.getHolderArtSet()->bearerType()], al.slot))			{				mainConstituent = ci.art;			}		}	}	return mainConstituent;}void CCombinedArtifactInstance::deserializationFix(){	for(ConstituentInfo &ci : constituentsInfo)		attachTo(ci.art);}bool CCombinedArtifactInstance::isPart(const CArtifactInstance *supposedPart) const{	bool me = CArtifactInstance::isPart(supposedPart);	if(me)		return true;	//check for constituents	for(const ConstituentInfo &constituent : constituentsInfo)		if(constituent.art == supposedPart)			return true;	return false;}CCombinedArtifactInstance::ConstituentInfo::ConstituentInfo(CArtifactInstance *Art, ArtifactPosition Slot){	art = Art;	slot = Slot;}bool CCombinedArtifactInstance::ConstituentInfo::operator==(const ConstituentInfo &rhs) const{	return art == rhs.art && slot == rhs.slot;}CArtifactSet::~CArtifactSet() = default;const CArtifactInstance* CArtifactSet::getArt(ArtifactPosition pos, bool excludeLocked) const{	if(const ArtSlotInfo *si = getSlot(pos))	{		if(si->artifact && (!excludeLocked || !si->locked))			return si->artifact;	}	return nullptr;}CArtifactInstance* CArtifactSet::getArt(ArtifactPosition pos, bool excludeLocked){	return const_cast<CArtifactInstance*>((const_cast<const CArtifactSet*>(this))->getArt(pos, excludeLocked));}ArtifactPosition CArtifactSet::getArtPos(int aid, bool onlyWorn) const{	for(auto i = artifactsWorn.cbegin(); i != artifactsWorn.cend(); i++)		if(i->second.artifact->artType->id == aid)			return i->first;	if(onlyWorn)		return ArtifactPosition::PRE_FIRST;	for(int i = 0; i < artifactsInBackpack.size(); i++)		if(artifactsInBackpack[i].artifact->artType->id == aid)			return ArtifactPosition(GameConstants::BACKPACK_START + i);	return ArtifactPosition::PRE_FIRST;}ArtifactPosition CArtifactSet::getArtPos(const CArtifactInstance *art) const{	for(auto i : artifactsWorn)		if(i.second.artifact == art)			return i.first;	for(int i = 0; i < artifactsInBackpack.size(); i++)		if(artifactsInBackpack[i].artifact == art)			return ArtifactPosition(GameConstants::BACKPACK_START + i);	return ArtifactPosition::PRE_FIRST;}const CArtifactInstance * CArtifactSet::getArtByInstanceId( ArtifactInstanceID artInstId ) const{	for(auto i : artifactsWorn)		if(i.second.artifact->id == artInstId)			return i.second.artifact;	for(auto i : artifactsInBackpack)		if(i.artifact->id == artInstId)			return i.artifact;	return nullptr;}bool CArtifactSet::hasArt(ui32 aid, bool onlyWorn,                          bool searchBackpackAssemblies) const{	return getArtPos(aid, onlyWorn) != ArtifactPosition::PRE_FIRST ||	       (searchBackpackAssemblies && getHiddenArt(aid));}std::pair<const CCombinedArtifactInstance *, const CArtifactInstance *>CArtifactSet::searchForConstituent(int aid) const{	for(auto & slot : artifactsInBackpack)	{		auto art = slot.artifact;		if(art->canBeDisassembled())		{			auto ass = static_cast<CCombinedArtifactInstance *>(art.get());			for(auto& ci : ass->constituentsInfo)			{				if(ci.art->artType->id == aid)				{					return {ass, ci.art};				}			}		}	}	return {nullptr, nullptr};}const CArtifactInstance *CArtifactSet::getHiddenArt(int aid) const{	return searchForConstituent(aid).second;}const CCombinedArtifactInstance *CArtifactSet::getAssemblyByConstituent(int aid) const{	return searchForConstituent(aid).first;}const ArtSlotInfo * CArtifactSet::getSlot(ArtifactPosition pos) const{	if(vstd::contains(artifactsWorn, pos))		return &artifactsWorn.at(pos);	if(pos >= ArtifactPosition::AFTER_LAST )	{		int backpackPos = (int)pos - GameConstants::BACKPACK_START;		if(backpackPos < 0 || backpackPos >= artifactsInBackpack.size())			return nullptr;		else			return &artifactsInBackpack[backpackPos];	}	return nullptr;}bool CArtifactSet::isPositionFree(ArtifactPosition pos, bool onlyLockCheck) const{	if(const ArtSlotInfo *s = getSlot(pos))		return (onlyLockCheck || !s->artifact) && !s->locked;	return true; //no slot means not used}ArtSlotInfo & CArtifactSet::retreiveNewArtSlot(ArtifactPosition slot){	assert(!vstd::contains(artifactsWorn, slot));	ArtSlotInfo &ret = slot < GameConstants::BACKPACK_START		? artifactsWorn[slot]		: *artifactsInBackpack.insert(artifactsInBackpack.begin() + (slot - GameConstants::BACKPACK_START), ArtSlotInfo());	return ret;}void CArtifactSet::setNewArtSlot(ArtifactPosition slot, CArtifactInstance *art, bool locked){	ArtSlotInfo &asi = retreiveNewArtSlot(slot);	asi.artifact = art;	asi.locked = locked;}void CArtifactSet::eraseArtSlot(ArtifactPosition slot){	if(slot < GameConstants::BACKPACK_START)	{		artifactsWorn.erase(slot);	}	else	{		slot = ArtifactPosition(slot - GameConstants::BACKPACK_START);		artifactsInBackpack.erase(artifactsInBackpack.begin() + slot);	}}void CArtifactSet::artDeserializationFix(CBonusSystemNode *node){	for(auto & elem : artifactsWorn)		if(elem.second.artifact && !elem.second.locked)			node->attachTo(elem.second.artifact);}void CArtifactSet::serializeJsonArtifacts(JsonSerializeFormat & handler, const std::string & fieldName, CMap * map){	//todo: creature and commander artifacts	if(handler.saving && artifactsInBackpack.empty() && artifactsWorn.empty())		return;	if(!handler.saving)	{		assert(map);		artifactsInBackpack.clear();		artifactsWorn.clear();	}	auto s = handler.enterStruct(fieldName);	switch(bearerType())	{	case ArtBearer::HERO:		serializeJsonHero(handler, map);		break;	case ArtBearer::CREATURE:		serializeJsonCreature(handler, map);		break;	case ArtBearer::COMMANDER:		serializeJsonCommander(handler, map);		break;	default:		assert(false);		break;	}}void CArtifactSet::serializeJsonHero(JsonSerializeFormat & handler, CMap * map){	for(ArtifactPosition ap = ArtifactPosition::HEAD; ap < ArtifactPosition::AFTER_LAST; ap.advance(1))	{		serializeJsonSlot(handler, ap, map);	}	std::vector<ArtifactID> backpackTemp;	if(handler.saving)	{		backpackTemp.reserve(artifactsInBackpack.size());		for(const ArtSlotInfo & info : artifactsInBackpack)			backpackTemp.push_back(info.artifact->artType->id);	}	handler.serializeIdArray(NArtifactPosition::backpack, backpackTemp);	if(!handler.saving)	{        for(const ArtifactID & artifactID : backpackTemp)		{			auto artifact = CArtifactInstance::createArtifact(map, artifactID.toEnum());			auto slot = ArtifactPosition(GameConstants::BACKPACK_START + artifactsInBackpack.size());			if(artifact->canBePutAt(this, slot))				putArtifact(slot, artifact);		}	}}void CArtifactSet::serializeJsonCreature(JsonSerializeFormat & handler, CMap * map){	logGlobal->error("CArtifactSet::serializeJsonCreature not implemented");}void CArtifactSet::serializeJsonCommander(JsonSerializeFormat & handler, CMap * map){	logGlobal->error("CArtifactSet::serializeJsonCommander not implemented");}void CArtifactSet::serializeJsonSlot(JsonSerializeFormat & handler, const ArtifactPosition & slot, CMap * map){	ArtifactID artifactID;	if(handler.saving)	{		const ArtSlotInfo * info = getSlot(slot);		if(info != nullptr && !info->locked)		{			artifactID = info->artifact->artType->id;			handler.serializeId(NArtifactPosition::namesHero[slot.num], artifactID, ArtifactID::NONE);		}	}	else	{		handler.serializeId(NArtifactPosition::namesHero[slot.num], artifactID, ArtifactID::NONE);		if(artifactID != ArtifactID::NONE)		{			auto artifact = CArtifactInstance::createArtifact(map, artifactID.toEnum());			if(artifact->canBePutAt(this, slot))			{				putArtifact(slot, artifact);			}			else			{				logGlobal->debug("Artifact can't be put at the specified location."); //TODO add more debugging information			}		}	}}
 |