Index: /experiments/frams/deathmatch/data/deathmatch_sample.gen
===================================================================
--- /experiments/frams/deathmatch/data/deathmatch_sample.gen	(revision 231)
+++ /experiments/frams/deathmatch/data/deathmatch_sample.gen	(revision 231)
@@ -0,0 +1,44 @@
+# created Mon Jan 16 16:46:24 2012
+# by Framsticks 3.2rc2+
+org:
+name:HP Box Finder
+genotype:~
+lllfffX[0:2.420,2:-2,1 :-1][-1 :1,0:1,0:-1][-1:1](RRlllfffMMMX[|-1:-10]lllFFFMMMX[|-2:-1],fffIXlllfffMMMsX[|6:10,3:-10](RRlllfffMMMIX[|-4:-10]lllFFFMMMIX[|-5:-1][Sp,pop:0],,RRlllfffMMMIX[|-7:10]lllFFFMMMIX[|-8:-1][Sp,pop:0]),RRlllfffMMMX[|-10:10]lllFFFMMMX[|-11:-1.784])~
+info:~
+Land food finder. Created manually and then evolved by Miron Sadziak
+
+Modified by Mateusz Cichenski - replaced smell sensors with Smell Population sensor from FramsDM to seek HP Boxes
+(manually modified)~
+num:6
+gnum:0
+popsiz:3
+lifespan:4951.33
+velocity:0.00718781
+distance:35.6247
+vertvel:0.000165422
+vertpos:0.0150043
+user1:null
+user2:null
+user3:null
+uid:g5
+
+org:
+name:Food finder
+genotype:~
+lllfffX[0:2.420,2:-2,1 :-1][-1 :1,0:1,0:-1][-1:1](RRlllfffMMMX[|-1:-10]lllFFFMMMX[|-2:-1],fffIXlllfffMMMsX[|6:10,3:-10](RRlllfffMMMIX[|-4:-10]lllFFFMMMIX[|-5:-1][S:1],,RRlllfffMMMIX[|-7:10]lllFFFMMMIX[|-8:-1][S:1]),RRlllfffMMMX[|-10:10]lllFFFMMMX[|-11:-1.784])~
+info:~
+Land food finder. Created manually and then evolved by Miron Sadziak
+(manually modified)~
+num:6
+gnum:0
+popsiz:10
+lifespan:1673
+velocity:0.00637827
+distance:10.4275
+vertvel:0
+vertpos:0
+user1:null
+user2:null
+user3:null
+uid:g9
+
Index: /experiments/frams/deathmatch/data/scripts/deathmatch-levels.inc
===================================================================
--- /experiments/frams/deathmatch/data/scripts/deathmatch-levels.inc	(revision 231)
+++ /experiments/frams/deathmatch/data/scripts/deathmatch-levels.inc	(revision 231)
@@ -0,0 +1,45 @@
+// if you want to create levels file of your own make sure it implements:
+// 1. global variable levels
+// 2. function initLevels() that initializes levels structure
+// 3. function nextLevel() that puts into ExpParams.level variable the value of next level's index (or -1 if random) 
+
+// descriptions of game levels
+global levels;
+
+function initLevels(){
+  // define available levels
+  // fields in an level array are:
+  // 1: name,
+  // 2: world size,
+  // 3: world band,
+  // 4: world type,
+  // 5: world map,
+  
+  levels = Vector.new(); 
+  // #1
+  levels.add([
+			"Small arena", 
+            50,                  
+            2,
+            0,
+			""
+			]);
+			
+  // #1
+  levels.add([
+			"Medium arena", 
+            65,                  
+            2,
+            0,
+			""
+			]);
+  
+  // #1
+  levels.add([
+			"Large arena", 
+            80,                  
+            2,
+            0,
+			""
+			]);
+}
Index: /experiments/frams/deathmatch/data/scripts/deathmatch-smellPopulation.neuro
===================================================================
--- /experiments/frams/deathmatch/data/scripts/deathmatch-smellPopulation.neuro	(revision 231)
+++ /experiments/frams/deathmatch/data/scripts/deathmatch-smellPopulation.neuro	(revision 231)
@@ -0,0 +1,29 @@
+class:
+name:Sp
+longname:Smell population
+description:Detects the population given by the "pop" parameter
+prefoutput:1
+preflocation:1
+vhints:32
+# 32=receptor class
+icon:~
+64,5,7,100,50,90,50,90,40,70,40,80,50,70,60,90,60,90,50,3,10,40,15,45,15,55,10,60,5,20,30,25,35,30,45,30,55,25,65,20,70,4,15,35,20,40,22,50,20,60,15,65,5,75,50,50,50,45,45,40,50,45,55,50,50~
+code:~
+function init()
+{}
+
+function go()
+{
+  if (Populations.size>Fields.pop)
+	Neuro.state = Populations[Fields.pop].senseCreaturesProperty(
+		Neuro.position_x, Neuro.position_y, Neuro.position_z,
+		"energy", Neuro.creature);
+  else
+	Neuro.state=0;
+}
+~
+
+prop:
+id:pop
+name:population
+type:d
Index: /experiments/frams/deathmatch/data/scripts/deathmatch-utils.inc
===================================================================
--- /experiments/frams/deathmatch/data/scripts/deathmatch-utils.inc	(revision 231)
+++ /experiments/frams/deathmatch/data/scripts/deathmatch-utils.inc	(revision 231)
@@ -0,0 +1,62 @@
+// ///////////////////////////////////////////////////
+// Computing angle between two vectors
+function vectorsAngle(v1, v2)
+{
+  var x1 = v1[0];
+  var y1 = v1[1];
+  var l1 = Math.sqrt(x1 * x1 + y1 * y1);
+
+  var x2 = v2[0];
+  var y2 = v2[1];
+  var l2 = Math.sqrt(x2 * x2 + y2 * y2);
+
+  var vProduct = x1 * y2 - y1 * x2;
+  var direction;
+
+  if (vProduct >= 0)
+    direction = 1.0;
+  else
+    direction = -1.0;
+
+  var cosinus = (x1 * x2 + y1 * y2) / (l1 * l2);
+  var angle = Math.acos(cosinus);
+
+  print("Vector1: [" + x1 + ", " + y1 + "] Vector2: " + x1 + ", " + y1 + "] " +
+        "Angle: " + (180.0 * angle / Math.pi) + " Direction: " + direction);
+
+  return direction * angle;
+}
+
+// ///////////////////////////////////////////////////
+// Computing rotated vector v1 by angle theta
+function rotateVector(v1, theta)
+{
+  var x1 = v1[0];
+  var y1 = v1[1];
+  var x2 = x1 * Math.cos(inRads(theta)) -  y1 * Math.sin(inRads(theta));
+  var y2 = x1 * Math.sin(inRads(theta)) +  y1 * Math.cos(inRads(theta));
+
+  return [x2, y2];
+}
+
+// ///////////////////////////////////////////////////
+// Convert degrees to radians
+function inRads(degrees)
+{
+  return (Math.pi * degrees) / 180.0;
+}
+
+// ///////////////////////////////////////////////////
+// Returns the index of the row containing given value in given column
+// -1 if value was not found
+function arrayContains(array, column, value)
+{
+  if (array == null) return -1;
+  var i = 0;
+  for (i = 0; i < array.size; i++)
+  {
+    if (array.get(i) != null)
+      if (array.get(i).get(column) == value) return i;
+  }
+  return -1;
+}
Index: /experiments/frams/deathmatch/data/scripts/deathmatch.expdef
===================================================================
--- /experiments/frams/deathmatch/data/scripts/deathmatch.expdef	(revision 231)
+++ /experiments/frams/deathmatch/data/scripts/deathmatch.expdef	(revision 231)
@@ -0,0 +1,887 @@
+expdef:
+name:Deathmatch
+info:~
+This experiment simulates a team deathmatch. It means that framstick creatures are grouped in teams and the basic rule is: "My team vs WORLD!".
+
+The basic battle rules and collectibles are implemented. Statistics for the battle are saved to text files in CSV format: creatures.txt, teams.txt.
+
+For more information, see  http://www.framsticks.com/deathmatch
+~
+code:~
+
+//Author: Mateusz Cicheñski @ Poznan University of Technology, 2012
+
+@include "deathmatch-utils.inc"
+@include "deathmatch-levels.inc"
+
+global idleSimulationSteps;
+global battleNumber;
+global teams;
+
+function onExpDefLoad()
+{
+  idleSimulationSteps = 0;
+  battleNumber = 0;
+  initLevels();
+  initPopulations();
+  
+  Shapes.set("1p_weaponbox","//0\n"+
+  "p:-0.5, -0.5, -0.5\n"+
+  "p: 0.5, -0.5, -0.5\n"+
+  "p: 0.5, 0.5, -0.5\n"+
+  "p: -0.5, 0.5, -0.5\n"+
+  "p:-0.5, -0.5, 0.5\n"+
+  "p:0.5, -0.5, 0.5\n"+
+  "p:0.5, 0.5, 0.5\n"+
+  "p:-0.5, 0.5, 0.5\n"+
+  "j: 0,1\n"+
+  "j: 1,2\n"+
+  "j: 2,3\n"+
+  "j: 3,0\n"+
+  "j: 4,5\n"+
+  "j: 5,6\n"+
+  "j: 6,7\n"+
+  "j: 7,4\n"+
+  "j: 0,4\n"+
+  "j: 1,5\n"+
+  "j: 2,6\n"+
+  "j: 3,7", 0x0FAA0F);
+  
+  Shapes.set("1p_hpbox","//0\n"+
+  "p:-0.5, -0.5, -0.5\n"+
+  "p: 0.5, -0.5, -0.5\n"+
+  "p: 0.5, 0.5, -0.5\n"+
+  "p: -0.5, 0.5, -0.5\n"+
+  "p:-0.5, -0.5, 0.5\n"+
+  "p:0.5, -0.5, 0.5\n"+
+  "p:0.5, 0.5, 0.5\n"+
+  "p:-0.5, 0.5, 0.5\n"+
+  "j: 0,1\n"+
+  "j: 1,2\n"+
+  "j: 2,3\n"+
+  "j: 3,0\n"+
+  "j: 4,5\n"+
+  "j: 5,6\n"+
+  "j: 6,7\n"+
+  "j: 7,4\n"+
+  "j: 0,4\n"+
+  "j: 1,5\n"+
+  "j: 2,6\n"+
+  "j: 3,7", 0xAA0F0F);
+}
+
+function initPopulations()
+{
+  //clear anything in populations
+  Populations.clear();
+  
+  //add Food population (group 0)
+  Population.name = "Food";
+  Population.nnsim = 0;
+  Population.enableperf = 0;
+  Population.death = 1;
+  Population.energy = 1;
+  Population.selfmask = 0x20002;
+  Population.othermask = 0x10002;
+  
+  //add Weapon Upgrade population (group 1)
+  Populations.addGroup("Upgrades");
+  Population.nnsim = 0;
+  Population.enableperf = 0;
+  Population.death = 1;
+  Population.energy = 1;
+  Population.selfmask = 0x20002;
+  Population.othermask = 0x10002;
+
+  //add Teams populations (group above 2)
+  var i = 0;
+  teams = ExpParams.teamCount;
+  if (teams == 1) {
+	GenePools.group = 0;
+	teams = GenePool.size;
+  }
+  for (i = 0; i < teams; i++)
+  {
+    Populations.addGroup("Team "+i);
+    Population.nnsim = 1;
+    Population.enableperf = 1;
+    Population.death = 0;
+    Population.energy = 1;
+    Population.selfmask = 0x50001;
+    Population.othermask = 0x60001;
+  }
+  
+  //Collisions on 32 bit mask:
+  // standard - rebound creatures => if lower bits 0xFFFF
+  // custom - custom function handler => if higher bits 0xFFFF0000
+  //Collisions map:
+  // Food vs Food = 0x20002 & 0x10002 = 2 - standard
+  // Upgrades vs Upgrades = 0x20002 & 0x10002 = 2 - standard
+  // Team X vs Team X = 0x50001 & 0x60001 = 0x40001 - standard + custom
+  // Team X vs Team Y = 0x50001 & 0x60001 = 0x40001 - standard + custom
+  // Food vs Upgrades = 0x20002 & 0x10002 = 2 - standard
+  // Food vs Team X = 0x20002 & 0x60001 = 0x20000 || 0x50001 & 0x10002 = 0x10000 - custom
+  // Upgrades vs Team X = 0x20002 & 0x60001 = 0x20000 || 0x50001 & 0x10002 = 0x10000 - custom
+}
+
+function initCreatures()
+{
+  var i = 0;
+  var j = 0;
+  GenePools.group = 0;
+  for (i = 0; i < Populations.size - 2; i++)
+  {
+    Populations.group = i + 2;
+	for (j = 0; j < ExpParams.teamSize; j++)
+	{
+	  if (ExpParams.teamCount != 1)
+		GenePools.genotype = Math.random(GenePool.size);
+	  else
+	    GenePools.genotype = i;
+	  Population.createFromGenotype();
+	  Creature.name = "Member " + j;
+	}
+  }
+}
+
+function onExpInit()
+{
+  battleNumber = 0;
+  initAll();
+}
+
+function initAll()
+{
+  idleSimulationSteps = 0;
+  battleNumber += 1;
+  initLevel();
+  initPopulations();
+  initCreatures();
+  initFiles();
+  writeTeamStatistics();
+}
+
+function initFiles()
+{
+  var f=File.createDirect("creatures"+battleNumber+".txt","Creature statistics");
+  f.writeString("#Creature name; Team; Kills; Assists; Total damage dealt; Total damage received; HP boxes collected; Upgrade boxes collected; Lifespan\n");
+  f.close();
+  
+  var s = "#Time interval; ";
+  var i = 0;
+  for (i = 0; i < Populations.size - 2; i++)
+  {
+    s += "Team " + i + "; ";
+	s += "Team members; ";
+  }
+  s += "Total energy\n";
+  
+  var f=File.createDirect("teams"+battleNumber+".txt","Teams statistics");
+  f.writeString(s);
+  f.close();
+}
+
+function initLevel()
+{
+  var levelNumber;
+  if (ExpParams.level == -1)
+    levelNumber = Math.random(levels.size);
+  else
+    levelNumber = ExpParams.level;
+
+  var level = levels.get(levelNumber);
+  Simulator.print("Level #" + (levelNumber + 1) + ": " + level[0]);
+
+  World.wrldbnd = level[2];
+  World.wrldsiz = level[1];
+  World.wrldwat = -1;
+  World.wrldtyp = level[3];
+  World.wrldmap = level[4];
+
+  World.wrldchg();
+}
+
+function tryCreateHPBox()
+{
+  Populations.group = 0;
+  
+  if (Population.size >= ExpParams.hpBoxTotalCount) return;
+  
+  if (Math.rnd01 > ExpParams.hpBoxProbability) return;
+  
+  var food = Populations.createFromString("//0\nm:Vstyle=hpbox\np:");
+  food.name = "HP Box";
+  food.idleen = ExpParams.hpBoxIdleEnergy;
+  food.energ0 = ExpParams.hpBoxStartingEnergy;
+  food.energy = food.energ0;
+  food.nnenabled = 0;
+}
+
+function tryCreateUpgradeBox()
+{
+  Populations.group = 1;
+  
+  if (Population.size >= ExpParams.upgradeBoxTotalCount) return;
+  
+  if (Math.rnd01 > ExpParams.upgradeBoxProbability) return;
+  
+  var weapon = Populations.createFromString("//0\nm:Vstyle=weaponbox\np:");
+  weapon.name = "Weapon Box";
+  weapon.idleen = ExpParams.upgradeBoxIdleEnergy;
+  weapon.energ0 = ExpParams.upgradeBoxStartingEnergy;
+  weapon.energy = weapon.energ0;
+  weapon.nnenabled = 0;
+}
+
+//handle creature placement in the world
+function onBorn()
+{
+  // place newly born creature
+  var retry = 20; //try 20 times
+  var placed_ok = 0;
+  while (retry-- && !placed_ok)
+  {
+    switch (Populations.group)
+    {
+    case 0: //food placement
+      place_centerhead();
+      break;
+    case 1: //upgrade placement
+      place_centerhead();
+      break;
+    default:
+	  Creature.user1 = Vector.new();
+	  Creature.user1.add(0.0); //attack cooldown (0)
+	  Creature.user1.add(0.0); //bonus damage (1)
+	  Creature.user2 = Vector.new();
+	  Creature.user2.add(0); //kills (0)
+	  Creature.user2.add(0); //assists (1)
+	  Creature.user2.add(0); //total damage dealt (2)
+	  Creature.user2.add(0); //total damage received (3)
+	  Creature.user2.add(0); //HP boxes collected (4)
+	  Creature.user2.add(0); //Upgrade boxes collected (5)
+	  Creature.user3 = Vector.new(); //who dealt damage to this creature
+	  Creature.energ0 = ExpParams.creatureStartingEnergy;
+	  Creature.energy = Creature.energ0;
+      place_inTeamSpot();
+      break;
+    }
+
+    if (!Populations.creatBBCollisions(0))
+      placed_ok = 1;
+  }
+  if (!placed_ok)
+    print("onBorn() could not avoid collisions.");
+}
+
+//place creature in random spot and make it head center of the map
+function place_centerhead()
+{
+  var x, y, z;
+  x = (World.wrldsiz - Creature.size_x) * Math.rnd01 - Creature.size_x / 2;
+  y = (World.wrldsiz - Creature.size_y) * Math.rnd01 - Creature.size_y / 2;
+  z = WorldMap.getHeight(x, y);
+
+  var hx = 0.0 + (World.wrldsiz / 2) - x;
+  var hy = 0.0 + (World.wrldsiz / 2) - y;
+  var alpha_rad = vectorsAngle([1.0, 0.0], [hx, hy]);
+
+  Creature.rotate(0, 0, alpha_rad);
+  Creature.moveAbs(x, y, z - 0.999);
+  
+  print("Creature placed in [" + x +" " + y + " " + z);
+
+  return ;
+}
+
+//Place creature in correct team spot (valid for Team creatures only)
+//Creatures are placed in circle with random rotation (commented out: heading center of the map) in their respective team groups
+function place_inTeamSpot()
+{
+  var radius = 360.00 * (Populations.group - 1) / teams;
+
+  var x, y, z;
+  x = (ExpParams.teamSpawningAreaSize) * Math.rnd01 - Creature.size_x / 2 - ExpParams.teamSpawningAreaSize/2;
+  y = (ExpParams.teamSpawningAreaSize) * Math.rnd01 - Creature.size_y / 2 - ExpParams.teamSpawningAreaSize/2;
+  
+  //vector of length half the world size minus spawning area size
+  var vect = [0.0 + World.wrldsiz/2 - ExpParams.teamSpawningAreaSize/2 - 1.0, 0.0];
+  vect = rotateVector(vect, radius);
+  
+  //translate creature by given vector
+  //then translate by halfworld (as it is the center of the world
+  //and the positions were calculated for point [0,0]
+  x = x + vect[0] + World.wrldsiz/2;
+  y = y + vect[1] + World.wrldsiz/2;
+
+  z = WorldMap.getHeight(x, y);
+  
+  var alpha_rad = 360.0 * Math.rnd01; //random rotation
+  
+  //this would rotate creatures so that they would face the world center:
+  //var hx = 0.0 + (World.wrldsiz / 2) - x;
+  //var hy = 0.0 + (World.wrldsiz / 2) - y;
+  //var alpha_rad = vectorsAngle([1.0, 0.0], [hx, hy]);
+  
+  Creature.rotate(0, 0, alpha_rad);
+  //place it mid-air
+  Creature.moveAbs(x, y, z);
+
+  return ;
+}
+
+// ////////////////////////////////
+// Simulation steps
+
+function onStep()
+{  
+  idleSimulationSteps += 1;
+  tryCreateHPBox();
+  tryCreateUpgradeBox();
+  
+  lowerCooldownCounters();
+  applySuddenDeathModifier();
+
+  clearDeadCreatures();
+  checkGameState();
+}
+
+function writeTeamStatistics()
+{
+  var s = "" + Simulator.time + "; ";
+  var i = 0;
+  var j = 0;
+  var total = 0.0;
+  for (i = 0; i < Populations.size - 2; i++)
+  {
+    Populations.group = i + 2;
+	var sum = 0.0;
+	for (j = 0; j < Population.size; j++)
+	{
+	  Populations.creature = j;
+	  sum += Creature.energy;
+	}
+    s += "" + sum + "; ";
+	s += "" + Population.size + "; ";
+	total += sum;
+  }
+  if (total < 0) total == 0;
+  s += "" + total + "\n";
+  
+  var f=File.appendDirect("teams"+battleNumber+".txt","Teams statistics");
+  f.writeString(s);
+  f.close();
+}
+
+function clearDeadCreatures()
+{
+  var i = 0;
+  var j = 0;
+  for (i = 0; i < Populations.size - 2; i++)
+  {
+    Populations.group = i + 2;
+	for (j = 0; j < Population.size; j++)
+	{
+      Populations.creature = j;
+      if (Creature.energy <= 0) Populations.killSelected();
+	}
+  }
+}
+
+function applySuddenDeathModifier()
+{
+  if (idleSimulationSteps < ExpParams.stagnationInterval) return;
+
+  var i = 0;
+  var j = 0;
+  for (i = 0; i < Populations.size - 2; i++)
+  {
+    Populations.group = i + 2;
+	for (j = 0; j < Population.size; j++)
+	{
+	  Populations.creature = j;
+	  Creature.energy -= ExpParams.stagnationHealthReduce;
+	}
+  }
+}
+function lowerCooldownCounters()
+{
+  var i = 0;
+  var j = 0;
+  for (i = 0; i < Populations.size - 2; i++)
+  {
+    Populations.group = i + 2;
+	for (j = 0; j < Population.size; j++)
+	{
+	  Populations.creature = j;
+	  if (Creature.user1 != null)
+	  {
+	    if (Creature.user1.get(0) > 0) Creature.user1.set(0, Creature.user1.get(0) - 1);
+	    if (Creature.user1.get(0) < 0) Creature.user1.set(0,0.0);
+	    if (Creature.user1.get(1) > 0) Creature.user1.set(1, Creature.user1.get(1) - ExpParams.upgradeBoxDecay);
+	    if (Creature.user1.get(1) < 0) Creature.user1.set(1,0.0);
+	  }
+	}
+  }
+}
+
+function checkGameState()
+{
+  var i = 0;
+  var alivePop = 0;
+  for (i = 0; i < Populations.size - 2; i++)
+  {
+    Populations.group = i + 2;
+	if (Population.size > 0) alivePop += 1;
+  }
+  
+  if (alivePop == 0 && Simulator.time <= 1)
+  {
+    onExpInit(); //do initialization for user, because he forgot to do so
+	return;
+  }
+  
+  if (alivePop <= 1)
+  {
+    for (i = 0; i < Populations.size - 2; i++)
+    {
+      Populations.group = i + 2;
+	  if (Population.size > 0)
+	  {
+	    Simulator.print("Battle is OVER! The winner is: " + Population.name);
+		
+		//write stats of last living creatures
+		var f=File.appendDirect("creatures"+battleNumber+".txt","Creature statistics");
+		var j = 0;
+		for (j = 0; j < Population.size; j++)
+		{
+		  Populations.creature = j;
+		  //f.writeString("Creature name; Team; Kills; Assists; Total damage dealt; Total damage received; HP boxes collected; Upgrade boxes collected; Lifespan");
+		  if (Creature.user2 != null)
+		    f.writeString(Creature.name + "; " + Population.name + "; " + Creature.user2.get(0) + "; " + Creature.user2.get(1) + "; " + Creature.user2.get(2) + "; " + Creature.user2.get(3) + "; " + Creature.user2.get(4) + "; " + Creature.user2.get(5) + "; " + Creature.lifespan + "\n");
+		}
+
+		f.close();
+	  }
+    }
+	if (alivePop == 0) Simulator.print("Battle is OVER! Total annihilation!");
+	
+	Simulator.stop();
+  }
+}
+
+function print(msg)
+{
+  if (ExpParams.debug)
+    Simulator.print(msg);
+}
+
+function stop(msg)
+{
+  Simulator.print(msg);
+  Simulator.stop();
+}
+
+// ////////////////////////////
+// Collisions
+
+//function executed on any Creature collission
+function onTeam0CrCollision, onTeam1CrCollision, onTeam2CrCollision, onTeam3CrCollision, onTeam4CrCollision, onTeam5CrCollision, onTeam6CrCollision, onTeam7CrCollision, onTeam8CrCollision, onTeam9CrCollision()
+{
+  var c1 = CrCollision.Creature1;
+  var c2 = CrCollision.Creature2;
+  
+  if (c1 == null) stop("2");
+  if (c2 == null) stop("2");
+  
+  if (c1.energy <= 0 || c2.energy <= 0)
+  {
+    //collision with already killed creature (chain resolution)
+	print("onCrCollision called with less than 2 Creatures");
+	return ;
+  }
+  
+  if (c1.group == null || c2.group == null)
+  {
+    //collision of creature without a group
+	print("onCrCollision called for creature without group, ignoring");
+	return ;
+  }
+  
+  //first condition should never occur, but who knows?
+  if (c1.group.index < 2 || c2.group.index < 2)
+  {
+    //collision with HP Box and Upgrades are handled separately
+	print("onCrCollision called for population 0 or 1, ignoring");
+	return ;
+  }
+ 
+  idleSimulationSteps = 0;
+ 
+  var kill_c1 = 0;
+  var kill_c2 = 0;
+  var dice1 = 0;
+  var dice2 = 0;
+  var changed = 0;
+  var i = 0;
+  for (i = 0; i < ExpParams.diceCount; i++)
+  {
+    dice1 = dice1 + Math.random(ExpParams.diceSides) + 1;
+	dice2 = dice2 + Math.random(ExpParams.diceSides) + 1;
+  }
+  var energy1 = 0.0 + ExpParams.creatureDamage * dice1;
+  var energy2 = 0.0 + ExpParams.creatureDamage * dice2;
+  energy1 += ExpParams.upgradeMultiplier * c1.user1.get(1);
+  energy2 += ExpParams.upgradeMultiplier * c2.user1.get(1);
+  if (c1.group == c2.group)
+  {
+    energy1 = ExpParams.friendlyFireDamageMultiplier * energy1;
+	energy2 = ExpParams.friendlyFireDamageMultiplier * energy2;
+  }
+  
+  if (c1.user1 == null) stop("3");
+  if (c1.user2 == null) stop("3");
+  if (c1.user3 == null) stop("3");
+  if (c2.user1 == null) stop("3");
+  if (c2.user2 == null) stop("3");
+  if (c2.user2 == null) stop("3");
+  
+  if (c2.user1.get(0) <= 0 && energy2 > 0)
+  {
+    changed = 1;
+  
+    c1.energy = c1.energy - energy2;
+	if (c1.energy < 0) c1.energy = 0;
+    print(c2.name + " [" + c2.group.name + "] rolled " + dice2 + " and dealt " + energy2 + " [+" + (ExpParams.upgradeMultiplier * c2.user1.get(1)) +" bonus] damage to " + c1.name + " [" + c1.group.name + "]");
+	c2.user1.set(0, ExpParams.attackCooldown);
+	c2.user2.set(2, c2.user2.get(2) + energy1);
+	c1.user2.set(3, c1.user2.get(3) + energy1);
+	var vect = Vector.new();
+	vect.add(c2.uid);
+	vect.add(c2.name);
+	var arrindex = arrayContains(c1.user3, 0, c2.uid);
+	if (arrindex != -1)
+	{
+	  vect.add(energy2 + c1.user3.get(arrindex).get(2));
+	  c1.user3.set(arrindex, vect);
+	}
+	else
+	{
+	  vect.add(energy2);
+	  if (c1.user3 == null) c1.user3 = Vector.new();
+	  c1.user3.add(vect);
+	  if (c1.energy > 0)
+	  {
+        c2.user2.set(1, c2.user2.get(1) + 1); //increase assists statistic
+	  }
+	}
+	if (c1.energy <= 0)
+	{
+	  c2.user2.set(0, c2.user2.get(0) + 1); //increase kills statistic
+	  kill_c1 = 1;
+	}
+  }
+  
+  if (c1.user1.get(0) <= 0 && energy1 > 0)
+  {
+    changed = 1;
+	
+    c2.energy = c2.energy - energy1;
+	if (c2.energy < 0) c2.energy = 0;
+    print(c1.name + " [" + c1.group.name + "] rolled " + dice1 + " and dealt " + energy1 + " [+" + (ExpParams.upgradeMultiplier * c1.user1.get(1)) +" bonus] damage to " + c2.name + " [" + c2.group.name + "]");
+    c1.user1.set(0, ExpParams.attackCooldown);
+	c1.user2.set(2, c1.user2.get(2) + energy1);
+	c2.user2.set(3, c2.user2.get(3) + energy1);
+	var vect = Vector.new();
+	vect.add(c1.uid);
+	vect.add(c1.name);
+	var arrindex = arrayContains(c2.user3, 0, c1.uid);
+	if (arrindex != -1)
+	{
+	  vect.add(energy1 + c2.user3.get(arrindex).get(2));
+	  c2.user3.set(arrindex, vect);
+	}
+	else
+	{
+	  vect.add(energy1);
+	  if (c2.user3 == null) c2.user3 = Vector.new();
+	  c2.user3.add(vect);
+	  if (c2.energy > 0)
+	  {
+        c1.user2.set(1, c1.user2.get(1) + 1); //increase assists statistic
+	  }
+	}
+	if (c2.energy <= 0)
+	{
+	  c1.user2.set(0, c1.user2.get(0) + 1); //increase kills statistic
+	  kill_c2 = 1;
+	}
+  }
+  
+  if (changed == 1)
+  {
+    writeTeamStatistics();
+  }
+  
+  //resolve kills
+  if (kill_c1 == 1)
+  {
+    Populations.group = c1.group.index;
+    Populations.creature = c1.group.findUID(c1.uid);
+	if (Populations.creature == -1) stop("4");
+    Populations.killSelected();
+  }
+  if (kill_c2 == 1)
+  {
+    Populations.group = c2.group.index;
+    Populations.creature = c2.group.findUID(c2.uid);
+	if (Populations.creature == -1) stop("4");
+    Populations.killSelected();
+  }
+}
+
+//function executed on collision with Food population group
+function onFoodCollision()
+{
+  if (ExpParams.pickupNotStagnation == 1) idleSimulationSteps = 0;
+
+  //collect HP Box (by eating it :D)
+  print(Collision.Creature2.name + " [" + Collision.Creature2.group.name + "] picked a HP box");
+  Collision.Creature2.user2.set(4, Collision.Creature2.user2.get(4) + 1);
+  Collision.Creature1.energy_m = Collision.Creature1.energy_m + Collision.Creature1.energy;
+  Collision.Creature2.energy_p = Collision.Creature2.energy_p + Collision.Creature1.energy;
+  
+  //kill HP Box
+  Populations.group = Collision.Creature1.group.index;
+  Populations.creature = Collision.Creature1.group.findUID(Collision.Creature1.uid);
+  Populations.killSelected();
+  
+  writeTeamStatistics();
+}
+
+//function executed on collision with Upgrade population group
+function onUpgradesCollision()
+{
+  if (ExpParams.pickupNotStagnation == 1) idleSimulationSteps = 0;
+
+  //collect Upgrade Box (by eating it :D)
+  print(Collision.Creature2.name + " [" + Collision.Creature2.group.name + "] picked an upgrade box");
+  Collision.Creature2.user2.set(5, Collision.Creature2.user2.get(5) + 1);
+  Collision.Creature1.energy_m = Collision.Creature1.energy_m + Collision.Creature1.energy;
+  Collision.Creature2.user1.set(1, Collision.Creature2.user1.get(1) + Collision.Creature1.energy);
+  
+  //kill Upgrade Box
+  Populations.group = Collision.Creature1.group.index;
+  Populations.creature = Collision.Creature1.group.findUID(Collision.Creature1.uid);
+  Populations.killSelected();
+  
+  writeTeamStatistics();
+}
+
+//function executed on Creature death
+//TODO: implement some statistics for dieing creature
+function onDied()
+{
+  //ignore death of hp boxes and upgrades
+  if (Populations.group < 2)
+   return ;
+
+  print(Creature.name + " was killed");
+  var i = 0;
+  for (i = 0; i < Creature.user3.size; i++)
+  {
+    print(Creature.user3.get(i).get(1) + " dealt " + Creature.user3.get(i).get(2) + " damage");
+  }
+  
+  var f=File.appendDirect("creatures"+battleNumber+".txt","Creature statistics");
+  //f.writeString("Creature name; Team; Kills; Assists; Total damage dealt; Total damage received; HP boxes collected; Upgrade boxes collected; Lifespan");
+  f.writeString(Creature.name + "; " + Population.name + "; " + Creature.user2.get(0) + "; " + Creature.user2.get(1) + "; " + Creature.user2.get(2) + "; " + Creature.user2.get(3) + "; " + Creature.user2.get(4) + "; " + Creature.user2.get(5) + "; " + Creature.lifespan + "\n");
+  f.close();
+  
+  writeTeamStatistics();
+}
+
+// ////////////////////////////
+// ExpParams setters
+
+//reinit populations on change
+function ExpParams_teamCount_set()
+{
+  initPopulations();
+}
+
+function ExpParams_level_set()
+{
+  initLevel();
+}
+~
+
+# ################################
+# Team
+
+prop:
+id:teamCount
+name:Number of teams in Deathmatch
+type:d 1 10 1
+group:Team
+help:If set to 1, the number of teams will be equal to the number of genotypes in gene pool.
+
+prop:
+id:teamSize
+name:Number of creatures per team
+type:d 1 10 5
+group:Team
+
+prop:
+id:teamSpawningAreaSize
+name:Spawning area size for team
+type:f 10 30 20
+group:Team
+
+prop:
+id:creatureStartingEnergy
+name:Starting energy of creature
+type:f 0 1000 300
+group:Team
+help:Base starting energy level for each creature in teams
+
+# ################################
+# Attack parameters
+
+prop:
+id:diceSides
+name:Number of sides of dices
+type:f 1 10 1
+group:Attack rules
+
+prop:
+id:diceCount
+name:Number of dices to roll per attack
+type:f 0 10 5
+group:Attack rules
+
+prop:
+id:creatureDamage
+name:Basic damage multiplied by result of dice roll
+type:f 0 100 10
+group:Attack rules
+
+prop:
+id:friendlyFireDamageMultiplier
+name:Multiplier of energy taken when Creatures of the same team collide
+type:f 0 1 0.0
+group:Attack rules
+help:Set to 0 for no friendly fire, set to 1 if full damage should be dealt when interacting with team member
+
+prop:
+id:attackCooldown
+name:Number of simulation steps between two consecutive attacks of a creature
+type:f 100 10000 1000
+group:Attack rules
+help:Set this to 100 to nearly instantly resolve battles
+
+# ################################
+# Collectibles - HP Boxes & Upgrades
+
+prop:
+id:pickupNotStagnation
+name:Collectible pick-up resets stagnation counter?
+type:d 0 1 1
+help:If set to true picking up an item will restart counting towards stagnation, effects in longer battles.
+
+prop:
+id:hpBoxStartingEnergy
+name:Starting energy of HP Box
+type:f 0 1000 100
+group:Collectibles
+help:Base starting energy level for HP Box
+
+prop:
+id:hpBoxIdleEnergy
+name:Amount of energy lost by HP Box
+type:f 0 10 0.00
+group:Collectibles
+help:How much energy HP Box looses each step (0 - it will not disappear)
+
+prop:
+id:hpBoxProbability
+name:Probablity that new HP Box will spawn
+type:f 0 1 0.0005
+group:Collectibles
+help:Probability of HP Box appearing each step of simulation
+
+prop:
+id:hpBoxTotalCount
+name:Maximum number of HP Boxes on the field
+type:d 0 20 10
+group:Collectibles
+help:The total number of HP Boxes on the field will never exceed this number
+
+prop:
+id:upgradeBoxStartingEnergy
+name:Starting energy of Upgrade Box
+type:f 0 1000 500
+group:Collectibles
+help:Base starting energy level for Upgrade Box
+
+prop:
+id:upgradeBoxIdleEnergy
+name:Amount of energy lost by Upgrade Box
+type:f 0 10 0.00
+group:Collectibles
+help:How much energy Upgrade Box looses each step (0 - it will not disappear)
+
+prop:
+id:upgradeBoxProbability
+name:Probablity that new Upgrade Box will spawn
+type:f 0 1 0.0005
+group:Collectibles
+help:Probability of Upgrade Box appearing each step of simulation
+
+prop:
+id:upgradeBoxTotalCount
+name:Maximum number of Upgrade Boxes on the field
+type:d 0 20 10
+group:Collectibles
+help:The total number of Upgrade Boxes on the field will never exceed this number
+
+prop:
+id:upgradeBoxDecay
+name:How fast upgrade boost fades from creature
+type:f 0 10 0.1
+group:Collectibles
+help:Each step creature damage bonus will be decreased by given amount until it reaches 0
+
+prop:
+id:upgradeMultiplier
+name:Multiplier of bonus damage
+type:f 0 1 0.1
+group:Collectibles
+help:Value of collected bonus multiplied by this variable is taken as bonus damage
+
+# ################################
+# Other
+
+prop:
+id:level
+name:Level
+type:d -1 2 0
+help:Number of a level to battle (-1 is random)
+
+prop:
+id:stagnationInterval
+name:Number of idle simulation steps before Sudden Death
+type:d 0 10000 5000
+help:If no combat occurs during given number of steps, the Sudden Death mode is turned on (0 = Sudden Death all the time)
+
+prop:
+id:stagnationHealthReduce
+name:Sudden Death health reduce
+type:f 0 100 0.1
+help:If Suddent Death is turned on, creatures will lose given amount of HP each simulation step
+
+prop:
+id:debug
+name:Show additional debug messages
+type:d 0 1 0
+
Index: /experiments/frams/deathmatch/gnuplot/teamsEnergy.gp
===================================================================
--- /experiments/frams/deathmatch/gnuplot/teamsEnergy.gp	(revision 231)
+++ /experiments/frams/deathmatch/gnuplot/teamsEnergy.gp	(revision 231)
@@ -0,0 +1,18 @@
+#!/usr/bin/gnuplot -persist
+set xlabel "Time"
+set ylabel "Energy"
+set title "Energy flow"
+set rmargin 5
+set terminal pngcairo size 1024,800
+set output "energyFlow.png"
+plot \
+"teams1.txt" using 1:2 title "Team 0" w l,\
+"teams1.txt" using 1:4 title "Team 1" w l
+#"teams1.txt" using 1:6 title "Team 2" w l,\
+#"teams1.txt" using 1:8 title "Team 3" w l,\
+#"teams1.txt" using 1:10 title "Team 4" w l,\
+#"teams1.txt" using 1:12 title "Team 5" w l,\
+#"teams1.txt" using 1:14 title "Team 6" w l,\
+#"teams1.txt" using 1:16 title "Team 7" w l,\
+#"teams1.txt" using 1:18 title "Team 8" w l,\
+#"teams1.txt" using 1:20 title "Team 9" w l
Index: /experiments/frams/deathmatch/gnuplot/teamsMembers.gp
===================================================================
--- /experiments/frams/deathmatch/gnuplot/teamsMembers.gp	(revision 231)
+++ /experiments/frams/deathmatch/gnuplot/teamsMembers.gp	(revision 231)
@@ -0,0 +1,18 @@
+#!/usr/bin/gnuplot -persist
+set xlabel "Time"
+set ylabel "Team size"
+set title "Number of creatures in each team"
+set rmargin 5
+set terminal pngcairo size 1024,800
+set output "teamSize.png"
+plot \
+"teams1.txt" using 1:3 title "Team 0" w l,\
+"teams1.txt" using 1:5 title "Team 1" w l
+#"teams1.txt" using 1:7 title "Team 2" w l,\
+#"teams1.txt" using 1:9 title "Team 3" w l,\
+#"teams1.txt" using 1:11 title "Team 4" w l,\
+#"teams1.txt" using 1:13 title "Team 5" w l,\
+#"teams1.txt" using 1:15 title "Team 6" w l,\
+#"teams1.txt" using 1:17 title "Team 7" w l,\
+#"teams1.txt" using 1:19 title "Team 8" w l,\
+#"teams1.txt" using 1:21 title "Team 9" w l
