瀏覽代碼

Merge pull request #660 from ShubusCorporation/shc_rmg_issue

Mod system improvement: Patch 2
Alexander Shishkin 5 年之前
父節點
當前提交
9ca9c809c6

+ 19 - 2
client/windows/CCastleInterface.cpp

@@ -846,6 +846,23 @@ void CCastleBuildings::enterFountain(const BuildingID & building, BuildingSubID:
 {
 	std::vector<std::shared_ptr<CComponent>> comps(1, std::make_shared<CComponent>(CComponent::building,town->subID, building));
 	std::string descr = town->town->buildings.find(building)->second->Description();
+	std::string hasNotProduced;
+	std::string hasProduced;
+
+	if(this->town->town->faction->index == (TFaction)ETownType::RAMPART)
+	{
+		hasNotProduced = CGI->generaltexth->allTexts[677];
+		hasProduced = CGI->generaltexth->allTexts[678];
+	}
+	else
+	{
+		auto buildingName = town->town->getSpecialBuilding(subID)->Name();
+
+		hasNotProduced = std::string(CGI->generaltexth->localizedTexts["townHall"]["hasNotProduced"].String());
+		hasProduced = std::string(CGI->generaltexth->localizedTexts["townHall"]["hasProduced"].String());
+		boost::algorithm::replace_first(hasNotProduced, "%s", buildingName);
+		boost::algorithm::replace_first(hasProduced, "%s", buildingName);
+	}
 
 	bool isMysticPondOrItsUpgrade = subID == BuildingSubID::MYSTIC_POND 
 		|| (upgrades != BuildingID::NONE 
@@ -857,10 +874,10 @@ void CCastleBuildings::enterFountain(const BuildingID & building, BuildingSubID:
 	if(isMysticPondOrItsUpgrade) //for vanila Rampart like towns
 	{
 		if(town->bonusValue.first == 0) //Mystic Pond produced nothing;
-			descr += "\n\n" + CGI->generaltexth->allTexts[677];
+			descr += "\n\n" + hasNotProduced;
 		else //Mystic Pond produced something;
 		{
-			descr += "\n\n" + CGI->generaltexth->allTexts[678];
+			descr += "\n\n" + hasProduced;
 			boost::algorithm::replace_first(descr, "%s", CGI->generaltexth->restypes[town->bonusValue.first]);
 			boost::algorithm::replace_first(descr, "%d", boost::lexical_cast<std::string>(town->bonusValue.second));
 		}

+ 12 - 1
client/windows/GUIClasses.cpp

@@ -1269,7 +1269,18 @@ CUniversityWindow::CUniversityWindow(const CGHeroInstance * _hero, const IMarket
 	bars->preload();
 
 	if(market->o->ID == Obj::TOWN)
-		titlePic = std::make_shared<CAnimImage>(CGI->townh->factions[ETownType::CONFLUX]->town->clientInfo.buildingsIcons, BuildingID::MAGIC_UNIVERSITY);
+	{
+		auto town = dynamic_cast<const CGTownInstance *>(_market);
+
+		if(town)
+		{
+			auto faction = town->town->faction->index;
+			auto bid = town->town->getSpecialBuilding(BuildingSubID::MAGIC_UNIVERSITY)->bid;
+			titlePic = std::make_shared<CAnimImage>(CGI->townh->factions[faction]->town->clientInfo.buildingsIcons, bid);
+		}
+		else
+			titlePic = std::make_shared<CAnimImage>(CGI->townh->factions[ETownType::CONFLUX]->town->clientInfo.buildingsIcons, BuildingID::MAGIC_UNIVERSITY);
+	}
 	else
 		titlePic = std::make_shared<CPicture>("UNIVBLDG");
 

+ 9 - 1
config/translate.json

@@ -43,7 +43,15 @@
 	},
 	"townHall" :
 	{
-		"missingBase" : "Base building %s must be built first"
+		"missingBase" : "Base building %s must be built first",
+		"greetingManaVortex" : "As you near the %s your body is filled with new energy. You have doubled your normal spell points.",
+		"greetingKnowledge" : "You study the glyphs on the %s and gain insight into the workings of various magics (+1 Knowledge).",
+		"greetingSpellPower" : "The %s teaches you new ways to focus your magical powers (+1 Power).",
+		"greetingExperience" : "A visit to the %s teaches you many new skills (+1000 Experience).",
+		"greetingAttack" : "Some time spent at the %s allows you to learn more effective combat skills (+1 Attack Skill).",
+		"greetingDefence" : "Spending time in the %s, the experienced warriors therein teach you additional defensive skills (+1 Defense).",
+		"hasNotProduced" : "The %s has not produced anything yet.",
+		"hasProduced" : "The %s produced %d %s this week."
 	},
 	"logicalExpressions" :
 	{

+ 10 - 0
lib/CTownHandler.cpp

@@ -217,6 +217,16 @@ BuildingID::EBuildingID CTown::getBuildingType(BuildingSubID::EBuildingSubID sub
 	return building == nullptr ? BuildingID::NONE : building->bid.num;
 }
 
+const std::string CTown::getGreeting(BuildingSubID::EBuildingSubID subID) const
+{
+	return VLC->townh->getMappedValue<const std::string, BuildingSubID::EBuildingSubID>(subID, std::string(), specialMessages, false);
+}
+
+void CTown::setGreeting(BuildingSubID::EBuildingSubID subID, const std::string message) const
+{
+	specialMessages.insert(std::pair<BuildingSubID::EBuildingSubID, const std::string>(subID, message));
+}
+
 CTownHandler::CTownHandler()
 {
 	VLC->townh = this;

+ 7 - 1
lib/CTownHandler.h

@@ -122,7 +122,7 @@ public:
 			h & subId;
 			h & height;
 		}
-		else if(!h.saving)
+		if(!h.saving && version < 793)
 		{
 			update792(bid, subId, height);
 		}
@@ -228,6 +228,8 @@ public:
 	std::string getBuildingScope() const;
 	std::set<si32> getAllBuildings() const;
 	const CBuilding * getSpecialBuilding(BuildingSubID::EBuildingSubID subID) const;
+	const std::string getGreeting(BuildingSubID::EBuildingSubID subID) const;
+	void setGreeting(BuildingSubID::EBuildingSubID subID, const std::string message) const; //may affect only mutable field
 	BuildingID::EBuildingID getBuildingType(BuildingSubID::EBuildingSubID subID) const;
 
 	CFaction * faction;
@@ -335,6 +337,10 @@ public:
 		}
 		h & defaultTavernChance;
 	}
+	
+private:
+	///generated bonusing buildings messages for all towns of this type.
+	mutable std::map<BuildingSubID::EBuildingSubID, const std::string> specialMessages; //may be changed by CGTownBuilding::getVisitingBonusGreeting() const
 };
 
 class DLL_LINKAGE CTownHandler : public IHandlerBase

+ 42 - 13
lib/mapObjects/CGTownInstance.cpp

@@ -1644,11 +1644,11 @@ void CGTownInstance::serializeJsonOptions(JsonSerializeFormat & handler)
 	}
 }
 
-COPWBonus::COPWBonus (BuildingID bid, BuildingSubID::EBuildingSubID subId, CGTownInstance *TOWN)
+COPWBonus::COPWBonus(BuildingID bid, BuildingSubID::EBuildingSubID subId, CGTownInstance * cgTown)
 {
 	bID = bid;
 	bType = subId;
-	town = TOWN;
+	town = cgTown;
 	indexOnTV = static_cast<si32>(town->bonusingBuildings.size());
 }
 
@@ -1701,7 +1701,7 @@ void COPWBonus::onHeroVisit(const CGHeroInstance * h) const
 						cb->setManaPoints(heroID, 2 * h->manaLimit());
 					//TODO: investigate line below
 					//cb->setObjProperty (town->id, ObjProperty::VISITED, true);
-					iw.text << VLC->generaltexth->allTexts[579];
+					iw.text << getVisitingBonusGreeting();
 					cb->showInfoDialog(&iw);
 					//extra visit penalty if hero alredy had double mana points (or even more?!)
 					town->addHeroToStructureVisitors(h, indexOnTV);
@@ -1710,11 +1710,11 @@ void COPWBonus::onHeroVisit(const CGHeroInstance * h) const
 		}
 	}
 }
