Procházet zdrojové kódy

changed specialty json format to struct with base (WIP)

Henning Koehler před 8 roky
rodič
revize
eb02ce0f31
6 změnil soubory, kde provedl 133 přidání a 10 odebrání
  1. 2 2
      config/heroes/castle.json
  2. 29 3
      lib/CHeroHandler.cpp
  3. 28 5
      lib/HeroBonus.cpp
  4. 3 0
      lib/HeroBonus.h
  5. 60 0
      lib/JsonNode.cpp
  6. 11 0
      lib/JsonNode.h

+ 2 - 2
config/heroes/castle.json

@@ -51,7 +51,7 @@
 					],
 					"type" : "CREATURE_TYPE_LIMITER"
 				},
-				"subtype" : "attack",
+				"subtype" : "primSkill.attack",
 				"type" : "PRIMARY_SKILL",
 				"updater" : {
 					"parameters" : [
@@ -69,7 +69,7 @@
 					],
 					"type" : "CREATURE_TYPE_LIMITER"
 				},
-				"subtype" : "defence",
+				"subtype" : "primSkill.defence",
 				"type" : "PRIMARY_SKILL",
 				"updater" : {
 					"parameters" : [

+ 29 - 3
lib/CHeroHandler.cpp

@@ -737,17 +737,43 @@ void CHeroHandler::afterLoadFinalization()
 		if(hero->specDeprecated.size() > 0)
 		{
 			logMod->debug("Converting specialties format for hero %s(%s)", hero->identifier, VLC->townh->encodeFaction(hero->heroClass->faction));
-			JsonNode specVec(JsonNode::JsonType::DATA_VECTOR);
+			std::vector<JsonNode> specVec;
+			std::vector<std::string> specNames;
 			for(const SSpecialtyInfo & spec : hero->specDeprecated)
 			{
 				for(std::shared_ptr<Bonus> bonus : SpecialtyInfoToBonuses(spec, hero->ID.getNum()))
 				{
 					hero->specialty.push_back(bonus);
-					specVec.Vector().push_back(bonus->toJsonNode());
+					specVec.push_back(bonus->toJsonNode());
+					// find fitting & unique bonus name
+					std::string bonusName = nameForBonus(*bonus);
+					if(vstd::contains(specNames, bonusName))
+					{
+						int i = 2;
+						while(vstd::contains(specNames, bonusName + std::to_string(i)))
+							i++;
+						bonusName += std::to_string(i);
+					}
+					specNames.push_back(bonusName);
 				}
 			}
 			hero->specDeprecated.clear();
-			logMod->trace("\"specialty\" : %s", specVec.toJson());
+			// log new format for easy copy-and-paste
+			JsonNode specNode(JsonNode::JsonType::DATA_STRUCT);
+			if(specVec.size() > 1)
+			{
+				JsonNode base = JsonUtils::intersect(specVec);
+				if(!base.isEmpty())
+				{
+					specNode["base"] = base;
+					//TODO: subtract base from bonuses
+				}
+			}
+			// add json for bonuses
+			specNode["bonuses"].Struct();
+			for(int i = 0; i < specVec.size(); i++)
+				specNode["bonuses"][specNames[i]] = specVec[i];
+			logMod->trace("\"specialty\" : %s", specNode.toJson());
 		}
 	}
 }

+ 28 - 5
lib/HeroBonus.cpp

@@ -1169,17 +1169,17 @@ JsonNode subtypeToJson(Bonus::BonusType type, int subtype)
 	switch(type)
 	{
 	case Bonus::PRIMARY_SKILL:
-		return JsonUtils::stringNode(PrimarySkill::names[subtype]);
+		return JsonUtils::stringNode("primSkill." + PrimarySkill::names[subtype]);
 	case Bonus::SECONDARY_SKILL_PREMY:
-		return JsonUtils::stringNode(NSecondarySkill::names[subtype]);
+		return JsonUtils::stringNode("skill." + NSecondarySkill::names[subtype]);
 	case Bonus::SPECIAL_SPELL_LEV:
 	case Bonus::SPECIFIC_SPELL_DAMAGE:
 	case Bonus::SPECIAL_BLESS_DAMAGE:
 	case Bonus::MAXED_SPELL:
 	case Bonus::SPECIAL_PECULIAR_ENCHANT:
-		return JsonUtils::stringNode((*VLC->spellh)[SpellID::ESpellID(subtype)]->identifier);
+		return JsonUtils::stringNode("spell." + (*VLC->spellh)[SpellID::ESpellID(subtype)]->identifier);
 	case Bonus::SPECIAL_UPGRADE:
-		return JsonUtils::stringNode(CreatureID::encode(subtype));
+		return JsonUtils::stringNode("creature." + CreatureID::encode(subtype));
 	case Bonus::GENERATE_RESOURCE:
 		return JsonUtils::stringNode(GameConstants::RESOURCE_NAMES[subtype]);
 	default:
@@ -1192,7 +1192,7 @@ JsonNode additionalInfoToJson(Bonus::BonusType type, int addInfo)
 	switch(type)
 	{
 	case Bonus::SPECIAL_UPGRADE:
-		return JsonUtils::stringNode(CreatureID::encode(addInfo));
+		return JsonUtils::stringNode("creature." + CreatureID::encode(addInfo));
 	default:
 		return JsonUtils::intNode(addInfo);
 	}
@@ -1717,3 +1717,26 @@ JsonNode ScalingUpdater::toJsonNode() const
 
 	return root;
 }
