unit.vala 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901
  1. /*
  2. * Copyright (c) 2012-2025 Daniele Bartolini et al.
  3. * SPDX-License-Identifier: GPL-3.0-or-later
  4. */
  5. namespace Crown
  6. {
  7. public struct Unit
  8. {
  9. public static Hashtable _component_registry;
  10. public Database _db;
  11. public Guid _id;
  12. public Unit(Database db, Guid id)
  13. {
  14. _db = db;
  15. _id = id;
  16. }
  17. /// Loads the unit @a name.
  18. public static int load_unit(out Guid prefab_id, Database db, string name)
  19. {
  20. return db.add_from_resource_path(out prefab_id, name + ".unit");
  21. }
  22. public void create_empty()
  23. {
  24. _db.create(_id, OBJECT_TYPE_UNIT);
  25. }
  26. public void create(string? prefab)
  27. {
  28. create_empty();
  29. if (prefab != null)
  30. _db.set_property_string(_id, "prefab", prefab);
  31. }
  32. public Value? get_component_property(Guid component_id, string key, Value? deffault = null)
  33. {
  34. Value? val;
  35. // Search in components
  36. val = _db.get_property(_id, "components");
  37. if (val != null) {
  38. if (((Gee.HashSet<Guid?>)val).contains(component_id))
  39. return _db.get_property(component_id, key, deffault);
  40. }
  41. // Search in modified_components
  42. val = _db.get_property(_id, "modified_components.#" + component_id.to_string() + "." + key);
  43. if (val != null)
  44. return val;
  45. // Search in prefab
  46. val = _db.get_property(_id, "prefab");
  47. if (val != null) {
  48. // Convert prefab path to object ID.
  49. string prefab = (string)val;
  50. Guid prefab_id = GUID_ZERO;
  51. Unit.load_unit(out prefab_id, _db, prefab);
  52. Unit unit = Unit(_db, prefab_id);
  53. return unit.get_component_property(component_id, key, deffault);
  54. }
  55. return deffault;
  56. }
  57. public bool get_component_property_bool(Guid component_id, string key, bool deffault = false)
  58. {
  59. return (bool)get_component_property(component_id, key, deffault);
  60. }
  61. public double get_component_property_double(Guid component_id, string key, double deffault = 0.0)
  62. {
  63. return (double)get_component_property(component_id, key, deffault);
  64. }
  65. public string get_component_property_string(Guid component_id, string key, string deffault = "")
  66. {
  67. return (string)get_component_property(component_id, key, deffault);
  68. }
  69. public Guid get_component_property_guid(Guid component_id, string key, Guid deffault = GUID_ZERO)
  70. {
  71. return (Guid)get_component_property(component_id, key, deffault);
  72. }
  73. public Vector3 get_component_property_vector3(Guid component_id, string key, Vector3 deffault = VECTOR3_ZERO)
  74. {
  75. return (Vector3)get_component_property(component_id, key, deffault);
  76. }
  77. public Quaternion get_component_property_quaternion(Guid component_id, string key, Quaternion deffault = QUATERNION_IDENTITY)
  78. {
  79. return (Quaternion)get_component_property(component_id, key, deffault);
  80. }
  81. public void set_component_property_bool(Guid component_id, string key, bool val)
  82. {
  83. // Search in components
  84. Value? components = _db.get_property(_id, "components");
  85. if (components != null && ((Gee.HashSet<Guid?>)components).contains(component_id)) {
  86. _db.set_property_bool(component_id, key, val);
  87. return;
  88. }
  89. _db.set_property_bool(_id, "modified_components.#" + component_id.to_string() + "." + key, val);
  90. }
  91. public void set_component_property_double(Guid component_id, string key, double val)
  92. {
  93. // Search in components
  94. Value? components = _db.get_property(_id, "components");
  95. if (components != null && ((Gee.HashSet<Guid?>)components).contains(component_id)) {
  96. _db.set_property_double(component_id, key, val);
  97. return;
  98. }
  99. _db.set_property_double(_id, "modified_components.#" + component_id.to_string() + "." + key, val);
  100. }
  101. public void set_component_property_string(Guid component_id, string key, string val)
  102. {
  103. // Search in components
  104. Value? components = _db.get_property(_id, "components");
  105. if (components != null && ((Gee.HashSet<Guid?>)components).contains(component_id)) {
  106. _db.set_property_string(component_id, key, val);
  107. return;
  108. }
  109. _db.set_property_string(_id, "modified_components.#" + component_id.to_string() + "." + key, val);
  110. }
  111. public void set_component_property_guid(Guid component_id, string key, Guid val)
  112. {
  113. // Search in components
  114. Value? components = _db.get_property(_id, "components");
  115. if (components != null && ((Gee.HashSet<Guid?>)components).contains(component_id)) {
  116. _db.set_property_guid(component_id, key, val);
  117. return;
  118. }
  119. _db.set_property_guid(_id, "modified_components.#" + component_id.to_string() + "." + key, val);
  120. }
  121. public void set_component_property_vector3(Guid component_id, string key, Vector3 val)
  122. {
  123. // Search in components
  124. Value? components = _db.get_property(_id, "components");
  125. if (components != null && ((Gee.HashSet<Guid?>)components).contains(component_id)) {
  126. _db.set_property_vector3(component_id, key, val);
  127. return;
  128. }
  129. _db.set_property_vector3(_id, "modified_components.#" + component_id.to_string() + "." + key, val);
  130. }
  131. public void set_component_property_quaternion(Guid component_id, string key, Quaternion val)
  132. {
  133. // Search in components
  134. Value? components = _db.get_property(_id, "components");
  135. if (components != null && ((Gee.HashSet<Guid?>)components).contains(component_id)) {
  136. _db.set_property_quaternion(component_id, key, val);
  137. return;
  138. }
  139. _db.set_property_quaternion(_id, "modified_components.#" + component_id.to_string() + "." + key, val);
  140. }
  141. /// Returns whether the @a unit_id has a component of type @a component_type.
  142. public static bool has_component_static(out Guid component_id, out Guid owner_id, string component_type, Database db, Guid unit_id)
  143. {
  144. Value? val;
  145. component_id = GUID_ZERO;
  146. owner_id = GUID_ZERO;
  147. bool prefab_has_component = false;
  148. // If the component type is found inside the "components" array, the unit has the component
  149. // and it owns it.
  150. val = db.get_property(unit_id, "components");
  151. if (val != null) {
  152. foreach (Guid id in (Gee.HashSet<Guid?>)val) {
  153. if ((string)db.object_type(id) == component_type) {
  154. component_id = id;
  155. owner_id = unit_id;
  156. return true;
  157. }
  158. }
  159. }
  160. // Otherwise, search if any prefab has the component.
  161. val = db.get_property(unit_id, "prefab");
  162. if (val != null) {
  163. // Convert prefab path to object ID.
  164. string prefab = (string)val;
  165. Guid prefab_id = GUID_ZERO;
  166. Unit.load_unit(out prefab_id, db, prefab);
  167. prefab_has_component = has_component_static(out component_id
  168. , out owner_id
  169. , component_type
  170. , db
  171. , prefab_id
  172. );
  173. }
  174. // If the prefab does not have the component, so does this unit.
  175. if (prefab_has_component)
  176. return db.get_property(unit_id, "deleted_components.#" + component_id.to_string()) == null;
  177. component_id = GUID_ZERO;
  178. owner_id = GUID_ZERO;
  179. return false;
  180. }
  181. /// Returns whether the unit has the component_type.
  182. public bool has_component_with_owner(out Guid component_id, out Guid owner_id, string component_type)
  183. {
  184. return Unit.has_component_static(out component_id, out owner_id, component_type, _db, _id);
  185. }
  186. /// Returns whether the unit has the component_type.
  187. public bool has_component(out Guid component_id, string component_type)
  188. {
  189. Guid owner_id;
  190. return has_component_with_owner(out component_id, out owner_id, component_type);
  191. }
  192. public Vector3 local_position()
  193. {
  194. Vector3 position;
  195. Guid component_id;
  196. if (has_component(out component_id, OBJECT_TYPE_TRANSFORM))
  197. position = get_component_property_vector3(component_id, "data.position");
  198. else
  199. position = _db.get_property_vector3(_id, "position");
  200. return position;
  201. }
  202. public Quaternion local_rotation()
  203. {
  204. Quaternion rotation;
  205. Guid component_id;
  206. if (has_component(out component_id, OBJECT_TYPE_TRANSFORM))
  207. rotation = get_component_property_quaternion(component_id, "data.rotation");
  208. else
  209. rotation = _db.get_property_quaternion(_id, "rotation");
  210. return rotation;
  211. }
  212. public Vector3 local_scale()
  213. {
  214. Vector3 scale;
  215. Guid component_id;
  216. if (has_component(out component_id, OBJECT_TYPE_TRANSFORM))
  217. scale = get_component_property_vector3(component_id, "data.scale");
  218. else
  219. scale = _db.get_property_vector3(_id, "scale");
  220. return scale;
  221. }
  222. public void set_local_position(Vector3 position)
  223. {
  224. Guid component_id;
  225. if (has_component(out component_id, OBJECT_TYPE_TRANSFORM))
  226. set_component_property_vector3(component_id, "data.position", position);
  227. else
  228. _db.set_property_vector3(_id, "position", position);
  229. }
  230. public void set_local_rotation(Quaternion rotation)
  231. {
  232. Guid component_id;
  233. if (has_component(out component_id, OBJECT_TYPE_TRANSFORM))
  234. set_component_property_quaternion(component_id, "data.rotation", rotation);
  235. else
  236. _db.set_property_quaternion(_id, "rotation", rotation);
  237. }
  238. public void set_local_scale(Vector3 scale)
  239. {
  240. Guid component_id;
  241. if (has_component(out component_id, OBJECT_TYPE_TRANSFORM))
  242. set_component_property_vector3(component_id, "data.scale", scale);
  243. else
  244. _db.set_property_vector3(_id, "scale", scale);
  245. }
  246. // Adds the @a component_type to the unit and returns its ID.
  247. public Guid add_component_type(string component_type)
  248. {
  249. // Create a new component.
  250. Guid component_id = Guid.new_guid();
  251. _db.create(component_id, component_type);
  252. // Initialize component data based on its type.
  253. if (component_type == OBJECT_TYPE_TRANSFORM) {
  254. _db.set_property_vector3 (component_id, "data.position", VECTOR3_ZERO);
  255. _db.set_property_quaternion(component_id, "data.rotation", QUATERNION_IDENTITY);
  256. _db.set_property_vector3 (component_id, "data.scale", VECTOR3_ONE);
  257. } else if (component_type == OBJECT_TYPE_CAMERA) {
  258. _db.set_property_string(component_id, "data.projection", "perspective");
  259. _db.set_property_double(component_id, "data.fov", MathUtils.rad(45.0));
  260. _db.set_property_double(component_id, "data.far_range", 0.01);
  261. _db.set_property_double(component_id, "data.near_range", 1000.0);
  262. } else if (component_type == OBJECT_TYPE_MESH_RENDERER) {
  263. _db.set_property_string(component_id, "data.mesh_resource", "core/components/noop");
  264. _db.set_property_string(component_id, "data.geometry_name", "Noop");
  265. _db.set_property_string(component_id, "data.material", "core/components/noop");
  266. _db.set_property_bool (component_id, "data.visible", true);
  267. _db.set_property_bool (component_id, "data.cast_shadows", true);
  268. } else if (component_type == OBJECT_TYPE_SPRITE_RENDERER) {
  269. _db.set_property_string(component_id, "data.sprite_resource", "core/components/noop");
  270. _db.set_property_string(component_id, "data.material", "core/components/noop");
  271. _db.set_property_double(component_id, "data.layer", 0);
  272. _db.set_property_double(component_id, "data.depth", 0);
  273. _db.set_property_bool (component_id, "data.visible", true);
  274. } else if (component_type == OBJECT_TYPE_LIGHT) {
  275. _db.set_property_string (component_id, "data.type", "directional");
  276. _db.set_property_double (component_id, "data.range", 1.0);
  277. _db.set_property_double (component_id, "data.intensity", 1.0);
  278. _db.set_property_double (component_id, "data.spot_angle", MathUtils.rad(45.0));
  279. _db.set_property_vector3(component_id, "data.color", VECTOR3_ONE);
  280. _db.set_property_double (component_id, "data.shadow_bias", 0.0001);
  281. _db.set_property_bool (component_id, "data.cast_shadows", true);
  282. } else if (component_type == OBJECT_TYPE_FOG) {
  283. _db.set_property_vector3(component_id, "data.color", Vector3(0.83, 0.83, 0.83));
  284. _db.set_property_double (component_id, "data.density", 0.02);
  285. _db.set_property_double (component_id, "data.range_min", 0.0);
  286. _db.set_property_double (component_id, "data.range_max", 1000.0);
  287. _db.set_property_double (component_id, "data.sun_blend", 0.0);
  288. _db.set_property_bool (component_id, "data.enabled", false);
  289. } else if (component_type == OBJECT_TYPE_GLOBAL_LIGHTING) {
  290. _db.set_property_string (component_id, "data.skydome_map", "core/units/skydome/skydome");
  291. _db.set_property_double (component_id, "data.skydome_intensity", 1.0);
  292. _db.set_property_vector3(component_id, "data.ambient_color", Vector3(0.0, 0.0, 0.0));
  293. } else if (component_type == OBJECT_TYPE_BLOOM) {
  294. _db.set_property_bool (component_id, "data.enabled", true);
  295. _db.set_property_double(component_id, "data.threshold", 0.0);
  296. _db.set_property_double(component_id, "data.weight", 0.05);
  297. _db.set_property_double(component_id, "data.intensity", 1.0);
  298. } else if (component_type == OBJECT_TYPE_TONEMAP) {
  299. _db.set_property_string(component_id, "data.type", "reinhard");
  300. } else if (component_type == OBJECT_TYPE_SCRIPT) {
  301. _db.set_property_string(component_id, "data.script_resource", "core/components/noop");
  302. } else if (component_type == OBJECT_TYPE_COLLIDER) {
  303. _db.set_property_string (component_id, "data.shape", "box");
  304. _db.set_property_string (component_id, "data.source", "mesh"); // "inline" or "mesh"
  305. // if "mesh"
  306. _db.set_property_string (component_id, "data.scene", "core/components/noop");
  307. _db.set_property_string (component_id, "data.name", "Noop");
  308. // if "inline"
  309. _db.set_property_vector3 (component_id, "data.collider_data.position", VECTOR3_ZERO);
  310. _db.set_property_quaternion(component_id, "data.collider_data.rotation", QUATERNION_IDENTITY);
  311. _db.set_property_vector3 (component_id, "data.collider_data.half_extents", VECTOR3_ZERO); // for "box"
  312. _db.set_property_double (component_id, "data.collider_data.radius", 0.0); // for "sphere" and "capsule"
  313. _db.set_property_double (component_id, "data.collider_data.height", 0.0); // for "capsule"
  314. } else if (component_type == OBJECT_TYPE_ACTOR) {
  315. _db.set_property_bool (component_id, "data.lock_translation_x", false);
  316. _db.set_property_bool (component_id, "data.lock_translation_y", false);
  317. _db.set_property_bool (component_id, "data.lock_translation_z", false);
  318. _db.set_property_bool (component_id, "data.lock_rotation_x", false);
  319. _db.set_property_bool (component_id, "data.lock_rotation_y", false);
  320. _db.set_property_bool (component_id, "data.lock_rotation_z", false);
  321. _db.set_property_string(component_id, "data.class", "static");
  322. _db.set_property_double(component_id, "data.mass", 1.0);
  323. _db.set_property_string(component_id, "data.collision_filter", "default");
  324. _db.set_property_string(component_id, "data.material", "default");
  325. } else if (component_type == OBJECT_TYPE_MOVER) {
  326. _db.set_property_double(component_id, "data.height", 1.80);
  327. _db.set_property_double(component_id, "data.radius", 0.50);
  328. _db.set_property_double(component_id, "data.max_slope_angle", MathUtils.rad(45.0f));
  329. _db.set_property_string(component_id, "data.collision_filter", "default");
  330. } else if (component_type == OBJECT_TYPE_ANIMATION_STATE_MACHINE) {
  331. _db.set_property_string(component_id, "data.state_machine_resource", "core/components/noop");
  332. } else {
  333. logw("Unregistered component type `%s`".printf(component_type));
  334. }
  335. _db.add_to_set(_id, "components", component_id);
  336. return component_id;
  337. }
  338. /// Removes the @a component_type from the unit.
  339. public void remove_component_type(string component_type)
  340. {
  341. Guid component_id;
  342. Guid owner_id;
  343. if (has_component_with_owner(out component_id, out owner_id, component_type)) {
  344. if (_id == owner_id) {
  345. _db.remove_from_set(_id, "components", component_id);
  346. } else {
  347. _db.set_property_bool(_id, "deleted_components.#" + component_id.to_string(), false);
  348. // Clean all modified_components keys that matches the deleted component ID.
  349. string[] unit_keys = _db.get_keys(_id);
  350. for (int ii = 0; ii < unit_keys.length; ++ii) {
  351. if (unit_keys[ii].has_prefix("modified_components.#" + component_id.to_string()))
  352. _db.set_property_null(_id, unit_keys[ii]);
  353. }
  354. }
  355. _db.add_restore_point((int)ActionType.DESTROY_OBJECTS, new Guid?[] { _id, component_id });
  356. } else {
  357. logw("The unit has no such component type `%s`".printf(component_type));
  358. }
  359. }
  360. public static void register_component_type(string type, string depends_on)
  361. {
  362. if (_component_registry == null)
  363. _component_registry = new Hashtable();
  364. _component_registry[type] = depends_on;
  365. }
  366. public string? prefab()
  367. {
  368. return _db.has_property(_id, "prefab")
  369. ? _db.get_property_string(_id, "prefab")
  370. : null
  371. ;
  372. }
  373. /// Returns whether the unit has a prefab.
  374. public bool has_prefab()
  375. {
  376. return _db.has_property(_id, "prefab");
  377. }
  378. /// Returns whether the unit is a light unit.
  379. public bool is_light()
  380. {
  381. return has_prefab()
  382. && _db.get_property_string(_id, "prefab") == "core/units/light";
  383. }
  384. /// Returns whether the unit is a camera unit.
  385. public bool is_camera()
  386. {
  387. return has_prefab()
  388. && _db.get_property_string(_id, "prefab") == "core/units/camera";
  389. }
  390. public static int generate_spawn_unit_commands(StringBuilder sb, Guid?[] object_ids, Database db)
  391. {
  392. int i;
  393. if (object_ids.length > 1 && Unit.is_component(object_ids[1], db)) {
  394. for (i = 1; i < object_ids.length; ++i) {
  395. if (!is_component(object_ids[i], db))
  396. break;
  397. Guid unit_id = object_ids[0];
  398. Guid component_id = object_ids[i];
  399. string component_type = db.object_type(component_id);
  400. Unit unit = Unit(db, unit_id);
  401. sb.append("editor_nv, editor_nq, editor_nm = Device.temp_count()");
  402. if (component_type == OBJECT_TYPE_TRANSFORM) {
  403. sb.append(LevelEditorApi.add_tranform_component(unit_id
  404. , component_id
  405. , unit.get_component_property_vector3 (component_id, "data.position")
  406. , unit.get_component_property_quaternion(component_id, "data.rotation")
  407. , unit.get_component_property_vector3 (component_id, "data.scale")
  408. ));
  409. } else if (component_type == OBJECT_TYPE_CAMERA) {
  410. sb.append(LevelEditorApi.add_camera_component(unit_id
  411. , component_id
  412. , unit.get_component_property_string(component_id, "data.projection")
  413. , unit.get_component_property_double(component_id, "data.fov")
  414. , unit.get_component_property_double(component_id, "data.far_range")
  415. , unit.get_component_property_double(component_id, "data.near_range")
  416. ));
  417. } else if (component_type == OBJECT_TYPE_MESH_RENDERER) {
  418. sb.append(LevelEditorApi.add_mesh_renderer_component(unit_id
  419. , component_id
  420. , unit.get_component_property_string(component_id, "data.mesh_resource")
  421. , unit.get_component_property_string(component_id, "data.geometry_name")
  422. , unit.get_component_property_string(component_id, "data.material")
  423. , unit.get_component_property_bool (component_id, "data.visible")
  424. , unit.get_component_property_bool (component_id, "data.cast_shadows", true)
  425. ));
  426. } else if (component_type == OBJECT_TYPE_SPRITE_RENDERER) {
  427. sb.append(LevelEditorApi.add_sprite_renderer_component(unit_id
  428. , component_id
  429. , unit.get_component_property_string(component_id, "data.sprite_resource")
  430. , unit.get_component_property_string(component_id, "data.material")
  431. , unit.get_component_property_double(component_id, "data.layer")
  432. , unit.get_component_property_double(component_id, "data.depth")
  433. , unit.get_component_property_bool (component_id, "data.visible")
  434. , unit.get_component_property_bool (component_id, "data.flip_x")
  435. , unit.get_component_property_bool (component_id, "data.flip_y")
  436. ));
  437. } else if (component_type == OBJECT_TYPE_LIGHT) {
  438. sb.append(LevelEditorApi.add_light_component(unit_id
  439. , component_id
  440. , unit.get_component_property_string (component_id, "data.type")
  441. , unit.get_component_property_double (component_id, "data.range")
  442. , unit.get_component_property_double (component_id, "data.intensity")
  443. , unit.get_component_property_double (component_id, "data.spot_angle")
  444. , unit.get_component_property_vector3(component_id, "data.color")
  445. , unit.get_component_property_double (component_id, "data.shadow_bias", 0.0001)
  446. , unit.get_component_property_bool (component_id, "data.cast_shadows", true)
  447. ));
  448. } else if (component_type == OBJECT_TYPE_FOG) {
  449. sb.append(LevelEditorApi.add_fog_component(unit_id, component_id));
  450. sb.append(LevelEditorApi.set_fog(unit_id
  451. , unit.get_component_property_vector3(component_id, "data.color")
  452. , unit.get_component_property_double (component_id, "data.density")
  453. , unit.get_component_property_double (component_id, "data.range_min")
  454. , unit.get_component_property_double (component_id, "data.range_max")
  455. , unit.get_component_property_double (component_id, "data.sun_blend")
  456. , unit.get_component_property_bool (component_id, "data.enabled")
  457. ));
  458. } else if (component_type == OBJECT_TYPE_GLOBAL_LIGHTING) {
  459. sb.append(LevelEditorApi.add_global_lighting_component(unit_id, component_id));
  460. sb.append(LevelEditorApi.set_global_lighting(unit_id
  461. , unit.get_component_property_string (component_id, "data.skydome_map")
  462. , unit.get_component_property_double (component_id, "data.skydome_intensity")
  463. , unit.get_component_property_vector3(component_id, "data.ambient_color")
  464. ));
  465. } else if (component_type == OBJECT_TYPE_BLOOM) {
  466. sb.append(LevelEditorApi.add_bloom_component(unit_id, component_id));
  467. sb.append(LevelEditorApi.set_bloom(unit_id
  468. , unit.get_component_property_bool (component_id, "data.enabled")
  469. , unit.get_component_property_double(component_id, "data.threshold")
  470. , unit.get_component_property_double(component_id, "data.weight")
  471. , unit.get_component_property_double(component_id, "data.intensity")
  472. ));
  473. } else if (component_type == OBJECT_TYPE_TONEMAP) {
  474. sb.append(LevelEditorApi.add_tonemap_component(unit_id, component_id));
  475. sb.append(LevelEditorApi.set_tonemap(unit_id
  476. , unit.get_component_property_string(component_id, "data.type")
  477. ));
  478. } else if (component_type == OBJECT_TYPE_SCRIPT) {
  479. /*
  480. * sb.append(LevelEditorApi.add_script_component(unit_id
  481. * , component_id
  482. * , unit.get_component_property_string(component_id, "data.script_resource")
  483. * ));
  484. */
  485. } else if (component_type == OBJECT_TYPE_COLLIDER) {
  486. /*
  487. * sb.append(LevelEditorApi.add_collider_component(unit_id
  488. * , component_id
  489. * , unit.get_component_property_string (component_id, "data.shape")
  490. * , unit.get_component_property_string (component_id, "data.source")
  491. * // if "mesh"
  492. * , unit.get_component_property_string (component_id, "data.scene")
  493. * , unit.get_component_property_string (component_id, "data.name")
  494. * // if "inline"
  495. * , unit.get_component_property_vector3 (component_id, "data.collider_data.position")
  496. * , unit.get_component_property_quaternion(component_id, "data.collider_data.rotation")
  497. * , unit.get_component_property_vector3 (component_id, "data.collider_data.half_extents")
  498. * , unit.get_component_property_double (component_id, "data.collider_data.radius")
  499. * , unit.get_component_property_double (component_id, "data.collider_data.height")
  500. * ));
  501. */
  502. } else if (component_type == OBJECT_TYPE_ACTOR) {
  503. /*
  504. * sb.append(LevelEditorApi.add_actor_component(unit_id
  505. * , component_id
  506. * , unit.get_component_property_bool (component_id, "data.lock_translation_x")
  507. * , unit.get_component_property_bool (component_id, "data.lock_translation_y")
  508. * , unit.get_component_property_bool (component_id, "data.lock_translation_z")
  509. * , unit.get_component_property_bool (component_id, "data.lock_rotation_x")
  510. * , unit.get_component_property_bool (component_id, "data.lock_rotation_y")
  511. * , unit.get_component_property_bool (component_id, "data.lock_rotation_z")
  512. * , unit.get_component_property_string(component_id, "data.class")
  513. * , unit.get_component_property_double(component_id, "data.mass")
  514. * , unit.get_component_property_string(component_id, "data.collision_filter")
  515. * , unit.get_component_property_string(component_id, "data.material")
  516. * ));
  517. */
  518. } else if (component_type == OBJECT_TYPE_MOVER) {
  519. sb.append(LevelEditorApi.add_mover_component(unit_id
  520. , component_id
  521. , unit.get_component_property_double(component_id, "data.height")
  522. , unit.get_component_property_double(component_id, "data.radius")
  523. , unit.get_component_property_double(component_id, "data.max_slope_angle")
  524. , unit.get_component_property_string(component_id, "data.collision_filter")
  525. ));
  526. } else if (component_type == OBJECT_TYPE_ANIMATION_STATE_MACHINE) {
  527. /*
  528. * sb.append(LevelEditorApi.add_animation_state_machine_component(unit_id
  529. * , component_id
  530. * , unit.get_component_property_string(component_id, "data.state_machine_resource")
  531. * ));
  532. */
  533. } else {
  534. logw("Unregistered component type `%s`".printf(component_type));
  535. }
  536. sb.append("Device.set_temp_count(editor_nv, editor_nq, editor_nm)");
  537. }
  538. } else {
  539. for (i = 0; i < object_ids.length; ++i) {
  540. if (db.object_type(object_ids[i]) != OBJECT_TYPE_UNIT)
  541. break;
  542. Guid unit_id = object_ids[i];
  543. Unit unit = Unit(db, unit_id);
  544. string? prefab = unit.prefab();
  545. sb.append("editor_nv, editor_nq, editor_nm = Device.temp_count()");
  546. if (prefab != null) {
  547. sb.append(LevelEditorApi.spawn_unit(unit_id
  548. , prefab
  549. , unit.local_position()
  550. , unit.local_rotation()
  551. , unit.local_scale()
  552. ));
  553. generate_change_commands(sb, { unit_id }, db);
  554. } else {
  555. sb.append(LevelEditorApi.spawn_empty_unit(unit_id));
  556. Guid component_id;
  557. if (unit.has_component(out component_id, OBJECT_TYPE_TRANSFORM)) {
  558. string s = LevelEditorApi.add_tranform_component(unit_id
  559. , component_id
  560. , unit.get_component_property_vector3 (component_id, "data.position")
  561. , unit.get_component_property_quaternion(component_id, "data.rotation")
  562. , unit.get_component_property_vector3 (component_id, "data.scale")
  563. );
  564. sb.append(s);
  565. }
  566. if (unit.has_component(out component_id, OBJECT_TYPE_CAMERA)) {
  567. string s = LevelEditorApi.add_camera_component(unit_id
  568. , component_id
  569. , unit.get_component_property_string(component_id, "data.projection")
  570. , unit.get_component_property_double(component_id, "data.fov")
  571. , unit.get_component_property_double(component_id, "data.far_range")
  572. , unit.get_component_property_double(component_id, "data.near_range")
  573. );
  574. sb.append(s);
  575. }
  576. if (unit.has_component(out component_id, OBJECT_TYPE_MESH_RENDERER)) {
  577. string s = LevelEditorApi.add_mesh_renderer_component(unit_id
  578. , component_id
  579. , unit.get_component_property_string(component_id, "data.mesh_resource")
  580. , unit.get_component_property_string(component_id, "data.geometry_name")
  581. , unit.get_component_property_string(component_id, "data.material")
  582. , unit.get_component_property_bool (component_id, "data.visible")
  583. , unit.get_component_property_bool (component_id, "data.cast_shadows", true)
  584. );
  585. sb.append(s);
  586. }
  587. if (unit.has_component(out component_id, OBJECT_TYPE_SPRITE_RENDERER)) {
  588. string s = LevelEditorApi.add_sprite_renderer_component(unit_id
  589. , component_id
  590. , unit.get_component_property_string(component_id, "data.sprite_resource")
  591. , unit.get_component_property_string(component_id, "data.material")
  592. , unit.get_component_property_double(component_id, "data.layer")
  593. , unit.get_component_property_double(component_id, "data.depth")
  594. , unit.get_component_property_bool (component_id, "data.visible")
  595. , unit.get_component_property_bool (component_id, "data.flip_x")
  596. , unit.get_component_property_bool (component_id, "data.flip_y")
  597. );
  598. sb.append(s);
  599. }
  600. if (unit.has_component(out component_id, OBJECT_TYPE_LIGHT)) {
  601. string s = LevelEditorApi.add_light_component(unit_id
  602. , component_id
  603. , unit.get_component_property_string (component_id, "data.type")
  604. , unit.get_component_property_double (component_id, "data.range")
  605. , unit.get_component_property_double (component_id, "data.intensity")
  606. , unit.get_component_property_double (component_id, "data.spot_angle")
  607. , unit.get_component_property_vector3(component_id, "data.color")
  608. , unit.get_component_property_double (component_id, "data.shadow_bias", 0.0001)
  609. , unit.get_component_property_bool (component_id, "data.cast_shadows", true)
  610. );
  611. sb.append(s);
  612. }
  613. if (unit.has_component(out component_id, OBJECT_TYPE_MOVER)) {
  614. sb.append(LevelEditorApi.add_mover_component(unit_id
  615. , component_id
  616. , unit.get_component_property_double(component_id, "data.height")
  617. , unit.get_component_property_double(component_id, "data.radius")
  618. , unit.get_component_property_double(component_id, "data.max_slope_angle")
  619. , unit.get_component_property_string(component_id, "data.collision_filter")
  620. ));
  621. }
  622. if (unit.has_component(out component_id, OBJECT_TYPE_FOG)) {
  623. sb.append(LevelEditorApi.add_fog_component(unit_id, component_id));
  624. sb.append(LevelEditorApi.set_fog(unit_id
  625. , unit.get_component_property_vector3(component_id, "data.color")
  626. , unit.get_component_property_double (component_id, "data.density")
  627. , unit.get_component_property_double (component_id, "data.range_min")
  628. , unit.get_component_property_double (component_id, "data.range_max")
  629. , unit.get_component_property_double (component_id, "data.sun_blend")
  630. , unit.get_component_property_bool (component_id, "data.enabled")
  631. ));
  632. }
  633. if (unit.has_component(out component_id, OBJECT_TYPE_GLOBAL_LIGHTING)) {
  634. sb.append(LevelEditorApi.add_global_lighting_component(unit_id, component_id));
  635. sb.append(LevelEditorApi.set_global_lighting(unit_id
  636. , unit.get_component_property_string (component_id, "data.skydome_map")
  637. , unit.get_component_property_double (component_id, "data.skydome_intensity")
  638. , unit.get_component_property_vector3(component_id, "data.ambient_color")
  639. ));
  640. }
  641. if (unit.has_component(out component_id, OBJECT_TYPE_BLOOM)) {
  642. sb.append(LevelEditorApi.add_bloom_component(unit_id, component_id));
  643. sb.append(LevelEditorApi.set_bloom(unit_id
  644. , unit.get_component_property_bool (component_id, "data.enabled")
  645. , unit.get_component_property_double(component_id, "data.threshold")
  646. , unit.get_component_property_double(component_id, "data.weight")
  647. , unit.get_component_property_double(component_id, "data.intensity")
  648. ));
  649. }
  650. if (unit.has_component(out component_id, OBJECT_TYPE_TONEMAP)) {
  651. sb.append(LevelEditorApi.add_tonemap_component(unit_id, component_id));
  652. sb.append(LevelEditorApi.set_tonemap(unit_id
  653. , unit.get_component_property_string(component_id, "data.type")
  654. ));
  655. }
  656. }
  657. sb.append("Device.set_temp_count(editor_nv, editor_nq, editor_nm)");
  658. }
  659. }
  660. return i;
  661. }
  662. public static int generate_destroy_commands(StringBuilder sb, Guid?[] object_ids, Database db)
  663. {
  664. int i;
  665. if (object_ids.length > 1 && Unit.is_component(object_ids[1], db)) {
  666. for (i = 1; i < object_ids.length; ++i) {
  667. if (!is_component(object_ids[i], db))
  668. break;
  669. Guid unit_id = object_ids[0];
  670. Guid component_id = object_ids[i];
  671. string component_type = db.object_type(component_id);
  672. sb.append(LevelEditorApi.unit_destroy_component_type(unit_id, component_type));
  673. }
  674. } else {
  675. for (i = 0; i < object_ids.length; ++i) {
  676. if (db.object_type(object_ids[i]) != OBJECT_TYPE_UNIT)
  677. break;
  678. sb.append(LevelEditorApi.destroy(object_ids[i]));
  679. }
  680. }
  681. return i;
  682. }
  683. public static int generate_change_commands(StringBuilder sb, Guid?[] object_ids, Database db)
  684. {
  685. int i;
  686. for (i = 0; i < object_ids.length; ++i) {
  687. if (db.object_type(object_ids[i]) != OBJECT_TYPE_UNIT)
  688. break;
  689. Guid unit_id = object_ids[i];
  690. Unit unit = Unit(db, unit_id);
  691. sb.append(LevelEditorApi.move_object(unit_id
  692. , unit.local_position()
  693. , unit.local_rotation()
  694. , unit.local_scale()
  695. ));
  696. _component_registry.foreach((g) => {
  697. string component_type = g.key;
  698. Guid component_id;
  699. if (!unit.has_component(out component_id, component_type))
  700. return true;
  701. sb.append("editor_nv, editor_nq, editor_nm = Device.temp_count()");
  702. if (component_type == OBJECT_TYPE_TRANSFORM) {
  703. sb.append(LevelEditorApi.move_object(unit_id
  704. , unit.get_component_property_vector3 (component_id, "data.position")
  705. , unit.get_component_property_quaternion(component_id, "data.rotation")
  706. , unit.get_component_property_vector3 (component_id, "data.scale")
  707. ));
  708. } else if (component_type == OBJECT_TYPE_CAMERA) {
  709. sb.append(LevelEditorApi.set_camera(unit_id
  710. , unit.get_component_property_string(component_id, "data.projection")
  711. , unit.get_component_property_double(component_id, "data.fov")
  712. , unit.get_component_property_double(component_id, "data.far_range")
  713. , unit.get_component_property_double(component_id, "data.near_range")
  714. ));
  715. } else if (component_type == OBJECT_TYPE_MESH_RENDERER) {
  716. sb.append(LevelEditorApi.set_mesh(unit_id
  717. , unit.get_component_property_string(component_id, "data.mesh_resource")
  718. , unit.get_component_property_string(component_id, "data.geometry_name")
  719. , unit.get_component_property_string(component_id, "data.material")
  720. , unit.get_component_property_bool (component_id, "data.visible")
  721. , unit.get_component_property_bool (component_id, "data.cast_shadows", true)
  722. ));
  723. } else if (component_type == OBJECT_TYPE_SPRITE_RENDERER) {
  724. sb.append(LevelEditorApi.set_sprite(unit_id
  725. , unit.get_component_property_string(component_id, "data.sprite_resource")
  726. , unit.get_component_property_string(component_id, "data.material")
  727. , unit.get_component_property_double(component_id, "data.layer")
  728. , unit.get_component_property_double(component_id, "data.depth")
  729. , unit.get_component_property_bool (component_id, "data.visible")
  730. , unit.get_component_property_bool (component_id, "data.flip_x")
  731. , unit.get_component_property_bool (component_id, "data.flip_y")
  732. ));
  733. } else if (component_type == OBJECT_TYPE_LIGHT) {
  734. sb.append(LevelEditorApi.set_light(unit_id
  735. , unit.get_component_property_string (component_id, "data.type")
  736. , unit.get_component_property_double (component_id, "data.range")
  737. , unit.get_component_property_double (component_id, "data.intensity")
  738. , unit.get_component_property_double (component_id, "data.spot_angle")
  739. , unit.get_component_property_vector3(component_id, "data.color")
  740. , unit.get_component_property_double (component_id, "data.shadow_bias", 0.0001)
  741. , unit.get_component_property_bool (component_id, "data.cast_shadows", true)
  742. ));
  743. } else if (component_type == OBJECT_TYPE_FOG) {
  744. sb.append(LevelEditorApi.set_fog(unit_id
  745. , unit.get_component_property_vector3(component_id, "data.color")
  746. , unit.get_component_property_double (component_id, "data.density")
  747. , unit.get_component_property_double (component_id, "data.range_min")
  748. , unit.get_component_property_double (component_id, "data.range_max")
  749. , unit.get_component_property_double (component_id, "data.sun_blend")
  750. , unit.get_component_property_bool (component_id, "data.enabled")
  751. ));
  752. } else if (component_type == OBJECT_TYPE_GLOBAL_LIGHTING) {
  753. sb.append(LevelEditorApi.set_global_lighting(unit_id
  754. , unit.get_component_property_string (component_id, "data.skydome_map")
  755. , unit.get_component_property_double (component_id, "data.skydome_intensity")
  756. , unit.get_component_property_vector3(component_id, "data.ambient_color")
  757. ));
  758. } else if (component_type == OBJECT_TYPE_BLOOM) {
  759. sb.append(LevelEditorApi.set_bloom(unit_id
  760. , unit.get_component_property_bool (component_id, "data.enabled")
  761. , unit.get_component_property_double(component_id, "data.threshold")
  762. , unit.get_component_property_double(component_id, "data.weight")
  763. , unit.get_component_property_double(component_id, "data.intensity")
  764. ));
  765. } else if (component_type == OBJECT_TYPE_TONEMAP) {
  766. sb.append(LevelEditorApi.set_tonemap(unit_id
  767. , unit.get_component_property_string(component_id, "data.type")
  768. ));
  769. } else if (component_type == OBJECT_TYPE_SCRIPT) {
  770. /* No sync. */
  771. } else if (component_type == OBJECT_TYPE_COLLIDER) {
  772. /* No sync. */
  773. } else if (component_type == OBJECT_TYPE_ACTOR) {
  774. /* No sync. */
  775. } else if (component_type == OBJECT_TYPE_MOVER) {
  776. /* No sync. */
  777. } else if (component_type == OBJECT_TYPE_ANIMATION_STATE_MACHINE) {
  778. /* No sync. */
  779. } else {
  780. logw("Unregistered component type `%s`".printf(component_type));
  781. }
  782. sb.append("Device.set_temp_count(editor_nv, editor_nq, editor_nm)");
  783. return true;
  784. });
  785. }
  786. return i;
  787. }
  788. public static bool is_component(Guid id, Database db)
  789. {
  790. return (db.object_type_flags(StringId64(db.object_type(id))) & ObjectTypeFlags.UNIT_COMPONENT) != 0;
  791. }
  792. public void add_component_type_dependencies(ref Gee.ArrayList<Guid?> components_added, string component_type)
  793. {
  794. Guid dummy;
  795. if (has_component(out dummy, component_type))
  796. return;
  797. string[] component_type_dependencies = ((string)Unit._component_registry[component_type]).split(", ");
  798. foreach (unowned string dependency in component_type_dependencies) {
  799. Guid dependency_component_id;
  800. if (!has_component(out dependency_component_id, dependency))
  801. add_component_type_dependencies(ref components_added, dependency);
  802. }
  803. components_added.add(add_component_type(component_type));
  804. }
  805. }
  806. } /* namespace Crown */