Browse Source

IMarket now able to store artifacts

SoundSSGood 1 year ago
parent
commit
39bb6d5f39

+ 11 - 11
lib/gameState/CGameState.cpp

@@ -804,10 +804,10 @@ void CGameState::initTowns()
 		//init buildings
 		if(vstd::contains(vti->builtBuildings, BuildingID::DEFAULT)) //give standard set of buildings
 		{
-			vti->builtBuildings.erase(BuildingID::DEFAULT);
-			vti->builtBuildings.insert(BuildingID::VILLAGE_HALL);
+			vti->removeBuilding(BuildingID::DEFAULT);
+			vti->addBuilding(BuildingID::VILLAGE_HALL);
 			if(vti->tempOwner != PlayerColor::NEUTRAL)
-				vti->builtBuildings.insert(BuildingID::TAVERN);
+				vti->addBuilding(BuildingID::TAVERN);
 
 			auto definesBuildingsChances = VLC->settings()->getVector(EGameSettings::TOWNS_STARTING_DWELLING_CHANCES);
 
@@ -815,32 +815,32 @@ void CGameState::initTowns()
 			{
 				if((getRandomGenerator().nextInt(1,100) <= definesBuildingsChances[i]))
 				{
-					vti->builtBuildings.insert(basicDwellings[i]);
+					vti->addBuilding(basicDwellings[i]);
 				}
 			}
 		}
 
 		// village hall must always exist
-		vti->builtBuildings.insert(BuildingID::VILLAGE_HALL);
+		vti->addBuilding(BuildingID::VILLAGE_HALL);
 
 		//init hordes
 		for (int i = 0; i < vti->town->creatures.size(); i++)
 		{
 			if (vstd::contains(vti->builtBuildings, hordes[i])) //if we have horde for this level
 			{
-				vti->builtBuildings.erase(hordes[i]);//remove old ID
+				vti->removeBuilding(hordes[i]);//remove old ID
 				if (vti->getTown()->hordeLvl.at(0) == i)//if town first horde is this one
 				{
-					vti->builtBuildings.insert(BuildingID::HORDE_1);//add it
+					vti->addBuilding(BuildingID::HORDE_1);//add it
 					//if we have upgraded dwelling as well
 					if (vstd::contains(vti->builtBuildings, upgradedDwellings[i]))
-						vti->builtBuildings.insert(BuildingID::HORDE_1_UPGR);//add it as well
+						vti->addBuilding(BuildingID::HORDE_1_UPGR);//add it as well
 				}
 				if (vti->getTown()->hordeLvl.at(1) == i)//if town second horde is this one
 				{
-					vti->builtBuildings.insert(BuildingID::HORDE_2);
+					vti->addBuilding(BuildingID::HORDE_2);
 					if (vstd::contains(vti->builtBuildings, upgradedDwellings[i]))
-						vti->builtBuildings.insert(BuildingID::HORDE_2_UPGR);
+						vti->addBuilding(BuildingID::HORDE_2_UPGR);
 				}
 			}
 		}
@@ -853,7 +853,7 @@ void CGameState::initTowns()
 			});
 
 		if (vstd::contains(vti->builtBuildings, BuildingID::SHIPYARD) && vti->shipyardStatus()==IBoatGenerator::TILE_BLOCKED)
-			vti->builtBuildings.erase(BuildingID::SHIPYARD);//if we have harbor without water - erase it (this is H3 behaviour)
+			vti->removeBuilding(BuildingID::SHIPYARD);//if we have harbor without water - erase it (this is H3 behaviour)
 
 		//Early check for #1444-like problems
 		for([[maybe_unused]] const auto & building : vti->builtBuildings)

+ 2 - 2
lib/gameState/CGameStateCampaign.cpp

@@ -664,10 +664,10 @@ void CGameStateCampaign::initTowns()
 			if (newBuilding == BuildingID::NONE)
 				break;
 
-			if (town->builtBuildings.count(newBuilding) != 0)
+			if(town->hasBuilt(newBuilding))
 				break;
 
-			town->builtBuildings.insert(newBuilding);
+			town->addBuilding(newBuilding);
 
 			auto building = town->town->buildings.at(newBuilding);
 			newBuilding = building->upgrade;

