diff --git a/proj/sxgame/vs2013/sxgame.vcxproj b/proj/sxgame/vs2013/sxgame.vcxproj
index 2e16dab93c6757bd80479bc292c96d3455221b3c..7f3e5ddc166ec66e758f117e73184bdad7a61ab0 100644
--- a/proj/sxgame/vs2013/sxgame.vcxproj
+++ b/proj/sxgame/vs2013/sxgame.vcxproj
@@ -195,6 +195,7 @@
     <ClCompile Include="..\..\..\source\game\EditorObject.cpp" />
     <ClCompile Include="..\..\..\source\game\EditorOutputsTab.cpp" />
     <ClCompile Include="..\..\..\source\game\EntityFactory.cpp" />
+    <ClCompile Include="..\..\..\source\game\EntityList.cpp" />
     <ClCompile Include="..\..\..\source\game\EntityManager.cpp" />
     <ClCompile Include="..\..\..\source\game\EnvSkybox.cpp" />
     <ClCompile Include="..\..\..\source\game\FuncRotating.cpp" />
@@ -267,6 +268,7 @@
     <ClInclude Include="..\..\..\source\game\EditorExtension.h" />
     <ClInclude Include="..\..\..\source\game\EditorObject.h" />
     <ClInclude Include="..\..\..\source\game\EditorOutputsTab.h" />
+    <ClInclude Include="..\..\..\source\game\EntityList.h" />
     <ClInclude Include="..\..\..\source\game\EntityPointer.h" />
     <ClInclude Include="..\..\..\source\game\EnvSkybox.h" />
     <ClInclude Include="..\..\..\source\game\FuncRotating.h" />
diff --git a/proj/sxgame/vs2013/sxgame.vcxproj.filters b/proj/sxgame/vs2013/sxgame.vcxproj.filters
index 57e82a2c547b2b30e821154fe9649143bff55863..7371a9da2d308080086def4dac1f06d82cdde174 100644
--- a/proj/sxgame/vs2013/sxgame.vcxproj.filters
+++ b/proj/sxgame/vs2013/sxgame.vcxproj.filters
@@ -312,6 +312,9 @@
     <ClCompile Include="..\..\..\source\common\guid.cpp">
       <Filter>Source Files</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\..\source\game\EntityList.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="..\..\..\source\game\sxgame.h">
@@ -542,6 +545,9 @@
     <ClInclude Include="..\..\..\source\game\EntityPointer.h">
       <Filter>Header Files</Filter>
     </ClInclude>
+    <ClInclude Include="..\..\..\source\game\EntityList.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ResourceCompile Include="..\..\..\source\game\sxgame.rc">
diff --git a/source/game/BaseEntity.cpp b/source/game/BaseEntity.cpp
index d9156c7f4636497057e23d060046f4ed7cb8065b..18df921c0603dad288073ba305b3ccf39be22221 100644
--- a/source/game/BaseEntity.cpp
+++ b/source/game/BaseEntity.cpp
@@ -18,7 +18,7 @@ See the license in LICENSE
 
 BEGIN_PROPTABLE_NOBASE(CBaseEntity)
 	//! Имя объекта
-	DEFINE_FIELD_STRING(m_szName, 0, "name", "Name", EDITOR_TEXTFIELD)
+	DEFINE_FIELD_STRINGFN(m_szName, 0, "name", "Name", setName, EDITOR_TEXTFIELD)
 	//! Позиция в мире
 	DEFINE_FIELD_VECTORFN(m_vPosition, 0, "origin", "Origin", setPos, EDITOR_TEXTFIELD)
 	//! Ориентация в мире, углы эйлера или кватернион
@@ -57,7 +57,7 @@ void CBaseEntity::setDefaults()
 				}
 				else if(pt->pData[i].type == PDF_ENTITY)
 				{
-					(this->*((CEntityPointer<CBaseEntity> ThisClass::*)pt->pData[i].pField)).init(m_pMgr, this);
+					(this->*((CEntityPointer<CBaseEntity> ThisClass::*)pt->pData[i].pField)).init(this);
 				}
 			}
 		}
@@ -126,12 +126,12 @@ void CBaseEntity::setClassName(const char * name)
 	m_szClassName = name;
 }
 
-const char * CBaseEntity::getName()
+const char* CBaseEntity::getName()
 {
 	return(m_szName);
 }
 
-const char * CBaseEntity::getClassName()
+const char* CBaseEntity::getClassName()
 {
 	return(m_szClassName);
 }
@@ -428,14 +428,13 @@ bool CBaseEntity::setKV(const char * name, const char * value)
 			iConns = parse_str(str, parts, iConns, ',');
 
 			output_t *pOutput = &(this->*((output_t ThisClass::*)field->pField));
-			mem_delete_a(pOutput->pOutputs);
 			{
 				char *pTmpData = (char*)pOutput->pData;
 				mem_delete_a(pTmpData);
 			}
 			pOutput->pData = str;
-			pOutput->pOutputs = new named_output_t[iConns];
-			pOutput->bDirty = true;
+			pOutput->aOutputs.clearFast();
+			pOutput->aOutputs.reserve(iConns);
 
 			int curr = 0;
 			for(int i = 0; i < iConns; ++i)
@@ -470,11 +469,13 @@ bool CBaseEntity::setKV(const char * name, const char * value)
 					printf(COLOR_LRED "Unable to parse output delay '%s' ent %s\n" COLOR_RESET, name, m_szName);
 					continue;
 				}
