Source: globalscripts/Technologies.js

/**
 * This file contains shared logic for applying tech modifications in GUI, AI,
 * and simulation scripts. As such it must be fully deterministic and not store
 * any global state, but each context should do its own caching as needed.
 * Also it cannot directly access the simulation and requires data passed to it.
 */
 
/**
 * Returns modified property value if at least one tech modification is found
 *   applicable to the given entity template; else, returns its original value.
 *
 * currentTechModifications: mapping of property names to modification arrays,
 *   retrieved from the intended player's TechnologyManager.
 * entityTemplateData: raw entity template object.
 * propertyName: name of the tech modification to apply.
 * propertyValue: original value of property to be modified.
 */
function GetTechModifiedProperty(currentTechModifications, entityTemplateData, propertyName, propertyValue)
{
	// Get all modifications to this value
	var modifications = currentTechModifications[propertyName];
	if (!modifications) // no modifications so return the original value
		return propertyValue;

	// TODO: will we ever need the full template?
	// Get the classes which this entity template belongs to
	var classes = [];
	if (entityTemplateData && entityTemplateData.Identity)
		classes = GetIdentityClasses(entityTemplateData.Identity);

	var retValue = propertyValue;

	for (var i in modifications)
	{
		var modification = modifications[i];
		if (DoesModificationApply(modification, classes))
		{
			// We found a match, apply the modification

			// Nothing is cumulative so that ordering doesn't matter as much as possible
			if (modification.multiply)
				retValue += (modification.multiply - 1) * propertyValue;
			else if (modification.add)
				retValue += modification.add;
			else if (modification.replace !== undefined) // This will depend on ordering because there is no choice
				retValue = modification.replace;
			else
				warn("GetTechModifiedProperty: modification format not recognised (modifying " + propertyName + "): " + uneval(modification));
		}
	}

	return retValue;
}

/**
 * Returns whether the given modification applies to the entity containing the given class list
 */
function DoesModificationApply(modification, classes)
{
	return MatchesClassList(classes, modification.affects);
}