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)