level_editor.vala 44 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486
  1. /*
  2. * Copyright (c) 2012-2017 Daniele Bartolini and individual contributors.
  3. * License: https://github.com/dbartolini/crown/blob/master/LICENSE
  4. */
  5. using Gdk; // Pixbuf
  6. using Gee;
  7. using Gtk;
  8. namespace Crown
  9. {
  10. public enum ToolType
  11. {
  12. PLACE,
  13. MOVE,
  14. ROTATE,
  15. SCALE
  16. }
  17. public enum SnapMode
  18. {
  19. RELATIVE,
  20. ABSOLUTE
  21. }
  22. public enum ReferenceSystem
  23. {
  24. LOCAL,
  25. WORLD
  26. }
  27. public enum StartGame
  28. {
  29. NORMAL,
  30. TEST
  31. }
  32. public class LevelEditor : Gtk.Window
  33. {
  34. // Editor state
  35. private double _grid_size;
  36. private double _rotation_snap;
  37. private bool _show_grid;
  38. private bool _snap_to_grid;
  39. private bool _debug_render_world;
  40. private bool _debug_physics_world;
  41. private ToolType _tool_type;
  42. private SnapMode _snap_mode;
  43. private ReferenceSystem _reference_system;
  44. // Engine connections
  45. private GLib.Subprocess _compiler_process;
  46. private GLib.Subprocess _engine_process;
  47. private GLib.Subprocess _game_process;
  48. private ConsoleClient _compiler;
  49. private ConsoleClient _engine;
  50. private ConsoleClient _game;
  51. // Level data
  52. private Project _project;
  53. private Database _database;
  54. private Level _level;
  55. private string _level_filename;
  56. private DataCompiler _data_compiler;
  57. // Widgets
  58. private ConsoleView _console_view;
  59. private EngineView _engine_view;
  60. private LevelTreeView _level_treeview;
  61. private LevelLayersTreeView _level_layers_treeview;
  62. private PropertiesView _properties_view;
  63. private PreferencesDialog _preferences_dialog;
  64. private ResourceBrowser _resource_browser;
  65. private Gtk.Alignment _alignment_engine;
  66. private Gtk.Alignment _alignment_level_tree_view;
  67. private Gtk.Alignment _alignment_properties_view;
  68. private Gtk.ActionGroup _action_group;
  69. private Gtk.UIManager _ui_manager;
  70. private Gtk.Toolbar _toolbar;
  71. private Gtk.Paned _pane_left;
  72. private Gtk.Paned _pane_right;
  73. private Gtk.Notebook _notebook_left;
  74. private Gtk.Notebook _notebook_right;
  75. private Gtk.Box _vbox;
  76. private Gtk.FileFilter _file_filter;
  77. const Gtk.ActionEntry[] action_entries =
  78. {
  79. { "menu-file", null, "_File", null, null, null },
  80. { "new", null, "New", "<ctrl>N", null, on_new },
  81. { "open", null, "Open...", "<ctrl>O", null, on_open },
  82. { "save", null, "Save", "<ctrl>S", null, on_save },
  83. { "save-as", null, "Save As...", "<shift><ctrl>S", null, on_save_as },
  84. { "import", null, "Import", null, null, null },
  85. { "import-sprites", null, "Sprites...", null, null, on_import_sprites },
  86. { "import-meshes", null, "Meshes...", null, null, on_import_meshes },
  87. { "import-sounds", null, "Sounds...", null, null, on_import_sounds },
  88. { "import-textures", null, "Textures...", null, null, on_import_textures },
  89. { "preferences", null, "Preferences", null, null, on_preferences },
  90. { "quit", null, "Quit", "<ctrl>Q", null, on_quit },
  91. { "menu-edit", null, "_Edit", null, null, null },
  92. { "undo", null, "Undo", "<ctrl>Z", null, on_undo },
  93. { "redo", null, "Redo", "<shift><ctrl>Z", null, on_redo },
  94. { "duplicate", null, "Duplicate", "<ctrl>D", null, on_duplicate },
  95. { "delete", null, "Delete", "<ctrl>K", null, on_delete },
  96. { "menu-grid", null, "Grid", null, null, null },
  97. { "grid-custom", null, "Custom", "<ctrl>G", null, on_custom_grid },
  98. { "menu-rotation-snap", null, "Rotation Snap", null, null, null },
  99. { "rotation-snap-custom", null, "Custom", "<ctrl>H", null, on_rotation_snap },
  100. { "menu-create", null, "Create", null, null, null },
  101. { "menu-primitives", null, "Primitives", null, null, null },
  102. { "primitive-cube", null, "Cube", null, null, on_create_cube },
  103. { "primitive-sphere", null, "Sphere", null, null, on_create_sphere },
  104. { "primitive-cone", null, "Cone", null, null, on_create_cone },
  105. { "primitive-cylinder", null, "Cylinder", null, null, on_create_cylinder },
  106. { "primitive-plane", null, "Plane", null, null, on_create_plane },
  107. { "camera", null, "Camera", null, null, on_create_camera },
  108. { "light", null, "Light", null, null, on_create_light },
  109. { "sound-source", null, "Sound Source", null, null, on_create_sound_source },
  110. { "menu-camera", null, "Camera", null, null, null },
  111. { "camera-view-perspective", null, "Perspective", "KP_5", null, on_camera_view_perspective },
  112. { "camera-view-front", null, "View Front", "KP_1", null, on_camera_view_front },
  113. { "camera-view-back", null, "View Back", "<ctrl>KP_1", null, on_camera_view_back },
  114. { "camera-view-right", null, "View Right", "KP_3", null, on_camera_view_right },
  115. { "camera-view-left", null, "View Left", "<ctrl>KP_3", null, on_camera_view_left },
  116. { "camera-view-top", null, "View Top", "KP_7", null, on_camera_view_top },
  117. { "camera-view-bottom", null, "View Bottom", "<ctrl>KP_7", null, on_camera_view_bottom },
  118. { "menu-engine", null, "En_gine", null, null, null },
  119. { "menu-view", null, "View", null, null, null },
  120. { "resource-browser", null, "Resource Browser", "<ctrl>P", null, on_resource_browser },
  121. { "restart", null, "_Restart", null, null, on_engine_restart },
  122. { "reload-lua", null, "Reload Lua", "F7", null, on_reload_lua },
  123. { "menu-run", null, "_Run", null, null, null },
  124. { "test-level", "run", "Test Level", "F5", "Test Level", on_test_level },
  125. { "run-game", null, "Run Game", null, null, on_run_game },
  126. { "menu-help", null, "Help", null, null, null },
  127. { "manual", null, "Manual", "F1", null, on_manual },
  128. { "report-issue", null, "Report an Issue", null, null, on_report_issue },
  129. { "open-last-log", null, "Open last.log", null, null, on_open_last_log },
  130. { "about", null, "About", null, null, on_about }
  131. };
  132. const Gtk.RadioActionEntry[] grid_entries =
  133. {
  134. { "grid-0.1", null, "0.1m", null, null, 10 },
  135. { "grid-0.2", null, "0.2m", null, null, 20 },
  136. { "grid-0.5", null, "0.5m", null, null, 50 },
  137. { "grid-1", null, "1m", null, null, 100 },
  138. { "grid-2", null, "2m", null, null, 200 },
  139. { "grid-5", null, "5m", null, null, 500 }
  140. };
  141. const RadioActionEntry[] rotation_snap_entries =
  142. {
  143. { "rotation-snap-1", null, "1°", null, null, 1 },
  144. { "rotation-snap-15", null, "15°", null, null, 15 },
  145. { "rotation-snap-30", null, "30°", null, null, 30 },
  146. { "rotation-snap-45", null, "45°", null, null, 45 },
  147. { "rotation-snap-90", null, "90°", null, null, 90 },
  148. { "rotation-snap-180", null, "180°", null, null, 180 }
  149. };
  150. const RadioActionEntry[] tool_entries =
  151. {
  152. { "place", "tool-place", "Place", null, "Place", (int)ToolType.PLACE },
  153. { "move", "tool-move", "Move", null, "Move", (int)ToolType.MOVE },
  154. { "rotate", "tool-rotate", "Rotate", null, "Rotate", (int)ToolType.ROTATE },
  155. { "scale", "tool-scale", "Scale", null, "Scale", (int)ToolType.SCALE }
  156. };
  157. const RadioActionEntry[] snap_mode_entries =
  158. {
  159. { "snap-relative", "reference-local", "Relative Snap", null, "Relative Snap", (int)SnapMode.RELATIVE },
  160. { "snap-absolute", "reference-world", "Absolute Snap", null, "Absolute Snap", (int)SnapMode.ABSOLUTE }
  161. };
  162. const RadioActionEntry[] reference_system_entries =
  163. {
  164. { "reference-system-local", "axis-local", "Local Axis", null, "Local Axis", (int)ReferenceSystem.LOCAL },
  165. { "reference-system-world", "axis-world", "World Axis", null, "World Axis", (int)ReferenceSystem.WORLD }
  166. };
  167. const ToggleActionEntry[] snap_to_entries =
  168. {
  169. { "snap-to-grid", "snap-to-grid", "Snap To Grid", "<ctrl>U", "Snap To Grid", on_snap_to_grid, true },
  170. { "grid-show", null, "Show Grid", null, "Show Grid", on_show_grid, true }
  171. };
  172. const ToggleActionEntry[] view_entries =
  173. {
  174. { "debug-render-world", null, "Debug Render World", null, null, on_debug_render_world, false },
  175. { "debug-physics-world", null, "Debug Physics World", null, null, on_debug_physics_world, false }
  176. };
  177. public LevelEditor(Project project, Database database, Level level, ConsoleClient compiler, ConsoleClient engine, ConsoleClient game)
  178. {
  179. this.title = "Level Editor";
  180. // Editor state
  181. _grid_size = 1.0;
  182. _rotation_snap = 15.0;
  183. _show_grid = true;
  184. _snap_to_grid = true;
  185. _debug_render_world = false;
  186. _debug_physics_world = false;
  187. _tool_type = ToolType.MOVE;
  188. _snap_mode = SnapMode.RELATIVE;
  189. _reference_system = ReferenceSystem.LOCAL;
  190. // Engine connections
  191. _compiler_process = null;
  192. _engine_process = null;
  193. _game_process = null;
  194. _compiler = compiler;
  195. _compiler.connected.connect(on_compiler_connected);
  196. _compiler.disconnected.connect(on_compiler_disconnected);
  197. _compiler.message_received.connect(on_message_received);
  198. _engine = engine;
  199. _engine.connected.connect(on_engine_connected);
  200. _engine.disconnected.connect(on_engine_disconnected);
  201. _engine.message_received.connect(on_message_received);
  202. _game = game;
  203. _game.connected.connect(on_game_connected);
  204. _game.disconnected.connect(on_game_disconnected);
  205. _game.message_received.connect(on_message_received);
  206. // Level data
  207. _project = project;
  208. _database = database;
  209. _level = level;
  210. _level_filename = null;
  211. _data_compiler = new DataCompiler(_compiler);
  212. // Widgets
  213. _console_view = new ConsoleView(_engine, _project);
  214. _level_treeview = new LevelTreeView(_database, _level);
  215. _level_layers_treeview = new LevelLayersTreeView(_database, _level);
  216. _properties_view = new PropertiesView(_level);
  217. _alignment_engine = new Gtk.Alignment(0, 0, 1, 1);
  218. _alignment_level_tree_view = new Gtk.Alignment(0, 0, 1, 1);
  219. _alignment_properties_view = new Gtk.Alignment(0, 0, 1, 1);
  220. _alignment_engine.add(new StartingCompiler());
  221. _alignment_level_tree_view.add(new StartingCompiler());
  222. _alignment_properties_view.add(new StartingCompiler());
  223. _action_group = new Gtk.ActionGroup("group");
  224. _action_group.add_actions(action_entries, this);
  225. _action_group.add_radio_actions(grid_entries, (int)(_grid_size*100.0), this.on_grid_changed);
  226. _action_group.add_radio_actions(rotation_snap_entries, (int)_rotation_snap, this.on_rotation_snap_changed);
  227. _action_group.add_radio_actions(tool_entries, (int)_tool_type, on_tool_changed);
  228. _action_group.add_radio_actions(snap_mode_entries, (int)_snap_mode, on_snap_mode_changed);
  229. _action_group.add_radio_actions(reference_system_entries, (int)_reference_system, on_reference_system_changed);
  230. _action_group.add_toggle_actions(snap_to_entries, this);
  231. _action_group.add_toggle_actions(view_entries, this);
  232. _ui_manager = new UIManager();
  233. try
  234. {
  235. _ui_manager.add_ui_from_resource("/org/crown/level_editor/level_editor.xml");
  236. _ui_manager.insert_action_group(_action_group, 0);
  237. add_accel_group(_ui_manager.get_accel_group());
  238. }
  239. catch (Error e)
  240. {
  241. error(e.message);
  242. }
  243. _toolbar = _ui_manager.get_widget("/toolbar") as Toolbar;
  244. _toolbar.set_icon_size(Gtk.IconSize.SMALL_TOOLBAR);
  245. _toolbar.set_style(Gtk.ToolbarStyle.ICONS);
  246. _pane_left = new Gtk.Paned(Gtk.Orientation.VERTICAL);
  247. _pane_left.pack1(_alignment_engine, true, true);
  248. _pane_left.pack2(_console_view, true, true);
  249. Gtk.Box vb = new Gtk.Box(Gtk.Orientation.VERTICAL, 0);
  250. vb.pack_start(_toolbar, false, false, 0);
  251. vb.pack_start(_pane_left, true, true, 0);
  252. _notebook_right = new Notebook();
  253. _notebook_right.show_border = false;
  254. _notebook_right.append_page(_level_treeview, new Gtk.Image.from_icon_name("level-tree", IconSize.SMALL_TOOLBAR));
  255. _notebook_right.append_page(_level_layers_treeview, new Gtk.Image.from_icon_name("level-layers", IconSize.SMALL_TOOLBAR));
  256. Gtk.Paned rb = new Gtk.Paned(Gtk.Orientation.VERTICAL);
  257. rb.pack1(_alignment_level_tree_view, true, true);
  258. rb.pack2(_alignment_properties_view, true, true);
  259. _pane_right = new Gtk.Paned(Gtk.Orientation.HORIZONTAL);
  260. _pane_right.pack1(vb, true, false);
  261. _pane_right.pack2(rb, true, false);
  262. _notebook_left = new Notebook();
  263. _notebook_left.show_border = false;
  264. _notebook_left.append_page(_pane_right, new Gtk.Label("Level"));
  265. MenuBar menu = (MenuBar)_ui_manager.get_widget("/menubar");
  266. _vbox = new Gtk.Box(Gtk.Orientation.VERTICAL, 0);
  267. _vbox.pack_start(menu, false, false, 0);
  268. _vbox.pack_start(_notebook_left, true, true, 0);
  269. _file_filter = new FileFilter();
  270. _file_filter.set_filter_name("Level (*.level)");
  271. _file_filter.add_pattern("*.level");
  272. _resource_browser = new ResourceBrowser(_project);
  273. _resource_browser.relative_to = _toolbar;
  274. _resource_browser.resource_selected.connect(on_resource_browser_resource_selected);
  275. _resource_browser.delete_event.connect(() => { _resource_browser.hide(); return true; });
  276. _resource_browser.modal = true;
  277. // Save level once every 5 minutes.
  278. GLib.Timeout.add_seconds(5*3600, save_timeout);
  279. this.destroy.connect(this.on_destroy);
  280. this.delete_event.connect(this.on_delete_event);
  281. this.key_press_event.connect(this.on_key_press);
  282. this.key_release_event.connect(this.on_key_release);
  283. this.add(_vbox);
  284. this.maximize();
  285. this.show_all();
  286. start_compiler();
  287. }
  288. private bool on_key_press(Gdk.EventKey ev)
  289. {
  290. if (ev.keyval == Gdk.Key.Control_L)
  291. _engine.send_script(LevelEditorApi.key_down("left_ctrl"));
  292. else if (ev.keyval == Gdk.Key.Shift_L)
  293. _engine.send_script(LevelEditorApi.key_down("left_shift"));
  294. return false;
  295. }
  296. private bool on_key_release(Gdk.EventKey ev)
  297. {
  298. if (ev.keyval == Gdk.Key.Control_L)
  299. _engine.send_script(LevelEditorApi.key_up("left_ctrl"));
  300. else if (ev.keyval == Gdk.Key.Shift_L)
  301. _engine.send_script(LevelEditorApi.key_up("left_shift"));
  302. return false;
  303. }
  304. private void on_resource_browser_resource_selected(string type, string name)
  305. {
  306. _engine.send_script(LevelEditorApi.set_placeable(type, name));
  307. _action_group.get_action("place").activate();
  308. }
  309. private void on_compiler_connected()
  310. {
  311. _console_view.logi("Editor", "Compiler connected");
  312. _compiler.receive_async();
  313. }
  314. private void on_compiler_disconnected()
  315. {
  316. _console_view.logi("Editor", "Compiler disconnected");
  317. }
  318. private void on_engine_connected()
  319. {
  320. _console_view.logi("Editor", "Engine connected");
  321. _engine.receive_async();
  322. }
  323. private void on_engine_disconnected()
  324. {
  325. _console_view.logi("Editor", "Engine disconnected");
  326. }
  327. private void on_game_connected()
  328. {
  329. _console_view.logi("Editor", "Game connected");
  330. _game.receive_async();
  331. }
  332. private void on_game_disconnected()
  333. {
  334. _console_view.logi("Editor", "Game disconnected");
  335. _project.delete_level_editor_test_level();
  336. }
  337. private void on_message_received(ConsoleClient client, uint8[] json)
  338. {
  339. try
  340. {
  341. Hashtable msg = JSON.decode(json) as Hashtable;
  342. string msg_type = msg["type"] as string;
  343. if (msg_type == "message")
  344. {
  345. _console_view.log((string)msg["system"], (string)msg["message"], (string)msg["severity"]);
  346. }
  347. else if (msg_type == "add_file")
  348. {
  349. string path = (string)msg["path"];
  350. _project.add_file(path);
  351. }
  352. else if (msg_type == "remove_file")
  353. {
  354. string path = (string)msg["path"];
  355. _project.remove_file(path);
  356. }
  357. else if (msg_type == "compile")
  358. {
  359. Guid id = Guid.parse((string)msg["id"]);
  360. if (msg.has_key("start"))
  361. {
  362. // FIXME
  363. }
  364. else if (msg.has_key("success"))
  365. {
  366. _data_compiler.finished((bool)msg["success"]);
  367. }
  368. }
  369. else if (msg_type == "unit_spawned")
  370. {
  371. string id = (string) msg["id"];
  372. string name = (string) msg["name"];
  373. ArrayList<Value?> pos = (ArrayList<Value?>)msg["position"];
  374. ArrayList<Value?> rot = (ArrayList<Value?>)msg["rotation"];
  375. ArrayList<Value?> scl = (ArrayList<Value?>)msg["scale"];
  376. _level.on_unit_spawned(Guid.parse(id)
  377. , name
  378. , Vector3.from_array(pos)
  379. , Quaternion.from_array(rot)
  380. , Vector3.from_array(scl)
  381. );
  382. }
  383. else if (msg_type == "sound_spawned")
  384. {
  385. string id = (string) msg["id"];
  386. string name = (string) msg["name"];
  387. ArrayList<Value?> pos = (ArrayList<Value?>)msg["position"];
  388. ArrayList<Value?> rot = (ArrayList<Value?>)msg["rotation"];
  389. ArrayList<Value?> scl = (ArrayList<Value?>)msg["scale"];
  390. double range = (double) msg["range"];
  391. double volume = (double) msg["volume"];
  392. bool loop = (bool) msg["loop"];
  393. _level.on_sound_spawned(Guid.parse(id)
  394. , name
  395. , Vector3.from_array(pos)
  396. , Quaternion.from_array(rot)
  397. , Vector3.from_array(scl)
  398. , range
  399. , volume
  400. , loop
  401. );
  402. }
  403. else if (msg_type == "move_objects")
  404. {
  405. Hashtable ids = (Hashtable)msg["ids"];
  406. Hashtable new_positions = (Hashtable)msg["new_positions"];
  407. Hashtable new_rotations = (Hashtable)msg["new_rotations"];
  408. Hashtable new_scales = (Hashtable)msg["new_scales"];
  409. ArrayList<string> keys = new ArrayList<string>.wrap(ids.keys.to_array());
  410. keys.sort(Gee.Functions.get_compare_func_for(typeof(string)));
  411. Guid[] n_ids = new Guid[keys.size];
  412. Vector3[] n_positions = new Vector3[keys.size];
  413. Quaternion[] n_rotations = new Quaternion[keys.size];
  414. Vector3[] n_scales = new Vector3[keys.size];
  415. for (int i = 0; i < keys.size; ++i)
  416. {
  417. string k = keys[i];
  418. n_ids[i] = Guid.parse((string)ids[k]);
  419. n_positions[i] = Vector3.from_array((ArrayList<Value?>)(new_positions[k]));
  420. n_rotations[i] = Quaternion.from_array((ArrayList<Value?>)new_rotations[k]);
  421. n_scales[i] = Vector3.from_array((ArrayList<Value?>)new_scales[k]);
  422. }
  423. _level.on_move_objects(n_ids, n_positions, n_rotations, n_scales);
  424. }
  425. else if (msg_type == "selection")
  426. {
  427. Hashtable objects = (Hashtable)msg["objects"];
  428. ArrayList<string> keys = new ArrayList<string>.wrap(objects.keys.to_array());
  429. keys.sort(Gee.Functions.get_compare_func_for(typeof(string)));
  430. Guid[] ids = new Guid[keys.size];
  431. for (int i = 0; i < keys.size; ++i)
  432. {
  433. string k = keys[i];
  434. ids[i] = Guid.parse((string)objects[k]);
  435. }
  436. _level.on_selection(ids);
  437. }
  438. else if (msg_type == "error")
  439. {
  440. _console_view.loge("Editor", "Error: " + (string)msg["message"]);
  441. }
  442. else
  443. {
  444. _console_view.loge("Editor", "Unknown message type: " + msg_type);
  445. }
  446. }
  447. catch (Error e)
  448. {
  449. _console_view.loge("Editor", e.message);
  450. }
  451. // Receive next message
  452. client.receive_async();
  453. }
  454. private void send_state()
  455. {
  456. StringBuilder sb = new StringBuilder();
  457. sb.append(LevelEditorApi.set_grid_size(_grid_size));
  458. sb.append(LevelEditorApi.set_rotation_snap(_rotation_snap));
  459. sb.append(LevelEditorApi.enable_show_grid(_show_grid));
  460. sb.append(LevelEditorApi.enable_snap_to_grid(_snap_to_grid));
  461. sb.append(LevelEditorApi.enable_debug_render_world(_debug_render_world));
  462. sb.append(LevelEditorApi.enable_debug_physics_world(_debug_physics_world));
  463. sb.append(LevelEditorApi.set_tool_type(_tool_type));
  464. sb.append(LevelEditorApi.set_snap_mode(_snap_mode));
  465. sb.append(LevelEditorApi.set_reference_system(_reference_system));
  466. _engine.send_script(sb.str);
  467. }
  468. private bool on_button_press(EventButton ev)
  469. {
  470. // Prevent accelerators to step on camera's toes
  471. remove_accel_group(_ui_manager.get_accel_group());
  472. return true;
  473. }
  474. private bool on_button_release(EventButton ev)
  475. {
  476. add_accel_group(_ui_manager.get_accel_group());
  477. return true;
  478. }
  479. private void start_compiler()
  480. {
  481. string args[] =
  482. {
  483. ENGINE_EXE,
  484. "--source-dir", _project.source_dir(),
  485. "--map-source-dir", "core", _project.toolchain_dir(),
  486. "--server",
  487. "--wait-console",
  488. null
  489. };
  490. GLib.SubprocessLauncher sl = new GLib.SubprocessLauncher(SubprocessFlags.NONE);
  491. sl.set_cwd(ENGINE_DIR);
  492. try
  493. {
  494. _compiler_process = sl.spawnv(args);
  495. }
  496. catch (Error e)
  497. {
  498. _console_view.loge("Editor", e.message);
  499. }
  500. for (int tries = 0; !_compiler.is_connected() && tries < 5; ++tries)
  501. {
  502. _compiler.connect("127.0.0.1", CROWN_DEFAULT_SERVER_PORT);
  503. GLib.Thread.usleep(100*1000);
  504. }
  505. _data_compiler.compile.begin(_project.data_dir(), _project.platform(), (obj, res) => {
  506. if (_data_compiler.compile.end(res))
  507. {
  508. if (_engine_view != null)
  509. return;
  510. _engine_view = new EngineView(_engine);
  511. _engine_view.realized.connect(on_engine_view_realized);
  512. _engine_view.button_press_event.connect(on_button_press);
  513. _engine_view.button_release_event.connect(on_button_release);
  514. _alignment_engine.remove(_alignment_engine.get_child());
  515. _alignment_level_tree_view.remove(_alignment_level_tree_view.get_child());
  516. _alignment_properties_view.remove(_alignment_properties_view.get_child());
  517. _alignment_engine.add(_engine_view);
  518. _alignment_level_tree_view.add(_notebook_right);
  519. _alignment_properties_view.add(_properties_view);
  520. _alignment_engine.show_all();
  521. _alignment_level_tree_view.show_all();
  522. _alignment_properties_view.show_all();
  523. }
  524. });
  525. }
  526. private void stop_compiler()
  527. {
  528. _compiler.close();
  529. if (_compiler_process != null)
  530. {
  531. _compiler_process.force_exit();
  532. _compiler_process.wait();
  533. }
  534. }
  535. private void start_engine(uint window_xid)
  536. {
  537. string args[] =
  538. {
  539. ENGINE_EXE,
  540. "--data-dir", _project.data_dir(),
  541. "--boot-dir", LEVEL_EDITOR_BOOT_DIR,
  542. "--parent-window", window_xid.to_string(),
  543. "--wait-console",
  544. null
  545. };
  546. GLib.SubprocessLauncher sl = new GLib.SubprocessLauncher(SubprocessFlags.NONE);
  547. sl.set_cwd(ENGINE_DIR);
  548. try
  549. {
  550. _engine_process = sl.spawnv(args);
  551. }
  552. catch (Error e)
  553. {
  554. _console_view.loge("Editor", e.message);
  555. }
  556. for (int tries = 0; !_engine.is_connected() && tries < 5; ++tries)
  557. {
  558. _engine.connect("127.0.0.1", 10001);
  559. GLib.Thread.usleep(100*1000);
  560. }
  561. _level.send_level();
  562. send_state();
  563. }
  564. private void stop_engine()
  565. {
  566. _engine.close();
  567. if (_engine_process != null)
  568. {
  569. _engine_process.force_exit();
  570. _engine_process.wait();
  571. }
  572. }
  573. private void restart_engine()
  574. {
  575. stop_engine();
  576. start_engine(_engine_view.window_id);
  577. }
  578. private void start_game(StartGame sg)
  579. {
  580. _level.save(_project.level_editor_test_level());
  581. _database.touch();
  582. _data_compiler.compile.begin(_project.data_dir(), _project.platform(), (obj, res) => {
  583. if (_data_compiler.compile.end(res))
  584. {
  585. string args[] =
  586. {
  587. ENGINE_EXE,
  588. "--data-dir", _project.data_dir(),
  589. "--console-port", "12345",
  590. "--wait-console",
  591. "--lua-string", sg == StartGame.TEST ? "TEST=true" : "",
  592. null
  593. };
  594. GLib.SubprocessLauncher sl = new GLib.SubprocessLauncher(SubprocessFlags.NONE);
  595. sl.set_cwd(ENGINE_DIR);
  596. try
  597. {
  598. _game_process = sl.spawnv(args);
  599. }
  600. catch (Error e)
  601. {
  602. _console_view.loge("Editor", e.message);
  603. }
  604. for (int tries = 0; !_game.is_connected() && tries < 5; ++tries)
  605. {
  606. _game.connect("127.0.0.1", 12345);
  607. GLib.Thread.usleep(100*1000);
  608. }
  609. }
  610. });
  611. }
  612. private void stop_game()
  613. {
  614. _game.close();
  615. if (_game_process != null)
  616. {
  617. _game_process.force_exit();
  618. _game_process.wait();
  619. }
  620. }
  621. private void on_engine_view_realized()
  622. {
  623. start_engine(_engine_view.window_id);
  624. }
  625. private void on_tool_changed(Gtk.Action action)
  626. {
  627. RadioAction ra = (RadioAction)action;
  628. _tool_type = (ToolType)ra.current_value;
  629. send_state();
  630. }
  631. private void on_snap_mode_changed(Gtk.Action action)
  632. {
  633. RadioAction ra = (RadioAction)action;
  634. _snap_mode = (SnapMode)ra.current_value;
  635. send_state();
  636. }
  637. private void on_reference_system_changed(Gtk.Action action)
  638. {
  639. RadioAction ra = (RadioAction)action;
  640. _reference_system = (ReferenceSystem)ra.current_value;
  641. send_state();
  642. }
  643. private void on_grid_changed(Gtk.Action action)
  644. {
  645. RadioAction ra = (RadioAction)action;
  646. _grid_size = (float)ra.current_value/100.0;
  647. send_state();
  648. }
  649. private void on_rotation_snap_changed(Gtk.Action action)
  650. {
  651. RadioAction ra = (RadioAction)action;
  652. _rotation_snap = (float)ra.current_value;
  653. send_state();
  654. }
  655. private void new_level()
  656. {
  657. _level_filename = null;
  658. _level.load_empty_level();
  659. _level.send_level();
  660. }
  661. private void load()
  662. {
  663. FileChooserDialog fcd = new FileChooserDialog("Open..."
  664. , this
  665. , FileChooserAction.OPEN
  666. , "Cancel"
  667. , ResponseType.CANCEL
  668. , "Open"
  669. , ResponseType.ACCEPT
  670. );
  671. fcd.add_filter(_file_filter);
  672. fcd.set_current_folder(_project.source_dir());
  673. if (fcd.run() == (int)ResponseType.ACCEPT)
  674. {
  675. string filename = fcd.get_filename();
  676. if (filename.has_suffix(".level"))
  677. {
  678. _level_filename = filename;
  679. _level.load(_level_filename);
  680. _level.send_level();
  681. send_state();
  682. }
  683. }
  684. fcd.destroy();
  685. }
  686. private bool save_as()
  687. {
  688. bool saved = false;
  689. FileChooserDialog fcd = new FileChooserDialog("Save As..."
  690. , this
  691. , FileChooserAction.SAVE
  692. , "Cancel"
  693. , ResponseType.CANCEL
  694. , "Save"
  695. , ResponseType.ACCEPT
  696. );
  697. fcd.add_filter(_file_filter);
  698. fcd.set_current_folder(_project.source_dir());
  699. if (fcd.run() == (int)ResponseType.ACCEPT)
  700. {
  701. _level_filename = fcd.get_filename();
  702. if (!_level_filename.has_suffix(".level"))
  703. _level_filename += ".level";
  704. _level.save(_level_filename);
  705. saved = true;
  706. }
  707. fcd.destroy();
  708. return saved;
  709. }
  710. private bool save()
  711. {
  712. bool saved = false;
  713. if (_level_filename == null)
  714. {
  715. saved = save_as();
  716. }
  717. else
  718. {
  719. _level.save(_level_filename);
  720. saved = true;
  721. }
  722. return saved;
  723. }
  724. private bool save_timeout()
  725. {
  726. if (_level_filename != null)
  727. save();
  728. return true;
  729. }
  730. private void shutdown()
  731. {
  732. if (_resource_browser != null)
  733. _resource_browser.destroy();
  734. if (_preferences_dialog != null)
  735. _preferences_dialog.destroy();
  736. stop_game();
  737. stop_engine();
  738. stop_compiler();
  739. Gtk.main_quit();
  740. }
  741. private void quit()
  742. {
  743. if (!_database.changed())
  744. {
  745. shutdown();
  746. return;
  747. }
  748. Gtk.MessageDialog md = new Gtk.MessageDialog(this
  749. , Gtk.DialogFlags.MODAL
  750. , Gtk.MessageType.WARNING
  751. , Gtk.ButtonsType.NONE
  752. , "File changed, save?"
  753. );
  754. md.add_button("Quit _without Saving", ResponseType.NO);
  755. md.add_button("_Cancel", ResponseType.CANCEL);
  756. md.add_button("_Save", ResponseType.YES);
  757. md.set_default_response(ResponseType.YES);
  758. int rt = md.run();
  759. md.destroy();
  760. if (rt == (int)ResponseType.YES && save() || rt == (int)ResponseType.NO)
  761. shutdown();
  762. }
  763. private void on_new()
  764. {
  765. if (!_database.changed())
  766. {
  767. new_level();
  768. send_state();
  769. return;
  770. }
  771. Gtk.MessageDialog md = new Gtk.MessageDialog(this
  772. , Gtk.DialogFlags.MODAL
  773. , Gtk.MessageType.WARNING
  774. , Gtk.ButtonsType.NONE
  775. , "File changed, save?"
  776. );
  777. md.add_button("New _without Saving", ResponseType.NO);
  778. md.add_button("_Cancel", ResponseType.CANCEL);
  779. md.add_button("_Save", ResponseType.YES);
  780. md.set_default_response(ResponseType.YES);
  781. int rt = md.run();
  782. md.destroy();
  783. if (rt == (int)ResponseType.YES && save() || rt == (int)ResponseType.NO)
  784. {
  785. new_level();
  786. send_state();
  787. }
  788. }
  789. private void on_open(Gtk.Action action)
  790. {
  791. if (!_database.changed())
  792. {
  793. load();
  794. return;
  795. }
  796. Gtk.MessageDialog md = new Gtk.MessageDialog(this
  797. , Gtk.DialogFlags.MODAL
  798. , Gtk.MessageType.WARNING
  799. , Gtk.ButtonsType.NONE
  800. , "File changed, save?"
  801. );
  802. md.add_button("Open _without Saving", ResponseType.NO);
  803. md.add_button("_Cancel", ResponseType.CANCEL);
  804. md.add_button("_Save", ResponseType.YES);
  805. md.set_default_response(ResponseType.YES);
  806. int rt = md.run();
  807. md.destroy();
  808. if (rt == (int)ResponseType.YES && save() || rt == (int)ResponseType.NO)
  809. load();
  810. }
  811. private void on_save(Gtk.Action action)
  812. {
  813. save();
  814. }
  815. private void on_save_as(Gtk.Action action)
  816. {
  817. save_as();
  818. }
  819. private void on_import_begin(Gtk.FileFilter ff, out SList<string> filenames, out string filename)
  820. {
  821. FileChooserDialog fcd = new FileChooserDialog("Import..."
  822. , this
  823. , FileChooserAction.OPEN
  824. , "Cancel"
  825. , ResponseType.CANCEL
  826. , "Open"
  827. , ResponseType.ACCEPT
  828. );
  829. fcd.select_multiple = true;
  830. fcd.add_filter(ff);
  831. if (fcd.run() != (int)ResponseType.ACCEPT)
  832. {
  833. fcd.destroy();
  834. return;
  835. }
  836. filenames = fcd.get_filenames();
  837. fcd.destroy();
  838. FileChooserDialog dst = new FileChooserDialog("Select destination folder..."
  839. , this
  840. , FileChooserAction.SELECT_FOLDER
  841. , "Cancel"
  842. , ResponseType.CANCEL
  843. , "Select"
  844. , ResponseType.ACCEPT
  845. );
  846. dst.set_current_folder(_project.source_dir());
  847. if (dst.run() != (int)ResponseType.ACCEPT)
  848. {
  849. dst.destroy();
  850. return;
  851. }
  852. filename = dst.get_filename();
  853. dst.destroy();
  854. }
  855. private void on_import_end()
  856. {
  857. _data_compiler.compile.begin(_project.data_dir(), _project.platform(), (obj, res) => {
  858. _data_compiler.compile.end(res);
  859. });
  860. }
  861. private void on_import_sprites(Gtk.Action action)
  862. {
  863. Gtk.FileFilter ff = new FileFilter();
  864. ff.set_filter_name("Sprite (*.png)");
  865. ff.add_pattern("*.png");
  866. SList<string> filenames;
  867. string filename;
  868. on_import_begin(ff, out filenames, out filename);
  869. _project.import_sprites(filenames, filename);
  870. on_import_end();
  871. }
  872. private void on_import_meshes(Gtk.Action action)
  873. {
  874. Gtk.FileFilter ff = new FileFilter();
  875. ff.set_filter_name("Mesh (*.mesh)");
  876. ff.add_pattern("*.mesh");
  877. SList<string> filenames;
  878. string filename;
  879. on_import_begin(ff, out filenames, out filename);
  880. _project.import_meshes(filenames, filename);
  881. on_import_end();
  882. }
  883. private void on_import_sounds(Gtk.Action action)
  884. {
  885. Gtk.FileFilter ff = new FileFilter();
  886. ff.set_filter_name("Sound (*.wav)");
  887. ff.add_pattern("*.wav");
  888. SList<string> filenames;
  889. string filename;
  890. on_import_begin(ff, out filenames, out filename);
  891. _project.import_sounds(filenames, filename);
  892. on_import_end();
  893. }
  894. private void on_import_textures(Gtk.Action action)
  895. {
  896. Gtk.FileFilter ff = new FileFilter();
  897. ff.set_filter_name("Texture (*.png, *.tga, *.dds, *.ktx, *.pvr)");
  898. ff.add_pattern("*.png");
  899. ff.add_pattern("*.tga");
  900. ff.add_pattern("*.dds");
  901. ff.add_pattern("*.ktx");
  902. ff.add_pattern("*.pvr");
  903. SList<string> filenames;
  904. string filename;
  905. on_import_begin(ff, out filenames, out filename);
  906. _project.import_textures(filenames, filename);
  907. on_import_end();
  908. }
  909. private void on_preferences(Gtk.Action action)
  910. {
  911. if (_preferences_dialog == null)
  912. {
  913. _preferences_dialog = new PreferencesDialog(_engine);
  914. _preferences_dialog.set_transient_for(this);
  915. _preferences_dialog.delete_event.connect(() => { _preferences_dialog.hide(); return true; });
  916. }
  917. _preferences_dialog.show_all();
  918. }
  919. private void on_quit(Gtk.Action action)
  920. {
  921. quit();
  922. }
  923. private void on_show_grid(Gtk.Action action)
  924. {
  925. ToggleAction ta = (ToggleAction)action;
  926. _show_grid = ta.active;
  927. send_state();
  928. }
  929. private void on_custom_grid()
  930. {
  931. MessageDialog dg = new MessageDialog(this
  932. , DialogFlags.MODAL
  933. , MessageType.OTHER
  934. , ButtonsType.OK_CANCEL
  935. , "Grid size in meters:"
  936. );
  937. SpinButtonDouble sb = new SpinButtonDouble(_grid_size, 0.1, 1000);
  938. (dg.message_area as Gtk.Box).add(sb);
  939. dg.show_all();
  940. if (dg.run() == (int)ResponseType.OK)
  941. {
  942. _grid_size = sb.value;
  943. send_state();
  944. }
  945. dg.destroy();
  946. }
  947. private void on_rotation_snap(Gtk.Action action)
  948. {
  949. MessageDialog dg = new MessageDialog(this
  950. , DialogFlags.MODAL
  951. , MessageType.OTHER
  952. , ButtonsType.OK_CANCEL
  953. , "Rotation snap in degrees:"
  954. );
  955. SpinButtonDouble sb = new SpinButtonDouble(_rotation_snap, 1.0, 180.0);
  956. (dg.message_area as Gtk.Box).add(sb);
  957. dg.show_all();
  958. if (dg.run() == (int)ResponseType.OK)
  959. {
  960. _rotation_snap = sb.value;
  961. send_state();
  962. }
  963. dg.destroy();
  964. }
  965. private void on_create_cube(Gtk.Action action)
  966. {
  967. _engine.send_script(LevelEditorApi.set_placeable("unit", "core/units/primitives/cube"));
  968. _action_group.get_action("place").activate();
  969. }
  970. private void on_create_sphere(Gtk.Action action)
  971. {
  972. _engine.send_script(LevelEditorApi.set_placeable("unit", "core/units/primitives/sphere"));
  973. _action_group.get_action("place").activate();
  974. }
  975. private void on_create_cone(Gtk.Action action)
  976. {
  977. _engine.send_script(LevelEditorApi.set_placeable("unit", "core/units/primitives/cone"));
  978. _action_group.get_action("place").activate();
  979. }
  980. private void on_create_cylinder(Gtk.Action action)
  981. {
  982. _engine.send_script(LevelEditorApi.set_placeable("unit", "core/units/primitives/cylinder"));
  983. _action_group.get_action("place").activate();
  984. }
  985. private void on_create_plane(Gtk.Action action)
  986. {
  987. _engine.send_script(LevelEditorApi.set_placeable("unit", "core/units/primitives/plane"));
  988. _action_group.get_action("place").activate();
  989. }
  990. private void on_create_camera(Gtk.Action action)
  991. {
  992. _engine.send_script(LevelEditorApi.set_placeable("unit", "core/units/camera"));
  993. _action_group.get_action("place").activate();
  994. }
  995. private void on_create_light(Gtk.Action action)
  996. {
  997. _engine.send_script(LevelEditorApi.set_placeable("unit", "core/units/light"));
  998. _action_group.get_action("place").activate();
  999. }
  1000. private void on_create_sound_source(Gtk.Action action)
  1001. {
  1002. _engine.send_script(LevelEditorApi.set_placeable("sound", ""));
  1003. _action_group.get_action("place").activate();
  1004. }
  1005. private void on_camera_view_perspective(Gtk.Action action)
  1006. {
  1007. _engine.send_script("LevelEditor:camera_view_perspective()");
  1008. }
  1009. private void on_camera_view_front(Gtk.Action action)
  1010. {
  1011. _engine.send_script("LevelEditor:camera_view_front()");
  1012. }
  1013. private void on_camera_view_back(Gtk.Action action)
  1014. {
  1015. _engine.send_script("LevelEditor:camera_view_back()");
  1016. }
  1017. private void on_camera_view_right(Gtk.Action action)
  1018. {
  1019. _engine.send_script("LevelEditor:camera_view_right()");
  1020. }
  1021. private void on_camera_view_left(Gtk.Action action)
  1022. {
  1023. _engine.send_script("LevelEditor:camera_view_left()");
  1024. }
  1025. private void on_camera_view_top(Gtk.Action action)
  1026. {
  1027. _engine.send_script("LevelEditor:camera_view_top()");
  1028. }
  1029. private void on_camera_view_bottom(Gtk.Action action)
  1030. {
  1031. _engine.send_script("LevelEditor:camera_view_bottom()");
  1032. }
  1033. private void on_resource_browser(Gtk.Action action)
  1034. {
  1035. _resource_browser.show_all();
  1036. }
  1037. private void on_engine_restart(Gtk.Action action)
  1038. {
  1039. restart_engine();
  1040. }
  1041. private void on_reload_lua(Gtk.Action action)
  1042. {
  1043. _data_compiler.compile.begin(_project.data_dir(), _project.platform(), (obj, res) => {
  1044. if (_data_compiler.compile.end(res))
  1045. {
  1046. _engine.send(DeviceApi.pause());
  1047. _engine.send(DeviceApi.reload("lua", "core/editors/level_editor/level_editor"));
  1048. _engine.send(DeviceApi.unpause());
  1049. }
  1050. });
  1051. }
  1052. public void on_snap_to_grid(Gtk.Action action)
  1053. {
  1054. ToggleAction ta = (ToggleAction)action;
  1055. _snap_to_grid = ta.active;
  1056. send_state();
  1057. }
  1058. private void on_debug_render_world(Gtk.Action action)
  1059. {
  1060. ToggleAction ta = (ToggleAction)action;
  1061. _debug_render_world = ta.active;
  1062. send_state();
  1063. }
  1064. private void on_debug_physics_world(Gtk.Action action)
  1065. {
  1066. ToggleAction ta = (ToggleAction)action;
  1067. _debug_physics_world = ta.active;
  1068. send_state();
  1069. }
  1070. private void on_test_level(Gtk.Action action)
  1071. {
  1072. start_game(StartGame.TEST);
  1073. }
  1074. private void on_run_game(Gtk.Action action)
  1075. {
  1076. start_game(StartGame.NORMAL);
  1077. }
  1078. private void on_undo(Gtk.Action action)
  1079. {
  1080. _database.undo();
  1081. }
  1082. private void on_redo(Gtk.Action action)
  1083. {
  1084. _database.redo();
  1085. }
  1086. private void on_duplicate(Gtk.Action action)
  1087. {
  1088. _level.duplicate_selected_objects();
  1089. }
  1090. private void on_delete(Gtk.Action action)
  1091. {
  1092. _level.destroy_selected_objects();
  1093. }
  1094. private void on_manual(Gtk.Action action)
  1095. {
  1096. try
  1097. {
  1098. AppInfo.launch_default_for_uri("https://dbartolini.github.io/crown/html", null);
  1099. }
  1100. catch (Error e)
  1101. {
  1102. _console_view.loge("Editor", e.message);
  1103. }
  1104. }
  1105. private void on_report_issue(Gtk.Action action)
  1106. {
  1107. try
  1108. {
  1109. AppInfo.launch_default_for_uri("https://github.com/dbartolini/crown/issues", null);
  1110. }
  1111. catch (Error e)
  1112. {
  1113. _console_view.loge("Editor", e.message);
  1114. }
  1115. }
  1116. private void on_open_last_log(Gtk.Action action)
  1117. {
  1118. File file = File.new_for_path(_project.data_dir() + "/last.log");
  1119. try
  1120. {
  1121. AppInfo.launch_default_for_uri(file.get_uri(), null);
  1122. }
  1123. catch (Error e)
  1124. {
  1125. _console_view.loge("Editor", e.message);
  1126. }
  1127. }
  1128. private void on_about(Gtk.Action action)
  1129. {
  1130. Gtk.AboutDialog dlg = new Gtk.AboutDialog();
  1131. dlg.set_destroy_with_parent(true);
  1132. dlg.set_transient_for(this);
  1133. dlg.set_modal(true);
  1134. try
  1135. {
  1136. dlg.set_logo(new Pixbuf.from_resource("/org/crown/ui/icons/128x128/pepper.png"));
  1137. }
  1138. catch (Error e)
  1139. {
  1140. stderr.printf("%s\n", e.message);
  1141. }
  1142. dlg.program_name = "Crown Game Engine";
  1143. dlg.version = CROWN_VERSION;
  1144. dlg.website = "https://github.com/dbartolini/crown";
  1145. dlg.copyright = "Copyright (c) 2012-2017 Daniele Bartolini and individual contributors.";
  1146. dlg.license = "Crown Game Engine.\n"
  1147. + "Copyright (c) 2012-2017 Daniele Bartolini and individual contributors.\n"
  1148. + "\n"
  1149. + "This program is free software; you can redistribute it and/or\n"
  1150. + "modify it under the terms of the GNU General Public License\n"
  1151. + "as published by the Free Software Foundation; either version 2\n"
  1152. + "of the License, or (at your option) any later version.\n"
  1153. + "\n"
  1154. + "This program is distributed in the hope that it will be useful,\n"
  1155. + "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
  1156. + "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
  1157. + "GNU General Public License for more details.\n"
  1158. + "\n"
  1159. + "You should have received a copy of the GNU General Public License\n"
  1160. + "along with this program; if not, write to the Free Software\n"
  1161. + "Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n"
  1162. ;
  1163. dlg.run();
  1164. dlg.destroy();
  1165. }
  1166. private void on_destroy()
  1167. {
  1168. Gtk.main_quit();
  1169. }
  1170. private bool on_delete_event()
  1171. {
  1172. quit();
  1173. return true;
  1174. }
  1175. }
  1176. public static int main (string[] args)
  1177. {
  1178. Gtk.init(ref args);
  1179. Intl.setlocale(LocaleCategory.ALL, "C");
  1180. Gtk.Settings.get_default().gtk_application_prefer_dark_theme = true;
  1181. Gtk.CssProvider provider = new Gtk.CssProvider();
  1182. Gdk.Screen screen = Gdk.Display.get_default().get_default_screen();
  1183. Gtk.StyleContext.add_provider_for_screen(screen, provider, STYLE_PROVIDER_PRIORITY_APPLICATION);
  1184. provider.load_from_resource("/org/crown/ui/theme/style.css");
  1185. try
  1186. {
  1187. Gtk.IconTheme.add_builtin_icon("tool-place", 16, new Pixbuf.from_resource("/org/crown/ui/icons/theme/tool-place.png"));
  1188. Gtk.IconTheme.add_builtin_icon("tool-move", 16, new Pixbuf.from_resource("/org/crown/ui/icons/theme/tool-move.png"));
  1189. Gtk.IconTheme.add_builtin_icon("tool-rotate", 16, new Pixbuf.from_resource("/org/crown/ui/icons/theme/tool-rotate.png"));
  1190. Gtk.IconTheme.add_builtin_icon("tool-scale", 16, new Pixbuf.from_resource("/org/crown/ui/icons/theme/tool-scale.png"));
  1191. Gtk.IconTheme.add_builtin_icon("axis-local", 16, new Pixbuf.from_resource("/org/crown/ui/icons/theme/axis-local.png"));
  1192. Gtk.IconTheme.add_builtin_icon("axis-world", 16, new Pixbuf.from_resource("/org/crown/ui/icons/theme/axis-world.png"));
  1193. Gtk.IconTheme.add_builtin_icon("snap-to-grid", 16, new Pixbuf.from_resource("/org/crown/ui/icons/theme/snap-to-grid.png"));
  1194. Gtk.IconTheme.add_builtin_icon("reference-local", 16, new Pixbuf.from_resource("/org/crown/ui/icons/theme/reference-local.png"));
  1195. Gtk.IconTheme.add_builtin_icon("reference-world", 16, new Pixbuf.from_resource("/org/crown/ui/icons/theme/reference-world.png"));
  1196. Gtk.IconTheme.add_builtin_icon("run", 16, new Pixbuf.from_resource("/org/crown/ui/icons/theme/run.png"));
  1197. Gtk.IconTheme.add_builtin_icon("level-tree", 16, new Pixbuf.from_resource("/org/crown/ui/icons/theme/level-tree.png"));
  1198. Gtk.IconTheme.add_builtin_icon("level-layers", 16, new Pixbuf.from_resource("/org/crown/ui/icons/theme/level-layers.png"));
  1199. Gtk.IconTheme.add_builtin_icon("layer-visible", 16, new Pixbuf.from_resource("/org/crown/ui/icons/theme/layer-visible.png"));
  1200. Gtk.IconTheme.add_builtin_icon("layer-locked", 16, new Pixbuf.from_resource("/org/crown/ui/icons/theme/layer-locked.png"));
  1201. }
  1202. catch (Error e)
  1203. {
  1204. stderr.printf(e.message);
  1205. }
  1206. string source_dir = "";
  1207. if (args.length > 1)
  1208. {
  1209. if (!GLib.FileUtils.test(args[1], FileTest.EXISTS) || !GLib.FileUtils.test(args[1], FileTest.IS_DIR))
  1210. {
  1211. stdout.printf("Source directory does not exist or it is not a directory\n");
  1212. return -1;
  1213. }
  1214. source_dir = args[1];
  1215. }
  1216. else
  1217. {
  1218. stdout.printf("You must specify a source directory\n");
  1219. return -1;
  1220. }
  1221. string toolchain_dir = "";
  1222. if (args.length > 2)
  1223. {
  1224. if (!GLib.FileUtils.test(args[2], FileTest.EXISTS) || !GLib.FileUtils.test(args[2], FileTest.IS_DIR))
  1225. {
  1226. stdout.printf("Toolchain directory does not exist or it is not a directory\n");
  1227. return -1;
  1228. }
  1229. toolchain_dir = args[2];
  1230. }
  1231. else
  1232. {
  1233. bool found = false;
  1234. /// More desirable paths come first
  1235. string toolchain_paths[] =
  1236. {
  1237. "../..",
  1238. "../../../samples"
  1239. };
  1240. for (int i = 0; i < toolchain_paths.length; ++i)
  1241. {
  1242. string path = Path.build_filename(toolchain_paths[i], "core");
  1243. // Try to locate the toolchain directory
  1244. if (GLib.FileUtils.test(path, FileTest.EXISTS) && GLib.FileUtils.test(path, FileTest.IS_DIR))
  1245. {
  1246. toolchain_dir = toolchain_paths[i];
  1247. found = true;
  1248. break;
  1249. }
  1250. }
  1251. if (!found)
  1252. {
  1253. stdout.printf("Unable to find the toolchain directory\n");
  1254. return -1;
  1255. }
  1256. }
  1257. Project project = new Project();
  1258. project.load(source_dir, toolchain_dir);
  1259. Database database = new Database();
  1260. ConsoleClient compiler = new ConsoleClient();
  1261. ConsoleClient engine = new ConsoleClient();
  1262. ConsoleClient game = new ConsoleClient();
  1263. Level level = new Level(database, engine, project.source_dir(), project.toolchain_dir());
  1264. LevelEditor editor = new LevelEditor(project, database, level, compiler, engine, game);
  1265. level.load_empty_level();
  1266. editor.show_all();
  1267. Gtk.main();
  1268. return 0;
  1269. }
  1270. }