-CTownBonus::CTownBonus (BuildingID index, BuildingSubID::EBuildingSubID subId, CGTownInstance *TOWN)
+CTownBonus::CTownBonus(BuildingID index, BuildingSubID::EBuildingSubID subId, CGTownInstance * cgTown)
 {
 	bID = index;
 	bType = subId;
-	town = TOWN;
+	town = cgTown;
 	indexOnTV = static_cast<si32>(town->bonusingBuildings.size());
 }
 
@@ -1729,7 +1729,6 @@ void CTownBonus::onHeroVisit(const CGHeroInstance * h) const
 	ObjectInstanceID heroID = h->id;
 	if (town->hasBuilt(bID) && visitors.find(heroID) == visitors.end())
 	{
-		si32 mid = 0;
 		si64 val = 0;
 		InfoWindow iw;
 		PrimarySkill::PrimarySkill what = PrimarySkill::ATTACK;
@@ -1739,41 +1738,35 @@ void CTownBonus::onHeroVisit(const CGHeroInstance * h) const
 		case BuildingSubID::KNOWLEDGE_VISITING_BONUS: //wall of knowledge
 			what = PrimarySkill::KNOWLEDGE;
 			val = 1;
-			mid = 581;
 			iw.components.push_back(Component(Component::PRIM_SKILL, 3, 1, 0));
 			break;
 
 		case BuildingSubID::SPELL_POWER_VISITING_BONUS: //order of fire
 			what = PrimarySkill::SPELL_POWER;
 			val = 1;
-			mid = 582;
 			iw.components.push_back(Component(Component::PRIM_SKILL, 2, 1, 0));
 			break;
 
 		case BuildingSubID::ATTACK_VISITING_BONUS: //hall of Valhalla
 			what = PrimarySkill::ATTACK;
 			val = 1;
-			mid = 584;
 			iw.components.push_back(Component(Component::PRIM_SKILL, 0, 1, 0));
 			break;
 
 		case BuildingSubID::EXPERIENCE_VISITING_BONUS: //academy of battle scholars
 			what = PrimarySkill::EXPERIENCE;
 			val = static_cast<int>(h->calculateXp(1000));
-			mid = 583;
 			iw.components.push_back(Component(Component::EXPERIENCE, 0, val, 0));
 			break;
 
 		case BuildingSubID::DEFENSE_VISITING_BONUS: //cage of warlords
 			what = PrimarySkill::DEFENSE;
 			val = 1;
-			mid = 585;
 			iw.components.push_back(Component(Component::PRIM_SKILL, 1, 1, 0));
 			break;
 		}
