├── .gitattributes ├── .gitignore ├── Components ├── InputCatcher.gd ├── InputCatcher.gd.uid ├── LineData.gd ├── LineData.gd.uid ├── Macro.gd └── Macro.gd.uid ├── LICENSE ├── Macros ├── Function.gd ├── Function.gd.uid ├── Init.gd ├── Init.gd.uid ├── Node.gd ├── Node.gd.uid ├── Ready.gd ├── Ready.gd.uid ├── SetGet.gd ├── SetGet.gd.uid ├── Tween.gd ├── Tween.gd.uid ├── Variables.gd └── Variables.gd.uid ├── MagicMacros.gd ├── MagicMacros.gd.uid ├── README.md └── plugin.cfg /.gitattributes: -------------------------------------------------------------------------------- 1 | # Images 2 | *.jpeg filter=lfs diff=lfs merge=lfs -text 3 | *.jpg filter=lfs diff=lfs merge=lfs -text 4 | *.png filter=lfs diff=lfs merge=lfs -text 5 | # Sound 6 | *.ogg filter=lfs diff=lfs merge=lfs -text 7 | *.wav filter=lfs diff=lfs merge=lfs -text 8 | # Scene Files / 3D Models 9 | *.collada filter=lfs diff=lfs merge=lfs -text 10 | *.dae filter=lfs diff=lfs merge=lfs -text 11 | *.fbx filter=lfs diff=lfs merge=lfs -text 12 | *.obj filter=lfs diff=lfs merge=lfs -text 13 | *.gltf filter=lfs diff=lfs merge=lfs -text 14 | *.glb filter=lfs diff=lfs merge=lfs -text 15 | *.bin filter=lfs diff=lfs merge=lfs -text 16 | # Other 17 | *.shape filter=lfs diff=lfs merge=lfs -text 18 | *.mesh filter=lfs diff=lfs merge=lfs -text 19 | *.ico filter=lfs diff=lfs merge=lfs -text 20 | *.afdesign filter=lfs diff=lfs merge=lfs -text 21 | *.ttf filter=lfs diff=lfs merge=lfs -text 22 | *.otf filter=lfs diff=lfs merge=lfs -text 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled source # 2 | ################### 3 | *.com 4 | *.class 5 | *.dll 6 | *.exe 7 | *.o 8 | *.so 9 | 10 | # Packages # 11 | ############ 12 | # it's better to unpack these files and commit the raw source 13 | # git has its own built in compression methods 14 | *.7z 15 | *.dmg 16 | *.gz 17 | *.iso 18 | *.jar 19 | *.rar 20 | *.tar 21 | *.zip 22 | 23 | # Logs and databases # 24 | ###################### 25 | *.log 26 | *.sql 27 | *.sqlite 28 | 29 | # OS generated files # 30 | ###################### 31 | .DS_Store 32 | .DS_Store? 33 | ._* 34 | .Spotlight-V100 35 | .Trashes 36 | ehthumbs.db 37 | Thumbs.db 38 | 39 | # Godot-specific ignores # 40 | ########################## 41 | .import/ 42 | .godot/ 43 | .mono/ 44 | export.cfg 45 | export_presets.cfg 46 | *.translation 47 | data_*/ 48 | mono_crash.*.json 49 | *.tmp 50 | 51 | # Project-specific ignores # 52 | ############################ 53 | !_exports/.gitignore 54 | *.lnk 55 | *.psd 56 | -------------------------------------------------------------------------------- /Components/InputCatcher.gd: -------------------------------------------------------------------------------- 1 | class_name MagicMacrosInputCatcher 2 | extends Node 3 | # This class intercepts inputs meant for the CodeEditor 4 | # This is to prevent the CodeEditor from inserting Tabs or perfomring code completion 5 | # When you are accepting a macro 6 | 7 | signal tab_pressed 8 | 9 | 10 | func _init() -> void: 11 | EditorInterface.get_script_editor().add_child(self) 12 | 13 | 14 | func _input(event: InputEvent) -> void: 15 | if not event is InputEventKey: 16 | return 17 | 18 | var e: InputEventKey = event 19 | 20 | if not e.keycode == KEY_TAB: 21 | return 22 | 23 | if e.pressed and not e.is_echo() and not e.is_released(): 24 | tab_pressed.emit() 25 | -------------------------------------------------------------------------------- /Components/InputCatcher.gd.uid: -------------------------------------------------------------------------------- 1 | uid://derq3irdahusj 2 | -------------------------------------------------------------------------------- /Components/LineData.gd: -------------------------------------------------------------------------------- 1 | class_name MagicMacrosLineData 2 | extends RefCounted 3 | # Analyzes a provided Line to determine its contents and applicable macro. 4 | 5 | const DEFAULT_IDENTIFIER: String = "identifier" 6 | const DEFAULT_TYPE: String = "type" 7 | const DEFAULT_REMAINDER: String = "none" 8 | const NON_PASCAL_TYPES: Array[String] = ["void", "bool", "float", "int"] 9 | 10 | # ID of the Line within its TextEditor 11 | var line_index: int = -1 12 | # The raw text of the line 13 | var source_text: String = "" 14 | 15 | # The output of the macro 16 | var modified_text: String: 17 | get: 18 | if not is_valid: 19 | return source_text 20 | return detected_macro.call(_plugin.macros_apply_func, self) 21 | 22 | # The macro applicable to this line, if any 23 | var detected_macro: Script 24 | # The argument with which the macro is detected. 25 | var macro_arg: String = "" 26 | 27 | # Identifiers detected within the line 28 | # identifiers are always snake_case, follows GDScript style guide 29 | var identifier_args: Array[String] = [] 30 | 31 | var has_identifier: bool: 32 | get: return not identifier_args.is_empty() 33 | 34 | # Convenience helper value for retrieving the first identifier in the line. 35 | var identifier: String: 36 | get: return identifier_args[0] if has_identifier else DEFAULT_IDENTIFIER 37 | 38 | # Types detected within the line 39 | # Types are always PascalCase, follows GDScript style guide 40 | var type_args: Array[String] = [] 41 | 42 | var has_type: bool: 43 | get: return not type_args.is_empty() 44 | 45 | # Convenience helper value for retrieving the first type in the line. 46 | var type: String: 47 | get: return type_args[0] if has_type else DEFAULT_TYPE 48 | 49 | # Any remaining arguments that are not identifiers or types 50 | var remainder_args: Array[String] = [] 51 | 52 | var has_remainder: bool: 53 | get: return not remainder_args.is_empty() 54 | 55 | # Convenience helper value for retreiving the first remainder value. 56 | var remainder: String: 57 | get: return remainder_args[0] if has_remainder else DEFAULT_REMAINDER 58 | 59 | var is_valid: bool: 60 | get: return true if detected_macro else false 61 | 62 | # Reference to the plugin script 63 | var _plugin: MagicMacros 64 | 65 | # Line indentation 66 | var _indent: String = "" 67 | 68 | var indent: String: 69 | get: return _indent 70 | 71 | 72 | func _init(plugin: MagicMacros, line_id: int, line_text: String) -> void: 73 | _plugin = plugin 74 | line_index = line_id 75 | source_text = line_text 76 | 77 | _parse_line() 78 | 79 | 80 | func _parse_line() -> void: 81 | # Count only tabs in the beginning of the line 82 | # and remember line indentation 83 | _indent = _get_indentation() 84 | 85 | # Replace tabs in line and get the individual arguments 86 | var args: PackedStringArray = source_text.replace(" ", "").split(" ", false) 87 | if args.is_empty(): 88 | return 89 | 90 | # The first argument must be a macro argument 91 | # Eg. 'fn' or 'rdy' 92 | if _arg_is_macro(args[0]): 93 | macro_arg = args[0] 94 | args.remove_at(0) 95 | 96 | var types: Array[String] = [] 97 | var identifiers: Array[String] = [] 98 | var remainders: Array[String] = [] 99 | 100 | # Detect and sort arguments by category. 101 | for arg: String in args: 102 | if _arg_is_type(arg): 103 | types.append(arg) 104 | elif _arg_is_identifier(arg): 105 | identifiers.append(arg) 106 | else: 107 | remainders.append(arg) 108 | 109 | type_args = types 110 | identifier_args = identifiers 111 | remainder_args = remainders 112 | 113 | # Retrieve the macro 114 | detected_macro = _get_macro_script() 115 | 116 | 117 | func _get_indentation() -> String: 118 | var i: String = "" 119 | 120 | for c: String in source_text: 121 | if c in [" ", " "]: 122 | i += c 123 | continue 124 | break 125 | 126 | return i 127 | 128 | 129 | func _arg_is_macro(arg: String) -> bool: 130 | for macro: Script in _plugin.macros: 131 | # Will return a bool. See MagicMacroMacro for this. 132 | if macro.call(_plugin.macros_alias_func, arg): 133 | return true 134 | return false 135 | 136 | 137 | func _arg_is_type(arg: String) -> bool: 138 | if arg in NON_PASCAL_TYPES: 139 | return true 140 | 141 | return _plugin.is_pascal_case(arg) 142 | 143 | 144 | func _arg_is_identifier(arg: String) -> bool: 145 | return _plugin.is_snake_case(arg) 146 | 147 | 148 | func _get_macro_script() -> Script: 149 | for macro: Script in _plugin.macros: 150 | var matches: bool = macro.call(_plugin.macros_alias_func, macro_arg) 151 | if matches: 152 | return macro 153 | return null 154 | -------------------------------------------------------------------------------- /Components/LineData.gd.uid: -------------------------------------------------------------------------------- 1 | uid://cyc8xvohp1c05 2 | -------------------------------------------------------------------------------- /Components/Macro.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | class_name MagicMacrosMacro 3 | extends RefCounted 4 | 5 | 6 | # Return true if the argument provided is the trigger for this macro. 7 | # Eg: The ready macro uses "ready" and "rdy" 8 | static func is_macro_alias(arg: String) -> bool: 9 | return arg in [] 10 | 11 | 12 | # Return the new line to be inserted 13 | # Add line breaks using \n 14 | # LineData provides all arguments detected in the line 15 | static func apply_macro(line_data: MagicMacrosLineData) -> String: 16 | return line_data.source_text 17 | -------------------------------------------------------------------------------- /Components/Macro.gd.uid: -------------------------------------------------------------------------------- 1 | uid://ddngm5t28huv3 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Manuel 'TheDuriel' Fischer 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Macros/Function.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends MagicMacrosMacro 3 | 4 | const ALIASES: Array[String] = ["fn", "fnc"] 5 | 6 | 7 | static func is_macro_alias(arg: String) -> bool: 8 | return arg in ALIASES 9 | 10 | 11 | static func apply_macro(line_data: MagicMacrosLineData) -> String: 12 | var s: String = "" 13 | s += "func %s() -> %s:" % [line_data.identifier, line_data.type] 14 | s += "\n" 15 | s += " pass" 16 | return s 17 | -------------------------------------------------------------------------------- /Macros/Function.gd.uid: -------------------------------------------------------------------------------- 1 | uid://b5mw2ouyt64sf 2 | -------------------------------------------------------------------------------- /Macros/Init.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends MagicMacrosMacro 3 | 4 | const ALIASES: Array[String] = ["init"] 5 | 6 | 7 | static func is_macro_alias(arg: String) -> bool: 8 | return arg in ALIASES 9 | 10 | 11 | static func apply_macro(_line_data: MagicMacrosLineData) -> String: 12 | var s: String = "" 13 | s += "func _init() -> void:" 14 | s += "\n" 15 | s += " pass" 16 | 17 | return s 18 | -------------------------------------------------------------------------------- /Macros/Init.gd.uid: -------------------------------------------------------------------------------- 1 | uid://bqeobnreesxmd 2 | -------------------------------------------------------------------------------- /Macros/Node.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends MagicMacrosMacro 3 | 4 | const ALIASES: Array[String] = ["node"] 5 | 6 | 7 | static func is_macro_alias(arg: String) -> bool: 8 | return arg in ALIASES 9 | 10 | 11 | static func apply_macro(line_data: MagicMacrosLineData) -> String: 12 | var s: String = "@onready var %s: %s = %s" % [line_data.identifier, line_data.type, line_data.remainder] 13 | return s 14 | -------------------------------------------------------------------------------- /Macros/Node.gd.uid: -------------------------------------------------------------------------------- 1 | uid://dmgiwti1cgdbn 2 | -------------------------------------------------------------------------------- /Macros/Ready.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends MagicMacrosMacro 3 | 4 | const ALIASES: Array[String] = ["rdy", "ready"] 5 | 6 | 7 | static func is_macro_alias(arg: String) -> bool: 8 | return arg in ALIASES 9 | 10 | 11 | static func apply_macro(_line_data: MagicMacrosLineData) -> String: 12 | var s: String = "" 13 | s += "func _ready() -> void:" 14 | s += "\n" 15 | s += " pass" 16 | 17 | return s 18 | -------------------------------------------------------------------------------- /Macros/Ready.gd.uid: -------------------------------------------------------------------------------- 1 | uid://bral7intuc2cv 2 | -------------------------------------------------------------------------------- /Macros/SetGet.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends MagicMacrosMacro 3 | 4 | const ALIASES: Array[String] = ["setget", "sg"] 5 | 6 | 7 | static func is_macro_alias(arg: String) -> bool: 8 | return arg in ALIASES 9 | 10 | 11 | static func apply_macro(line_data: MagicMacrosLineData) -> String: 12 | var s: String = "" 13 | s += "var %s: %s = %s:\n" % [line_data.identifier, line_data.type, line_data.remainder] 14 | s += " set(value):\n" 15 | s += " %s = value\n" % line_data.identifier 16 | s += " get:\n" 17 | s += " return %s\n" % line_data.identifier 18 | 19 | return s 20 | -------------------------------------------------------------------------------- /Macros/SetGet.gd.uid: -------------------------------------------------------------------------------- 1 | uid://duf0f4lch00hv 2 | -------------------------------------------------------------------------------- /Macros/Tween.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends MagicMacrosMacro 3 | 4 | const ALIASES: Array[String] = ["tw", "tween"] 5 | 6 | 7 | static func is_macro_alias(arg: String) -> bool: 8 | return arg in ALIASES 9 | 10 | 11 | static func apply_macro(line_data: MagicMacrosLineData) -> String: 12 | var s: String = line_data.indent + "var tween: Tween = create_tween()" 13 | return s 14 | -------------------------------------------------------------------------------- /Macros/Tween.gd.uid: -------------------------------------------------------------------------------- 1 | uid://cshrioa4du1rm 2 | -------------------------------------------------------------------------------- /Macros/Variables.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends MagicMacrosMacro 3 | 4 | const ALIASES: Array[String] = ["vars", "vs"] 5 | 6 | 7 | static func is_macro_alias(arg: String) -> bool: 8 | return arg in ALIASES 9 | 10 | 11 | static func apply_macro(line_data: MagicMacrosLineData) -> String: 12 | var identifiers: Array[String] = line_data.identifier_args.duplicate() 13 | var types: Array[String] = line_data.type_args.duplicate() 14 | var values: Array[String] = line_data.remainder_args.duplicate() 15 | 16 | var s: String = "" 17 | 18 | for idx: int in identifiers.size(): 19 | var identifier: String = identifiers[idx] 20 | var type: String = types[min(idx, types.size() - 1)] if not types.is_empty() else "type" 21 | var value: String = values[min(idx, values.size() - 1)] if not values.is_empty() else "null" 22 | 23 | var ss: String = line_data.indent + "var %s: %s = %s" % [identifier, type, value] 24 | s += ss 25 | if not idx == identifiers.size() - 1: 26 | s += "\n" 27 | 28 | return s 29 | -------------------------------------------------------------------------------- /Macros/Variables.gd.uid: -------------------------------------------------------------------------------- 1 | uid://c3oye5dhjb6mb 2 | -------------------------------------------------------------------------------- /MagicMacros.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | class_name MagicMacros 3 | extends EditorPlugin 4 | 5 | const MACROS_DIR: String = "res://addons/MagicMacros/Macros/" 6 | 7 | const THEME_COLOR_CONSTANT: String = "current_line_color" 8 | # Color with which to highlight a valid macro with 9 | const THEME_COLOR_VALID: Color = Color(0.0, 1.0, 0.0, 0.15) 10 | # regex patterns used for argument detection. See LineData 11 | const PASCAL_CASE_REGEX_PATTERN: String = '^[A-Z][a-zA-Z0-9]*$' 12 | const SNAKE_CASE_REGEX_PATTERN: String = '^[a-z0-9_]+$' 13 | 14 | var pascal_case_regex: RegEx 15 | var snake_case_regex: RegEx 16 | 17 | # This is a bit silly. But since the list of macros is dynamic and not constant 18 | # This is the only "reasonable" way of reliably getting the method name to call() in LineData 19 | var macros_alias_func: String = MagicMacrosMacro.is_macro_alias.get_method() 20 | var macros_apply_func: String = MagicMacrosMacro.apply_macro.get_method() 21 | 22 | # List of Macro Script resources found in MagicMacros/Macros 23 | # Macros are not instanced, but statically called 24 | var macros: Array[Script] = [] 25 | 26 | var _input_catcher: MagicMacrosInputCatcher 27 | 28 | # While the ScriptEditor is always present, it will instance one CodeEditor per open script 29 | # This mess is part of making sure that we only interact with the currently visible script editor 30 | var _current_editor: ScriptEditorBase: 31 | set(value): 32 | if value == _current_editor: 33 | return 34 | 35 | var base: TextEdit 36 | 37 | if is_instance_valid(_current_editor): 38 | base = _current_editor.get_base_editor() 39 | if base: 40 | base.text_changed.disconnect(_on_text_changed) 41 | base.caret_changed.disconnect(_on_caret_changed) 42 | 43 | _current_editor = value 44 | 45 | if _current_editor: 46 | base = _current_editor.get_base_editor() 47 | if base: 48 | base.text_changed.connect(_on_text_changed) 49 | base.caret_changed.connect(_on_caret_changed) 50 | 51 | # LineData for the currently highlight line in the CodeEditor 52 | var _current_line_data: MagicMacrosLineData: 53 | set(value): 54 | _current_line_data = value 55 | _update_line_color() 56 | 57 | 58 | func _ready() -> void: 59 | # HACK: Fixes macros not loading? Unconfirmeed 60 | await get_tree().process_frame 61 | 62 | _load_macros() 63 | 64 | EditorInterface.get_script_editor().editor_script_changed.connect(_on_script_changed) 65 | 66 | _input_catcher = MagicMacrosInputCatcher.new() 67 | _input_catcher.tab_pressed.connect(_on_tab_pressed) 68 | 69 | _current_editor = EditorInterface.get_script_editor().get_current_editor() 70 | 71 | pascal_case_regex = RegEx.new() 72 | pascal_case_regex.compile(PASCAL_CASE_REGEX_PATTERN) 73 | snake_case_regex = RegEx.new() 74 | snake_case_regex.compile(SNAKE_CASE_REGEX_PATTERN) 75 | 76 | print("MagicMacros: %s macros enabled" % macros.size()) 77 | 78 | 79 | func _exit_tree() -> void: 80 | if _current_editor: 81 | var base: TextEdit = _current_editor.get_base_editor() 82 | base.remove_theme_color_override(THEME_COLOR_CONSTANT) 83 | 84 | if _input_catcher: 85 | _input_catcher.queue_free() 86 | 87 | print("MagicMacros: Disabled") 88 | 89 | 90 | # TODO: Consider making this path configurable. But it's probably not worth the trouble. 91 | func _load_macros() -> void: 92 | var files: PackedStringArray = DirAccess.get_files_at(MACROS_DIR) 93 | for file: String in files: 94 | 95 | if file.ends_with(".remap") or file.ends_with(".uid"): 96 | continue 97 | 98 | var file_path: String = MACROS_DIR.path_join(file) 99 | var script: Script = load(file_path) 100 | if script.has_method(macros_alias_func): 101 | macros.append(script) 102 | 103 | 104 | # When the currently edited script changes. 105 | func _on_script_changed(_script: Script) -> void: 106 | _current_editor = EditorInterface.get_script_editor().get_current_editor() 107 | _get_line_data() 108 | 109 | 110 | func _on_text_changed() -> void: 111 | _get_line_data() 112 | 113 | 114 | func _on_caret_changed() -> void: 115 | _get_line_data() 116 | 117 | 118 | func _get_line_data() -> void: 119 | _current_line_data = null 120 | 121 | var base: TextEdit = _current_editor.get_base_editor() 122 | if not base: 123 | return 124 | var line_id: int = base.get_caret_line() 125 | var line_text: String = base.get_line(line_id) 126 | 127 | _current_line_data = MagicMacrosLineData.new(self, line_id, line_text) 128 | 129 | 130 | func _update_line_color() -> void: 131 | if not is_instance_valid(_current_editor): 132 | return 133 | 134 | var base: TextEdit = _current_editor.get_base_editor() 135 | if not base: 136 | return 137 | if not _current_line_data: 138 | return 139 | 140 | if _current_line_data.is_valid: 141 | base.add_theme_color_override(THEME_COLOR_CONSTANT, THEME_COLOR_VALID) 142 | else: 143 | base.remove_theme_color_override(THEME_COLOR_CONSTANT) 144 | 145 | 146 | func _on_tab_pressed() -> void: 147 | var base: CodeEdit = _current_editor.get_base_editor() 148 | if not base: 149 | return 150 | if not base.is_visible_in_tree() and base.has_focus(): 151 | return 152 | if not _current_line_data: 153 | return 154 | if not _current_line_data.is_valid: 155 | return 156 | 157 | _current_editor.get_viewport().set_input_as_handled() 158 | base.cancel_code_completion() 159 | 160 | base.set_line(_current_line_data.line_index, _current_line_data.modified_text) 161 | base.set_caret_column(0) 162 | base.set_caret_line(_current_line_data.line_index) 163 | 164 | base.cancel_code_completion() 165 | 166 | 167 | func is_pascal_case(string: String) -> bool: 168 | return true if pascal_case_regex.search(string) else false 169 | 170 | 171 | func is_snake_case(string: String) -> bool: 172 | return true if snake_case_regex.search(string) else false 173 | -------------------------------------------------------------------------------- /MagicMacros.gd.uid: -------------------------------------------------------------------------------- 1 | uid://dspaatppuhbi5 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MagicMacros 2 | Godot Addon for enhanced autocomplete and code snippets 3 | 4 | # What does this do? 5 | This addon integrates with the Script Editor in Godot. It will scan the currently edited line for a pattern that fits one of the loaded macros, and when it finds a match highlight the line in green. Pressing tab will execute the macro on the contents of the line. 6 | 7 | Example: 8 | 9 | ![AAADADSA](https://github.com/user-attachments/assets/779b4cb5-f285-4489-a907-017e6e85bcb3) 10 | 11 | # How do I make new macros? 12 | 13 | Define macros in MagicMacros/Macros. New macros must extend MagicMacrosMacro type. Disable and reenable the plugin afterwards. You may have to do this twice for the new macros to load. 14 | 15 | Made a macro? Consider contributing it! 16 | 17 | # Installation 18 | 19 | This repository is designed to be loaded as a GIT Submodule. How you add these depends on your client of choice. 20 | 21 | [Refer to this if you're mad enough to use the command line :P](https://git-scm.com/book/en/v2/Git-Tools-Submodules) 22 | 23 | Alternatively, download the zip from this page and unpack it into your addons folder under res://addons/MagicMacros 24 | 25 | # Current Macros 26 | 27 | * setget = creates a variable with specified name, type, value, if any, and setters and getters. 28 | * init = creates the _init function. 29 | * ready | rdy = creates the _ready function. 30 | * fn | fnc = creates a function with specified name and return type if any. 31 | * node = creates an @onready variable with specified name, type, value if any. 32 | 33 | --- 34 | 35 | ### Looking for more? 36 | 37 | Check out Nylon! https://theduriel.itch.io/nylon 38 | 39 | Nylon for Godot is a Deep Dialogue sequencing addon, perfect for making complex RPG dialogue, cutscenes, and more. It's easily modified and used over the network as well, and includes a template project that incldues **many many more** utility systems for quickly building up your own game. Including menu and game state management, option menus, save files, audio, and more. 40 | 41 | ### Support me! 42 | 43 | I don't have a donation link. But instead of giving something for nothing, you can buy Nylon above! And get something in the process! :D 44 | 45 | ### Need support? 46 | 47 | This repository is provided as is. However I will happily answer questions via twitter: https://twitter.com/the_duriel 48 | -------------------------------------------------------------------------------- /plugin.cfg: -------------------------------------------------------------------------------- 1 | [plugin] 2 | 3 | name="MagicMacros" 4 | description="" 5 | author="TheDuriel" 6 | version="" 7 | script="MagicMacros.gd" 8 | --------------------------------------------------------------------------------