-				pOutput->pOutputs[curr].fDelay = fDelayFrom;
-				pOutput->pOutputs[curr].fDelayTo = fDelayTo;
+				named_output_t &out = pOutput->aOutputs[curr];
+
+				out.fDelay = fDelayFrom;
+				out.fDelayTo = fDelayTo;
 				if(fDelayFrom < fDelayTo)
 				{
-					pOutput->pOutputs[curr].useRandomDelay = true;
+					out.useRandomDelay = true;
 				}
 				if(fDelayFrom > fDelayTo)
 				{
@@ -482,14 +483,15 @@ bool CBaseEntity::setKV(const char * name, const char * value)
 					continue;
 				}
 
-				pOutput->pOutputs[curr].szTargetName = fields[0];
-				pOutput->pOutputs[curr].szTargetInput = fields[1];
-				pOutput->pOutputs[curr].szTargetData = param;
-				pOutput->pOutputs[curr].iFireLimit = iFireLimit;
+				out.szTargetName = fields[0];
+				out.szTargetInput = fields[1];
+				out.szTargetData = param;
+				out.iFireLimit = iFireLimit;
 				
+				out.init(this);
+
 				++curr;
 			}
-			pOutput->iOutCount = curr;
 		}
 		// target_name:input:delay:parameter\n<repeat>
 		return(false);
@@ -543,37 +545,39 @@ bool CBaseEntity::getKV(const char * name, char * out, int bufsize)
 			output_t *pOutput = &(this->*((output_t ThisClass::*)field->pField));
 			int iWritten = 0;
 			char * szOutBuf = out;
-			if(pOutput->iOutCount == 0)
+			*out = 0;
+			if(pOutput->aOutputs.size() == 0)
 			{
 				*out = 0;
 			}
-			for(int i = 0; i < pOutput->iOutCount; ++i)
+			for(UINT i = 0, l = pOutput->aOutputs.size(); i < l; ++i)
 			{
+				named_output_t &out = pOutput->aOutputs[i];
 				if(i > 0)
 				{
 					*szOutBuf = ',';
 					++szOutBuf;
 				}
 				int c;
-				if(pOutput->pOutputs[i].useRandomDelay)
+				if(out.useRandomDelay)
 				{
 					c = _snprintf(szOutBuf, bufsize - iWritten, "%s:%s:%f-%f:%s:%d", 
-						pOutput->pOutputs[i].szTargetName, 
-						pOutput->pOutputs[i].szTargetInput, 
-						pOutput->pOutputs[i].fDelay,
-						pOutput->pOutputs[i].fDelayTo,
-						pOutput->pOutputs[i].szTargetData ? pOutput->pOutputs[i].szTargetData : ENT_OUTPUT_PARAM_NONE,
-						pOutput->pOutputs[i].iFireLimit
+						out.szTargetName, 
+						out.szTargetInput, 
+						out.fDelay,
+						out.fDelayTo,
+						out.szTargetData ? out.szTargetData : ENT_OUTPUT_PARAM_NONE,
+						out.iFireLimit
 						);
 				}
 				else
 				{
 					c = _snprintf(szOutBuf, bufsize - iWritten, "%s:%s:%f:%s:%d", 
-						pOutput->pOutputs[i].szTargetName, 
-						pOutput->pOutputs[i].szTargetInput, 
-						pOutput->pOutputs[i].fDelay, 
-						pOutput->pOutputs[i].szTargetData ? pOutput->pOutputs[i].szTargetData : ENT_OUTPUT_PARAM_NONE,
-						pOutput->pOutputs[i].iFireLimit
+						out.szTargetName, 
+						out.szTargetInput, 
+						out.fDelay, 
+						out.szTargetData ? out.szTargetData : ENT_OUTPUT_PARAM_NONE,
+						out.iFireLimit
 						);
 				}
 				iWritten += c + 1;
@@ -670,7 +674,6 @@ CBaseEntity* CBaseEntity::getParent()
 
 void CBaseEntity::onPostLoad()
 {
-	updateOutputs();
 	updateFlags();
 }
 
@@ -732,126 +735,6 @@ int CBaseEntity::countEntByName(const char *szName)
 	return(m_pMgr->countEntityByName(szName));
 }
 
