level_editor.vala 45 KB

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