Index: gui/session/utility_functions.js
===================================================================
--- gui/session/utility_functions.js	(revision 12562)
+++ gui/session/utility_functions.js	(working copy)
@@ -386,6 +386,58 @@
 	return "\n\n[font=\"serif-bold-13\"][color=\"red\"]Insufficient resources:[/color][/font]\n" + formatted.join(", ");
 }
 
+/**
+ * Returns a formatted requirements tooltip.
+ */
+function getFormattedRequirementsTooltip(tmpl, vars)
+{
+	// TODO: Support entire requirements format.
+	if (vars.tech)
+	{
+		return tmpl;
+	}
+	else if (vars.all)
+	{
+		return tmpl;
+	}
+	else if (vars.any)
+	{
+		return tmpl;
+	}
+	else if (vars.class)
+	{
+		// TODO: Replace with human-readable class name.
+		var tooltip = tmpl.replace("%class%", vars.class);
+		if (vars.number && vars.number > 0)
+		{
+			tooltip = tooltip.replace("%number%", vars.number);
+		}
+		else
+		{
+			tooltip = tooltip.replace("%number%", 0);
+		}
+		if (vars.numberCount && vars.numberCount > 0)
+		{
+			tooltip = tooltip.replace("%numberCount%", vars.numberCount);
+		}
+		else
+		{
+			tooltip = tooltip.replace("%numberCount%", 0);
+		}
+		if (vars.numberLeft && vars.numberLeft > 0)
+		{
+			tooltip = tooltip.replace("%numberLeft%", vars.numberLeft);
+		}
+		else
+		{
+			tooltip = tooltip.replace("%numberLeft%", 0);
+		}
+		return tooltip;
+	}
+
+	return tmpl;
+}
+
 function getEntitySpeed(template)
 {
     var speed = "";
Index: gui/session/unit_commands.js
===================================================================
--- gui/session/unit_commands.js	(revision 12562)
+++ gui/session/unit_commands.js	(working copy)
@@ -575,7 +575,8 @@
 			if (guiName == RESEARCH && !Engine.GuiInterfaceCall("CheckTechnologyRequirements", entType))
 			{
 				button.enabled = false;
-				button.tooltip += "\n" + GetTechnologyData(entType).requirementsTooltip;
+				var reqVars = Engine.GuiInterfaceCall("GetRequirementsVars", template.requirements);
+				button.tooltip += "\n" + getFormattedRequirementsTooltip(GetTechnologyData(entType).requirementsTooltip, reqVars);
 				grayscale = "grayscale:";
 			}
 			
Index: simulation/components/GuiInterface.js
===================================================================
--- simulation/components/GuiInterface.js	(revision 12562)
+++ simulation/components/GuiInterface.js	(working copy)
@@ -516,6 +516,7 @@
 	}
 	ret.tooltip = template.tooltip;
 	
+	ret.requirements = template.requirements;
 	if (template.requirementsTooltip)
 		ret.requirementsTooltip = template.requirementsTooltip;
 	else
@@ -575,6 +576,11 @@
 	return cmpPlayer.GetNeededResources(amounts);
 };
 
+GuiInterface.prototype.GetRequirementsVars = function(player, reqs)
+{
+	var cmpTechnologyManager = QueryPlayerIDInterface(player, IID_TechnologyManager);
+	return cmpTechnologyManager.GetRequirementsVars(reqs);
+};
 
 GuiInterface.prototype.PushNotification = function(notification)
 {
@@ -1627,6 +1633,7 @@
 	"CheckTechnologyRequirements": 1,
 	"GetStartedResearch": 1,
 	"GetNeededResources": 1,
+	"GetRequirementsVars": 1,
 	"GetNextNotification": 1,
 
 	"GetAvailableFormations": 1,
Index: simulation/components/TechnologyManager.js
===================================================================
--- simulation/components/TechnologyManager.js	(revision 12562)
+++ simulation/components/TechnologyManager.js	(working copy)
@@ -163,6 +163,57 @@
 	return false;
 };
 
+
+// Private function for returning a set of measures pertaining to requirements
+TechnologyManager.prototype.GetRequirementsVars = function (reqs)
+{
+	var reqVars = {};
+
+	// If there are no requirements then all requirements are met
+	if (!reqs)
+		return false;
+	
+	if (reqs.tech)
+	{
+		return false;
+	}
+	else if (reqs.all)
+	{
+		return false;
+	}
+	else if (reqs.any)
+	{
+		return false;
+	}
+	else if (reqs.class)
+	{
+		reqVars.class = reqs.class;
+		if (reqs.numberOfTypes)
+			return false;
+		else if (reqs.number)
+		{
+			reqVars.number = reqs.number;
+			if (!this.classCounts[reqs.class])
+			{
+				reqVars.numberCount = 0;
+				reqVars.numberLeft = reqs.number;
+			}
+			else if (this.classCounts[reqs.class] < reqs.number)
+			{			
+				reqVars.numberCount = this.classCounts[reqs.class];
+				reqVars.numberLeft = reqs.number - this.classCounts[reqs.class];
+			}
+			else
+				reqVars = false;
+			return reqVars;
+		}
+	}
+	
+	// The technologies requirements are not a recognised format
+	error("GetRequirementsVars: Bad requirements " + uneval(reqs));
+	return false;
+};
+
 TechnologyManager.prototype.OnGlobalOwnershipChanged = function (msg)
 {
 	// This automatically updates typeCounts, classCounts and typeCountsByClass
Index: simulation/data/technologies/phase_town.json
===================================================================
--- simulation/data/technologies/phase_town.json	(revision 12562)
+++ simulation/data/technologies/phase_town.json	(working copy)
@@ -3,7 +3,7 @@
 	"description": "Advances from a small village to a bustling town, ready to expand rapidly.",
 	"cost": { "food": 500, "wood": 500, "stone": 0, "metal": 0 },
 	"requirements": { "class": "Village", "number": 6 },
-	"requirementsTooltip": "Requires 5 Village Phase structures (except Palisades and Farm Fields).",
+	"requirementsTooltip": "Requires 6 Village Phase structures (except Palisades and Farm Fields). ([color=\"red\"]%numberCount%/%number%[/color] built.)",
 	"supersedes": "phase_village",
 	"icon": "town_phase.png",
 	"researchTime": 30,
