Index: source/simulation2/components/CCmpFootprint.cpp
===================================================================
--- source/simulation2/components/CCmpFootprint.cpp	(revision 16736)
+++ source/simulation2/components/CCmpFootprint.cpp	(working copy)
@@ -59,15 +59,21 @@
 			"<choice>"
 				"<element name='Square' a:help='Set the footprint to a square of the given size'>"
 					"<attribute name='width' a:help='Size of the footprint along the left/right direction (in metres)'>"
-						"<ref name='positiveDecimal'/>"
+						"<data type='decimal'>"
+							"<param name='minExclusive'>0.0</param>"
+						"</data>"
 					"</attribute>"
 					"<attribute name='depth' a:help='Size of the footprint along the front/back direction (in metres)'>"
-						"<ref name='positiveDecimal'/>"
+						"<data type='decimal'>"
+							"<param name='minExclusive'>0.0</param>"
+						"</data>"
 					"</attribute>"
 				"</element>"
 				"<element name='Circle' a:help='Set the footprint to a circle of the given size'>"
 					"<attribute name='radius' a:help='Radius of the footprint (in metres)'>"
-						"<ref name='positiveDecimal'/>"
+						"<data type='decimal'>"
+							"<param name='minExclusive'>0.0</param>"
+						"</data>"
 					"</attribute>"
 				"</element>"
 			"</choice>"
Index: source/simulation2/components/CCmpObstruction.cpp
===================================================================
--- source/simulation2/components/CCmpObstruction.cpp	(revision 16736)
+++ source/simulation2/components/CCmpObstruction.cpp	(working copy)
@@ -112,15 +112,21 @@
 			"<choice>"
 				"<element name='Static'>"
 					"<attribute name='width'>"
-						"<ref name='positiveDecimal'/>"
+						"<data type='decimal'>"
+							"<param name='minExclusive'>0.0</param>"
+						"</data>"
 					"</attribute>"
 					"<attribute name='depth'>"
-						"<ref name='positiveDecimal'/>"
+						"<data type='decimal'>"
+							"<param name='minExclusive'>0.0</param>"
+						"</data>"
 					"</attribute>"
 				"</element>"
 				"<element name='Unit'>"
 					"<attribute name='radius'>"
-						"<ref name='positiveDecimal'/>"
+						"<data type='decimal'>"
+							"<param name='minExclusive'>0.0</param>"
+						"</data>"
 					"</attribute>"
 				"</element>"
 				"<element name='Obstructions'>"
@@ -138,10 +144,14 @@
 								"</attribute>"
 							"</optional>"
 							"<attribute name='width'>"
-								"<ref name='positiveDecimal'/>"
+								"<data type='decimal'>"
+									"<param name='minExclusive'>0.0</param>"
+								"</data>"
 							"</attribute>"
 							"<attribute name='depth'>"
-								"<ref name='positiveDecimal'/>"
+								"<data type='decimal'>"
+									"<param name='minExclusive'>0.0</param>"
+								"</data>"
 							"</attribute>"
 						"</element>"
 					"</zeroOrMore>"
Index: source/simulation2/components/CCmpVisualActor.cpp
===================================================================
--- source/simulation2/components/CCmpVisualActor.cpp	(revision 16736)
+++ source/simulation2/components/CCmpVisualActor.cpp	(working copy)
@@ -146,21 +146,31 @@
 						"</element>"
 						"<element name='Box' a:help='Sets the selection shape to a box of specified dimensions'>"
 							"<attribute name='width'>"
-								"<ref name='positiveDecimal' />"
+								"<data type='decimal'>"
+									"<param name='minExclusive'>0.0</param>"
+								"</data>"
 							"</attribute>"
 							"<attribute name='height'>"
-								"<ref name='positiveDecimal' />"
+								"<data type='decimal'>"
+									"<param name='minExclusive'>0.0</param>"
+								"</data>"
 							"</attribute>"
 							"<attribute name='depth'>"
