luboslenco 1 ano atrás
pai
commit
ff3e8a1197
45 arquivos alterados com 501 adições e 550 exclusões
  1. 2 2
      armorlab/Sources/nodes/variance_node.ts
  2. 5 5
      armorlab/Sources/render_path_paint.ts
  3. 27 27
      armorpaint/Sources/nodes/brush_output_node.ts
  4. 11 10
      armorpaint/Sources/nodes/input_node.ts
  5. 5 3
      armorpaint/Sources/nodes/tex_image_node.ts
  6. 5 5
      armorpaint/Sources/render_path_paint.ts
  7. 3 3
      armorpaint/Sources/tab_layers.ts
  8. 24 22
      armorsculpt/Sources/nodes/brush_output_node.ts
  9. 72 143
      base/Sources/base.ts
  10. 1 1
      base/Sources/box_export.ts
  11. 11 6
      base/Sources/box_preferences.ts
  12. 17 17
      base/Sources/camera.ts
  13. 20 32
      base/Sources/config.ts
  14. 2 2
      base/Sources/context.ts
  15. 2 0
      base/Sources/enums.ts
  16. 3 3
      base/Sources/gizmo.ts
  17. 7 2
      base/Sources/logic_node.ts
  18. 16 17
      base/Sources/main.ts
  19. 3 2
      base/Sources/nodes/boolean_node.ts
  20. 3 2
      base/Sources/nodes/color_node.ts
  21. 3 2
      base/Sources/nodes/float_node.ts
  22. 3 2
      base/Sources/nodes/integer_node.ts
  23. 5 4
      base/Sources/nodes/math_node.ts
  24. 1 1
      base/Sources/nodes/null_node.ts
  25. 5 4
      base/Sources/nodes/random_node.ts
  26. 8 5
      base/Sources/nodes/separate_vector_node.ts
  27. 3 2
      base/Sources/nodes/string_node.ts
  28. 7 4
      base/Sources/nodes/time_node.ts
  29. 7 5
      base/Sources/nodes/vector_math_node.ts
  30. 9 8
      base/Sources/nodes/vector_node.ts
  31. 4 3
      base/Sources/operator.ts
  32. 2 1
      base/Sources/physics_body.ts
  33. 1 1
      base/Sources/render_path_base.ts
  34. 4 4
      base/Sources/tab_materials.ts
  35. 1 1
      base/Sources/tab_meshes.ts
  36. 1 1
      base/Sources/tab_textures.ts
  37. 7 8
      base/Sources/translator.ts
  38. 106 105
      base/Sources/ui_base.ts
  39. 15 15
      base/Sources/ui_header.ts
  40. 34 34
      base/Sources/ui_menu.ts
  41. 9 9
      base/Sources/ui_nodes.ts
  42. 18 18
      base/Sources/ui_toolbar.ts
  43. 5 5
      base/Sources/ui_view2d.ts
  44. 3 3
      base/Sources/uniforms_ext.ts
  45. 1 1
      base/Sources/util_particle.ts

+ 2 - 2
armorlab/Sources/nodes/variance_node.ts

