level.vala 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829
  1. /*
  2. * Copyright (c) 2012-2018 Daniele Bartolini and individual contributors.
  3. * License: https://github.com/dbartolini/crown/blob/master/LICENSE
  4. */
  5. using GLib;
  6. using Gee;
  7. namespace Crown
  8. {
  9. /// Manages objects in a level.
  10. public class Level
  11. {
  12. public Project _project;
  13. // Engine connections
  14. public ConsoleClient _client;
  15. // Data
  16. public Database _db;
  17. public Database _prefabs;
  18. public Gee.HashSet<string> _loaded_prefabs;
  19. public Gee.ArrayList<Guid?> _selection;
  20. public uint _num_units;
  21. public uint _num_sounds;
  22. public string _filename;
  23. // Signals
  24. public signal void selection_changed(Gee.ArrayList<Guid?> selection);
  25. public signal void object_editor_name_changed(Guid object_id, string name);
  26. public Level(Database db, ConsoleClient client, Project project)
  27. {
  28. _project = project;
  29. // Engine connections
  30. _client = client;
  31. // Data
  32. _db = db;
  33. _db.undo_redo.connect(undo_redo_action);
  34. _prefabs = new Database();
  35. _loaded_prefabs = new Gee.HashSet<string>();
  36. _selection = new Gee.ArrayList<Guid?>();
  37. reset();
  38. }
  39. /// Resets the level
  40. public void reset()
  41. {
  42. _db.reset();
  43. _prefabs.reset();
  44. _loaded_prefabs.clear();
  45. _selection.clear();
  46. selection_changed(_selection);
  47. _num_units = 0;
  48. _num_sounds = 0;
  49. _filename = null;
  50. }
  51. /// Loads the level from @a path.
  52. public void load(string path)
  53. {
  54. reset();
  55. _db.load(path);
  56. _filename = path;
  57. }
  58. /// Saves the level to @a path.
  59. public void save(string path)
  60. {
  61. _db.save(path);
  62. _filename = path;
  63. }
  64. /// Loads the empty level template.
  65. public void load_empty_level()
  66. {
  67. load(_project.toolchain_dir() + "/" + "core/editors/levels/empty.level");
  68. _filename = null;
  69. }
  70. public void spawn_unit(Guid id, string name, Vector3 pos, Quaternion rot, Vector3 scl)
  71. {
  72. on_unit_spawned(id, name, pos, rot, scl);
  73. send_spawn_units(new Guid[] { id });
  74. }
  75. public void destroy_objects(Guid[] ids)
  76. {
  77. Guid[] units = {};
  78. Guid[] sounds = {};
  79. foreach (Guid id in ids)
  80. {
  81. if (is_unit(id))
  82. units += id;
  83. else if (is_sound(id))
  84. sounds += id;
  85. }
  86. if (units.length > 0)
  87. {
  88. _db.add_restore_point((int)ActionType.DESTROY_UNIT, units);
  89. foreach (Guid id in units)
  90. {
  91. _db.remove_from_set(GUID_ZERO, "units", id);
  92. _db.destroy(id);
  93. }
  94. }
  95. if (sounds.length > 0)
  96. {
  97. _db.add_restore_point((int)ActionType.DESTROY_SOUND, sounds);
  98. foreach (Guid id in sounds)
  99. {
  100. _db.remove_from_set(GUID_ZERO, "sounds", id);
  101. _db.destroy(id);
  102. }
  103. }
  104. send_destroy_objects(ids);
  105. }
  106. public void move_selected_objects(Vector3 pos, Quaternion rot, Vector3 scl)
  107. {
  108. if (_selection.size == 0)
  109. return;
  110. Guid id = _selection.last();
  111. on_move_objects(new Guid[] { id }, new Vector3[] { pos }, new Quaternion[] { rot }, new Vector3[] { scl });
  112. send_move_objects(new Guid[] { id }, new Vector3[] { pos }, new Quaternion[] { rot }, new Vector3[] { scl });
  113. }
  114. public void duplicate_selected_objects()
  115. {
  116. if (_selection.size > 0)
  117. {
  118. Guid[] ids = new Guid[_selection.size];
  119. // FIXME
  120. {
  121. Guid?[] tmp = _selection.to_array();
  122. for (int i = 0; i < tmp.length; ++i)
  123. ids[i] = tmp[i];
  124. }
  125. Guid[] new_ids = new Guid[ids.length];
  126. for (int i = 0; i < new_ids.length; ++i)
  127. new_ids[i] = Guid.new_guid();
  128. duplicate_objects(ids, new_ids);
  129. }
  130. }
  131. public void destroy_selected_objects()
  132. {
  133. Guid[] ids = new Guid[_selection.size];
  134. // FIXME
  135. {
  136. Guid?[] tmp = _selection.to_array();
  137. for (int i = 0; i < tmp.length; ++i)
  138. ids[i] = tmp[i];
  139. }
  140. _selection.clear();
  141. destroy_objects(ids);
  142. }
  143. public void duplicate_objects(Guid[] ids, Guid[] new_ids)
  144. {
  145. _db.add_restore_point((int)ActionType.DUPLICATE_OBJECTS, new_ids);
  146. for (int i = 0; i < ids.length; ++i)
  147. {
  148. _db.duplicate(ids[i], new_ids[i]);
  149. if (is_unit(ids[i]))
  150. {
  151. _db.add_to_set(GUID_ZERO, "units", new_ids[i]);
  152. }
  153. else if (is_sound(ids[i]))
  154. {
  155. _db.add_to_set(GUID_ZERO, "sounds", new_ids[i]);
  156. }
  157. }
  158. send_spawn_objects(new_ids);
  159. }
  160. public void on_unit_spawned(Guid id, string name, Vector3 pos, Quaternion rot, Vector3 scl)
  161. {
  162. load_prefab(name);
  163. _db.add_restore_point((int)ActionType.SPAWN_UNIT, new Guid[] { id });
  164. _db.create(id);
  165. _db.set_property_string(id, "editor.name", "unit_%04u".printf(_num_units++));
  166. _db.set_property_string(id, "prefab", name);
  167. Guid transform_id = GUID_ZERO;
  168. Unit unit = new Unit(_db, id, _prefabs);
  169. if (unit.has_component("transform", ref transform_id))
  170. {
  171. unit.set_component_property_vector3 (transform_id, "data.position", pos);
  172. unit.set_component_property_quaternion(transform_id, "data.rotation", rot);
  173. unit.set_component_property_vector3 (transform_id, "data.scale", scl);
  174. unit.set_component_property_string (transform_id, "type", "transform");
  175. }
  176. else
  177. {
  178. _db.set_property_vector3 (id, "position", pos);
  179. _db.set_property_quaternion(id, "rotation", rot);
  180. _db.set_property_vector3 (id, "scale", scl);
  181. }
  182. _db.add_to_set(GUID_ZERO, "units", id);
  183. }
  184. public void on_sound_spawned(Guid id, string name, Vector3 pos, Quaternion rot, Vector3 scl, double range, double volume, bool loop)
  185. {
  186. _db.add_restore_point((int)ActionType.SPAWN_SOUND, new Guid[] { id });
  187. _db.create(id);
  188. _db.set_property_string (id, "editor.name", "sound_%04u".printf(_num_sounds++));
  189. _db.set_property_vector3 (id, "position", pos);
  190. _db.set_property_quaternion(id, "rotation", rot);
  191. _db.set_property_string (id, "name", name);
  192. _db.set_property_double (id, "range", range);
  193. _db.set_property_double (id, "volume", volume);
  194. _db.set_property_bool (id, "loop", loop);
  195. _db.add_to_set(GUID_ZERO, "sounds", id);
  196. }
  197. public void on_move_objects(Guid[] ids, Vector3[] positions, Quaternion[] rotations, Vector3[] scales)
  198. {
  199. _db.add_restore_point((int)ActionType.MOVE_OBJECTS, ids);
  200. for (int i = 0; i < ids.length; ++i)
  201. {
  202. Guid id = ids[i];
  203. Vector3 pos = positions[i];
  204. Quaternion rot = rotations[i];
  205. Vector3 scl = scales[i];
  206. if (is_unit(id))
  207. {
  208. Guid transform_id = GUID_ZERO;
  209. Unit unit = new Unit(_db, id, _prefabs);
  210. if (unit.has_component("transform", ref transform_id))
  211. {
  212. unit.set_component_property_vector3 (transform_id, "data.position", pos);
  213. unit.set_component_property_quaternion(transform_id, "data.rotation", rot);
  214. unit.set_component_property_vector3 (transform_id, "data.scale", scl);
  215. }
  216. else
  217. {
  218. _db.set_property_vector3 (id, "position", pos);
  219. _db.set_property_quaternion(id, "rotation", rot);
  220. _db.set_property_vector3 (id, "scale", scl);
  221. }
  222. }
  223. else if (is_sound(id))
  224. {
  225. _db.set_property_vector3 (id, "position", pos);
  226. _db.set_property_quaternion(id, "rotation", rot);
  227. }
  228. }
  229. // FIXME: Hack to force update the properties view
  230. selection_changed(_selection);
  231. }
  232. public void on_selection(Guid[] ids)
  233. {
  234. _selection.clear();
  235. foreach (Guid id in ids)
  236. _selection.add(id);
  237. selection_changed(_selection);
  238. }
  239. public void selection_set(Guid[] ids)
  240. {
  241. _selection.clear();
  242. for (int i = 0; i < ids.length; ++i)
  243. _selection.add(ids[i]);
  244. _client.send_script(LevelEditorApi.selection_set(ids));
  245. selection_changed(_selection);
  246. }
  247. public void set_light(Guid unit_id, Guid component_id, string type, double range, double intensity, double spot_angle, Vector3 color)
  248. {
  249. _db.add_restore_point((int)ActionType.SET_LIGHT, new Guid[] { unit_id });
  250. Unit unit = new Unit(_db, unit_id, _prefabs);
  251. unit.set_component_property_string (component_id, "data.type", type);
  252. unit.set_component_property_double (component_id, "data.range", range);
  253. unit.set_component_property_double (component_id, "data.intensity", intensity);
  254. unit.set_component_property_double (component_id, "data.spot_angle", spot_angle);
  255. unit.set_component_property_vector3(component_id, "data.color", color);
  256. unit.set_component_property_string (component_id, "type", "light");
  257. _client.send_script(LevelEditorApi.set_light(unit_id, type, range, intensity, spot_angle, color));
  258. }
  259. public void set_mesh(Guid unit_id, Guid component_id, string mesh_resource, string geometry, string material, bool visible)
  260. {
  261. _db.add_restore_point((int)ActionType.SET_MESH, new Guid[] { unit_id });
  262. Unit unit = new Unit(_db, unit_id, _prefabs);
  263. unit.set_component_property_string(component_id, "data.mesh_resource", mesh_resource);
  264. unit.set_component_property_string(component_id, "data.geometry_name", geometry);
  265. unit.set_component_property_string(component_id, "data.material", material);
  266. unit.set_component_property_bool (component_id, "data.visible", visible);
  267. unit.set_component_property_string(component_id, "type", "mesh_renderer");
  268. _client.send_script(LevelEditorApi.set_mesh(unit_id, 0 /*instance_id*/, material, visible));
  269. }
  270. public void set_sprite(Guid unit_id, Guid component_id, double layer, double depth, string material, string sprite_resource, bool visible)
  271. {
  272. _db.add_restore_point((int)ActionType.SET_SPRITE, new Guid[] { unit_id });
  273. Unit unit = new Unit(_db, unit_id, _prefabs);
  274. unit.set_component_property_double(component_id, "data.layer", layer);
  275. unit.set_component_property_double(component_id, "data.depth", depth);
  276. unit.set_component_property_string(component_id, "data.material", material);
  277. unit.set_component_property_string(component_id, "data.sprite_resource", sprite_resource);
  278. unit.set_component_property_bool (component_id, "data.visible", visible);
  279. unit.set_component_property_string(component_id, "type", "sprite_renderer");
  280. _client.send_script(LevelEditorApi.set_sprite(unit_id, material, layer, depth, visible));
  281. }
  282. public void set_camera(Guid unit_id, Guid component_id, string projection, double fov, double near_range, double far_range)
  283. {
  284. _db.add_restore_point((int)ActionType.SET_CAMERA, new Guid[] { unit_id });
  285. Unit unit = new Unit(_db, unit_id, _prefabs);
  286. unit.set_component_property_string(component_id, "data.projection", projection);
  287. unit.set_component_property_double(component_id, "data.fov", fov);
  288. unit.set_component_property_double(component_id, "data.near_range", near_range);
  289. unit.set_component_property_double(component_id, "data.far_range", far_range);
  290. unit.set_component_property_string(component_id, "type", "camera");
  291. _client.send_script(LevelEditorApi.set_camera(unit_id, projection, fov, near_range, far_range));
  292. }
  293. public void set_collider(Guid unit_id, Guid component_id, string shape, string scene, string name)
  294. {
  295. _db.add_restore_point((int)ActionType.SET_COLLIDER, new Guid[] { unit_id });
  296. Unit unit = new Unit(_db, unit_id, _prefabs);
  297. unit.set_component_property_string(component_id, "data.shape", shape);
  298. unit.set_component_property_string(component_id, "data.scene", scene);
  299. unit.set_component_property_string(component_id, "data.name", name);
  300. unit.set_component_property_string(component_id, "type", "collider");
  301. // No synchronization
  302. }
  303. public void set_actor(Guid unit_id, Guid component_id, string class, string collision_filter, string material, double mass)
  304. {
  305. _db.add_restore_point((int)ActionType.SET_ACTOR, new Guid[] { unit_id });
  306. Unit unit = new Unit(_db, unit_id, _prefabs);
  307. unit.set_component_property_string(component_id, "data.class", class);
  308. unit.set_component_property_string(component_id, "data.collision_filter", collision_filter);
  309. unit.set_component_property_string(component_id, "data.material", material);
  310. unit.set_component_property_double(component_id, "data.mass", mass);
  311. unit.set_component_property_bool (component_id, "data.lock_rotation_x", (bool)unit.get_component_property_bool(component_id, "data.lock_rotation_x"));
  312. unit.set_component_property_bool (component_id, "data.lock_rotation_y", (bool)unit.get_component_property_bool(component_id, "data.lock_rotation_y"));
  313. unit.set_component_property_bool (component_id, "data.lock_rotation_z", (bool)unit.get_component_property_bool(component_id, "data.lock_rotation_z"));
  314. unit.set_component_property_bool (component_id, "data.lock_translation_x", (bool)unit.get_component_property_bool(component_id, "data.lock_translation_x"));
  315. unit.set_component_property_bool (component_id, "data.lock_translation_y", (bool)unit.get_component_property_bool(component_id, "data.lock_translation_y"));
  316. unit.set_component_property_bool (component_id, "data.lock_translation_z", (bool)unit.get_component_property_bool(component_id, "data.lock_translation_z"));
  317. unit.set_component_property_string(component_id, "type", "actor");
  318. // No synchronization
  319. }
  320. public void set_sound(Guid sound_id, string name, double range, double volume, bool loop)
  321. {
  322. _db.add_restore_point((int)ActionType.SET_SOUND, new Guid[] { sound_id });
  323. _db.set_property_string(sound_id, "name", name);
  324. _db.set_property_double(sound_id, "range", range);
  325. _db.set_property_double(sound_id, "volume", volume);
  326. _db.set_property_bool (sound_id, "loop", loop);
  327. _client.send_script(LevelEditorApi.set_sound_range(sound_id, range));
  328. }
  329. public string object_editor_name(Guid object_id)
  330. {
  331. if (_db.has_property(object_id, "editor.name"))
  332. return _db.get_property_string(object_id, "editor.name");
  333. else
  334. return "<unnamed>";
  335. }
  336. public void object_set_editor_name(Guid object_id, string name)
  337. {
  338. _db.add_restore_point((int)ActionType.OBJECT_SET_EDITOR_NAME, new Guid[] { object_id });
  339. _db.set_property_string(object_id, "editor.name", name);
  340. object_editor_name_changed(object_id, name);
  341. }
  342. private void send_spawn_units(Guid[] ids)
  343. {
  344. StringBuilder sb = new StringBuilder();
  345. generate_spawn_unit_commands(ids, sb);
  346. _client.send_script(sb.str);
  347. }
  348. private void send_spawn_sounds(Guid[] ids)
  349. {
  350. StringBuilder sb = new StringBuilder();
  351. generate_spawn_sound_commands(ids, sb);
  352. _client.send_script(sb.str);
  353. }
  354. private void send_spawn_objects(Guid[] ids)
  355. {
  356. StringBuilder sb = new StringBuilder();
  357. for (int i = 0; i < ids.length; ++i)
  358. {
  359. if (is_unit(ids[i]))
  360. {
  361. generate_spawn_unit_commands(new Guid[] { ids[i] }, sb);
  362. }
  363. else if (is_sound(ids[i]))
  364. {
  365. generate_spawn_sound_commands(new Guid[] { ids[i] }, sb);
  366. }
  367. }
  368. _client.send_script(sb.str);
  369. }
  370. private void send_destroy_objects(Guid[] ids)
  371. {
  372. StringBuilder sb = new StringBuilder();
  373. foreach (Guid id in ids)
  374. sb.append(LevelEditorApi.destroy(id));
  375. _client.send_script(sb.str);
  376. }
  377. private void send_move_objects(Guid[] ids, Vector3[] positions, Quaternion[] rotations, Vector3[] scales)
  378. {
  379. StringBuilder sb = new StringBuilder();
  380. for (int i = 0; i < ids.length; ++i)
  381. sb.append(LevelEditorApi.move_object(ids[i], positions[i], rotations[i], scales[i]));
  382. _client.send_script(sb.str);
  383. }
  384. public void send_level()
  385. {
  386. HashSet<Guid?> units = _db.get_property_set(GUID_ZERO, "units", new HashSet<Guid?>());
  387. HashSet<Guid?> sounds = _db.get_property_set(GUID_ZERO, "sounds", new HashSet<Guid?>());
  388. Guid[] unit_ids = new Guid[units.size];
  389. Guid[] sound_ids = new Guid[sounds.size];
  390. // FIXME
  391. {
  392. Guid?[] tmp = units.to_array();
  393. for (int i = 0; i < tmp.length; ++i)
  394. unit_ids[i] = tmp[i];
  395. }
  396. // FIXME
  397. {
  398. Guid?[] tmp = sounds.to_array();
  399. for (int i = 0; i < tmp.length; ++i)
  400. sound_ids[i] = tmp[i];
  401. }
  402. StringBuilder sb = new StringBuilder();
  403. sb.append(LevelEditorApi.reset());
  404. generate_spawn_unit_commands(unit_ids, sb);
  405. generate_spawn_sound_commands(sound_ids, sb);
  406. _client.send_script(sb.str);
  407. }
  408. /// <summary>
  409. /// Loads the prefab name into the database of prefabs.
  410. /// </summary>
  411. private void load_prefab(string name)
  412. {
  413. if (_loaded_prefabs.contains(name))
  414. return;
  415. Database prefab_db = new Database();
  416. // Try to load from toolchain directory first
  417. File file = File.new_for_path(_project.toolchain_dir() + "/" + name + ".unit");
  418. if (file.query_exists())
  419. prefab_db.load(file.get_path());
  420. else
  421. prefab_db.load(_project.source_dir() + "/" + name + ".unit");
  422. // Recursively load all sub-prefabs
  423. Value? prefab = prefab_db.get_property(GUID_ZERO, "prefab");
  424. if (prefab != null)
  425. load_prefab((string)prefab);
  426. prefab_db.copy_to(_prefabs, name);
  427. _loaded_prefabs.add(name);
  428. }
  429. private void generate_spawn_unit_commands(Guid[] unit_ids, StringBuilder sb)
  430. {
  431. foreach (Guid unit_id in unit_ids)
  432. {
  433. Unit unit = new Unit(_db, unit_id, _prefabs);
  434. if (unit.has_prefab())
  435. load_prefab(_db.get_property_string(unit_id, "prefab"));
  436. sb.append(LevelEditorApi.spawn_empty_unit(unit_id));
  437. Guid component_id = GUID_ZERO;
  438. if (unit.has_component("transform", ref component_id))
  439. {
  440. string s = LevelEditorApi.add_tranform_component(unit_id
  441. , component_id
  442. , unit.get_component_property_vector3 (component_id, "data.position")
  443. , unit.get_component_property_quaternion(component_id, "data.rotation")
  444. , unit.get_component_property_vector3 (component_id, "data.scale")
  445. );
  446. sb.append(s);
  447. }
  448. if (unit.has_component("mesh_renderer", ref component_id))
  449. {
  450. string s = LevelEditorApi.add_mesh_component(unit_id
  451. , component_id
  452. , unit.get_component_property_string(component_id, "data.mesh_resource")
  453. , unit.get_component_property_string(component_id, "data.geometry_name")
  454. , unit.get_component_property_string(component_id, "data.material")
  455. , unit.get_component_property_bool (component_id, "data.visible")
  456. );
  457. sb.append(s);
  458. }
  459. if (unit.has_component("sprite_renderer", ref component_id))
  460. {
  461. string s = LevelEditorApi.add_sprite_component(unit_id
  462. , component_id
  463. , unit.get_component_property_string(component_id, "data.sprite_resource")
  464. , unit.get_component_property_string(component_id, "data.material")
  465. , unit.get_component_property_double(component_id, "data.layer")
  466. , unit.get_component_property_double(component_id, "data.depth")
  467. , unit.get_component_property_bool (component_id, "data.visible")
  468. );
  469. sb.append(s);
  470. }
  471. if (unit.has_component("light", ref component_id))
  472. {
  473. string s = LevelEditorApi.add_light_component(unit_id
  474. , component_id
  475. , unit.get_component_property_string (component_id, "data.type")
  476. , unit.get_component_property_double (component_id, "data.range")
  477. , unit.get_component_property_double (component_id, "data.intensity")
  478. , unit.get_component_property_double (component_id, "data.spot_angle")
  479. , unit.get_component_property_vector3(component_id, "data.color")
  480. );
  481. sb.append(s);
  482. }
  483. if (unit.has_component("camera", ref component_id))
  484. {
  485. string s = LevelEditorApi.add_camera_component(unit_id
  486. , component_id
  487. , unit.get_component_property_string(component_id, "data.projection")
  488. , unit.get_component_property_double(component_id, "data.fov")
  489. , unit.get_component_property_double(component_id, "data.far_range")
  490. , unit.get_component_property_double(component_id, "data.near_range")
  491. );
  492. sb.append(s);
  493. }
  494. }
  495. }
  496. private void generate_spawn_sound_commands(Guid[] sound_ids, StringBuilder sb)
  497. {
  498. foreach (Guid sound_id in sound_ids)
  499. {
  500. string s = LevelEditorApi.spawn_sound(sound_id
  501. , _db.get_property_string (sound_id, "name")
  502. , _db.get_property_vector3 (sound_id, "position")
  503. , _db.get_property_quaternion(sound_id, "rotation")
  504. , _db.get_property_double (sound_id, "range")
  505. , _db.get_property_double (sound_id, "volume")
  506. , _db.get_property_bool (sound_id, "loop")
  507. );
  508. sb.append(s);
  509. }
  510. }
  511. private void undo_redo_action(bool undo, int id, Guid[] data)
  512. {
  513. switch (id)
  514. {
  515. case (int)ActionType.SPAWN_UNIT:
  516. {
  517. if (undo)
  518. send_destroy_objects(data);
  519. else
  520. send_spawn_units(data);
  521. }
  522. break;
  523. case (int)ActionType.DESTROY_UNIT:
  524. {
  525. if (undo)
  526. send_spawn_units(data);
  527. else
  528. send_destroy_objects(data);
  529. }
  530. break;
  531. case (int)ActionType.SPAWN_SOUND:
  532. {
  533. if (undo)
  534. send_destroy_objects(data);
  535. else
  536. send_spawn_sounds(data);
  537. }
  538. break;
  539. case (int)ActionType.DESTROY_SOUND:
  540. {
  541. if (undo)
  542. send_spawn_sounds(data);
  543. else
  544. send_destroy_objects(data);
  545. }
  546. break;
  547. case (int)ActionType.MOVE_OBJECTS:
  548. {
  549. Guid[] ids = data;
  550. Vector3[] positions = new Vector3[ids.length];
  551. Quaternion[] rotations = new Quaternion[ids.length];
  552. Vector3[] scales = new Vector3[ids.length];
  553. for (int i = 0; i < ids.length; ++i)
  554. {
  555. if (is_unit(ids[i]))
  556. {
  557. Guid unit_id = ids[i];
  558. Guid transform_id = GUID_ZERO;
  559. Unit unit = new Unit(_db, unit_id, _prefabs);
  560. if (unit.has_component("transform", ref transform_id))
  561. {
  562. positions[i] = unit.get_component_property_vector3 (transform_id, "data.position");
  563. rotations[i] = unit.get_component_property_quaternion(transform_id, "data.rotation");
  564. scales[i] = unit.get_component_property_vector3 (transform_id, "data.scale");
  565. }
  566. else
  567. {
  568. positions[i] = _db.get_property_vector3 (unit_id, "position");
  569. rotations[i] = _db.get_property_quaternion(unit_id, "rotation");
  570. scales[i] = _db.get_property_vector3 (unit_id, "scale");
  571. }
  572. }
  573. else if (is_sound(ids[i]))
  574. {
  575. Guid sound_id = ids[i];
  576. positions[i] = _db.get_property_vector3 (sound_id, "position");
  577. rotations[i] = _db.get_property_quaternion(sound_id, "rotation");
  578. scales[i] = Vector3(1.0, 1.0, 1.0);
  579. }
  580. else
  581. {
  582. assert(false);
  583. }
  584. }
  585. send_move_objects(ids, positions, rotations, scales);
  586. // FIXME: Hack to force update the properties view
  587. selection_changed(_selection);
  588. }
  589. break;
  590. case (int)ActionType.DUPLICATE_OBJECTS:
  591. {
  592. Guid[] new_ids = data;
  593. if (undo)
  594. send_destroy_objects(new_ids);
  595. else
  596. send_spawn_objects(new_ids);
  597. }
  598. break;
  599. case (int)ActionType.OBJECT_SET_EDITOR_NAME:
  600. object_editor_name_changed(data[0], object_editor_name(data[0]));
  601. break;
  602. case (int)ActionType.SET_LIGHT:
  603. {
  604. Guid unit_id = data[0];
  605. Guid component_id = GUID_ZERO;
  606. Unit unit = new Unit(_db, unit_id, _prefabs);
  607. unit.has_component("light", ref component_id);
  608. _client.send_script(LevelEditorApi.set_light(unit_id
  609. , unit.get_component_property_string (component_id, "data.type")
  610. , unit.get_component_property_double (component_id, "data.range")
  611. , unit.get_component_property_double (component_id, "data.intensity")
  612. , unit.get_component_property_double (component_id, "data.spot_angle")
  613. , unit.get_component_property_vector3(component_id, "data.color")
  614. ));
  615. // FIXME: Hack to force update the properties view
  616. selection_changed(_selection);
  617. }
  618. break;
  619. case (int)ActionType.SET_MESH:
  620. {
  621. Guid unit_id = data[0];
  622. Guid component_id = GUID_ZERO;
  623. Unit unit = new Unit(_db, unit_id, _prefabs);
  624. unit.has_component("mesh_renderer", ref component_id);
  625. _client.send_script(LevelEditorApi.set_mesh(unit_id
  626. , 0/*instance_id*/
  627. , unit.get_component_property_string(component_id, "data.material")
  628. , unit.get_component_property_bool (component_id, "data.visible")
  629. ));
  630. // FIXME: Hack to force update the properties view
  631. selection_changed(_selection);
  632. }
  633. break;
  634. case (int)ActionType.SET_SPRITE:
  635. {
  636. Guid unit_id = data[0];
  637. Guid component_id = GUID_ZERO;
  638. Unit unit = new Unit(_db, unit_id, _prefabs);
  639. unit.has_component("sprite_renderer", ref component_id);
  640. _client.send_script(LevelEditorApi.set_sprite(unit_id
  641. , unit.get_component_property_string(component_id, "data.material")
  642. , unit.get_component_property_double(component_id, "data.layer")
  643. , unit.get_component_property_double(component_id, "data.depth")
  644. , unit.get_component_property_bool (component_id, "data.visible")
  645. ));
  646. // FIXME: Hack to force update the properties view
  647. selection_changed(_selection);
  648. }
  649. break;
  650. case (int)ActionType.SET_CAMERA:
  651. {
  652. Guid unit_id = data[0];
  653. Guid component_id = GUID_ZERO;
  654. Unit unit = new Unit(_db, unit_id, _prefabs);
  655. unit.has_component("camera", ref component_id);
  656. _client.send_script(LevelEditorApi.set_camera(unit_id
  657. , unit.get_component_property_string(component_id, "data.projection")
  658. , unit.get_component_property_double(component_id, "data.fov")
  659. , unit.get_component_property_double(component_id, "data.near_range")
  660. , unit.get_component_property_double(component_id, "data.far_range")
  661. ));
  662. // FIXME: Hack to force update the properties view
  663. selection_changed(_selection);
  664. }
  665. break;
  666. case (int)ActionType.SET_COLLIDER:
  667. {
  668. // FIXME: Hack to force update the properties view
  669. selection_changed(_selection);
  670. }
  671. break;
  672. case (int)ActionType.SET_ACTOR:
  673. {
  674. // FIXME: Hack to force update the properties view
  675. selection_changed(_selection);
  676. }
  677. break;
  678. case (int)ActionType.SET_SOUND:
  679. {
  680. Guid sound_id = data[0];
  681. _client.send_script(LevelEditorApi.set_sound_range(sound_id
  682. , _db.get_property_double(sound_id, "range")
  683. ));
  684. // FIXME: Hack to force update the properties view
  685. selection_changed(_selection);
  686. }
  687. break;
  688. default:
  689. stdout.printf("Unknown undo/redo action: %d\n", id);
  690. assert(false);
  691. break;
  692. }
  693. }
  694. public bool is_unit(Guid id)
  695. {
  696. return _db.get_property_set(GUID_ZERO, "units", new HashSet<Guid?>()).contains(id);
  697. }
  698. public bool is_sound(Guid id)
  699. {
  700. return _db.get_property_set(GUID_ZERO, "sounds", new HashSet<Guid?>()).contains(id);
  701. }
  702. }
  703. }