-		assert(mid);
 		iw.player = cb->getOwner(heroID);
-		iw.text << VLC->generaltexth->allTexts[mid];
+		iw.text << getVisitingBonusGreeting();
 		cb->showInfoDialog(&iw);
 		cb->changePrimSkill(cb->getHero(heroID), what, val);
 		town->addHeroToStructureVisitors(h, indexOnTV);
@@ -1811,3 +1804,39 @@ int GrowthInfo::totalGrowth() const
 
 	return ret;
 }
+
+const std::string CGTownBuilding::getVisitingBonusGreeting() const
+{
+	auto bonusGreeting = town->town->getGreeting(bType);
+
+	if(!bonusGreeting.empty())
+		return bonusGreeting;
+
+	switch(bType)
+	{
+	case BuildingSubID::MANA_VORTEX:
+		bonusGreeting = std::string(VLC->generaltexth->localizedTexts["townHall"]["greetingManaVortex"].String());
+		break;
+	case BuildingSubID::KNOWLEDGE_VISITING_BONUS:
+		bonusGreeting = std::string(VLC->generaltexth->localizedTexts["townHall"]["greetingKnowledge"].String());
+		break;
+	case BuildingSubID::SPELL_POWER_VISITING_BONUS:
+		bonusGreeting = std::string(VLC->generaltexth->localizedTexts["townHall"]["greetingSpellPower"].String());
+		break;
+	case BuildingSubID::ATTACK_VISITING_BONUS:
+		bonusGreeting = std::string(VLC->generaltexth->localizedTexts["townHall"]["greetingAttack"].String());
+		break;
+	case BuildingSubID::EXPERIENCE_VISITING_BONUS:
+		bonusGreeting = std::string(VLC->generaltexth->localizedTexts["townHall"]["greetingExperience"].String());
+		break;
+	case BuildingSubID::DEFENSE_VISITING_BONUS:
+		bonusGreeting = std::string(VLC->generaltexth->localizedTexts["townHall"]["greetingDefence"].String());
+		break;
+	}
+
+	assert(!bonusGreeting.empty());
+	auto buildingName = town->town->getSpecialBuilding(bType)->Name();
+	boost::algorithm::replace_first(bonusGreeting, "%s", buildingName);
+	town->town->setGreeting(bType, bonusGreeting);
+	return bonusGreeting;
+}