-void CBaseEntity::updateOutputs()
-{
-	proptable_t * pt = getPropTable();
-	while(pt)
-	{
-		for(int i = 0; i < pt->numFields; ++i)
-		{
-			if(pt->pData[i].type == PDF_OUTPUT)
-			{
-				output_t *pOutput = &(this->*((output_t ThisClass::*)pt->pData[i].pField));
-
-				for(int j = 0, jl = pOutput->iOutCount; j < jl; ++j)
-				{
-					named_output_t *pOut = &pOutput->pOutputs[j];
-					mem_delete_a(pOut->pOutputs);
-
-					pOut->iOutCount = countEntByName(pOut->szTargetName);
-					if(!pOut->iOutCount)
-					{
-						printf(COLOR_CYAN "Broken output target '%s' source '%s'.'%s'\n" COLOR_RESET, pOut->szTargetName, getClassName(), m_szName);
-						continue;
-					}
-					pOut->pOutputs = new input_t[pOut->iOutCount];
-					memset(pOut->pOutputs, 0, sizeof(input_t) * pOut->iOutCount);
-
-
-					CBaseEntity * pEnt = NULL;
-					int c = 0;
-					while((pEnt = getEntByName(pOut->szTargetName, pEnt)))
-					{
-						propdata_t * pField = pEnt->getField(pOut->szTargetInput);
-						if(!pField || !(pField->flags & PDFF_INPUT))
-						{
-							printf(COLOR_CYAN "Class '%s' has no input '%s', obj '%s'\n" COLOR_RESET, pEnt->getClassName(), pOut->szTargetInput, pOut->szTargetName);
-							--pOut->iOutCount;
-							continue;
-						}
-
-						pOut->pOutputs[c].fnInput = pField->fnInput;
-						pOut->pOutputs[c].pTarget = pEnt;
-						pOut->pOutputs[c].data.type = pField->type;
-						if((pOut->pOutputs[c].useOverrideData = pOut->szTargetData != NULL))
-						{
-							float3_t f3;
-							float4_t f4;
-							SMQuaternion q;
-							int d;
-							float f;
-							const char * value = pOut->szTargetData;
-							bool bParsed = false;
-							switch(pField->type)
-							{
-							case PDF_NONE:
-								bParsed = true;
-								break;
-							case PDF_INT:
-								if(1 == sscanf(value, "%d", &d))
-								{
-									pOut->pOutputs[c].data.parameter.i = d;
-									bParsed = true;
-								}
-								break;
-							case PDF_FLOAT:
-								if(1 == sscanf(value, "%f", &f))
-								{
-									pOut->pOutputs[c].data.parameter.f = f;
-									bParsed = true;
-								}
-								break;
-							case PDF_VECTOR:
-								if(3 == sscanf(value, "%f %f %f", &f3.x, &f3.y, &f3.z))
-								{
-									pOut->pOutputs[c].data.v3Parameter = f3;
-									bParsed = true;
-								}
-								break;
-							case PDF_VECTOR4:
-								{
-									int iPrm = sscanf(value, "%f %f %f %f", &f4.x, &f4.y, &f4.z, &f4.w);
-									if(iPrm > 2)
-									{
-										if(iPrm == 3)
-										{
-											f4.w = 1.0f;
-										}
-										pOut->pOutputs[c].data.v4Parameter = f4;
-										bParsed = true;
-									}
-								}
-								break;
-							case PDF_BOOL:
-								if(1 == sscanf(value, "%d", &d))
-								{
-									pOut->pOutputs[c].data.parameter.b = d != 0;
-									bParsed = true;
-								}
-								break;
-							case PDF_STRING:
-								_setStrVal(&pOut->pOutputs[c].data.parameter.str, value);
-								bParsed = true;
-								break;
-							}
-
-							if(!bParsed)
-							{
-								printf(COLOR_CYAN "Cannot parse input parameter '%s', class '%s', input '%s', obj '%s'\n" COLOR_RESET, value, pEnt->getClassName(), pOut->szTargetInput, pOut->szTargetName);
-								--pOut->iOutCount;
-								continue;
-							}
-						}
-
-						++c;
-					}
-				}
-			}
-		}
-		pt = pt->pBaseProptable;
-	}
-}
-
 void CBaseEntity::dispatchDamage(CTakeDamageInfo &takeDamageInfo)
 {
 	float fHealth = takeDamageInfo.m_fDamage * 0.1f;
@@ -1028,6 +911,15 @@ void CBaseEntity::notifyPointers()
 	ScopedSpinLock lock(m_slPointers);
 	for(UINT i = 0, l = m_aPointers.size(); i < l; ++i)
 	{
-		m_aPointers[i]->onTargetRemoved();
+		m_aPointers[i]->onTargetRemoved(this);
+	}
+}
+
+void CBaseEntity::setName(const char *szName)
+{
+	if(fstrcmp(m_szName, szName))
+	{
+		m_pMgr->onEntityNameChanged(this, m_szName, szName);
+		_setStrVal(&m_szName, szName);
 	}
 }
diff --git a/source/game/BaseEntity.h b/source/game/BaseEntity.h
index 59106d18b8ffab9cde2bf9944d696180553f7b68..68a3595589f16edabde539a34e456e41374336f0 100644
--- a/source/game/BaseEntity.h
+++ b/source/game/BaseEntity.h
@@ -48,7 +48,7 @@ class SXGAME_EXPORT CBaseEntity
 
 	template<typename T>
 	friend class CEntityPointer;
-
+	friend class CEntityList;
 public:
 	//! Конструктор
 	CBaseEntity();
@@ -158,6 +158,8 @@ public:
 		m_isSeparateMovement = set;
 	}
 
+	void setName(const char *szName);
+
 private:
 	void setClassName(const char *name);
 	void setDefaults();
@@ -203,22 +205,18 @@ private:
 	//! Вращение смещения (для иерархической структуры)
 	SMQuaternion m_qOffsetOrient;
 
+	//! Имя объекта
+	const char *m_szName = NULL;
+
+	//! Флаги
+	UINT m_iFlags = 0;
 protected:
 	virtual void _cleanup();
 	virtual void _initEditorBoxes();
 	virtual void _releaseEditorBoxes();
 
 	CEntityManager *m_pMgr = NULL;
-
-	//! Идентификатор в системе
-	ID m_iId = 0;
-
-	//! Флаги
-	UINT m_iFlags = 0;
-
-	//! Имя объекта
-	const char *m_szName = NULL;
-
+	
 	//! Родитель
 	// CBaseEntity *m_pParent = NULL;
 	CEntityPointer<CBaseEntity> m_pParent;
@@ -236,16 +234,6 @@ protected:
 	//! Получает вращение для кости
 	virtual SMQuaternion getAttachmentRot(int id);
 
-//	/*! Устанавливает значение строкового свойства
-//	\note только для внутреннего использования
-//	*/
-//	void _setStrVal(const char ** to, const char * value);
-
-	/*! Обновляет выходы
-	\note только для внутреннего использования
-	*/
-	void updateOutputs();
-	
 	//! здоровье [0,+inf]
 	float m_fHealth = 100.0f;
 
diff --git a/source/game/EditorOutputsTab.cpp b/source/game/EditorOutputsTab.cpp
index 0dbf3aac286801dac50c9db9cf0fdcd6a9aaa619..a5efe61d073bf5208d7280aba8fab7315aba03e7 100644
--- a/source/game/EditorOutputsTab.cpp
+++ b/source/game/EditorOutputsTab.cpp
@@ -427,9 +427,9 @@ void CEditorOutputsTab::initSelection()
 						propdata_t *pField = pEnt->getField(pPropData->szKey);
 						output_t *pOutput = &(pEnt->*((output_t CBaseEntity::*)pField->pField));
 						
