Index: binaries/data/mods/public/gui/common/functions_utility.js
===================================================================
--- binaries/data/mods/public/gui/common/functions_utility.js	(revision 16244)
+++ binaries/data/mods/public/gui/common/functions_utility.js	(working copy)
@@ -240,3 +240,51 @@
 	// Limit the length to 20 characters
 	return sanitizedName.substr(0,20);
 }
+
+function tryAutoComplete(text, autoCompleteList)
+{
+	if (text.length == 0)
+		return text;
+
+	var wordSplit = text.split(/\s/g);
+
+	if (!wordSplit.length)
+		return text;
+
+	var lastWord = wordSplit.pop();
+	if (!lastWord.length)
+		return text;
+
+	for (var wordAutoCompletion of autoCompleteList)
+	{
+		if (wordAutoCompletion.toLowerCase().indexOf(lastWord.toLowerCase()) == 0)
+		{
+			text = wordSplit.join(" ")
+			if (text.length > 0)
+				text += " ";
+
+			text += wordAutoCompletion;
+			break;
+		}
+	}
+	return text;
+}
+
+//listPlayer parameter must be an array and every object with name property
+function autoCompleteNick(guiName, listPlayer)
+{
+	var input = Engine.GetGUIObjectByName(guiName);
+	var text = input.caption;
+	if (!text.length)
+		return;
+
+	var autoCompleteList = [];
+	for (var player of listPlayer)
+		autoCompleteList.push(player.name);
+
+	var bufferPosition = input.buffer_position;
+	var textTillBufferPosition = text.substring(0, bufferPosition);
+	var newText = tryAutoComplete(textTillBufferPosition, autoCompleteList);
+	input.caption = newText + text.substring(bufferPosition);
+	input.buffer_position = bufferPosition + (newText.length - textTillBufferPosition.length);
+}
\ No newline at end of file
Index: binaries/data/mods/public/gui/lobby/lobby.js
===================================================================
--- binaries/data/mods/public/gui/lobby/lobby.js	(revision 16244)
+++ binaries/data/mods/public/gui/lobby/lobby.js	(working copy)
@@ -752,35 +752,6 @@
 	}
 }
 
-function completeNick()
-{
-	var input = Engine.GetGUIObjectByName("chatInput");
-	var text = escapeText(input.caption);
-	if (text.length)
-	{
-		var matched = false;
-		for each (var playerObj in Engine.GetPlayerList())
-		{
-			var player = playerObj.name;
-			var breaks = text.match(/(\s+)/g) || [];
-			text.split(/\s+/g).reduceRight(function (wordsSoFar, word, index)
-			{
-				if (matched)
-					return null;
-				var matchCandidate = word + (breaks[index - 1] || "") + wordsSoFar;
-				if (player.toUpperCase().indexOf(matchCandidate.toUpperCase().trim()) == 0)
-				{
-					input.caption = text.replace(matchCandidate.trim(), player);
-					matched = true;
-				}
-				return matchCandidate;
-			}, "");
-		if (matched)
-			break;
-		}
-	}
-}
-
 function isValidNick(nick)
 {
 	var prohibitedNicks = ["system"];
Index: binaries/data/mods/public/gui/lobby/lobby.xml
===================================================================
--- binaries/data/mods/public/gui/lobby/lobby.xml	(revision 16244)
+++ binaries/data/mods/public/gui/lobby/lobby.xml	(working copy)
@@ -231,7 +231,7 @@
 				<object name="chatText" size="0 0 100% 94%" type="text" style="ChatPanel" font="sans-13"/>
 				<object name="chatInput" size="0 94% 100% 100%" type="input" style="ModernInput" font="sans-13">
 					<action on="Press">submitChatInput();</action>
-					<action on="Tab">completeNick();</action>
+					<action on="Tab">autoCompleteNick("chatInput", Engine.GetPlayerList());</action>
 				</object>
 			</object>
 		</object>
Index: binaries/data/mods/public/gui/session/messages.js
===================================================================
--- binaries/data/mods/public/gui/session/messages.js	(revision 16244)
+++ binaries/data/mods/public/gui/session/messages.js	(working copy)
 			msg.hide = true;
 		recurse = true;
 		break;
+	case "/allies":
+		var player = g_Players[Engine.GetPlayerID()];
+
+		if (player && player.isAlly[sender])
+			msg.context = translate("Ally");
+		else
+			msg.hide = true;
+
+		recurse = true;
+		break;
 	case "/enemy":
 		// Check if we are in a team.
 		if (g_Players[Engine.GetPlayerID()] && g_Players[Engine.GetPlayerID()].team != -1)
Index: source/gui/CInput.cpp
===================================================================
--- source/gui/CInput.cpp	(revision 16244)
+++ source/gui/CInput.cpp	(working copy)
@@ -42,6 +42,8 @@
 
 extern int g_yres;
 
+#define UpdateBufferPositionSetting int* bufferPos = (int*)m_Settings["buffer_position"].m_pSetting; *bufferPos = m_iBufferPos;
+
 //-------------------------------------------------------------------
 //  Constructor / Destructor
 //-------------------------------------------------------------------
@@ -52,6 +54,7 @@
 {
 	AddSetting(GUIST_float,					"buffer_zone");
 	AddSetting(GUIST_CStrW,					"caption");
+	AddSetting(GUIST_int,					"buffer_position");
 	AddSetting(GUIST_int,					"cell_id");
 	AddSetting(GUIST_CStrW,					"font");
 	AddSetting(GUIST_CStrW,					"mask_char");
@@ -84,6 +87,7 @@
 	CStrW *pCaption = (CStrW*)m_Settings["caption"].m_pSetting;
 	pCaption->erase(m_iInsertPos, m_iComposedLength);
 	m_iBufferPos = m_iInsertPos;
+	UpdateBufferPositionSetting;
 	m_iComposedLength = 0;
 	m_iComposedPos = 0;
 }
@@ -126,6 +130,7 @@
 		UpdateText(m_iBufferPos, m_iBufferPos, m_iBufferPos+1);
 
 		m_iBufferPos += text.length();
+		UpdateBufferPositionSetting;
 		m_iBufferPos_Tail = -1;
 
 		UpdateAutoScroll();
@@ -171,6 +176,7 @@
 			m_iBufferPos_Tail = -1;
 		}
 