-								"<ref name='positiveDecimal' />"
+								"<data type='decimal'>"
+									"<param name='minExclusive'>0.0</param>"
+								"</data>"
 							"</attribute>"
 						"</element>"
 						"<element name='Cylinder' a:help='Sets the selection shape to a cylinder of specified dimensions'>"
 							"<attribute name='radius'>"
-								"<ref name='positiveDecimal' />"
+								"<data type='decimal'>"
+									"<param name='minExclusive'>0.0</param>"
+								"</data>"
 							"</attribute>"
 							"<attribute name='height'>"
-								"<ref name='positiveDecimal' />"
+								"<data type='decimal'>"
+									"<param name='minExclusive'>0.0</param>"
+								"</data>"
 							"</attribute>"
 						"</element>"
 					"</choice>"
Index: source/simulation2/docs/SimulationDocs.h
===================================================================
--- source/simulation2/docs/SimulationDocs.h	(revision 16736)
+++ source/simulation2/docs/SimulationDocs.h	(working copy)
@@ -297,13 +297,40 @@
 - <code>&lt;text/></code>
 - <code>&lt;data type='boolean'/></code>
 - <code>&lt;data type='decimal'/></code>
+- <code>&lt;data type='integer'/></code>
 - <code>&lt;data type='nonNegativeInteger'/></code>
 - <code>&lt;data type='positiveInteger'/></code>
+- <code>&lt;ref name='decimal'/></code>
 - <code>&lt;ref name='nonNegativeDecimal'/></code>
 - <code>&lt;ref name='positiveDecimal'/></code>
 