+ 4 - 1
lib/mapObjects/CGTownInstance.h

@@ -127,9 +127,12 @@ public:
 		if(version >= 792)
 			h & bType;
 	}
+
 protected:
 	BuildingID bID; //from buildig list
 	BuildingSubID::EBuildingSubID bType;
+
+	const std::string getVisitingBonusGreeting() const;
 };
 
 class DLL_LINKAGE COPWBonus : public CGTownBuilding
@@ -251,7 +254,7 @@ public:
 			return false;
 		});
 
-		if(!h.saving && version < 792)
+		if(!h.saving && version < 793)
 			updateBonusingBuildings();
 	}
 	//////////////////////////////////////////////////////////////////////////

+ 17 - 1
lib/mapObjects/CObjectClassesHandler.cpp

@@ -196,11 +196,27 @@ void CObjectClassesHandler::loadObjectEntry(const std::string & identifier, cons
 
 	//some mods redefine content handlers in the decoration.json in such way:
 	//"core:sign" : { "types" : { "forgeSign" : { ...
-	if (!obj->subObjects.count(id)) // DO NOT override
+	static const std::vector<std::string> knownProblemObjects
+	{
+		"hota.hota decorations:hotaPandoraBox"
+		, "hota.hota decorations:hotaSubterreanGate"
+	};
+	bool overrideForce = !obj->subObjects.count(id) ||
+	std::any_of(knownProblemObjects.begin(), knownProblemObjects.end(), [obj, id](const std::string & str)
+	{
+		return str.compare(obj->subObjects[id]->subTypeName) == 0;
+	});
+
+	if (overrideForce) // DO NOT override mod handlers by default
 	{
 		obj->subObjects[id] = handler;
 		obj->subIds[convertedId] = id;
 	}
+	else
+	{
+		logGlobal->warn("Don't override handler %s in object %s(%d)::%s(%d) subTypeName : %s"
+			, obj->handlerName, obj->identifier, obj->id, convertedId, id, obj->subObjects[id]->subTypeName);
+	}
 }
 
 CObjectClassesHandler::ObjectContainter * CObjectClassesHandler::loadFromJson(const JsonNode & json, const std::string & name)

+ 8 - 7
lib/rmg/CMapGenerator.cpp

@@ -610,15 +610,16 @@ void CMapGenerator::createConnections2()
 
 				std::vector<int3> commonTiles;
 
+				auto lambda = [](const int3 & lhs, const int3 & rhs) -> bool
+				{
+					//https://stackoverflow.com/questions/45966807/c-invalid-comparator-assert
+					return std::tie(lhs.x, lhs.y) < std::tie(rhs.x, rhs.y); //ignore z coordinate
+				};
 				//required for set_intersection
-				boost::sort(tilesA);
-				boost::sort(tilesB);
+				boost::sort(tilesA, lambda);
+				boost::sort(tilesB, lambda);
 
-				boost::set_intersection(tilesA, tilesB, std::back_inserter(commonTiles), [](const int3 &lhs, const int3 &rhs) -> bool
-				{
-					//ignore z coordinate
-					return lhs.x < rhs.x || lhs.y < rhs.y;
-				});
+				boost::set_intersection(tilesA, tilesB, std::back_inserter(commonTiles), lambda);
 
 				vstd::erase_if(commonTiles, [](const int3 &tile) -> bool
 				{

+ 1 - 1
lib/serializer/CSerializer.h

@@ -12,7 +12,7 @@
 #include "../ConstTransitivePtr.h"
 #include "../GameConstants.h"
 
-const ui32 SERIALIZATION_VERSION = 792;
+const ui32 SERIALIZATION_VERSION = 793;
 const ui32 MINIMAL_SERIALIZATION_VERSION = 753;
 const std::string SAVEGAME_MAGIC = "VCMISVG";