+		UpdateBufferPositionSetting;
 		UpdateText(m_iBufferPos, m_iBufferPos, m_iBufferPos+1);
 
 		UpdateAutoScroll();
@@ -551,6 +557,7 @@
 				break;
 		}
 
+		UpdateBufferPositionSetting;
 		return IN_HANDLED;
 	}
 
@@ -585,6 +592,7 @@
 			UpdateText(m_iBufferPos, m_iBufferPos, m_iBufferPos+1);
 
 			m_iBufferPos += (int)wcslen(text);
+			UpdateBufferPositionSetting;
 
 			sys_clipboard_free(text);
 		}
@@ -660,6 +668,7 @@
 				}
 			}
 
+			UpdateBufferPositionSetting;
 			DeleteCurSelection();
 		}
 		return IN_HANDLED;
@@ -692,6 +701,7 @@
 
 				m_iBufferPos++;
 			}
+			UpdateBufferPositionSetting;
 			DeleteCurSelection();
 		}
 		return IN_HANDLED;
@@ -748,6 +758,7 @@
 			m_iBufferPos_Tail = -1;
 		}
 
+		UpdateBufferPositionSetting;
 		UpdateAutoScroll();
 
 		return IN_HANDLED;
@@ -796,6 +807,7 @@
 			m_iBufferPos_Tail = -1;
 		}
 
+		UpdateBufferPositionSetting;
 		UpdateAutoScroll();
 
 		return IN_HANDLED;
@@ -841,6 +853,11 @@
 			GetScrollBar(0).SetScrollBarStyle(scrollbar_style);
 		}
 
+		if (Message.value == CStr("buffer_position"))
+		{
+			GUI<int>::GetSetting(this, Message.value, m_iBufferPos);
+		}
+
 		if (Message.value == CStr("size") || 
 			Message.value == CStr("z") ||
 			Message.value == CStr("font") || 
@@ -1098,6 +1115,7 @@
 	default:
 		break;
 	}
+	UpdateBufferPositionSetting;
 }
 
 void CInput::UpdateCachedSize()
@@ -1552,6 +1570,7 @@
 	// Ensure positions are valid after caption changes
 	m_iBufferPos = std::min(m_iBufferPos, (int)caption.size());
 	m_iBufferPos_Tail = std::min(m_iBufferPos_Tail, (int)caption.size());
+	UpdateBufferPositionSetting;
 
 	if (font_name.empty())
 	{
@@ -2053,6 +2072,7 @@
 	// Remove selection
 	m_iBufferPos_Tail = -1;
 	m_iBufferPos = virtualFrom;
+	UpdateBufferPositionSetting;
 }
 
 bool CInput::SelectingText() const