level.vala 20 KB

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