Index: binaries/data/mods/public/simulation/components/Capturable.js
===================================================================
--- binaries/data/mods/public/simulation/components/Capturable.js	(révision 18615)
+++ binaries/data/mods/public/simulation/components/Capturable.js	(copie de travail)
@@ -96,13 +96,13 @@ Capturable.prototype.Reduce = function(a
 			{
 				removedAmount += this.cp[i];
 				this.cp[i] = 0;
 			}
 		}
-		distributedAmount = numberOfEnemies ? (amount - removedAmount) / numberOfEnemies : 0;	    
+		distributedAmount = numberOfEnemies ? (amount - removedAmount) / numberOfEnemies : 0;
 	}
-	
+
 	// give all cp taken to the player
 	var takenCp = this.maxCp - this.cp.reduce((a, b) => a + b);
 	this.cp[playerID] += takenCp;
 
 	this.CheckTimer();
@@ -216,11 +216,11 @@ Capturable.prototype.TimerTick = functio
 
 	// nothing changed, stop the timer
 	var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
 	cmpTimer.CancelTimer(this.timer);
 	this.timer = 0;
-	Engine.PostMessage(this.entity, MT_CaptureRegenStateChanged, {"regenerating": false, "regenRate": 0, "territoryDecay": 0});
+	Engine.PostMessage(this.entity, MT_CaptureRegenStateChanged, { "regenerating": false, "regenRate": 0, "territoryDecay": 0 });
 };
 
 /**
  * Start the regeneration timer when no timer exists.
  * When nothing can be modified (f.e. because it is fully regenerated), the
@@ -237,11 +237,11 @@ Capturable.prototype.CheckTimer = functi
 	if (regenRate == 0 && decay == 0)
 		return;
 
 	var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
 	this.timer = cmpTimer.SetInterval(this.entity, IID_Capturable, "TimerTick", 1000, 1000, null);
-	Engine.PostMessage(this.entity, MT_CaptureRegenStateChanged, {"ticking": true, "regenRate": regenRate, "territoryDecay": decay});
+	Engine.PostMessage(this.entity, MT_CaptureRegenStateChanged, { "regenerating": true, "regenRate": regenRate, "territoryDecay": decay });
 };
 
 //// Message Listeners ////
 
 Capturable.prototype.OnValueModification = function(msg)
Index: binaries/data/mods/public/simulation/components/interfaces/Capturable.js
===================================================================
--- binaries/data/mods/public/simulation/components/interfaces/Capturable.js	(révision 18615)
+++ binaries/data/mods/public/simulation/components/interfaces/Capturable.js	(copie de travail)
@@ -7,11 +7,9 @@ Engine.RegisterInterface("Capturable");
  */
 Engine.RegisterMessageType("CapturePointsChanged");
 
 /**
  * Message in the form of { "regenerating": boolean, "rate": number, "territoryDecay": number }
- * or in the form { "ticking": boolean, "rate": number, "territoryDecay": number }
  * where "rate" value is always zero when not decaying,
  * sent from Capturable component.
  */
 Engine.RegisterMessageType("CaptureRegenStateChanged");