+ 1 - 1
lib/mapObjectConstructors/CommonConstructors.cpp

@@ -248,7 +248,7 @@ CGMarket * MarketInstanceConstructor::createObject(IGameCallback * cb) const
 
 void MarketInstanceConstructor::initializeObject(CGMarket * market) const
 {
-	market->marketModes = marketModes;
+	market->addMarketMode(marketModes);
 	market->marketEfficiency = marketEfficiency;
 	
 	if(auto univercity = dynamic_cast<CGUniversity*>(market))

+ 0 - 5
lib/mapObjects/CGMarket.cpp

@@ -38,11 +38,6 @@ int CGMarket::getMarketEfficiency() const
 	return marketEfficiency;
 }
 
-bool CGMarket::allowsTrade(EMarketMode mode) const
-{
-	return marketModes.count(mode);
-}
-
 int CGMarket::availableUnits(EMarketMode mode, int marketItemSerial) const
 {
 	return -1;

+ 0 - 4
lib/mapObjects/CGMarket.h

@@ -18,8 +18,6 @@ VCMI_LIB_NAMESPACE_BEGIN
 class DLL_LINKAGE CGMarket : public CGObjectInstance, public IMarket
 {
 public:
-	
-	std::set<EMarketMode> marketModes;
 	int marketEfficiency;
 	
 	CGMarket(IGameCallback *cb);
@@ -29,7 +27,6 @@ public:
 
 	///IMarket
 	int getMarketEfficiency() const override;
-	bool allowsTrade(EMarketMode mode) const override;
 	int availableUnits(EMarketMode mode, int marketItemSerial) const override; //-1 if unlimited
 	std::vector<TradeItemBuy> availableItemsIds(EMarketMode mode) const override;
 
@@ -37,7 +34,6 @@ public:
 	{
 		h & static_cast<CGObjectInstance&>(*this);
 		h & static_cast<IMarket&>(*this);
-		h & marketModes;
 		h & marketEfficiency;
 	}
 };

+ 53 - 33
lib/mapObjects/CGTownInstance.cpp

@@ -690,35 +690,6 @@ int CGTownInstance::getMarketEfficiency() const
 	return marketCount;
 }
 
-bool CGTownInstance::allowsTrade(EMarketMode mode) const
-{
-	switch(mode)
-	{
-	case EMarketMode::RESOURCE_RESOURCE:
-	case EMarketMode::RESOURCE_PLAYER:
-		return hasBuilt(BuildingID::MARKETPLACE);
-
-	case EMarketMode::ARTIFACT_RESOURCE:
-	case EMarketMode::RESOURCE_ARTIFACT:
-		return hasBuilt(BuildingSubID::ARTIFACT_MERCHANT);
-
-	case EMarketMode::CREATURE_RESOURCE:
-		return hasBuilt(BuildingSubID::FREELANCERS_GUILD);
-
-	case EMarketMode::CREATURE_UNDEAD:
-		return hasBuilt(BuildingSubID::CREATURE_TRANSFORMER);
-
-	case EMarketMode::RESOURCE_SKILL:
-		return hasBuilt(BuildingSubID::MAGIC_UNIVERSITY);
-	case EMarketMode::CREATURE_EXP:
-	case EMarketMode::ARTIFACT_EXP:
-		return false;
-	default:
-		assert(0);
-		return false;
-	}
-}
-
 std::vector<TradeItemBuy> CGTownInstance::availableItemsIds(EMarketMode mode) const
 {
 	if(mode == EMarketMode::RESOURCE_ARTIFACT)
@@ -962,6 +933,55 @@ bool CGTownInstance::hasBuilt(const BuildingID & buildingID, FactionID townID) c
 	return false;
 }
 
