ScriptEditorComponent.cpp 58 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project
  3. *
  4. * SPDX-License-Identifier: Apache-2.0 OR MIT
  5. *
  6. */
  7. #include "AzToolsFramework_precompiled.h"
  8. #include <AzToolsFramework/ToolsComponents/ScriptEditorComponent.h>
  9. #include <AzCore/Script/ScriptSystemBus.h>
  10. #include <AzCore/EBus/Results.h>
  11. #include <AzCore/Asset/AssetManager.h>
  12. #include <AzCore/Component/ComponentApplicationBus.h>
  13. #include <AzFramework/StringFunc/StringFunc.h>
  14. #include <AzFramework/Asset/AssetCatalogBus.h>
  15. #include <AzToolsFramework/UI/PropertyEditor/PropertyEditorAPI.h>
  16. #include <AzToolsFramework/API/ToolsApplicationAPI.h>
  17. #include <AzToolsFramework/API/EditorAssetSystemAPI.h>
  18. #include <AzCore/std/sort.h>
  19. #include <AzCore/Script/ScriptContextDebug.h>
  20. extern "C" {
  21. #include<Lua/lualib.h>
  22. #include<Lua/lauxlib.h>
  23. }
  24. namespace AZ
  25. {
  26. namespace Edit
  27. {
  28. class AttributeDynamicScriptValue
  29. : public Attribute
  30. {
  31. public:
  32. AZ_RTTI(AttributeDynamicScriptValue, "{46803928-11c9-4176-b2fe-2f0aed99bfeb}", Attribute);
  33. AZ_CLASS_ALLOCATOR(AttributeDynamicScriptValue, AZ::SystemAllocator, 0)
  34. AttributeDynamicScriptValue(const DynamicSerializableField& value)
  35. : m_value(value) {}
  36. virtual ~AttributeDynamicScriptValue()
  37. {
  38. m_value.DestroyData();
  39. }
  40. template<class T>
  41. bool Get(T& value) const
  42. {
  43. // We deal only with exact types no base types, etc.
  44. // We can do that if needed, but introduces lots of complications
  45. // which we are not convinced they are worth it.
  46. //if (dhtypeid(AZStd::remove_pointer<T>::type) == m_value.m_typeId)
  47. if ( AzTypeInfo<T>::Uuid() == m_value.m_typeId)
  48. {
  49. GetValue(value, AZStd::is_pointer<T>::type());
  50. return true;
  51. }
  52. return false;
  53. }
  54. template<class T>
  55. void GetValue(T& value, AZStd::false_type /*AZStd::is_pointer<T>::type()*/) { value = *reinterpret_cast<T*>(m_value.m_data); }
  56. template<class T>
  57. void GetValue(T& value, AZStd::true_type /*AZStd::is_pointer<T>::type()*/) { value = reinterpret_cast<T>(m_value.m_data); }
  58. DynamicSerializableField m_value;
  59. };
  60. } // namespace Edit
  61. } // namespace AZ
  62. namespace AzToolsFramework
  63. {
  64. namespace Components
  65. {
  66. const char* AssetTypeName = "asset";
  67. const char* EntityRefName = "entity";
  68. const char* UiFieldName = "ui";
  69. const char* UiOrderValue = "order";
  70. const char* DescriptionFieldName = "description";
  71. bool ScriptEditorComponent::DoComponentsMatch(const ScriptEditorComponent* thisComponent, const ScriptEditorComponent* otherComponent)
  72. {
  73. // These are "matching" iff the script asset is the same between them
  74. return thisComponent->m_scriptAsset.GetId() == otherComponent->m_scriptAsset.GetId();
  75. }
  76. //=========================================================================
  77. // ~ScriptEditorComponent
  78. //=========================================================================
  79. ScriptEditorComponent::~ScriptEditorComponent()
  80. {
  81. ClearDataElements();
  82. }
  83. //=========================================================================
  84. // Init
  85. //=========================================================================
  86. void ScriptEditorComponent::Init()
  87. {
  88. // Ensure m_scriptComponent's asset reference is up to date on deserialize.
  89. SetScript(m_scriptAsset);
  90. }
  91. //=========================================================================
  92. // Activate
  93. //=========================================================================
  94. void ScriptEditorComponent::Activate()
  95. {
  96. // Setup the context
  97. m_scriptComponent.Init();
  98. if (m_scriptAsset.GetId().IsValid())
  99. {
  100. // Re-retrieve the asset in case it was reloaded while we were inactive.
  101. m_scriptAsset = AZ::Data::AssetManager::Instance().GetAsset<AZ::ScriptAsset>(m_scriptAsset.GetId(), m_scriptAsset.GetAutoLoadBehavior());
  102. SetScript(m_scriptAsset);
  103. AZ::Data::AssetBus::Handler::BusConnect(m_scriptAsset.GetId());
  104. m_scriptAsset.QueueLoad();
  105. }
  106. else
  107. {
  108. SetScript({ nullptr, AZ::Data::AssetLoadBehavior::Default });
  109. }
  110. }
  111. //=========================================================================
  112. // Deactivate
  113. //=========================================================================
  114. void ScriptEditorComponent::Deactivate()
  115. {
  116. AZ::Data::AssetBus::Handler::BusDisconnect();
  117. ClearDataElements();
  118. }
  119. //=========================================================================
  120. // CacheString
  121. //=========================================================================
  122. const char* ScriptEditorComponent::CacheString(const char* str)
  123. {
  124. if (str == nullptr)
  125. {
  126. return nullptr;
  127. }
  128. return m_cachedStrings.insert(AZStd::make_pair(str, AZStd::string(str))).first->second.c_str();
  129. }
  130. //=========================================================================
  131. // LoadDefaultAsset
  132. //=========================================================================
  133. bool ScriptEditorComponent::LoadDefaultAsset(AZ::ScriptDataContext& sdc, int valueIndex, const char* name, AzFramework::ScriptPropertyGroup& group, ElementInfo& elementInfo)
  134. {
  135. LSV_BEGIN(sdc.GetNativeContext(), 0);
  136. (void)elementInfo;
  137. //Test to see if in fact is an asset, and then if so assign it.
  138. AZ::ScriptDataContext arrayTable;
  139. if (sdc.IsTable(valueIndex) && sdc.InspectTable(valueIndex, arrayTable))
  140. {
  141. const char* fieldName;
  142. int fieldIndex;
  143. int elementIndex;
  144. int count = 0;
  145. while (arrayTable.InspectNextElement(elementIndex, fieldName, fieldIndex))
  146. {
  147. if (!arrayTable.IsString(elementIndex) || fieldName == nullptr || strcmp(fieldName, AssetTypeName))
  148. {
  149. return false;
  150. }
  151. ++count;
  152. }
  153. if (count == 1)
  154. {
  155. group.m_properties.emplace_back(aznew AZ::ScriptPropertyAsset(name));
  156. return true;
  157. }
  158. }
  159. return false;
  160. }
  161. //=========================================================================
  162. // LoadDefaultEntityRef
  163. //=========================================================================
  164. bool ScriptEditorComponent::LoadDefaultEntityRef(AZ::ScriptDataContext& sdc, int valueIndex, const char* name, AzFramework::ScriptPropertyGroup& group, ElementInfo& elementInfo)
  165. {
  166. LSV_BEGIN(sdc.GetNativeContext(), 0);
  167. AZ::ScriptDataContext arrayTable;
  168. if (sdc.IsTable(valueIndex) && sdc.InspectTable(valueIndex, arrayTable))
  169. {
  170. const char* fieldName;
  171. int fieldIndex;
  172. int elementIndex;
  173. bool isEntity = false;
  174. const size_t entityRefNameLength = strlen(EntityRefName);
  175. const size_t descriptionFieldNameLength = strlen(DescriptionFieldName);
  176. while (arrayTable.InspectNextElement(elementIndex, fieldName, fieldIndex))
  177. {
  178. if (fieldName == nullptr || 0 != strncmp(fieldName, EntityRefName, entityRefNameLength))
  179. {
  180. if (fieldName && 0 == azstrnicmp(fieldName, DescriptionFieldName, descriptionFieldNameLength))
  181. {
  182. if (arrayTable.IsString(elementIndex))
  183. {
  184. arrayTable.ReadValue(elementIndex, elementInfo.m_editData.m_description);
  185. }
  186. }
  187. continue;
  188. }
  189. else
  190. {
  191. isEntity = true;
  192. }
  193. }
  194. if (isEntity)
  195. {
  196. (void)group;
  197. AZ::Data::AssetInfo scriptInfo;
  198. AZ::Data::AssetCatalogRequestBus::BroadcastResult(scriptInfo, &AZ::Data::AssetCatalogRequestBus::Events::GetAssetInfoById, m_scriptAsset.GetId());
  199. AZ_Warning("Script Component", false, "%s: The syntax \"%s = { entity = [value], ... }\" for declaring entity references has been deprecated, please use \"%s = { default = EntityId(), ... }\" instead.", scriptInfo.m_relativePath.c_str(), name, name);
  200. // Create an EntityId instance
  201. const AZ::SerializeContext* context = nullptr;
  202. EBUS_EVENT_RESULT(context, AZ::ComponentApplicationBus, GetSerializeContext);
  203. const AZ::SerializeContext::ClassData* entityIdClassData = context->FindClassData(azrtti_typeid<AZ::EntityId>());
  204. AZ_Assert(entityIdClassData && entityIdClassData->m_factory, "AZ::EntityId is missing ClassData or factory in the SerializeContext");
  205. AZ::EntityId* entityId = static_cast<AZ::EntityId*>(entityIdClassData->m_factory->Create(name));
  206. // Create new property
  207. AZ::DynamicSerializableField field;
  208. field.Set(entityId);
  209. group.m_properties.emplace_back(aznew AZ::ScriptPropertyGenericClass(name, field));
  210. return true;
  211. }
  212. }
  213. return false;
  214. }
  215. //=========================================================================
  216. // LoadAttribute
  217. //=========================================================================
  218. bool ScriptEditorComponent::LoadAttribute(AZ::ScriptDataContext& sdc, int valueIndex, const char* name, AZ::Edit::ElementData& ed, AZ::ScriptProperty* prop)
  219. {
  220. LSV_BEGIN(sdc.GetNativeContext(), 0);
  221. /////////////////////////////////////////////////////////////////////////////////
  222. // This is an example that you can do you the custom OnUnhandledAttribute message
  223. // check for data element properties, not real attributes.
  224. if (azstricmp(name, "enumValues") == 0)
  225. {
  226. if (azrtti_istypeof<AZ::ScriptPropertyString>(prop))
  227. {
  228. return LoadEnumValuesString(sdc, valueIndex, ed);
  229. }
  230. else if (azrtti_istypeof<AZ::ScriptPropertyNumber>(prop))
  231. {
  232. return LoadEnumValuesDouble(sdc, valueIndex, ed);
  233. }
  234. else
  235. {
  236. AZ_Warning("Script", false, "enumValues must have the same type as the default value! Currently we support numbers and strings!");
  237. return false;
  238. }
  239. }
  240. /////////////////////////////////////////////////////////////////////////////////
  241. // just attribute key/value pairs
  242. if (sdc.IsNumber(valueIndex))
  243. {
  244. double value;
  245. sdc.ReadValue(valueIndex, value);
  246. ed.m_attributes.push_back(AZ::Edit::AttributePair(AZ::Crc32(name), aznew AZ::Edit::AttributeData<double>(value)));
  247. }
  248. else if (sdc.IsBoolean(valueIndex))
  249. {
  250. bool value;
  251. sdc.ReadValue(valueIndex, value);
  252. ed.m_attributes.push_back(AZ::Edit::AttributePair(AZ::Crc32(name), aznew AZ::Edit::AttributeData<bool>(value)));
  253. }
  254. else if (sdc.IsString(valueIndex))
  255. {
  256. const char* value = nullptr;
  257. sdc.ReadValue(valueIndex, value);
  258. value = CacheString(value);
  259. ed.m_attributes.push_back(AZ::Edit::AttributePair(AZ::Crc32(name), aznew AZ::Edit::AttributeData<const char*>(value)));
  260. }
  261. else if (sdc.IsRegisteredClass(valueIndex))
  262. {
  263. AZ::DynamicSerializableField value;
  264. value.m_data = AZ::Internal::LuaAnyClassFromStack(sdc.GetNativeContext(), sdc.GetStartIndex() + valueIndex, &value.m_typeId);
  265. if (value.m_data)
  266. {
  267. ed.m_attributes.push_back(AZ::Edit::AttributePair(AZ::Crc32(name), aznew AZ::Edit::AttributeDynamicScriptValue(value)));
  268. }
  269. }
  270. else if (sdc.IsFunction(valueIndex))
  271. {
  272. // TODO we need a new LUA Function attribute AttributeDynamicScriptFunction TODO when we have a use case
  273. // We need to cache the script context and function name (of cache the function in the VM, which should not be called often)
  274. AZ_Assert(false, "Function as property attribute is not implemented yet!");
  275. }
  276. else
  277. {
  278. return false;
  279. }
  280. return true;
  281. }
  282. //=========================================================================
  283. // LoadAttribute
  284. //=========================================================================
  285. bool ScriptEditorComponent::LoadEnumValuesDouble(AZ::ScriptDataContext& sdc, int valueIndex, AZ::Edit::ElementData& ed)
  286. {
  287. LSV_BEGIN(sdc.GetNativeContext(), 0);
  288. typedef AZStd::pair<double, AZStd::string> EnumPairType;
  289. bool isValidValues = false;
  290. if (sdc.IsTable(valueIndex))
  291. {
  292. AZ::ScriptDataContext enumValuesTable;
  293. if (sdc.InspectTable(valueIndex, enumValuesTable))
  294. {
  295. const char* fieldName;
  296. int fieldIndex;
  297. int enumIndex;
  298. while (enumValuesTable.InspectNextElement(enumIndex, fieldName, fieldIndex))
  299. {
  300. bool isValidValue = false;
  301. EnumPairType enumValue(0, "Enum Value");
  302. if (enumValuesTable.IsNumber(enumIndex))
  303. {
  304. double value;
  305. enumValuesTable.ReadValue(enumIndex, value);
  306. enumValue.first = value;
  307. isValidValue = true;
  308. }
  309. else if (enumValuesTable.IsTable(enumIndex))
  310. {
  311. AZ::ScriptDataContext valueTable;
  312. if (enumValuesTable.InspectTable(enumIndex, valueTable))
  313. {
  314. const char* tableFieldName;
  315. int tableFieldIndex;
  316. int enumValueIndex;
  317. while (valueTable.InspectNextElement(enumValueIndex, tableFieldName, tableFieldIndex))
  318. {
  319. if (valueTable.IsNumber(enumValueIndex) && tableFieldIndex == 1)
  320. {
  321. double value;
  322. valueTable.ReadValue(enumValueIndex, value);
  323. enumValue.first = value;
  324. isValidValue = true;
  325. }
  326. else if (valueTable.IsString(enumValueIndex) && tableFieldIndex == 2)
  327. {
  328. const char* value = nullptr;
  329. valueTable.ReadValue(enumValueIndex, value);
  330. enumValue.second = value;
  331. }
  332. }
  333. }
  334. }
  335. AZ_Warning("Script", isValidValue, "Attribute enumValues can be table containing either be a number (default description is used) or\
  336. a table (value is at index 1 and description is at index 2). Example: EnumValues = { { 1, 'My value 1'}, 2 }\n");
  337. if (isValidValue)
  338. {
  339. isValidValues = true;
  340. ed.m_attributes.push_back(AZ::Edit::AttributePair(AZ::Edit::InternalAttributes::EnumValue, aznew AZ::Edit::AttributeData<EnumPairType>(enumValue)));
  341. }
  342. }
  343. }
  344. }
  345. return isValidValues;
  346. }
  347. //=========================================================================
  348. // LoadAttribute
  349. //=========================================================================
  350. bool ScriptEditorComponent::LoadEnumValuesString(AZ::ScriptDataContext& sdc, int valueIndex, AZ::Edit::ElementData& ed)
  351. {
  352. LSV_BEGIN(sdc.GetNativeContext(), 0);
  353. typedef AZStd::pair<AZStd::string, AZStd::string> EnumPairType;
  354. bool isValidValues = false;
  355. if (sdc.IsTable(valueIndex))
  356. {
  357. AZ::ScriptDataContext enumValuesTable;
  358. if (sdc.InspectTable(valueIndex, enumValuesTable))
  359. {
  360. const char* fieldName;
  361. int fieldIndex;
  362. int enumIndex;
  363. while (enumValuesTable.InspectNextElement(enumIndex, fieldName, fieldIndex))
  364. {
  365. bool isValidValue = false;
  366. EnumPairType enumValue("", "Enum Value");
  367. if (enumValuesTable.IsString(enumIndex))
  368. {
  369. const char* value = nullptr;
  370. enumValuesTable.ReadValue(enumIndex, value);
  371. enumValue.first = value;
  372. isValidValue = true;
  373. }
  374. else if (enumValuesTable.IsTable(enumIndex))
  375. {
  376. AZ::ScriptDataContext valueTable;
  377. if (enumValuesTable.InspectTable(enumIndex, valueTable))
  378. {
  379. const char* tableFieldName = nullptr;
  380. int tableFieldIndex;
  381. int enumValueIndex;
  382. while (valueTable.InspectNextElement(enumValueIndex, tableFieldName, tableFieldIndex))
  383. {
  384. if (valueTable.IsString(enumValueIndex) && tableFieldIndex == 1)
  385. {
  386. const char* value = nullptr;
  387. valueTable.ReadValue(enumValueIndex, value);
  388. enumValue.first = value;
  389. isValidValue = true;
  390. }
  391. else if (valueTable.IsString(enumValueIndex) && tableFieldIndex == 2)
  392. {
  393. const char* value = nullptr;
  394. valueTable.ReadValue(enumValueIndex, value);
  395. enumValue.second = value;
  396. }
  397. }
  398. }
  399. }
  400. AZ_Warning("Script", isValidValue, "Attribute enumValues can be table containing either be a string (default description is used) or\
  401. a table (value is at index 1 and description is at index 2). Example: EnumValues = { { 'String', 'My string value'}, 'String2' }\n");
  402. if (isValidValue)
  403. {
  404. isValidValues = true;
  405. ed.m_attributes.push_back(AZ::Edit::AttributePair(AZ::Edit::InternalAttributes::EnumValue, aznew AZ::Edit::AttributeData<EnumPairType>(enumValue)));
  406. }
  407. }
  408. }
  409. }
  410. return isValidValues;
  411. }
  412. void ScriptEditorComponent::LoadProperties(AZ::ScriptDataContext& sdc, AzFramework::ScriptPropertyGroup& group)
  413. {
  414. LSV_BEGIN(sdc.GetNativeContext(), -2);
  415. const bool restrictToPropertyArrays = true;
  416. const char* propertyName;
  417. int propertyFieldIndex;
  418. int propertyIndex;
  419. while (sdc.InspectNextElement(propertyIndex, propertyName, propertyFieldIndex))
  420. {
  421. if (propertyName == nullptr)
  422. {
  423. continue; // skip index elements
  424. }
  425. ElementInfo ei;
  426. ei.m_editData.m_name = CacheString(propertyName);
  427. ei.m_editData.m_description = "";
  428. ei.m_editData.m_elementId = AZ::Edit::UIHandlers::Default;
  429. ei.m_sortOrder = FLT_MAX;
  430. bool isValidProperty = true;
  431. if (sdc.IsTable(propertyIndex))
  432. {
  433. AZ::ScriptDataContext propertyTable;
  434. if (sdc.InspectTable(propertyIndex, propertyTable))
  435. {
  436. int defaultValueIndex = 0;
  437. if (propertyTable.PushTableElement(AzFramework::ScriptComponent::DefaultFieldName, &defaultValueIndex)) // Is this a value or a group
  438. {
  439. bool needToCreateProperty = false;
  440. AZ::ScriptProperty* groupProperty = group.GetProperty(propertyName);
  441. if (!groupProperty)
  442. {
  443. needToCreateProperty = true;
  444. }
  445. else
  446. {
  447. if (groupProperty->DoesTypeMatch(propertyTable, defaultValueIndex))
  448. {
  449. needToCreateProperty = false;
  450. }
  451. else
  452. {
  453. if (auto itr = AZStd::find(group.m_properties.begin(), group.m_properties.end(), groupProperty))
  454. {
  455. if (itr != group.m_properties.end())
  456. {
  457. delete *itr;
  458. group.m_properties.erase(itr);
  459. }
  460. }
  461. needToCreateProperty = true;
  462. }
  463. }
  464. if (needToCreateProperty)
  465. {
  466. if (AZ::ScriptProperty* scriptProperty = propertyTable.ConstructScriptProperty(defaultValueIndex, propertyName, restrictToPropertyArrays))
  467. {
  468. group.m_properties.emplace_back(scriptProperty);
  469. }
  470. else
  471. {
  472. AZ_Warning("Script", false, "We support only boolean, number, string and registered classes as properties. Check '%s'!", propertyName);
  473. isValidProperty = false;
  474. }
  475. }
  476. else if (azrtti_istypeof<AZ::ScriptPropertyGenericClassArray>(groupProperty))
  477. {
  478. // If this is a GenericClassArray, check if it has an unset element type ID, construct a vanilla copy, and populate it from that
  479. AZ::ScriptPropertyGenericClassArray* groupArrayProperty = azdynamic_cast<AZ::ScriptPropertyGenericClassArray*>(groupProperty);
  480. if (groupArrayProperty->GetElementTypeUuid().IsNull())
  481. {
  482. AZ::ScriptProperty* tempScriptProperty = propertyTable.ConstructScriptProperty(defaultValueIndex, propertyName, restrictToPropertyArrays);
  483. AZ::ScriptPropertyGenericClassArray* tempArrayProperty = azdynamic_cast<AZ::ScriptPropertyGenericClassArray*>(tempScriptProperty);
  484. if (tempArrayProperty)
  485. {
  486. groupArrayProperty->SetElementTypeUuid(tempArrayProperty->GetElementTypeUuid());
  487. }
  488. if(tempScriptProperty)
  489. {
  490. delete tempScriptProperty;
  491. }
  492. }
  493. }
  494. // load all other elements as attributes
  495. if (isValidProperty)
  496. {
  497. if (sdc.InspectTable(propertyIndex, propertyTable)) // restart table inspect
  498. {
  499. const char* attrName = nullptr;
  500. int attrFieldIndex = 0; // We won't use this, it's just for arrays
  501. int attrIndex = 0;
  502. while (propertyTable.InspectNextElement(attrIndex, attrName, attrFieldIndex) && isValidProperty)
  503. {
  504. if (attrName == nullptr)
  505. {
  506. AZ_Warning("Script", false, "LUA Script Error - Malformed Entry in Property Table: %s", propertyName);
  507. isValidProperty = false;
  508. continue; // Skip malformed properties
  509. }
  510. else if (strcmp(attrName, AzFramework::ScriptComponent::DefaultFieldName) == 0)
  511. {
  512. continue; // skip parsed fields
  513. }
  514. else if (strcmp(attrName, DescriptionFieldName) == 0)
  515. {
  516. // Handle 'description' field
  517. if (propertyTable.IsString(attrIndex))
  518. {
  519. const char* value = nullptr;
  520. propertyTable.ReadValue(attrIndex, value);
  521. ei.m_editData.m_description = value;
  522. }
  523. }
  524. else if (strcmp(attrName, UiFieldName) == 0)
  525. {
  526. // Handle 'ui' field
  527. // See: AZ::Edit::UIHandlers
  528. if (propertyTable.IsNumber(attrIndex))
  529. {
  530. AZ::u64 value = 0;
  531. propertyTable.ReadValue(attrIndex, value);
  532. ei.m_editData.m_elementId = aznumeric_cast<AZ::u32>(value);
  533. }
  534. else if (propertyTable.IsString(attrIndex))
  535. {
  536. const char* value = nullptr;
  537. propertyTable.ReadValue(attrIndex, value);
  538. ei.m_editData.m_elementId = AZ::Crc32(value);
  539. }
  540. }
  541. else if (strcmp(attrName, UiOrderValue) == 0)
  542. {
  543. // Handle ui sorting value
  544. if (propertyTable.IsNumber(attrIndex))
  545. {
  546. propertyTable.ReadValue(attrIndex, ei.m_sortOrder);
  547. }
  548. }
  549. else
  550. {
  551. LoadAttribute(propertyTable, attrIndex, attrName, ei.m_editData, group.m_properties.back());
  552. }
  553. }
  554. }
  555. }
  556. }
  557. else
  558. {
  559. // check if we have any elements with a key, otherwise it might be a value array m_property = { true, false }
  560. bool isValueArray = true;
  561. const char* elementName = nullptr;
  562. int elementValueIndex = 0;
  563. int elementArrayIndex = 0;
  564. while (propertyTable.InspectNextElement(elementValueIndex, elementName, elementArrayIndex))
  565. {
  566. if (elementName != nullptr)
  567. {
  568. isValueArray = false;
  569. break;
  570. }
  571. }
  572. // Check first if this is an asset
  573. if (!isValueArray && LoadDefaultAsset(sdc, propertyIndex, propertyName, group, ei))
  574. {
  575. // Load DefaultAsset adds the property if required, do nothing here
  576. }
  577. // Check if this is an entity ref
  578. else if (!isValueArray && LoadDefaultEntityRef(sdc, propertyIndex, propertyName, group, ei))
  579. {
  580. // Load DefaultAsset adds the property if required, do nothing here
  581. }
  582. // If not this must be a group or a user error, we assume group
  583. else if (!isValueArray && sdc.InspectTable(propertyIndex, propertyTable)) // restart table inspect
  584. {
  585. isValidProperty = false;
  586. AzFramework::ScriptPropertyGroup* subGroup = group.GetGroup(propertyName);
  587. if (subGroup == nullptr)
  588. {
  589. group.m_groups.emplace_back();
  590. subGroup = &group.m_groups.back();
  591. subGroup->m_name = propertyName;
  592. }
  593. LoadProperties(propertyTable, *subGroup);
  594. }
  595. else
  596. {
  597. // This case will handle the syntax `Property = { EntityId(), }` of declaring arrays
  598. if (AZ::ScriptProperty* scriptProperty = sdc.ConstructScriptProperty(propertyIndex, propertyName, restrictToPropertyArrays))
  599. {
  600. group.m_properties.emplace_back(scriptProperty);
  601. }
  602. else
  603. {
  604. AZ_Warning("Script", false, "We support only boolean, number, string and registered classes as properties %s!", propertyName);
  605. isValidProperty = false;
  606. }
  607. }
  608. }
  609. }
  610. }
  611. else
  612. {
  613. if (!group.GetProperty(propertyName))
  614. {
  615. // handle value properties
  616. if (AZ::ScriptProperty* scriptProperty = sdc.ConstructScriptProperty(propertyIndex, propertyName, restrictToPropertyArrays))
  617. {
  618. group.m_properties.emplace_back(scriptProperty);
  619. }
  620. else
  621. {
  622. AZ_Warning("Script", false, "We support only boolean, number, string and registered classes as properties %s!", propertyName);
  623. isValidProperty = false;
  624. }
  625. }
  626. }
  627. if (isValidProperty)
  628. {
  629. if (AZ::ScriptProperty* scriptProperty = group.GetProperty(propertyName))
  630. {
  631. // add the attributes to the map of default values
  632. ei.m_uuid = scriptProperty->GetDataTypeUuid();
  633. ei.m_isAttributeOwner = true;
  634. m_dataElements.insert(AZStd::make_pair(scriptProperty->GetDataAddress(), ei));
  635. // Also register to the script property itself, so friendly data can be displayed at its own level.
  636. ei.m_uuid = azrtti_typeid(scriptProperty);
  637. ei.m_isAttributeOwner = false;
  638. m_dataElements.insert(AZStd::make_pair(scriptProperty, ei));
  639. }
  640. }
  641. }
  642. }
  643. void ScriptEditorComponent::RemovedOldProperties(AzFramework::ScriptPropertyGroup& group)
  644. {
  645. for (auto it = group.m_properties.begin(); it != group.m_properties.end();)
  646. {
  647. AZ::ScriptProperty* prop = *it;
  648. if (m_dataElements.find(prop->GetDataAddress()) == m_dataElements.end())
  649. {
  650. delete prop;
  651. it = group.m_properties.erase(it); // we did not find a data element it must an old property
  652. }
  653. else
  654. {
  655. ++it;
  656. }
  657. }
  658. for (auto it = group.m_groups.begin(); it != group.m_groups.end();)
  659. {
  660. AzFramework::ScriptPropertyGroup& subGroup = *it;
  661. RemovedOldProperties(subGroup);
  662. if (subGroup.m_properties.empty() && subGroup.m_groups.empty())
  663. {
  664. it = group.m_groups.erase(it);
  665. }
  666. else
  667. {
  668. ++it;
  669. }
  670. }
  671. }
  672. void ScriptEditorComponent::SortProperties(AzFramework::ScriptPropertyGroup& group)
  673. {
  674. // sort properties
  675. AZStd::sort(group.m_properties.begin(), group.m_properties.end(),
  676. [&](const AZ::ScriptProperty* lhs, const AZ::ScriptProperty* rhs) -> bool
  677. {
  678. auto lhsElementInfoPair = m_dataElements.find(lhs);
  679. auto rhsElementInfoPair = m_dataElements.find(rhs);
  680. AZ_Assert(lhsElementInfoPair != m_dataElements.end() && rhsElementInfoPair != m_dataElements.end(), "We have script properties that have do not have dataElements, this should not be possible!");
  681. if (lhsElementInfoPair->second.m_sortOrder == FLT_MAX && rhsElementInfoPair->second.m_sortOrder == FLT_MAX)
  682. {
  683. // use alphabetical sort
  684. return lhs->m_name < rhs->m_name;
  685. }
  686. else
  687. {
  688. // sort based on the order defined by user
  689. return lhsElementInfoPair->second.m_sortOrder < rhsElementInfoPair->second.m_sortOrder;
  690. }
  691. }
  692. );
  693. // process subgroups (should we sort groups? in general or alphabetically)
  694. for (AzFramework::ScriptPropertyGroup& subGroup : group.m_groups)
  695. {
  696. SortProperties(subGroup);
  697. }
  698. }
  699. void ScriptEditorComponent::LoadScript()
  700. {
  701. LSV_BEGIN(m_scriptComponent.m_context->NativeContext(), 0);
  702. // At this point we're loading the script to populate the properties.
  703. // Disable debugging during this stage as the game is not running yet.
  704. bool pausedBreakpoints = false;
  705. if (m_scriptComponent.m_context->GetDebugContext())
  706. {
  707. m_scriptComponent.m_context->GetDebugContext()->DisconnectHook();
  708. pausedBreakpoints = true;
  709. }
  710. if (m_scriptComponent.LoadInContext())
  711. {
  712. LoadProperties();
  713. }
  714. if (pausedBreakpoints)
  715. {
  716. m_scriptComponent.m_context->GetDebugContext()->ConnectHook();
  717. }
  718. EBUS_EVENT(ToolsApplicationEvents::Bus, InvalidatePropertyDisplay, Refresh_EntireTree);
  719. }
  720. void ScriptEditorComponent::LoadProperties()
  721. {
  722. ClearDataElements();
  723. AZ::ScriptContext* context = m_scriptComponent.m_context;
  724. LSV_BEGIN(context->NativeContext(), -1);
  725. bool success = false;
  726. AZ::ScriptSystemRequestBus::BroadcastResult(success, &AZ::ScriptSystemRequestBus::Events::Load, GetScript(), AZ::k_scriptLoadBinaryOrText, context->GetId());
  727. if (!success)
  728. {
  729. return;
  730. }
  731. AZ::ScriptDataContext stackContext;
  732. if (context->ReadStack(stackContext))
  733. {
  734. // Create reference to table, so that we may call InspectTableCached
  735. int value = lua_gettop(context->NativeContext());
  736. int ref = stackContext.CacheValue(value);
  737. // maintaining the same stack manipulations as in the previous states. Since cache value duplicates the target value
  738. // then creates a reference, vs destructively making something into a reference directly from the stack.
  739. lua_pop(context->NativeContext(),1);
  740. AZ::ScriptDataContext dc;
  741. if (context->InspectTableCached(ref, dc))
  742. {
  743. int valueIndex;
  744. if (dc.PushTableElement(m_scriptComponent.m_properties.m_name.c_str(), &valueIndex) &&
  745. dc.IsTable(valueIndex))
  746. {
  747. AZ::ScriptDataContext propTable;
  748. if (dc.InspectTable(valueIndex, propTable))
  749. {
  750. LoadProperties(propTable, m_scriptComponent.m_properties);
  751. }
  752. }
  753. }
  754. stackContext.ReleaseCached(ref);
  755. }
  756. // Pop the script table
  757. lua_pop(context->NativeContext(), 1);
  758. // TODO: Call this at the end of a session, instead of on load. This will allow
  759. // users to quickly revert back to old properties instead of losing them right away.
  760. // Remove all old properties, every confirmed property will have
  761. // a corresponding Element data
  762. RemovedOldProperties(m_scriptComponent.m_properties);
  763. SortProperties(m_scriptComponent.m_properties);
  764. EBUS_EVENT(ToolsApplicationEvents::Bus, InvalidatePropertyDisplay, Refresh_EntireTree);
  765. }
  766. void ScriptEditorComponent::ClearDataElements()
  767. {
  768. for (auto it = m_dataElements.begin(); it != m_dataElements.end(); ++it)
  769. {
  770. if (it->second.m_isAttributeOwner)
  771. {
  772. it->second.m_editData.ClearAttributes();
  773. }
  774. }
  775. m_dataElements.clear();
  776. // The display tree might still be holding onto pointers to our attributes that we just cleared above, so force a refresh to remove them.
  777. // However, only force the refresh if we have a valid entity. If we don't have an entity, this component isn't currently being shown or
  778. // edited, so a refresh is at best superfluous, and at worst could cause a feedback loop of infinite refreshes.
  779. if (GetEntity())
  780. {
  781. AzToolsFramework::ToolsApplicationEvents::Bus::Broadcast(
  782. &AzToolsFramework::ToolsApplicationEvents::InvalidatePropertyDisplay,
  783. AzToolsFramework::Refresh_EntireTree);
  784. }
  785. }
  786. const AZ::Edit::ElementData* ScriptEditorComponent::GetDataElement(const void* element, const AZ::Uuid& typeUuid) const
  787. {
  788. auto it = m_dataElements.find(element);
  789. if (it != m_dataElements.end())
  790. {
  791. if (it->second.m_uuid == typeUuid)
  792. {
  793. return &it->second.m_editData;
  794. }
  795. }
  796. return nullptr;
  797. }
  798. void ScriptEditorComponent::BuildGameEntity(AZ::Entity* gameEntity)
  799. {
  800. AZ::SerializeContext* context = nullptr;
  801. AZ::ComponentApplicationBus::BroadcastResult(context, &AZ::ComponentApplicationBus::Events::GetSerializeContext);
  802. if (!context)
  803. {
  804. AZ_Error("ScriptEditorComponent", false, "Can't get serialize context from component application.");
  805. return;
  806. }
  807. gameEntity->AddComponent(context->CloneObject(&m_scriptComponent));
  808. }
  809. void ScriptEditorComponent::SetPrimaryAsset(const AZ::Data::AssetId& assetId)
  810. {
  811. AZ::Data::Asset<AZ::ScriptAsset> asset = AZ::Data::AssetManager::Instance().FindOrCreateAsset<AZ::ScriptAsset>(assetId, m_scriptAsset.GetAutoLoadBehavior());
  812. if (asset)
  813. {
  814. SetScript(asset);
  815. ScriptHasChanged();
  816. EBUS_EVENT(AzToolsFramework::ToolsApplicationRequests::Bus, AddDirtyEntity, GetEntityId());
  817. }
  818. }
  819. AZ::u32 ScriptEditorComponent::ScriptHasChanged()
  820. {
  821. AZ::Data::AssetBus::Handler::BusDisconnect();
  822. // Only clear properties and data elements if the asset we're changing to is not the same one we already had set on our scriptComponent
  823. // The only time we shouldn't do this is when someone has set the same script on the component through the editor
  824. if (m_scriptAsset != m_scriptComponent.GetScript())
  825. {
  826. m_scriptComponent.m_properties.Clear();
  827. ClearDataElements();
  828. }
  829. if (m_scriptAsset.GetId().IsValid())
  830. {
  831. SetScript(m_scriptAsset);
  832. AZ::Data::AssetBus::Handler::BusConnect(m_scriptAsset.GetId());
  833. m_scriptAsset.QueueLoad();
  834. }
  835. else
  836. {
  837. SetScript({nullptr, AZ::Data::AssetLoadBehavior::Default});
  838. }
  839. return AZ::Edit::PropertyRefreshLevels::EntireTree;
  840. }
  841. void ScriptEditorComponent::LaunchLuaEditor(const AZ::Data::AssetId& assetId, const AZ::Data::AssetType&)
  842. {
  843. AZStd::string scriptFilename;
  844. if (assetId.IsValid())
  845. {
  846. EBUS_EVENT_RESULT(scriptFilename, AZ::Data::AssetCatalogRequestBus, GetAssetPathById, assetId);
  847. }
  848. EBUS_EVENT(AzToolsFramework::EditorRequests::Bus, LaunchLuaEditor, scriptFilename.c_str());
  849. }
  850. void ScriptEditorComponent::OnAssetReady(AZ::Data::Asset<AZ::Data::AssetData> asset)
  851. {
  852. SetScript(asset);
  853. LoadScript();
  854. }
  855. void ScriptEditorComponent::OnAssetReloaded(AZ::Data::Asset<AZ::Data::AssetData> asset)
  856. {
  857. SetScript(asset);
  858. LoadScript();
  859. }
  860. void ScriptEditorComponent::OnAssetError(AZ::Data::Asset<AZ::Data::AssetData> asset)
  861. {
  862. // only notify for asset errors for the asset we care about.
  863. #if defined(AZ_ENABLE_TRACING)
  864. if ((asset.GetId().IsValid()) && (asset == m_scriptAsset))
  865. {
  866. AZ_Error("Lua Script", false, "Failed to load asset for ScriptComponent: %s", m_scriptAsset.ToString<AZStd::string>().c_str());
  867. }
  868. #else // else if AZ_ENABLE_TRACING is not currently defined...
  869. AZ_UNUSED(asset);
  870. #endif
  871. }
  872. void ScriptEditorComponent::SetScript(const AZ::Data::Asset<AZ::ScriptAsset>& script)
  873. {
  874. m_scriptAsset = script;
  875. m_scriptComponent.SetScript(script);
  876. m_customName = "Lua Script";
  877. if (script)
  878. {
  879. bool outcome = false;
  880. AZStd::string folderFoundIn;
  881. AZ::Data::AssetInfo assetInfo;
  882. AssetSystemRequestBus::BroadcastResult(outcome, &AssetSystemRequestBus::Events::GetSourceInfoBySourceUUID, m_scriptAsset.GetId().m_guid, assetInfo, folderFoundIn);
  883. if (outcome)
  884. {
  885. AZStd::string name;
  886. AzFramework::StringFunc::Path::GetFileName(assetInfo.m_relativePath.c_str(), name);
  887. m_customName += AZStd::string::format(" - %s", name.c_str());
  888. }
  889. }
  890. }
  891. const AZ::Edit::ElementData* ScriptEditorComponent::GetScriptPropertyEditData(const void* handlerPtr, const void* elementPtr, const AZ::Uuid& elementType)
  892. {
  893. const ScriptEditorComponent* owner = reinterpret_cast<const ScriptEditorComponent*>(handlerPtr);
  894. return owner->GetDataElement(elementPtr, elementType);
  895. }
  896. void ScriptEditorComponent::Reflect(AZ::ReflectContext* context)
  897. {
  898. AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
  899. if (serializeContext)
  900. {
  901. // NOTE: We perform the reflection for AZ::ScriptComponent here because we are making use of the editor-only
  902. // PropertyVisibility in the editor reflection.
  903. AzFramework::ScriptComponent::Reflect(context);
  904. serializeContext->Class<ScriptEditorComponent, EditorComponentBase>()
  905. ->Field("ScriptComponent", &ScriptEditorComponent::m_scriptComponent)
  906. ->Field("ScriptAsset", &ScriptEditorComponent::m_scriptAsset);
  907. AZ::EditContext* ec = serializeContext->GetEditContext();
  908. if (ec)
  909. {
  910. ec->Class<ScriptEditorComponent>("Lua Script", "The Lua Script component allows you to add arbitrary Lua logic to an entity in the form of a Lua script")
  911. ->ClassElement(AZ::Edit::ClassElements::EditorData, "")
  912. ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c))
  913. ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("UI", 0x27ff46b0))
  914. ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("CanvasUI", 0xe1e05605))
  915. ->Attribute(AZ::Edit::Attributes::NameLabelOverride, &ScriptEditorComponent::m_customName)
  916. ->Attribute(AZ::Edit::Attributes::AutoExpand, true)
  917. ->Attribute(AZ::Edit::Attributes::Category, "Scripting")
  918. ->Attribute(AZ::Edit::Attributes::Icon, "Icons/Components/LuaScript.svg")
  919. ->Attribute(AZ::Edit::Attributes::PrimaryAssetType, AZ::AzTypeInfo<AZ::ScriptAsset>::Uuid())
  920. ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/Script.png")
  921. ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://docs.o3de.org/docs/user-guide/components/reference/lua-script/")
  922. ->DataElement("AssetRef", &ScriptEditorComponent::m_scriptAsset, "Script", "Which script to use")
  923. ->Attribute(AZ::Edit::Attributes::ChangeNotify, &ScriptEditorComponent::ScriptHasChanged)
  924. ->Attribute("BrowseIcon", ":/stylesheet/img/UI20/browse-edit-select-files.svg")
  925. ->Attribute("EditButton", "")
  926. ->Attribute("EditDescription", "Open in Lua Editor")
  927. ->Attribute("EditCallback", &ScriptEditorComponent::LaunchLuaEditor)
  928. ->DataElement(0, &ScriptEditorComponent::m_scriptComponent, "Script properties", "The script template")
  929. ->SetDynamicEditDataProvider(&ScriptEditorComponent::GetScriptPropertyEditData)
  930. ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
  931. ;
  932. ec->Class<AzFramework::ScriptComponent>("Script Component", "Adding scripting functionality to the entity!")
  933. ->DataElement(0, &AzFramework::ScriptComponent::m_properties, "Properties", "Lua script properties")
  934. ->Attribute(AZ::Edit::Attributes::AutoExpand, true)
  935. ->DataElement(0, &AzFramework::ScriptComponent::m_script, "Asset", "")
  936. ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::Hide)
  937. ->Attribute(AZ::Edit::Attributes::SliceFlags, AZ::Edit::SliceFlags::NotPushable) // Only the editor-component's script asset needs to be slice-pushable.
  938. ;
  939. ec->Class<AzFramework::ScriptPropertyGroup>("Script Property group", "This is a script property group")->
  940. ClassElement(AZ::Edit::ClassElements::EditorData, "ScriptPropertyGroup's class attributes.")->
  941. Attribute(AZ::Edit::Attributes::NameLabelOverride, &AzFramework::ScriptPropertyGroup::m_name)->
  942. Attribute(AZ::Edit::Attributes::AutoExpand, true)->
  943. DataElement(0, &AzFramework::ScriptPropertyGroup::m_properties, "m_properties", "Properties in this property group")->
  944. Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)->
  945. DataElement(0, &AzFramework::ScriptPropertyGroup::m_groups, "m_groups", "Subgroups in this property group")->
  946. Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly);
  947. ec->Class<AZ::ScriptProperty>("Script Property", "Base class for script properties")->
  948. ClassElement(AZ::Edit::ClassElements::EditorData, "ScriptPropertyGroup's class attributes.")->
  949. Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly);
  950. ec->Class<AZ::ScriptPropertyBoolean>("Script Property (bool)", "A script boolean property")->
  951. ClassElement(AZ::Edit::ClassElements::EditorData, "ScriptPropertyGroup's class attributes.")->
  952. Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)->
  953. DataElement(0, &AZ::ScriptPropertyBoolean::m_value, "m_value", "A boolean")->
  954. Attribute(AZ::Edit::Attributes::NameLabelOverride, &AZ::ScriptProperty::m_name);
  955. ec->Class<AZ::ScriptPropertyNumber>("Script Property (number)", "A script number property")->
  956. ClassElement(AZ::Edit::ClassElements::EditorData, "ScriptPropertyGroup's class attributes.")->
  957. Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)->
  958. DataElement(0, &AZ::ScriptPropertyNumber::m_value, "m_value", "A number")->
  959. Attribute(AZ::Edit::Attributes::NameLabelOverride, &AZ::ScriptProperty::m_name);
  960. ec->Class<AZ::ScriptPropertyString>("Script Property (string)", "A script string property")->
  961. ClassElement(AZ::Edit::ClassElements::EditorData, "ScriptPropertyGroup's class attributes.")->
  962. Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)->
  963. DataElement(0, &AZ::ScriptPropertyString::m_value, "m_value", "A string")->
  964. Attribute(AZ::Edit::Attributes::NameLabelOverride, &AZ::ScriptProperty::m_name);
  965. ec->Class<AZ::ScriptPropertyGenericClass>("Script Property (object)", "A script object property")->
  966. ClassElement(AZ::Edit::ClassElements::EditorData, "ScriptPropertyGroup's class attributes.")->
  967. Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)->
  968. DataElement(0, &AZ::ScriptPropertyGenericClass::m_value, "m_value", "An object")->
  969. Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly);
  970. ec->Class<AZ::ScriptPropertyBooleanArray>("Script Property Array(bool)", "A script bool array property")->
  971. ClassElement(AZ::Edit::ClassElements::EditorData, "ScriptPropertyBooleanArray's class attributes.")->
  972. Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)->
  973. DataElement(0, &AZ::ScriptPropertyBooleanArray::m_values, "m_value", "An object")->
  974. Attribute(AZ::Edit::Attributes::NameLabelOverride, &AZ::ScriptProperty::m_name);
  975. ec->Class<AZ::ScriptPropertyNumberArray>("Script Property Array(number)", "A script number array property")->
  976. ClassElement(AZ::Edit::ClassElements::EditorData, "ScriptPropertyNumberArray's class attributes.")->
  977. Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)->
  978. DataElement(0, &AZ::ScriptPropertyNumberArray::m_values, "m_value", "An object")->
  979. Attribute(AZ::Edit::Attributes::NameLabelOverride, &AZ::ScriptProperty::m_name);
  980. ec->Class<AZ::ScriptPropertyStringArray>("Script Property Array(string)", "A script string array property")->
  981. ClassElement(AZ::Edit::ClassElements::EditorData, "ScriptPropertyStringArray's class attributes.")->
  982. Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)->
  983. DataElement(0, &AZ::ScriptPropertyStringArray::m_values, "m_value", "An object")->
  984. Attribute(AZ::Edit::Attributes::NameLabelOverride, &AZ::ScriptProperty::m_name);
  985. ec->Class<AZ::ScriptPropertyGenericClassArray>("Script Property Array(object)", "A script object array property")->
  986. ClassElement(AZ::Edit::ClassElements::EditorData, "ScriptPropertyGenericClassArray's class attributes.")->
  987. Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)->
  988. Attribute(AZ::Edit::Attributes::DynamicElementType, &AZ::ScriptPropertyGenericClassArray::GetElementTypeUuid)->
  989. DataElement(0, &AZ::ScriptPropertyGenericClassArray::m_values, "m_value", "An object")->
  990. ElementAttribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)->
  991. Attribute(AZ::Edit::Attributes::NameLabelOverride, &AZ::ScriptProperty::m_name);
  992. ec->Class<AZ::ScriptPropertyAsset>("Script Property Asset(asset)", "A script asset property")->
  993. ClassElement(AZ::Edit::ClassElements::EditorData, "ScriptPropertyEditorAsset's class attributes.")->
  994. Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)->
  995. DataElement("Asset", &AZ::ScriptPropertyAsset::m_value, "m_value", "An object")->
  996. Attribute(AZ::Edit::Attributes::NameLabelOverride, &AZ::ScriptProperty::m_name);
  997. ec->Class<AZ::ScriptPropertyEntityRef>("Script Property Entity(EntityRef)", "A script entity reference property")->
  998. ClassElement(AZ::Edit::ClassElements::EditorData, "ScriptPropertyEditorEntityRef's class attributes.")->
  999. Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)->
  1000. DataElement("EntityRef", &AZ::ScriptPropertyEntityRef::m_value, "m_entity", "An entity reference")->
  1001. Attribute(AZ::Edit::Attributes::NameLabelOverride, &AZ::ScriptProperty::m_name);
  1002. }
  1003. }
  1004. }
  1005. } // namespace Component
  1006. } // namespace AzToolsFramework