+
+std::string nameForBonus(const Bonus & bonus)
+{
+	switch(bonus.type)
+	{
+	case Bonus::PRIMARY_SKILL:
+		return PrimarySkill::names[bonus.subtype];
+	case Bonus::SECONDARY_SKILL_PREMY:
+		return NSecondarySkill::names[bonus.subtype];
+	case Bonus::SPECIAL_SPELL_LEV:
+	case Bonus::SPECIFIC_SPELL_DAMAGE:
+	case Bonus::SPECIAL_BLESS_DAMAGE:
+	case Bonus::MAXED_SPELL:
+	case Bonus::SPECIAL_PECULIAR_ENCHANT:
+		return (*VLC->spellh)[SpellID::ESpellID(bonus.subtype)]->identifier;
+	case Bonus::SPECIAL_UPGRADE:
+		return CreatureID::encode(bonus.subtype) + "2" + CreatureID::encode(bonus.additionalInfo);
+	case Bonus::GENERATE_RESOURCE:
+		return GameConstants::RESOURCE_NAMES[bonus.subtype];
+	default:
+		return vstd::findKey(bonusNameMap, bonus.type);
+	}
+}

+ 3 - 0
lib/HeroBonus.h

@@ -1057,3 +1057,6 @@ struct DLL_LINKAGE ScalingUpdater : public IUpdater
 	virtual std::string toString() const override;
 	virtual JsonNode toJsonNode() const override;
 };
+
+// generate suitable name for bonus - e.g. for storing in json struct
+DLL_LINKAGE std::string nameForBonus(const Bonus & bonus);

+ 60 - 0
lib/JsonNode.cpp

@@ -214,6 +214,24 @@ bool JsonNode::isNumber() const
 	return type == JsonType::DATA_INTEGER || type == JsonType::DATA_FLOAT;
 }
 
+bool JsonNode::isEmpty() const
+{
+	switch(type)
+	{
+		case JsonType::DATA_NULL:
+			return true;
+		case JsonType::DATA_STRUCT:
+			for(auto elem : *data.Struct)
+			{
+				if(!elem.second.isEmpty())
+					return false;
+			}
+			return true;
+		default:
+			return false;
+	}
+}
+
 void JsonNode::clear()
 {
 	setType(JsonType::DATA_NULL);
@@ -839,6 +857,48 @@ void JsonUtils::inherit(JsonNode & descendant, const JsonNode & base)
 	descendant.swap(inheritedNode);
 }
 
+JsonNode JsonUtils::intersect(const std::vector<JsonNode> & nodes, bool pruneEmpty)
+{
+	if(nodes.size() == 0)
+		return nullNode;
+
+	JsonNode result = nodes[0];
+	for(int i = 1; i < nodes.size(); i++)
+	{
+		if(result.isNull())
+			break;
+		result = JsonUtils::intersect(result, nodes[i], pruneEmpty);
+	}
+	return result;
+}
+
+JsonNode JsonUtils::intersect(const JsonNode & a, const JsonNode & b, bool pruneEmpty)
+{
+	if(a.getType() == JsonNode::JsonType::DATA_STRUCT && b.getType() == JsonNode::JsonType::DATA_STRUCT)
+	{
+		// intersect individual properties
+		JsonNode result(JsonNode::JsonType::DATA_STRUCT);
+		for(auto property : a.Struct())
+		{
+			if(vstd::contains(b.Struct(), property.first))
+			{
+				JsonNode propertyIntersect = JsonUtils::intersect(property.second, b.Struct().find(property.first)->second);
+				if(pruneEmpty && propertyIntersect.isEmpty())
+					continue;
+				result[property.first] = propertyIntersect;
+			}
+		}
+		return result;
+	}
+	else
+	{
+		// not a struct - same or different, no middle ground
+		if(a == b)
+			return a;
+	}
+	return nullNode;
+}
+
 JsonNode JsonUtils::assembleFromFiles(std::vector<std::string> files)
 {
 	bool isValid;

+ 11 - 0
lib/JsonNode.h

@@ -75,6 +75,7 @@ public:
 
 	bool isNull() const;
 	bool isNumber() const;
+	bool isEmpty() const;
 	/// removes all data from node and sets type to null
 	void clear();
 
@@ -188,6 +189,16 @@ namespace JsonUtils
      */
 	DLL_LINKAGE void inherit(JsonNode & descendant, const JsonNode & base);
 
+	/**
+	 * @brief construct node representing the common structure of input nodes
+	 * @param pruneEmpty - omit common properties whose intersection is empty
+	 * different types: null
+	 * struct: recursive intersect on common properties
+	 * other: input if equal, null otherwise
+	 */
+	DLL_LINKAGE JsonNode intersect(const JsonNode & a, const JsonNode & b, bool pruneEmpty = true);
+	DLL_LINKAGE JsonNode intersect(const std::vector<JsonNode> & nodes, bool pruneEmpty = true);
+
 	/**
 	 * @brief generate one Json structure from multiple files
 	 * @param files - list of filenames with parts of json structure