+void CGTownInstance::addBuilding(const BuildingID & buildingID)
+{
+	if(buildingID == BuildingID::NONE)
+		return;
+
+	builtBuildings.insert(buildingID);
+	marketBuildingModeMapper(buildingID, [this](const EMarketMode mode) {addMarketMode(mode);});
+}
+
+void CGTownInstance::removeBuilding(const BuildingID & buildingID)
+{
+	if(!vstd::contains(builtBuildings, buildingID))
+		return;
+
+	builtBuildings.erase(buildingID);
+	marketBuildingModeMapper(buildingID, [this](const EMarketMode mode) {removeMarketMode(mode);});
+}
+
+void CGTownInstance::marketBuildingModeMapper(const BuildingID & buildingID, const std::function<void(const EMarketMode)> & func)
+{
+	const auto townType = (*VLC->townh)[getFaction()]->town;
+	if(townType->buildings.find(buildingID) == townType->buildings.end())
+		return;
+
+	// TODO how to remove hardcoded buildings?
+	if(buildingID == BuildingID::MARKETPLACE)
+	{
+		func(EMarketMode::RESOURCE_RESOURCE);
+		func(EMarketMode::RESOURCE_PLAYER);
+	}
+	else if(townType->buildings.at(buildingID)->subId == BuildingSubID::ARTIFACT_MERCHANT)
+	{
+		func(EMarketMode::ARTIFACT_RESOURCE);
+		func(EMarketMode::RESOURCE_ARTIFACT);
+	}
+	else if(townType->buildings.at(buildingID)->subId == BuildingSubID::FREELANCERS_GUILD)
+	{
+		func(EMarketMode::CREATURE_RESOURCE);
+	}
+	else if(townType->buildings.at(buildingID)->subId == BuildingSubID::CREATURE_TRANSFORMER)
+	{
+		func(EMarketMode::CREATURE_UNDEAD);
+	}
+	else if(townType->buildings.at(buildingID)->subId == BuildingSubID::MAGIC_UNIVERSITY)
+	{
+		func(EMarketMode::RESOURCE_SKILL);
+	}
+}
+
 TResources CGTownInstance::getBuildingCost(const BuildingID & buildingID) const
 {
 	if (vstd::contains(town->buildings, buildingID))
@@ -1132,23 +1152,23 @@ void CGTownInstance::serializeJsonOptions(JsonSerializeFormat & handler)
 		{
 			handler.serializeLIC("buildings", buildingsLIC);
 
-			builtBuildings.insert(BuildingID::VILLAGE_HALL);
+			addBuilding(BuildingID::VILLAGE_HALL);
 
 			if(buildingsLIC.none.empty() && buildingsLIC.all.empty())
 			{
-				builtBuildings.insert(BuildingID::DEFAULT);
+				addBuilding(BuildingID::DEFAULT);
 
 				bool hasFort = false;
 				handler.serializeBool("hasFort",hasFort);
 				if(hasFort)
-					builtBuildings.insert(BuildingID::FORT);
+					addBuilding(BuildingID::FORT);
 			}
 			else
 			{
 				for(const si32 item : buildingsLIC.none)
 					forbiddenBuildings.insert(BuildingID(item));
 				for(const si32 item : buildingsLIC.all)
-					builtBuildings.insert(BuildingID(item));
+					addBuilding(BuildingID(item));
 			}
 		}
 	}

+ 3 - 1
lib/mapObjects/CGTownInstance.h

@@ -153,7 +153,6 @@ public:
 	EGeneratorState shipyardStatus() const override;
 	const IObjectInterface * getObject() const override;
 	int getMarketEfficiency() const override; //=market count
-	bool allowsTrade(EMarketMode mode) const override;
 	std::vector<TradeItemBuy> availableItemsIds(EMarketMode mode) const override;
 
 	void updateAppearance();
@@ -175,6 +174,8 @@ public:
 	//checks if building is constructed and town has same subID
 	bool hasBuilt(const BuildingID & buildingID) const;
 	bool hasBuilt(const BuildingID & buildingID, FactionID townID) const;
+	void addBuilding(const BuildingID & buildingID);
+	void removeBuilding(const BuildingID & buildingID);
 
 	TResources getBuildingCost(const BuildingID & buildingID) const;
 	TResources dailyIncome() const; //calculates daily income of this town
@@ -227,6 +228,7 @@ protected:
 	void setPropertyDer(ObjProperty what, ObjPropertyID identifier) override;
 	void serializeJsonOptions(JsonSerializeFormat & handler) override;
 	void blockingDialogAnswered(const CGHeroInstance *hero, int32_t answer) const override;