-(The last two are slightly different since they're not standard data types.)
 
+The <code>&lt;data&gt;</code> elements are native elements, while the <code>&lt;ref&gt;</code> elements are elements added for our engine. These non-native elements allow the definition of an operation that depends on the parent template. Possible operations are "add", "sub" mul" and "div", and can be applied as the example below.
+
+Say the parent template is
+@code 
+<Entity> 
+  <Example> 
+    <Name>Semi-Humanoids</Name> 
+    <Height>9000</Height> 
+    <Eyes/> 
+  </Example> 
+  <!-- ... other components ... --> 
+</Entity> 
+@endcode 
+and the child template appears like
+@code 
+<Entity> 
+  <Example> 
+    <Name>Barney</Name> 
+    <Height op="add">5</Height> 
+    <Eyes/> 
+  </Example> 
+  <!-- ... other components ... --> 
+</Entity> 
+@endcode 
+then Barney would have a height of 9005. 
+
 Elements can be wrapped in <code>&lt;optional></code>.
 Groups of elements can be wrapped in <code>&lt;choice></code> to allow only one of them.
 The content of an <code>&lt;element></code> can be further nested elements, but note that
Index: source/simulation2/system/ComponentManager.cpp
===================================================================
--- source/simulation2/system/ComponentManager.cpp	(revision 16736)
+++ source/simulation2/system/ComponentManager.cpp	(working copy)
@@ -1092,13 +1092,30 @@
 
 std::string CComponentManager::GenerateSchema()
 {
+	std::string numericOperation =
+		"<optional>"
+			"<attribute name='op'>"
+				"<choice>"
+					"<value>add</value>"
+					"<value>sub</value>"
+					"<value>mul</value>"
+					"<value>div</value>"
+				"</choice>"
+			"</attribute>"
+		"</optional>";
 	std::string schema =
 		"<grammar xmlns='http://relaxng.org/ns/structure/1.0' xmlns:a='http://ns.wildfiregames.com/entity' datatypeLibrary='http://www.w3.org/2001/XMLSchema-datatypes'>"
+			"<define name='decimal'>"
+				"<data type='decimal'/>"
+				+ numericOperation +
+			"</define>"
 			"<define name='nonNegativeDecimal'>"
 				"<data type='decimal'><param name='minInclusive'>0</param></data>"
+				+ numericOperation +
 			"</define>"
 			"<define name='positiveDecimal'>"
 				"<data type='decimal'><param name='minExclusive'>0</param></data>"
+				+ numericOperation +
 			"</define>"
 			"<define name='anything'>"
 				"<zeroOrMore>"
Index: source/simulation2/system/ParamNode.cpp
===================================================================
--- source/simulation2/system/ParamNode.cpp	(revision 16736)
+++ source/simulation2/system/ParamNode.cpp	(working copy)
@@ -76,7 +76,15 @@
 	// Look for special attributes
 	int at_disable = xmb.GetAttributeID("disable");
 	int at_replace = xmb.GetAttributeID("replace");
+	int at_op = xmb.GetAttributeID("op");
 	int at_datatype = xmb.GetAttributeID("datatype");
+	enum op {
+		INVALID,
+		ADD,
+		SUB,
+		MUL,
+		DIV
+	} op = INVALID;
 	bool replacing = false;
 	{
 		XERO_ITER_ATTR(element, attr)
@@ -91,6 +99,19 @@
 				m_Childs.erase(name);
 				replacing = true;
 			}
+			else if (attr.Name == at_op)
+			{
+				if (std::wstring(attr.Value.begin(), attr.Value.end()) == L"add") 
+					op = ADD; 
+				else if (std::wstring(attr.Value.begin(), attr.Value.end()) == L"sub") 
+					op = SUB; 
+				else if (std::wstring(attr.Value.begin(), attr.Value.end()) == L"mul") 
+					op = MUL; 
+				else if (std::wstring(attr.Value.begin(), attr.Value.end()) == L"div") 
+					op = DIV; 
+				else 
+					LOGWARNING("Invalid op '%ls'", attr.Value); 
+			}
 		}
 	}
 	{
@@ -137,6 +158,32 @@
 
 	// Add this element as a child node
 	CParamNode& node = m_Childs[name];
+	if (op != INVALID) 
+	{ 
+		// TODO Support parsing of data types other than fixed; log warnings in other cases 
+		fixed oldval = node.ToFixed(); 
+		fixed mod = fixed::FromString(CStrW(value)); 
+		switch (op) 
+		{ 
+		case SUB: mod = -mod; 
+		case ADD: 
+			node.m_Value = (oldval + mod).ToString().FromUTF8(); 
+			break; 
+		case MUL: 
+			node.m_Value = (oldval.Multiply(mod)).ToString().FromUTF8(); 
+			break; 
+		case DIV: 
+			if (mod.IsZero()) 
+			{ 
+				// Even though it's undefined, just return the original value 
+				LOGWARNING("Attempted divide-by-zero in '%hs'%ls'.", name, sourceIdentifier ? (" in '" + utf8_from_wstring(sourceIdentifier) + "'") : ""); 
+				break; 
+			} 
+			node.m_Value = (oldval / mod).ToString().FromUTF8(); 
+			break; 
+		} 
+		hasSetValue = true; 
+	} 
 	if (!hasSetValue)
 		node.m_Value = value;
 
@@ -150,7 +197,8 @@
 	XERO_ITER_ATTR(element, attr)
 	{
 		// Skip special attributes
-		if (attr.Name == at_replace) continue;
+		if (attr.Name == at_replace || attr.Name == at_op)
+			continue;
 		// Add any others
 		std::string attrName = xmb.GetAttributeString(attr.Name);
 		node.m_Childs["@" + attrName].m_Value = attr.Value.FromUTF8();
Index: source/tools/templatesanalyzer/CreateRMTest.py
===================================================================
--- source/tools/templatesanalyzer/CreateRMTest.py	(revision 16736)
+++ source/tools/templatesanalyzer/CreateRMTest.py	(working copy)
@@ -1,3 +1,27 @@
+''' 
+* CreateRMTest.py 
+* 
+* Copyright (C) 2015 Wildfire Games.
+* 
+* Permission is hereby granted, free of charge, to any person obtaining a copy
+* of this software and associated documentation files (the "Software"), to deal
+* in the Software without restriction, including without limitation the rights
+* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+* copies of the Software, and to permit persons to whom the Software is
+* furnished to do so, subject to the following conditions:
+
+* The above copyright notice and this permission notice shall be included in
+* all copies or substantial portions of the Software.
+
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+* THE SOFTWARE.
+''' 
+
 import xml.etree.ElementTree as ET
 import os
 
Index: source/tools/templatesanalyzer/unitTables.py
===================================================================
--- source/tools/templatesanalyzer/unitTables.py	(revision 16736)
+++ source/tools/templatesanalyzer/unitTables.py	(working copy)
@@ -1,3 +1,27 @@
+''' 
+* unitTables.py 
+* 
+* Copyright (C) 2015 Wildfire Games.
+* 
+* Permission is hereby granted, free of charge, to any person obtaining a copy
+* of this software and associated documentation files (the "Software"), to deal
+* in the Software without restriction, including without limitation the rights
+* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+* copies of the Software, and to permit persons to whom the Software is
+* furnished to do so, subject to the following conditions:
+
+* The above copyright notice and this permission notice shall be included in
+* all copies or substantial portions of the Software.
+
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+* THE SOFTWARE.
+''' 
+
 import xml.etree.ElementTree as ET
 import os
 import glob
@@ -51,6 +75,20 @@
 def htout(file, value):
 	file.write("<p>" + value + "</p>\n" )
 
+def NumericStatProcess(unit, newValue, templateValue):
+	if (newValue not in unit):
+		unit[newValue] = 0;	# This is kind of a sanity hack.
+	if (templateValue.attrib == {}):
+		unit[newValue] = templateValue.text
+	elif (templateValue.attrib == "add"):
+		newValue += templateValue.text
+	elif (templateValue.attrib == "sub"):
+		newValue -= templateValue.text
+	elif (templateValue.attrib == "mul"):
+		newValue *= templateValue.text
+	elif (templateValue.attrib == "div"):
+		newValue /= templateValue.text
+
 def CalcUnit(UnitName, existingUnit = None):
 	unit = { 'HP' : "0", "BuildTime" : "0", "Cost" : { 'food' : "0", "wood" : "0", "stone" : "0", "metal" : "0", "population" : "0"},
 	'Attack' : { "Melee" : { "Hack" : 0, "Pierce" : 0, "Crush" : 0 }, "Ranged" : { "Hack" : 0, "Pierce" : 0, "Crush" : 0 } },
@@ -69,26 +107,26 @@
 		unit["Parent"] = Template.getroot().get("parent") + ".xml"
 
 	if (Template.find("./Identity/Civ") != None):
-		unit['Civ'] = Template.find("./Identity/Civ").text
+		NumericStatProcess(unit, 'Civ', Template.find("./Identity/Civ"))
 
 	if (Template.find("./Health/Max") != None):
-		unit['HP'] = Template.find("./Health/Max").text
+		NumericStatProcess(unit, 'HP', Template.find("./Health/Max"))
 
 	if (Template.find("./Cost/BuildTime") != None):
-		unit['BuildTime'] = Template.find("./Cost/BuildTime").text
+		NumericStatProcess(unit, 'BuildTime', Template.find("./Cost/BuildTime"))
 	
 	if (Template.find("./Cost/Resources") != None):
 		for type in list(Template.find("./Cost/Resources")):
-			unit['Cost'][type.tag] = type.text
+			NumericStatProcess(unit['Cost'], type.tag, type)
 
 	if (Template.find("./Attack/Melee") != None):
 		if (Template.find("./Attack/Melee/RepeatTime") != None):
-			unit['RepeatRate']["Melee"] = Template.find("./Attack/Melee/RepeatTime").text
+			NumericStatProcess(unit['RepeatRate'], "Melee", Template.find("./Attack/Melee/RepeatTime"))
 		if (Template.find("./Attack/Melee/PrepareTime") != None):
-			unit['PrepRate']["Melee"] = Template.find("./Attack/Melee/PrepareTime").text
+			NumericStatProcess(unit['PrepRate'], "Melee", Template.find("./Attack/Melee/PrepareTime"))
 		for atttype in AttackTypes:
 			if (Template.find("./Attack/Melee/"+atttype) != None):
-				unit['Attack']['Melee'][atttype] = Template.find("./Attack/Melee/"+atttype).text
+				NumericStatProcess(unit['Attack']['Melee'], atttype, Template.find("./Attack/Melee/"+atttype))
 		if (Template.find("./Attack/Melee/Bonuses") != None):
 			for Bonus in Template.find("./Attack/Melee/Bonuses"):
 				Against = []
@@ -112,14 +150,14 @@
 	if (Template.find("./Attack/Ranged") != None):
 		unit['Ranged'] = "true"
 		if (Template.find("./Attack/Ranged/MaxRange") != None):
-			unit['Range'] = Template.find("./Attack/Ranged/MaxRange").text
+			NumericStatProcess(unit, 'Range', Template.find("./Attack/Ranged/MaxRange"))
 		if (Template.find("./Attack/Ranged/RepeatTime") != None):
-			unit['RepeatRate']["Ranged"] = Template.find("./Attack/Ranged/RepeatTime").text
+			NumericStatProcess(unit['RepeatRate'], "Ranged", Template.find("./Attack/Ranged/RepeatTime"))
 		if (Template.find("./Attack/Ranged/PrepareTime") != None):
-			unit['PrepRate']["Ranged"] = Template.find("./Attack/Ranged/PrepareTime").text
+			NumericStatProcess(unit['PrepRate'], "Ranged", Template.find("./Attack/Ranged/PrepareTime"))
 		for atttype in AttackTypes:
 			if (Template.find("./Attack/Ranged/"+atttype) != None):
-				unit['Attack']['Ranged'][atttype] = Template.find("./Attack/Ranged/"+atttype).text
+				NumericStatProcess(unit['Attack']['Ranged'], atttype, Template.find("./Attack/Ranged/"+atttype))
 		if (Template.find("./Attack/Ranged/Bonuses") != None):
 			for Bonus in Template.find("./Attack/Ranged/Bonuses"):
 				Against = []
@@ -142,11 +180,11 @@
 	if (Template.find("./Armour") != None):
 		for atttype in AttackTypes:
 			if (Template.find("./Armour/"+atttype) != None):
-				unit['Armour'][atttype] = Template.find("./Armour/"+atttype).text
+				NumericStatProcess(unit['Armour'], atttype, Template.find("./Armour/"+atttype))
 
 	if (Template.find("./UnitMotion") != None):
 		if (Template.find("./UnitMotion/WalkSpeed") != None):
-				unit['WalkSpeed'] = Template.find("./UnitMotion/WalkSpeed").text
+				NumericStatProcess(unit, 'WalkSpeed', Template.find("./UnitMotion/WalkSpeed"))
 
 	if (Template.find("./Identity/VisibleClasses") != None):
 		newClasses = Template.find("./Identity/VisibleClasses").text.split(" ")
@@ -174,11 +212,11 @@
 
 	rstr += "<td style=\"text-align:right;\">" + Name + "</td>\n"
 	
-	rstr += "<td>" + UnitDict["HP"] + "</td>\n"
+	rstr += "<td>" + str(UnitDict["HP"]) + "</td>\n"
 
-	rstr += "<td>" + UnitDict["BuildTime"] + "</td>\n"
+	rstr += "<td>" + str(UnitDict["BuildTime"]) + "</td>\n"
 
-	rstr += "<td>" + UnitDict["WalkSpeed"] + "</td>\n"
+	rstr += "<td>" + str(UnitDict["WalkSpeed"]) + "</td>\n"
 	
 	rstr += "<td>" + UnitDict["Cost"]["food"] + "F / " + UnitDict["Cost"]["wood"] + "W / " + UnitDict["Cost"]["stone"] + "S / " + UnitDict["Cost"]["metal"] + "M</td>\n"
 
@@ -696,4 +734,4 @@
 		CivUnitComparisons(CivData[Civ],CivData[oCiv])
 	f.write("</table>")
 
-f.write("</body>\n</html>")
\ No newline at end of file
+f.write("</body>\n</html>")
