level_editor.vala 44 KB

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