-
Index: binaries/data/mods/public/simulation/components/tests/test_Capturable.js
===================================================================
--- binaries/data/mods/public/simulation/components/tests/test_Capturable.js	(nonexistent)
+++ binaries/data/mods/public/simulation/components/tests/test_Capturable.js	(copie de travail)
@@ -0,0 +1,243 @@
+Engine.LoadHelperScript("Player.js");
+Engine.LoadHelperScript("ValueModification.js");
+Engine.LoadComponentScript("interfaces/AuraManager.js");
+Engine.LoadComponentScript("interfaces/Auras.js");
+Engine.LoadComponentScript("interfaces/Capturable.js");
+Engine.LoadComponentScript("interfaces/GarrisonHolder.js");
+Engine.LoadComponentScript("interfaces/StatisticsTracker.js");
+Engine.LoadComponentScript("interfaces/TechnologyManager.js");
+Engine.LoadComponentScript("interfaces/TerritoryDecay.js");
+Engine.LoadComponentScript("interfaces/Timer.js");
+Engine.LoadComponentScript("Capturable.js");
+
+let structure = 20;
+let playerID = 1;
+let regenRate = 2;
+let garrisonedEntities = [30,31,32,33];
+let decay = false;
+let decayRate = 30;
+let maxCp = 3000;
+
+function testCapturable(test_function)
+{
+	ResetState();
+
+	AddMock(SYSTEM_ENTITY, IID_Timer, {
+		"SetInterval": (ent, iid, funcname, time, repeattime, data) => {},
+		"CancelTimer": timer => {}
+	});
+
+	AddMock(structure, IID_Ownership, {
+		"GetOwner": () => playerID,
+		"SetOwner": id => {}
+	});
+
+	AddMock(structure, IID_GarrisonHolder, {
+		"GetEntities": () => garrisonedEntities
+	});
+
+	AddMock(structure, IID_Fogging, {
+		"Activate": () => {}
+	});
+
+	AddMock(10, IID_Player, {
+		"IsEnemy": id => id != 0
+	});
+
+	AddMock(11, IID_Player, {
+		"IsEnemy": id => id != 1 && id != 2
+	});
+
+	AddMock(12, IID_Player, {
+		"IsEnemy": id => id != 1 && id != 2
+	});
+
+	AddMock(13, IID_Player, {
+		"IsEnemy": id => id != 3
+	});
+
+	AddMock(SYSTEM_ENTITY, IID_PlayerManager, {
+		"GetNumPlayers": () => 4,
+		"GetPlayerByID": id => 10 + id
+	});
+
+	AddMock(structure, IID_StatisticsTracker, {
+		"LostEntity" : () => {},
+		"CapturedBuilding": () => {}
+	});
+
+	let cmpCapturable = ConstructComponent(structure, "Capturable", {
+		"CapturePoints" : maxCp,
+		"RegenRate" : regenRate,
+		"GarrisonRegenRate" : 5
+	});
+
+	AddMock(structure, IID_TerritoryDecay, {
+		"IsDecaying": () => decay,
+		"GetDecayRate": () => decayRate,
+		"GetConnectedNeighbours": () => [20, 0, 20, 10]
+	});
+
+	test_function(cmpCapturable);
+	Engine.PostMessage = (ent, iid, message) => {};
+}
+
+// Tests initialisation of the capture points when the entity is created
+testCapturable( cmpCapturable => {
+	Engine.PostMessage = function(ent, iid, message)
+	{
+		TS_ASSERT_UNEVAL_EQUALS(message, { "regenerating": true, "regenRate": 22 , "territoryDecay": 0 });
+	};
+	cmpCapturable.OnOwnershipChanged({ "from": -1, "to": playerID });
+	TS_ASSERT_UNEVAL_EQUALS(cmpCapturable.GetCapturePoints(), [0, maxCp, 0, 0])
+});
+
+// Tests if the message is sent when capture points change
+testCapturable( cmpCapturable => {
+	cmpCapturable.SetCapturePoints([0, 2000, 0 , 1000]);
+	TS_ASSERT_UNEVAL_EQUALS(cmpCapturable.GetCapturePoints(), [0, 2000, 0 , 1000]);
+	Engine.PostMessage = function(ent, iid, message)
+	{
+		TS_ASSERT_UNEVAL_EQUALS(message, { "capturePoints": [0, 2000, 0 , 1000] });
+	};
+	cmpCapturable.RegisterCapturePointsChanged();
+});
+
+// Tests reducing capture points (after a capture attack or a decay)
+testCapturable( cmpCapturable => {
+	cmpCapturable.SetCapturePoints([0, 2000, 0 , 1000]);
+	cmpCapturable.CheckTimer();
+	Engine.PostMessage = function(ent, iid, message)
+	{
+		if (iid == MT_CapturePointsChanged)
+			TS_ASSERT_UNEVAL_EQUALS(message, { "capturePoints": [0, 2000 - 100, 0, 1000 + 100] });
+		if (iid == MT_CaptureRegenStateChanged)
+			TS_ASSERT_UNEVAL_EQUALS(message, { "regenerating": true, "regenRate": 22, "territoryDecay": 0 });
+	};
+	TS_ASSERT_UNEVAL_EQUALS(cmpCapturable.Reduce(100, 3), 100);
+	TS_ASSERT_UNEVAL_EQUALS(cmpCapturable.GetCapturePoints(), [0, 2000 - 100, 0, 1000 + 100]);
+});
+
+// Tests reducing capture points (after a capture attack or a decay)
+testCapturable( cmpCapturable => {
+	cmpCapturable.SetCapturePoints([0, 2000, 0 , 1000]);
+	cmpCapturable.CheckTimer();
+	Engine.PostMessage = function(ent, iid, message)
+	{
+		if (iid == MT_CapturePointsChanged)
+			TS_ASSERT_UNEVAL_EQUALS(message, { "capturePoints": [0, 0, 0, 3000] });
+		if (iid == MT_CaptureRegenStateChanged)
+			TS_ASSERT_UNEVAL_EQUALS(message, { "regenerating": true, "regenRate": 22, "territoryDecay": 0 });
+	};
+	cmpCapturable.Reduce(2500, 3);
+	TS_ASSERT_UNEVAL_EQUALS(cmpCapturable.GetCapturePoints(), [0, 0, 0, 3000]);
+});
+
+// Tests regeneration
+testCapturable( cmpCapturable => {
+	cmpCapturable.SetCapturePoints([12, 2950, 2 , 36]);
+	cmpCapturable.CheckTimer();
+
+	Engine.PostMessage = function(ent, iid, message)
+	{
+		if (iid == MT_CapturePointsChanged)
+			TS_ASSERT_UNEVAL_EQUALS(message, { "capturePoints": [1, 2972, 2, 25] });
+		if (iid == MT_CaptureRegenStateChanged)
+			TS_ASSERT_UNEVAL_EQUALS(message, { "regenerating": true, "regenRate": 22, "territoryDecay": 0 });
+	};
+	cmpCapturable.TimerTick();
+	TS_ASSERT_UNEVAL_EQUALS(cmpCapturable.GetCapturePoints(), [1, 2972, 2, 25]);
+
+	Engine.PostMessage = function(ent, iid, message)
+	{
+		if (iid == MT_CapturePointsChanged)
+			TS_ASSERT_UNEVAL_EQUALS(message, { "capturePoints": [0, 2994, 2, 4] });
+		if (iid == MT_CaptureRegenStateChanged)
+			TS_ASSERT_UNEVAL_EQUALS(message, { "regenerating": true, "regenRate": 22, "territoryDecay": 0 });
+	};
+	cmpCapturable.TimerTick();
+	TS_ASSERT_UNEVAL_EQUALS(cmpCapturable.GetCapturePoints(), [0, 2994, 2, 4 ]);
+
+	Engine.PostMessage = function(ent, iid, message)
+	{
+		if (iid == MT_CapturePointsChanged)
+			TS_ASSERT_UNEVAL_EQUALS(message, { "capturePoints": [0, 2998, 2, 0] });
+		if (iid == MT_CaptureRegenStateChanged)
+			TS_ASSERT_UNEVAL_EQUALS(message, { "regenerating": true, "regenRate": 22, "territoryDecay": 0 });
+	};
+	cmpCapturable.TimerTick();
+	TS_ASSERT_UNEVAL_EQUALS(cmpCapturable.GetCapturePoints(), [0, 2998, 2, 0]);
+
+	Engine.PostMessage = function(ent, iid, message)
+	{
+		if (iid == MT_CapturePointsChanged)
+			TS_ASSERT_UNEVAL_EQUALS(message, { "capturePoints": [0, 2998, 2, 0] });
+		if (iid == MT_CaptureRegenStateChanged)
+			TS_ASSERT_UNEVAL_EQUALS(message, { "regenerating": false, "regenRate": 0, "territoryDecay": 0 });
+	};
+	cmpCapturable.TimerTick();
+	TS_ASSERT_UNEVAL_EQUALS(cmpCapturable.GetCapturePoints(), [0, 2998, 2, 0]);
+});
+
+// If the regeneration rate becomes negative, capture points are given in favour of gaia.
+regenRate = -32;
+testCapturable( cmpCapturable => {
+	cmpCapturable.SetCapturePoints([100, 2800, 50, 50]);
+	cmpCapturable.CheckTimer();
+	TS_ASSERT_EQUALS(cmpCapturable.GetRegenRate(), -12);
+
+	Engine.PostMessage = function(ent, iid, message)
+	{
+		if (iid == MT_CapturePointsChanged)
+			TS_ASSERT_UNEVAL_EQUALS(message, { "capturePoints": [112, 2796, 46, 46] });
+		if (iid == MT_CaptureRegenStateChanged)
+			TS_ASSERT_UNEVAL_EQUALS(message, { "regenerating": true, "regenRate": -12, "territoryDecay": 0 });
+	};
+	cmpCapturable.TimerTick();
+	TS_ASSERT_UNEVAL_EQUALS(cmpCapturable.GetCapturePoints(), [112, 2796, 46, 46]);
+});
+regenRate=2;
+
+// Tests decay
+testCapturable( cmpCapturable => {
+	decay = true;
+	cmpCapturable.SetCapturePoints([2900, 35, 10, 55]);
+	cmpCapturable.CheckTimer();
+
+	let call = 1;
+	Engine.PostMessage = function(ent, iid, message)
+	{
+		if (iid == MT_CapturePointsChanged && call == 1)
+		{
+			TS_ASSERT_UNEVAL_EQUALS(message, { "capturePoints": [2912, 5, 22, 61] });
+			++call;
+		}
+		else if (iid == MT_CapturePointsChanged && call == 2)
+				TS_ASSERT_UNEVAL_EQUALS(message, { "capturePoints": [2901, 27, 22, 50] });
+		else if (iid == MT_CaptureRegenStateChanged)
+			TS_ASSERT_UNEVAL_EQUALS(message, { "regenerating": true, "regenRate": 22 , "territoryDecay": 30 });
+	};
+	cmpCapturable.TimerTick();
+	TS_ASSERT_UNEVAL_EQUALS(cmpCapturable.GetCapturePoints(), [2901, 27, 22, 50]);
+	decay = false;
+});
+
+// Tests Reduce
+function testReduce(amount, player, taken)
+{
+	testCapturable(cmpCapturable => {
+		cmpCapturable.SetCapturePoints([0, 2000, 0 , 1000]);
+		cmpCapturable.CheckTimer();
+		TS_ASSERT_UNEVAL_EQUALS(cmpCapturable.Reduce(amount, player), taken);
+	});
+}
+
+testReduce(50, 3, 50);
+testReduce(50, 2, 50);
+testReduce(50, 1, 50);
+testReduce(-50, 3, 0);
+testReduce(50, 0, 50);
+testReduce(0, 3, 0);
+testReduce(1500, 3, 1500);
+testReduce(2000, 3, 2000);
+testReduce(3000, 3, 2000);