@@ -32,9 +32,9 @@ function variance_node_buttons(ui: zui_t, nodes: zui_nodes_t, node: zui_node_t)
 }
 
 function variance_node_get_as_image(self: variance_node_t, from: i32): image_t {
-	let strength = (variance_node_inst.inputs[1].node as any).value;
+	let strength: f32 = variance_node_inst.base.inputs[1].node.value;
 
-	let source: image_t = variance_node_inst.inputs[0].get_as_image();
+	let source: image_t = logic_node_input_get_as_image(variance_node_inst.base.inputs[0]);
 	g2_begin(variance_node_temp);
 	g2_draw_scaled_image(source, 0, 0, 512, 512);
 	g2_end();

+ 5 - 5
armorlab/Sources/render_path_paint.ts

@@ -262,9 +262,9 @@ function render_path_paint_bind_layers() {
 	let canvas = ui_nodes_get_canvas(true);
 	if (nodes.nodes_selected_id.length > 0) {
 		let node = zui_get_node(canvas.nodes, nodes.nodes_selected_id[0]);
-		let brushNode = parser_logic_get_logic_node(node);
-		if (brushNode != null) {
-			image = brushNode.get_cached_image();
+		let brush_node: inpaint_node_t = parser_logic_get_logic_node(node);
+		if (brush_node: inpaint_node_t != null) {
+			image = brush_node: inpaint_node_t.get_cached_image();
 		}
 	}
 	if (image != null) {
@@ -294,8 +294,8 @@ function render_path_paint_bind_layers() {
 		let node = zui_get_node(canvas.nodes, nodes.nodes_selected_id[0]);
 		let inpaint = node.type == "InpaintNode";
 		if (inpaint) {
-			let brushNode = parser_logic_get_logic_node(node);
-			map_get(render_path_render_targets, "texpaint_node_target")._image = (brushNode as InpaintNode).getTarget();
+			let brush_node: inpaint_node_t = parser_logic_get_logic_node(node);
+			map_get(render_path_render_targets, "texpaint_node_target")._image = inpaint_node_get_target(brush_node);
 		}
 	}
 	else {

+ 27 - 27
armorpaint/Sources/nodes/brush_output_node.ts

@@ -18,46 +18,46 @@ function brush_output_node_parse_inputs(self: brush_output_node_t) {
 	let last_mask: image_t = context_raw.brush_mask_image;
 	let last_stencil: image_t = context_raw.brush_stencil_image;
 
-	let input0: any = logic_node_input_get(self.base.inputs[0]);
-	let input1: any = logic_node_input_get(self.base.inputs[1]);
-	let input2: any = logic_node_input_get(self.base.inputs[2]);
-	let input3: any = logic_node_input_get(self.base.inputs[3]);
-	let input4: any = logic_node_input_get(self.base.inputs[4]);
-	let input5: any = logic_node_input_get(self.base.inputs[5]);
-	let input6: any = logic_node_input_get(self.base.inputs[6]);
-
-	context_raw.paint_vec = input0;
-	context_raw.brush_nodes_radius = input1;
-	context_raw.brush_nodes_scale = input2;
-	context_raw.brush_nodes_angle = input3;
-
-	let opac: any = input4; // Float or texture name
+	let input0: logic_node_value_t = logic_node_input_get(self.base.inputs[0]);
+	let input1: logic_node_value_t = logic_node_input_get(self.base.inputs[1]);
+	let input2: logic_node_value_t = logic_node_input_get(self.base.inputs[2]);
+	let input3: logic_node_value_t = logic_node_input_get(self.base.inputs[3]);
+	let input4: logic_node_value_t = logic_node_input_get(self.base.inputs[4]);
+	let input5: logic_node_value_t = logic_node_input_get(self.base.inputs[5]);
+	let input6: logic_node_value_t = logic_node_input_get(self.base.inputs[6]);
+
+	context_raw.paint_vec = input0._any;
+	context_raw.brush_nodes_radius = input1._f32;
+	context_raw.brush_nodes_scale = input2._f32;
+	context_raw.brush_nodes_angle = input3._f32;
+
+	let opac: logic_node_value_t = input4; // Float or texture name
 	if (opac == null) {
-		opac = 1.0;
+		opac = { _f32: 1.0 };
 	}
-	if (typeof opac == "string") {
-		context_raw.brush_mask_image_is_alpha = ends_with(opac, ".a");
-		opac = substring(opac, 0, string_last_index_of(opac, "."));
+	if (opac._any != null) { // string
+		context_raw.brush_mask_image_is_alpha = ends_with(opac._any, ".a");
+		opac._any = substring(opac._any, 0, string_last_index_of(opac._any, "."));
 		context_raw.brush_nodes_opacity = 1.0;
-		let index: i32 = array_index_of(project_asset_names, opac);
+		let index: i32 = array_index_of(project_asset_names, opac._any);
 		let asset: asset_t = project_assets[index];
 		context_raw.brush_mask_image = project_get_image(asset);
 	}
 	else {
-		context_raw.brush_nodes_opacity = opac;
+		context_raw.brush_nodes_opacity = opac._f32;
 		context_raw.brush_mask_image = null;
 	}
 
-	context_raw.brush_nodes_hardness = input5;
+	context_raw.brush_nodes_hardness = input5._f32;
 
-	let stencil: any = input6; // Float or texture name
+	let stencil: logic_node_value_t = input6; // Float or texture name
 	if (stencil == null) {
-		stencil = 1.0;
+		stencil = { _f32: 1.0 };
 	}
-	if (typeof stencil == "string") {
-		context_raw.brush_stencil_image_is_alpha = ends_with(stencil, ".a");
-		stencil = substring(stencil, 0, string_last_index_of(stencil, "."));
-		let index: i32 = array_index_of(project_asset_names, stencil);
+	if (stencil._any != null) { // string
+		context_raw.brush_stencil_image_is_alpha = ends_with(stencil._any, ".a");
+		stencil._any = substring(stencil._any, 0, string_last_index_of(stencil._any, "."));
+		let index: i32 = array_index_of(project_asset_names, stencil._any);
 		let asset: asset_t = project_assets[index];
 		context_raw.brush_stencil_image = project_get_image(asset);
 	}

+ 11 - 10
armorpaint/Sources/nodes/input_node.ts

@@ -33,11 +33,11 @@ function input_node_update(self: float_node_t) {
 	}
 
 	let decal: bool = context_raw.tool == workspace_tool_t.DECAL || context_raw.tool == workspace_tool_t.TEXT;
-	let decal_mask: bool = decal && operator_shortcut(config_keymap.decal_mask + "+" + config_keymap.action_paint, shortcut_type_t.DOWN);
+	let decal_mask: bool = decal && operator_shortcut(map_get(config_keymap, "decal_mask") + "+" + map_get(config_keymap, "action_paint"), shortcut_type_t.DOWN);
 
 	let lazy_paint: bool = context_raw.brush_lazy_radius > 0 &&
-		(operator_shortcut(config_keymap.action_paint, shortcut_type_t.DOWN) ||
-			operator_shortcut(config_keymap.brush_ruler + "+" + config_keymap.action_paint, shortcut_type_t.DOWN) ||
+		(operator_shortcut(map_get(config_keymap, "action_paint"), shortcut_type_t.DOWN) ||
+			operator_shortcut(map_get(config_keymap, "brush_ruler") + "+" + map_get(config_keymap, "action_paint"), shortcut_type_t.DOWN) ||
 			decal_mask);
 
 	let paint_x: f32 = mouse_view_x() / app_w();
@@ -56,7 +56,7 @@ function input_node_update(self: float_node_t) {
 		input_node_start_y = pen_view_y() / app_h();
 	}
 
-	if (operator_shortcut(config_keymap.brush_ruler + "+" + config_keymap.action_paint, shortcut_type_t.DOWN)) {
+	if (operator_shortcut(map_get(config_keymap, "brush_ruler") + "+" + map_get(config_keymap, "action_paint"), shortcut_type_t.DOWN)) {
 		if (input_node_lock_x) {
 			paint_x = input_node_start_x;
 		}
@@ -87,12 +87,12 @@ function input_node_update(self: float_node_t) {
 		}
 	}
 
-	if (keyboard_started(config_keymap.brush_ruler)) {
+	if (keyboard_started(map_get(config_keymap, "brush_ruler"))) {
 		input_node_lock_start_x = mouse_view_x();
 		input_node_lock_start_y = mouse_view_y();
 		input_node_lock_begin = true;
 	}
-	else if (keyboard_released(config_keymap.brush_ruler)) {
+	else if (keyboard_released(map_get(config_keymap, "brush_ruler"))) {
 		input_node_lock_x = input_node_lock_y = input_node_lock_begin = false;
 	}
 
@@ -120,10 +120,11 @@ function input_node_update(self: float_node_t) {
 	context_raw.parse_brush_inputs(context_raw.brush_output_node_inst);
 }
 
-function input_node_get(self: input_node_t, from: i32): any {
-	context_raw.brush_lazy_radius = logic_node_input_get(self.base.inputs[0]);
-	context_raw.brush_lazy_step = logic_node_input_get(self.base.inputs[1]);;
-	return input_node_coords;
+function input_node_get(self: input_node_t, from: i32): logic_node_value_t {
+	context_raw.brush_lazy_radius = logic_node_input_get(self.base.inputs[0])._f32;
+	context_raw.brush_lazy_step = logic_node_input_get(self.base.inputs[1])._f32;
+	let v: logic_node_value_t = { _any: input_node_coords };
+	return v;
 }
 
 let input_node_def: zui_node_t = {

+ 5 - 3
armorpaint/Sources/nodes/tex_image_node.ts

@@ -12,12 +12,14 @@ function tex_image_node_create(arg: any): tex_image_node_t {
 	return n;
 }
 
-function tex_image_node_get(self: tex_image_node_t, from: i32): any {
+function tex_image_node_get(self: tex_image_node_t, from: i32): logic_node_value_t {
 	if (from == 0) {
-		return self.file + ".rgb";
+		let v: logic_node_value_t = { _any: self.file + ".rgb" };
+		return v;
 	}
 	else {
-		return self.file + ".a";
+		let v: logic_node_value_t = { _any: self.file + ".a" };
+		return v;
 	}
 }
 

+ 5 - 5
armorpaint/Sources/render_path_paint.ts

@@ -452,7 +452,7 @@ function render_path_paint_commands_live_brush() {
 	let _pdirty: i32 = context_raw.pdirty;
 	context_raw.last_paint_vec_x = context_raw.paint_vec.x;
 	context_raw.last_paint_vec_y = context_raw.paint_vec.y;
-	if (operator_shortcut(config_keymap.brush_ruler)) {
+	if (operator_shortcut(map_get(config_keymap, "brush_ruler"))) {
 		context_raw.last_paint_vec_x = context_raw.last_paint_x;
 		context_raw.last_paint_vec_y = context_raw.last_paint_y;
 	}
@@ -474,7 +474,7 @@ function render_path_paint_commands_live_brush() {
 function render_path_paint_commands_cursor() {
 	if (!config_raw.brush_3d) return;
 	let decal: bool = context_raw.tool == workspace_tool_t.DECAL || context_raw.tool == workspace_tool_t.TEXT;
-	let decal_mask: bool = decal && operator_shortcut(config_keymap.decal_mask, shortcut_type_t.DOWN);
+	let decal_mask: bool = decal && operator_shortcut(map_get(config_keymap, "decal_mask"), shortcut_type_t.DOWN);
 	let tool: workspace_tool_t = context_raw.tool;
 	if (tool != workspace_tool_t.BRUSH &&
 		tool != workspace_tool_t.ERASER &&
@@ -513,7 +513,7 @@ function render_path_paint_draw_cursor(mx: f32, my: f32, radius: f32, tint_r: f3
 	render_path_set_target("");
 	g4_set_pipeline(base_pipe_cursor);
 	let decal: bool = context_raw.tool == workspace_tool_t.DECAL || context_raw.tool == workspace_tool_t.TEXT;
-	let decal_mask: bool = decal && operator_shortcut(config_keymap.decal_mask, shortcut_type_t.DOWN);
+	let decal_mask: bool = decal && operator_shortcut(map_get(config_keymap, "decal_mask"), shortcut_type_t.DOWN);
 	let img: image_t = (decal && !decal_mask) ? context_raw.decal_image : resource_get("cursor.k");
 	g4_set_tex(base_cursor_tex, img);
 	let gbuffer0: image_t = map_get(render_path_render_targets, "gbuffer0")._image;
@@ -739,8 +739,8 @@ function render_path_paint_draw() {
 						}
 						app_notify_on_init(_render_final);
 					}
-					let bake_type: bake_type_t = context_raw.bake_type as bake_type_t;
-					app_notify_on_init(bake_type == bake_type_t.DERIVATIVE ? _render_deriv : _render_final);
+					// @ts-ignore
+					app_notify_on_init(context_raw.bake_type == bake_type_t.DERIVATIVE ? _render_deriv : _render_final);
 				}
 			}
 			else if (context_raw.bake_type == bake_type_t.OBJECTID) {

+ 3 - 3
armorpaint/Sources/tab_layers.ts

@@ -55,7 +55,7 @@ function tab_layers_button_2d_view() {
 		ui_base_show_2d_view(view_2d_type_t.LAYER);
 	}
 	else if (ui.is_hovered) {
-		zui_tooltip(tr("Show 2D View") + " (" + config_keymap.toggle_2d_view + ")");
+		zui_tooltip(tr("Show 2D View") + " (" + map_get(config_keymap, "toggle_2d_view") + ")");
 	}
 }
 
@@ -591,7 +591,7 @@ function tab_layers_handle_layer_icon_state(l: slot_layer_t, i: i32, state: zui_
 			zui_tooltip_image(texpaint_preview);
 		}
 		if (i < 9) {
-			zui_tooltip(l.name + " - (" + config_keymap.select_layer + " " + (i + 1) + ")");
+			zui_tooltip(l.name + " - (" + map_get(config_keymap, "select_layer") + " " + (i + 1) + ")");
 		}
 		else {
 			zui_tooltip(l.name);
@@ -677,7 +677,7 @@ function tab_layers_draw_layer_icon(l: slot_layer_t, i: i32, uix: f32, uiy: f32,
 		// Draw layer numbers when selecting a layer via keyboard shortcut
 		let is_typing: bool = ui.is_typing || ui_view2d_ui.is_typing || ui_nodes_ui.is_typing;
 		if (!is_typing) {
-			if (i < 9 && operator_shortcut(config_keymap.select_layer, shortcut_type_t.DOWN)) {
+			if (i < 9 && operator_shortcut(map_get(config_keymap, "select_layer"), shortcut_type_t.DOWN)) {
 				let number: string = any_to_string(i + 1) ;
 				let width: i32 = g2_font_width(ui.font, ui.font_size, number) + 10;
 				let height: i32 = g2_font_height(ui.font, ui.font_size);

+ 24 - 22
armorsculpt/Sources/nodes/brush_output_node.ts

@@ -16,40 +16,42 @@ function brush_output_node_parse_inputs(self: brush_output_node_t) {
 	let last_mask = context_raw.brush_mask_image;
 	let last_stencil = context_raw.brush_stencil_image;
 
-	let input0: any = logic_node_input_get(self.base.inputs[0]);
-	let input1: any = logic_node_input_get(self.base.inputs[1]);
-	let input2: any = logic_node_input_get(self.base.inputs[2]);
-	let input3: any = logic_node_input_get(self.base.inputs[3]);
-	let input4: any = logic_node_input_get(self.base.inputs[4]);
-
-	context_raw.paint_vec = input0;
-	context_raw.brush_nodes_radius = input1;
-
-	let opac: any = input2; // Float or texture name
-	if (opac == null) opac = 1.0;
-	if (typeof opac == "string") {
-		context_raw.brush_mask_image_is_alpha = ends_with(opac, ".a");
-		opac = substring(opac, 0, string_last_index_of(opac, "."));
+	let input0: logic_node_value_t = logic_node_input_get(self.base.inputs[0]);
+	let input1: logic_node_value_t = logic_node_input_get(self.base.inputs[1]);
+	let input2: logic_node_value_t = logic_node_input_get(self.base.inputs[2]);
+	let input3: logic_node_value_t = logic_node_input_get(self.base.inputs[3]);
+	let input4: logic_node_value_t = logic_node_input_get(self.base.inputs[4]);
+
+	context_raw.paint_vec = input0._any;
+	context_raw.brush_nodes_radius = input1._any;
+
+	let opac: logic_node_value_t = input2; // Float or texture name
+	if (opac == null) {
+		opac = { _f32: 1.0 };
+	}
+	if (opac._any != null) { // string
+		context_raw.brush_mask_image_is_alpha = ends_with(opac._any, ".a");
+		opac._any = substring(opac._any, 0, string_last_index_of(opac._any, "."));
 		context_raw.brush_nodes_opacity = 1.0;
-		let index = array_index_of(project_asset_names, opac);
+		let index = array_index_of(project_asset_names, opac._any);
 		let asset = project_assets[index];
 		context_raw.brush_mask_image = project_get_image(asset);
 	}
 	else {
-		context_raw.brush_nodes_opacity = opac;
+		context_raw.brush_nodes_opacity = opac._f32;
 		context_raw.brush_mask_image = null;
 	}
 
 	context_raw.brush_nodes_hardness = input3;
 
-	let stencil: any = input4; // Float or texture name
+	let stencil: logic_node_value_t = input4; // Float or texture name
 	if (stencil == null) {
-		stencil = 1.0;
+		stencil = { _f32: 1.0 };
 	}
-	if (typeof stencil == "string") {
-		context_raw.brush_stencil_image_is_alpha = ends_with(stencil, ".a");
-		stencil = substring(stencil, 0, string_last_index_of(stencil, "."));
-		let index = array_index_of(project_asset_names, stencil);
+	if (stencil._any != null) { // string
+		context_raw.brush_stencil_image_is_alpha = ends_with(stencil._any, ".a");
+		stencil._any = substring(stencil._any, 0, string_last_index_of(stencil._any, "."));
+		let index = array_index_of(project_asset_names, stencil._any);
 		let asset = project_assets[index];
 		context_raw.brush_stencil_image = project_get_image(asset);
 	}

+ 72 - 143
base/Sources/base.ts

@@ -102,151 +102,80 @@ let base_max_layers: i32 = 255;
 ///end
 let base_default_fov: f32 = 0.69;
 
-let base_default_keymap: any = {
-	action_paint: "left",
-	action_rotate: "alt+left",
-	action_pan: "alt+middle",
-	action_zoom: "alt+right",
-	rotate_light: "shift+middle",
-	rotate_envmap: "ctrl+middle",
-	set_clone_source: "alt",
-	stencil_transform: "ctrl",
-	stencil_hide: "z",
-	brush_radius: "f",
-	brush_radius_decrease: "[",
-	brush_radius_increase: "]",
-	brush_ruler: "shift",
-	file_new: "ctrl+n",
-	file_open: "ctrl+o",
-	file_open_recent: "ctrl+shift+o",
-	file_save: "ctrl+s",
-	file_save_as: "ctrl+shift+s",
-	file_reimport_mesh: "ctrl+r",
-	file_reimport_textures: "ctrl+shift+r",
-	file_import_assets: "ctrl+i",
-	file_export_textures: "ctrl+e",
-	file_export_textures_as: "ctrl+shift+e",
-	edit_undo: "ctrl+z",
-	edit_redo: "ctrl+shift+z",
-	edit_prefs: "ctrl+k",
-	view_reset: "0",
-	view_front: "1",
-	view_back: "ctrl+1",
-	view_right: "3",
-	view_left: "ctrl+3",
-	view_top: "7",
-	view_bottom: "ctrl+7",
-	view_camera_type: "5",
-	view_orbit_left: "4",
-	view_orbit_right: "6",
-	view_orbit_up: "8",
-	view_orbit_down: "2",
-	view_orbit_opposite: "9",
-	view_zoom_in: "",
-	view_zoom_out: "",
-	view_distract_free: "f11",
-	viewport_mode: "ctrl+m",
-	toggle_node_editor: "tab",
-	toggle_2d_view: "shift+tab",
-	toggle_browser: "`",
-	node_search: "space",
-	operator_search: "space",
+function base_get_default_keymap(): map_t<string, string> {
+	let keymap: map_t<string, string> = map_create();
+	map_set(keymap, "action_paint", "left");
+	map_set(keymap, "action_rotate", "alt+left");
+	map_set(keymap, "action_pan", "alt+middle");
+	map_set(keymap, "action_zoom", "alt+right");
+	map_set(keymap, "rotate_light", "shift+middle");
+	map_set(keymap, "rotate_envmap", "ctrl+middle");
+	map_set(keymap, "set_clone_source", "alt");
+	map_set(keymap, "stencil_transform", "ctrl");
+	map_set(keymap, "stencil_hide", "z");
+	map_set(keymap, "brush_radius", "f");
+	map_set(keymap, "brush_radius_decrease", "[");
+	map_set(keymap, "brush_radius_increase", "]");
+	map_set(keymap, "brush_ruler", "shift");
+	map_set(keymap, "file_new", "ctrl+n");
+	map_set(keymap, "file_open", "ctrl+o");
+	map_set(keymap, "file_open_recent", "ctrl+shift+o");
+	map_set(keymap, "file_save", "ctrl+s");
+	map_set(keymap, "file_save_as", "ctrl+shift+s");
+	map_set(keymap, "file_reimport_mesh", "ctrl+r");
+	map_set(keymap, "file_reimport_textures", "ctrl+shift+r");
+	map_set(keymap, "file_import_assets", "ctrl+i");
+	map_set(keymap, "file_export_textures", "ctrl+e");
+	map_set(keymap, "file_export_textures_as", "ctrl+shift+e");
+	map_set(keymap, "edit_undo", "ctrl+z");
+	map_set(keymap, "edit_redo", "ctrl+shift+z");
+	map_set(keymap, "edit_prefs", "ctrl+k");
+	map_set(keymap, "view_reset", "0");
+	map_set(keymap, "view_front", "1");
+	map_set(keymap, "view_back", "ctrl+1");
+	map_set(keymap, "view_right", "3");
+	map_set(keymap, "view_left", "ctrl+3");
+	map_set(keymap, "view_top", "7");
+	map_set(keymap, "view_bottom", "ctrl+7");
+	map_set(keymap, "view_camera_type", "5");
+	map_set(keymap, "view_orbit_left", "4");
+	map_set(keymap, "view_orbit_right", "6");
+	map_set(keymap, "view_orbit_up", "8");
+	map_set(keymap, "view_orbit_down", "2");
+	map_set(keymap, "view_orbit_opposite", "9");
+	map_set(keymap, "view_zoom_in", "");
+	map_set(keymap, "view_zoom_out", "");
+	map_set(keymap, "view_distract_free", "f11");
+	map_set(keymap, "viewport_mode", "ctrl+m");
+	map_set(keymap, "toggle_node_editor", "tab");
+	map_set(keymap, "toggle_2d_view", "shift+tab");
+	map_set(keymap, "toggle_browser", "`");
+	map_set(keymap, "node_search", "space");
+	map_set(keymap, "operator_search", "space");
 	///if (is_paint || is_sculpt)
-	decal_mask: "ctrl",
-	select_material: "shift+number",
-	select_layer: "alt+number",
-	brush_opacity: "shift+f",
-	brush_angle: "alt+f",
-	tool_brush: "b",
-	tool_eraser: "e",
-	tool_fill: "g",
-	tool_decal: "d",
-	tool_text: "t",
-	tool_clone: "l",
-	tool_blur: "u",
-	tool_smudge: "m",
-	tool_particle: "p",
-	tool_colorid: "c",
-	tool_picker: "v",
-	tool_bake: "k",
-	tool_gizmo: "",
-	tool_material: "",
-	swap_brush_eraser: "",
+	map_set(keymap, "decal_mask", "ctrl");
+	map_set(keymap, "select_material", "shift+number");
+	map_set(keymap, "select_layer", "alt+number");
+	map_set(keymap, "brush_opacity", "shift+f");
+	map_set(keymap, "brush_angle", "alt+f");
+	map_set(keymap, "tool_brush", "b");
+	map_set(keymap, "tool_eraser", "e");
+	map_set(keymap, "tool_fill", "g");
+	map_set(keymap, "tool_decal", "d");
+	map_set(keymap, "tool_text", "t");
+	map_set(keymap, "tool_clone", "l");
+	map_set(keymap, "tool_blur", "u");
+	map_set(keymap, "tool_smudge", "m");
+	map_set(keymap, "tool_particle", "p");
+	map_set(keymap, "tool_colorid", "c");
+	map_set(keymap, "tool_picker", "v");
+	map_set(keymap, "tool_bake", "k");
+	map_set(keymap, "tool_gizmo", "");
+	map_set(keymap, "tool_material", "");
+	map_set(keymap, "swap_brush_eraser", "");
 	///end
-};
-
-let base_keymap_keys: string[] = [
-	"action_paint",
-	"action_rotate",
-	"action_pan",
-	"action_zoom",
-	"rotate_light",
-	"rotate_envmap",
-	"set_clone_source",
-	"stencil_transform",
-	"stencil_hide",
-	"brush_radius",
-	"brush_radius_decrease",
-	"brush_radius_increase",
-	"brush_ruler",
-	"file_new",
-	"file_open",
-	"file_open_recent",
-	"file_save",
-	"file_save_as",
-	"file_reimport_mesh",
-	"file_reimport_textures",
-	"file_import_assets",
-	"file_export_textures",
-	"file_export_textures_as",
-	"edit_undo",
-	"edit_redo",
-	"edit_prefs",
-	"view_reset",
-	"view_front",
-	"view_back",
-	"view_right",
-	"view_left",
-	"view_top",
-	"view_bottom",
-	"view_camera_type",
-	"view_orbit_left",
-	"view_orbit_right",
-	"view_orbit_up",
-	"view_orbit_down",
-	"view_orbit_opposite",
-	"view_zoom_in",
-	"view_zoom_out",
-	"view_distract_free",
-	"viewport_mode",
-	"toggle_node_editor",
-	"toggle_2d_view",
-	"toggle_browser",
-	"node_search",
-	"operator_search",
-	///if (is_paint || is_sculpt)
-	"decal_mask",
-	"select_material",
-	"select_layer",
-	"brush_opacity",
-	"brush_angle",
-	"tool_brush",
-	"tool_eraser",
-	"tool_fill",
-	"tool_decal",
-	"tool_text",
-	"tool_clone",
-	"tool_blur",
-	"tool_smudge",
-	"tool_particle",
-	"tool_colorid",
-	"tool_picker",
-	"tool_bake",
-	"tool_gizmo",
-	"tool_material",
-	"swap_brush_eraser",
-	///end
-];
+	return keymap;
+}
 
 function base_init() {
 	base_last_window_width = sys_width();

+ 1 - 1
base/Sources/box_export.ts

@@ -167,7 +167,7 @@ function box_export_tab_export_textures(ui: zui_t, title: string, bake_material:
 			}
 		}
 		if (ui.is_hovered) {
-			zui_tooltip(tr("Export texture files") + " (" + config_keymap.file_export_textures + ")");
+			zui_tooltip(tr("Export texture files") + " (" + map_get(config_keymap, "file_export_textures") + ")");
 		}
 	}
 }

+ 11 - 6
base/Sources/box_preferences.ts

@@ -258,7 +258,11 @@ function box_preferences_show() {
 
 				ui.changed = false;
 
-				if (typeof val == "boolean") {
+				if (key == "FILL_WINDOW_BG" ||
+					key == "FILL_BUTTON_BG" ||
+					key == "FILL_ACCENT_BG" ||
+					key == "FULL_TABS" ||
+					key == "ROUND_CORNERS") {
 					h.selected = val;
 					let b: bool = zui_check(h, key);
 					theme[key] = b;
@@ -572,7 +576,7 @@ function box_preferences_show() {
 						zui_row([0.5, 0.5]);
 						let keymap_name: string = zui_text_input(zui_handle(__ID__, { text: "new_keymap" }), tr("Name"));
 						if (zui_button(tr("OK")) || ui.is_return_down) {
-							let template: string = json_stringify(base_default_keymap);
+							let template: string = json_stringify(base_get_default_keymap());
 							if (!ends_with(keymap_name, ".json")) {
 								keymap_name += ".json";
 							}
@@ -610,12 +614,13 @@ function box_preferences_show() {
 
 			let index: i32 = 0;
 			ui.changed = false;
-			for (let i: i32 = 0; i < base_keymap_keys.length; ++i) {
-				let key: string = base_keymap_keys[i];
+			let keys: string[] = map_keys_to_array(config_keymap);
+			for (let i: i32 = 0; i < keys.length; ++i) {
+				let key: string = keys[i];
 				let h: zui_handle_t = zui_nest(zui_handle(__ID__), index++);
-				h.text = config_keymap[key];
+				h.text = map_get(config_keymap, key);
 				let text: string = zui_text_input(h, key, zui_align_t.LEFT);
-				config_keymap[key] = text;
+				map_set(config_keymap, key, text);
 			}
 			if (ui.changed) {
 				config_apply();

+ 17 - 17
base/Sources/camera.ts

@@ -42,24 +42,24 @@ function camera_update() {
 	}
 
 	let modif_key: bool = keyboard_down("alt") || keyboard_down("shift") || keyboard_down("control");
-	let modif: bool = modif_key || config_keymap.action_rotate == "middle";
+	let modif: bool = modif_key || map_get(config_keymap, "action_rotate") == "middle";
 	let default_keymap: bool = config_raw.keymap == "default.json";
 
-	if (operator_shortcut(config_keymap.action_rotate, shortcut_type_t.STARTED) ||
-		operator_shortcut(config_keymap.action_zoom, shortcut_type_t.STARTED) ||
-		operator_shortcut(config_keymap.action_pan, shortcut_type_t.STARTED) ||
-		operator_shortcut(config_keymap.rotate_envmap, shortcut_type_t.STARTED) ||
-		operator_shortcut(config_keymap.rotate_light, shortcut_type_t.STARTED) ||
+	if (operator_shortcut(map_get(config_keymap, "action_rotate"), shortcut_type_t.STARTED) ||
+		operator_shortcut(map_get(config_keymap, "action_zoom"), shortcut_type_t.STARTED) ||
+		operator_shortcut(map_get(config_keymap, "action_pan"), shortcut_type_t.STARTED) ||
+		operator_shortcut(map_get(config_keymap, "rotate_envmap"), shortcut_type_t.STARTED) ||
+		operator_shortcut(map_get(config_keymap, "rotate_light"), shortcut_type_t.STARTED) ||
 		(mouse_started("right") && !modif) ||
 		(mouse_started("middle") && !modif) ||
 		(mouse_wheel_delta != 0 && !modif_key)) {
 		camera_controls_down = true;
 	}
-	else if (!operator_shortcut(config_keymap.action_rotate, shortcut_type_t.DOWN) &&
-		!operator_shortcut(config_keymap.action_zoom, shortcut_type_t.DOWN) &&
-		!operator_shortcut(config_keymap.action_pan, shortcut_type_t.DOWN) &&
-		!operator_shortcut(config_keymap.rotate_envmap, shortcut_type_t.DOWN) &&
-		!operator_shortcut(config_keymap.rotate_light, shortcut_type_t.DOWN) &&
+	else if (!operator_shortcut(map_get(config_keymap, "action_rotate"), shortcut_type_t.DOWN) &&
+		!operator_shortcut(map_get(config_keymap, "action_zoom"), shortcut_type_t.DOWN) &&
+		!operator_shortcut(map_get(config_keymap, "action_pan"), shortcut_type_t.DOWN) &&
+		!operator_shortcut(map_get(config_keymap, "rotate_envmap"), shortcut_type_t.DOWN) &&
+		!operator_shortcut(map_get(config_keymap, "rotate_light"), shortcut_type_t.DOWN) &&
 		!(mouse_down("right") && !modif) &&
 		!(mouse_down("middle") && !modif) &&
 		(mouse_wheel_delta == 0 && !modif_key)) {
@@ -71,7 +71,7 @@ function camera_update() {
 	}
 
 	let controls: camera_controls_t = context_raw.camera_controls;
-	if (controls == camera_controls_t.ORBIT && (operator_shortcut(config_keymap.action_rotate, shortcut_type_t.DOWN) || (mouse_down("right") && !modif && default_keymap))) {
+	if (controls == camera_controls_t.ORBIT && (operator_shortcut(map_get(config_keymap, "action_rotate"), shortcut_type_t.DOWN) || (mouse_down("right") && !modif && default_keymap))) {
 		camera_redraws = 2;
 		let dist: f32 = camera_distance();
 		transform_move(camera.base.transform, camera_object_look_world(camera), dist);
@@ -82,7 +82,7 @@ function camera_update() {
 		}
 		transform_move(camera.base.transform, camera_object_look_world(camera), -dist);
 	}
-	else if (controls == camera_controls_t.ROTATE && (operator_shortcut(config_keymap.action_rotate, shortcut_type_t.DOWN) || (mouse_down("right") && !modif && default_keymap))) {
+	else if (controls == camera_controls_t.ROTATE && (operator_shortcut(map_get(config_keymap, "action_rotate"), shortcut_type_t.DOWN) || (mouse_down("right") && !modif && default_keymap))) {
 		camera_redraws = 2;
 		let t: transform_t = context_main_object().base.transform;
 		let up: vec4_t = vec4_normalize(transform_up(t));
@@ -98,7 +98,7 @@ function camera_update() {
 	if (controls == camera_controls_t.ROTATE || controls == camera_controls_t.ORBIT) {
 		camera_pan_action(modif, default_keymap);
 
-		if (operator_shortcut(config_keymap.action_zoom, shortcut_type_t.DOWN)) {
+		if (operator_shortcut(map_get(config_keymap, "action_zoom"), shortcut_type_t.DOWN)) {
 			camera_redraws = 2;
 			let f: f32 = camera_get_zoom_delta() / 150;
 			f *= camera_get_zoom_speed();
@@ -170,7 +170,7 @@ function camera_update() {
 		transform_rotate(camera.base.transform, camera_object_right(camera), -mouse_movement_y / 200 * config_raw.camera_rotation_speed);
 	}
 
-	if (operator_shortcut(config_keymap.rotate_light, shortcut_type_t.DOWN)) {
+	if (operator_shortcut(map_get(config_keymap, "rotate_light"), shortcut_type_t.DOWN)) {
 		camera_redraws = 2;
 		let light: light_object_t = scene_lights[0];
 		context_raw.light_angle = (context_raw.light_angle + ((mouse_movement_x / 100) % (2 * math_pi()) + 2 * math_pi())) % (2 * math_pi());
@@ -179,7 +179,7 @@ function camera_update() {
 		transform_decompose(light.base.transform);
 	}
 
-	if (operator_shortcut(config_keymap.rotate_envmap, shortcut_type_t.DOWN)) {
+	if (operator_shortcut(map_get(config_keymap, "rotate_envmap"), shortcut_type_t.DOWN)) {
 		camera_redraws = 2;
 		context_raw.envmap_angle -= mouse_movement_x / 100;
 	}
@@ -224,7 +224,7 @@ function camera_reset(view_index: i32 = -1) {
 
 function camera_pan_action(modif: bool, default_keymap: bool) {
 	let camera: camera_object_t = scene_camera;
-	if (operator_shortcut(config_keymap.action_pan, shortcut_type_t.DOWN) || (mouse_down("middle") && !modif && default_keymap)) {
+	if (operator_shortcut(map_get(config_keymap, "action_pan"), shortcut_type_t.DOWN) || (mouse_down("middle") && !modif && default_keymap)) {
 		camera_redraws = 2;
 		let look: vec4_t = vec4_mult(vec4_normalize(transform_look(camera.base.transform)), mouse_movement_y / 150 * config_raw.camera_pan_speed);
 		let right: vec4_t = vec4_mult(vec4_normalize(transform_right(camera.base.transform)), -mouse_movement_x / 150 * config_raw.camera_pan_speed);

+ 20 - 32
base/Sources/config.ts

@@ -1,6 +1,6 @@
 
 let config_raw: config_t = null;
-let config_keymap: any;
+let config_keymap: map_t<string, string>;
 let config_loaded: bool = false;
 let config_button_align: zui_align_t = zui_align_t.LEFT;
 let config_default_button_spacing: string = "       ";
@@ -181,19 +181,14 @@ function config_apply() {
 }
 
 function config_load_keymap() {
-	if (config_raw.keymap == "default.json") { // Built-in default
-		config_keymap = base_default_keymap;
-	}
-	else {
+	config_keymap = base_get_default_keymap();
+	if (config_raw.keymap != "default.json") {
 		let blob: buffer_t = data_get_blob("keymap_presets/" + config_raw.keymap);
-		config_keymap = json_parse(sys_buffer_to_string(blob));
-		// Fill in undefined keys with defaults
-		for (let i: i32 = 0; i < base_keymap_keys.length; ++i) {
-			let key: string = base_keymap_keys[i];
-			if (!(key in config_keymap)) {
-				let adefault_keymap: any = base_default_keymap;
-				config_keymap[key] = adefault_keymap[key];
-			}
+		let new_keymap: map_t<string, string> = json_parse_to_map(sys_buffer_to_string(blob));
+		let keys: string[] = map_keys_to_array(new_keymap);
+		for (let i: i32 = 0; i < keys.length; ++i) {
+			let key: string = keys[i];
+			map_set(config_keymap, key, map_get(new_keymap, key));
 		}
 	}
 }
@@ -254,36 +249,29 @@ function config_get_texture_res_pos(i: i32): i32 {
 		   i == 16384 ? texture_res_t.RES16384 : 0;
 }
 
-function config_load_theme(theme: string, tagRedraw: bool = true) {
-	if (theme == "default.json") { // Built-in default
-		base_theme = zui_theme_create();
-	}
-	else {
+function config_load_theme(theme: string, tag_redraw: bool = true) {
+	base_theme = zui_theme_create();
+
+	if (theme != "default.json") {
 		let b: buffer_t = data_get_blob("themes/" + theme);
 		let parsed: any = json_parse(sys_buffer_to_string(b));
-		base_theme = zui_theme_create();
-		for (let key in base_theme) {
-			if (key == "theme_") {
-				continue;
-			}
-			if (starts_with(key, "set_")) {
-				continue;
-			}
-			if (starts_with(key, "get_")) {
-				key = substring(key, 4, key.length);
-			}
-			let atheme: any = base_theme;
-			atheme[key] = parsed[key];
+		for (let i: i32 = 0; i < zui_theme_keys.length; ++i) {
+			let key: string = zui_theme_keys[i];
+			// @ts-ignore
+			base_theme[key] = parsed[key];
 		}
 	}
+
 	base_theme.FILL_WINDOW_BG = true;
-	if (tagRedraw) {
+
+	if (tag_redraw) {
 		for (let i: i32 = 0; i < base_get_uis().length; ++i) {
 			let ui: zui_t = base_get_uis()[i];
 			ui.t = base_theme;
 		}
 		ui_base_tag_ui_redraw();
 	}
+
 	if (config_raw.touch_ui) {
 		// Enlarge elements
 		base_theme.FULL_TABS = true;

+ 2 - 2
base/Sources/context.ts

@@ -634,7 +634,7 @@ function context_select_tool(i: i32) {
 	make_material_parse_mesh_material();
 	context_raw.ddirty = 3;
 	let _viewport_mode: viewport_mode_t = context_raw.viewport_mode;
-	context_raw.viewport_mode = -1 as viewport_mode_t;
+	context_raw.viewport_mode = viewport_mode_t.MINUS_ONE;
 	context_set_viewport_mode(_viewport_mode);
 
 	///if (is_paint || is_sculpt)
@@ -814,7 +814,7 @@ function context_get_area_type(): area_type_t {
 		return area_type_t.MATERIALS;
 	}
 	///end
-	return -1 as area_type_t;
+	return area_type_t.MINUS_ONE;
 }
 
 function context_set_viewport_mode(mode: viewport_mode_t) {

+ 2 - 0
base/Sources/enums.ts

@@ -46,6 +46,7 @@ enum bake_up_axis_t {
 }
 
 enum viewport_mode_t {
+	MINUS_ONE = -1,
 	LIT = 0,
 	BASE_COLOR = 1,
 	NORMAL_MAP = 2,
@@ -254,6 +255,7 @@ enum workspace_tool_t {
 }
 
 enum area_type_t {
+	MINUS_ONE = -1,
 	VIEWPORT = 0,
 	VIEW2D = 1,
 	LAYERS = 2,

+ 3 - 3
base/Sources/gizmo.ts

@@ -11,7 +11,7 @@ function gizmo_update() {
 	let is_decal: bool = base_is_decal_layer();
 
 	let gizmo: object_t = context_raw.gizmo;
-	let hide: bool = operator_shortcut(config_keymap.stencil_hide, shortcut_type_t.DOWN);
+	let hide: bool = operator_shortcut(map_get(config_keymap, "stencil_hide"), shortcut_type_t.DOWN);
 	gizmo.visible = (is_object || is_decal) && !hide;
 	if (!gizmo.visible) {
 		return;
@@ -82,9 +82,9 @@ function gizmo_update() {
 
 			transform_build_matrix(paint_object.transform);
 			///if arm_physics
-			let pb: any = (paint_object as any).physicsBody;
+			let pb: physics_body_t = map_get(physics_body_object_map, paint_object);
 			if (pb != null) {
-				pb.syncTransform();
+				physics_body_sync_transform(pb);
 			}
 			///end
 		}

+ 7 - 2
base/Sources/logic_node.ts

@@ -2,12 +2,17 @@
 type logic_node_t = {
 	inputs?: logic_node_input_t[];
 	outputs?: logic_node_t[][];
-	get?: (self: any, from: i32)=>any;
+	get?: (self: any, from: i32)=>logic_node_value_t;
 	get_as_image?: (self: any, from: i32)=>image_t;
 	get_cached_image?: (self: any)=>image_t;
 	set?: (self: any, value: any)=>void;
 };
 
+type logic_node_value_t = {
+	_f32?: f32;
+	_any?: any;
+};
+
 type logic_node_input_t = {
 	node?: any; // logic_node_t
 	from?: i32; // Socket index
@@ -53,7 +58,7 @@ function logic_node_input_create(node: logic_node_t, from: i32): logic_node_inpu
 	return inp;
 }
 
-function logic_node_input_get(self: logic_node_input_t): any {
+function logic_node_input_get(self: logic_node_input_t): logic_node_value_t {
 	return self.node.base.get(self.node, self.from);
 }
 

+ 16 - 17
base/Sources/main.ts

@@ -43,23 +43,22 @@ function main_start() {
 		base_init_layout();
 	}
 	krom_set_app_name(manifest_title);
-	app_init(function () {
-		let o: object_t = scene_set_active("Scene");
-		uniforms_ext_init();
-		render_path_base_init();
-
-		if (context_raw.render_mode == render_mode_t.FORWARD) {
-			render_path_deferred_init(); // Allocate gbuffer
-			render_path_forward_init();
-			render_path_commands = render_path_forward_commands;
-		}
-		else {
-			render_path_deferred_init();
-			render_path_commands = render_path_deferred_commands;
-		}
-
-		base_init();
-	});
+	app_init();
+	scene_set_active("Scene");
+	uniforms_ext_init();
+	render_path_base_init();
+
+	if (context_raw.render_mode == render_mode_t.FORWARD) {
+		render_path_deferred_init(); // Allocate gbuffer
+		render_path_forward_init();
+		render_path_commands = render_path_forward_commands;
+	}
+	else {
+		render_path_deferred_init();
+		render_path_commands = render_path_deferred_commands;
+	}
+
+	base_init();
 }
 
 ///if arm_snapshot

+ 3 - 2
base/Sources/nodes/boolean_node.ts

@@ -13,12 +13,13 @@ function boolean_node_create(arg: bool): boolean_node_t {
 	return n;
 }
 
-function boolean_node_get(self: boolean_node_t, from: i32): any {
+function boolean_node_get(self: boolean_node_t, from: i32): logic_node_value_t {
 	if (self.base.inputs.length > 0) {
 		return logic_node_input_get(self.base.inputs[0]);
 	}
 	else {
-		return self.value;
+		let v: logic_node_value_t = { _f32: self.value ? 1.0 : 0.0 };
+		return v;
 	}
 }
 

+ 3 - 2
base/Sources/nodes/color_node.ts

@@ -19,12 +19,13 @@ function color_node_create(args: any): color_node_t {
 	return n;
 }
 
-function color_node_get(self: color_node_t, from: i32): any {
+function color_node_get(self: color_node_t, from: i32): logic_node_value_t {
 	if (self.base.inputs.length > 0) {
 		return logic_node_input_get(self.base.inputs[0]);
 	}
 	else {
-		return self.value;
+		let v: logic_node_value_t = { _any: self.value };
+		return v;
 	}
 }
 

+ 3 - 2
base/Sources/nodes/float_node.ts

@@ -15,12 +15,13 @@ function float_node_create(arg: f32): float_node_t {
 	return n;
 }
 
-function float_node_get(self: float_node_t, from: i32): any {
+function float_node_get(self: float_node_t, from: i32): logic_node_value_t {
 	if (self.base.inputs.length > 0) {
 		return logic_node_input_get(self.base.inputs[0]);
 	}
 	else {
-		return self.value;
+		let v: logic_node_value_t = { _f32: self.value };
+		return v;
 	}
 }
 

+ 3 - 2
base/Sources/nodes/integer_node.ts

@@ -13,12 +13,13 @@ function integer_node_create(arg: i32): integer_node_t {
 	return n;
 }
 
-function integer_node_get(self: integer_node_t, from: i32) {
+function integer_node_get(self: integer_node_t, from: i32): logic_node_value_t {
 	if (self.base.inputs.length > 0) {
 		return logic_node_input_get(self.base.inputs[0]);
 	}
 	else {
-		return self.value;
+		let v: logic_node_value_t = { _f32: self.value };
+		return v;
 	}
 }
 

+ 5 - 4
base/Sources/nodes/math_node.ts

@@ -12,9 +12,9 @@ function math_node_create(arg: any): math_node_t {
 	return n;
 }
 
-function math_node_get(self: math_node_t, from: i32): any {
-	let v1: f32 = logic_node_input_get(self.base.inputs[0]);
-	let v2: f32 = logic_node_input_get(self.base.inputs[1]);
+function math_node_get(self: math_node_t, from: i32): logic_node_value_t {
+	let v1: f32 = logic_node_input_get(self.base.inputs[0])._f32;
+	let v2: f32 = logic_node_input_get(self.base.inputs[1])._f32;
 	let f: f32 = 0.0;
 	let op: string = self.operation;
 	if (op == "Add") {
@@ -127,7 +127,8 @@ function math_node_get(self: math_node_t, from: i32): any {
 		f = f < 0.0 ? 0.0 : (f > 1.0 ? 1.0 : f);
 	}
 
-	return f;
+	let v: logic_node_value_t = { _f32: f };
+	return v;
 }
 
 let math_node_def: zui_node_t = {

+ 1 - 1
base/Sources/nodes/null_node.ts

@@ -10,6 +10,6 @@ function null_node_create(arg: any): null_node_t {
 	return n;
 }
 
-function null_node_get(self: null_node_t, from: i32): any {
+function null_node_get(self: null_node_t, from: i32): logic_node_value_t {
 	return null;
 }

+ 5 - 4
base/Sources/nodes/random_node.ts

@@ -15,10 +15,11 @@ function random_node_create(arg: any): random_node_t {
 	return n;
 }
 
-function random_node_get(self: random_node_t, from: i32): any {
-	let min: f32 = logic_node_input_get(self.base.inputs[0]);
-	let max: f32 = logic_node_input_get(self.base.inputs[1]);
-	return min + random_node_get_float() * (max - min);
+function random_node_get(self: random_node_t, from: i32): logic_node_value_t {
+	let min: f32 = logic_node_input_get(self.base.inputs[0])._f32;
+	let max: f32 = logic_node_input_get(self.base.inputs[1])._f32;
+	let v: logic_node_value_t = { _f32: min + random_node_get_float() * (max - min) };
+	return v;
 }
 
 function random_node_get_int(): i32 {

+ 8 - 5
base/Sources/nodes/separate_vector_node.ts

@@ -10,16 +10,19 @@ function separate_vector_node_create(arg: any): separate_vector_node_t {
 	return n;
 }
 
-function separate_vector_node_get(self: separate_vector_node_t, from: i32): any {
-	let vector: vec4_t = logic_node_input_get(self.base.inputs[0]);
+function separate_vector_node_get(self: separate_vector_node_t, from: i32): logic_node_value_t {
+	let vector: vec4_t = logic_node_input_get(self.base.inputs[0])._any;
 	if (from == 0) {
-		return vector.x;
+		let v: logic_node_value_t = { _f32: vector.x };
+		return v;
 	}
 	else if (from == 1) {
-		return vector.y;
+		let v: logic_node_value_t = { _f32: vector.y };
+		return v;
 	}
 	else {
-		return vector.z;
+		let v: logic_node_value_t = { _f32: vector.z };
+		return v;
 	}
 }
 

+ 3 - 2
base/Sources/nodes/string_node.ts

@@ -13,12 +13,13 @@ function string_node_create(arg: string): string_node_t {
 	return n;
 }
 
-function string_node_get(self: string_node_t, from: i32): any {
+function string_node_get(self: string_node_t, from: i32): logic_node_value_t {
 	if (self.base.inputs.length > 0) {
 		return logic_node_input_get(self.base.inputs[0]);
 	}
 	else {
-		return self.value;
+		let v: logic_node_value_t = { _any: self.value };
+		return v;
 	}
 }
 

+ 7 - 4
base/Sources/nodes/time_node.ts

@@ -10,15 +10,18 @@ function time_node_create(arg: any): time_node_t {
 	return n;
 }
 
-function time_node_get(self: time_node_t, from: i32): any {
+function time_node_get(self: time_node_t, from: i32): logic_node_value_t {
 	if (from == 0) {
-		return time_time();
+		let v: logic_node_value_t = { _f32: time_time() };
+		return v;
 	}
 	else if (from == 1) {
-		return time_delta();
+		let v: logic_node_value_t = { _f32: time_delta() };
+		return v;
 	}
 	else {
-		return context_raw.brush_time;
+		let v: logic_node_value_t = { _f32: context_raw.brush_time };
+		return v;
 	}
 }
 

+ 7 - 5
base/Sources/nodes/vector_math_node.ts

@@ -13,9 +13,9 @@ function vector_math_node_create(arg: any): vector_math_node_t {
 	return n;
 }
 
-function vector_math_node_get(self: vector_math_node_t, from: i32): any {
-	let v1: vec4_t = logic_node_input_get(self.base.inputs[0]);
-	let v2: vec4_t = logic_node_input_get(self.base.inputs[1]);
+function vector_math_node_get(self: vector_math_node_t, from: i32): logic_node_value_t {
+	let v1: vec4_t = logic_node_input_get(self.base.inputs[0])._any;
+	let v2: vec4_t = logic_node_input_get(self.base.inputs[1])._any;
 	vec4_set_from(self.v, v1);
 	let f: f32 = 0.0;
 	let op: string = self.operation;
@@ -131,10 +131,12 @@ function vector_math_node_get(self: vector_math_node_t, from: i32): any {
 	}
 
 	if (from == 0) {
-		return self.v;
+		let v: logic_node_value_t = { _any: self.v };
+		return v;
 	}
 	else {
-		return f;
+		let v: logic_node_value_t = { _f32: f };
+		return v;
 	}
 }
 

+ 9 - 8
base/Sources/nodes/vector_node.ts

@@ -22,14 +22,15 @@ function vector_node_create(args: any): vector_node_t {
 	return n;
 }
 
-function vector_node_get(self: vector_node_t, from: i32): any {
-	let x: f32 = logic_node_input_get(self.base.inputs[0]);
-	let y: f32 = logic_node_input_get(self.base.inputs[1]);
-	let z: f32 = logic_node_input_get(self.base.inputs[2]);
+function vector_node_get(self: vector_node_t, from: i32): logic_node_value_t {
+	let x: f32 = logic_node_input_get(self.base.inputs[0])._f32;
+	let y: f32 = logic_node_input_get(self.base.inputs[1])._f32;
+	let z: f32 = logic_node_input_get(self.base.inputs[2])._f32;
 	self.value.x = x;
 	self.value.y = y;
 	self.value.z = z;
-	return self.value;
+	let v: logic_node_value_t = { _any: self.value };
+	return v;
 }
 
 function vector_node_get_as_image(self: vector_node_t, from: i32): image_t {
@@ -41,9 +42,9 @@ function vector_node_get_as_image(self: vector_node_t, from: i32): image_t {
 	}
 	let b: buffer_t = buffer_create(16);
 	let v: buffer_view_t = buffer_view_create(b);
-	buffer_view_set_f32(v, 0, (self.base.inputs[0].node as any).value);
-	buffer_view_set_f32(v, 4, (self.base.inputs[1].node as any).value);
-	buffer_view_set_f32(v, 8, (self.base.inputs[2].node as any).value);
+	buffer_view_set_f32(v, 0, self.base.inputs[0].node.value);
+	buffer_view_set_f32(v, 4, self.base.inputs[1].node.value);
+	buffer_view_set_f32(v, 8, self.base.inputs[2].node.value);
 	buffer_view_set_f32(v, 12, 1.0);
 	self.image = image_from_bytes(b, 1, 1, tex_format_t.RGBA128);
 	return self.image;

+ 4 - 3
base/Sources/operator.ts

@@ -13,9 +13,10 @@ function operator_run(name: string) {
 
 function operator_update() {
 	if (mouse_started_any() || keyboard_started_any()) {
-		for (let i: i32 = 0; i < base_keymap_keys.length; ++i) {
-			let op: string = base_keymap_keys[i];
-			if (operator_shortcut(config_keymap[op])) {
+		let keys: string[] = map_keys_to_array(config_keymap);
+		for (let i: i32 = 0; i < keys.length; ++i) {
+			let op: string = keys[i];
+			if (operator_shortcut(map_get(config_keymap, op))) {
 				operator_run(op);
 			}
 		}

+ 2 - 1
base/Sources/physics_body.ts

@@ -48,6 +48,7 @@ let physics_body_quat: quat_t = quat_create();
 let physics_body_convex_hull_cache: map_t<mesh_data_t, Ammo.btConvexHullShape> = map_create();
 let physics_body_triangle_mesh_cache: map_t<mesh_data_t, Ammo.btTriangleMesh> = map_create();
 let physics_body_users_cache: map_t<mesh_data_t, i32> = map_create();
+let physics_body_object_map: map_t<object_t, physics_body_t> = map_create();
 
 function physics_body_create(): physics_body_t {
 	if (physics_body_first) {
@@ -91,7 +92,7 @@ function physics_body_set_mass(pb: physics_body_t, f: f32) {
 		let t: physics_body_t = physics_body_create();
 		t._mass = f;
 		physics_body_init(t, pb.object);
-		(pb.object as any).physicsBody = t;
+		map_set(physics_body_object_map, pb.object, t);
 	}
 	else {
 		pb._mass = f;

+ 1 - 1
base/Sources/render_path_base.ts

@@ -545,7 +545,7 @@ function render_path_base_draw_gbuffer() {
 		}
 	}
 
-	let hide: bool = operator_shortcut(config_keymap.stencil_hide, shortcut_type_t.DOWN) || keyboard_down("control");
+	let hide: bool = operator_shortcut(map_get(config_keymap, "stencil_hide"), shortcut_type_t.DOWN) || keyboard_down("control");
 	let is_decal: bool = base_is_decal_layer();
 	if (is_decal && !hide) {
 		line_draw_render(context_raw.layer.decal_mat);

+ 4 - 4
base/Sources/tab_materials.ts

@@ -43,7 +43,7 @@ function tab_materials_button_nodes() {
 		ui_base_show_material_nodes();
 	}
 	else if (ui.is_hovered) {
-		zui_tooltip(tr("Show Node Editor") + " (" + config_keymap.toggle_node_editor + ")");
+		zui_tooltip(tr("Show Node Editor") + " (" + map_get(config_keymap, "toggle_node_editor") + ")");
 	}
 }
 
@@ -116,7 +116,7 @@ function tab_materials_draw_slots(mini: bool) {
 			// Draw material numbers when selecting a material via keyboard shortcut
 			let is_typing: bool = ui.is_typing || ui_view2d_ui.is_typing || ui_nodes_ui.is_typing;
 			if (!is_typing) {
-				if (i < 9 && operator_shortcut(config_keymap.select_material, shortcut_type_t.DOWN)) {
+				if (i < 9 && operator_shortcut(map_get(config_keymap, "select_material"), shortcut_type_t.DOWN)) {
 					let number: string = any_to_string(i + 1);
 					let width: i32 = g2_font_width(ui.font, ui.font_size, number) + 10;
 					let height: i32 = g2_font_height(ui.font, ui.font_size);
@@ -237,7 +237,7 @@ function tab_materials_draw_slots(mini: bool) {
 			if (ui.is_hovered) {
 				zui_tooltip_image(imgFull);
 				if (i < 9) {
-					zui_tooltip(project_materials[i].canvas.name + " - (" + config_keymap.select_material + " " + (i + 1) + ")");
+					zui_tooltip(project_materials[i].canvas.name + " - (" + map_get(config_keymap, "select_material") + " " + (i + 1) + ")");
 				}
 				else {
 					zui_tooltip(project_materials[i].canvas.name);
@@ -250,7 +250,7 @@ function tab_materials_draw_slots(mini: bool) {
 				zui_text(project_materials[i].canvas.name, zui_align_t.CENTER);
 				if (ui.is_hovered) {
 					if (i < 9) {
-						zui_tooltip(project_materials[i].canvas.name + " - (" + config_keymap.select_material + " " + (i + 1) + ")");
+						zui_tooltip(project_materials[i].canvas.name + " - (" + map_get(config_keymap, "select_material") + " " + (i + 1) + ")");
 					}
 					else {
 						zui_tooltip(project_materials[i].canvas.name);

+ 1 - 1
base/Sources/tab_meshes.ts

@@ -28,7 +28,7 @@ function tab_meshes_draw(htab: zui_handle_t) {
 
 		if (zui_button(tr("Import"))) {
 			ui_menu_draw(function (ui: zui_t) {
-				if (ui_menu_button(ui, tr("Replace Existing"), config_keymap.file_import_assets)) {
+				if (ui_menu_button(ui, tr("Replace Existing"), map_get(config_keymap, "file_import_assets"))) {
 					project_import_mesh(true);
 				}
 				if (ui_menu_button(ui, tr("Append"))) {

+ 1 - 1
base/Sources/tab_textures.ts

@@ -24,7 +24,7 @@ function tab_textures_draw(htab: zui_handle_t) {
 			});
 		}
 		if (ui.is_hovered) {
-			zui_tooltip(tr("Import texture file") + " (" + config_keymap.file_import_assets + ")");
+			zui_tooltip(tr("Import texture file") + " (" + map_get(config_keymap, "file_import_assets") + ")");
 		}
 
 		if (zui_button(tr("2D View"))) {

+ 7 - 8
base/Sources/translator.ts

@@ -18,8 +18,7 @@ function tr(id: string, vars: map_t<string, string> = null): string {
 
 	// English is the source language
 	if (config_raw.locale != "en" && map_get(translator_translations, id) != null) {
-		let atranslations: any = translator_translations as any;
-		translation = atranslations[id];
+		translation = map_get(translator_translations, id);
 	}
 
 	if (vars != null) {
@@ -69,10 +68,11 @@ function translator_load_translations(new_locale: string) {
 		// Load the translation file
 		let translation_json: string = sys_buffer_to_string(krom_load_blob("data/locale/" + config_raw.locale + ".json"));
 
-		let data: any = json_parse(translation_json);
-		for (let field in data) {
-			let atranslations: any = translator_translations as any;
-			atranslations[field] = data[field];
+		let data: map_t<string, string> = json_parse_to_map(translation_json);
+		let keys: string[] = map_keys_to_array(data);
+		for (let i: i32 = 0; i < keys.length; ++i) {
+			let field: string = keys[i];
+			map_set(translator_translations, field, map_get(data, field));
 		}
 	}
 
@@ -148,8 +148,7 @@ function translator_init_font(cjk: bool, font_path: string, font_scale: f32) {
 
 		let f: g2_font_t = data_get_font(font_path);
 		if (cjk) {
-			let acjk_font_indices: any = translator_cjk_font_indices as any;
-			let font_index: i32 = map_get(translator_cjk_font_indices, config_raw.locale) != null ? acjk_font_indices[config_raw.locale] : 0;
+			let font_index: i32 = map_get(translator_cjk_font_indices, config_raw.locale) != null ? map_get(translator_cjk_font_indices, config_raw.locale) : 0;
 			g2_font_set_font_index(f, font_index);
 		}
 		base_font = f;

+ 106 - 105
base/Sources/ui_base.ts

@@ -241,7 +241,7 @@ function ui_base_update() {
 	}
 
 	if (!ui_nodes_ui.is_typing && !ui_base_ui.is_typing) {
-		if (operator_shortcut(config_keymap.toggle_node_editor)) {
+		if (operator_shortcut(map_get(config_keymap, "toggle_node_editor"))) {
 			///if (is_paint || is_sculpt)
 			ui_nodes_canvas_type == canvas_type_t.MATERIAL ? ui_base_show_material_nodes() : ui_base_show_brush_nodes();
 			///end
@@ -249,11 +249,11 @@ function ui_base_update() {
 			ui_base_show_material_nodes();
 			///end
 		}
-		else if (operator_shortcut(config_keymap.toggle_browser)) {
+		else if (operator_shortcut(map_get(config_keymap, "toggle_browser"))) {
 			ui_base_toggle_browser();
 		}
 
-		else if (operator_shortcut(config_keymap.toggle_2d_view)) {
+		else if (operator_shortcut(map_get(config_keymap, "toggle_2d_view"))) {
 			///if (is_paint || is_sculpt)
 			ui_base_show_2d_view(view_2d_type_t.LAYER);
 			///else
@@ -262,29 +262,29 @@ function ui_base_update() {
 		}
 	}
 
-	if (operator_shortcut(config_keymap.file_save_as)) {
+	if (operator_shortcut(map_get(config_keymap, "file_save_as"))) {
 		project_save_as();
 	}
-	else if (operator_shortcut(config_keymap.file_save)) {
+	else if (operator_shortcut(map_get(config_keymap, "file_save"))) {
 		project_save();
 	}
-	else if (operator_shortcut(config_keymap.file_open)) {
+	else if (operator_shortcut(map_get(config_keymap, "file_open"))) {
 		project_open();
 	}
-	else if (operator_shortcut(config_keymap.file_open_recent)) {
+	else if (operator_shortcut(map_get(config_keymap, "file_open_recent"))) {
 		box_projects_show();
 	}
-	else if (operator_shortcut(config_keymap.file_reimport_mesh)) {
+	else if (operator_shortcut(map_get(config_keymap, "file_reimport_mesh"))) {
 		project_reimport_mesh();
 	}
-	else if (operator_shortcut(config_keymap.file_reimport_textures)) {
+	else if (operator_shortcut(map_get(config_keymap, "file_reimport_textures"))) {
 		project_reimport_textures();
 	}
-	else if (operator_shortcut(config_keymap.file_new)) {
+	else if (operator_shortcut(map_get(config_keymap, "file_new"))) {
 		project_new_box();
 	}
 	///if (is_paint || is_lab)
-	else if (operator_shortcut(config_keymap.file_export_textures)) {
+	else if (operator_shortcut(map_get(config_keymap, "file_export_textures"))) {
 		if (context_raw.texture_export_path == "") { // First export, ask for path
 			///if is_paint
 			context_raw.layers_export = export_mode_t.VISIBLE;
@@ -297,21 +297,21 @@ function ui_base_update() {
 			});
 		}
 	}
-	else if (operator_shortcut(config_keymap.file_export_textures_as)) {
+	else if (operator_shortcut(map_get(config_keymap, "file_export_textures_as"))) {
 		///if (is_paint || is_sculpt)
 		context_raw.layers_export = export_mode_t.VISIBLE;
 		///end
 		box_export_show_textures();
 	}
 	///end
-	else if (operator_shortcut(config_keymap.file_import_assets)) {
+	else if (operator_shortcut(map_get(config_keymap, "file_import_assets"))) {
 		project_import_asset();
 	}
-	else if (operator_shortcut(config_keymap.edit_prefs)) {
+	else if (operator_shortcut(map_get(config_keymap, "edit_prefs"))) {
 		box_preferences_show();
 	}
 
-	if (keyboard_started(config_keymap.view_distract_free) || (keyboard_started("escape") && !ui_base_show && !ui_box_show)) {
+	if (keyboard_started(map_get(config_keymap, "view_distract_free")) || (keyboard_started("escape") && !ui_base_show && !ui_box_show)) {
 		ui_base_toggle_distract_free();
 	}
 
@@ -323,28 +323,28 @@ function ui_base_update() {
 
 	///if (is_paint || is_sculpt)
 	let decal: bool = context_raw.tool == workspace_tool_t.DECAL || context_raw.tool == workspace_tool_t.TEXT;
-	let decal_mask: bool = decal && operator_shortcut(config_keymap.decal_mask, shortcut_type_t.DOWN);
+	let decal_mask: bool = decal && operator_shortcut(map_get(config_keymap, "decal_mask"), shortcut_type_t.DOWN);
 
 	if ((context_raw.brush_can_lock || context_raw.brush_locked) && mouse_moved) {
-		if (operator_shortcut(config_keymap.brush_radius, shortcut_type_t.DOWN) ||
-			operator_shortcut(config_keymap.brush_opacity, shortcut_type_t.DOWN) ||
-			operator_shortcut(config_keymap.brush_angle, shortcut_type_t.DOWN) ||
-			(decal_mask && operator_shortcut(config_keymap.decal_mask + "+" + config_keymap.brush_radius, shortcut_type_t.DOWN))) {
+		if (operator_shortcut(map_get(config_keymap, "brush_radius"), shortcut_type_t.DOWN) ||
+			operator_shortcut(map_get(config_keymap, "brush_opacity"), shortcut_type_t.DOWN) ||
+			operator_shortcut(map_get(config_keymap, "brush_angle"), shortcut_type_t.DOWN) ||
+			(decal_mask && operator_shortcut(map_get(config_keymap, "decal_mask") + "+" + map_get(config_keymap, "brush_radius"), shortcut_type_t.DOWN))) {
 			if (context_raw.brush_locked) {
-				if (operator_shortcut(config_keymap.brush_opacity, shortcut_type_t.DOWN)) {
+				if (operator_shortcut(map_get(config_keymap, "brush_opacity"), shortcut_type_t.DOWN)) {
 					context_raw.brush_opacity += mouse_movement_x / 500;
 					context_raw.brush_opacity = math_max(0.0, math_min(1.0, context_raw.brush_opacity));
 					context_raw.brush_opacity = math_round(context_raw.brush_opacity * 100) / 100;
 					context_raw.brush_opacity_handle.value = context_raw.brush_opacity;
 				}
-				else if (operator_shortcut(config_keymap.brush_angle, shortcut_type_t.DOWN)) {
+				else if (operator_shortcut(map_get(config_keymap, "brush_angle"), shortcut_type_t.DOWN)) {
 					context_raw.brush_angle += mouse_movement_x / 5;
 					context_raw.brush_angle = math_floor(context_raw.brush_angle) % 360;
 					if (context_raw.brush_angle < 0) context_raw.brush_angle += 360;
 					context_raw.brush_angle_handle.value = context_raw.brush_angle;
 					make_material_parse_paint_material();
 				}
-				else if (decal_mask && operator_shortcut(config_keymap.decal_mask + "+" + config_keymap.brush_radius, shortcut_type_t.DOWN)) {
+				else if (decal_mask && operator_shortcut(map_get(config_keymap, "decal_mask") + "+" + map_get(config_keymap, "brush_radius"), shortcut_type_t.DOWN)) {
 					context_raw.brush_decal_mask_radius += mouse_movement_x / 150;
 					context_raw.brush_decal_mask_radius = math_max(0.01, math_min(4.0, context_raw.brush_decal_mask_radius));
 					context_raw.brush_decal_mask_radius = math_round(context_raw.brush_decal_mask_radius * 100) / 100;
@@ -368,7 +368,7 @@ function ui_base_update() {
 
 	///if is_lab
 	if ((context_raw.brush_can_lock || context_raw.brush_locked) && mouse_moved) {
-		if (operator_shortcut(config_keymap.brush_radius, shortcut_type_t.DOWN)) {
+		if (operator_shortcut(map_get(config_keymap, "brush_radius"), shortcut_type_t.DOWN)) {
 			if (context_raw.brush_locked) {
 				context_raw.brush_radius += mouse_movement_x / 150;
 				context_raw.brush_radius = math_max(0.01, math_min(4.0, context_raw.brush_radius));
@@ -388,7 +388,7 @@ function ui_base_update() {
 
 	///if (is_paint || is_sculpt)
 	if (!is_typing) {
-		if (operator_shortcut(config_keymap.select_material, shortcut_type_t.DOWN)) {
+		if (operator_shortcut(map_get(config_keymap, "select_material"), shortcut_type_t.DOWN)) {
 			ui_base_hwnds[tab_area_t.SIDEBAR1].redraws = 2;
 			for (let i: i32 = 1; i < 10; ++i) {
 				if (keyboard_started(i + "")) {
@@ -396,7 +396,7 @@ function ui_base_update() {
 				}
 			}
 		}
-		else if (operator_shortcut(config_keymap.select_layer, shortcut_type_t.DOWN)) {
+		else if (operator_shortcut(map_get(config_keymap, "select_layer"), shortcut_type_t.DOWN)) {
 			ui_base_hwnds[tab_area_t.SIDEBAR0].redraws = 2;
 			for (let i: i32 = 1; i < 10; ++i) {
 				if (keyboard_started(i + "")) {
@@ -412,49 +412,49 @@ function ui_base_update() {
 
 		///if is_paint
 		if (!mouse_down("right")) { // Fly mode off
-			if (operator_shortcut(config_keymap.tool_brush)) {
+			if (operator_shortcut(map_get(config_keymap, "tool_brush"))) {
 				context_select_tool(workspace_tool_t.BRUSH);
 			}
-			else if (operator_shortcut(config_keymap.tool_eraser)) {
+			else if (operator_shortcut(map_get(config_keymap, "tool_eraser"))) {
 				context_select_tool(workspace_tool_t.ERASER);
 			}
-			else if (operator_shortcut(config_keymap.tool_fill)) {
+			else if (operator_shortcut(map_get(config_keymap, "tool_fill"))) {
 				context_select_tool(workspace_tool_t.FILL);
 			}
-			else if (operator_shortcut(config_keymap.tool_colorid)) {
+			else if (operator_shortcut(map_get(config_keymap, "tool_colorid"))) {
 				context_select_tool(workspace_tool_t.COLORID);
 			}
-			else if (operator_shortcut(config_keymap.tool_decal)) {
+			else if (operator_shortcut(map_get(config_keymap, "tool_decal"))) {
 				context_select_tool(workspace_tool_t.DECAL);
 			}
-			else if (operator_shortcut(config_keymap.tool_text)) {
+			else if (operator_shortcut(map_get(config_keymap, "tool_text"))) {
 				context_select_tool(workspace_tool_t.TEXT);
 			}
-			else if (operator_shortcut(config_keymap.tool_clone)) {
+			else if (operator_shortcut(map_get(config_keymap, "tool_clone"))) {
 				context_select_tool(workspace_tool_t.CLONE);
 			}
-			else if (operator_shortcut(config_keymap.tool_blur)) {
+			else if (operator_shortcut(map_get(config_keymap, "tool_blur"))) {
 				context_select_tool(workspace_tool_t.BLUR);
 			}
-			else if (operator_shortcut(config_keymap.tool_smudge)) {
+			else if (operator_shortcut(map_get(config_keymap, "tool_smudge"))) {
 				context_select_tool(workspace_tool_t.SMUDGE);
 			}
-			else if (operator_shortcut(config_keymap.tool_particle)) {
+			else if (operator_shortcut(map_get(config_keymap, "tool_particle"))) {
 				context_select_tool(workspace_tool_t.PARTICLE);
 			}
-			else if (operator_shortcut(config_keymap.tool_picker)) {
+			else if (operator_shortcut(map_get(config_keymap, "tool_picker"))) {
 				context_select_tool(workspace_tool_t.PICKER);
 			}
-			else if (operator_shortcut(config_keymap.tool_bake)) {
+			else if (operator_shortcut(map_get(config_keymap, "tool_bake"))) {
 				context_select_tool(workspace_tool_t.BAKE);
 			}
-			else if (operator_shortcut(config_keymap.tool_gizmo)) {
+			else if (operator_shortcut(map_get(config_keymap, "tool_gizmo"))) {
 				context_select_tool(workspace_tool_t.GIZMO);
 			}
-			else if (operator_shortcut(config_keymap.tool_material)) {
+			else if (operator_shortcut(map_get(config_keymap, "tool_material"))) {
 				context_select_tool(workspace_tool_t.MATERIAL);
 			}
-			else if (operator_shortcut(config_keymap.swap_brush_eraser)) {
+			else if (operator_shortcut(map_get(config_keymap, "swap_brush_eraser"))) {
 				context_select_tool(context_raw.tool == workspace_tool_t.BRUSH ? workspace_tool_t.ERASER : workspace_tool_t.BRUSH);
 			}
 		}
@@ -468,10 +468,10 @@ function ui_base_update() {
 			context_raw.tool == workspace_tool_t.BLUR   ||
 			context_raw.tool == workspace_tool_t.SMUDGE   ||
 			context_raw.tool == workspace_tool_t.PARTICLE) {
-			if (operator_shortcut(config_keymap.brush_radius) ||
-				operator_shortcut(config_keymap.brush_opacity) ||
-				operator_shortcut(config_keymap.brush_angle) ||
-				(decal_mask && operator_shortcut(config_keymap.decal_mask + "+" + config_keymap.brush_radius))) {
+			if (operator_shortcut(map_get(config_keymap, "brush_radius")) ||
+				operator_shortcut(map_get(config_keymap, "brush_opacity")) ||
+				operator_shortcut(map_get(config_keymap, "brush_angle")) ||
+				(decal_mask && operator_shortcut(map_get(config_keymap, "decal_mask") + "+" + map_get(config_keymap, "brush_radius")))) {
 				context_raw.brush_can_lock = true;
 				if (!pen_connected) {
 					mouse_lock();
@@ -479,26 +479,26 @@ function ui_base_update() {
 				context_raw.lock_started_x = mouse_x;
 				context_raw.lock_started_y = mouse_y;
 			}
-			else if (operator_shortcut(config_keymap.brush_radius_decrease, shortcut_type_t.REPEAT)) {
+			else if (operator_shortcut(map_get(config_keymap, "brush_radius_decrease"), shortcut_type_t.REPEAT)) {
 				context_raw.brush_radius -= ui_base_get_radius_increment();
 				context_raw.brush_radius = math_max(math_round(context_raw.brush_radius * 100) / 100, 0.01);
 				context_raw.brush_radius_handle.value = context_raw.brush_radius;
 				ui_header_handle.redraws = 2;
 			}
-			else if (operator_shortcut(config_keymap.brush_radius_increase, shortcut_type_t.REPEAT)) {
+			else if (operator_shortcut(map_get(config_keymap, "brush_radius_increase"), shortcut_type_t.REPEAT)) {
 				context_raw.brush_radius += ui_base_get_radius_increment();
 				context_raw.brush_radius = math_round(context_raw.brush_radius * 100) / 100;
 				context_raw.brush_radius_handle.value = context_raw.brush_radius;
 				ui_header_handle.redraws = 2;
 			}
 			else if (decal_mask) {
-				if (operator_shortcut(config_keymap.decal_mask + "+" + config_keymap.brush_radius_decrease, shortcut_type_t.REPEAT)) {
+				if (operator_shortcut(map_get(config_keymap, "decal_mask") + "+" + map_get(config_keymap, "brush_radius_decrease"), shortcut_type_t.REPEAT)) {
 					context_raw.brush_decal_mask_radius -= ui_base_get_radius_increment();
 					context_raw.brush_decal_mask_radius = math_max(math_round(context_raw.brush_decal_mask_radius * 100) / 100, 0.01);
 					context_raw.brush_decal_mask_radius_handle.value = context_raw.brush_decal_mask_radius;
 					ui_header_handle.redraws = 2;
 				}
-				else if (operator_shortcut(config_keymap.decal_mask + "+" + config_keymap.brush_radius_increase, shortcut_type_t.REPEAT)) {
+				else if (operator_shortcut(map_get(config_keymap, "decal_mask") + "+" + map_get(config_keymap, "brush_radius_increase"), shortcut_type_t.REPEAT)) {
 					context_raw.brush_decal_mask_radius += ui_base_get_radius_increment();
 					context_raw.brush_decal_mask_radius = math_round(context_raw.brush_decal_mask_radius * 100) / 100;
 					context_raw.brush_decal_mask_radius_handle.value = context_raw.brush_decal_mask_radius;
@@ -507,7 +507,7 @@ function ui_base_update() {
 			}
 		}
 
-		if (decal_mask && (operator_shortcut(config_keymap.decal_mask, shortcut_type_t.STARTED) || operator_shortcut(config_keymap.decal_mask, shortcut_type_t.RELEASED))) {
+		if (decal_mask && (operator_shortcut(map_get(config_keymap, "decal_mask"), shortcut_type_t.STARTED) || operator_shortcut(map_get(config_keymap, "decal_mask"), shortcut_type_t.RELEASED))) {
 			ui_header_handle.redraws = 2;
 		}
 		///end
@@ -519,7 +519,7 @@ function ui_base_update() {
 				context_raw.tool == workspace_tool_t.CLONE  ||
 				context_raw.tool == workspace_tool_t.BLUR   ||
 				context_raw.tool == workspace_tool_t.SMUDGE) {
-				if (operator_shortcut(config_keymap.brush_radius)) {
+				if (operator_shortcut(map_get(config_keymap, "brush_radius"))) {
 					context_raw.brush_can_lock = true;
 					if (!pen_connected) {
 						mouse_lock();
@@ -527,13 +527,13 @@ function ui_base_update() {
 					context_raw.lock_started_x = mouse_x;
 					context_raw.lock_started_y = mouse_y;
 				}
-				else if (operator_shortcut(config_keymap.brush_radius_decrease, shortcut_type_t.REPEAT)) {
+				else if (operator_shortcut(map_get(config_keymap, "brush_radius_decrease"), shortcut_type_t.REPEAT)) {
 					context_raw.brush_radius -= ui_base_get_radius_increment();
 					context_raw.brush_radius = math_max(math_round(context_raw.brush_radius * 100) / 100, 0.01);
 					context_raw.brush_radius_handle.value = context_raw.brush_radius;
 					ui_header_handle.redraws = 2;
 				}
-				else if (operator_shortcut(config_keymap.brush_radius_increase, shortcut_type_t.REPEAT)) {
+				else if (operator_shortcut(map_get(config_keymap, "brush_radius_increase"), shortcut_type_t.REPEAT)) {
 					context_raw.brush_radius += ui_base_get_radius_increment();
 					context_raw.brush_radius = math_round(context_raw.brush_radius * 100) / 100;
 					context_raw.brush_radius_handle.value = context_raw.brush_radius;
@@ -545,52 +545,52 @@ function ui_base_update() {
 
 		// Viewpoint
 		if (mouse_view_x() < app_w()) {
-			if (operator_shortcut(config_keymap.view_reset)) {
+			if (operator_shortcut(map_get(config_keymap, "view_reset"))) {
 				viewport_reset();
 				viewport_scale_to_bounds();
 			}
-			else if (operator_shortcut(config_keymap.view_back)) {
+			else if (operator_shortcut(map_get(config_keymap, "view_back"))) {
 				viewport_set_view(0, 1, 0, math_pi() / 2, 0, math_pi());
 			}
-			else if (operator_shortcut(config_keymap.view_front)) {
+			else if (operator_shortcut(map_get(config_keymap, "view_front"))) {
 				viewport_set_view(0, -1, 0, math_pi() / 2, 0, 0);
 			}
-			else if (operator_shortcut(config_keymap.view_left)) {
+			else if (operator_shortcut(map_get(config_keymap, "view_left"))) {
 				viewport_set_view(-1, 0, 0, math_pi() / 2, 0, -math_pi() / 2);
 			}
-			else if (operator_shortcut(config_keymap.view_right)) {
+			else if (operator_shortcut(map_get(config_keymap, "view_right"))) {
 				viewport_set_view(1, 0, 0, math_pi() / 2, 0, math_pi() / 2);
 			}
-			else if (operator_shortcut(config_keymap.view_bottom)) {
+			else if (operator_shortcut(map_get(config_keymap, "view_bottom"))) {
 				viewport_set_view(0, 0, -1, math_pi(), 0, math_pi());
 			}
-			else if (operator_shortcut(config_keymap.view_camera_type)) {
+			else if (operator_shortcut(map_get(config_keymap, "view_camera_type"))) {
 				context_raw.camera_type = context_raw.camera_type == camera_type_t.PERSPECTIVE ? camera_type_t.ORTHOGRAPHIC : camera_type_t.PERSPECTIVE;
 				context_raw.cam_handle.position = context_raw.camera_type;
 				viewport_update_camera_type(context_raw.camera_type);
 			}
-			else if (operator_shortcut(config_keymap.view_orbit_left, shortcut_type_t.REPEAT)) {
+			else if (operator_shortcut(map_get(config_keymap, "view_orbit_left"), shortcut_type_t.REPEAT)) {
 				viewport_orbit(-math_pi() / 12, 0);
 			}
-			else if (operator_shortcut(config_keymap.view_orbit_right, shortcut_type_t.REPEAT)) {
+			else if (operator_shortcut(map_get(config_keymap, "view_orbit_right"), shortcut_type_t.REPEAT)) {
 				viewport_orbit(math_pi() / 12, 0);
 			}
-			else if (operator_shortcut(config_keymap.view_orbit_up, shortcut_type_t.REPEAT)) {
+			else if (operator_shortcut(map_get(config_keymap, "view_orbit_up"), shortcut_type_t.REPEAT)) {
 				viewport_orbit(0, -math_pi() / 12);
 			}
-			else if (operator_shortcut(config_keymap.view_orbit_down, shortcut_type_t.REPEAT)) {
+			else if (operator_shortcut(map_get(config_keymap, "view_orbit_down"), shortcut_type_t.REPEAT)) {
 				viewport_orbit(0, math_pi() / 12);
 			}
-			else if (operator_shortcut(config_keymap.view_orbit_opposite)) {
+			else if (operator_shortcut(map_get(config_keymap, "view_orbit_opposite"))) {
 				viewport_orbit_opposite();
 			}
-			else if (operator_shortcut(config_keymap.view_zoom_in, shortcut_type_t.REPEAT)) {
+			else if (operator_shortcut(map_get(config_keymap, "view_zoom_in"), shortcut_type_t.REPEAT)) {
 				viewport_zoom(0.2);
 			}
-			else if (operator_shortcut(config_keymap.view_zoom_out, shortcut_type_t.REPEAT)) {
+			else if (operator_shortcut(map_get(config_keymap, "view_zoom_out"), shortcut_type_t.REPEAT)) {
 				viewport_zoom(-0.2);
 			}
-			else if (operator_shortcut(config_keymap.viewport_mode)) {
+			else if (operator_shortcut(map_get(config_keymap, "viewport_mode"))) {
 
 				let count: i32;
 
@@ -659,7 +659,7 @@ function ui_base_update() {
 			}
 		}
 
-		if (operator_shortcut(config_keymap.operator_search)) ui_base_operator_search();
+		if (operator_shortcut(map_get(config_keymap, "operator_search"))) ui_base_operator_search();
 	}
 
 	if (context_raw.brush_can_lock || context_raw.brush_locked) {
@@ -670,14 +670,14 @@ function ui_base_update() {
 
 		///if (is_paint || is_sculpt)
 		let b: bool = (context_raw.brush_can_lock || context_raw.brush_locked) &&
-			!operator_shortcut(config_keymap.brush_radius, shortcut_type_t.DOWN) &&
-			!operator_shortcut(config_keymap.brush_opacity, shortcut_type_t.DOWN) &&
-			!operator_shortcut(config_keymap.brush_angle, shortcut_type_t.DOWN) &&
-			!(decal_mask && operator_shortcut(config_keymap.decal_mask + "+" + config_keymap.brush_radius, shortcut_type_t.DOWN));
+			!operator_shortcut(map_get(config_keymap, "brush_radius"), shortcut_type_t.DOWN) &&
+			!operator_shortcut(map_get(config_keymap, "brush_opacity"), shortcut_type_t.DOWN) &&
+			!operator_shortcut(map_get(config_keymap, "brush_angle"), shortcut_type_t.DOWN) &&
+			!(decal_mask && operator_shortcut(map_get(config_keymap, "decal_mask") + "+" + map_get(config_keymap, "brush_radius"), shortcut_type_t.DOWN));
 		///end
 		///if is_lab
 		let b: bool = (context_raw.brush_can_lock || context_raw.brush_locked) &&
-			!operator_shortcut(config_keymap.brush_radius, shortcut_type_t.DOWN);
+			!operator_shortcut(map_get(config_keymap, "brush_radius"), shortcut_type_t.DOWN);
 		///end
 
 		if (b) {
@@ -816,7 +816,7 @@ function ui_base_update() {
 			body.ccd = true;
 			mo.base.transform.radius /= 10; // Lower ccd radius
 			physics_body_init(body, mo.base);
-			(mo.base as any).physicsBody = body;
+			map_set(physics_body_object_map, mo.base, body);
 			mo.base.transform.radius *= 10;
 
 			let ray: ray_t = raycast_get_ray(mouse_view_x(), mouse_view_y(), camera);
@@ -888,11 +888,12 @@ function ui_base_operator_search() {
 		let count: i32 = 0;
 		let BUTTON_COL: i32 = ui.t.BUTTON_COL;
 
-		for (let i: i32 = 0; i < base_keymap_keys.length; ++i) {
-			let n: string = base_keymap_keys[i];
+		let keys: string[] = map_keys_to_array(config_keymap);
+		for (let i: i32 = 0; i < keys.length; ++i) {
+			let n: string = keys[i];
 			if (string_index_of(n, search) >= 0) {
 				ui.t.BUTTON_COL = count == ui_base_operator_search_offset ? ui.t.HIGHLIGHT_COL : ui.t.SEPARATOR_COL;
-				if (zui_button(n, zui_align_t.LEFT, config_keymap[n]) || (enter && count == ui_base_operator_search_offset)) {
+				if (zui_button(n, zui_align_t.LEFT, map_get(config_keymap, n)) || (enter && count == ui_base_operator_search_offset)) {
 					if (enter) {
 						ui.changed = true;
 						count = 6; // Trigger break
@@ -955,8 +956,8 @@ function ui_base_update_ui() {
 	///if (is_paint || is_sculpt)
 	// Same mapping for paint and rotate (predefined in touch keymap)
 	if (context_in_viewport()) {
-		if (mouse_started() && config_keymap.action_paint == config_keymap.action_rotate) {
-			ui_base_action_paint_remap = config_keymap.action_paint;
+		if (mouse_started() && map_get(config_keymap, "action_paint") == map_get(config_keymap, "action_rotate")) {
+			ui_base_action_paint_remap = map_get(config_keymap, "action_paint");
 			util_render_pick_pos_nor_tex();
 			let is_mesh: bool = math_abs(context_raw.posx_picked) < 50 && math_abs(context_raw.posy_picked) < 50 && math_abs(context_raw.posz_picked) < 50;
 			///if krom_android
@@ -969,23 +970,23 @@ function ui_base_update_ui() {
 			// Mesh picked - disable rotate
 			// Pen painting only - rotate with touch, paint with pen
 			if ((is_mesh && !pen_only) || is_pen) {
-				config_keymap.action_rotate = "";
-				config_keymap.action_paint = ui_base_action_paint_remap;
+				map_set(config_keymap, "action_rotate", "");
+				map_set(config_keymap, "action_paint", ui_base_action_paint_remap);
 			}
 			// World sphere picked - disable paint
 			else {
-				config_keymap.action_paint = "";
-				config_keymap.action_rotate = ui_base_action_paint_remap;
+				map_set(config_keymap, "action_paint", "");
+				map_set(config_keymap, "action_rotate", ui_base_action_paint_remap);
 			}
 		}
 		else if (!mouse_down() && ui_base_action_paint_remap != "") {
-			config_keymap.action_rotate = ui_base_action_paint_remap;
-			config_keymap.action_paint = ui_base_action_paint_remap;
+			map_set(config_keymap, "action_rotate", ui_base_action_paint_remap);
+			map_set(config_keymap, "action_paint", ui_base_action_paint_remap);
 			ui_base_action_paint_remap = "";
 		}
 	}
 
-	if (context_raw.brush_stencil_image != null && operator_shortcut(config_keymap.stencil_transform, shortcut_type_t.DOWN)) {
+	if (context_raw.brush_stencil_image != null && operator_shortcut(map_get(config_keymap, "stencil_transform"), shortcut_type_t.DOWN)) {
 		let r: rect_t = ui_base_get_brush_stencil_rect();
 		if (mouse_started("left")) {
 			context_raw.brush_stencil_scaling =
@@ -1037,21 +1038,21 @@ function ui_base_update_ui() {
 	}
 	///end
 
-	let set_clone_source: bool = context_raw.tool == workspace_tool_t.CLONE && operator_shortcut(config_keymap.set_clone_source + "+" + config_keymap.action_paint, shortcut_type_t.DOWN);
+	let set_clone_source: bool = context_raw.tool == workspace_tool_t.CLONE && operator_shortcut(map_get(config_keymap, "set_clone_source") + "+" + map_get(config_keymap, "action_paint"), shortcut_type_t.DOWN);
 
 	///if (is_paint || is_sculpt)
 	let decal: bool = context_raw.tool == workspace_tool_t.DECAL || context_raw.tool == workspace_tool_t.TEXT;
-	let decal_mask: bool = decal && operator_shortcut(config_keymap.decal_mask + "+" + config_keymap.action_paint, shortcut_type_t.DOWN);
-	let down: bool = operator_shortcut(config_keymap.action_paint, shortcut_type_t.DOWN) ||
+	let decal_mask: bool = decal && operator_shortcut(map_get(config_keymap, "decal_mask") + "+" + map_get(config_keymap, "action_paint"), shortcut_type_t.DOWN);
+	let down: bool = operator_shortcut(map_get(config_keymap, "action_paint"), shortcut_type_t.DOWN) ||
 					 decal_mask ||
 					 set_clone_source ||
-					 operator_shortcut(config_keymap.brush_ruler + "+" + config_keymap.action_paint, shortcut_type_t.DOWN) ||
+					 operator_shortcut(map_get(config_keymap, "brush_ruler") + "+" + map_get(config_keymap, "action_paint"), shortcut_type_t.DOWN) ||
 					 (pen_down() && !keyboard_down("alt"));
 	///end
 	///if is_lab
-	let down: bool = operator_shortcut(config_keymap.action_paint, shortcut_type_t.DOWN) ||
+	let down: bool = operator_shortcut(map_get(config_keymap, "action_paint"), shortcut_type_t.DOWN) ||
 					 set_clone_source ||
-					 operator_shortcut(config_keymap.brush_ruler + "+" + config_keymap.action_paint, shortcut_type_t.DOWN) ||
+					 operator_shortcut(map_get(config_keymap, "brush_ruler") + "+" + map_get(config_keymap, "action_paint"), shortcut_type_t.DOWN) ||
 					 (pen_down() && !keyboard_down("alt"));
 	///end
 
@@ -1110,7 +1111,7 @@ function ui_base_update_ui() {
 					!base_is_combo_selected()) { // Paint started
 
 					// Draw line
-					if (operator_shortcut(config_keymap.brush_ruler + "+" + config_keymap.action_paint, shortcut_type_t.DOWN)) {
+					if (operator_shortcut(map_get(config_keymap, "brush_ruler") + "+" + map_get(config_keymap, "action_paint"), shortcut_type_t.DOWN)) {
 						context_raw.last_paint_vec_x = context_raw.last_paint_x;
 						context_raw.last_paint_vec_y = context_raw.last_paint_y;
 					}
@@ -1225,8 +1226,8 @@ function ui_base_update_ui() {
 	}
 	///end
 
-	let undo_pressed: bool = operator_shortcut(config_keymap.edit_undo);
-	let redo_pressed: bool = operator_shortcut(config_keymap.edit_redo) ||
+	let undo_pressed: bool = operator_shortcut(map_get(config_keymap, "edit_undo"));
+	let redo_pressed: bool = operator_shortcut(map_get(config_keymap, "edit_redo")) ||
 							 (keyboard_down("control") && keyboard_started("y"));
 
 	// Two-finger tap to undo, three-finger tap to redo
@@ -1406,16 +1407,16 @@ function ui_base_render_cursor() {
 		my += context_raw.lock_started_y - sys_height() / 2;
 	}
 
-	let tool: workspace_tool_t = context_raw.tool as workspace_tool_t;
-
 	///if is_paint
 	if (context_raw.brush_stencil_image != null &&
-		tool != workspace_tool_t.BAKE &&
-		tool != workspace_tool_t.PICKER &&
-		tool != workspace_tool_t.MATERIAL &&
-		tool != workspace_tool_t.COLORID) {
+		// @ts-ignore
+		context_raw.tool != workspace_tool_t.BAKE &&
+		context_raw.tool != workspace_tool_t.PICKER &&
+		// @ts-ignore
+		context_raw.tool != workspace_tool_t.MATERIAL &&
+		context_raw.tool != workspace_tool_t.COLORID) {
 		let r: rect_t = ui_base_get_brush_stencil_rect();
-		if (!operator_shortcut(config_keymap.stencil_hide, shortcut_type_t.DOWN)) {
+		if (!operator_shortcut(map_get(config_keymap, "stencil_hide"), shortcut_type_t.DOWN)) {
 			g2_set_color(0x88ffffff);
 			let angle: f32 = context_raw.brush_stencil_angle;
 			let cx: f32 = r.x + r.w / 2;
@@ -1425,7 +1426,7 @@ function ui_base_render_cursor() {
 			g2_set_transformation(null);
 			g2_set_color(0xffffffff);
 		}
-		let transform: bool = operator_shortcut(config_keymap.stencil_transform, shortcut_type_t.DOWN);
+		let transform: bool = operator_shortcut(map_get(config_keymap, "stencil_transform"), shortcut_type_t.DOWN);
 		if (transform) {
 			// Outline
 			g2_draw_rect(r.x, r.y, r.w, r.h);
@@ -1473,7 +1474,7 @@ function ui_base_render_cursor() {
 	let decal: bool = context_raw.tool == workspace_tool_t.DECAL || context_raw.tool == workspace_tool_t.TEXT;
 
 	if (!config_raw.brush_3d || context_in_2d_view() || decal) {
-		let decal_mask: bool = decal && operator_shortcut(config_keymap.decal_mask, shortcut_type_t.DOWN);
+		let decal_mask: bool = decal && operator_shortcut(map_get(config_keymap, "decal_mask"), shortcut_type_t.DOWN);
 		if (decal && !context_in_nodes()) {
 			let decal_alpha: f32 = 0.5;
 			if (!decal_mask) {

+ 15 - 15
base/Sources/ui_header.ts

@@ -310,15 +310,15 @@ function ui_header_draw_tool_properties(ui: zui_t) {
 			 context_raw.tool == workspace_tool_t.PARTICLE) {
 
 		let decal: bool = context_raw.tool == workspace_tool_t.DECAL || context_raw.tool == workspace_tool_t.TEXT;
-		let decal_mask: bool = decal && operator_shortcut(config_keymap.decal_mask, shortcut_type_t.DOWN);
+		let decal_mask: bool = decal && operator_shortcut(map_get(config_keymap, "decal_mask"), shortcut_type_t.DOWN);
 		if (context_raw.tool != workspace_tool_t.FILL) {
 			if (decal_mask) {
 				context_raw.brush_decal_mask_radius = zui_slider(context_raw.brush_decal_mask_radius_handle, tr("Radius"), 0.01, 2.0, true);
 				if (ui.is_hovered) {
 					let vars: map_t<string, string> = map_create();
-					map_set(vars, "brush_radius", config_keymap.brush_radius);
-					map_set(vars, "brush_radius_decrease", config_keymap.brush_radius_decrease);
-					map_set(vars, "brush_radius_increase", config_keymap.brush_radius_increase);
+					map_set(vars, "brush_radius", map_get(config_keymap, "brush_radius"));
+					map_set(vars, "brush_radius_decrease", map_get(config_keymap, "brush_radius_decrease"));
+					map_set(vars, "brush_radius_increase", map_get(config_keymap, "brush_radius_increase"));
 					zui_tooltip(tr("Hold {brush_radius} and move mouse to the left or press {brush_radius_decrease} to decrease the radius\nHold {brush_radius} and move mouse to the right or press {brush_radius_increase} to increase the radius", vars));
 				}
 			}
@@ -326,9 +326,9 @@ function ui_header_draw_tool_properties(ui: zui_t) {
 				context_raw.brush_radius = zui_slider(context_raw.brush_radius_handle, tr("Radius"), 0.01, 2.0, true);
 				if (ui.is_hovered) {
 					let vars: map_t<string, string> = map_create();
-					map_set(vars, "brush_radius", config_keymap.brush_radius);
-					map_set(vars, "brush_radius_decrease", config_keymap.brush_radius_decrease);
-					map_set(vars, "brush_radius_increase", config_keymap.brush_radius_increase);
+					map_set(vars, "brush_radius", map_get(config_keymap, "brush_radius"));
+					map_set(vars, "brush_radius_decrease", map_get(config_keymap, "brush_radius_decrease"));
+					map_set(vars, "brush_radius_increase", map_get(config_keymap, "brush_radius_increase"));
 					zui_tooltip(tr("Hold {brush_radius} and move mouse to the left or press {brush_radius_decrease} to decrease the radius\nHold {brush_radius} and move mouse to the right or press {brush_radius_increase} to increase the radius", vars));
 				}
 			}
@@ -356,7 +356,7 @@ function ui_header_draw_tool_properties(ui: zui_t) {
 			context_raw.brush_angle = zui_slider(context_raw.brush_angle_handle, tr("Angle"), 0.0, 360.0, true, 1);
 			if (ui.is_hovered) {
 				let vars: map_t<string, string> = map_create();
-				map_set(vars, "brush_angle", config_keymap.brush_angle);
+				map_set(vars, "brush_angle", map_get(config_keymap, "brush_angle"));
 				zui_tooltip(tr("Hold {brush_angle} and move mouse to the left to decrease the angle\nHold {brush_angle} and move mouse to the right to increase the angle", vars));
 			}
 
@@ -368,7 +368,7 @@ function ui_header_draw_tool_properties(ui: zui_t) {
 		context_raw.brush_opacity = zui_slider(context_raw.brush_opacity_handle, tr("Opacity"), 0.0, 1.0, true);
 		if (ui.is_hovered) {
 			let vars: map_t<string, string> = map_create();
-			map_set(vars, "brush_opacity", config_keymap.brush_opacity);
+			map_set(vars, "brush_opacity", map_get(config_keymap, "brush_opacity"));
 			zui_tooltip(tr("Hold {brush_opacity} and move mouse to the left to decrease the opacity\nHold {brush_opacity} and move mouse to the right to increase the opacity", vars));
 		}
 
@@ -521,9 +521,9 @@ function ui_header_draw_tool_properties(ui: zui_t) {
 		context_raw.brush_radius = zui_slider(context_raw.brush_radius_handle, tr("Radius"), 0.01, 2.0, true);
 		if (ui.is_hovered) {
 			let vars: map_t<string, string> = map_create();
-			map_set(vars, "brush_radius", config_keymap.brush_radius);
-			map_set(vars, "brush_radius_decrease", config_keymap.brush_radius_decrease);
-			map_set(vars, "brush_radius_increase", config_keymap.brush_radius_increase);
+			map_set(vars, "brush_radius", map_get(config_keymap, "brush_radius"));
+			map_set(vars, "brush_radius_decrease", map_get(config_keymap, "brush_radius_decrease"));
+			map_set(vars, "brush_radius_increase", map_get(config_keymap, "brush_radius_increase"));
 			zui_tooltip(tr("Hold {brush_radius} and move mouse to the left or press {brush_radius_decrease} to decrease the radius\nHold {brush_radius} and move mouse to the right or press {brush_radius_increase} to increase the radius", vars));
 		}
 	}
@@ -547,9 +547,9 @@ function ui_header_draw_tool_properties(ui: zui_t) {
 			context_raw.brush_radius = zui_slider(context_raw.brush_radius_handle, tr("Radius"), 0.01, 2.0, true);
 			if (ui.is_hovered) {
 				let vars: map_t<string, string> = map_create();
-				map_set(vars, "brush_radius", config_keymap.brush_radius);
-				map_set(vars, "brush_radius_decrease", config_keymap.brush_radius_decrease);
-				map_set(vars, "brush_radius_increase", config_keymap.brush_radius_increase);
+				map_set(vars, "brush_radius", map_get(config_keymap, "brush_radius"));
+				map_set(vars, "brush_radius_decrease", map_get(config_keymap, "brush_radius_decrease"));
+				map_set(vars, "brush_radius_increase", map_get(config_keymap, "brush_radius_increase"));
 				zui_tooltip(tr("Hold {brush_radius} and move mouse to the left or press {brush_radius_decrease} to decrease the radius\nHold {brush_radius} and move mouse to the right or press {brush_radius_increase} to increase the radius", vars));
 			}
 		}

+ 34 - 34
base/Sources/ui_menu.ts

@@ -37,23 +37,23 @@ function ui_menu_render() {
 	else {
 		ui_menu_start(ui);
 		if (ui_menu_category == menu_category_t.FILE) {
-			if (ui_menu_button(ui, tr("New .."), config_keymap.file_new)) {
+			if (ui_menu_button(ui, tr("New .."), map_get(config_keymap, "file_new"))) {
 				project_new_box();
 			}
-			if (ui_menu_button(ui, tr("Open..."), config_keymap.file_open)) {
+			if (ui_menu_button(ui, tr("Open..."), map_get(config_keymap, "file_open"))) {
 				project_open();
 			}
-			if (ui_menu_button(ui, tr("Open Recent..."), config_keymap.file_open_recent)) {
+			if (ui_menu_button(ui, tr("Open Recent..."), map_get(config_keymap, "file_open_recent"))) {
 				box_projects_show();
 			}
-			if (ui_menu_button(ui, tr("Save"), config_keymap.file_save)) {
+			if (ui_menu_button(ui, tr("Save"), map_get(config_keymap, "file_save"))) {
 				project_save();
 			}
-			if (ui_menu_button(ui, tr("Save As..."), config_keymap.file_save_as)) {
+			if (ui_menu_button(ui, tr("Save As..."), map_get(config_keymap, "file_save_as"))) {
 				project_save_as();
 			}
 			ui_menu_separator(ui);
-			if (ui_menu_button(ui, tr("Import Texture..."), config_keymap.file_import_assets)) {
+			if (ui_menu_button(ui, tr("Import Texture..."), map_get(config_keymap, "file_import_assets"))) {
 				project_import_asset(path_texture_formats.join(","), false);
 			}
 			if (ui_menu_button(ui, tr("Import Envmap..."))) {
@@ -86,15 +86,15 @@ function ui_menu_render() {
 			if (ui_menu_button(ui, tr("Import Mesh..."))) {
 				project_import_mesh();
 			}
-			if (ui_menu_button(ui, tr("Reimport Mesh"), config_keymap.file_reimport_mesh)) {
+			if (ui_menu_button(ui, tr("Reimport Mesh"), map_get(config_keymap, "file_reimport_mesh"))) {
 				project_reimport_mesh();
 			}
-			if (ui_menu_button(ui, tr("Reimport Textures"), config_keymap.file_reimport_textures)) {
+			if (ui_menu_button(ui, tr("Reimport Textures"), map_get(config_keymap, "file_reimport_textures"))) {
 				project_reimport_textures();
 			}
 			ui_menu_separator(ui);
 			///if (is_paint || is_lab)
-			if (ui_menu_button(ui, tr("Export Textures..."), config_keymap.file_export_textures_as)) {
+			if (ui_menu_button(ui, tr("Export Textures..."), map_get(config_keymap, "file_export_textures_as"))) {
 				///if is_paint
 				context_raw.layers_export = export_mode_t.VISIBLE;
 				///end
@@ -133,24 +133,24 @@ function ui_menu_render() {
 			ui.enabled = history_undos > 0;
 			let vars_undo: map_t<string, string> = map_create();
 			map_set(vars_undo, "step", step_undo);
-			if (ui_menu_button(ui, tr("Undo {step}", vars_undo), config_keymap.edit_undo)) {
+			if (ui_menu_button(ui, tr("Undo {step}", vars_undo), map_get(config_keymap, "edit_undo"))) {
 				history_undo();
 			}
 
 			ui.enabled = history_redos > 0;
 			let vars_redo: map_t<string, string> = map_create();
 			map_set(vars_redo, "step", step_redo);
-			if (ui_menu_button(ui, tr("Redo {step}", vars_redo), config_keymap.edit_redo)) {
+			if (ui_menu_button(ui, tr("Redo {step}", vars_redo), map_get(config_keymap, "edit_redo"))) {
 				history_redo();
 			}
 			ui.enabled = true;
 			ui_menu_separator(ui);
-			if (ui_menu_button(ui, tr("Preferences..."), config_keymap.edit_prefs)) {
+			if (ui_menu_button(ui, tr("Preferences..."), map_get(config_keymap, "edit_prefs"))) {
 				box_preferences_show();
 			}
 		}
 		else if (ui_menu_category == menu_category_t.VIEWPORT) {
-			if (ui_menu_button(ui, tr("Distract Free"), config_keymap.view_distract_free)) {
+			if (ui_menu_button(ui, tr("Distract Free"), map_get(config_keymap, "view_distract_free"))) {
 				ui_base_toggle_distract_free();
 				ui_base_ui.is_hovered = false;
 			}
@@ -186,7 +186,7 @@ function ui_menu_render() {
 			context_raw.envmap_angle = zui_slider(enva_handle, tr("Environment Angle"), 0.0, 360.0, true, 1) / 180.0 * math_pi();
 			if (ui.is_hovered) {
 				let vars: map_t<string, string> = map_create();
-				map_set(vars, "shortcut", config_keymap.rotate_envmap);
+				map_set(vars, "shortcut", map_get(config_keymap, "rotate_envmap"));
 				zui_tooltip(tr("{shortcut} and move mouse", vars));
 			}
 			if (enva_handle.changed) {
@@ -215,7 +215,7 @@ function ui_menu_render() {
 				let new_angle: f32 = zui_slider(lahandle, tr("Light Angle"), 0.0, 360.0, true, 1) / 180 * math_pi();
 				if (ui.is_hovered) {
 					let vars: map_t<string, string> = map_create();
-					map_set(vars, "shortcut", config_keymap.rotate_light);
+					map_set(vars, "shortcut", map_get(config_keymap, "rotate_light"));
 					zui_tooltip(tr("{shortcut} and move mouse", vars));
 				}
 				let ldiff: f32 = new_angle - context_raw.light_angle;
@@ -364,7 +364,7 @@ function ui_menu_render() {
 
 			for (let i: i32 = 0; i < modes.length; ++i) {
 				ui_menu_fill(ui);
-				let shortcut: string = config_raw.touch_ui ? "" : config_keymap.viewport_mode + ", " + shortcuts[i];
+				let shortcut: string = config_raw.touch_ui ? "" : map_get(config_keymap, "viewport_mode") + ", " + shortcuts[i];
 				zui_radio(mode_handle, i, modes[i], shortcut);
 			}
 
@@ -378,52 +378,52 @@ function ui_menu_render() {
 			}
 		}
 		else if (ui_menu_category == menu_category_t.CAMERA) {
-			if (ui_menu_button(ui, tr("Reset"), config_keymap.view_reset)) {
+			if (ui_menu_button(ui, tr("Reset"), map_get(config_keymap, "view_reset"))) {
 				viewport_reset();
 				viewport_scale_to_bounds();
 			}
 			ui_menu_separator(ui);
-			if (ui_menu_button(ui, tr("Front"), config_keymap.view_front)) {
+			if (ui_menu_button(ui, tr("Front"), map_get(config_keymap, "view_front"))) {
 				viewport_set_view(0, -1, 0, math_pi() / 2, 0, 0);
 			}
-			if (ui_menu_button(ui, tr("Back"), config_keymap.view_back)) {
+			if (ui_menu_button(ui, tr("Back"), map_get(config_keymap, "view_back"))) {
 				viewport_set_view(0, 1, 0, math_pi() / 2, 0, math_pi());
 			}
-			if (ui_menu_button(ui, tr("Right"), config_keymap.view_right)) {
+			if (ui_menu_button(ui, tr("Right"), map_get(config_keymap, "view_right"))) {
 				viewport_set_view(1, 0, 0, math_pi() / 2, 0, math_pi() / 2);
 			}
-			if (ui_menu_button(ui, tr("Left"), config_keymap.view_left)) {
+			if (ui_menu_button(ui, tr("Left"), map_get(config_keymap, "view_left"))) {
 				viewport_set_view(-1, 0, 0, math_pi() / 2, 0, -math_pi() / 2);
 			}
-			if (ui_menu_button(ui, tr("Top"), config_keymap.view_top)) {
+			if (ui_menu_button(ui, tr("Top"), map_get(config_keymap, "view_top"))) {
 				viewport_set_view(0, 0, 1, 0, 0, 0);
 			}
-			if (ui_menu_button(ui, tr("Bottom"), config_keymap.view_bottom)) {
+			if (ui_menu_button(ui, tr("Bottom"), map_get(config_keymap, "view_bottom"))) {
 				viewport_set_view(0, 0, -1, math_pi(), 0, math_pi());
 			}
 			ui_menu_separator(ui);
 
 			ui.changed = false;
 
-			if (ui_menu_button(ui, tr("Orbit Left"), config_keymap.view_orbit_left)) {
+			if (ui_menu_button(ui, tr("Orbit Left"), map_get(config_keymap, "view_orbit_left"))) {
 				viewport_orbit(-math_pi() / 12, 0);
 			}
-			if (ui_menu_button(ui, tr("Orbit Right"), config_keymap.view_orbit_right)) {
+			if (ui_menu_button(ui, tr("Orbit Right"), map_get(config_keymap, "view_orbit_right"))) {
 				viewport_orbit(math_pi() / 12, 0);
 			}
-			if (ui_menu_button(ui, tr("Orbit Up"), config_keymap.view_orbit_up)) {
+			if (ui_menu_button(ui, tr("Orbit Up"), map_get(config_keymap, "view_orbit_up"))) {
 				viewport_orbit(0, -math_pi() / 12);
 			}
-			if (ui_menu_button(ui, tr("Orbit Down"), config_keymap.view_orbit_down)) {
+			if (ui_menu_button(ui, tr("Orbit Down"), map_get(config_keymap, "view_orbit_down"))) {
 				viewport_orbit(0, math_pi() / 12);
 			}
-			if (ui_menu_button(ui, tr("Orbit Opposite"), config_keymap.view_orbit_opposite)) {
+			if (ui_menu_button(ui, tr("Orbit Opposite"), map_get(config_keymap, "view_orbit_opposite"))) {
 				viewport_orbit_opposite();
 			}
-			if (ui_menu_button(ui, tr("Zoom In"), config_keymap.view_zoom_in)) {
+			if (ui_menu_button(ui, tr("Zoom In"), map_get(config_keymap, "view_zoom_in"))) {
 				viewport_zoom(0.2);
 			}
-			if (ui_menu_button(ui, tr("Zoom Out"), config_keymap.view_zoom_out)) {
+			if (ui_menu_button(ui, tr("Zoom Out"), map_get(config_keymap, "view_zoom_out"))) {
 				viewport_zoom(-0.2);
 			}
 			// menuSeparator(ui);
@@ -444,9 +444,9 @@ function ui_menu_render() {
 			context_raw.camera_controls = zui_inline_radio(camera_controls_handle, [tr("Orbit"), tr("Rotate"), tr("Fly")], zui_align_t.LEFT);
 
 			let vars: map_t<string, string> = map_create();
-			map_set(vars, "rotate_shortcut", config_keymap.action_rotate);
-			map_set(vars, "zoom_shortcut", config_keymap.action_zoom);
-			map_set(vars, "pan_shortcut", config_keymap.action_pan);
+			map_set(vars, "rotate_shortcut", map_get(config_keymap, "action_rotate"));
+			map_set(vars, "zoom_shortcut", map_get(config_keymap, "action_zoom"));
+			map_set(vars, "pan_shortcut", map_get(config_keymap, "action_pan"));
 			let orbit_and_rotate_tooltip: string = tr("Orbit and Rotate mode:\n{rotate_shortcut} or move right mouse button to rotate.\n{zoom_shortcut} or scroll to zoom.\n{pan_shortcut} or move middle mouse to pan.", vars);
 			let fly_tooltip: string = tr("Fly mode:\nHold the right mouse button and one of the following commands:\nmove mouse to rotate.\nw, up or scroll up to move forward.\ns, down or scroll down to move backward.\na or left to move left.\nd or right to move right.\ne to move up.\nq to move down.\nHold shift to move faster or alt to move slower.");
 			if (ui.is_hovered) {
@@ -457,7 +457,7 @@ function ui_menu_render() {
 			ui_menu_align(ui);
 			context_raw.camera_type = zui_inline_radio(context_raw.cam_handle, [tr("Perspective"), tr("Orthographic")], zui_align_t.LEFT);
 			if (ui.is_hovered) {
-				zui_tooltip(tr("Camera Type") + " (" + config_keymap.view_camera_type + ")");
+				zui_tooltip(tr("Camera Type") + " (" + map_get(config_keymap, "view_camera_type") + ")");
 			}
 			if (context_raw.cam_handle.changed) {
 				viewport_update_camera_type(context_raw.camera_type);

+ 9 - 9
base/Sources/ui_nodes.ts

@@ -387,14 +387,14 @@ function ui_nodes_get_canvas_control(ui: zui_t, controls_down: bool): zui_canvas
 		}
 	}
 
-	if (operator_shortcut(config_keymap.action_pan, shortcut_type_t.STARTED) ||
-		operator_shortcut(config_keymap.action_zoom, shortcut_type_t.STARTED) ||
+	if (operator_shortcut(map_get(config_keymap, "action_pan"), shortcut_type_t.STARTED) ||
+		operator_shortcut(map_get(config_keymap, "action_zoom"), shortcut_type_t.STARTED) ||
 		ui.input_started_r ||
 		ui.input_wheel_delta != 0.0) {
 		controls_down = true;
 	}
-	else if (!operator_shortcut(config_keymap.action_pan, shortcut_type_t.DOWN) &&
-		!operator_shortcut(config_keymap.action_zoom, shortcut_type_t.DOWN) &&
+	else if (!operator_shortcut(map_get(config_keymap, "action_pan"), shortcut_type_t.DOWN) &&
+		!operator_shortcut(map_get(config_keymap, "action_zoom"), shortcut_type_t.DOWN) &&
 		!ui.input_down_r &&
 		ui.input_wheel_delta == 0.0) {
 		controls_down = false;
@@ -408,8 +408,8 @@ function ui_nodes_get_canvas_control(ui: zui_t, controls_down: bool): zui_canvas
 		}
 	}
 
-	let pan: bool = ui.input_down_r || operator_shortcut(config_keymap.action_pan, shortcut_type_t.DOWN);
-	let zoom_delta: f32 = operator_shortcut(config_keymap.action_zoom, shortcut_type_t.DOWN) ? ui_nodes_get_zoom_delta(ui) / 100.0 : 0.0;
+	let pan: bool = ui.input_down_r || operator_shortcut(map_get(config_keymap, "action_pan"), shortcut_type_t.DOWN);
+	let zoom_delta: f32 = operator_shortcut(map_get(config_keymap, "action_zoom"), shortcut_type_t.DOWN) ? ui_nodes_get_zoom_delta(ui) / 100.0 : 0.0;
 	let control: zui_canvas_control_t = {
 		pan_x: pan ? ui.input_dx : 0.0,
 		pan_y: pan ? ui.input_dy : 0.0,
@@ -545,14 +545,14 @@ function ui_nodes_update() {
 	}
 
 	// Node search popup
-	if (operator_shortcut(config_keymap.node_search)) ui_nodes_node_search();
+	if (operator_shortcut(map_get(config_keymap, "node_search"))) ui_nodes_node_search();
 	if (ui_nodes_node_search_spawn != null) {
 		ui_nodes_ui.input_x = mouse_x; // Fix inputDX after popup removal
 		ui_nodes_ui.input_y = mouse_y;
 		ui_nodes_node_search_spawn = null;
 	}
 
-	if (operator_shortcut(config_keymap.view_reset)) {
+	if (operator_shortcut(map_get(config_keymap, "view_reset"))) {
 		nodes.pan_x = 0.0;
 		nodes.pan_y = 0.0;
 		nodes.zoom = 1.0;
@@ -1131,7 +1131,7 @@ function ui_nodes_render() {
 			}
 		}
 		if (ui_nodes_ui.is_hovered) {
-			zui_tooltip(tr("Search for nodes") + " (" + config_keymap.node_search + ")");
+			zui_tooltip(tr("Search for nodes") + " (" + map_get(config_keymap, "node_search") + ")");
 		}
 		ui_nodes_ui._x += ui_nodes_ui._w + 3;
 		ui_nodes_ui._y = 2 + start_y;

+ 18 - 18
base/Sources/ui_toolbar.ts

@@ -127,30 +127,30 @@ function ui_toolbar_render_ui() {
 		ui._y -= 4 * zui_SCALE(ui);
 
 		let vars_brush: map_t<string, string> = map_create();
-		map_set(vars_brush, "key", config_keymap.brush_ruler);
-		map_set(vars_brush, "action_paint", config_keymap.action_paint);
+		map_set(vars_brush, "key", map_get(config_keymap, "brush_ruler"));
+		map_set(vars_brush, "action_paint", map_get(config_keymap, "action_paint"));
 
 		let vars_decal: map_t<string, string> = map_create();
-		map_set(vars_decal, "key", config_keymap.decal_mask);
+		map_set(vars_decal, "key", map_get(config_keymap, "decal_mask"));
 
 		let vars_clone: map_t<string, string> = map_create();
-		map_set(vars_clone, "key", config_keymap.set_clone_source);
+		map_set(vars_clone, "key", map_get(config_keymap, "set_clone_source"));
 
 		let keys: string[] = [
-			"(" + config_keymap.tool_brush + ") - " + tr("Hold {action_paint} to paint\nHold {key} and press {action_paint} to paint a straight line (ruler mode)", vars_brush),
-			"(" + config_keymap.tool_eraser + ") - " + tr("Hold {action_paint} to erase\nHold {key} and press {action_paint} to erase a straight line (ruler mode)", vars_brush),
-			"(" + config_keymap.tool_fill + ")",
-			"(" + config_keymap.tool_decal + ") - " + tr("Hold {key} to paint on a decal mask", vars_decal),
-			"(" + config_keymap.tool_text + ") - " + tr("Hold {key} to use the text as a mask", vars_decal),
-			"(" + config_keymap.tool_clone + ") - " + tr("Hold {key} to set source", vars_clone),
-			"(" + config_keymap.tool_blur + ")",
-			"(" + config_keymap.tool_smudge + ")",
-			"(" + config_keymap.tool_particle + ")",
-			"(" + config_keymap.tool_colorid + ")",
-			"(" + config_keymap.tool_picker + ")",
-			"(" + config_keymap.tool_bake + ")",
-			"(" + config_keymap.tool_gizmo + ")",
-			"(" + config_keymap.tool_material + ")",
+			"(" + map_get(config_keymap, "tool_brush") + ") - " + tr("Hold {action_paint} to paint\nHold {key} and press {action_paint} to paint a straight line (ruler mode)", vars_brush),
+			"(" + map_get(config_keymap, "tool_eraser") + ") - " + tr("Hold {action_paint} to erase\nHold {key} and press {action_paint} to erase a straight line (ruler mode)", vars_brush),
+			"(" + map_get(config_keymap, "tool_fill") + ")",
+			"(" + map_get(config_keymap, "tool_decal") + ") - " + tr("Hold {key} to paint on a decal mask", vars_decal),
+			"(" + map_get(config_keymap, "tool_text") + ") - " + tr("Hold {key} to use the text as a mask", vars_decal),
+			"(" + map_get(config_keymap, "tool_clone") + ") - " + tr("Hold {key} to set source", vars_clone),
+			"(" + map_get(config_keymap, "tool_blur") + ")",
+			"(" + map_get(config_keymap, "tool_smudge") + ")",
+			"(" + map_get(config_keymap, "tool_particle") + ")",
+			"(" + map_get(config_keymap, "tool_colorid") + ")",
+			"(" + map_get(config_keymap, "tool_picker") + ")",
+			"(" + map_get(config_keymap, "tool_bake") + ")",
+			"(" + map_get(config_keymap, "tool_gizmo") + ")",
+			"(" + map_get(config_keymap, "tool_material") + ")",
 		];
 
 		ui_toolbar_draw_tool(workspace_tool_t.BRUSH, ui, img, icon_accent, keys);

+ 5 - 5
base/Sources/ui_view2d.ts

@@ -447,13 +447,13 @@ function ui_view2d_update() {
 
 	///if (is_paint || is_sculpt)
 	let decal: bool = context_raw.tool == workspace_tool_t.DECAL || context_raw.tool == workspace_tool_t.TEXT;
-	let decal_mask: bool = decal && operator_shortcut(config_keymap.decal_mask + "+" + config_keymap.action_paint, shortcut_type_t.DOWN);
-	let set_clone_source: bool = context_raw.tool == workspace_tool_t.CLONE && operator_shortcut(config_keymap.set_clone_source + "+" + config_keymap.action_paint, shortcut_type_t.DOWN);
+	let decal_mask: bool = decal && operator_shortcut(map_get(config_keymap, "decal_mask") + "+" + map_get(config_keymap, "action_paint"), shortcut_type_t.DOWN);
+	let set_clone_source: bool = context_raw.tool == workspace_tool_t.CLONE && operator_shortcut(map_get(config_keymap, "set_clone_source") + "+" + map_get(config_keymap, "action_paint"), shortcut_type_t.DOWN);
 
 	if (ui_view2d_type == view_2d_type_t.LAYER &&
 		!ui_view2d_text_input_hover &&
-		(operator_shortcut(config_keymap.action_paint, shortcut_type_t.DOWN) ||
-			operator_shortcut(config_keymap.brush_ruler + "+" + config_keymap.action_paint, shortcut_type_t.DOWN) ||
+		(operator_shortcut(map_get(config_keymap, "action_paint"), shortcut_type_t.DOWN) ||
+			operator_shortcut(map_get(config_keymap, "brush_ruler") + "+" + map_get(config_keymap, "action_paint"), shortcut_type_t.DOWN) ||
 			decal_mask ||
 			set_clone_source ||
 			config_raw.brush_live)) {
@@ -498,7 +498,7 @@ function ui_view2d_update() {
 		ui_view2d_pan_y = -tw / 2 - hh / 2 + border;
 	}
 
-	if (operator_shortcut(config_keymap.view_reset)) {
+	if (operator_shortcut(map_get(config_keymap, "view_reset"))) {
 		ui_view2d_pan_x = 0.0;
 		ui_view2d_pan_y = 0.0;
 		ui_view2d_pan_scale = 1.0;

+ 3 - 3
base/Sources/uniforms_ext.ts

@@ -23,7 +23,7 @@ function uniforms_ext_f32_link(object: object_t, mat: material_data_t, link: str
 	if (link == "_brushRadius") {
 		///if (is_paint || is_sculpt)
 		let decal: bool = context_raw.tool == workspace_tool_t.DECAL || context_raw.tool == workspace_tool_t.TEXT;
-		let decal_mask: bool = decal && operator_shortcut(config_keymap.decal_mask + "+" + config_keymap.action_paint, shortcut_type_t.DOWN);
+		let decal_mask: bool = decal && operator_shortcut(map_get(config_keymap, "decal_mask") + "+" + map_get(config_keymap, "action_paint"), shortcut_type_t.DOWN);
 		let brush_decal_mask_radius: f32 = context_raw.brush_decal_mask_radius;
 		if (config_raw.brush_3d) {
 			brush_decal_mask_radius *= context_raw.paint2d ? 0.55 * ui_view2d_pan_scale : 2.0;
@@ -82,7 +82,7 @@ function uniforms_ext_f32_link(object: object_t, mat: material_data_t, link: str
 		return val;
 	}
 	else if (link == "_brushHardness") {
-		let decal_mask: bool = operator_shortcut(config_keymap.decal_mask + "+" + config_keymap.action_paint, shortcut_type_t.DOWN);
+		let decal_mask: bool = operator_shortcut(map_get(config_keymap, "decal_mask") + "+" + map_get(config_keymap, "action_paint"), shortcut_type_t.DOWN);
 		if (context_raw.tool != workspace_tool_t.BRUSH && context_raw.tool != workspace_tool_t.ERASER && context_raw.tool != workspace_tool_t.CLONE && !decal_mask) {
 			return 1.0;
 		}
@@ -300,7 +300,7 @@ function uniforms_ext_vec4_link(object: object_t, mat: material_data_t, link: st
 		return uniforms_ext_vec;
 	}
 	else if (link == "_decalMask") {
-		let decal_mask: bool = operator_shortcut(config_keymap.decal_mask + "+" + config_keymap.action_paint, shortcut_type_t.DOWN);
+		let decal_mask: bool = operator_shortcut(map_get(config_keymap, "decal_mask") + "+" + map_get(config_keymap, "action_paint"), shortcut_type_t.DOWN);
 		let val: f32 = (context_raw.brush_radius * context_raw.brush_nodes_radius) / 15.0;
 		let scale2d: f32 = (900 / base_h()) * config_raw.window_scale;
 		val *= scale2d; // Projection ratio

+ 1 - 1
base/Sources/util_particle.ts

@@ -111,7 +111,7 @@ function util_particle_init_mesh() {
 	context_raw.paint_body = physics_body_create();
 	context_raw.paint_body.shape = shape_type_t.MESH;
 	physics_body_init(context_raw.paint_body, po.base);
-	(po.base as any).physicsBody = context_raw.paint_body;
+	map_set(physics_body_object_map, po.base, context_raw.paint_body);
 }
 
 ///end