1 // Copyright 2001-2019 Crytek GmbH / Crytek Group. All rights reserved.
4 #include "Script/GraphNodes/ScriptGraphSwitchNode.h"
6 #include <CryString/CryStringUtils.h>
7 #include <CrySchematyc2/GUIDRemapper.h>
8 #include <CrySchematyc2/IDomainContext.h>
9 #include <CrySchematyc2/IObject.h>
10 #include <CrySchematyc2/LibUtils.h>
11 #include <CrySchematyc2/Script/IScriptGraphNodeCompiler.h>
12 #include <CrySchematyc2/Script/IScriptGraphNodeCreationMenu.h>
13 #include <CrySchematyc2/Serialization/ISerializationContext.h>
15 #include "AggregateTypeIdSerialize.h"
16 #include "DomainContext.h"
17 #include "Script/ScriptVariableDeclaration.h"
18 #include "Script/GraphNodes/ScriptGraphNodeFactory.h"
22 const SGUID
CScriptGraphSwitchNode::s_typeGUID
= "1d081133-e900-4244-add5-f0831d27b16f";
24 CScriptGraphSwitchNode::SCase::SCase() {}
26 CScriptGraphSwitchNode::SCase::SCase(const IAnyPtr
& _pValue
)
30 void CScriptGraphSwitchNode::SCase::Serialize(Serialization::IArchive
& archive
)
32 const IAny
* pDefaultValue
= archive
.context
<IAny
>();
33 SCHEMATYC2_SYSTEM_ASSERT(pDefaultValue
);
34 if(!pValue
&& pDefaultValue
)
36 pValue
= pDefaultValue
->Clone();
40 archive(*pValue
, "value", "^Value");
41 if(archive
.isValidation())
43 char stringBuffer
[512] = "";
44 ToString(*pValue
, stringBuffer
);
47 archive
.error(*this, "Empty case value!");
51 else if(archive
.isValidation())
53 archive
.error(*this, "Unable to instantiate case value!");
57 CScriptGraphSwitchNode::CScriptGraphSwitchNode(const SGUID
& guid
)
58 : CScriptGraphNodeBase(guid
)
61 CScriptGraphSwitchNode::CScriptGraphSwitchNode(const SGUID
& guid
, const Vec2
& pos
)
62 : CScriptGraphNodeBase(guid
, pos
)
65 SGUID
CScriptGraphSwitchNode::GetTypeGUID() const
70 EScriptGraphColor
CScriptGraphSwitchNode::GetColor() const
72 return EScriptGraphColor::Orange
;
75 void CScriptGraphSwitchNode::Refresh(const SScriptRefreshParams
& params
)
77 CRY_PROFILE_FUNCTION(PROFILE_LOADING_ONLY
);
79 CScriptGraphNodeBase::Refresh(params
);
81 CScriptGraphNodeBase::SetName("Switch");
82 CScriptGraphNodeBase::AddInput(EScriptGraphPortFlags::MultiLink
| EScriptGraphPortFlags::Execute
, "In");
83 CScriptGraphNodeBase::AddOutput(EScriptGraphPortFlags::Execute
| EScriptGraphPortFlags::SpacerBelow
, "Default");
87 CScriptGraphNodeBase::AddInput(EScriptGraphPortFlags::Data
, "Value", m_typeId
);
89 uint32 invalidCaseCount
= 0;
90 for(SCase
& _case
: m_cases
)
92 char outputName
[128] = "";
95 ToString(*_case
.pValue
, outputName
);
99 cry_sprintf(outputName
, "INVALID_CASE[%d]", ++ invalidCaseCount
);
101 CScriptGraphNodeBase::AddOutput(EScriptGraphPortFlags::Removable
| EScriptGraphPortFlags::Execute
, outputName
);
106 void CScriptGraphSwitchNode::Serialize(Serialization::IArchive
& archive
)
108 CRY_PROFILE_FUNCTION(PROFILE_LOADING_ONLY
);
110 CScriptGraphNodeBase::Serialize(archive
);
112 switch(SerializationContext::GetPass(archive
))
114 case ESerializationPass::PreLoad
:
116 SerializeBasicInfo(archive
);
119 case ESerializationPass::PostLoad
:
121 CreateDefaultValue();
122 SerializeCases(archive
);
125 case ESerializationPass::Save
:
127 SerializeBasicInfo(archive
);
128 SerializeCases(archive
);
131 case ESerializationPass::Edit
:
134 SerializeCases(archive
);
138 case ESerializationPass::Validate
:
140 SerializeCases(archive
);
147 void CScriptGraphSwitchNode::RemapGUIDs(IGUIDRemapper
& guidRemapper
)
149 CScriptGraphNodeBase::RemapGUIDs(guidRemapper
);
151 if(m_typeId
.GetDomain() == EDomain::Script
) // #SchematycTODO : Create CAggregateTypeId::RemapGUIDs() function?
153 m_typeId
= CAggregateTypeId::FromScriptTypeGUID(guidRemapper
.Remap(m_typeId
.AsScriptTypeGUID()));
157 void CScriptGraphSwitchNode::Compile_New(IScriptGraphNodeCompiler
& compiler
) const
161 compiler
.BindCallback(&Execute
);
163 const uint32 caseCount
= m_cases
.size();
164 compiler
.BindAttribute(EAttributeId::CaseCount
, caseCount
);
165 for(uint32 caseIdx
= 0; caseIdx
< caseCount
; ++ caseIdx
)
167 if(m_cases
[caseIdx
].pValue
)
169 compiler
.BindAnyAttribute(EAttributeId::FirstCase
+ caseIdx
, *m_cases
[caseIdx
].pValue
);
173 compiler
.BindAnyInput(EInputIdx::Value
, *m_pDefaultValue
);
177 void CScriptGraphSwitchNode::RegisterCreator(CScriptGraphNodeFactory
& factory
)
179 class CCreator
: public IScriptGraphNodeCreator
183 class CNodeCreationMenuCommand
: public IScriptGraphNodeCreationMenuCommand
189 IScriptGraphNodePtr
Execute(const Vec2
& pos
)
191 return std::make_shared
<CScriptGraphSwitchNode
>(gEnv
->pSchematyc2
->CreateGUID(), pos
);
199 // IScriptGraphNodeCreator
201 virtual SGUID
GetTypeGUID() const override
203 return CScriptGraphSwitchNode::s_typeGUID
;
206 virtual IScriptGraphNodePtr
CreateNode() override
208 return std::make_shared
<CScriptGraphSwitchNode
>(gEnv
->pSchematyc2
->CreateGUID());
211 virtual void PopulateNodeCreationMenu(IScriptGraphNodeCreationMenu
& nodeCreationMenu
, const IDomainContext
& domainContext
, const IScriptGraphExtension
& graph
) override
213 nodeCreationMenu
.AddOption("Switch", "Switch on input value", "", std::make_shared
<CNodeCreationMenuCommand
>());
216 // ~IScriptGraphNodeCreator
219 factory
.RegisterCreator(std::make_shared
<CCreator
>());
222 void CScriptGraphSwitchNode::CreateDefaultValue()
224 if(!m_pDefaultValue
|| (m_pDefaultValue
->GetTypeInfo().GetTypeId() != m_typeId
))
226 m_pDefaultValue
= MakeScriptVariableValueShared(CScriptGraphNodeBase::GetGraph()->GetElement_New().GetFile(), m_typeId
);
232 void CScriptGraphSwitchNode::SerializeBasicInfo(Serialization::IArchive
& archive
)
234 archive(m_typeId
, "typeId");
237 void CScriptGraphSwitchNode::SerializeCases(Serialization::IArchive
& archive
)
239 Serialization::SContext
context(archive
, static_cast<IAny
*>(m_pDefaultValue
.get()));
240 archive(m_cases
, "cases", "Cases");
243 void CScriptGraphSwitchNode::Edit(Serialization::IArchive
& archive
)
246 Serialization::StringList typeNames
;
248 typeNames
.reserve(50);
250 auto visitEnvType
= [&typeIds
, &typeNames
] (const IEnvTypeDesc
& envTypeDesc
) -> EVisitStatus
252 if((envTypeDesc
.GetCategory() == EEnvTypeCategory::Enumeration
) || ((envTypeDesc
.GetFlags() & EEnvTypeFlags::Switchable
) != 0))
254 typeIds
.push_back(envTypeDesc
.GetTypeInfo().GetTypeId());
255 typeNames
.push_back(envTypeDesc
.GetName());
257 return EVisitStatus::Continue
;
259 gEnv
->pSchematyc2
->GetEnvRegistry().VisitTypeDescs(EnvTypeDescVisitor::FromLambdaFunction(visitEnvType
)); // #SchematycTODO : Use domain context?
261 const IScriptElement
& scriptElement
= CScriptGraphNodeBase::GetGraph()->GetElement_New();
262 CDomainContext
domainContext(SDomainContextScope(&scriptElement
.GetFile(), scriptElement
.GetScopeGUID()));
263 auto visitScriptEnumeration
= [&typeIds
, &typeNames
, &domainContext
] (const IScriptEnumeration
& scriptEnumeration
) -> EVisitStatus
265 typeIds
.push_back(scriptEnumeration
.GetTypeId());
267 stack_string typeName
;
268 domainContext
.QualifyName(scriptEnumeration
, EDomainQualifier::Local
, typeName
);
269 typeNames
.push_back(typeName
.c_str());
271 return EVisitStatus::Continue
;
273 domainContext
.VisitScriptEnumerations(ScriptEnumerationConstVisitor::FromLambdaFunction(visitScriptEnumeration
), EDomainScope::Derived
);
275 if(archive
.isInput())
277 Serialization::StringListValue
typeName(typeNames
, 0);
278 archive(typeName
, "typeName", "Type");
280 const CAggregateTypeId oldTypeId
= m_typeId
;
281 const CAggregateTypeId newTypeId
= typeIds
[typeName
.index()];
282 m_typeId
= newTypeId
;
283 if (oldTypeId
!= newTypeId
)
286 CScriptGraphNodeBase::GetGraph()->RemoveLinks(CScriptGraphNodeBase::GetGUID());
288 CreateDefaultValue();
290 else if(archive
.isOutput())
293 TypeIds::iterator itTypeId
= std::find(typeIds
.begin(), typeIds
.end(), m_typeId
);
294 if(itTypeId
!= typeIds
.end())
296 typeIdx
= static_cast<int>(itTypeId
- typeIds
.begin());
300 typeIds
.push_back(m_typeId
);
301 typeNames
.push_back("None");
302 typeIdx
= static_cast<int>(typeIds
.size() - 1);
305 Serialization::StringListValue
typeName(typeNames
, typeIdx
);
306 archive(typeName
, "typeName", "Type");
310 void CScriptGraphSwitchNode::Validate(Serialization::IArchive
& archive
)
312 std::vector
<stack_string
> caseStrings
;
313 uint32 duplicateCaseValueCount
= 0;
314 for(const SCase
& _case
: m_cases
)
318 char stringBuffer
[128] = "";
319 ToString(*_case
.pValue
, stringBuffer
);
322 stack_string caseString
= stringBuffer
;
323 if(std::find(caseStrings
.begin(), caseStrings
.end(), caseString
) != caseStrings
.end())
325 ++ duplicateCaseValueCount
;
329 caseStrings
.push_back(caseString
);
334 if(duplicateCaseValueCount
)
336 archive
.error(*this, "Duplicate case values detected!");
340 SRuntimeResult
CScriptGraphSwitchNode::Execute(IObject
* pObject
, const SRuntimeActivationParams
& activationParams
, CRuntimeNodeData
& data
)
342 const IAny
* pValue
= data
.GetInput(EInputIdx::Value
);
343 // Note: we can't make this into a fatal assert because this will then crash the editor when the user
344 // changes the type of the switch node.
345 //SCHEMATYC2_SYSTEM_ASSERT_FATAL(pValue != nullptr);
346 if (pValue
!= nullptr)
348 const uint32 caseCount
= *data
.GetAttribute
<uint32
>(EAttributeId::CaseCount
);
349 for (uint32 caseIdx
= 0; caseIdx
< caseCount
; ++caseIdx
)
351 const IAny
* pCaseValue
= data
.GetAttribute(EAttributeId::FirstCase
+ caseIdx
);
352 if (pCaseValue
!= nullptr)
354 if (*pValue
== *pCaseValue
)
356 return SRuntimeResult(ERuntimeStatus::Continue
, EOutputIdx::FirstCase
+ caseIdx
);
361 return SRuntimeResult(ERuntimeStatus::Continue
, EOutputIdx::Default
);