+	void marketBuildingModeMapper(const BuildingID & buildingID, const std::function<void(const EMarketMode)> & func);
 
 private:
 	FactionID randomizeFaction(vstd::RNG & rand);

+ 27 - 0
lib/mapObjects/IMarket.cpp

@@ -20,6 +20,11 @@
 
 VCMI_LIB_NAMESPACE_BEGIN
 
+bool IMarket::allowsTrade(const EMarketMode mode) const
+{
+	return marketModes.count(mode) > 0;
+}
+
 bool IMarket::getOffer(int id1, int id2, int &val1, int &val2, EMarketMode mode) const
 {
 	switch(mode)
@@ -135,6 +140,28 @@ int IMarket::availableUnits(const EMarketMode mode, const int marketItemSerial)
 	}
 }
 
+void IMarket::addMarketMode(const EMarketMode mode)
+{
+	marketModes.insert(mode);
+
+	if(mode == EMarketMode::ARTIFACT_EXP)
+		altarArtifacts = std::make_shared<CArtifactSetAltar>();
+}
+
+void IMarket::addMarketMode(const std::set<EMarketMode> & modes)
+{
+	for(const auto & mode : modes)
+		addMarketMode(mode);
+}
+
+void IMarket::removeMarketMode(const EMarketMode mode)
+{
+	marketModes.erase(mode);
+
+	if(mode == EMarketMode::ARTIFACT_EXP)
+		altarArtifacts.reset();
+}
+
 std::vector<TradeItemBuy> IMarket::availableItemsIds(const EMarketMode mode) const
 {
 	std::vector<TradeItemBuy> ret;

+ 9 - 4
lib/mapObjects/IMarket.h

@@ -25,22 +25,27 @@ public:
 	};
 
 	virtual int getMarketEfficiency() const = 0;
-	virtual bool allowsTrade(const EMarketMode mode) const = 0;
+	virtual bool allowsTrade(const EMarketMode mode) const;
 	virtual int availableUnits(const EMarketMode mode, const int marketItemSerial) const; //-1 if unlimited
 	virtual std::vector<TradeItemBuy> availableItemsIds(const EMarketMode mode) const;
+	void addMarketMode(const EMarketMode mode);
+	void addMarketMode(const std::set<EMarketMode> & modes);
+	void removeMarketMode(const EMarketMode mode);
 
 	bool getOffer(int id1, int id2, int &val1, int &val2, EMarketMode mode) const; //val1 - how many units of id1 player has to give to receive val2 units
 	std::vector<EMarketMode> availableModes() const;
 
 	template <typename Handler> void serialize(Handler & h)
 	{
-		if(h.loadingGamestate)
-		{
-		}
+		h & marketModes;
+
+		if(h.loadingGamestate && vstd::contains(marketModes, EMarketMode::ARTIFACT_EXP))
+			altarArtifacts = std::make_shared<CArtifactSetAltar>();
 	}
 
 private:
 	std::shared_ptr<CArtifactSetAltar> altarArtifacts;
+	std::set<EMarketMode> marketModes;
 };
 
 VCMI_LIB_NAMESPACE_END

+ 7 - 5
lib/mapping/MapFormatH3M.cpp