-						for(int i = 0; i < pOutput->iOutCount; ++i)
+						for(int i = 0, l = pOutput->aOutputs.size(); i < l; ++i)
 						{
-							const named_output_t &namedOutput = pOutput->pOutputs[i];
+							const named_output_t &namedOutput = pOutput->aOutputs[i];
 
 							int idx = m_pCurrentCommand->m_aRows.indexOf(namedOutput, [&](const Row &row, const named_output_t &out){
 								return(
diff --git a/source/game/EntityList.cpp b/source/game/EntityList.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..4c5f9ad0476f88c5373aacaef261a593f17bfd28
--- /dev/null
+++ b/source/game/EntityList.cpp
@@ -0,0 +1,146 @@
+#include "EntityList.h"
+#include "BaseEntity.h"
+
+CEntityList::~CEntityList()
+{
+	clearList();
+
+	unregisterWait();
+}
+
+//! Установка имени или GUID связанного объекта
+void CEntityList::setEntityName(const char *szName)
+{
+	clearList();
+
+	unregisterWait();
+	m_sName = szName;
+
+	if(szName[0])
+	{
+		registerWait();
+
+		CBaseEntity *pEnt = NULL;
+		while((pEnt = m_pThisEntity->getEntByName(szName, pEnt)))
+		{
+			addEntity(pEnt);
+		}
+	}
+}
+
+//! Установка
+void CEntityList::addEntity(CBaseEntity *pEnt)
+{
+	assert(pEnt);
+
+	pEnt->registerPointer(this);
+
+	int idx = m_aEntities.indexOf(pEnt);
+	assert(idx < 0);
+	if(idx < 0)
+	{
+		m_aEntities.push_back(pEnt);
+		onLinkEstablished(pEnt);
+	}
+}
+
+void CEntityList::removeEntity(CBaseEntity *pEnt)
+{
+	assert(pEnt);
+	int idx = m_aEntities.indexOf(pEnt);
+	assert(idx >= 0);
+	if(idx >= 0)
+	{
+		onLinkBroken(pEnt);
+		m_aEntities[idx] = m_aEntities[m_aEntities.size() - 1];
+		m_aEntities.erase(m_aEntities.size() - 1);
+	}
+}
+
+//! Получение указателя на объект
+CBaseEntity* CEntityList::getEntity(int idx)
+{
+	return((int)m_aEntities.size() >= idx ? m_aEntities[idx] : NULL);
+}
+
+UINT CEntityList::getEntityCount()
+{
+	return(m_aEntities.size());
+}
+
+const char* CEntityList::getEntityName()
+{
+	return(m_sName.c_str());
+}
+
+CBaseEntity* CEntityList::operator[](int idx)
+{
+	return(getEntity(idx));
+}
+
+CEntityList& CEntityList::operator+=(CBaseEntity *pEnt)
+{
+	addEntity(pEnt);
+	return(*this);
+}
+
+CEntityList& CEntityList::operator-=(CBaseEntity *pEnt)
+{
+	removeEntity(pEnt);
+	return(*this);
+}
+
+void CEntityList::onLinkBroken(CBaseEntity *pOldEnt)
+{
+	if(m_pfnOnLinkBroken)
+	{
+		(m_pThisEntity->*m_pfnOnLinkBroken)(pOldEnt);
+	}
+	if(m_pOnLinkBroken)
+	{
+		m_pOnLinkBroken->onEvent(pOldEnt);
+	}
+}
+void CEntityList::onLinkEstablished(CBaseEntity *pNewEnt)
+{
+	if(m_pfnOnLinkEstablished)
+	{
+		(m_pThisEntity->*m_pfnOnLinkEstablished)(pNewEnt);
+	}
+	if(m_pOnLinkEstablished)
+	{
+		m_pOnLinkEstablished->onEvent(pNewEnt);
+	}
+}
+void CEntityList::onTargetRemoved(CBaseEntity *pEnt)
+{
+	removeEntity(pEnt);
+}
+
+void CEntityList::registerWait()
+{
+	if(!m_isWaiting)
+	{
+		m_pThisEntity->m_pMgr->registerWaitForName(m_sName.c_str(), this);
+		m_isWaiting = true;
+	}
+}
+void CEntityList::unregisterWait()
+{
+	if(m_isWaiting)
+	{
+		m_pThisEntity->m_pMgr->unregisterWaitForName(m_sName.c_str(), this);
+		m_isWaiting = false;
+	}
+}
+
+void CEntityList::clearList()
+{
+	for(UINT i = 0, l = m_aEntities.size(); i < l; ++i)
+	{
+		m_aEntities[i]->unregisterPointer(this);
+	}
+	m_aEntities.clearFast();
+}
+
+
diff --git a/source/game/EntityList.h b/source/game/EntityList.h
new file mode 100644
index 0000000000000000000000000000000000000000..5e03cb9ad4ded82c260d7bca0afaa4945d4cedba
--- /dev/null
+++ b/source/game/EntityList.h
@@ -0,0 +1,95 @@
+#ifndef __ENTITY_LIST_H
+#define __ENTITY_LIST_H
+
+#include <gdefines.h>
+#include "EntityPointer.h"
+#include <common/string.h>
+
+class CBaseEntity;
+class CEntityManager;
+class CEntityList: public IEntityPointer
+{
+	DECLARE_CLASS_NOBASE(CEntityList);
+	friend class CBaseEntity;
+	friend struct named_output_t;
+public:
+	~CEntityList();
+
+	//! Установка имени или GUID связанного объекта
+	void setEntityName(const char *szName);
+	
+	//! Установка
+	void addEntity(CBaseEntity *pEnt);
+
+	void removeEntity(CBaseEntity *pEnt);
+
+	//! Получение указателя на объект
+	CBaseEntity* getEntity(int idx);
+
+	UINT getEntityCount();
+
+	const char* getEntityName();
+	
+	CBaseEntity* operator[](int idx);
+
+	CEntityList& operator+=(CBaseEntity *pEnt);
+
+	CEntityList& operator-=(CBaseEntity *pEnt);
+
+	//! Вызывается при разрушении связи
+	template<typename F>
+	void setLinkBrokenListener(void(F::*func)(CBaseEntity*))
+	{
+		static_assert(std::is_base_of<CBaseEntity, F>::value, "F must be subclass of CBaseEntity");
+
+		m_pfnOnLinkBroken = func;
+	}
+
+	//! Вызывается при установке связи
+	template<typename F>
+	void setLinkEstablishedListener(void(F::*func)(CBaseEntity*))
+	{
+		static_assert(std::is_base_of<CBaseEntity, F>::value, "F must be subclass of CBaseEntity");
+
+		m_pfnOnLinkEstablished = func;
+	}
+
+	void setLinkBrokenListener(IEntityPointerEvent *pFunc)
+	{
+		m_pOnLinkBroken = pFunc;
+	}
+	void setLinkEstablishedListener(IEntityPointerEvent *pFunc)
+	{
+		m_pOnLinkEstablished = pFunc;
+	}
+	
+private:
+	String m_sName;
+	Array<CBaseEntity*> m_aEntities;
+	CBaseEntity *m_pThisEntity = NULL;
+
+	void(CBaseEntity::*m_pfnOnLinkEstablished)(CBaseEntity*) = NULL;
+	void(CBaseEntity::*m_pfnOnLinkBroken)(CBaseEntity*) = NULL;
+	IEntityPointerEvent *m_pOnLinkEstablished = NULL;
+	IEntityPointerEvent *m_pOnLinkBroken = NULL;
+
+	void onLinkBroken(CBaseEntity *pOldEnt);
+	void onLinkEstablished(CBaseEntity *pNewEnt);
+	void onTargetRemoved(CBaseEntity *pEnt) override;
+
+	bool m_isWaiting = false;
+	void registerWait();
+	void unregisterWait();
+	void onWaitDone(CBaseEntity *pEnt) override
+	{
+	}
+
+	void clearList();
+
+	void init(CBaseEntity *pThisEntity)
+	{
+		m_pThisEntity = pThisEntity;
+	}
+};
+
+#endif
diff --git a/source/game/EntityManager.cpp b/source/game/EntityManager.cpp
index cd1da1ffa179a6fc7f440c0363d724de1ab027bf..1f808ecc7eef7fee82a41b1741286f61a7a1dbe8 100644
--- a/source/game/EntityManager.cpp
+++ b/source/game/EntityManager.cpp
@@ -72,30 +72,31 @@ void CEntityManager::update(int thread)
 			inputData.pActivator = to->data.pActivator;
 			inputData.pInflictor = to->data.pInflictor;
 
-			for(int j = 0; j < to->pOutput->iOutCount; ++j)
+			for(UINT j = 0, jl = to->pOutput->aOutputs.size(); j < jl; ++j)
 			{
-				if(!to->pOutput->pOutputs[j].pTarget)
+				input_t &out = to->pOutput->aOutputs[j];
+				if(!out.pTarget)
 				{
 					continue;
 				}
 
-				inputData.type = to->pOutput->pOutputs[j].data.type;
+				inputData.type = out.data.type;
 
 				if(inputData.type == PDF_STRING)
 				{
 					inputData.parameter.str = NULL;
 				}
 
-				if(to->pOutput->pOutputs[j].useOverrideData)
+				if(out.useOverrideData)
 				{
-					inputData.setParameter(to->pOutput->pOutputs[j].data);
+					inputData.setParameter(out.data);
 				}
 				else
 				{
 					inputData.setParameter(to->data);
 				}
 
-				(to->pOutput->pOutputs[j].pTarget->*(to->pOutput->pOutputs[j].fnInput))(&inputData);
+				(out.pTarget->*(out.fnInput))(&inputData);
 
 				if(inputData.type == PDF_STRING && inputData.parameter.str != GetEmptyString())
 				{
@@ -243,6 +244,10 @@ const XGUID* CEntityManager::reg(CBaseEntity *pEnt, const XGUID *pGUID)
 	if(pGUID)
 	{
 		notifyWaitForGUID(*pGUID, pEnt);
+
+		char tmp[64];
+		XGUIDToSting(*pGUID, tmp, sizeof(tmp));
+		onEntityNameChanged(pEnt, "", tmp);
 	}
 
 	return(pNewGUID);
@@ -266,16 +271,18 @@ void CEntityManager::unreg(CBaseEntity *pEnt)
 		to = &m_vOutputTimeout[i];
 		if(to->status == TS_WAIT)
 		{
-			int iRemoved = 0;
-			for(int j = 0; j < to->pOutput->iOutCount; ++j)
+			UINT iRemoved = 0;
+			for(UINT j = 0, jl = to->pOutput->aOutputs.size(); j < jl; ++j)
 			{
-				if(to->pOutput->pOutputs[j].pTarget == pEnt)
+				input_t &out = to->pOutput->aOutputs[j];
+
+				if(out.pTarget == pEnt)
 				{
-					to->pOutput->pOutputs[j].pTarget = NULL;
+					out.pTarget = NULL;
 					++iRemoved;
 				}
 			}
-			if(iRemoved == to->pOutput->iOutCount)
+			if(iRemoved == to->pOutput->aOutputs.size())
 			{
 				to->status = TS_DONE;
 			}
@@ -1316,3 +1323,47 @@ void CEntityManager::notifyWaitForGUID(const XGUID &guid, CBaseEntity *pEnt)
 		m_maWaitingPointers.erase(guid);
 	}
 }
+
+void CEntityManager::registerWaitForName(const char *szName, CEntityList *pList)
+{
+	ScopedSpinLock lock(m_slWaitingLists);
+
+	Array<CEntityList*> &list = m_maWaitingLists[szName];
+
+	assert(list.indexOf(pList) < 0);
+
+	list.push_back(pList);
+}
+void CEntityManager::unregisterWaitForName(const char *szName, CEntityList *pList)
+{
+	ScopedSpinLock lock(m_slWaitingLists);
+
+	Array<CEntityList*> &list = m_maWaitingLists[szName];
+
+	int idx = list.indexOf(pList);
+	assert(idx >= 0);
+	list[idx] = list[list.size() - 1];
+	list.erase(list.size() - 1);
+}
+
+void CEntityManager::onEntityNameChanged(CBaseEntity *pEnt, const char *szOldName, const char *szNewName)
+{
+	const Map<String, Array<CEntityList*>>::Node *pNode;
+	if(szOldName[0] && m_maWaitingLists.KeyExists(szOldName, &pNode))
+	{
+		Array<CEntityList*> &list = *pNode->Val;
+		for(UINT i = 0, l = list.size(); i < l; ++i)
+		{
+			list[i]->removeEntity(pEnt);
+		}
+	}
+
+	if(szNewName[0] && m_maWaitingLists.KeyExists(szNewName, &pNode))
+	{
+		Array<CEntityList*> &list = *pNode->Val;
+		for(UINT i = 0, l = list.size(); i < l; ++i)
+		{
+			list[i]->addEntity(pEnt);
+		}
+	}
+}
diff --git a/source/game/EntityManager.h b/source/game/EntityManager.h
index 373d7a7698693421532aeb72a32f27c63614208a..1444c9dede3c96560b45583a38bbb7003234b437 100644
--- a/source/game/EntityManager.h
+++ b/source/game/EntityManager.h
@@ -91,6 +91,7 @@ class CEntityManager
 	friend class CEntityFactoryMap;
 	template<typename T>
 	friend class CEntityPointer;
+	friend class CEntityList;
 public:
 	CEntityManager();
 	~CEntityManager();
@@ -187,6 +188,12 @@ private:
 	void notifyWaitForGUID(const XGUID &guid, CBaseEntity *pEnt);
 
 	bool m_isOldImported = false;
+
+	SpinLock m_slWaitingLists;
+	Map<String, Array<CEntityList*>> m_maWaitingLists;
+	void registerWaitForName(const char *szName, CEntityList *pList);
+	void unregisterWaitForName(const char *szName, CEntityList *pList);
+	void onEntityNameChanged(CBaseEntity *pEnt, const char *szOldName, const char *szNewName);
 };
 
 #endif
diff --git a/source/game/EntityPointer.h b/source/game/EntityPointer.h
index da7a94bbce3658bb71ad7a6751405b709c88773f..5a4a45efbda53ef29aaff41be5db1be3a67b8b66 100644
--- a/source/game/EntityPointer.h
+++ b/source/game/EntityPointer.h
@@ -9,10 +9,16 @@ class IEntityPointer
 	friend class CEntityManager;
 
 private:
-	virtual void onTargetRemoved() = 0;
+	virtual void onTargetRemoved(CBaseEntity *pEnt) = 0;
 	virtual void onWaitDone(CBaseEntity *pEnt) = 0;
 };
 
+class IEntityPointerEvent
+{
+public:
+	virtual void onEvent(CBaseEntity *pEnt) = 0;
+};
+
 class CBaseEntity;
 class CEntityManager;
 template<typename T>
@@ -47,7 +53,7 @@ public:
 			}
 		}
 
-		CBaseEntity *pEnt = m_pMgr->findEntityByName(szName);
+		CBaseEntity *pEnt = ((CBaseEntity*)m_pThisEntity)->getEntByName(szName, NULL);
 
 		if(pEnt)
 		{
@@ -68,7 +74,7 @@ public:
 	//! Установка GUID
 	void setEntityGUID(const XGUID &guid)
 	{
-		CBaseEntity *pEnt = m_pMgr->getByGUID(guid);
+		CBaseEntity *pEnt = ((CBaseEntity*)m_pThisEntity)->m_pMgr->getByGUID(guid);
 		
 		if(pEnt)
 		{
@@ -128,7 +134,7 @@ public:
 	{
 		if(m_pEntity && m_pEntity->getName()[0])
 		{
-			if(m_pMgr->countEntityByName(m_pEntity->getName()) == 1)
+			if(((CBaseEntity*)m_pThisEntity)->m_pMgr->countEntityByName(m_pEntity->getName()) == 1)
 			{
 				strncpy(szOutput, m_pEntity->getName(), iBufSize);
 				szOutput[iBufSize - 1] = 0;
@@ -187,20 +193,29 @@ public:
 
 		m_pfnOnLinkEstablished = (void(CBaseEntity::*)(T*))func;
 	}
+
+	void setLinkBrokenListener(IEntityPointerEvent *pFunc)
+	{
+		m_pOnLinkBroken = pFunc;
+	}
+	void setLinkEstablishedListener(IEntityPointerEvent *pFunc)
+	{
+		m_pOnLinkEstablished = pFunc;
+	}
 	
 private:
 	XGUID m_guid;
 	T *m_pEntity = NULL;
-	CEntityManager *m_pMgr = NULL;
 	CBaseEntity *m_pThisEntity = NULL;
 	bool(*m_pfnCheckEntityType)(CBaseEntity*) = NULL;
 
-	void(CBaseEntity::*m_pfnOnLinkBroken)(T*) = NULL;
 	void(CBaseEntity::*m_pfnOnLinkEstablished)(T*) = NULL;
+	void(CBaseEntity::*m_pfnOnLinkBroken)(T*) = NULL;
+	IEntityPointerEvent *m_pOnLinkEstablished = NULL;
+	IEntityPointerEvent *m_pOnLinkBroken = NULL;
 
-	void init(CEntityManager *pWorld, CBaseEntity *pThisEntity)
+	void init(CBaseEntity *pThisEntity)
 	{
-		m_pMgr = pWorld;
 		m_pThisEntity = pThisEntity;
 	}
 
@@ -210,6 +225,10 @@ private:
 		{
 			(m_pThisEntity->*m_pfnOnLinkBroken)(pOldEnt);
 		}
+		if(m_pOnLinkBroken)
+		{
+			m_pOnLinkBroken->onEvent(pOldEnt);
+		}
 	}
 	void onLinkEstablished(T *pNewEnt)
 	{
@@ -219,9 +238,15 @@ private:
 		{
 			(m_pThisEntity->*m_pfnOnLinkEstablished)(pNewEnt);
 		}
+		if(m_pOnLinkEstablished)
+		{
+			m_pOnLinkEstablished->onEvent(pNewEnt);
+		}
 	}
-	void onTargetRemoved() override
+	void onTargetRemoved(CBaseEntity *pEnt) override
 	{
+		assert(pEnt == m_pEntity);
+
 		onLinkBroken(NULL);
 		m_pEntity = NULL;
 		registerWait();
@@ -232,7 +257,7 @@ private:
 	{
 		if(!m_isWaiting)
 		{
-			m_pMgr->registerWaitForGUID(m_guid, this);
+			((CBaseEntity*)m_pThisEntity)->m_pMgr->registerWaitForGUID(m_guid, this);
 			m_isWaiting = true;
 		}
 	}
@@ -240,7 +265,7 @@ private:
 	{
 		if(m_isWaiting)
 		{
-			m_pMgr->unregisterWaitForGUID(m_guid, this);
+			((CBaseEntity*)m_pThisEntity)->m_pMgr->unregisterWaitForGUID(m_guid, this);
 			m_isWaiting = false;
 		}
 	}
diff --git a/source/game/proptable.cpp b/source/game/proptable.cpp
index c203467d4501ba223d96a114e0e812d8725b9ef4..6210a2f8dbd4e59066dec68f97a43105ac50d27a 100644
--- a/source/game/proptable.cpp
+++ b/source/game/proptable.cpp
@@ -81,7 +81,7 @@ prop_editor_t _GetEditorFilefield(int start, ...)
 	return(out);
 }
 
-const char * GetEmptyString()
+const char* GetEmptyString()
 {
 	static const char * str = "";
 	return(str);
@@ -92,30 +92,31 @@ void output_t::fire(CBaseEntity *pInflictor, CBaseEntity *pActivator, inputdata_
 	inputdata_t data = {0};
 	data.pActivator = pActivator;
 	data.pInflictor = pInflictor;
-	for(int i = 0; i < iOutCount; ++i)
+	for(UINT i = 0, l = aOutputs.size(); i < l; ++i)
 	{
+		named_output_t &out = aOutputs[i];
 		data.type = PDF_NONE;
-		if(pOutputs[i].fDelay == 0.0f && !pOutputs[i].useRandomDelay)
+		if(out.fDelay == 0.0f && !out.useRandomDelay)
 		{
-			for(int j = 0; j < pOutputs[i].iOutCount; ++j)
+			for(int j = 0, jl = out.aOutputs.size(); j < jl; ++j)
 			{
-				data.type = pOutputs[i].pOutputs[j].data.type;
+				data.type = out.aOutputs[j].data.type;
 				
 				if(data.type == PDF_STRING)
 				{
 					data.parameter.str = NULL;
 				}
 
-				if(pOutputs[i].pOutputs[j].useOverrideData)
+				if(out.aOutputs[j].useOverrideData)
 				{
-					data.setParameter(pOutputs[i].pOutputs[j].data);
+					data.setParameter(out.aOutputs[j].data);
 				}
 				else if(pInputData)
 				{
 					data.setParameter(*pInputData);
 				}
 
-				(pOutputs[i].pOutputs[j].pTarget->*(pOutputs[i].pOutputs[j].fnInput))(&data);
+				(out.aOutputs[j].pTarget->*(out.aOutputs[j].fnInput))(&data);
 
 				if(data.type == PDF_STRING && data.parameter.str != GetEmptyString())
 				{
@@ -130,11 +131,120 @@ void output_t::fire(CBaseEntity *pInflictor, CBaseEntity *pActivator, inputdata_
 				data.type = pInputData->type;
 				data.setParameter(*pInputData);
 			}
-			pActivator->getManager()->setOutputTimeout(&pOutputs[i], &data);
+			pActivator->getManager()->setOutputTimeout(&out, &data);
 		}
 	}
 }
 
+void named_output_t::init(CBaseEntity *pThisEntity)
+{
+	listTargets.init(pThisEntity);
+	listTargets.setEntityName(szTargetName);
+}
+
+void named_output_t::onEntityAdded(CBaseEntity *pEnt)
+{
+	propdata_t * pField = pEnt->getField(szTargetInput);
+	if(!pField || !(pField->flags & PDFF_INPUT))
+	{
+		printf(COLOR_CYAN "Class '%s' has no input '%s', obj '%s'\n" COLOR_RESET, pEnt->getClassName(), szTargetInput, szTargetName);
+	}
+
+	input_t &out = aOutputs[aOutputs.size()];
+
+	out.fnInput = pField->fnInput;
+	out.pTarget = pEnt;
+	memset(&out.data, 0, sizeof(out.data));
+	out.data.type = pField->type;
+	if((out.useOverrideData = szTargetData != NULL))
+	{
+		float3_t f3;
+		float4_t f4;
+		SMQuaternion q;
+		int d;
+		float f;
+		const char *value = szTargetData;
+		bool bParsed = false;
+		switch(pField->type)
+		{
+		case PDF_NONE:
+			bParsed = true;
+			break;
+		case PDF_INT:
+			if(1 == sscanf(value, "%d", &d))
+			{
+				out.data.parameter.i = d;
+				bParsed = true;
+			}
+			break;
+		case PDF_FLOAT:
+			if(1 == sscanf(value, "%f", &f))
+			{
+				out.data.parameter.f = f;
+				bParsed = true;
+			}
+			break;
+		case PDF_VECTOR:
+			if(3 == sscanf(value, "%f %f %f", &f3.x, &f3.y, &f3.z))
+			{
+				out.data.v3Parameter = f3;
+				bParsed = true;
+			}
+			break;
+		case PDF_VECTOR4:
+			{
+				int iPrm = sscanf(value, "%f %f %f %f", &f4.x, &f4.y, &f4.z, &f4.w);
+				if(iPrm > 2)
+				{
+					if(iPrm == 3)
+					{
+						f4.w = 1.0f;
+					}
+					out.data.v4Parameter = f4;
+					bParsed = true;
+				}
+			}
+			break;
+		case PDF_BOOL:
+			if(1 == sscanf(value, "%d", &d))
+			{
+				out.data.parameter.b = d != 0;
+				bParsed = true;
+			}
+			break;
+		case PDF_STRING:
+			_setStrVal(&out.data.parameter.str, value);
+			bParsed = true;
+			break;
+		}
+
+		if(!bParsed)
+		{
+			printf(COLOR_CYAN "Cannot parse input parameter '%s', class '%s', input '%s', obj '%s'\n" COLOR_RESET, value, pEnt->getClassName(), szTargetInput, szTargetName);
+			aOutputs.erase(aOutputs.size() - 1);
+		}
+	}
+}
+void named_output_t::onEntityRemoved(CBaseEntity *pEnt)
+{
+	int idx = aOutputs.indexOf(pEnt, [](const input_t &a, CBaseEntity *b){
+		return(a.pTarget == b);
+	});
+	assert(idx >= 0);
+	if(idx >= 0)
+	{
+		if(aOutputs[idx].useOverrideData && aOutputs[idx].data.type == PDF_STRING)
+		{
+			const char *estr = GetEmptyString();
+			if(estr != aOutputs[idx].data.parameter.str)
+			{
+				mem_delete_a(aOutputs[idx].data.parameter.str);
+			}
+		}
+		aOutputs[idx] = aOutputs[aOutputs.size() - 1];
+		aOutputs.erase(aOutputs.size() - 1);
+	}
+}
 
 void _setStrVal(const char **to, const char *value)
 {
diff --git a/source/game/proptable.h b/source/game/proptable.h
index 40b0e428974c671ca970595fc7ac9c3f05c39585..c77a4b75ab038d1d030130d22a8f3963d022f5d9 100644
--- a/source/game/proptable.h
+++ b/source/game/proptable.h
@@ -9,6 +9,7 @@ See the license in LICENSE
 
 #include <common/Math.h>
 #include "EntityPointer.h"
+#include "EntityList.h"
 
 class CBaseEntity;
 
@@ -278,25 +279,64 @@ struct named_output_t
 	const char *szTargetData = NULL;
 	int iFireLimit = -1;
 
-	int iOutCount = 0;
-	input_t *pOutputs = NULL;
+	CEntityList listTargets;
+	Array<input_t> aOutputs;
+	// int iOutCount = 0;
+	// input_t *pOutputs = NULL;
+
+	void init(CBaseEntity *pThisEntity);
+	void onEntityAdded(CBaseEntity *pEnt);
+	void onEntityRemoved(CBaseEntity *pEnt);
+
+	class OnLinkEstablished final: public IEntityPointerEvent
+	{
+	public:
+		OnLinkEstablished(named_output_t *pOut):
+			m_pOut(pOut)
+		{
+		}
+		void onEvent(CBaseEntity *pEnt) override
+		{
+			m_pOut->onEntityAdded(pEnt);
+		}
+	private:
+		named_output_t *m_pOut;
+	};
+	class OnLinkBroken final: public IEntityPointerEvent
+	{
+	public:
+		OnLinkBroken(named_output_t *pOut):
+			m_pOut(pOut)
+		{
+		}
+		void onEvent(CBaseEntity *pEnt) override
+		{
+			m_pOut->onEntityRemoved(pEnt);
+		}
+	private:
+		named_output_t *m_pOut;
+	};
+
+	OnLinkEstablished onLinkEstablished;
+	OnLinkBroken onLinkBroken;
+
+	named_output_t():
+		onLinkEstablished(this),
+		onLinkBroken(this)
+	{
+		listTargets.setLinkEstablishedListener(&onLinkEstablished);
+		listTargets.setLinkBrokenListener(&onLinkBroken);
+	}
 };
 
 struct output_t
 {
-	output_t():
-		iOutCount(0),
-		pOutputs(NULL),
-		pData(NULL),
-		bDirty(false)
-	{
-	}
 	void fire(CBaseEntity *pInflictor, CBaseEntity *pActivator, inputdata_t *pInputData = NULL);
-
-	bool bDirty;
-	int iOutCount;
-	named_output_t * pOutputs;
-	void * pData;
+	
+	// bool bDirty = false;
+	// int iOutCount = 0;
+	Array<named_output_t> aOutputs;
+	void *pData = NULL;
 };
 
 #define FIRE_OUTPUT(output, inflictor) (output).fire((inflictor), this)