@tool extends RefCounted class_name CsgBlockoutConfig static var _instance: CsgBlockoutConfig static func get_config() -> CsgBlockoutConfig: if _instance == null: _instance = CsgBlockoutConfig.new() _instance._ensure_settings_exist() return _instance # ProjectSettings paths const SETTING_ACTION_KEY = "addons/csg_blockout/action_key" const SETTING_AUTO_HIDE = "addons/csg_blockout/auto_hide" const SETTING_LANGUAGE_OVERRIDE = "addons/csg_blockout/language_override" const SETTING_MATERIAL_PRESET = "addons/csg_blockout/material_preset" const SETTING_DEFAULT_OPERATION = "addons/csg_blockout/default_operation" const SETTING_CUSTOM_MATERIAL_PATH = "addons/csg_blockout/custom_material_path" # Default values const DEFAULT_ACTION_KEY = KEY_SHIFT const DEFAULT_AUTO_HIDE = true const DEFAULT_LANGUAGE_OVERRIDE = "auto" const DEFAULT_MATERIAL_PRESET = MaterialPreset.GRID_LIGHT enum MaterialPreset { NONE = 0, GRID_LIGHT = 1, GRID_DARK = 2, GRID_ORANGE = 3, CUSTOM = 4 } signal config_saved() signal material_preset_changed(preset: MaterialPreset) signal custom_material_changed(mat: Material) signal default_operation_changed(op: CSGShape3D.Operation) # Configurable properties ## Key to hold for primary action (e.g., opening pie menu) var action_key: Key = KEY_SHIFT: get: return _get_setting(SETTING_ACTION_KEY, DEFAULT_ACTION_KEY) set(value): _set_setting(SETTING_ACTION_KEY, value) ## Whether to auto-hide the CSG blockout UI when not in use var auto_hide: bool = true: get: return _get_setting(SETTING_AUTO_HIDE, DEFAULT_AUTO_HIDE) set(value): _set_setting(SETTING_AUTO_HIDE, value) ## Language override (auto, en, zh_CN, ja, ko, es, pt, ru) var language_override: String = "auto": get: return _get_setting(SETTING_LANGUAGE_OVERRIDE, DEFAULT_LANGUAGE_OVERRIDE) set(value): _set_setting(SETTING_LANGUAGE_OVERRIDE, value) ## Material Preset (None, Light Grid, Dark Grid, Orange Grid, Custom) var material_preset: MaterialPreset = MaterialPreset.GRID_LIGHT: get: return _get_setting(SETTING_MATERIAL_PRESET, DEFAULT_MATERIAL_PRESET) as MaterialPreset set(value): _set_setting(SETTING_MATERIAL_PRESET, value) material_preset_changed.emit(value) ## Default CSG boolean operation applied to newly created nodes (shared by pie menu & sidebar) var default_operation: CSGShape3D.Operation = CSGShape3D.OPERATION_UNION: get: return _get_setting(SETTING_DEFAULT_OPERATION, CSGShape3D.OPERATION_UNION) as CSGShape3D.Operation set(value): _set_setting(SETTING_DEFAULT_OPERATION, value) default_operation_changed.emit(value) var custom_material: Material = null: get: if custom_material == null: var path: String = _get_setting(SETTING_CUSTOM_MATERIAL_PATH, "") if not path.is_empty() and ResourceLoader.exists(path): custom_material = ResourceLoader.load(path) as Material return custom_material set(value): custom_material = value _set_setting(SETTING_CUSTOM_MATERIAL_PATH, value.resource_path if value != null else "") custom_material_changed.emit(value) func _ensure_settings_exist() -> void: """Register settings in ProjectSettings if they don't exist.""" if not ProjectSettings.has_setting(SETTING_ACTION_KEY): ProjectSettings.set_setting(SETTING_ACTION_KEY, DEFAULT_ACTION_KEY) ProjectSettings.set_initial_value(SETTING_ACTION_KEY, DEFAULT_ACTION_KEY) ProjectSettings.add_property_info({ "name": SETTING_ACTION_KEY, "type": TYPE_INT, "hint": PROPERTY_HINT_ENUM, "hint_string": "Shift:%d,Ctrl:%d,Alt:%d,Meta:%d" % [KEY_SHIFT, KEY_CTRL, KEY_ALT, KEY_META] }) if not ProjectSettings.has_setting(SETTING_AUTO_HIDE): ProjectSettings.set_setting(SETTING_AUTO_HIDE, DEFAULT_AUTO_HIDE) ProjectSettings.set_initial_value(SETTING_AUTO_HIDE, DEFAULT_AUTO_HIDE) ProjectSettings.add_property_info({ "name": SETTING_AUTO_HIDE, "type": TYPE_BOOL }) if not ProjectSettings.has_setting(SETTING_LANGUAGE_OVERRIDE): ProjectSettings.set_setting(SETTING_LANGUAGE_OVERRIDE, DEFAULT_LANGUAGE_OVERRIDE) ProjectSettings.set_initial_value(SETTING_LANGUAGE_OVERRIDE, DEFAULT_LANGUAGE_OVERRIDE) ProjectSettings.add_property_info({ "name": SETTING_LANGUAGE_OVERRIDE, "type": TYPE_STRING, "hint": PROPERTY_HINT_ENUM, "hint_string": "auto,en,zh_CN,ja,ko,es,pt,ru" }) if not ProjectSettings.has_setting(SETTING_MATERIAL_PRESET): ProjectSettings.set_setting(SETTING_MATERIAL_PRESET, DEFAULT_MATERIAL_PRESET) ProjectSettings.set_initial_value(SETTING_MATERIAL_PRESET, DEFAULT_MATERIAL_PRESET) ProjectSettings.add_property_info({ "name": SETTING_MATERIAL_PRESET, "type": TYPE_INT, "hint": PROPERTY_HINT_ENUM, "hint_string": "None,Light Grid,Dark Grid,Orange Grid,Custom" }) if not ProjectSettings.has_setting(SETTING_DEFAULT_OPERATION): ProjectSettings.set_setting(SETTING_DEFAULT_OPERATION, CSGShape3D.OPERATION_UNION) ProjectSettings.set_initial_value(SETTING_DEFAULT_OPERATION, CSGShape3D.OPERATION_UNION) ProjectSettings.add_property_info({ "name": SETTING_DEFAULT_OPERATION, "type": TYPE_INT, "hint": PROPERTY_HINT_ENUM, "hint_string": "Union:0,Intersection:1,Subtraction:2" }) func get_preset_material(preset: MaterialPreset) -> Material: var file_name := "" match preset: MaterialPreset.GRID_LIGHT: file_name = "mat_grid_light.tres" MaterialPreset.GRID_DARK: file_name = "mat_grid_dark.tres" MaterialPreset.GRID_ORANGE: file_name = "mat_grid_orange.tres" _: return null var paths := [ "res://addons/csg_blockout/res/materials/" + file_name, "res://res/materials/" + file_name ] if not CsgBlockout.csg_plugin_path.is_empty(): paths.push_front(CsgBlockout.csg_plugin_path.path_join("res/materials").path_join(file_name)) for p in paths: if ResourceLoader.exists(p): return ResourceLoader.load(p) as Material return null func get_active_material() -> Material: match material_preset: MaterialPreset.NONE: return null MaterialPreset.GRID_LIGHT, MaterialPreset.GRID_DARK, MaterialPreset.GRID_ORANGE: return get_preset_material(material_preset) MaterialPreset.CUSTOM: return custom_material _: return null func _get_setting(path: String, default_value: Variant) -> Variant: """Get a setting from ProjectSettings.""" return ProjectSettings.get_setting(path, default_value) func _set_setting(path: String, value: Variant) -> void: """Set a setting in ProjectSettings.""" ProjectSettings.set_setting(path, value) func save_config() -> void: """Save settings to project.godot file.""" var err = ProjectSettings.save() if err == OK: print("CsgBlockout: Saved Config to ProjectSettings") config_saved.emit() else: push_error("CsgBlockout: Failed to save config - error code %d" % err)