@@ -2198,18 +2198,20 @@ CGObjectInstance * CMapLoaderH3M::readTown(const int3 & position, std::shared_pt
 	bool hasCustomBuildings = reader->readBool();
 	if(hasCustomBuildings)
 	{
-		reader->readBitmaskBuildings(object->builtBuildings, faction);
-		reader->readBitmaskBuildings(object->forbiddenBuildings, faction);
+		object->subID = faction.value();
+		for(const auto & building : reader->readBitmaskBuildings(faction))
+			object->addBuilding(building);
+		object->forbiddenBuildings = reader->readBitmaskBuildings(faction);
 	}
 	// Standard buildings
 	else
 	{
 		bool hasFort = reader->readBool();
 		if(hasFort)
-			object->builtBuildings.insert(BuildingID::FORT);
+			object->addBuilding(BuildingID::FORT);
 
 		//means that set of standard building should be included
-		object->builtBuildings.insert(BuildingID::DEFAULT);
+		object->addBuilding(BuildingID::DEFAULT);
 	}
 
 	if(features.levelAB)
@@ -2257,7 +2259,7 @@ CGObjectInstance * CMapLoaderH3M::readTown(const int3 & position, std::shared_pt
 		reader->skipZero(17);
 
 		// New buildings
-		reader->readBitmaskBuildings(event.buildings, faction);
+		event.buildings = reader->readBitmaskBuildings(faction);
 
 		event.creatures.resize(7);
 		for(int i = 0; i < 7; ++i)

+ 3 - 1
lib/mapping/MapReaderH3M.cpp

@@ -257,9 +257,10 @@ PlayerColor MapReaderH3M::readPlayer32()
 	return PlayerColor(value);
 }
 
-void MapReaderH3M::readBitmaskBuildings(std::set<BuildingID> & dest, std::optional<FactionID> faction)
+std::set<BuildingID> MapReaderH3M::readBitmaskBuildings(std::optional<FactionID> faction)
 {
 	std::set<BuildingID> h3m;
+	std::set<BuildingID> dest;
 	readBitmask(h3m, features.buildingsBytes, features.buildingsCount, false);
 
 	for (auto const & h3mEntry : h3m)
@@ -269,6 +270,7 @@ void MapReaderH3M::readBitmaskBuildings(std::set<BuildingID> & dest, std::option
 		if (mapped != BuildingID::NONE) // artifact merchant may be set in random town, but not present in actual town
 			dest.insert(mapped);
 	}
+	return dest;
 }
 
 void MapReaderH3M::readBitmaskFactions(std::set<FactionID> & dest, bool invert)

+ 1 - 1
lib/mapping/MapReaderH3M.h

@@ -48,7 +48,7 @@ public:
 	PlayerColor readPlayer();
 	PlayerColor readPlayer32();
 
-	void readBitmaskBuildings(std::set<BuildingID> & dest, std::optional<FactionID> faction);
+	std::set<BuildingID> readBitmaskBuildings(std::optional<FactionID> faction);
 	void readBitmaskFactions(std::set<FactionID> & dest, bool invert);
 	void readBitmaskPlayers(std::set<PlayerColor> & dest, bool invert);
 	void readBitmaskResources(std::set<GameResID> & dest, bool invert);

+ 2 - 2
lib/networkPacks/NetPacksLib.cpp

@@ -1332,7 +1332,7 @@ void NewStructures::applyGs(CGameState *gs)
 	for(const auto & id : bid)
 	{
 		assert(t->town->buildings.at(id) != nullptr);
-		t->builtBuildings.insert(id);
+		t->addBuilding(id);
 	}
 	t->updateAppearance();
 	t->built = built;
@@ -1344,7 +1344,7 @@ void RazeStructures::applyGs(CGameState *gs)
 	CGTownInstance *t = gs->getTown(tid);
 	for(const auto & id : bid)
 	{
-		t->builtBuildings.erase(id);
+		t->removeBuilding(id);
 
 		t->updateAppearance();
 	}

+ 4 - 4
lib/rmg/modificators/TownPlacer.cpp

@@ -79,8 +79,8 @@ void TownPlacer::placeTowns(ObjectManager & manager)
 
 		CGTownInstance * town = dynamic_cast<CGTownInstance *>(townFactory->create(map.mapInstance->cb, nullptr));
 		town->tempOwner = player;
-		town->builtBuildings.insert(BuildingID::FORT);
-		town->builtBuildings.insert(BuildingID::DEFAULT);
+		town->addBuilding(BuildingID::FORT);
+		town->addBuilding(BuildingID::DEFAULT);
 		
 
 		for(auto spellID : VLC->spellh->getDefaultAllowed()) //add all regular spells to town
@@ -203,8 +203,8 @@ void TownPlacer::addNewTowns(int count, bool hasFort, const PlayerColor & player
 		
 		town->tempOwner = player;
 		if (hasFort)
-			town->builtBuildings.insert(BuildingID::FORT);
-		town->builtBuildings.insert(BuildingID::DEFAULT);
+			town->addBuilding(BuildingID::FORT);
+		town->addBuilding(BuildingID::DEFAULT);
 		
 		for(auto spellID : VLC->spellh->getDefaultAllowed()) //add all regular spells to town
 			town->possibleSpells.push_back(spellID);