├── .gitignore ├── .travis.yml ├── ATTRIBUTIONS.md ├── Godot Node Extensions.csproj ├── Godot Node Extensions.sln ├── LICENSE ├── Properties └── AssemblyInfo.cs ├── README.md ├── addons ├── godot-next-cs │ ├── 2d │ │ ├── Geometry2D.cs │ │ └── Trail2D.cs │ ├── 3d │ │ └── Trail3D.cs │ ├── godot_next_cs_plugin.gd │ ├── icons │ │ ├── icon_geometry_2d.svg │ │ ├── icon_geometry_2d.svg.import │ │ ├── icon_trail_2d.svg │ │ ├── icon_trail_2d.svg.import │ │ ├── icon_trail_3d.svg │ │ └── icon_trail_3d.svg.import │ └── plugin.cfg └── godot-next │ ├── 2d │ ├── geometry_2d.gd │ ├── trail_2d.gd │ └── vector_display_2d.gd │ ├── 3d │ ├── trail_3d.gd │ └── vector_display_3d.gd │ ├── data │ └── singleton_cache.tres │ ├── global │ ├── editor_tools.gd │ ├── file_search.gd │ ├── file_system_link.gd │ ├── inspector_controls.gd │ ├── physics_layers.gd │ ├── project_tools.gd │ ├── singletons.gd │ └── variant.gd │ ├── godot_next_plugin.gd │ ├── gui │ ├── cycle.gd │ ├── debug_label.gd │ └── v_box_item_list.gd │ ├── icons │ ├── icon_add.svg │ ├── icon_add.svg.import │ ├── icon_cycle.svg │ ├── icon_cycle.svg.import │ ├── icon_geometry_2d.svg │ ├── icon_geometry_2d.svg.import │ ├── icon_import_fail.svg │ ├── icon_import_fail.svg.import │ ├── icon_mirror_y.svg │ ├── icon_mirror_y.svg.import │ ├── icon_trail_2d.svg │ ├── icon_trail_2d.svg.import │ ├── icon_trail_3d.svg │ ├── icon_trail_3d.svg.import │ ├── icon_v_box_item_list.svg │ └── icon_v_box_item_list.svg.import │ ├── inspector_plugins │ └── delegation_inspector_plugin.gd │ ├── nodes │ ├── callback_delegator.gd │ └── message_dispatcher_wrapper.gd │ ├── plugin.cfg │ ├── references │ ├── array_2d.gd │ ├── bit_flag.gd │ ├── bitset.gd │ ├── class_type.gd │ ├── csv_file.gd │ ├── inflector.gd │ ├── message_dispatcher.gd │ ├── property_info.gd │ ├── property_info_factory.gd │ └── tween_sequence │ │ ├── tween_sequence.gd │ │ └── tweener.gd │ ├── resources │ ├── behavior.gd │ ├── discrete_gradient_texture.gd │ ├── resource_collections │ │ ├── resource_array.gd │ │ ├── resource_collection.gd │ │ └── resource_set.gd │ └── singletons │ │ └── singleton_cache.gd │ └── singletons │ └── icons.gd ├── default_env.tres ├── demo ├── demo.tscn ├── demo_2d.tscn ├── demo_3d.tscn └── scripts │ ├── demo.gd │ ├── test_vector_display.gd │ ├── wandering_node_2d.gd │ └── wandering_node_3d.gd ├── docs └── use_cases.md ├── format.sh ├── icon.png ├── icon.png.import ├── licenses └── LICENSE_Inflector.md └── project.godot /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Godot-specific ignores 3 | .import/ 4 | export.cfg 5 | export_presets.cfg 6 | 7 | # Imported translations (automatically generated from CSV files) 8 | *.translation 9 | 10 | # Mono-specific ignores 11 | .mono/ 12 | data_*/ 13 | 14 | # System/tool-specific ignores 15 | .directory 16 | *~ 17 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | dist: bionic 2 | 3 | stages: 4 | - build 5 | 6 | matrix: 7 | include: 8 | - name: Static checks (format.sh) 9 | stage: build 10 | os: linux 11 | addons: 12 | apt: 13 | packages: 14 | - dos2unix 15 | - recode 16 | 17 | script: 18 | - bash ./format.sh 19 | -------------------------------------------------------------------------------- /ATTRIBUTIONS.md: -------------------------------------------------------------------------------- 1 | This file is for recording any credits related to scripts or icons in the repository for which people's contributions would not otherwise be listed. 2 | 3 | - trail_2d.gd: The script work was started initially by following the [YouTube tutorial](https://www.youtube.com/watch?v=s5DwZZ0fZDg) on creating a Trail using Line2D by [@KrystianErber](https://twitter.com/KrystianErber). 4 | 5 | - csv_file.gd: The csv-parsing algorithm was extracted from a [Java tutorial](https://www.mkyong.com/java/how-to-read-and-parse-csv-file-in-java/) by [@mkyong](https://twitter.com/mkyong). 6 | 7 | - editor_tools.gd: 'is_in_edited_scene' sourced from [@Zylann](https://github.com/Zylann) 8 | 9 | - project_tools.gd: `try_set_setting` and `set_setting` initially derived from [/u/WordOfRabbit](https://www.reddit.com/user/WordOfRabbit)'s [blog](https://dfaction.net/handling-custom-project-settings-using-gdscript/). 10 | -------------------------------------------------------------------------------- /Godot Node Extensions.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Tools 5 | AnyCPU 6 | {D437D4D4-7298-404E-96C3-567974A257A9} 7 | Library 8 | .mono\temp\bin\$(Configuration) 9 | GodotNodeExtensions 10 | Godot Node Extensions 11 | v4.7 12 | 1.0.7374.16792 13 | .mono\temp\obj 14 | $(BaseIntermediateOutputPath)\$(Configuration) 15 | Debug 16 | Release 17 | 18 | 19 | true 20 | portable 21 | false 22 | $(GodotDefineConstants);GODOT;DEBUG; 23 | prompt 24 | 4 25 | false 26 | 27 | 28 | portable 29 | true 30 | $(GodotDefineConstants);GODOT; 31 | prompt 32 | 4 33 | false 34 | 35 | 36 | true 37 | portable 38 | false 39 | $(GodotDefineConstants);GODOT;DEBUG;TOOLS; 40 | prompt 41 | 4 42 | false 43 | 44 | 45 | 46 | False 47 | $(ProjectDir)/.mono/assemblies/$(ApiConfiguration)/GodotSharp.dll 48 | 49 | 50 | False 51 | $(ProjectDir)/.mono/assemblies/$(ApiConfiguration)/GodotSharpEditor.dll 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /Godot Node Extensions.sln: -------------------------------------------------------------------------------- 1 | Microsoft Visual Studio Solution File, Format Version 12.00 2 | # Visual Studio 2012 3 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Godot Node Extensions", "Godot Node Extensions.csproj", "{D437D4D4-7298-404E-96C3-567974A257A9}" 4 | EndProject 5 | Global 6 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 7 | Debug|Any CPU = Debug|Any CPU 8 | Release|Any CPU = Release|Any CPU 9 | Tools|Any CPU = Tools|Any CPU 10 | EndGlobalSection 11 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 12 | {D437D4D4-7298-404E-96C3-567974A257A9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 13 | {D437D4D4-7298-404E-96C3-567974A257A9}.Debug|Any CPU.Build.0 = Debug|Any CPU 14 | {D437D4D4-7298-404E-96C3-567974A257A9}.Release|Any CPU.ActiveCfg = Release|Any CPU 15 | {D437D4D4-7298-404E-96C3-567974A257A9}.Release|Any CPU.Build.0 = Release|Any CPU 16 | {D437D4D4-7298-404E-96C3-567974A257A9}.Tools|Any CPU.ActiveCfg = Tools|Any CPU 17 | {D437D4D4-7298-404E-96C3-567974A257A9}.Tools|Any CPU.Build.0 = Tools|Any CPU 18 | EndGlobalSection 19 | EndGlobal 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Will Nations 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 | -------------------------------------------------------------------------------- /Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | 3 | // Information about this assembly is defined by the following attributes. 4 | // Change them to the values specific to your project. 5 | 6 | [assembly: AssemblyTitle("Godot Node Extensions")] 7 | [assembly: AssemblyDescription("")] 8 | [assembly: AssemblyConfiguration("")] 9 | [assembly: AssemblyCompany("")] 10 | [assembly: AssemblyProduct("")] 11 | [assembly: AssemblyCopyright("")] 12 | [assembly: AssemblyTrademark("")] 13 | [assembly: AssemblyCulture("")] 14 | 15 | // The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". 16 | // The form "{Major}.{Minor}.*" will automatically update the build and revision, 17 | // and "{Major}.{Minor}.{Build}.*" will update just the revision. 18 | 19 | [assembly: AssemblyVersion("1.0.*")] 20 | 21 | // The following attributes are used to specify the signing key for the assembly, 22 | // if desired. See the Mono documentation for more information about signing. 23 | 24 | //[assembly: AssemblyDelaySign(false)] 25 | //[assembly: AssemblyKeyFile("")] 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # godot-next 2 | 3 | ## Disclaimer 4 | 5 | Please note that this repository has been moved to `godot-extended-libraries/godot-next`. Please visit there if you would like to track its continued development! I am still the maintainer of it within that organization. 6 | 7 | ## Description 8 | 9 | Godot Node Extensions, AKA Godot NExt, is a Godot 3.1+ repository dedicated to collecting basic script classes that are currently unavailable in vanilla Godot. 10 | 11 | As you might have noticed, Godot Engine's initial node offerings are general purpose and are intentionally not oriented towards particular types of games. 12 | 13 | This repository's purpose is to create classes that fulfill a particular function and work out-of-the-box. Users should be able to use your class immediately after creating it. For nodes, don't be afraid to design ones that have an array of dynamically generated children. ;-) 14 | 15 | Note: The repository is named after Nodes, but ultimately any general-purpose type is welcome here (References, Resources, etc.). 16 | 17 | If you like the project, please star it. If you'd like to support its development, please [send tips to my Kofi](https://ko-fi.com/willnationsdev). 18 | 19 | [Jump to Class List](#classes) 20 | 21 | ## How to Use 22 | 23 | 1. Download the repo directly (the AssetLib version is no longer maintained) 24 | 25 | 2. Copy the addons directory to any project you would like to use them in 26 | 27 | 3. Open Project Settings and go to the Plugins tab. 28 | 29 | 4. Find the `godot-next` plugin and select "Active" from the dropdown on the right-hand side. 30 | 31 | 5. You should now be able to create each new type of node in your project! 32 | 33 | ## How to Contribute 34 | 35 | ### Ideas 36 | If you have an idea for a node that you would like to have added to the repository, create a new Issue. 37 | 38 | ### Scripts 39 | 40 | **All** scripts must be script classes, i.e. scripts with [registered names](http://docs.godotengine.org/en/latest/getting_started/step_by_step/scripting_continued.html#register-scripts-as-classes). 41 | 42 | **All** scripts must have the author's name at the top. 43 | 44 | **All** credits related to any associated script code or icons should be kept in the `ATTRIBUTIONS.md` file. 45 | 46 | **All** scripts must adhere to the relevant language's styling conventions, modeled after Godot Docs examples and the Godot Engine source code. 47 | 48 | **All C#** scripts must be submitted to a separate `addons/godot-next-cs` folder. This is to ensure that users who aren't using the Mono-enabled version of Godot do not have C# scripts present. 49 | 50 | Submissions are **encouraged** to do the following: 51 | 52 | 1. Provide a 16x16 SVG icon for each submitted script. 53 | 2. Use statically-typed GDScript if submitting GDScript files. 54 | 3. Create mirrored versions of GDScript and C# scripts between folders. 55 | 4. Add your node(s)' information to the bottom of the README, if possible (less work for maintainers). 56 | 57 | That's it! I hope you've got ideas of what you'd like to share with others. 58 | 59 | # Classes 60 | 61 | |Linkable Node Name|Description|Languages 62 | |-|-|-| 63 | |[Array2D](addons/godot-next/references/array_2d.gd)|A 2D Array class.|GDScript 64 | |[BitFlag](addons/godot-next/references/bit_flag.gd)|A class that allows abstracts away the complexity of handling bit flag enum types.|GDScript 65 | |[Bitset](addons/godot-next/references/bitset.gd)|A class that allows for easily manipulated bitmasks of any size.|GDScript 66 | |[Behavior](addons/godot-next/resources/behavior.gd)|A Resource type that automatically calls Node-like notification methods when paired with the CallbackDelegator class.|GDScript 67 | |[CallbackDelegator](addons/godot-next/nodes/callback_delegator.gd)|A Node that manages a ResourceSet of resources and delegates Node callbacks to each instance.|GDScript 68 | |[ClassType](addons/godot-next/references/class_type.gd)|A class abstraction, both for engine and user-defined types.|GDScript 69 | |[CSVFile](addons/godot-next/references/csv_file.gd)|Similar to ConfigFile, parses a .csv file. Can generate a key-value store from rows. Supports .tsv files.|GDScript 70 | |[Cycle](addons/godot-next/gui/cycle.gd)|Cycles through child nodes without any visibility or container effects.|GDScript 71 | |[DebugLabel](addons/godot-next/gui/debug_label.gd)|A label which displays a list of property values in any `Object`-derived instance at run-time for debugging purposes.|GDScript 72 | |[EditorTools](addons/godot-next/global/editor_tools.gd)|A utility for any features useful in the context of the Editor.|GDScript 73 | |[FileSearch](addons/godot-next/global/file_search.gd)|A utility with helpful methods to search through one's project files (or any directory).|GDScript 74 | |[FileSystemLink](addons/godot-next/global/file_system_link.gd)|A utility for creating links (file/directory, symbolic/hard).|GDScript 75 | |[Geometry2D](addons/godot-next/2d/geometry_2d.gd)|A utility that draws a Shape2D using CollisionShape2D's editor plugin handles.|GDScript, C# 76 | |[Inflector](addons/godot-next/references/inflector.gd)|A vocabulary wrapper of inflection tools to pluralize and singularize strings.|GDScript 77 | |[InspectorControls](addons/godot-next/global/inspector_controls.gd)|A utility for creating data-editing GUI elements.|GDScript 78 | |[MessageDispatcher](addons/godot-next/objects/message_dispatcher.gd)|A base object that handles signaling for non predetermined signals.|GDScript 79 | |[PhysicsLayers](addons/godot-next/global/physics_layers.gd)|A Utility class which allows easy access to your physics layers via their names in the project settings.|GDScript 80 | |[ProjectTools](addons/godot-next/global/project_tools.gd)|A utility for any features useful in the context of a Godot Project.|GDScript 81 | |[PropertyInfo](addons/godot-next/references/property_info.gd)|A wrapper and utility class for generating PropertyInfo Dictionaries, for use in `Object._get_property_list()`.|GDScript 82 | |[ResourceArray](addons/godot-next/resources/resource_collections/resource_array.gd)|A ResourceCollection implementation that manages an Array of Resources.|GDScript 83 | |[ResourceCollection](addons/godot-next/resources/resource_collections/resource_collection.gd)|An abstract base class for data structures that store Resource objects.|GDScript 84 | |[ResourceSet](addons/godot-next/resources/resource_collections/resource_set.gd)|A ResourceCollection implementation that manages a Set of Resources.|GDScript 85 | |[Singletons](addons/godot-next/global/singletons.gd)|A utility for caching Reference-derived singletons. Resources with a `SELF_RESOURCE` constant with a path to a `*.tres` file will be automatically loaded when accessed.|GDScript 86 | |[Trail2D](addons/godot-next/2d/trail_2d.gd)|Creates a variable-length trail that tracks a "target" node.|GDScript, C# 87 | |[Trail3D](addons/godot-next/3d/trail_3d.gd)|Creates a variable-length trail on an ImmediateGeometry node.|GDScript, C# 88 | |[Tween Sequence](addons/godot-next/references/tween_sequence.gd)|A helper class for easier management and chaining of Tweens dynamically from code.|GDScript 89 | |[VectorDisplay2D](addons/godot-next/2d/vector_display_2d.gd)|Displays Vector2 members in the editor via Position2D nodes.|GDScript 90 | |[VectorDisplay3D](addons/godot-next/3d/vector_display_3d.gd)|Displays Vector3 members in the editor via Position3D nodes.|GDScript 91 | |[Variant](addons/godot-next/global/variant.gd)|A utility class for handling Variants (the type wrapper for all variables in Godot's scripting API).|GDScript 92 | |[VBoxItemList](addons/godot-next/gui/v_box_item_list.gd)|Creates a vertical list of items that can be added or removed. Items are a user-specified Script or Scene Control.|GDScript 93 | |[DiscreteGradientTexture](addons/godot-next/resources/discrete_gradient_texture.gd)|Creates a not interpolated texture for a gradient.|GDScript 94 | -------------------------------------------------------------------------------- /addons/godot-next-cs/2d/Geometry2D.cs: -------------------------------------------------------------------------------- 1 | using Godot; 2 | 3 | [Tool] 4 | public class Geometry2D : CollisionShape2D 5 | { 6 | private Color _color = Colors.White; 7 | private Vector2 _offsetPosition; 8 | 9 | [Export] public Color ShapeColor { get => _color; set { _color = value; Update(); } } 10 | [Export] public Vector2 OffsetPosition { get => _offsetPosition; set { _offsetPosition = value; Update(); } } 11 | 12 | public override void _Draw() 13 | { 14 | if (Shape is CircleShape2D) 15 | { 16 | DrawCircle(_offsetPosition, ((CircleShape2D)Shape).Radius, _color); 17 | } 18 | else if (Shape is RectangleShape2D) 19 | { 20 | RectangleShape2D rectangleShape = (RectangleShape2D)Shape; 21 | Rect2 rect = new Rect2(_offsetPosition - rectangleShape.Extents, rectangleShape.Extents * 2.0f); 22 | DrawRect(rect, _color); 23 | } 24 | else if (Shape is CapsuleShape2D) 25 | { 26 | CapsuleShape2D capsuleShape = (CapsuleShape2D)Shape; 27 | DrawCapsule(_offsetPosition, capsuleShape.Radius, capsuleShape.Height, _color); 28 | } 29 | } 30 | 31 | public void DrawCapsule(Vector2 capsulePosition, float capsuleRadius, float capsuleHeight, Color capsuleColor) 32 | { 33 | Vector2 upperCirclePosition = capsulePosition + new Vector2(0, capsuleHeight * 0.5f); 34 | DrawCircle(upperCirclePosition, capsuleRadius, capsuleColor); 35 | 36 | Vector2 lowerCirclePosition = capsulePosition - new Vector2(0, capsuleHeight * 0.5f); 37 | DrawCircle(lowerCirclePosition, capsuleRadius, capsuleColor); 38 | 39 | Vector2 rectPosition = capsulePosition - new Vector2(capsuleRadius, capsuleHeight * 0.5f); 40 | Rect2 rect = new Rect2(rectPosition, new Vector2(capsuleRadius * 2, capsuleHeight)); 41 | DrawRect(rect, capsuleColor); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /addons/godot-next-cs/2d/Trail2D.cs: -------------------------------------------------------------------------------- 1 | using Godot; 2 | 3 | public class Trail2D : Line2D 4 | { 5 | public enum Persistence 6 | { 7 | Off, // Do not persist. Remove all points after the trailLength. 8 | Always, // Always persist. Do not remove any points. 9 | Conditional, // Sometimes persist. Choose an algorithm for when to add and remove points. 10 | } 11 | 12 | public enum PersistWhen 13 | { 14 | OnMovement, // Add points during movement and remove points when not moving. 15 | Custom, // Override ShouldGrow() and ShouldShrink() to define when to add/remove points. 16 | } 17 | 18 | // The target node to track 19 | private Node2D _target; 20 | 21 | // The NodePath to the target 22 | [Export] public NodePath targetPath = ".."; 23 | // if not persisting, the number of points that should be allowed in the trail 24 | [Export] public int trailLength = 10; 25 | // To what degree the trail should remain in existence before automatically removing points. 26 | [Export] public Persistence persistence = Persistence.Off; 27 | // During conditional persistence, which persistence algorithm to use 28 | [Export] public PersistWhen persistWhen = PersistWhen.OnMovement; 29 | // During conditional persistence, how many points to remove per frame 30 | [Export] public int degenRate = 1; 31 | // if true, automatically set ZIndex to be one less than the 'target' 32 | [Export] public bool autoZIndex = true; 33 | // if true, will automatically setup a gradient for a gradually transparent trail 34 | [Export] public bool autoAlphaGradient = true; 35 | 36 | public override void _EnterTree() 37 | { 38 | SetAsToplevel(true); 39 | GlobalPosition = Vector2.Zero; 40 | GlobalRotation = 0; 41 | if (autoAlphaGradient && Gradient == null) 42 | { 43 | Gradient = new Gradient(); 44 | Color first = DefaultColor; 45 | first.a = 0; // TODO: Use https://github.com/godotengine/godot/pull/31658 instead. 46 | Gradient.SetColor(0, first); 47 | Gradient.SetColor(1, DefaultColor); 48 | } 49 | _target = GetNodeOrNull(targetPath); 50 | } 51 | 52 | public override void _Notification(int p_what) 53 | { 54 | switch (p_what) 55 | { 56 | case Node.NotificationParented: 57 | if (autoZIndex) 58 | { 59 | ZIndex = _target != null ? _target.ZIndex - 1 : 0; 60 | } 61 | break; 62 | case Node.NotificationUnparented: 63 | targetPath = @""; 64 | trailLength = 0; 65 | break; 66 | } 67 | } 68 | 69 | public override void _Process(float delta) 70 | { 71 | if (_target == null) 72 | { 73 | _target = GetNodeOrNull(targetPath); 74 | return; 75 | } 76 | 77 | switch (persistence) 78 | { 79 | case Persistence.Off: 80 | AddPoint(_target.GlobalPosition); 81 | while (GetPointCount() > trailLength) 82 | { 83 | RemovePoint(0); 84 | } 85 | break; 86 | case Persistence.Always: 87 | AddPoint(_target.GlobalPosition); 88 | break; 89 | case Persistence.Conditional: 90 | switch (persistWhen) 91 | { 92 | case PersistWhen.OnMovement: 93 | bool moved = GetPointCount() > 0 ? GetPointPosition(GetPointCount() - 1) != _target.GlobalPosition : false; 94 | if (GetPointCount() == 0 || moved) 95 | { 96 | AddPoint(_target.GlobalPosition); 97 | } 98 | else 99 | { 100 | for (int i = 0; i < degenRate; i++) 101 | { 102 | RemovePoint(0); 103 | } 104 | } 105 | break; 106 | case PersistWhen.Custom: 107 | if (ShouldGrow()) 108 | { 109 | AddPoint(_target.GlobalPosition); 110 | if (ShouldShrink()) 111 | { 112 | for (int i = 0; i < degenRate; i++) 113 | { 114 | RemovePoint(0); 115 | } 116 | } 117 | break; 118 | } 119 | break; 120 | } 121 | break; 122 | } 123 | } 124 | 125 | protected bool ShouldGrow() { return true; } 126 | protected bool ShouldShrink() { return true; } 127 | 128 | public void EraseTrail() 129 | { 130 | for (int i = 0; i < GetPointCount(); i++) 131 | { 132 | RemovePoint(0); 133 | } 134 | } 135 | 136 | public void SetTarget(Node2D value) 137 | { 138 | if (value != null) 139 | { 140 | if (GetPathTo(value) != targetPath) 141 | { 142 | targetPath = GetPathTo(value); 143 | } 144 | } 145 | else 146 | { 147 | targetPath = @""; 148 | } 149 | } 150 | 151 | public void SetTargetPath(NodePath value) 152 | { 153 | targetPath = value; 154 | _target = GetNode(value) as Node2D; 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /addons/godot-next-cs/3d/Trail3D.cs: -------------------------------------------------------------------------------- 1 | using Godot; 2 | 3 | public class Trail3D : ImmediateGeometry 4 | { 5 | [Export] public float length = 10.0f; 6 | [Export] public float maxRadius = 0.5f; 7 | [Export] public int densityLengthwise = 25; 8 | [Export] public int densityAround = 5; 9 | [Export] public float shape = 0; 10 | [Export] public Godot.Collections.Array points = new Godot.Collections.Array(); 11 | [Export] public float segmentLength = 1.0f; 12 | 13 | public override void _Ready() 14 | { 15 | if (length <= 0) length = 2; 16 | if (densityAround < 3) densityAround = 3; 17 | if (densityLengthwise < 2) densityLengthwise = 2; 18 | 19 | segmentLength = length / densityLengthwise; 20 | for (int i = 0; i < densityLengthwise; i++) 21 | { 22 | points.Add(GlobalTransform.origin); 23 | } 24 | } 25 | 26 | public override void _Process(float delta) 27 | { 28 | UpdateTrail(); 29 | RenderTrail(); 30 | } 31 | 32 | public void UpdateTrail() 33 | { 34 | int i = 0; 35 | Vector3 lastP = GlobalTransform.origin; 36 | foreach (Vector3 p in points) 37 | { 38 | Vector3 v = p; // We can't assign to foreach iterators in C#. 39 | float dis = v.DistanceTo(lastP); 40 | float segLen = segmentLength; 41 | if (i == 0) 42 | { 43 | segLen = 0.05f; 44 | } 45 | if (dis > segLen) 46 | { 47 | v = lastP + (v - lastP) / dis * segLen; 48 | } 49 | lastP = v; 50 | points[i] = v; 51 | i += 1; 52 | } 53 | } 54 | 55 | public void RenderTrail() 56 | { 57 | Clear(); 58 | Begin(Mesh.PrimitiveType.Triangles); 59 | Godot.Collections.Array localPoints = new Godot.Collections.Array(); 60 | foreach (Vector3 p in points) 61 | { 62 | localPoints.Add(p - GlobalTransform.origin); 63 | } 64 | Vector3 lastP = Vector3.Zero; 65 | Godot.Collections.Array> verts = new Godot.Collections.Array>(); 66 | int ind = 0; 67 | bool firstIteration = true; 68 | Vector3 lastFirstVec = Vector3.Zero; 69 | foreach (Vector3 p in localPoints) 70 | { 71 | Godot.Collections.Array newLastPoints = new Godot.Collections.Array(); 72 | var offset = lastP - p; 73 | if (offset == Vector3.Zero) 74 | { 75 | continue; 76 | } 77 | var yVec = offset.Normalized(); // get vector pointing from this point to last point 78 | var xVec = Vector3.Zero; 79 | if (firstIteration) 80 | { 81 | xVec = yVec.Cross(yVec.Rotated(Vector3.Right, 0.3f)); //cross product with random vector to get a perpendicular vector 82 | } 83 | else 84 | { 85 | xVec = yVec.Cross(lastFirstVec).Cross(yVec).Normalized(); // keep each loop at the same rotation as the previous 86 | } 87 | var width = maxRadius; 88 | if (shape != 0) 89 | { 90 | width = (1 - Mathf.Ease((ind + 1.0f) / densityLengthwise, shape)) * maxRadius; 91 | } 92 | Godot.Collections.Array segVerts = new Godot.Collections.Array(); 93 | var fIter = true; 94 | for (int i = 0; i < densityAround; i++) // set up row of verts for each level 95 | { 96 | var newVert = p + width * xVec.Rotated(yVec, i * 2 * Mathf.Pi / densityAround).Normalized(); 97 | if (fIter) 98 | { 99 | lastFirstVec = newVert - p; 100 | fIter = false; 101 | } 102 | segVerts.Add(newVert); 103 | } 104 | verts.Add(segVerts); 105 | lastP = p; 106 | ind += 1; 107 | firstIteration = false; 108 | } 109 | for (int j = 0; j < verts.Count - 1; j++) 110 | { 111 | 112 | var cur = verts[j]; 113 | var nxt = verts[j + 1]; 114 | for (int i = 0; i < densityAround; i++) 115 | { 116 | var nxtI = (i + 1) % densityAround; 117 | //order added affects normal 118 | AddVertex(cur[i]); 119 | AddVertex(cur[nxtI]); 120 | AddVertex(nxt[i]); 121 | AddVertex(cur[nxtI]); 122 | AddVertex(nxt[nxtI]); 123 | AddVertex(nxt[i]); 124 | } 125 | } 126 | if (verts.Count > 1) 127 | { 128 | for (int i = 0; i < densityAround; i++) 129 | { 130 | var nxt = (i + 1) % densityAround; 131 | AddVertex(verts[0][i]); 132 | AddVertex(Vector3.Zero); 133 | AddVertex(verts[0][nxt]); 134 | } 135 | 136 | for (int i = 0; i < densityAround; i++) 137 | { 138 | var nxt = (i + 1) % densityAround; 139 | AddVertex(verts[verts.Count - 1][i]); 140 | AddVertex(verts[verts.Count - 1][nxt]); 141 | AddVertex(lastP); 142 | } 143 | } 144 | End(); 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /addons/godot-next-cs/godot_next_cs_plugin.gd: -------------------------------------------------------------------------------- 1 | tool 2 | extends EditorPlugin 3 | 4 | const DelegationInspectorPlugin = preload("res://addons/godot-next/inspector_plugins/delegation_inspector_plugin.gd") 5 | 6 | var delegation_inspector_plugin 7 | 8 | func _enter_tree() -> void: 9 | # When this plugin node enters tree, add the custom types 10 | add_custom_type("Geometry2D", "CollisionShape2D", preload("2d/Geometry2D.cs"), preload("icons/icon_geometry_2d.svg")) 11 | add_custom_type("Trail2D", "Line2D", preload("2d/Trail2D.cs"), preload("icons/icon_trail_2d.svg")) 12 | add_custom_type("Trail3D", "ImmediateGeometry", preload("3d/Trail3D.cs"), preload("icons/icon_trail_3d.svg")) 13 | 14 | Singletons._register_editor_singletons(self) 15 | 16 | delegation_inspector_plugin = DelegationInspectorPlugin.new() 17 | add_inspector_plugin(delegation_inspector_plugin) 18 | 19 | 20 | func _exit_tree() -> void: 21 | remove_inspector_plugin(delegation_inspector_plugin) 22 | remove_custom_type("Trail3D") 23 | remove_custom_type("Trail2D") 24 | remove_custom_type("Geometry2D") 25 | -------------------------------------------------------------------------------- /addons/godot-next-cs/icons/icon_geometry_2d.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /addons/godot-next-cs/icons/icon_geometry_2d.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/icon_geometry_2d.svg-8eb868eaf31d28cd6a6d6e2990755585.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://addons/godot-next-cs/icons/icon_geometry_2d.svg" 13 | dest_files=[ "res://.import/icon_geometry_2d.svg-8eb868eaf31d28cd6a6d6e2990755585.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | stream=false 32 | size_limit=0 33 | detect_3d=true 34 | svg/scale=1.0 35 | -------------------------------------------------------------------------------- /addons/godot-next-cs/icons/icon_trail_2d.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 40 | 42 | 43 | 45 | image/svg+xml 46 | 48 | 49 | 50 | 51 | 52 | 57 | 64 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /addons/godot-next-cs/icons/icon_trail_2d.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/icon_trail_2d.svg-d47bf3d7df5294a0d532782f6dd5fa94.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://addons/godot-next-cs/icons/icon_trail_2d.svg" 13 | dest_files=[ "res://.import/icon_trail_2d.svg-d47bf3d7df5294a0d532782f6dd5fa94.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | stream=false 32 | size_limit=0 33 | detect_3d=true 34 | svg/scale=1.0 35 | -------------------------------------------------------------------------------- /addons/godot-next-cs/icons/icon_trail_3d.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 40 | 42 | 43 | 45 | image/svg+xml 46 | 48 | 49 | 50 | 51 | 52 | 57 | 64 | 70 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /addons/godot-next-cs/icons/icon_trail_3d.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/icon_trail_3d.svg-186ee87dfc04951744fc8103a0c04cca.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://addons/godot-next-cs/icons/icon_trail_3d.svg" 13 | dest_files=[ "res://.import/icon_trail_3d.svg-186ee87dfc04951744fc8103a0c04cca.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | stream=false 32 | size_limit=0 33 | detect_3d=true 34 | svg/scale=1.0 35 | -------------------------------------------------------------------------------- /addons/godot-next-cs/plugin.cfg: -------------------------------------------------------------------------------- 1 | [plugin] 2 | 3 | name="Godot Node Extensions C#" 4 | description="A plugin filled with basic type extensions and utilities for Godot C#." 5 | author="Will Nations, Aaron Franke" 6 | version="0.1" 7 | script="godot_next_cs_plugin.gd" 8 | -------------------------------------------------------------------------------- /addons/godot-next/2d/geometry_2d.gd: -------------------------------------------------------------------------------- 1 | tool 2 | class_name Geometry2D, "../icons/icon_geometry_2d.svg" 3 | extends CollisionShape2D 4 | # author: Henrique "Pigdev" Campos 5 | # license: MIT 6 | # description: Draws a Shape2D using CollisionShape2D's editor plugin handles. 7 | # notes: 8 | # - Don't use it as direct child of CollisionBody2D classes unless you intent 9 | # to use it as its CollisionShape2D. 10 | 11 | export (Color) var color := Color.white setget set_color 12 | export (Vector2) var offset_position := Vector2.ZERO setget set_offset 13 | 14 | func _draw() -> void: 15 | if shape is CircleShape2D: 16 | draw_circle(offset_position, shape.radius, color) 17 | elif shape is RectangleShape2D: 18 | var rect := Rect2(offset_position - shape.extents, shape.extents * 2.0) 19 | draw_rect(rect, color) 20 | elif shape is CapsuleShape2D: 21 | draw_capsule(offset_position, shape.radius, shape.height, color) 22 | 23 | 24 | func draw_capsule(capsule_position: Vector2, capsule_radius: float, 25 | capsule_height: float, capsule_color: Color) -> void: 26 | 27 | var upper_circle_position: Vector2 = capsule_position + Vector2(0, capsule_height * 0.5) 28 | draw_circle(upper_circle_position, capsule_radius, capsule_color) 29 | 30 | var lower_circle_position: Vector2 = capsule_position - Vector2(0, capsule_height * 0.5) 31 | draw_circle(lower_circle_position, capsule_radius, capsule_color) 32 | 33 | var rect_position: Vector2 = capsule_position - Vector2(capsule_radius, capsule_height * 0.5) 34 | var rect := Rect2(rect_position, Vector2(capsule_radius * 2, capsule_height)) 35 | draw_rect(rect, capsule_color) 36 | 37 | 38 | func set_color(new_color: Color) -> void: 39 | color = new_color 40 | update() 41 | 42 | 43 | func set_offset(offset: Vector2) -> void: 44 | offset_position = offset 45 | update() 46 | -------------------------------------------------------------------------------- /addons/godot-next/2d/trail_2d.gd: -------------------------------------------------------------------------------- 1 | class_name Trail2D, "../icons/icon_trail_2d.svg" 2 | extends Line2D 3 | # author: willnationsdev 4 | # brief description: Creates a variable-length trail that tracks the "target" node. 5 | # API details: 6 | # - Use CanvasItem.show_behind_parent or Node2D.z_index (Inspector) to control its layer visibility 7 | # - 'target' and 'target_path' (the 'target vars') will update each other as they are modified 8 | # - If you assign an empty 'target var', the value will automatically update to grab the parent. 9 | # - To completely turn off the Trail2D, set its `trail_length` to 0 10 | # - The node will automatically update its target vars when it is moved around in the tree 11 | # - You can set the "persistence" mode to have it... 12 | # - vanish the trail over time (Off) 13 | # - persist the trail forever, unless modified directly (Always) 14 | # - persist conditionally: 15 | # - persist automatically during movement and then shrink over time when you stop 16 | # - persist according to your own custom logic: 17 | # - use `bool _should_grow()` to return under what conditions 18 | # a point should be added underneath the target. 19 | # - use `bool _should_shrink()` to return under what conditions 20 | # the degen_rate should be removed from the trail's list of points. 21 | 22 | enum Persistence { 23 | OFF, # Do not persist. Remove all points after the trail_length. 24 | ALWAYS, # Always persist. Do not remove any points. 25 | CONDITIONAL, # Sometimes persist. Choose an algorithm for when to add and remove points. 26 | } 27 | 28 | enum PersistWhen { 29 | ON_MOVEMENT, # Add points during movement and remove points when not moving. 30 | CUSTOM, # Override _should_grow() and _should_shrink() to define when to add/remove points. 31 | } 32 | 33 | # The NodePath to the target. 34 | export var target_path: NodePath = @".." setget set_target_path 35 | # If not persisting, the number of points that should be allowed in the trail. 36 | export var trail_length: int = 10 37 | # To what degree the trail should remain in existence before automatically removing points. 38 | export(int, "Off", "Always", "Conditional") var persistence: int = Persistence.OFF 39 | # During conditional persistence, which persistence algorithm to use. 40 | export(int, "On Movement", "Custom") var persistence_condition: int = PersistWhen.ON_MOVEMENT 41 | # During conditional persistence, how many points to remove per frame. 42 | export var degen_rate: int = 1 43 | # If true, automatically set z_index to be one less than the 'target'. 44 | export var auto_z_index: bool = true 45 | # If true, will automatically setup a gradient for a gradually transparent trail. 46 | export var auto_alpha_gradient: bool = true 47 | 48 | # The target node to track. 49 | var target: Node2D setget set_target 50 | 51 | func _init(): 52 | set_as_toplevel(true) 53 | global_position = Vector2() 54 | global_rotation = 0 55 | if auto_alpha_gradient and not gradient: 56 | gradient = Gradient.new() 57 | var first = default_color 58 | first.a = 0 59 | gradient.set_color(0, first) 60 | gradient.set_color(1, default_color) 61 | 62 | 63 | func _notification(p_what: int): 64 | match p_what: 65 | NOTIFICATION_PARENTED: 66 | self.target_path = target_path 67 | if auto_z_index: 68 | z_index = target.z_index - 1 if target else 0 69 | NOTIFICATION_UNPARENTED: 70 | self.target_path = @"" 71 | self.trail_length = 0 72 | 73 | 74 | func _process(_delta: float): 75 | if target: 76 | match persistence: 77 | Persistence.OFF: 78 | add_point(target.global_position) 79 | while get_point_count() > trail_length: 80 | remove_point(0) 81 | Persistence.ALWAYS: 82 | add_point(target.global_position) 83 | pass 84 | Persistence.CONDITIONAL: 85 | match persistence_condition: 86 | PersistWhen.ON_MOVEMENT: 87 | var moved: bool = get_point_position(get_point_count()-1) != target.global_position if get_point_count() else false 88 | if not get_point_count() or moved: 89 | add_point(target.global_position) 90 | else: 91 | #warning-ignore:unused_variable 92 | for i in range(degen_rate): 93 | remove_point(0) 94 | PersistWhen.CUSTOM: 95 | if _should_grow(): 96 | add_point(target.global_position) 97 | if _should_shrink(): 98 | #warning-ignore:unused_variable 99 | for i in range(degen_rate): 100 | remove_point(0) 101 | 102 | 103 | func erase_trail(): 104 | #warning-ignore:unused_variable 105 | for i in range(get_point_count()): 106 | remove_point(0) 107 | 108 | 109 | func set_target(p_value: Node2D): 110 | if p_value: 111 | if get_path_to(p_value) != target_path: 112 | set_target_path(get_path_to(p_value)) 113 | else: 114 | target_path = @"" 115 | 116 | 117 | func set_target_path(p_value: NodePath): 118 | target_path = p_value 119 | target = get_node(p_value) as Node2D if has_node(p_value) else null 120 | 121 | 122 | func _should_grow() -> bool: 123 | return true 124 | 125 | 126 | func _should_shrink() -> bool: 127 | return true 128 | -------------------------------------------------------------------------------- /addons/godot-next/2d/vector_display_2d.gd: -------------------------------------------------------------------------------- 1 | tool 2 | class_name VectorDisplay2D 3 | extends Node 4 | # Displays Vector2 members in the editor via Position2D nodes. 5 | 6 | export(String) var variable_name = "" 7 | export(bool) var relative = true 8 | 9 | var _old_variable_name = null 10 | var _storage: Node2D 11 | 12 | 13 | func _process(delta): 14 | if Engine.is_editor_hint(): 15 | if not variable_name: 16 | if _old_variable_name != variable_name: 17 | _old_variable_name = variable_name 18 | printerr("VectorDisplay2D: Please provide a variable name.") 19 | return 20 | 21 | if not _storage: 22 | _storage = Node2D.new() 23 | get_tree().get_edited_scene_root().add_child(_storage) 24 | return 25 | 26 | for child in _storage.get_children(): 27 | child.queue_free() 28 | 29 | var parent = get_parent() 30 | if relative: 31 | _storage.transform.origin = parent.global_transform.origin 32 | else: 33 | _storage.transform.origin = Vector2.ZERO 34 | 35 | var variable = parent.get(variable_name) 36 | if variable == null: 37 | if _old_variable_name != variable_name: 38 | _old_variable_name = variable_name 39 | printerr("VectorDisplay2D: Variable '" + variable_name + "' not found or invalid on parent node '" + get_parent().get_name() + "'.") 40 | elif variable is Vector2: 41 | _add_position_child(variable) 42 | elif variable is PoolVector2Array: 43 | for item in variable: 44 | _add_position_child(item) 45 | elif variable is Array: 46 | for item in variable: 47 | if item is Vector2: 48 | _add_position_child(item) 49 | 50 | 51 | func _add_position_child(vector): 52 | var node = Position2D.new() 53 | node.transform.origin = vector 54 | _storage.add_child(node) 55 | node.set_owner(get_tree().get_edited_scene_root()) 56 | -------------------------------------------------------------------------------- /addons/godot-next/3d/trail_3d.gd: -------------------------------------------------------------------------------- 1 | class_name Trail3D, "../icons/icon_trail_3d.svg" 2 | extends ImmediateGeometry 3 | # author: miziziziz 4 | # brief description: Creates a variable-length trail on an ImmediateGeometry node. 5 | # API details: 6 | # - density_lengthwise: number of vertex loops in trail 7 | # - density_around: number of vertexes in each loop 8 | # - shape: curve used to shape trail, right click on this in inspector to see curve options 9 | 10 | export(float) var length = 10.0 11 | export var max_radius = 0.5 12 | export(int) var density_lengthwise = 25 13 | export(int) var density_around = 5 14 | export(float, EASE) var shape 15 | 16 | var points = [] 17 | var segment_length = 1.0 18 | 19 | func _ready(): 20 | if length <= 0: 21 | length = 2 22 | if density_around < 3: 23 | density_around = 3 24 | if density_lengthwise < 2: 25 | density_lengthwise = 2 26 | 27 | segment_length = length / density_lengthwise 28 | for i in range(density_lengthwise): 29 | points.append(global_transform.origin) 30 | 31 | 32 | func _process(_delta): 33 | update_trail() 34 | render_trail() 35 | 36 | 37 | func update_trail(): 38 | var ind = 0 39 | var last_p = global_transform.origin 40 | for p in points: 41 | var dis = p.distance_to(last_p) 42 | var seg_len = segment_length 43 | if ind == 0: 44 | seg_len = 0.05 45 | if dis > seg_len: 46 | p = last_p + (p - last_p) / dis * seg_len 47 | last_p = p 48 | points[ind] = p 49 | ind += 1 50 | 51 | 52 | func render_trail(): 53 | clear() 54 | begin(Mesh.PRIMITIVE_TRIANGLES) 55 | #begin(Mesh.PRIMITIVE_LINE_STRIP) 56 | var local_points = [] 57 | for p in points: 58 | local_points.append(p - global_transform.origin) 59 | var last_p = Vector3() 60 | var verts = [] 61 | var ind = 0 62 | var first_iteration = true 63 | var last_first_vec = Vector3() 64 | # Create vertex loops around points. 65 | for p in local_points: 66 | var new_last_points = [] 67 | var offset = last_p - p 68 | if offset == Vector3(): 69 | continue 70 | # Get vector pointing from this point to last point. 71 | var y_vec = offset.normalized() 72 | var x_vec = Vector3() 73 | if first_iteration: 74 | # Cross product with random vector to get a perpendicular vector. 75 | x_vec = y_vec.cross(y_vec.rotated(Vector3.RIGHT, 0.3)) 76 | else: 77 | # Keep each loop at the same rotation as the previous. 78 | x_vec = y_vec.cross(last_first_vec).cross(y_vec).normalized() 79 | var width = max_radius 80 | if shape != 0: 81 | width = (1 - ease((ind + 1.0) / density_lengthwise, shape)) * max_radius 82 | var seg_verts = [] 83 | var f_iter = true 84 | for i in range(density_around): # Set up row of verts for each level. 85 | var new_vert = p + width * x_vec.rotated(y_vec, i * TAU / density_around).normalized() 86 | if f_iter: 87 | last_first_vec = new_vert - p 88 | f_iter = false 89 | seg_verts.append(new_vert) 90 | verts.append(seg_verts) 91 | last_p = p 92 | ind += 1 93 | first_iteration = false 94 | 95 | # Create tris. 96 | for j in range(len(verts) - 1): 97 | var cur = verts[j] 98 | var nxt = verts[j + 1] 99 | for i in range(density_around): 100 | var nxt_i = (i + 1) % density_around 101 | # Order added affects normal. 102 | add_vertex(cur[i]) 103 | add_vertex(cur[nxt_i]) 104 | add_vertex(nxt[i]) 105 | add_vertex(cur[nxt_i]) 106 | add_vertex(nxt[nxt_i]) 107 | add_vertex(nxt[i]) 108 | 109 | if verts.size() > 1: 110 | # Cap off top. 111 | for i in range(density_around): 112 | var nxt = (i + 1) % density_around 113 | add_vertex(verts[0][i]) 114 | add_vertex(Vector3()) 115 | add_vertex(verts[0][nxt]) 116 | 117 | # Cap off bottom. 118 | for i in range(density_around): 119 | var nxt = (i + 1) % density_around 120 | add_vertex(verts[verts.size() - 1][i]) 121 | add_vertex(verts[verts.size() - 1][nxt]) 122 | add_vertex(last_p) 123 | end() 124 | -------------------------------------------------------------------------------- /addons/godot-next/3d/vector_display_3d.gd: -------------------------------------------------------------------------------- 1 | tool 2 | class_name VectorDisplay3D 3 | extends Node 4 | # Displays Vector3 members in the editor via Position3D nodes. 5 | 6 | export(String) var variable_name = "" 7 | export(bool) var relative = true 8 | 9 | var _old_variable_name = null 10 | var _storage: Spatial 11 | 12 | 13 | func _process(delta): 14 | if Engine.is_editor_hint(): 15 | if not variable_name: 16 | if _old_variable_name != variable_name: 17 | _old_variable_name = variable_name 18 | printerr("VectorDisplay3D: Please provide a variable name.") 19 | return 20 | 21 | if not _storage: 22 | _storage = Spatial.new() 23 | get_tree().get_edited_scene_root().add_child(_storage) 24 | return 25 | 26 | for child in _storage.get_children(): 27 | child.queue_free() 28 | 29 | var parent = get_parent() 30 | if relative: 31 | _storage.transform.origin = parent.global_transform.origin 32 | else: 33 | _storage.transform.origin = Vector3.ZERO 34 | 35 | var variable = parent.get(variable_name) 36 | if variable == null: 37 | if _old_variable_name != variable_name: 38 | _old_variable_name = variable_name 39 | printerr("VectorDisplay3D: Variable '" + variable_name + "' not found or invalid on parent node '" + get_parent().get_name() + "'.") 40 | elif variable is Vector3: 41 | _add_position_child(variable) 42 | elif variable is PoolVector3Array: 43 | for item in variable: 44 | _add_position_child(item) 45 | elif variable is Array: 46 | for item in variable: 47 | if item is Vector3: 48 | _add_position_child(item) 49 | 50 | 51 | func _add_position_child(vector): 52 | var node = Position3D.new() 53 | node.transform.origin = vector 54 | _storage.add_child(node) 55 | node.set_owner(get_tree().get_edited_scene_root()) 56 | -------------------------------------------------------------------------------- /addons/godot-next/data/singleton_cache.tres: -------------------------------------------------------------------------------- 1 | [gd_resource type="Resource" load_steps=2 format=2] 2 | 3 | [ext_resource path="res://addons/godot-next/resources/singletons/singleton_cache.gd" type="Script" id=1] 4 | 5 | [resource] 6 | script = ExtResource( 1 ) 7 | -------------------------------------------------------------------------------- /addons/godot-next/global/editor_tools.gd: -------------------------------------------------------------------------------- 1 | tool 2 | class_name EditorTools 3 | extends Reference 4 | # author: willnationsdev 5 | # description: A utility for any features useful in the context of the Editor. 6 | 7 | static func is_in_edited_scene(p_node: Node): 8 | if not p_node.is_inside_tree(): 9 | return false 10 | var edited_scene := p_node.get_tree().edited_scene_root 11 | if p_node == edited_scene: 12 | return true 13 | return edited_scene.is_parent_of(p_node) 14 | -------------------------------------------------------------------------------- /addons/godot-next/global/file_search.gd: -------------------------------------------------------------------------------- 1 | tool 2 | class_name FileSearch 3 | extends Reference 4 | # author: willnationsdev 5 | # license: MIT 6 | # description: A utility with helpful methods to search through one's project files (or any directory). 7 | 8 | class FileEvaluator extends Reference: 9 | var file_path: String = "" setget set_file_path 10 | 11 | # Assigns a new file path to the object. 12 | func _is_match() -> bool: 13 | return true 14 | 15 | 16 | # If _is_match() returns true, returns the key used to store the data. 17 | func _get_key(): 18 | return file_path 19 | 20 | 21 | # If _is_match() returns true, returns the data associated with the file. 22 | func _get_value() -> Dictionary: 23 | return { "path": file_path } 24 | 25 | 26 | # Assigns a new file path to the object. 27 | func set_file_path(p_value): 28 | file_path = p_value 29 | 30 | 31 | class FilesThatHaveString extends FileEvaluator: 32 | var _compare: String 33 | 34 | func _init(p_compare: String = ""): 35 | _compare = p_compare 36 | 37 | 38 | func _is_match() -> bool: 39 | return file_path.find(_compare) != -1 40 | 41 | 42 | class FilesThatAreSubsequenceOf extends FileEvaluator: 43 | var _compare: String 44 | var _case_sensitive: bool 45 | 46 | func _init(p_compare: String = "", p_case_sensitive: bool = false): 47 | _compare = p_compare 48 | _case_sensitive = p_case_sensitive 49 | 50 | 51 | func _is_match() -> bool: 52 | if _case_sensitive: 53 | return _compare.is_subsequence_of(file_path) 54 | return _compare.is_subsequence_ofi(file_path) 55 | 56 | 57 | class FilesThatMatchRegex extends FileEvaluator: 58 | var _regex: RegEx = RegEx.new() 59 | var _compare_full_path 60 | var _match: RegExMatch = null 61 | 62 | func _init(p_regex_str: String, p_compare_full_path: bool = false): 63 | _compare_full_path = p_compare_full_path 64 | if _regex.compile(p_regex_str) != OK: 65 | push_error("Check failed. FilesThatMatchRegex failed to compile regex: " + p_regex_str) 66 | return 67 | 68 | 69 | func _is_match() -> bool: 70 | if not _regex.is_valid(): 71 | return false 72 | _match = _regex.search(file_path if _compare_full_path else file_path.get_file()) 73 | return _match != null 74 | 75 | 76 | func _get_value() -> Dictionary: 77 | var data = ._get_value() 78 | data.match = _match 79 | return data 80 | 81 | 82 | class FilesThatExtendResource extends FileEvaluator: 83 | var _match_func: FuncRef 84 | var _exts: Dictionary 85 | 86 | func _init(p_types: PoolStringArray = PoolStringArray(["Resource"]), p_match_func: FuncRef = null, p_block_base_resource: bool = false): 87 | _match_func = p_match_func 88 | for type in p_types: 89 | for a_ext in ResourceLoader.get_recognized_extensions_for_type(type): 90 | _exts[a_ext] = null 91 | if p_block_base_resource: 92 | #warning-ignore:return_value_discarded 93 | #warning-ignore:return_value_discarded 94 | _exts.erase("tres") 95 | _exts.erase("res") 96 | 97 | 98 | func _is_match() -> bool: 99 | for a_ext in _exts: 100 | if file_path.get_file().get_extension() == a_ext: 101 | if _match_func: 102 | return _match_func.call_func(file_path) 103 | return true 104 | return false 105 | 106 | 107 | const SELF_PATH: String = "res://addons/godot-next/global/file_search.gd" 108 | 109 | static func search_string(p_str: String, p_from_dir: String = "res://", p_recursive: bool = true) -> Dictionary: 110 | return _search(FilesThatHaveString.new(p_str), p_from_dir, p_recursive) 111 | 112 | 113 | static func search_subsequence(p_str: String, p_from_dir: String = "res://", p_recursive: bool = true) -> Dictionary: 114 | return _search(FilesThatAreSubsequenceOf.new(p_str, false), p_from_dir, p_recursive) 115 | 116 | 117 | static func search_subsequence_i(p_str: String, p_from_dir: String = "res://", p_recursive: bool = true) -> Dictionary: 118 | return _search(FilesThatAreSubsequenceOf.new(p_str, true), p_from_dir, p_recursive) 119 | 120 | 121 | static func search_regex(p_regex: String, p_from_dir: String = "res://", p_recursive: bool = true) -> Dictionary: 122 | return _search(FilesThatMatchRegex.new(p_regex, false), p_from_dir, p_recursive) 123 | 124 | 125 | static func search_regex_full_path(p_regex: String, p_from_dir: String = "res://", p_recursive: bool = true) -> Dictionary: 126 | return _search(FilesThatMatchRegex.new(p_regex, true), p_from_dir, p_recursive) 127 | 128 | 129 | static func search_scripts(p_match_func: FuncRef = null, p_from_dir: String = "res://", p_recursive: bool = true) -> Dictionary: 130 | return _search(FilesThatExtendResource.new(["Script"], p_match_func), p_from_dir, p_recursive) 131 | 132 | 133 | static func search_scenes(p_match_func: FuncRef = null, p_from_dir: String = "res://", p_recursive: bool = true) -> Dictionary: 134 | return _search(FilesThatExtendResource.new(["PackedScene"], p_match_func), p_from_dir, p_recursive) 135 | 136 | 137 | static func search_types(p_match_func: FuncRef = null, p_from_dir: String = "res://", p_recursive: bool = true) -> Dictionary: 138 | return _search(FilesThatExtendResource.new(["Script", "PackedScene"], p_match_func), p_from_dir, p_recursive) 139 | 140 | 141 | static func search_resources(p_types: PoolStringArray = ["Resource"], p_match_func: FuncRef = null, p_from_dir: String = "res://", p_recursive: bool = true) -> Dictionary: 142 | return _search(FilesThatExtendResource.new(p_types, p_match_func), p_from_dir, p_recursive) 143 | 144 | 145 | static func _this() -> Script: 146 | return load(SELF_PATH) as Script 147 | 148 | 149 | # p_evaluator: A FileEvaluator type. 150 | # p_from_dir: The starting location from which to scan. 151 | # p_recursive: If true, scan all sub-directories, not just the given one. 152 | static func _search(p_evaluator: FileEvaluator, p_from_dir: String = "res://", p_recursive: bool = true) -> Dictionary: 153 | var dirs: Array = [p_from_dir] 154 | var dir: Directory = Directory.new() 155 | var data: Dictionary = {} 156 | var eval: FileEvaluator = p_evaluator 157 | 158 | # Generate 'data' map. 159 | while not dirs.empty(): 160 | var dir_name = dirs.back() 161 | dirs.pop_back() 162 | 163 | if dir.open(dir_name) == OK: 164 | #warning-ignore:return_value_discarded 165 | dir.list_dir_begin() 166 | var file_name = dir.get_next() 167 | while file_name: 168 | # Ignore hidden content. 169 | if not file_name.begins_with("."): 170 | var a_path = dir.get_current_dir().plus_file(file_name) 171 | eval.set_file_path(a_path) 172 | 173 | # If a directory, then add to list of directories to visit. 174 | if p_recursive and dir.current_is_dir(): 175 | dirs.push_back(a_path) 176 | # If a file, check if we already have a record for the same name. 177 | # Only use files with extensions. 178 | elif not data.has(a_path) and eval._is_match(): 179 | data[eval._get_key()] = eval._get_value() 180 | 181 | # Move on to the next file in this directory. 182 | file_name = dir.get_next() 183 | 184 | # We've exhausted all files in this directory. Close the iterator. 185 | dir.list_dir_end() 186 | 187 | return data 188 | -------------------------------------------------------------------------------- /addons/godot-next/global/file_system_link.gd: -------------------------------------------------------------------------------- 1 | class_name FileSystemLink 2 | extends Reference 3 | # author: willnationsdev 4 | # description: A utility for creating links (file/directory, symbolic/hard). 5 | # API details: 6 | # - All methods' parameters are ordered in Unix fashion, {,} 7 | # - Methods are aliased so that the parameters are implied by the method name. 8 | 9 | enum LinkTypes { 10 | SOFT, 11 | HARD 12 | } 13 | 14 | enum TargetTypes { 15 | FILE, 16 | DIR, 17 | WINDOWS_JUNCTION 18 | } 19 | 20 | static func mk_hard_file(p_target: String, p_linkpath: String = "") -> int: 21 | return _make_link(p_target, p_linkpath, TargetTypes.FILE, LinkTypes.HARD) 22 | 23 | 24 | static func mk_soft_file(p_target: String, p_linkpath: String = "") -> int: 25 | return _make_link(p_target, p_linkpath, TargetTypes.FILE, LinkTypes.SOFT) 26 | 27 | 28 | static func mk_hard_dir(p_target: String, p_linkpath: String = "") -> int: 29 | return _make_link(p_target, p_linkpath, TargetTypes.DIR, LinkTypes.HARD) 30 | 31 | 32 | static func mk_soft_dir(p_target: String, p_linkpath: String = "") -> int: 33 | return _make_link(p_target, p_linkpath, TargetTypes.DIR, LinkTypes.SOFT) 34 | 35 | 36 | static func mk_windows_junction(p_target: String, p_linkpath: String = "") -> int: 37 | return _make_link(p_target, p_linkpath, TargetTypes.WINDOWS_JUNCTION, LinkTypes.SOFT) 38 | 39 | 40 | static func _make_link(p_target: String, p_linkpath: String = "", p_target_type = TargetTypes.FILE, p_link_type: int = LinkTypes.SOFT) -> int: 41 | var params := PoolStringArray() 42 | var dir := Directory.new() 43 | var output := [] 44 | var target := ProjectSettings.globalize_path(p_target) 45 | var linkpath := ProjectSettings.globalize_path(p_linkpath) 46 | match p_target_type: 47 | TargetTypes.FILE: 48 | if not dir.file_exists(target): 49 | return ERR_FILE_NOT_FOUND 50 | TargetTypes.DIR, TargetTypes.WINDOWS_JUNCTION: 51 | if not dir.dir_exists(target): 52 | return ERR_FILE_NOT_FOUND 53 | 54 | match OS.get_name(): 55 | "Windows": 56 | match p_link_type: 57 | LinkTypes.SOFT: 58 | pass 59 | LinkTypes.HARD: 60 | params.append("/H") 61 | _: 62 | printerr("Unknown link type passed to FileSystemLink.make_link: ", p_link_type) 63 | return ERR_INVALID_PARAMETER 64 | #warning-ignore:unreachable_code 65 | match p_target_type: 66 | TargetTypes.FILE: 67 | pass 68 | TargetTypes.DIR: 69 | params.append("/D") 70 | TargetTypes.WINDOWS_JUNCTION: 71 | params.append("/J") 72 | _: 73 | printerr("Unknown target type passed to FileSystemLink.make_link: ", p_target_type) 74 | return ERR_INVALID_PARAMETER 75 | 76 | params.append(linkpath) 77 | params.append(target) 78 | #warning-ignore:return_value_discarded 79 | OS.execute("mklink", params, true, output) 80 | return OK 81 | "X11", "OSX", "LinuxBSD": 82 | match p_link_type: 83 | LinkTypes.SOFT: 84 | params.append("-s") 85 | LinkTypes.HARD: 86 | pass 87 | _: 88 | printerr("Unknown link type passed to FileSystemLink.make_link", p_link_type) 89 | return ERR_INVALID_PARAMETER 90 | 91 | match p_target_type: 92 | TargetTypes.FILE: 93 | pass 94 | TargetTypes.DIR: 95 | params.append("-d") 96 | TargetTypes.WINDOWS_JUNCTION: 97 | printerr("Junctions are a Windows feature") 98 | return ERR_INVALID_PARAMETER 99 | _: 100 | printerr("Unknown target type passed to FileSystemLink.make_link: ", p_target_type) 101 | return ERR_INVALID_PARAMETER 102 | 103 | params.append(target) 104 | params.append(linkpath) 105 | #warning-ignore:return_value_discarded 106 | OS.execute("ln", params, true, output) 107 | return OK 108 | _: 109 | return ERR_UNAVAILABLE 110 | -------------------------------------------------------------------------------- /addons/godot-next/global/inspector_controls.gd: -------------------------------------------------------------------------------- 1 | class_name InspectorControls 2 | extends Reference 3 | # author: xdgamestudios 4 | # license: MIT 5 | # description: 6 | # A collection of classes and factory methods for generating Controls 7 | # oriented towards editing data. Useful for modifying the 8 | # EditorInspector or generating your own in-game data-editing tools. 9 | 10 | const ADD_ICON = preload("res://addons/godot-next/icons/icon_add.svg") 11 | 12 | class DropdownAppender extends HBoxContainer: 13 | func get_button() -> ToolButton: 14 | return get_node("ToolButton") as ToolButton 15 | 16 | 17 | func get_dropdown() -> OptionButton: 18 | return get_node("Dropdown") as OptionButton 19 | 20 | 21 | func get_selected_label() -> String: 22 | var dropdown := get_dropdown() 23 | var index := dropdown.get_selected_id() 24 | return dropdown.get_item_text(index) 25 | 26 | 27 | func get_selected_meta(): 28 | return get_dropdown().get_selected_metadata() 29 | 30 | 31 | # Instantiates a Label. If align is not set the dafault ALIGN_LEFT will be used. 32 | static func new_label(p_label: String, p_align: int = Label.ALIGN_LEFT) -> Label: 33 | var label = Label.new() 34 | label.text = p_label 35 | label.align = p_align 36 | return label 37 | 38 | 39 | # Instantiates an empty control used to insert space between properties. 40 | static func new_space(p_size: Vector2, p_horizontal_flag: int = Control.SIZE_EXPAND_FILL, p_vertical_flag: int = Control.SIZE_EXPAND_FILL) -> Control: 41 | var control = Control.new() 42 | control.size_flags_horizontal = p_horizontal_flag 43 | control.size_flags_vertical = p_vertical_flag 44 | control.rect_min_size = p_size 45 | return control 46 | 47 | 48 | # Instantiates a Button. If toggle mode is set, p_object/p_callback 49 | # will connect to its "toggled" signal. Else, "pressed". 50 | static func new_button(p_label: String, p_toggle_mode: bool = false, p_object: Object = null, p_callback: String = "") -> Button: 51 | var button = Button.new() 52 | button.text = p_label 53 | button.name = "Button" 54 | button.toggle_mode = p_toggle_mode 55 | 56 | if p_object and p_callback: 57 | if p_toggle_mode: 58 | button.connect("toggled", p_object, p_callback) 59 | else: 60 | button.connect("pressed", p_object, p_callback) 61 | 62 | return button 63 | 64 | 65 | # Instantiates a ToolButton. If toggle mode is set, p_object/p_callback 66 | # will connect to its "toggled" signal. Else, "pressed". 67 | static func new_tool_button(p_icon: Texture, p_toggle_mode: bool = false, p_object: Object = null, p_callback: String = "") -> ToolButton: 68 | var button = ToolButton.new() 69 | button.icon = p_icon 70 | button.name = "ToolButton" 71 | button.toggle_mode = p_toggle_mode 72 | 73 | if p_object and p_callback: 74 | if p_toggle_mode: 75 | button.connect("toggled", p_object, p_callback) 76 | else: 77 | button.connect("pressed", p_object, p_callback) 78 | 79 | return button 80 | 81 | 82 | static func new_dropdown(p_elements: Dictionary, p_object: Object = null, p_callback: String = "") -> OptionButton: 83 | var dropdown := OptionButton.new() 84 | var index = 0 85 | for a_label in p_elements: 86 | dropdown.add_item(a_label, index) 87 | dropdown.set_item_metadata(index, p_elements[a_label]) 88 | index += 1 89 | dropdown.name = "Dropdown" 90 | dropdown.size_flags_horizontal = HBoxContainer.SIZE_EXPAND_FILL 91 | 92 | if p_object and p_callback: 93 | dropdown.connect("item_selected", p_object, p_callback, [dropdown]) 94 | 95 | return dropdown 96 | 97 | 98 | static func new_dropdown_appender(p_elements: Dictionary, p_object: Object = null, p_callback: String = "") -> DropdownAppender: 99 | var dropdown_appender := DropdownAppender.new() 100 | 101 | var dropdown := new_dropdown(p_elements) 102 | 103 | var tool_button = ToolButton.new() 104 | tool_button.name = "ToolButton" 105 | tool_button.icon = ADD_ICON 106 | 107 | dropdown_appender.add_child(dropdown) 108 | dropdown_appender.add_child(tool_button) 109 | 110 | if p_object and p_callback: 111 | tool_button.connect("pressed", p_object, p_callback, [dropdown_appender]) 112 | 113 | return dropdown_appender 114 | -------------------------------------------------------------------------------- /addons/godot-next/global/physics_layers.gd: -------------------------------------------------------------------------------- 1 | tool 2 | class_name PhysicsLayers 3 | extends Resource 4 | # author: xaguzman 5 | # license: MIT 6 | # description: 7 | # A Utility class which allows easy access to your physics 8 | # layers via their names in the project settings. 9 | 10 | const _PHYSICS_LAYERS_BIT: Dictionary = {} 11 | const _PHYSICS_2D_PREFIX = "2d_physics" 12 | const _PHYSICS_3D_PREFIX = "3d_physics" 13 | 14 | static func setup() -> void: 15 | for prefix in [_PHYSICS_2D_PREFIX, _PHYSICS_3D_PREFIX]: 16 | var path : String = "layer_names/".plus_file(prefix) 17 | for i in range(1, 21): 18 | var layer_path: String = path.plus_file(str("layer_", i)) 19 | var layer_name: String = ProjectSettings.get(layer_path) 20 | 21 | if not layer_name: 22 | layer_name = str("Layer ", i) 23 | 24 | var layer_key: String = prefix.plus_file(layer_name) 25 | _PHYSICS_LAYERS_BIT[layer_key] = i - 1 26 | 27 | 28 | # Get the corresponding bit of the layer named 29 | static func _get_physics_layer_index(layer_name: String) -> int: 30 | if not _PHYSICS_LAYERS_BIT.has(layer_name): 31 | setup() 32 | 33 | assert (_PHYSICS_LAYERS_BIT.has(layer_name)) 34 | return _PHYSICS_LAYERS_BIT[layer_name] 35 | 36 | 37 | # Get the layer that corresponds to to the combination of all the passed layer names. 38 | static func get_physics_layer(layer_names: Array, is_layer_3d := false) -> int: 39 | var res: int = 0 40 | for i in range(layer_names.size()): 41 | var layer_bit : int = get_physics_layer_index(layer_names[i], is_layer_3d) 42 | res |= 1 << layer_bit 43 | return res 44 | 45 | 46 | static func get_physics_layer_index(layer_name: String, is_layer_3d := false) -> int: 47 | var res: int 48 | if is_layer_3d: 49 | res = _get_physics_layer_index(_PHYSICS_3D_PREFIX.plus_file(layer_name)) 50 | else: 51 | res = _get_physics_layer_index(_PHYSICS_2D_PREFIX.plus_file(layer_name)) 52 | 53 | return res 54 | -------------------------------------------------------------------------------- /addons/godot-next/global/project_tools.gd: -------------------------------------------------------------------------------- 1 | tool 2 | class_name ProjectTools 3 | extends Reference 4 | # author: willnationsdev 5 | # license: MIT 6 | # description: A utility for any features useful in the context of a Godot Project. 7 | 8 | static func try_set_setting(p_name: String, p_default_value, p_pinfo: PropertyInfo) -> bool: 9 | if ProjectSettings.has_setting(p_name): 10 | return false 11 | set_setting(p_name, p_default_value, p_pinfo) 12 | return true 13 | 14 | 15 | static func set_setting(p_name: String, p_default_value, p_pinfo: PropertyInfo) -> void: 16 | ProjectSettings.set_setting(p_name, p_default_value) 17 | ProjectSettings.add_property_info(p_pinfo.to_dict()) 18 | ProjectSettings.set_initial_value(p_name, p_default_value) 19 | -------------------------------------------------------------------------------- /addons/godot-next/global/singletons.gd: -------------------------------------------------------------------------------- 1 | tool 2 | class_name Singletons 3 | extends Reference 4 | # author: xdgamestudios 5 | # license: MIT 6 | # description: An API for accessing singletons 7 | # deps: 8 | # - singleton_cache.tres 9 | 10 | const SINGLETON_CACHE = preload("res://addons/godot-next/data/singleton_cache.tres") 11 | 12 | # Look up a singleton by its script. If it doesn't exist yet, make it. 13 | # If it's a Resource with a persistent file path, load it in from memory. 14 | static func fetch(p_script: Script) -> Object: 15 | var cache: Dictionary = SINGLETON_CACHE.get_cache() 16 | if not cache.has(p_script): 17 | if p_script is Resource: 18 | var path = _get_persistent_path(p_script) 19 | if path: 20 | var paths: Dictionary = SINGLETON_CACHE.get_paths() 21 | cache[p_script] = ResourceLoader.load(path) if ResourceLoader.exists(path) else p_script.new() 22 | paths[p_script] = path 23 | else: 24 | cache[p_script] = p_script.new() 25 | else: 26 | cache[p_script] = p_script.new() 27 | return cache[p_script] 28 | 29 | 30 | # Returns a singleton by its class_name as a String. 31 | static func fetchs(p_name: String) -> Object: 32 | var ct = ClassType.new(p_name) 33 | if ct.res: 34 | return fetch(ct.res) 35 | return null 36 | 37 | 38 | # Returns an editor-only singleton by its class name. 39 | static func fetch_editor(p_class: GDScriptNativeClass) -> Object: 40 | if not Engine.editor_hint: 41 | push_warning("Cannot access '%s' (editor-only class) at runtime." % p_class.get_class()) 42 | return null 43 | 44 | var cache: Dictionary = SINGLETON_CACHE.get_cache() 45 | if cache.has(p_class): 46 | return cache[p_class] 47 | return null 48 | 49 | 50 | # Remove a singleton from the cache and any paths associated with it. 51 | static func erase(p_script: Script) -> bool: 52 | var cache: Dictionary = SINGLETON_CACHE.get_cache() 53 | var paths: Dictionary = SINGLETON_CACHE.get_paths() 54 | var erased = cache.erase(p_script) 55 | #warning-ignore:return_value_discarded 56 | paths.erase(p_script) 57 | return erased 58 | 59 | 60 | static func save(p_script: Script) -> void: 61 | var paths: Dictionary = SINGLETON_CACHE.get_paths() 62 | if not paths.has(p_script): 63 | return 64 | var cache: Dictionary = SINGLETON_CACHE.get_cache() 65 | #warning-ignore:return_value_discarded 66 | ResourceSaver.save(paths[p_script], cache[p_script]) 67 | 68 | 69 | static func save_all() -> void: 70 | var cache: Dictionary = SINGLETON_CACHE.get_cache() 71 | var paths: Dictionary = SINGLETON_CACHE.get_paths() 72 | for a_script in paths: 73 | #warning-ignore:return_value_discarded 74 | ResourceSaver.save(paths[a_script], cache[a_script]) 75 | 76 | 77 | static func _get_persistent_path(p_script: Script): 78 | return p_script.get("SELF_RESOURCE") 79 | 80 | 81 | # Register all editor-only singletons. 82 | static func _register_editor_singletons(plugin: EditorPlugin): 83 | var cache: Dictionary = SINGLETON_CACHE.get_cache() 84 | 85 | cache[UndoRedo] = plugin.get_undo_redo() 86 | 87 | cache[EditorInterface] = plugin.get_editor_interface() 88 | 89 | cache[ScriptEditor] = plugin.get_editor_interface().get_script_editor() 90 | cache[EditorSelection] = plugin.get_editor_interface().get_selection() 91 | cache[EditorSettings] = plugin.get_editor_interface().get_editor_settings() 92 | cache[EditorFileSystem] = plugin.get_editor_interface().get_resource_filesystem() 93 | cache[EditorResourcePreview] = plugin.get_editor_interface().get_resource_previewer() 94 | -------------------------------------------------------------------------------- /addons/godot-next/global/variant.gd: -------------------------------------------------------------------------------- 1 | tool 2 | class_name Variant 3 | extends Reference 4 | # author: willnationsdev 5 | # license: MIT 6 | # description: A utility class for handling Variants. 7 | 8 | # Returns a string form of all types, but allows Objects to override their string conversion. 9 | static func var_to_string(p_value) -> String: 10 | if typeof(p_value) == TYPE_OBJECT and p_value.has_method("_to_string"): 11 | return p_value._to_string() as String 12 | return var2str(p_value) 13 | 14 | 15 | # Returns the string text of a type's name, for all types. 16 | static func get_type(p_value) -> String: 17 | match typeof(p_value): 18 | TYPE_NIL: 19 | return "null" 20 | TYPE_BOOL: 21 | return "bool" 22 | TYPE_INT: 23 | return "int" 24 | TYPE_REAL: 25 | # can update this to conditionally display "double" once the engine 26 | # adds support for it and sets up an Engine.get_real_type() method or something. 27 | return "float" 28 | TYPE_STRING: 29 | return "String" 30 | TYPE_VECTOR2: 31 | return "Vector2" 32 | TYPE_RECT2: 33 | return "Rect2" 34 | TYPE_VECTOR3: 35 | return "Vector3" 36 | TYPE_TRANSFORM2D: 37 | return "Transform2D" 38 | TYPE_PLANE: 39 | return "Plane" 40 | TYPE_QUAT: 41 | return "Quat" 42 | TYPE_AABB: 43 | return "AABB" 44 | TYPE_BASIS: 45 | return "Basis" 46 | TYPE_TRANSFORM: 47 | return "Transform" 48 | TYPE_COLOR: 49 | return "Color" 50 | TYPE_NODE_PATH: 51 | return "NodePath" 52 | TYPE_RID: 53 | return "RID" 54 | TYPE_OBJECT: 55 | var ct := ClassType.new(p_value) 56 | return ct.get_type_class() 57 | #return p_value.get_class() 58 | TYPE_DICTIONARY: 59 | return "Dictionary" 60 | TYPE_ARRAY: 61 | return "Array" 62 | TYPE_RAW_ARRAY: 63 | return "PoolByteArray" 64 | TYPE_INT_ARRAY: 65 | return "PoolIntArray" 66 | TYPE_REAL_ARRAY: 67 | return "PoolRealArray" 68 | TYPE_STRING_ARRAY: 69 | return "PoolStringArray" 70 | TYPE_VECTOR2_ARRAY: 71 | return "PoolVector2Array" 72 | TYPE_VECTOR3_ARRAY: 73 | return "PoolVector3Array" 74 | TYPE_COLOR_ARRAY: 75 | return "PoolColorArray" 76 | return "" 77 | -------------------------------------------------------------------------------- /addons/godot-next/godot_next_plugin.gd: -------------------------------------------------------------------------------- 1 | tool 2 | extends EditorPlugin 3 | 4 | const DelegationInspectorPlugin = preload("res://addons/godot-next/inspector_plugins/delegation_inspector_plugin.gd") 5 | 6 | var delegation_inspector_plugin 7 | 8 | func _enter_tree() -> void: 9 | Singletons._register_editor_singletons(self) 10 | 11 | delegation_inspector_plugin = DelegationInspectorPlugin.new() 12 | add_inspector_plugin(delegation_inspector_plugin) 13 | 14 | 15 | func _exit_tree() -> void: 16 | remove_inspector_plugin(delegation_inspector_plugin) 17 | -------------------------------------------------------------------------------- /addons/godot-next/gui/cycle.gd: -------------------------------------------------------------------------------- 1 | class_name Cycle, "../icons/icon_cycle.svg" 2 | extends TabContainer 3 | # author: willnationsdev 4 | # description: Cycles through child nodes without any visibility or container effects. 5 | 6 | func _init(): 7 | tabs_visible = false 8 | self_modulate = Color(1, 1, 1, 0) 9 | 10 | 11 | func _ready(): 12 | for a_child in get_children(): 13 | if a_child is Control: 14 | (a_child as Control).set_as_toplevel(true) 15 | 16 | 17 | func add_child(p_value: Node, p_legible_unique_name: bool = false): 18 | .add_child(p_value, p_legible_unique_name) 19 | if p_value and p_value is Control: 20 | (p_value as Control).set_as_toplevel(true) 21 | 22 | 23 | func remove_child(p_value: Node): 24 | .remove_child(p_value) 25 | if p_value and p_value is Control: 26 | (p_value as Control).set_as_toplevel(false) 27 | -------------------------------------------------------------------------------- /addons/godot-next/gui/debug_label.gd: -------------------------------------------------------------------------------- 1 | tool 2 | class_name DebugLabel 3 | extends Label 4 | # author: Xrayez 5 | # license: MIT 6 | # description: 7 | # A label which displays a list of property values in any Object 8 | # instance, suitable for both in-game and editor debugging. 9 | # usage: 10 | # var debug_label = DebugLabel.new(node) 11 | # debug_label.watchv(["position:x", "scale", "rotation"]) 12 | # 13 | # todo: 14 | # Use RichTextLabel or custom drawing for color coding of core data types. 15 | # Unfortunately, it doesn't compute its minimum size the same way as a Label 16 | 17 | # Advanced: depending on the properties inspected, you may need to switch 18 | # to either "IDLE" or "PHYSICS" update mode to avoid thread issues. 19 | enum UpdateMode { 20 | IDLE, 21 | PHYSICS, 22 | MANUAL, 23 | } 24 | 25 | export(UpdateMode) var update_mode = UpdateMode.IDLE setget set_update_mode 26 | 27 | # Assign a node via inspector. If empty, a parent node is inspected instead. 28 | export var target_path := NodePath() setget set_target_path 29 | 30 | export var show_label_name := false 31 | export var show_target_name := false 32 | 33 | # A list of property names to be inspected and printed in the `target`. 34 | # Use indexed syntax (:) to access nested properties: "position:x". 35 | # Indexing will also work with any `onready` vars defined via script. 36 | # These can be set beforehand via inspector or via code with "watch()". 37 | export var properties := PoolStringArray() 38 | 39 | # Inspected object, not restricted to "Node" type, may be assigned via code. 40 | var target: Object 41 | 42 | func _init(p_target: Object = null) -> void: 43 | if p_target != null: 44 | target = p_target 45 | 46 | # # TODO: RichTextLabel relevant overrides 47 | # rect_clip_content = false 48 | # scroll_active = false 49 | # selection_enabled = true 50 | 51 | 52 | func _enter_tree() -> void: 53 | set_process_internal(true) 54 | 55 | if not OS.is_debug_build(): 56 | text = "" 57 | hide() 58 | return 59 | if target == null: 60 | if not target_path.is_empty(): 61 | _update_target_from_path() 62 | else: 63 | target = get_parent() 64 | 65 | 66 | func _exit_tree() -> void: 67 | set_process_internal(false) 68 | set_physics_process_internal(false) 69 | 70 | target = null 71 | 72 | 73 | func _notification(what: int) -> void: 74 | # Using internal processing as it guarantees that 75 | # debug info is updated even if the scene tree is paused. 76 | match what: 77 | NOTIFICATION_INTERNAL_PROCESS: 78 | _update_debug_info() 79 | NOTIFICATION_INTERNAL_PHYSICS_PROCESS: 80 | _update_debug_info() 81 | 82 | 83 | func set_target_path(p_path: NodePath) -> void: 84 | target_path = p_path 85 | call_deferred("_update_target_from_path") 86 | 87 | 88 | func set_update_mode(p_mode: int) -> void: 89 | update_mode = p_mode 90 | 91 | match update_mode: 92 | UpdateMode.IDLE: 93 | set_process_internal(true) 94 | set_physics_process_internal(false) 95 | UpdateMode.PHYSICS: 96 | set_process_internal(false) 97 | set_physics_process_internal(true) 98 | UpdateMode.MANUAL: 99 | set_process_internal(false) 100 | set_physics_process_internal(false) 101 | 102 | 103 | func watch(p_what: String) -> void: 104 | properties = PoolStringArray([p_what]) 105 | 106 | 107 | func watchv(p_what: PoolStringArray) -> void: 108 | properties = p_what 109 | 110 | 111 | func watch_append(p_what: String) -> void: 112 | properties.append(p_what) 113 | 114 | 115 | func watch_appendv(p_what: PoolStringArray) -> void: 116 | properties.append_array(p_what) 117 | 118 | 119 | func clear() -> void: 120 | properties = PoolStringArray() 121 | 122 | 123 | func update() -> void: 124 | # Have to be called manually if operating in UpdateMode.MANUAL 125 | _update_debug_info() 126 | .update() 127 | 128 | 129 | func _update_debug_info() -> void: 130 | if not OS.is_debug_build(): 131 | return 132 | 133 | text = "" 134 | 135 | if not is_instance_valid(target): 136 | text = "null" 137 | return 138 | 139 | if show_label_name: 140 | text += "%s\n" % [name] 141 | 142 | if show_target_name: 143 | var object_name := String() 144 | 145 | if target is Node: 146 | object_name = target.name 147 | elif target is Resource: 148 | object_name = target.resource_name 149 | 150 | if not object_name.empty(): 151 | text += "%s\n" % [object_name] 152 | 153 | for prop in properties: 154 | if prop.empty(): 155 | continue 156 | var var_str = var2str(target.get_indexed(prop)) 157 | text += "%s = %s\n" % [prop, var_str] 158 | 159 | 160 | func _update_target_from_path() -> void: 161 | if has_node(target_path): 162 | target = get_node(target_path) 163 | # target = get_node_or_null(target_path) # 3.2 164 | -------------------------------------------------------------------------------- /addons/godot-next/gui/v_box_item_list.gd: -------------------------------------------------------------------------------- 1 | class_name VBoxItemList, "../icons/icon_v_box_item_list.svg" 2 | extends VBoxContainer 3 | # author: willnationsdev 4 | # description: Creates a vertical list of items that can be added or removed. Items are a user-specified Script or Scene Control. 5 | # API details: 6 | # - The '_item_added' and '_item_removed' methods may be overridden for custom behaviors. 7 | # - External nodes can connect to the 'item_added' and 'item_removed' signals for custom reactions. 8 | # - Note that the node provided in the '_item_removed' virtual method and 'item_removed' signal is free'd after their conclusion. 9 | # - Note that the virtual methods will execute just ahead of emitting their signal counterpart. 10 | # - Items may define their own "_get_label" method which returns the string for their label text. 11 | # - If either 'allow_reordering' or 'editable_labels' is true, labels will not be generated automatically via item_prefix + index. 12 | 13 | signal item_inserted(p_index, p_control) 14 | signal item_removed(p_index, p_control) 15 | 16 | const ICON_ADD: Texture = preload("../icons/icon_add.svg") 17 | const ICON_DELETE: Texture = preload("../icons/icon_import_fail.svg") 18 | const ICON_SLIDE: Texture = preload("../icons/icon_mirror_y.svg") 19 | 20 | # The title text at the top of the node. 21 | export var title: String = "" setget set_title 22 | # The script for the item. Incompatible with item_scene. 23 | export var item_script: Script = null setget set_item_script 24 | # The scene for the item. Incompatible with item_script. 25 | export var item_scene: PackedScene = null setget set_item_scene 26 | # label: The prefix text before enumeration, e.g. 'Item' results in Item 1, Item 2, etc. 27 | # If empty, will generate no labels at all unless the item has '_get_label' implemented. 28 | export var item_prefix: String = "" setget set_item_prefix 29 | # label: The color of the highlighted (hovered over) label. 30 | export var label_tint: Color = Color(1, 1, 1, 1) setget set_label_tint 31 | # settings: If true, users may reorder items by dragging from the slide icon. Else the slide icon is hidden. 32 | export var allow_reordering: bool = true setget set_allow_reordering 33 | # settings: If true, users may double-click a label to edit its name. Else no effect. 34 | export var editable_labels: bool = true 35 | # settings: If true, users may click the delete button to delete an item. Else the delete button is hidden. 36 | export var deletable_items: bool = true setget set_deletable_items 37 | # settings: If true, indexes items with size + 1. Else indexes items with a constantly growing ID, i.e. doesn't reset due to item deletion. 38 | export var index_by_size: bool = false 39 | 40 | var label: Label 41 | var add_button: ToolButton 42 | var content: VBoxContainer 43 | 44 | var _dragged_item: HBoxContainer = null 45 | var _hovered_item: HBoxContainer = null 46 | var _insertions: int = 0 47 | var _removals: int = 0 48 | 49 | func _init(p_title: String = "", p_item_prefix: String = "", p_type: Resource = null): 50 | if p_type: 51 | if p_type is Script: 52 | item_script = p_type 53 | elif p_type is PackedScene: 54 | item_scene = p_type 55 | else: 56 | printerr("'p_type' in VBoxItemList.new() is not a Script or PackedScene") 57 | 58 | var main_toolbar := HBoxContainer.new() 59 | main_toolbar.name = "Toolbar" 60 | add_child(main_toolbar) 61 | 62 | label = Label.new() 63 | label.name = "Title" 64 | label.text = p_title 65 | main_toolbar.add_child(label) 66 | 67 | add_button = ToolButton.new() 68 | add_button.icon = ICON_ADD 69 | add_button.name = "AddButton" 70 | #warning-ignore:return_value_discarded 71 | add_button.connect("pressed", self, "append_item") 72 | main_toolbar.add_child(add_button) 73 | 74 | content = VBoxContainer.new() 75 | content.name = "Content" 76 | add_child(content) 77 | 78 | item_prefix = p_item_prefix 79 | 80 | 81 | func _process(_delta: float): 82 | if not get_global_rect().has_point(get_global_mouse_position()): 83 | for a_child in content.get_children(): 84 | (a_child.get_node("ItemEdit") as LineEdit).hide() 85 | (a_child.get_node("ItemLabel") as Label).show() 86 | 87 | 88 | #warning-ignore:unused_argument 89 | #warning-ignore:unused_argument 90 | func _item_inserted(p_index: int, p_control: Control): 91 | pass 92 | 93 | 94 | #warning-ignore:unused_argument 95 | #warning-ignore:unused_argument 96 | func _item_removed(p_index: int, p_control: Control): 97 | pass 98 | 99 | 100 | func insert_item(p_index: int) -> Control: 101 | var node: Control = _get_node_from_type() 102 | if not node: 103 | return null 104 | 105 | node.name = "Item" 106 | 107 | var hbox := HBoxContainer.new() 108 | #warning-ignore:return_value_discarded 109 | hbox.connect("gui_input", self, "_on_hbox_gui_input", [hbox]) 110 | 111 | var rect := TextureRect.new() 112 | rect.texture = ICON_SLIDE 113 | #warning-ignore:return_value_discarded 114 | rect.connect("gui_input", self, "_on_slide_gui_input", [rect]) 115 | rect.name = "ItemSlide" 116 | rect.set_visible(allow_reordering) 117 | hbox.add_child(rect) 118 | 119 | var item_label := Label.new() 120 | item_label.name = "ItemLabel" 121 | hbox.add_child(item_label) 122 | 123 | var item_edit := LineEdit.new() 124 | item_edit.name = "ItemEdit" 125 | item_edit.hide() 126 | #warning-ignore:return_value_discarded 127 | item_edit.connect("text_entered", self, "_on_edit_text_entered", [item_edit, item_label]) 128 | hbox.add_child(item_edit) 129 | 130 | hbox.add_child(node) 131 | 132 | var del_btn := ToolButton.new() 133 | del_btn.icon = ICON_DELETE 134 | del_btn.name = "DeleteButton" 135 | if not deletable_items: 136 | del_btn.visible = false 137 | hbox.add_child(del_btn) 138 | 139 | content.add_child(hbox) 140 | if p_index >= 0: 141 | content.move_child(node, p_index) 142 | else: 143 | p_index = len(content.get_children())-1 144 | 145 | _reset_prefix_on_label(item_label, p_index) 146 | #warning-ignore:return_value_discarded 147 | del_btn.connect("pressed", self, "_on_remove_item", [del_btn]) 148 | _item_inserted(p_index, node) 149 | 150 | emit_signal("item_inserted", p_index, node) 151 | 152 | _insertions += 1 153 | 154 | return node 155 | 156 | 157 | func get_item(p_index: int) -> Control: 158 | if p_index < 0 or p_index >= len(content.get_children()): 159 | return null 160 | return content.get_child(p_index).get_node("Item") as Control 161 | 162 | 163 | func append_item(): 164 | return insert_item(-1) 165 | 166 | 167 | func remove_item(p_idx: int): 168 | var node := content.get_child(p_idx) as HBoxContainer 169 | content.remove_child(node) 170 | if not (allow_reordering or editable_labels): 171 | _reset_prefixes() 172 | _item_removed(p_idx, node) 173 | emit_signal("item_removed", p_idx, node) 174 | if is_instance_valid(node): 175 | node.free() 176 | _removals += 1 177 | 178 | 179 | func _on_remove_item(p_del_btn: ToolButton): 180 | remove_item(p_del_btn.get_parent().get_index()) 181 | 182 | 183 | func _on_slide_gui_input(p_event: InputEvent, p_rect: TextureRect): 184 | if p_event is InputEventMouseButton: 185 | var mb := p_event as InputEventMouseButton 186 | if not mb.is_echo() and mb.button_index == BUTTON_LEFT and mb.pressed: 187 | _dragged_item = p_rect.get_parent() as HBoxContainer 188 | 189 | 190 | func _on_hbox_gui_input(p_event: InputEvent, p_hbox: HBoxContainer): 191 | if p_event is InputEventMouseButton: 192 | var mb := p_event as InputEventMouseButton 193 | if not mb.is_echo() and mb.button_index == BUTTON_LEFT and not mb.pressed and _dragged_item: 194 | _dragged_item = null 195 | print(p_hbox, ": stopped dragging") 196 | if mb.doubleclick and editable_labels: 197 | var edit := _hovered_item.get_node("ItemEdit") as LineEdit 198 | var label := _hovered_item.get_node("ItemLabel") as Label 199 | edit.text = label.text 200 | edit.show() 201 | label.hide() 202 | 203 | if p_event is InputEventMouseMotion: 204 | var mm := p_event as InputEventMouseMotion 205 | 206 | if _hovered_item and is_instance_valid(_hovered_item): 207 | (_hovered_item.get_node("ItemLabel") as Label).modulate = Color(1, 1, 1, 1) 208 | _hovered_item = p_hbox 209 | (_hovered_item.get_node("ItemLabel") as Label).modulate = label_tint 210 | 211 | if _dragged_item: 212 | var prev_idx = max(p_hbox.get_index() - 1, 0) 213 | var next_idx = min(p_hbox.get_index() + 1, p_hbox.get_parent().get_child_count() - 1) 214 | var previous = p_hbox.get_parent().get_child(prev_idx) 215 | var next = p_hbox.get_parent().get_child(next_idx) 216 | var moved := false 217 | 218 | if previous.get_global_rect().has_point(mm.global_position): 219 | content.move_child(_dragged_item, prev_idx) 220 | moved = true 221 | elif next.get_global_rect().has_point(mm.global_position): 222 | content.move_child(_dragged_item, next_idx) 223 | moved = true 224 | 225 | if moved: 226 | var del_btn := _dragged_item.get_node("DeleteButton") as ToolButton 227 | if del_btn.is_connected("pressed", self, "remove_item"): 228 | del_btn.disconnect("pressed", self, "remove_item") 229 | #warning-ignore:return_value_discarded 230 | del_btn.connect("pressed", self, "remove_item", [prev_idx]) 231 | 232 | 233 | func _on_edit_text_entered(p_text: String, p_edit: LineEdit, p_label: Label): 234 | p_label.text = p_text 235 | p_label.show() 236 | p_edit.hide() 237 | 238 | 239 | func _get_node_from_type() -> Control: 240 | if item_script: 241 | return item_script.new() as Control 242 | elif item_scene: 243 | return item_scene.instance() as Control 244 | else: 245 | return null 246 | 247 | 248 | func _reset_prefixes(): 249 | var index: int = 0 250 | for hbox in content.get_children(): 251 | var a_label: Label = null 252 | if (hbox as HBoxContainer).has_node("ItemLabel"): 253 | a_label = (hbox as HBoxContainer).get_node("ItemLabel") as Label 254 | _reset_prefix_on_label(a_label, index) 255 | index += 1 256 | 257 | 258 | func _reset_prefix_on_label(p_label: Label, p_index: int = -1): 259 | if not p_label: 260 | return 261 | if content.get_child(p_index).get_node("Item").has_method("_get_label"): 262 | var item := content.get_child(p_index).get_node("Item") as Node 263 | p_label.text = item._get_label() as String 264 | p_label.show() 265 | elif item_prefix: 266 | var idx = p_index 267 | if index_by_size: 268 | if p_index < 0: 269 | idx = len(content.get_children()) - 1 270 | else: 271 | idx = _insertions 272 | 273 | p_label.text = "%s %d" % [item_prefix, idx] 274 | p_label.show() 275 | else: 276 | p_label.hide() 277 | 278 | 279 | func _validate_item_type(p_res: Resource) -> bool: 280 | if not p_res: 281 | return true 282 | var node: Node = null 283 | if p_res is Script: 284 | node = p_res.new() as Control 285 | elif p_res is PackedScene: 286 | node = p_res.instance() as Control 287 | else: 288 | printerr("Item Resource is unassigned.") 289 | return false 290 | 291 | if not node: 292 | printerr("An error occurred in creating a node from the Item Resource.") 293 | return false 294 | elif not node is Control: 295 | printerr("Item Resource does not create a Control.") 296 | return false 297 | 298 | if node is Node: 299 | node.queue_free() 300 | 301 | return true 302 | 303 | 304 | func set_title(p_value: String): 305 | title = p_value 306 | label.text = title 307 | 308 | 309 | func set_item_prefix(p_value: String): 310 | item_prefix = p_value 311 | if not (allow_reordering or editable_labels): 312 | _reset_prefixes() 313 | 314 | 315 | func set_item_script(p_value: Script): 316 | if _validate_item_type(p_value): 317 | item_script = p_value 318 | 319 | 320 | func set_item_scene(p_value: PackedScene): 321 | if _validate_item_type(p_value): 322 | item_scene = p_value 323 | 324 | 325 | func set_allow_reordering(p_value: bool): 326 | allow_reordering = p_value 327 | if _hovered_item: 328 | (_hovered_item.get_node("ItemSlide") as TextureRect).set_visible(p_value) 329 | 330 | 331 | func set_label_tint(p_value: Color): 332 | label_tint = p_value 333 | if _hovered_item: 334 | (_hovered_item.get_node("ItemLabel") as Label).modulate = label_tint 335 | 336 | 337 | func set_deletable_items(p_value: bool): 338 | deletable_items = p_value 339 | for a_hbox in content.get_children(): 340 | var del_btn := (a_hbox as HBoxContainer).get_node("DeleteButton") as ToolButton 341 | del_btn.visible = p_value 342 | -------------------------------------------------------------------------------- /addons/godot-next/icons/icon_add.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /addons/godot-next/icons/icon_add.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/icon_add.svg-d3e2a1bf01ab1646fd533c0ae739c6af.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://addons/godot-next/icons/icon_add.svg" 13 | dest_files=[ "res://.import/icon_add.svg-d3e2a1bf01ab1646fd533c0ae739c6af.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | stream=false 32 | size_limit=0 33 | detect_3d=true 34 | svg/scale=1.0 35 | -------------------------------------------------------------------------------- /addons/godot-next/icons/icon_cycle.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 40 | 42 | 43 | 45 | image/svg+xml 46 | 48 | 49 | 50 | 51 | 52 | 57 | 65 | 71 | 79 | 85 | 93 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /addons/godot-next/icons/icon_cycle.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/icon_cycle.svg-704a1bd2b44d109f39c24978002f0840.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://addons/godot-next/icons/icon_cycle.svg" 13 | dest_files=[ "res://.import/icon_cycle.svg-704a1bd2b44d109f39c24978002f0840.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | stream=false 32 | size_limit=0 33 | detect_3d=true 34 | svg/scale=1.0 35 | -------------------------------------------------------------------------------- /addons/godot-next/icons/icon_geometry_2d.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /addons/godot-next/icons/icon_geometry_2d.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/icon_geometry_2d.svg-3e8b75bb6a38693255a76162660a3237.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://addons/godot-next/icons/icon_geometry_2d.svg" 13 | dest_files=[ "res://.import/icon_geometry_2d.svg-3e8b75bb6a38693255a76162660a3237.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | stream=false 32 | size_limit=0 33 | detect_3d=true 34 | svg/scale=1.0 35 | -------------------------------------------------------------------------------- /addons/godot-next/icons/icon_import_fail.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /addons/godot-next/icons/icon_import_fail.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/icon_import_fail.svg-7ad0927b5823c3399ba221a5daf198eb.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://addons/godot-next/icons/icon_import_fail.svg" 13 | dest_files=[ "res://.import/icon_import_fail.svg-7ad0927b5823c3399ba221a5daf198eb.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | stream=false 32 | size_limit=0 33 | detect_3d=true 34 | svg/scale=1.0 35 | -------------------------------------------------------------------------------- /addons/godot-next/icons/icon_mirror_y.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /addons/godot-next/icons/icon_mirror_y.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/icon_mirror_y.svg-6c3f2164b83aecbf9b78540511f7cf06.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://addons/godot-next/icons/icon_mirror_y.svg" 13 | dest_files=[ "res://.import/icon_mirror_y.svg-6c3f2164b83aecbf9b78540511f7cf06.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | stream=false 32 | size_limit=0 33 | detect_3d=true 34 | svg/scale=1.0 35 | -------------------------------------------------------------------------------- /addons/godot-next/icons/icon_trail_2d.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 40 | 42 | 43 | 45 | image/svg+xml 46 | 48 | 49 | 50 | 51 | 52 | 57 | 64 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /addons/godot-next/icons/icon_trail_2d.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/icon_trail_2d.svg-51013538d456b189cea8a9180694fa86.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://addons/godot-next/icons/icon_trail_2d.svg" 13 | dest_files=[ "res://.import/icon_trail_2d.svg-51013538d456b189cea8a9180694fa86.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | stream=false 32 | size_limit=0 33 | detect_3d=true 34 | svg/scale=1.0 35 | -------------------------------------------------------------------------------- /addons/godot-next/icons/icon_trail_3d.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 40 | 42 | 43 | 45 | image/svg+xml 46 | 48 | 49 | 50 | 51 | 52 | 57 | 64 | 70 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /addons/godot-next/icons/icon_trail_3d.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/icon_trail_3d.svg-6391c60a03eaeff4d2799492f411ae5b.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://addons/godot-next/icons/icon_trail_3d.svg" 13 | dest_files=[ "res://.import/icon_trail_3d.svg-6391c60a03eaeff4d2799492f411ae5b.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | stream=false 32 | size_limit=0 33 | detect_3d=true 34 | svg/scale=1.0 35 | -------------------------------------------------------------------------------- /addons/godot-next/icons/icon_v_box_item_list.svg: -------------------------------------------------------------------------------- 1 | 2 | 17 | 19 | 20 | 22 | image/svg+xml 23 | 25 | 26 | 27 | 28 | 29 | 31 | 51 | 58 | 64 | 71 | 77 | 84 | 90 | 91 | -------------------------------------------------------------------------------- /addons/godot-next/icons/icon_v_box_item_list.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/icon_v_box_item_list.svg-883dcb141d0b41e3fd00f437f70206b0.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://addons/godot-next/icons/icon_v_box_item_list.svg" 13 | dest_files=[ "res://.import/icon_v_box_item_list.svg-883dcb141d0b41e3fd00f437f70206b0.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | stream=false 32 | size_limit=0 33 | detect_3d=true 34 | svg/scale=1.0 35 | -------------------------------------------------------------------------------- /addons/godot-next/inspector_plugins/delegation_inspector_plugin.gd: -------------------------------------------------------------------------------- 1 | tool 2 | extends EditorInspectorPlugin 3 | # author: willnationsdev and xdgamestudios 4 | # license: MIT 5 | # description: 6 | # Attempts to call EditorInspectorPlugin 'parse' methods on every 7 | # object that appears in an EditorInspector anywhere. Enables 8 | # objects to define their own EditorInspector GUI logic without 9 | # the need for additional plugins. 10 | 11 | var obj_stack: Array 12 | 13 | #warning-ignore:unused_argument 14 | func can_handle(p_object: Object) -> bool: 15 | return true 16 | 17 | 18 | func parse_begin(p_object: Object) -> void: 19 | obj_stack.push_back(p_object) 20 | if p_object.has_method("_parse_begin"): 21 | p_object._parse_begin(self) 22 | 23 | 24 | func parse_category(p_object: Object, p_category: String) -> void: 25 | if p_object.has_method("_parse_category"): 26 | p_object._parse_category(self, p_category) 27 | 28 | 29 | func parse_property(p_object: Object, p_type: int, p_path: String, p_hint: int, p_hint_text: String, p_usage: int) -> bool: 30 | if p_object and p_object.has_method("_parse_property"): 31 | var pinfo = PropertyInfo.new(p_path, p_type, p_hint, p_hint_text, p_usage) 32 | return p_object._parse_property(self, pinfo) 33 | return false 34 | 35 | 36 | func parse_end() -> void: 37 | var obj = obj_stack.pop_back() 38 | if obj.has_method("_parse_end"): 39 | obj._parse_end(self) 40 | -------------------------------------------------------------------------------- /addons/godot-next/nodes/callback_delegator.gd: -------------------------------------------------------------------------------- 1 | tool 2 | class_name CallbackDelegator 3 | extends Node 4 | # author: xdgamestudios 5 | # license: MIT 6 | # description: 7 | # Manages a ResourceSet of resources and delegates Node callbacks to each instance. 8 | # As a ResourceSet, only one element of any given type is allowed on a single elements node. 9 | # deps: 10 | # - ResourceSet 11 | # - PropertyInfo 12 | # - ClassType 13 | # usage: 14 | # - Creating: 15 | # elements = elements.new() 16 | # - Adding elements: 17 | # elements.add_element(MyResource) # Returns a new or pre-existing instance of the element or null if given an invalid element script. 18 | # - Checking elements: 19 | # elements.has_element(MyResource) # Returns true if the element exists in the collection. 20 | # - Retrieving elements: 21 | # elements.get_element(MyResource) # Returns the element instance of the given type or null if not in the collection. 22 | # - Removing elements: 23 | # elements.remove_element(MyResource) # Removes the element from the collection. Returns true if successful. Else, returns false. 24 | # notes: 25 | # - Public interface of each stored Resource type: 26 | # - var owner: Node 27 | # - func get_enabled() -> bool 28 | # - Initialization Sequence: 29 | # 1. _awake() called during _enter_tree() after CallbackDelegator initializes owner (for Unity familiarity). 30 | # 2. _enter_tree() called immediately after (so they are virtually aliases for each other) 31 | # 3. _ready() called during _ready(). 32 | 33 | # The collection of Resources. Only one Resource of each type is allowed. 34 | var _elements: ResourceSet = ResourceSet.new() 35 | 36 | # The set of resource instances that have successfully registered themselves to each callback. 37 | var _callbacks: Dictionary = { 38 | "_enter_tree" : {}, 39 | "_exit_tree" : {}, 40 | "_ready" : {}, 41 | "_process" : {}, 42 | "_physics_process" : {}, 43 | "_input" : {}, 44 | "_unhandled_input" : {}, 45 | "_unhandled_key_input" : {} 46 | } 47 | 48 | # Assists with inheritance checks and name identification of script classes. 49 | var _class_type: ClassType = ClassType.new() 50 | 51 | func _ready() -> void: 52 | _handle_notification("_ready") 53 | 54 | 55 | # Initialize every element and de-activate any non-essential CallbackDelegator notifications. 56 | func _enter_tree() -> void: 57 | var elements = _elements.get_data().values() 58 | for an_element in elements: 59 | if not an_element.owner: 60 | _initialize_element(an_element) 61 | _check_for_empty_callbacks() 62 | 63 | _handle_notification("_enter_tree") 64 | 65 | 66 | func _exit_tree() -> void: 67 | _handle_notification("_exit_tree") 68 | 69 | 70 | func _process(delta: float) -> void: 71 | _handle_notification("_process", delta) 72 | 73 | 74 | func _physics_process(delta: float) -> void: 75 | _handle_notification("_physics_process", delta) 76 | 77 | 78 | func _input(event: InputEvent) -> void: 79 | _handle_notification("_input", event) 80 | 81 | 82 | func _unhandled_input(event: InputEvent) -> void: 83 | _handle_notification("_unhandled_input", event) 84 | 85 | 86 | func _unhandled_key_input(event: InputEventKey) -> void: 87 | _handle_notification("_unhandled_key_input", event) 88 | 89 | 90 | # Add an element to the CallbackDelegator. Does nothing if no 91 | # base_type is assigned. See `set_base_type(...)`. 92 | func add_element(p_type: Script) -> Resource: 93 | var elements = _elements.get_data() 94 | 95 | _class_type.res = p_type 96 | if not _class_type.is_type(_elements.get_base_type()): 97 | return null 98 | if has_element(p_type): 99 | return get_element(p_type) 100 | 101 | var element: Resource = p_type.new() 102 | 103 | elements[_class_type.get_script_class()] = element 104 | _initialize_element(element) 105 | 106 | return element 107 | 108 | 109 | # Return the element with the same type as p_type. 110 | func get_element(p_type: Script) -> Resource: 111 | var elements = _elements.get_data() 112 | _class_type.res = p_type 113 | return elements.get(_class_type.get_script_class(), null) 114 | 115 | 116 | # Returns true if the element exists in the internal collection. 117 | func has_element(p_type: Script) -> bool: 118 | var elements = _elements.get_data() 119 | _class_type.res = p_type 120 | return elements.has(_class_type.get_script_class()) 121 | 122 | 123 | # Returns true if successfully able to remove the element 124 | # from the internal collection. Else, returns false. 125 | func remove_element(p_type: Script) -> bool: 126 | var elements = _elements.get_data() 127 | var element = get_element(p_type) 128 | if element: 129 | _remove_from_callbacks(element) 130 | _class_type.res = p_type 131 | return elements.erase(_class_type.get_script_class()) 132 | return false 133 | 134 | 135 | # The order of returned Scripts is not deterministic. 136 | func get_element_types() -> Array: 137 | return _elements.get_data().keys() 138 | 139 | 140 | # The order of returned Resources is not deterministic. 141 | func get_elements() -> Array: 142 | return _elements.get_data().values() 143 | 144 | 145 | func _parse_property(p_inspector: EditorInspectorPlugin, p_pinfo: PropertyInfo) -> void: 146 | match p_pinfo.name: 147 | "_elements": 148 | p_inspector.add_custom_control(InspectorControls.new_button("Initialize Default Behavior", false, self, "_set_base_type_behavior")) 149 | 150 | 151 | func _get_property_list() -> Array: 152 | return [ PropertyInfoFactory.new_resource("_elements").to_dict() ] 153 | 154 | 155 | # Helper method to facilitate delegation of the callback. 156 | func _handle_notification(p_name: String, p_param = null) -> void: 157 | if Engine.editor_hint: 158 | return 159 | if p_param: 160 | for an_element in _callbacks[p_name]: 161 | an_element.call(p_name, p_param) 162 | else: 163 | for an_element in _callbacks[p_name]: 164 | an_element.call(p_name) 165 | 166 | 167 | # Setup the owner and initialization of the element. Ensure it updates its callbacks if the script is modified. 168 | func _initialize_element(p_element: Resource) -> void: 169 | _awake(p_element) 170 | #warning-ignore:return_value_discarded 171 | p_element.connect("script_changed", self, "_refresh_callbacks", [p_element]) 172 | _add_to_callbacks(p_element) 173 | 174 | 175 | # Register necessary callbacks for the element. 176 | func _add_to_callbacks(p_element: Resource) -> void: 177 | for a_callback in _callbacks: 178 | if p_element.has_method(a_callback) and p_element.get_enabled(): 179 | _callbacks[a_callback][p_element] = null 180 | 181 | 182 | # Unregister all callbacks for the element. 183 | func _remove_from_callbacks(p_element: Resource) -> void: 184 | for a_callback in _callbacks: 185 | _callbacks[a_callback].erase(p_element) 186 | _check_for_empty_callbacks() 187 | 188 | 189 | # Only delegate the call if a callback-implementing, enabled resource relies on it. 190 | func _check_for_empty_callbacks() -> void: 191 | for a_callback in _callbacks: 192 | match a_callback: 193 | "_process": 194 | set_process(not _callbacks[a_callback].empty()) 195 | "_physics_process": 196 | set_physics_process(not _callbacks[a_callback].empty()) 197 | "_input": 198 | set_process_input(not _callbacks[a_callback].empty()) 199 | "_unhandled_input": 200 | set_process_unhandled_input(not _callbacks[a_callback].empty()) 201 | "_unhandled_key_input": 202 | set_process_unhandled_key_input(not _callbacks[a_callback].empty()) 203 | 204 | 205 | # Sets up the owner instance on the Behavior. 206 | func _awake(p_element: Resource) -> void: 207 | p_element.owner = self 208 | if p_element.has_method("_awake"): 209 | p_element._awake() 210 | 211 | 212 | # Reset callback registrations in the event that the script is modified. 213 | func _on_element_script_change(p_element: Resource) -> void: 214 | _remove_from_callbacks(p_element) 215 | _add_to_callbacks(p_element) 216 | 217 | 218 | func _set_base_type_behavior() -> void: 219 | _class_type.name = "Behavior" 220 | _elements.set_base_type(_class_type.res) 221 | -------------------------------------------------------------------------------- /addons/godot-next/nodes/message_dispatcher_wrapper.gd: -------------------------------------------------------------------------------- 1 | class_name MessageDispatcherWrapper 2 | extends Node 3 | # author: MunWolf (Rikhardur Bjarni Einarsson) 4 | # license: MIT 5 | # copyright: Copyright (c) 2019 Rikhardur Bjarni Einarsson 6 | # description: 7 | # A wrapper for MessageDispatcher that is extendable on a Node, 8 | # people can also use this as a template to implement it on 9 | # their own if they want to extend something else. 10 | 11 | var _message_dispatcher = MessageDispatcher.new() 12 | 13 | # See same function on MessageDispatcher. 14 | func connect_message(message_type: String, obj: Object, function: String) -> void: 15 | _message_dispatcher.connect_message(message_type, obj, function) 16 | 17 | 18 | # See same function on MessageDispatcher. 19 | func disconnect_message(message_type: String, obj: Object, function: String) -> void: 20 | _message_dispatcher.disconnect_message(message_type, obj, function) 21 | 22 | 23 | # See same function on MessageDispatcher. 24 | func disconnect_all_message() -> void: 25 | _message_dispatcher.disconnect_all_message() 26 | 27 | 28 | # See same function on MessageDispatcher. 29 | func emit_message(message_type: String, message_data: Dictionary) -> bool: 30 | return _message_dispatcher.emit_message(message_type, message_data) 31 | -------------------------------------------------------------------------------- /addons/godot-next/plugin.cfg: -------------------------------------------------------------------------------- 1 | [plugin] 2 | 3 | name="Godot Node Extensions" 4 | description="A plugin filled with basic type extensions and utilities for Godot." 5 | author="Will Nations" 6 | version="1.0" 7 | script="godot_next_plugin.gd" 8 | -------------------------------------------------------------------------------- /addons/godot-next/references/array_2d.gd: -------------------------------------------------------------------------------- 1 | class_name Array2D 2 | extends Reference 3 | # author: willnationsdev 4 | # description: A 2D Array class 5 | 6 | var data: Array = [] 7 | 8 | 9 | func _init(p_array: Array = [], p_deep_copy : bool = true): 10 | if p_deep_copy: 11 | for row in p_array: 12 | if row is Array: 13 | data.append(row.duplicate()) 14 | else: 15 | data = p_array 16 | 17 | 18 | func get_data() -> Array: 19 | return data 20 | 21 | 22 | func has_cell(p_row: int, p_col: int) -> bool: 23 | return len(data) > p_row and len(data[p_row]) > p_col 24 | 25 | 26 | func set_cell(p_row: int, p_col: int, p_value): 27 | assert(has_cell(p_row, p_col)) 28 | data[p_row][p_col] = p_value 29 | 30 | 31 | func get_cell(p_row: int, p_col: int): 32 | assert(has_cell(p_row, p_col)) 33 | return data[p_row][p_col] 34 | 35 | 36 | func set_cell_if_exists(p_row: int, p_col: int, p_value) -> bool: 37 | if has_cell(p_row, p_col): 38 | set_cell(p_row, p_col, p_value) 39 | return true 40 | return false 41 | 42 | 43 | func has_cellv(p_pos: Vector2) -> bool: 44 | return len(data) > p_pos.x and len(data[p_pos.x]) > p_pos.y 45 | 46 | 47 | func set_cellv(p_pos: Vector2, p_value): 48 | assert(has_cellv(p_pos)) 49 | data[p_pos.x][p_pos.y] = p_value 50 | 51 | 52 | func get_cellv(p_pos: Vector2): 53 | assert(has_cellv(p_pos)) 54 | return data[p_pos.x][p_pos.y] 55 | 56 | 57 | func set_cellv_if_exists(p_pos: Vector2, p_value) -> bool: 58 | if has_cellv(p_pos): 59 | set_cellv(p_pos, p_value) 60 | return true 61 | return false 62 | 63 | 64 | func get_row(p_idx: int): 65 | assert(len(data) > p_idx) 66 | assert(p_idx >= 0) 67 | return data[p_idx].duplicate() 68 | 69 | 70 | func get_col(p_idx: int): 71 | var result = [] 72 | for a_row in data: 73 | assert(len(a_row) > p_idx) 74 | assert(p_idx >= 0) 75 | result.push_back(a_row[p_idx]) 76 | return result 77 | 78 | 79 | func get_row_ref(p_idx: int): 80 | assert(len(data) > p_idx) 81 | assert(p_idx >= 0) 82 | return data[p_idx] 83 | 84 | 85 | func get_rows() -> Array: 86 | return data 87 | 88 | 89 | func set_row(p_idx: int, p_row): 90 | assert(len(data) > p_idx) 91 | assert(p_idx >= 0) 92 | assert(len(data) == len(p_row)) 93 | data[p_idx] = p_row 94 | 95 | 96 | func set_col(p_idx: int, p_col): 97 | assert(len(data) > 0 and len(data[0]) > 0) 98 | assert(len(data) == len(p_col)) 99 | var idx = 0 100 | for a_row in data: 101 | assert(len(a_row) > p_idx) 102 | assert(p_idx >= 0) 103 | a_row[p_idx] = p_col[idx] 104 | idx += 1 105 | 106 | 107 | func insert_row(p_idx: int, p_array: Array): 108 | if p_idx < 0: 109 | data.append(p_array) 110 | else: 111 | data.insert(p_idx, p_array) 112 | 113 | 114 | func insert_col(p_idx: int, p_array: Array): 115 | var idx = 0 116 | for a_row in data: 117 | if p_idx < 0: 118 | a_row.append(p_array[idx]) 119 | else: 120 | a_row.insert(p_idx, p_array[idx]) 121 | idx += 1 122 | 123 | 124 | func append_row(p_array: Array): 125 | insert_row(-1, p_array) 126 | 127 | 128 | func append_col(p_array: Array): 129 | insert_col(-1, p_array) 130 | 131 | 132 | func sort_row(p_idx: int): 133 | _sort_axis(p_idx, true) 134 | 135 | 136 | func sort_col(p_idx: int): 137 | _sort_axis(p_idx, false) 138 | 139 | 140 | func sort_row_custom(p_idx: int, p_obj: Object, p_func: String): 141 | _sort_axis_custom(p_idx, true, p_obj, p_func) 142 | 143 | 144 | func sort_col_custom(p_idx: int, p_obj: Object, p_func: String): 145 | _sort_axis_custom(p_idx, false, p_obj, p_func) 146 | 147 | 148 | func duplicate() -> Reference: 149 | return load(get_script().resource_path).new(data) 150 | 151 | 152 | func hash() -> int: 153 | return hash(self) 154 | 155 | 156 | func shuffle(): 157 | for a_row in data: 158 | a_row.shuffle() 159 | 160 | 161 | func empty() -> bool: 162 | return len(data) == 0 163 | 164 | 165 | func size() -> int: 166 | if len(data) <= 0: 167 | return 0 168 | return len(data) * len(data[0]) 169 | 170 | 171 | func resize(p_height: int, p_width: int): 172 | data.resize(p_height) 173 | for i in range(len(data)): 174 | data[i] = [] 175 | data[i].resize(p_width) 176 | 177 | 178 | func resizev(p_dimensions: Vector2): 179 | resize(int(p_dimensions.x), int(p_dimensions.y)) 180 | 181 | 182 | func clear(): 183 | data = [] 184 | 185 | 186 | func fill(p_value): 187 | for a_row in range(data.size()): 188 | for a_col in range(data[a_row].size()): 189 | data[a_row][a_col] = p_value 190 | 191 | 192 | func fill_row(p_idx: int, p_value): 193 | assert(p_idx >= 0) 194 | assert(len(data) > p_idx) 195 | assert(len(data[0]) > 0) 196 | var arr = [] 197 | for i in len(data[0]): 198 | arr.push_back(p_value) 199 | data[p_idx] = arr 200 | 201 | 202 | func fill_col(p_idx: int, p_value): 203 | assert(p_idx >= 0) 204 | assert(len(data) > 0) 205 | assert(len(data[0]) > p_idx) 206 | var arr = [] 207 | for i in len(data): 208 | arr.push_back(p_value) 209 | set_col(p_idx, arr) 210 | 211 | 212 | func remove_row(p_idx: int): 213 | assert(p_idx >= 0) 214 | assert(len(data) > p_idx) 215 | data.remove(p_idx) 216 | 217 | 218 | func remove_col(p_idx: int): 219 | assert(len(data) > 0) 220 | assert(p_idx >= 0 and len(data[0]) > p_idx) 221 | for a_row in data: 222 | a_row.remove(p_idx) 223 | 224 | 225 | func count(p_value) -> int: 226 | var count = 0 227 | for a_row in data: 228 | for a_col in a_row: 229 | if p_value == data[a_row][a_col]: 230 | count += 1 231 | return count 232 | 233 | 234 | func has(p_value) -> bool: 235 | for a_row in data: 236 | for a_col in a_row: 237 | if p_value == data[a_row][a_col]: 238 | return true 239 | return false 240 | 241 | 242 | func invert() -> Reference: 243 | data.invert() 244 | return self 245 | 246 | 247 | func invert_row(p_idx: int) -> Reference: 248 | assert(p_idx >= 0 and len(data) > p_idx) 249 | data[p_idx].invert() 250 | return self 251 | 252 | 253 | func invert_col(p_idx: int) -> Reference: 254 | assert(len(data) > 0) 255 | assert(p_idx >= 0 and len(data[0]) > p_idx) 256 | var col = get_col(p_idx) 257 | col.invert() 258 | set_col(p_idx, col) 259 | return self 260 | 261 | 262 | func bsearch_row(p_idx: int, p_value, p_before: bool) -> int: 263 | assert(p_idx >= 0 and len(data) > p_idx) 264 | return data[p_idx].bsearch(p_value, p_before) 265 | 266 | 267 | func bsearch_col(p_idx: int, p_value, p_before: bool) -> int: 268 | assert(len(data) > 0) 269 | assert(p_idx >= 0 and len(data[0]) > p_idx) 270 | var col = get_col(p_idx) 271 | col.sort() 272 | return col[p_idx].bsearch(p_value, p_before) 273 | 274 | 275 | func find(p_value) -> Vector2: 276 | for a_row in data: 277 | for a_col in a_row: 278 | if p_value == data[a_row][a_col]: 279 | return Vector2(a_row, a_col) 280 | return Vector2(-1, -1) 281 | 282 | 283 | func rfind(p_value) -> Vector2: 284 | var i: int = len(data) - 1 285 | var j: int = len(data[0]) - 1 286 | while i: 287 | while j: 288 | if p_value == data[i][j]: 289 | return Vector2(i, j) 290 | j -= 1 291 | i -= 1 292 | return Vector2(-1, -1) 293 | 294 | 295 | func transpose() -> Reference: 296 | var width : int = len(data) 297 | var height : int = len(data[0]) 298 | var transposed_matrix : Array 299 | for i in range(height): 300 | transposed_matrix.append([]) 301 | var h : int = 0 302 | while h < height: 303 | for w in range(width): 304 | transposed_matrix[h].append(data[w][h]) 305 | h += 1 306 | return load(get_script().resource_path).new(transposed_matrix, false) 307 | 308 | 309 | func _to_string() -> String: 310 | var ret: String 311 | var width: int = len(data) 312 | var height: int = len(data[0]) 313 | for h in range(height): 314 | for w in range(width): 315 | ret += "[" + str(data[w][h]) + "]" 316 | if w == width - 1 and h != height -1: 317 | ret += "\n" 318 | else: 319 | if w == width - 1: 320 | ret += "\n" 321 | else: 322 | ret += ", " 323 | return ret 324 | 325 | 326 | func _sort_axis(p_idx: int, p_is_row: bool): 327 | if p_is_row: 328 | data[p_idx].sort() 329 | return 330 | var col = get_col(p_idx) 331 | col.sort() 332 | set_col(p_idx, col) 333 | 334 | 335 | func _sort_axis_custom(p_idx: int, p_is_row: bool, p_obj: Object, p_func: String): 336 | if p_is_row: 337 | data[p_idx].sort_custom(p_obj, p_func) 338 | return 339 | var col = get_col(p_idx) 340 | col.sort_custom(p_obj, p_func) 341 | set_col(p_idx, col) 342 | -------------------------------------------------------------------------------- /addons/godot-next/references/bit_flag.gd: -------------------------------------------------------------------------------- 1 | tool 2 | class_name BitFlag 3 | extends Reference 4 | # author: xdgamestudios 5 | # license: MIT 6 | # description: A class that allows abstracts away the complexity of handling bit flag enum types. 7 | # todo: Implement additional features 8 | # usage: 9 | # - Initial setup: 10 | # enum my_enum { a, b, c } 11 | # 12 | # - Creating: 13 | # bf = BitFlag.new(my_enum, to_flag = false) # Creates a new BitFlag given an enum. If 'to_flag' converts to flag enum 14 | # - Flag access: 15 | # bf. = true/false # Allows to set the current bit 16 | # - Set/Put flag: 17 | # bf.put(bf.a) # Enables the flag 'a'. Another BitFlag can be provided. 18 | # - Clear flag: 19 | # bf.clear(bf.b) # Disables the flag 'b'. Another BitFlag can be provided. 20 | # - Toggle flag: 21 | # bf.toggle(bf.b) # Inverts the flag 'b'. Another BitFlag can be provided. 22 | # - Check flag: 23 | # bf.check(bf.b) # Checks flag 'b'. Another BitFlag can be provided. 24 | # - Get flag names: 25 | # bf.get_active_keys() # Returns an array of active keys. 26 | # bf.get_active_keys_raw() # Returns the integer represented by the flag, example: a=true,b=true,c=false will return 3 27 | # bf.get_keys() # Returns an array of all flag keys. 28 | # - Convert to PropertyInfo dict 29 | # bf.to_pinfo_dict("property_name") # Returns a dictionary with export structure. 30 | 31 | var _enum: Dictionary = {} 32 | var _flags: int = 0 setget set_flags, get_flags 33 | 34 | func _init(p_enum: Dictionary, p_to_flag: bool = false): 35 | if p_to_flag: 36 | for a_key in p_enum: 37 | _enum[a_key] = 1 << p_enum[a_key] 38 | else: 39 | _enum = p_enum 40 | 41 | 42 | func _get(p_property: String) -> int: 43 | if _enum.has(p_property): 44 | return _enum[p_property] 45 | return 0 46 | 47 | 48 | func _set(p_property: String, p_value: bool) -> bool: 49 | if _enum.has(p_property): 50 | if p_value: 51 | _flags |= _enum[p_property] 52 | else: 53 | _flags &= _enum[p_property] 54 | return true 55 | return false 56 | 57 | 58 | func _get_value_flags(p_value) -> int: 59 | match typeof(p_value): 60 | TYPE_OBJECT: 61 | if p_value.get_script() == get_script(): 62 | return p_value._flags 63 | TYPE_INT: 64 | return p_value 65 | assert(false) 66 | return -1 67 | 68 | 69 | func put(p_value) -> int: 70 | if p_value == null: 71 | return _flags 72 | _flags |= _get_value_flags(p_value) 73 | return _flags 74 | 75 | 76 | func clear(p_value) -> int: 77 | if p_value == null: 78 | return _flags 79 | _flags &= ~(_get_value_flags(p_value)) 80 | return _flags 81 | 82 | 83 | func toggle(p_value) -> int: 84 | if p_value == null: 85 | return _flags 86 | _flags ^= _get_value_flags(p_value) 87 | return _flags 88 | 89 | 90 | func check(p_value) -> bool: 91 | if p_value == null: 92 | return false 93 | var flags: int = _get_value_flags(p_value) 94 | return (_flags & flags) == flags 95 | 96 | 97 | func get_active_keys() -> Array: 98 | var out: Array = [] 99 | for a_flag in _enum: 100 | if check(_enum[a_flag]): 101 | out.append(a_flag) 102 | return out 103 | 104 | func get_active_keys_raw() -> int: 105 | var value = 0 106 | for a_flag in _enum: 107 | if check(_enum[a_flag]): 108 | value += _get(a_flag) 109 | return value 110 | 111 | func get_keys() -> Array: 112 | return _enum.keys() 113 | 114 | 115 | func to_pinfo_dict(p_name: String) -> Dictionary: 116 | var hint_string = PoolStringArray(get_keys()).join(",") 117 | return PropertyInfo.new(p_name, TYPE_INT, PROPERTY_HINT_FLAGS, hint_string).to_dict() 118 | 119 | 120 | func get_flags() -> int: 121 | return _flags 122 | 123 | 124 | func set_flags(p_value) -> void: 125 | if p_value == null: 126 | return 127 | _flags = _get_value_flags(p_value) 128 | -------------------------------------------------------------------------------- /addons/godot-next/references/bitset.gd: -------------------------------------------------------------------------------- 1 | class_name Bitset 2 | extends Reference 3 | # author: milesturin 4 | # license: MIT 5 | # description: A class that allows for easily manipulated bitmasks of any size 6 | # usage: 7 | # By setting enforce_soft_size to false, the Bitset will allow the user to access 8 | # bits that have been reserved by the script, but are outside of the requested size. 9 | 10 | const MASK_SIZE := 32 11 | 12 | var bitmasks: PoolIntArray = [] 13 | var bits: int 14 | 15 | func _init(size: int, default_state: bool = false, enforce_soft_size: bool = true) -> void: 16 | resize(size, default_state, enforce_soft_size) 17 | 18 | 19 | func resize(size: int, default_state: bool = false, enforce_soft_size: bool = true) -> void: 20 | assert(size >= 0) 21 | var old_masks := bitmasks.size() 22 | if old_masks > 0 and bits % MASK_SIZE: 23 | if default_state: 24 | bitmasks[old_masks - 1] |= (~0 << (bits % MASK_SIZE)) 25 | else: 26 | bitmasks[old_masks - 1] &= ~((~0) << (bits % MASK_SIZE)) 27 | bitmasks.resize(ceil(size / float(MASK_SIZE))) 28 | bits = size if enforce_soft_size else bitmasks.size() * MASK_SIZE 29 | for i in range(old_masks, bitmasks.size()): 30 | bitmasks[i] = ~0 if default_state else 0 31 | 32 | 33 | func check_bit(index: int) -> bool: 34 | assert(index < bits) 35 | return bitmasks[index / MASK_SIZE] & (1 << (index % MASK_SIZE)) != 0 36 | 37 | 38 | func set_bit(index: int, state: bool) -> void: 39 | assert(index < bits) 40 | if state: 41 | bitmasks[index / MASK_SIZE] |= (1 << (index % MASK_SIZE)) 42 | else: 43 | bitmasks[index / MASK_SIZE] &= ~(1 << (index % MASK_SIZE)) 44 | 45 | 46 | func flip_bit(index: int) -> void: 47 | assert(index < bits) 48 | set_bit(index, !check_bit(index)) 49 | 50 | 51 | func print_bits(multiline: bool = true) -> void: 52 | if multiline: 53 | for i in range(bits): 54 | print("bit " + String(i) + ": " + String(check_bit(i))) 55 | else: 56 | var output := "" 57 | for i in range(bits): 58 | output += '1' if check_bit(i) else '0' 59 | print(output) 60 | -------------------------------------------------------------------------------- /addons/godot-next/references/csv_file.gd: -------------------------------------------------------------------------------- 1 | tool 2 | class_name CSVFile 3 | extends Reference 4 | # author: willnationsdev 5 | # description: Provides utilities for loading, saving, and editing CSV files. 6 | # dependencies: Array2D 7 | # API details: 8 | # - The data is stored internally as an Array2D. The 0th row is the headers. 9 | # - Use 'get_array()' to fetch the Array2D 10 | # - Use 'get_headers()' to get a Dictionary of the headers (values are their index in the first row) 11 | # - Use 'get_map()' to get a Dictionary of string keys to rows. 12 | # - if '_uses_map' is true, the key will be generated from the '_get_key()' virtual method (defaults to returning row[0]). 13 | # Else _map will be empty. Defaults to false. 14 | # - The CSVFile object dynamically generates properties that match the keys of the _map Dictionary. 15 | # - A .tsv file can be made simply by changing the '_sep' property to "\t". 16 | 17 | signal file_loaded(p_filepath) 18 | signal file_saved(p_filepath) 19 | 20 | const DEFAULT_SEP = "," 21 | const DEFAULT_QUOTE = "\"" 22 | 23 | var _filepath := "" 24 | 25 | var _array := Array2D.new() 26 | var _headers := {} 27 | var _map := {} 28 | 29 | var _sep := DEFAULT_SEP 30 | var _quote := DEFAULT_QUOTE 31 | var _uses_map := true 32 | 33 | func _init(p_sep: String = DEFAULT_SEP, p_quote: String = DEFAULT_QUOTE, p_uses_map: bool = true): 34 | _sep = p_sep 35 | _quote = p_quote 36 | _uses_map = p_uses_map 37 | 38 | 39 | func _get(p_property): 40 | if _map.has(p_property): 41 | return _map[p_property] 42 | 43 | 44 | func _set(p_property, p_value): 45 | if _map.has(p_property): 46 | _map[p_property] = p_value 47 | 48 | 49 | func _get_property_list(): 50 | var ret := [] 51 | for a_key in _map: 52 | ret.append({ 53 | "name": a_key, 54 | "type": typeof(_map[a_key]) 55 | }) 56 | return ret 57 | 58 | 59 | func _get_key(p_row: Array) -> String: 60 | if not p_row: 61 | return "" 62 | return p_row[0] 63 | 64 | 65 | func load_file(p_filepath: String) -> int: 66 | var f = File.new() 67 | var err = f.open(p_filepath, File.READ) 68 | if err != OK: 69 | return err 70 | _headers.clear() 71 | _map.clear() 72 | _array.clear() 73 | 74 | var headers = _parse_line(f.get_line()) 75 | _array.append_row(headers) 76 | for i in range(headers.size()): 77 | _headers[headers[i]] = i 78 | 79 | if not _uses_map: 80 | for a_header in _headers: 81 | _map[a_header] = [] 82 | 83 | #warning-ignore:unused_variable 84 | var line: String 85 | while not f.eof_reached(): 86 | var row = _parse_line(f.get_line()) 87 | if _uses_map: 88 | _map[_get_key(row)] = row 89 | _array.append_row(row) 90 | 91 | f.close() 92 | _filepath = p_filepath 93 | emit_signal("file_loaded", p_filepath) 94 | return OK 95 | 96 | 97 | func save_file(p_filepath: String) -> int: 98 | var f := File.new() 99 | var err := f.open(p_filepath, File.WRITE) 100 | if err != OK: 101 | return err 102 | for a_row in _array.data: 103 | var strings := PoolStringArray() 104 | for a_cell in a_row: 105 | var text := str(a_cell) 106 | text = text.replace(_quote, _quote + _quote) 107 | if text.find(_sep) != -1: 108 | text = _quote + text + _quote 109 | strings.push_back(text) 110 | f.store_line(strings.join(_sep)) 111 | f.close() 112 | emit_signal("file_saved", p_filepath) 113 | return OK 114 | 115 | 116 | func get_headers() -> Dictionary: 117 | return _headers 118 | 119 | 120 | func get_map() -> Dictionary: 121 | return _map 122 | 123 | 124 | func get_array2d() -> Array2D: 125 | return _array 126 | 127 | 128 | func map_has_value(p_key: String, p_header: String) -> bool: 129 | return _map.has(p_key) and _headers.has(p_header) 130 | 131 | 132 | func map_get_value(p_key: String, p_header: String): 133 | if not _uses_map: 134 | printerr("CSVFile is not using map, but 'get_map_value' was called") 135 | return null 136 | if not map_has_value(p_key, p_header): 137 | return null 138 | return _map[p_key][_headers[p_header]] 139 | 140 | 141 | func map_set_value(p_key: String, p_header: String, p_value): 142 | if not _uses_map: 143 | printerr("CSVFile is not using map, but 'get_map_value' was called") 144 | return null 145 | if not map_has_value(p_key, p_header): 146 | return null 147 | _map[p_key][_headers[p_header]] = p_value 148 | 149 | 150 | func _parse_line(p_line: String) -> Array: 151 | if not p_line: 152 | return [] 153 | 154 | var ret := [] 155 | var val := "" 156 | 157 | var in_quotes := false 158 | var start_collect_char := false 159 | var double_quotes_in_column := false 160 | 161 | var chars := p_line.to_utf8() 162 | for a_char in chars: 163 | var s := char(a_char) 164 | if in_quotes: 165 | start_collect_char = true 166 | if s == _sep: 167 | in_quotes = false 168 | double_quotes_in_column = false 169 | else: 170 | if s == "\"": 171 | if not double_quotes_in_column: 172 | val += s 173 | double_quotes_in_column = true 174 | else: 175 | val += s 176 | else: 177 | if s == _quote: 178 | in_quotes = true 179 | 180 | if p_line[0] != "\"" and _quote == "\"": 181 | val += "\"" 182 | 183 | if start_collect_char: 184 | val += "\"" 185 | elif s == _sep: 186 | ret.append(val) 187 | val = "" 188 | start_collect_char = false 189 | elif s == "\r": 190 | continue 191 | elif s == "\n": 192 | break 193 | else: 194 | val += s 195 | ret.append(val) 196 | return ret 197 | -------------------------------------------------------------------------------- /addons/godot-next/references/inflector.gd: -------------------------------------------------------------------------------- 1 | tool 2 | class_name Inflector 3 | extends Reference 4 | # author: xdgamestudios (adapted from C# .NET Humanizer, licensed under MIT) 5 | # license: MIT 6 | # description: Provides inflection tools to pluralize and singularize strings. 7 | # todo: Add more functionality 8 | # usage: 9 | # - Creating Inflector: 10 | # inflector = Inflector.new() # new inflector with empty vocabulary 11 | # inflector = Inflector.new(vocabulary) # new inflector with vocabulary 12 | # Note: 13 | # - Inflector with no vocabulary will not be able to apply convertion out-of-the-box. 14 | # - Creating Vocabulary: 15 | # vocabulary = Inflector.Vocabulary.new() # creates new empty vocabulary 16 | # vocabulary = Inflector.Vocabulary.build_default_vocabulary() 17 | # Note: 18 | # - Empty vocabulary can be manually configured with custom rules. 19 | # - Default vocabulary will apply convertions for english language. 20 | # - Get Vocabulary: 21 | # vocabulary = inflector.get_vocabulary() # reference to the vocabulary used by the inflector. 22 | # - Add Rules: 23 | # vocabulary.add_plural(rule, replacement) # adds convertion rule to plural 24 | # vocabulary.add_singular(rule, replacement) # adds convertion rule to singular 25 | # vocabulary.add_irregular(rule, replacement) # adds irregular convertion 26 | # vocabulary.add_uncountable(word) # add unconvertable word 27 | # Note: 28 | # - 'rule' is a String with regex syntax. 29 | # - 'replacement' is a String with regex systax. 30 | # - Using Inflector: 31 | # inflector.pluralize(word, p_force = false) # returns the plural of the word 32 | # inflector.singularize(word, p_force = false) # returns the singular of the word 33 | # Note: 34 | # - If the first parameter's state is unknown, use 'p_force = true' to force an unknown term into the desired state. 35 | 36 | class Rule extends Reference: 37 | var _regex: RegEx 38 | var _replacement: String 39 | 40 | func _init(p_rule: String, p_replacement: String) -> void: 41 | _regex = RegEx.new() 42 | #warning-ignore:return_value_discarded 43 | _regex.compile(p_rule) 44 | _replacement = p_replacement 45 | 46 | 47 | func apply(p_word: String): 48 | if not _regex.search(p_word): 49 | return null 50 | return _regex.sub(p_word, _replacement) 51 | 52 | 53 | class Vocabulary extends Reference: 54 | var _plurals: Array = [] setget, get_plurals 55 | var _singulars: Array = [] setget, get_singulars 56 | var _uncountables: Array = [] setget, get_uncountables 57 | 58 | 59 | func get_plurals() -> Array: 60 | return _plurals 61 | 62 | 63 | func get_singulars() -> Array: 64 | return _singulars 65 | 66 | 67 | func get_uncountables() -> Array: 68 | return _uncountables 69 | 70 | 71 | func add_plural(p_rule: String, p_replacement: String) -> void: 72 | _plurals.append(Rule.new(p_rule, p_replacement)) 73 | 74 | 75 | func add_singular(p_rule: String, p_replacement: String) -> void: 76 | _singulars.append(Rule.new(p_rule, p_replacement)) 77 | 78 | 79 | func add_irregular(p_singular: String, p_plural: String, p_match_ending: bool = true) -> void: 80 | if p_match_ending: 81 | var sfirst = p_singular[0] 82 | var pfirst = p_plural[0] 83 | var strimmed = p_singular.trim_prefix(sfirst) 84 | var ptrimmed = p_plural.trim_prefix(pfirst) 85 | add_plural("(" + sfirst + ")" + strimmed + "$", "$1" + ptrimmed) 86 | add_singular("(" + pfirst + ")" + ptrimmed + "$", "$1" + strimmed) 87 | else: 88 | add_plural("^%s$" % p_singular, p_plural) 89 | add_singular("^%s$" % p_plural, p_singular) 90 | 91 | 92 | func add_uncountable(p_word: String) -> void: 93 | _uncountables.append(p_word.to_lower()) 94 | 95 | 96 | func is_uncountable(p_word: String) -> bool: 97 | return _uncountables.has(p_word.to_lower()) 98 | 99 | 100 | static func build_default_vocabulary() -> Vocabulary: 101 | var vocabulary = Vocabulary.new() 102 | 103 | # Plural rules. 104 | vocabulary._plurals = [ 105 | Rule.new("$", "s"), 106 | Rule.new("s$", "s"), 107 | Rule.new("(ax|test)is$", "$1es"), 108 | Rule.new("(octop|vir|alumn|fung|cact|foc|hippopotam|radi|stimul|syllab|nucle)us$", "$1i"), 109 | Rule.new("(alias|bias|iris|status|campus|apparatus|virus|walrus|trellis)$", "$1es"), 110 | Rule.new("(buffal|tomat|volcan|ech|embarg|her|mosquit|potat|torped|vet)o$", "$1oes"), 111 | Rule.new("([dti])um$", "$1a"), 112 | Rule.new("sis$", "ses"), 113 | Rule.new("([lr])f$", "$1ves"), 114 | Rule.new("([^f])fe$", "$1ves"), 115 | Rule.new("(hive)$", "$1s"), 116 | Rule.new("([^aeiouy]|qu)y$", "$1ies"), 117 | Rule.new("(x|ch|ss|sh)$", "$1es"), 118 | Rule.new("(matr|vert|ind|d)ix|ex$", "$1ices"), 119 | Rule.new("([m|l])ouse$", "$1ice"), 120 | Rule.new("^(ox)$", "$1en"), 121 | Rule.new("(quiz)$", "$1zes"), 122 | Rule.new("(buz|blit|walt)z$", "$1zes"), 123 | Rule.new("(hoo|lea|loa|thie)f$", "$1ves"), 124 | Rule.new("(alumn|alg|larv|vertebr)a$", "$1ae"), 125 | Rule.new("(criteri|phenomen)on$", "$1a") 126 | ] 127 | 128 | # Singular rules. 129 | vocabulary._singulars = [ 130 | Rule.new("s$", ""), 131 | Rule.new("(n)ews$", "$1ews"), 132 | Rule.new("([dti])a$", "$1um"), 133 | Rule.new("(analy|ba|diagno|parenthe|progno|synop|the|ellip|empha|neuro|oa|paraly)ses$", "$1sis"), 134 | Rule.new("([^f])ves$", "$1fe"), 135 | Rule.new("(hive)s$", "$1"), 136 | Rule.new("(tive)s$", "$1"), 137 | Rule.new("([lr]|hoo|lea|loa|thie)ves$", "$1f"), 138 | Rule.new("(^zomb)?([^aeiouy]|qu)ies$", "$2y"), 139 | Rule.new("(s)eries$", "$1eries"), 140 | Rule.new("(m)ovies$", "$1ovie"), 141 | Rule.new("(x|ch|ss|sh)es$", "$1"), 142 | Rule.new("([m|l])ice$", "$1ouse"), 143 | Rule.new("(o)es$", "$1"), 144 | Rule.new("(shoe)s$", "$1"), 145 | Rule.new("(cris|ax|test)es$", "$1is"), 146 | Rule.new("(octop|vir|alumn|fung|cact|foc|hippopotam|radi|stimul|syllab|nucle)i$", "$1us"), 147 | Rule.new("(alias|bias|iris|status|campus|apparatus|virus|walrus|trellis)es$", "$1"), 148 | Rule.new("^(ox)en", "$1"), 149 | Rule.new("(matr|d)ices$", "$1ix"), 150 | Rule.new("(vert|ind)ices$", "$1ex"), 151 | Rule.new("(quiz)zes$", "$1"), 152 | Rule.new("(buz|blit|walt)zes$", "$1z"), 153 | Rule.new("(alumn|alg|larv|vertebr)ae$", "$1a"), 154 | Rule.new("(criteri|phenomen)a$", "$1on"), 155 | Rule.new("([b|r|c]ook|room|smooth)ies$", "$1ie") 156 | ] 157 | 158 | # Irregular rules. 159 | vocabulary.add_irregular("person", "people") 160 | vocabulary.add_irregular("man", "men") 161 | vocabulary.add_irregular("human", "humans") 162 | vocabulary.add_irregular("child", "children") 163 | vocabulary.add_irregular("sex", "sexes") 164 | vocabulary.add_irregular("move", "moves") 165 | vocabulary.add_irregular("goose", "geese") 166 | vocabulary.add_irregular("wave", "waves") 167 | vocabulary.add_irregular("die", "dice") 168 | vocabulary.add_irregular("foot", "feet") 169 | vocabulary.add_irregular("tooth", "teeth") 170 | vocabulary.add_irregular("curriculum", "curricula") 171 | vocabulary.add_irregular("database", "databases") 172 | vocabulary.add_irregular("zombie", "zombies") 173 | vocabulary.add_irregular("personnel", "personnel") 174 | 175 | vocabulary.add_irregular("is", "are", true) 176 | vocabulary.add_irregular("that", "those", true) 177 | vocabulary.add_irregular("this", "these", true) 178 | vocabulary.add_irregular("bus", "buses", true) 179 | vocabulary.add_irregular("staff", "staff", true) 180 | 181 | # Uncountables. 182 | vocabulary._uncountables = [ 183 | "equipment", 184 | "information", 185 | "corn", 186 | "milk", 187 | "rice", 188 | "money", 189 | "species", 190 | "series", 191 | "fish", 192 | "sheep", 193 | "deer", 194 | "aircraft", 195 | "oz", 196 | "tsp", 197 | "tbsp", 198 | "ml", 199 | "l", 200 | "water", 201 | "waters", 202 | "semen", 203 | "sperm", 204 | "bison", 205 | "grass", 206 | "hair", 207 | "mud", 208 | "elk", 209 | "luggage", 210 | "moose", 211 | "offspring", 212 | "salmon", 213 | "shrimp", 214 | "someone", 215 | "swine", 216 | "trout", 217 | "tuna", 218 | "corps", 219 | "scissors", 220 | "means", 221 | "mail" 222 | ] 223 | 224 | return vocabulary 225 | 226 | 227 | var _vocabulary: Vocabulary setget, get_vocabulary 228 | 229 | func _init(p_vocabulary = null) -> void: 230 | if not p_vocabulary: 231 | p_vocabulary = Vocabulary.build_default_vocabulary() 232 | else: 233 | _vocabulary = p_vocabulary 234 | 235 | 236 | func get_vocabulary() -> Vocabulary: 237 | return _vocabulary 238 | 239 | 240 | func pluralize(p_word: String, p_force: bool = false) -> String: 241 | var result = apply_rules(_vocabulary.get_plurals(), p_word) 242 | 243 | if not p_force: 244 | return result 245 | 246 | var as_singular = apply_rules(_vocabulary.get_singulars(), p_word) 247 | var as_singular_as_plural = apply_rules(_vocabulary.get_plurals(), as_singular) 248 | 249 | if as_singular and as_singular != p_word and as_singular + "s" != p_word and as_singular_as_plural == p_word and result != p_word: 250 | return p_word 251 | 252 | return result 253 | 254 | 255 | func singularize(p_word: String, p_force: bool = false) -> String: 256 | var result = apply_rules(_vocabulary.get_singulars(), p_word) 257 | 258 | if not p_force: 259 | return result 260 | 261 | var as_plural = apply_rules(_vocabulary.get_plurals(), p_word) 262 | var as_plural_as_singular = apply_rules(_vocabulary.get_singulars(), as_plural) 263 | 264 | if as_plural and p_word + "s" != as_plural and as_plural_as_singular == p_word and result != p_word: 265 | return p_word 266 | 267 | return result 268 | 269 | 270 | func apply_rules(p_rules: Array, p_word: String): 271 | if not p_word: 272 | return null 273 | 274 | if _vocabulary.is_uncountable(p_word): 275 | return p_word 276 | 277 | var result = p_word 278 | for i in range(len(p_rules) - 1, -1, -1): 279 | result = p_rules[i].apply(p_word) 280 | if result: 281 | break 282 | 283 | return result 284 | -------------------------------------------------------------------------------- /addons/godot-next/references/message_dispatcher.gd: -------------------------------------------------------------------------------- 1 | class_name MessageDispatcher 2 | extends Reference 3 | # author: MunWolf (Rikhardur Bjarni Einarsson) 4 | # license: MIT 5 | # copyright: Copyright (c) 2019 Rikhardur Bjarni Einarsson 6 | # description: 7 | # A message handler for non predefined signals, if you want to use this 8 | # by extending it on a Node, please use MessageDispatcherWrapper. 9 | 10 | var _message_handlers := {} 11 | 12 | # Connect a handler, obj has to have a function that corresponds to the parameter. 13 | # message_type: type of the message, we call obj.function(message) based on this. 14 | # obj: object that holds the callback. 15 | # function: function to be called on the object. 16 | func connect_message(message_type: String, obj: Object, function: String) -> void: 17 | assert(obj.has_method(function)) 18 | if !_message_handlers.has(message_type): 19 | _message_handlers[message_type] = [] 20 | 21 | _message_handlers[message_type].push_back([obj, function]) 22 | 23 | 24 | # Disconnect a handler, this assumes that some handler with this message type has been registered 25 | # message_type: type of the message, we call obj.function(message) based on this. 26 | # obj: object that holds the callback. 27 | # function: function to be called on the object. 28 | func disconnect_message(message_type: String, obj: Object, function: String) -> void: 29 | assert(_message_handlers[message_type] != null) 30 | _message_handlers[message_type].erase([obj, function]) 31 | 32 | 33 | # Disconnect all handlers. 34 | func disconnect_all_message() -> void: 35 | _message_handlers = {} 36 | 37 | 38 | # Emits a message to all handlers, message can be modified by the handlers 39 | # and it will show up inside the dictionary that was passed by the caller. 40 | # message_type: the type of the message, decides which handlers to call. 41 | # message_data: extra data that can be used by the handler or where the handler can store results. 42 | # return: returns if it was passed to any handler or not. 43 | func emit_message(message_type: String, message_data: Dictionary) -> bool: 44 | var handlers = _message_handlers[message_type] 45 | if handlers != null: 46 | var invalid = [] 47 | for handler in handlers: 48 | if is_instance_valid(handler[0]): 49 | handler[0].call(handler[1], message_type, message_data) 50 | else: 51 | invalid.push_back(handler) 52 | 53 | for handler in invalid: 54 | handlers.erase(handler) 55 | 56 | return handlers != null && !handlers.empty() 57 | -------------------------------------------------------------------------------- /addons/godot-next/references/property_info.gd: -------------------------------------------------------------------------------- 1 | tool 2 | class_name PropertyInfo 3 | extends Reference 4 | # author: xdgamestudios 5 | # license: MIT 6 | # description: 7 | # A wrapper and utility class for generating PropertyInfo 8 | # Dictionaries, of which Object._get_property_list() 9 | # returns an Array. 10 | 11 | var name: String 12 | var type: int 13 | var hint: int 14 | var hint_string: String 15 | var usage: int 16 | 17 | func _init(p_name: String = "", p_type: int = TYPE_NIL, p_hint: int = PROPERTY_HINT_NONE, p_hint_string: String = "", p_usage: int = PROPERTY_USAGE_DEFAULT) -> void: 18 | name = p_name 19 | type = p_type 20 | hint = p_hint 21 | hint_string = p_hint_string 22 | usage = p_usage 23 | 24 | 25 | func to_dict() -> Dictionary: 26 | return { 27 | "name": name, 28 | "type": type, 29 | "hint": hint, 30 | "hint_string": hint_string, 31 | "usage": usage 32 | } 33 | 34 | 35 | -------------------------------------------------------------------------------- /addons/godot-next/references/property_info_factory.gd: -------------------------------------------------------------------------------- 1 | tool 2 | class_name PropertyInfoFactory 3 | extends Reference 4 | 5 | static func from_dict(p_dict: Dictionary) -> Reference: 6 | var name: String = p_dict.name if p_dict.has("name") else "" 7 | var type: int = p_dict.type if p_dict.has("type") else TYPE_NIL 8 | var hint: int = p_dict.hint if p_dict.has("hint") else PROPERTY_HINT_NONE 9 | var hint_string: String = p_dict.hint_string if p_dict.has("hint_string") else "" 10 | var usage: int = p_dict.usage if p_dict.has("usage") else PROPERTY_USAGE_DEFAULT 11 | return PropertyInfo.new(name, type, hint, hint_string, usage) 12 | 13 | 14 | static func new_nil(p_name: String) -> Reference: 15 | return PropertyInfo.new(p_name, TYPE_NIL, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR) 16 | 17 | 18 | static func new_group(p_name: String, p_prefix: String = "") -> Reference: 19 | return PropertyInfo.new(p_name, TYPE_NIL, PROPERTY_HINT_NONE, p_prefix, PROPERTY_USAGE_GROUP) 20 | 21 | 22 | static func new_array(p_name: String, p_hint: int = PROPERTY_HINT_NONE, p_hint_string: String = "", p_usage: int = PROPERTY_USAGE_DEFAULT) -> Reference: 23 | return PropertyInfo.new(p_name, TYPE_ARRAY, p_hint, p_hint_string, p_usage) 24 | 25 | 26 | static func new_dictionary(p_name: String, p_hint: int = PROPERTY_HINT_NONE, p_hint_string: String = "", p_usage: int = PROPERTY_USAGE_DEFAULT) -> Reference: 27 | return PropertyInfo.new(p_name, TYPE_DICTIONARY, p_hint, p_hint_string, p_usage) 28 | 29 | 30 | static func new_resource(p_name: String, p_hint_string: String = "", p_usage: int = PROPERTY_USAGE_DEFAULT) -> Reference: 31 | return PropertyInfo.new(p_name, TYPE_OBJECT, PROPERTY_HINT_RESOURCE_TYPE, p_hint_string, p_usage) 32 | 33 | 34 | static func new_editor_only(p_name: String): 35 | return PropertyInfo.new(p_name, TYPE_NIL, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_SCRIPT_VARIABLE) 36 | 37 | 38 | static func new_storage_only(p_name: String): 39 | return PropertyInfo.new(p_name, TYPE_NIL, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_SCRIPT_VARIABLE) 40 | -------------------------------------------------------------------------------- /addons/godot-next/references/tween_sequence/tween_sequence.gd: -------------------------------------------------------------------------------- 1 | class_name TweenSequence 2 | extends Reference 3 | # author: KoBeWi 4 | # license: MIT 5 | # description: 6 | # A helper class for easier management and chaining of Tweens. 7 | # dynamically from code. 8 | # 9 | # Example usage: 10 | # var seq := TweenSequence.new(get_tree()) 11 | # seq.append($Sprite, "modulate", Color.red, 1) 12 | # seq.append($Sprite, "modulate", Color(1, 0, 0, 0), 1).set_delay(1) 13 | # This will create a Tween and automatically start it, 14 | # changing the Sprite to red color in one second 15 | # and then making it transparent after a delay. 16 | 17 | # Tweener for tweening properties. 18 | class PropertyTweener extends Tweener: 19 | var _target: Object 20 | var _property: NodePath 21 | var _from 22 | var _to 23 | var _duration: float 24 | var _trans: int 25 | var _ease: int 26 | 27 | var _delay: float 28 | var _continue := true 29 | var _advance := false 30 | 31 | func _init(target: Object, property: NodePath, to_value, duration: float) -> void: 32 | assert(target, "Invalid target Object.") 33 | _target = target 34 | _property = property 35 | _from = _target.get_indexed(property) 36 | _to = to_value 37 | _duration = duration 38 | _trans = Tween.TRANS_LINEAR 39 | _ease = Tween.EASE_IN_OUT 40 | 41 | 42 | # Sets custom starting value for the tweener. 43 | # By default, it starts from value at the start of this tweener. 44 | func from(val) -> Tweener: 45 | _from = val 46 | _continue = false 47 | return self 48 | 49 | 50 | # Sets the starting value to the current value, 51 | # i.e. value at the time of creating sequence. 52 | func from_current() -> Tweener: 53 | _continue = false 54 | return self 55 | 56 | 57 | # Sets transition type of this tweener, from Tween.TransitionType. 58 | func set_trans(t: int) -> Tweener: 59 | _trans = t 60 | return self 61 | 62 | 63 | # Sets ease type of this tweener, from Tween.EaseType. 64 | func set_ease(e: int) -> Tweener: 65 | _ease = e 66 | return self 67 | 68 | 69 | # Sets the delay after which this tweener will start. 70 | func set_delay(d: float) -> Tweener: 71 | _delay = d 72 | return self 73 | 74 | 75 | func _start(tween: Tween) -> void: 76 | if not is_instance_valid(_target): 77 | push_warning("Target object freed, aborting Tweener.") 78 | return 79 | 80 | if _continue: 81 | _from = _target.get_indexed(_property) 82 | 83 | if _advance: 84 | tween.interpolate_property(_target, _property, _from, _from + _to, _duration, _trans, _ease, _delay) 85 | else: 86 | tween.interpolate_property(_target, _property, _from, _to, _duration, _trans, _ease, _delay) 87 | 88 | 89 | # Generic tweener for creating delays in sequence. 90 | class IntervalTweener extends Tweener: 91 | var _time: float 92 | 93 | func _init(time: float) -> void: 94 | _time = time 95 | 96 | 97 | func _start(tween: Tween) -> void: 98 | tween.interpolate_callback(self, _time, "_") 99 | 100 | 101 | func _(): 102 | pass 103 | 104 | # Tweener for calling methods. 105 | class CallbackTweener extends Tweener: 106 | var _target: Object 107 | var _delay: float 108 | var _method: String 109 | var _args: Array 110 | 111 | func _init(target: Object, method: String, args: Array) -> void: 112 | assert(target, "Invalid target Object.") 113 | _target = target 114 | _method = method 115 | _args = args 116 | 117 | 118 | # Set delay after which the method will be called. 119 | func set_delay(d: float) -> Tweener: 120 | _delay = d 121 | return self 122 | 123 | 124 | func _start(tween: Tween) -> void: 125 | if not is_instance_valid(_target): 126 | push_warning("Target object freed, aborting Tweener.") 127 | return 128 | 129 | tween.interpolate_callback(_target, _delay, _method, 130 | _get_argument(0), _get_argument(1), _get_argument(2), 131 | _get_argument(3), _get_argument(4)) 132 | 133 | 134 | func _get_argument(i: int): 135 | if i < _args.size(): 136 | return _args[i] 137 | else: 138 | return null 139 | 140 | 141 | # Tweener for tweening arbitrary values using getter/setter method. 142 | class MethodTweener extends Tweener: 143 | var _target: Object 144 | var _method: String 145 | var _from 146 | var _to 147 | var _duration: float 148 | var _trans: int 149 | var _ease: int 150 | 151 | var _delay: float 152 | 153 | func _init(target: Object, method: String, from_value, to_value, duration: float) -> void: 154 | assert(target, "Invalid target Object.") 155 | _target = target 156 | _method = method 157 | _from = from_value 158 | _to = to_value 159 | _duration = duration 160 | _trans = Tween.TRANS_LINEAR 161 | _ease = Tween.EASE_IN_OUT 162 | 163 | 164 | # Sets transition type of this tweener, from Tween.TransitionType. 165 | func set_trans(t: int) -> Tweener: 166 | _trans = t 167 | return self 168 | 169 | 170 | # Sets ease type of this tweener, from Tween.EaseType. 171 | func set_ease(e: int) -> Tweener: 172 | _ease = e 173 | return self 174 | 175 | 176 | # Sets the delay after which this tweener will start. 177 | func set_delay(d: float) -> Tweener: 178 | _delay = d 179 | return self 180 | 181 | 182 | func _start(tween: Tween) -> void: 183 | if not is_instance_valid(_target): 184 | push_warning("Target object freed, aborting Tweener.") 185 | return 186 | 187 | tween.interpolate_method(_target, _method, _from, _to, _duration, _trans, _ease, _delay) 188 | 189 | 190 | # Emited when one step of the sequence is finished. 191 | signal step_finished(idx) 192 | # Emited when a loop of the sequence is finished. 193 | signal loop_finished() 194 | # Emitted when whole sequence is finished. Doesn't happen with inifnite loops. 195 | signal finished() 196 | 197 | var _tree: SceneTree 198 | var _tween: Tween 199 | var _tweeners: Array 200 | 201 | var _current_step := 0 202 | var _loops := 0 203 | var _autostart := true 204 | var _started := false 205 | var _running := false 206 | 207 | var _kill_when_finised := true 208 | var _parallel := false 209 | 210 | # You need to provide SceneTree to be used by the sequence. 211 | func _init(tree: SceneTree) -> void: 212 | _tree = tree 213 | _tween = Tween.new() 214 | _tween.set_meta("sequence", self) 215 | _tree.get_root().call_deferred("add_child", _tween) 216 | 217 | _tree.connect("idle_frame", self, "start", [], CONNECT_ONESHOT) 218 | _tween.connect("tween_all_completed", self, "_step_complete") 219 | 220 | # All Tweener-creating methods will return the Tweeners for further chained usage. 221 | 222 | # Appends a PropertyTweener for tweening properties. 223 | func append(target: Object, property: NodePath, to_value, duration: float) -> PropertyTweener: 224 | var tweener := PropertyTweener.new(target, property, to_value, duration) 225 | _add_tweener(tweener) 226 | return tweener 227 | 228 | 229 | # Appends a PropertyTweener operating on relative values. 230 | func append_advance(target: Object, property: NodePath, by_value, duration: float) -> PropertyTweener: 231 | var tweener := PropertyTweener.new(target, property, by_value, duration) 232 | tweener._advance = true 233 | _add_tweener(tweener) 234 | return tweener 235 | 236 | 237 | # Appends an IntervalTweener for creating delay intervals. 238 | func append_interval(time: float) -> IntervalTweener: 239 | var tweener := IntervalTweener.new(time) 240 | _add_tweener(tweener) 241 | return tweener 242 | 243 | 244 | # Appends a CallbackTweener for calling methods on target object. 245 | func append_callback(target: Object, method: String, args := []) -> CallbackTweener: 246 | var tweener := CallbackTweener.new(target, method, args) 247 | _add_tweener(tweener) 248 | return tweener 249 | 250 | 251 | # Appends a MethodTweener for tweening arbitrary values using methods. 252 | func append_method(target: Object, method: String, from_value, to_value, duration: float) -> MethodTweener: 253 | var tweener := MethodTweener.new(target, method, from_value, to_value, duration) 254 | _add_tweener(tweener) 255 | return tweener 256 | 257 | 258 | # When used, next Tweener will be added as a parallel to previous one. 259 | # Example: sequence.parallel().append(...) 260 | func parallel() -> Reference: 261 | if _tweeners.empty(): 262 | _tweeners.append([]) 263 | _parallel = true 264 | return self 265 | 266 | 267 | # Alias to parallel(), except it won't work without first tweener. 268 | func join() -> Reference: 269 | assert(!_tweeners.empty(), "Can't join with empty sequence!") 270 | _parallel = true 271 | return self 272 | 273 | 274 | # Sets the speed scale of tweening. 275 | func set_speed(speed: float) -> Reference: 276 | _tween.playback_speed = speed 277 | return self 278 | 279 | 280 | # Sets how many the sequence should repeat. 281 | # When used without arguments, sequence will run infinitely. 282 | func set_loops(loops := -1) -> Reference: 283 | _loops = loops 284 | return self 285 | 286 | 287 | # Whether the sequence should autostart or not. 288 | # Enabled by default. 289 | func set_autostart(autostart: bool) -> Reference: 290 | if _autostart and not autostart: 291 | _tree.disconnect("idle_frame", self, "start") 292 | elif not _autostart and autostart: 293 | _tree.connect("idle_frame", self, "start", [], CONNECT_ONESHOT) 294 | 295 | _autostart = autostart 296 | return self 297 | 298 | 299 | # Starts the sequence manually, unless it's already started. 300 | func start() -> void: 301 | assert(_tween, "Tween was removed!") 302 | assert(!_started, "Sequence already started!") 303 | _started = true 304 | _running = true 305 | _run_next_step() 306 | 307 | 308 | # Returns whether the sequence is currently running. 309 | func is_running() -> bool: 310 | return _running 311 | 312 | 313 | # Pauses the execution of the tweens. 314 | func pause() -> void: 315 | assert(_tween, "Tween was removed!") 316 | assert(_running, "Sequence not running!") 317 | _tween.stop_all() 318 | _running = false 319 | 320 | 321 | # Resumes the execution of the tweens. 322 | func resume() -> void: 323 | assert(_tween, "Tween was removed!") 324 | assert(!_running, "Sequence already running!") 325 | _tween.resume_all() 326 | _running = true 327 | 328 | 329 | # Stops the sequence and resets it to the beginning. 330 | func reset() -> void: 331 | assert(_tween, "Tween was removed!") 332 | if _running: 333 | pause() 334 | _started = false 335 | _current_step = 0 336 | _tween.reset_all() 337 | 338 | 339 | # Frees the underlying Tween. Sequence is unusable after this operation. 340 | func kill(): 341 | assert(_tween, "Tween was already removed!") 342 | if _running: 343 | pause() 344 | _tween.queue_free() 345 | 346 | 347 | # Whether the Tween should be freed when sequence finishes. 348 | # Default is true. If set to false, sequence will restart on end. 349 | func set_autokill(autokill: bool): 350 | _kill_when_finised = autokill 351 | 352 | 353 | func _add_tweener(tweener: Tweener): 354 | assert(_tween, "Tween was removed!") 355 | assert(!_started, "Can't append to a started sequence!") 356 | if not _parallel: 357 | _tweeners.append([]) 358 | _tweeners.back().append(tweener) 359 | _parallel = false 360 | 361 | 362 | func _run_next_step() -> void: 363 | assert(!_tweeners.empty(), "Sequence has no steps!") 364 | var group := _tweeners[_current_step] as Array 365 | for tweener in group: 366 | tweener._start(_tween) 367 | _tween.start() 368 | 369 | 370 | func _step_complete() -> void: 371 | emit_signal("step_finished", _current_step) 372 | _current_step += 1 373 | 374 | if _current_step == _tweeners.size(): 375 | _loops -= 1 376 | if _loops == -1: 377 | emit_signal("finished") 378 | if _kill_when_finised: 379 | kill() 380 | else: 381 | reset() 382 | else: 383 | emit_signal("loop_finished") 384 | _current_step = 0 385 | _run_next_step() 386 | else: 387 | _run_next_step() 388 | -------------------------------------------------------------------------------- /addons/godot-next/references/tween_sequence/tweener.gd: -------------------------------------------------------------------------------- 1 | class_name Tweener 2 | extends Reference 3 | # author: KoBeWi 4 | # license: MIT 5 | # description: 6 | # Abstract class for all Tweeners. 7 | 8 | func _start(tween: Tween) -> void: 9 | pass 10 | -------------------------------------------------------------------------------- /addons/godot-next/resources/behavior.gd: -------------------------------------------------------------------------------- 1 | tool 2 | class_name Behavior 3 | extends Resource 4 | # author: xdgamestudios 5 | # license: MIT 6 | # description: 7 | # This is an abstract base "Behavior" class for use in the "Behaviors" node class. 8 | # "Behaviors" manages "Behavior" resources and calls notification methods that the Behavior implements. 9 | # usage: 10 | # - Supported notifications: 11 | # _enter_tree() -> void 12 | # _exit_tree() -> void 13 | # _ready() -> void 14 | # _process(p_delta: float) -> void 15 | # _physics_process(delta: float) -> void: 16 | # _input(event: InputEvent) -> void: 17 | # _unhandled_input(event: InputEvent) -> void: 18 | # _unhandled_key_input(event: InputEventKey) -> void: 19 | # Note: 20 | # - If present notifications, are automatically triggered by the owner class. 21 | # - If the behavior is disabled its notifications will not be processed. 22 | 23 | # A reference to the owning Behaviors node. 24 | var owner: Node = null setget set_owner, get_owner 25 | 26 | # Allows users to toggle processing callbacks on the owner. 27 | var enabled: bool = true setget set_enabled, get_enabled 28 | 29 | # This name is used to match the convention in Unity's MonoBehaviour class. 30 | func _awake() -> void: 31 | pass 32 | 33 | 34 | # This name is used to match the convention in Unity's MonoBehaviour class. 35 | func _on_enable() -> void: 36 | pass 37 | 38 | 39 | # This name is used to match the convention in Unity's MonoBehaviour class. 40 | func _on_disable() -> void: 41 | pass 42 | 43 | 44 | # Returns an instance of the stored Behavior resource from the owner. 45 | func get_behavior(p_type: Script) -> Resource: 46 | return owner.get_element(p_type) 47 | 48 | 49 | func set_enabled(p_enable: bool) -> void: 50 | if enabled == p_enable: 51 | return 52 | enabled = p_enable 53 | if p_enable: 54 | _on_enable() 55 | owner._add_to_callbacks() 56 | else: 57 | _on_disable() 58 | owner._remove_from_callbacks() 59 | 60 | 61 | func get_enabled() -> bool: 62 | return enabled 63 | 64 | 65 | func set_owner(p_owner: Node) -> void: 66 | assert(p_owner) # Must be assigned a valid owner at all times, except initially. 67 | owner = p_owner 68 | 69 | 70 | func get_owner() -> Node: 71 | return owner 72 | 73 | 74 | # Should only override if one wishes to create their own abstract Behaviors 75 | # By default, the absence of this method is interpreted as a non-abstract type! 76 | static func is_abstract() -> bool: 77 | return true 78 | -------------------------------------------------------------------------------- /addons/godot-next/resources/discrete_gradient_texture.gd: -------------------------------------------------------------------------------- 1 | tool 2 | class_name DiscreteGradientTexture 3 | extends ImageTexture 4 | # author: Athrunen 5 | # license: MIT 6 | # description: Has the same functionality as the GradientTexture but does not interpolate colors. 7 | # todos: 8 | # - Write a more elegant way of updating the texture than changing the resolution. 9 | # - Persuade Godot to repeat the texture vertically in the inspector. 10 | 11 | export var resolution: int = 256 setget _update_resolution 12 | export var gradient: Gradient = Gradient.new() setget _update_gradient 13 | 14 | func _ready() -> void: 15 | _update_texture() 16 | 17 | 18 | func _update_texture() -> void: 19 | var image := Image.new() 20 | image.create(resolution, 1, false, Image.FORMAT_RGBA8) 21 | 22 | if not gradient: 23 | return 24 | 25 | image.lock() 26 | 27 | var last_offset := 0 28 | var last_pixel := 0 29 | var index := 0 30 | for offset in gradient.offsets: 31 | var amount := int(round((offset - last_offset) * resolution)) 32 | amount -= 1 if amount > 0 else 0 33 | var color := gradient.colors[index] 34 | for x in range(amount): 35 | image.set_pixel(x + last_pixel, 0, color) 36 | 37 | last_offset = offset 38 | last_pixel = last_pixel + amount 39 | index += 1 40 | 41 | if last_pixel < resolution: 42 | var color := gradient.colors[-1] 43 | for x in resolution - last_pixel: 44 | image.set_pixel(x + last_pixel, 0, color) 45 | 46 | image.unlock() 47 | self.create_from_image(image, 0) 48 | 49 | 50 | func _update_gradient(g: Gradient) -> void: 51 | gradient = g 52 | _update_texture() 53 | 54 | 55 | func _update_resolution(r: int) -> void: 56 | resolution = r 57 | _update_texture() 58 | -------------------------------------------------------------------------------- /addons/godot-next/resources/resource_collections/resource_array.gd: -------------------------------------------------------------------------------- 1 | tool 2 | class_name ResourceArray 3 | extends ResourceCollection 4 | # author: xdgamestudios 5 | # license: MIT 6 | # description: 7 | # A ResourceCollection implementation that manages an Array of Resources. 8 | # One can add multiple instances of any given Resource type. 9 | # deps: 10 | # - ResourceCollection 11 | # - PropertyInfo 12 | 13 | const COLLECTION_NAME = "[ Array ]" 14 | 15 | var _data := [] 16 | 17 | func _init() -> void: 18 | resource_name = COLLECTION_NAME 19 | 20 | 21 | func clear() -> void: 22 | _data.clear() 23 | 24 | 25 | func get_data() -> Array: 26 | return _data 27 | 28 | 29 | func _get(p_property: String): 30 | if p_property.begins_with(DATA_PREFIX): 31 | var index := int(p_property.trim_prefix(DATA_PREFIX + "item_")) 32 | return _data[index] if index < _data.size() else null 33 | return null 34 | 35 | 36 | func _set(p_property, p_value): 37 | if p_property.begins_with(DATA_PREFIX): 38 | var index := int(p_property.trim_prefix(DATA_PREFIX + "item_")) 39 | if not p_value: 40 | _data.remove(index) 41 | property_list_changed_notify() 42 | else: 43 | var res = _instantiate_script(p_value) if p_value is Script else p_value 44 | _class_type.res = res 45 | if res and _class_type.is_type(_type): 46 | _data[index] = res 47 | property_list_changed_notify() 48 | return true 49 | return false 50 | 51 | 52 | func _add_element(script) -> void: 53 | _data.append(script.new()) 54 | 55 | 56 | func _refresh_data() -> void: 57 | if _type == null: 58 | clear() 59 | return 60 | var data_cache := _data.duplicate() 61 | for a_resource in data_cache: 62 | if not ClassType.new(a_resource).is_type(_type): 63 | _data.erase(a_resource) 64 | 65 | 66 | func _export_data_group() -> Array: 67 | var list := ._export_data_group() 68 | list.append(PropertyInfoFactory.new_storage_only("_data").to_dict()) 69 | if _data.empty(): 70 | list.append(PropertyInfoFactory.new_nil(DATA_PREFIX + EMPTY_ENTRY).to_dict()) 71 | for an_index in _data.size(): 72 | list.append(PropertyInfoFactory.new_resource("%sitem_%s" % [DATA_PREFIX, an_index], "", PROPERTY_USAGE_EDITOR).to_dict()) 73 | return list 74 | -------------------------------------------------------------------------------- /addons/godot-next/resources/resource_collections/resource_collection.gd: -------------------------------------------------------------------------------- 1 | tool 2 | class_name ResourceCollection 3 | extends Resource 4 | # author: xdgamestudios 5 | # license: MIT 6 | # description: 7 | # An abstract base class for data structures that store Resource objects. 8 | # Uses a key-value store, but can also append items. 9 | 10 | const SETUP_PREFIX = "setup/" 11 | const DATA_PREFIX = "data/" 12 | 13 | const EMPTY_ENTRY = "[ Empty ]" 14 | 15 | var _type: Script = null 16 | var _type_readonly: bool = false 17 | var _class_type: ClassType = ClassType.new() 18 | 19 | func clear() -> void: 20 | assert(false) 21 | 22 | 23 | func get_base_type() -> Script: 24 | return _type 25 | 26 | 27 | func set_base_type(p_type: Script) -> void: 28 | if _type == p_type: 29 | return 30 | _type = p_type 31 | property_list_changed_notify() 32 | 33 | 34 | func is_type_readonly() -> bool: 35 | return _type_readonly 36 | 37 | 38 | func set_type_readonly(read_only: bool) -> void: 39 | if _type_readonly == read_only: 40 | return 41 | _type_readonly = read_only 42 | property_list_changed_notify() 43 | 44 | 45 | func _get(p_property: String): 46 | match p_property.trim_prefix(SETUP_PREFIX): 47 | "base_type": 48 | return _type 49 | return null 50 | 51 | 52 | func _set(p_property: String, p_value) -> bool: 53 | match p_property.trim_prefix(SETUP_PREFIX): 54 | "base_type": 55 | if _type != p_value: 56 | _type = p_value 57 | property_list_changed_notify() 58 | return true 59 | return false 60 | 61 | 62 | func _get_property_list() -> Array: 63 | var list := [] 64 | list += _export_setup_group() 65 | 66 | if not _type: 67 | return list 68 | 69 | list += _export_data_group() 70 | return list 71 | 72 | 73 | # Append an element to the collection. 74 | #warning-ignore:unused_argument 75 | func _add_element(p_script: Script) -> void: 76 | assert(false) 77 | 78 | 79 | # Refresh the data upon type change. 80 | func _refresh_data() -> void: 81 | assert(false) 82 | 83 | 84 | # Export properties within the 'data' group. 85 | func _export_data_group() -> Array: 86 | return [ PropertyInfoFactory.new_editor_only(DATA_PREFIX + "dropdown").to_dict() ] 87 | 88 | 89 | # Export properties within the 'setup' group. 90 | func _export_setup_group() -> Array: 91 | return [ PropertyInfoFactory.new_resource(SETUP_PREFIX + "base_type", "Script").to_dict() ] if not _type_readonly else [] 92 | 93 | 94 | # Injects controls to the EditorInspectorPlugin. 95 | func _parse_property(p_plugin: EditorInspectorPlugin, p_pinfo: PropertyInfo) -> bool: 96 | match p_pinfo.name.trim_prefix(DATA_PREFIX): 97 | "dropdown": 98 | var elements = _find_inheritors() 99 | var control = InspectorControls.new_dropdown_appender(elements, self, "_on_dropdown_selector_selected") 100 | p_plugin.add_custom_control(control) 101 | return true 102 | return false 103 | 104 | 105 | func _instantiate_script(p_script: Script) -> Resource: 106 | var res: Resource = null 107 | if ClassDB.is_parent_class(p_script.get_instance_base_type(), "Resource"): 108 | push_warning("Must assign non-Script Resource instances. Auto-instantiating the given Resource script.") 109 | res = p_script.new() 110 | else: 111 | push_error("Must assign non-Script Resource instances. Fallback error: cannot auto-instantiate non-Resource scripts into ResourceCollection.") 112 | return res 113 | 114 | 115 | func _find_inheritors() -> Dictionary: 116 | _class_type.res = _type 117 | var list = _class_type.get_deep_inheritors_list() 118 | var type_map = _class_type.get_deep_type_map() 119 | var inheritors = { } 120 | for a_name in list: 121 | inheritors[a_name] = load(type_map[a_name].path) 122 | return inheritors 123 | 124 | 125 | func _on_dropdown_selector_selected(dropdown_selector): 126 | var script = dropdown_selector.get_selected_meta() 127 | _add_element(script) 128 | property_list_changed_notify() 129 | -------------------------------------------------------------------------------- /addons/godot-next/resources/resource_collections/resource_set.gd: -------------------------------------------------------------------------------- 1 | tool 2 | class_name ResourceSet 3 | extends ResourceCollection 4 | # author: xdgamestudios 5 | # license: MIT 6 | # description: 7 | # A ResourceCollection implementation that manages a Set of Resources. 8 | # One can add only one instance of any given Resource type. 9 | # deps: 10 | # - ResourceCollection 11 | # - PropertyInfo 12 | 13 | const COLLECTION_NAME: String = "[ Set ]" 14 | 15 | var _data: Dictionary = {} 16 | 17 | func _init() -> void: 18 | resource_name = COLLECTION_NAME 19 | 20 | 21 | func clear() -> void: 22 | _data.clear() 23 | 24 | 25 | func get_data() -> Dictionary: 26 | return _data 27 | 28 | 29 | func _get(p_property: String): 30 | if p_property.begins_with(DATA_PREFIX): 31 | var key = p_property.trim_prefix(DATA_PREFIX) 32 | return _data.get(key, null) 33 | return null 34 | 35 | 36 | func _set(p_property: String, p_value) -> bool: 37 | if p_property.begins_with(DATA_PREFIX): 38 | var key = p_property.trim_prefix(DATA_PREFIX) 39 | if not p_value: 40 | #warning-ignore:return_value_discarded 41 | _data.erase(key) 42 | property_list_changed_notify() 43 | elif _data[key].get_script() == p_value.get_script(): 44 | var res = _instantiate_script(p_value) if p_value is Script else p_value 45 | if res: 46 | _data[key] = res 47 | property_list_changed_notify() 48 | return true 49 | return false 50 | 51 | 52 | func _add_element(p_script: Script) -> void: 53 | _class_type.res = p_script 54 | var key := _class_type.get_name() 55 | if not _data.has(key): 56 | _data[key] = p_script.new() 57 | 58 | 59 | func _refresh_data() -> void: 60 | if _type == null: 61 | clear() 62 | return 63 | var typenames := _data.keys() 64 | for a_typename in typenames: 65 | _class_type.res = _data[a_typename] 66 | if not _class_type.is_type(_type): 67 | #warning-ignore:return_value_discarded 68 | _data.erase(a_typename) 69 | 70 | 71 | func _export_data_group() -> Array: 72 | var list := ._export_data_group() 73 | list.append(PropertyInfoFactory.new_storage_only("_data").to_dict()) 74 | if _data.empty(): 75 | list.append(PropertyInfoFactory.new_nil(DATA_PREFIX + EMPTY_ENTRY).to_dict()) 76 | for a_typename in _data: 77 | list.append(PropertyInfoFactory.new_resource(DATA_PREFIX + a_typename, "", PROPERTY_USAGE_EDITOR).to_dict()) 78 | return list 79 | -------------------------------------------------------------------------------- /addons/godot-next/resources/singletons/singleton_cache.gd: -------------------------------------------------------------------------------- 1 | tool 2 | extends Resource 3 | # author: xdgamestudios 4 | # license: MIT 5 | # description: 6 | # A resource file that is preloaded into memory to allow for accessing 7 | # singleton classes project wide using Singletons 8 | 9 | var _cache: Dictionary = {} 10 | var _paths: Dictionary = {} 11 | 12 | func get_cache() -> Dictionary: 13 | return _cache 14 | 15 | 16 | func get_paths() -> Dictionary: 17 | return _paths 18 | -------------------------------------------------------------------------------- /addons/godot-next/singletons/icons.gd: -------------------------------------------------------------------------------- 1 | tool 2 | class_name Icons 3 | extends Reference 4 | # author: willnationsdev 5 | # license: MIT 6 | # description: 7 | # A singleton that stores paths to icons, but fetches them loaded for users. 8 | # icons must have a `icon_.svg` file path, anywhere in the project. 9 | # The name can then be accessed directly as a property of Icons.fetch(). 10 | 11 | const SELF_PATH: String = "res://addons/godot-next/singletons/icons.gd" 12 | 13 | var data: Dictionary = {} 14 | 15 | func _init() -> void: 16 | register_dir("res://") 17 | 18 | 19 | static func fetch() -> Reference: 20 | return Singletons.fetch(_this()) as Reference 21 | 22 | 23 | static func _this() -> Script: 24 | return load(SELF_PATH) as Script 25 | 26 | 27 | func register_dir(p_path: String) -> void: 28 | var files_data = FileSearch.search_regex_full_path(".*icon_(.*)\\.svg$", p_path) 29 | for a_data in files_data: 30 | var a_match = files_data[a_data]["match"] 31 | var name = ClassType.namify_path(a_match.strings[1]) 32 | data[name] = a_match.subject 33 | 34 | 35 | func _get_property_list(): 36 | var list := [] 37 | for a_name in data: 38 | list.append(PropertyInfoFactory.new_resource(a_name, "Texture").to_dict()) 39 | return list 40 | 41 | 42 | func _get(p_property): 43 | if data.has(p_property): 44 | return load(data[p_property]) if ResourceLoader.exists(data[p_property]) else null 45 | #return data[p_property] 46 | return null 47 | 48 | 49 | func _set(p_property, p_value) -> bool: 50 | if data.has(p_property): 51 | match typeof(p_value): 52 | TYPE_STRING: 53 | data[p_property] = p_value 54 | return true 55 | TYPE_OBJECT: 56 | if p_value: 57 | if p_value is Texture: 58 | data[p_property] = p_value.resource_path 59 | return true 60 | else: 61 | return false 62 | else: 63 | data[p_property] = null 64 | #warning-ignore:return_value_discarded 65 | data.erase(p_property) 66 | return false 67 | -------------------------------------------------------------------------------- /default_env.tres: -------------------------------------------------------------------------------- 1 | [gd_resource type="Environment" load_steps=2 format=2] 2 | 3 | [sub_resource type="ProceduralSky" id=1] 4 | 5 | [resource] 6 | background_mode = 2 7 | background_sky = SubResource( 1 ) 8 | -------------------------------------------------------------------------------- /demo/demo.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=4 format=2] 2 | 3 | [ext_resource path="res://demo/demo_2d.tscn" type="PackedScene" id=1] 4 | [ext_resource path="res://demo/demo_3d.tscn" type="PackedScene" id=2] 5 | [ext_resource path="res://demo/scripts/demo.gd" type="Script" id=4] 6 | 7 | [node name="Demo" type="Node"] 8 | script = ExtResource( 4 ) 9 | 10 | [node name="Demo2D" parent="." instance=ExtResource( 1 )] 11 | 12 | [node name="Demo3D" parent="." instance=ExtResource( 2 )] 13 | -------------------------------------------------------------------------------- /demo/demo_2d.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=8 format=2] 2 | 3 | [ext_resource path="res://addons/godot-next/2d/geometry_2d.gd" type="Script" id=1] 4 | [ext_resource path="res://addons/godot-next/2d/trail_2d.gd" type="Script" id=2] 5 | [ext_resource path="res://addons/godot-next/gui/debug_label.gd" type="Script" id=3] 6 | [ext_resource path="res://addons/godot-next/2d/vector_display_2d.gd" type="Script" id=4] 7 | [ext_resource path="res://demo/scripts/wandering_node_2d.gd" type="Script" id=5] 8 | [ext_resource path="res://demo/scripts/test_vector_display.gd" type="Script" id=6] 9 | 10 | [sub_resource type="CapsuleShape2D" id=1] 11 | radius = 50.0 12 | height = 100.0 13 | 14 | [node name="Demo2D" type="Node2D"] 15 | 16 | [node name="StaticBody2D" type="StaticBody2D" parent="."] 17 | position = Vector2( 900, 450 ) 18 | 19 | [node name="Geometry2D" type="CollisionShape2D" parent="StaticBody2D"] 20 | shape = SubResource( 1 ) 21 | script = ExtResource( 1 ) 22 | color = Color( 1, 1, 1, 1 ) 23 | offset_position = Vector2( 0, 0 ) 24 | 25 | [node name="WanderingNode2D" type="Node2D" parent="."] 26 | position = Vector2( 500, 450 ) 27 | script = ExtResource( 5 ) 28 | 29 | [node name="Trail2D" type="Line2D" parent="."] 30 | position = Vector2( 500, 450 ) 31 | script = ExtResource( 2 ) 32 | target_path = NodePath("../WanderingNode2D") 33 | trail_length = 50 34 | 35 | [node name="DebugLabel" type="Label" parent="."] 36 | margin_left = 100.0 37 | margin_top = 400.0 38 | margin_right = 289.0 39 | margin_bottom = 499.0 40 | text = "DebugLabel 41 | WanderingNode2D 42 | position = Vector2( 500, 450 ) 43 | velocity = null 44 | name = \"WanderingNode2D\" 45 | " 46 | script = ExtResource( 3 ) 47 | __meta__ = { 48 | "_edit_use_anchors_": false 49 | } 50 | update_mode = 0 51 | target_path = NodePath("../WanderingNode2D") 52 | show_label_name = true 53 | show_target_name = true 54 | properties = PoolStringArray( "position", "velocity", "name" ) 55 | 56 | [node name="TestVectorDisplay2D" type="Node2D" parent="."] 57 | position = Vector2( 100, 100 ) 58 | script = ExtResource( 6 ) 59 | pool_vector2_array = PoolVector2Array( 100, 200, 200, 100 ) 60 | 61 | [node name="VectorDisplay2D" type="Node" parent="TestVectorDisplay2D"] 62 | script = ExtResource( 4 ) 63 | variable_name = "pool_vector2_array" 64 | -------------------------------------------------------------------------------- /demo/demo_3d.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=5 format=2] 2 | 3 | [ext_resource path="res://addons/godot-next/3d/trail_3d.gd" type="Script" id=1] 4 | [ext_resource path="res://demo/scripts/wandering_node_3d.gd" type="Script" id=2] 5 | [ext_resource path="res://addons/godot-next/3d/vector_display_3d.gd" type="Script" id=3] 6 | [ext_resource path="res://demo/scripts/test_vector_display.gd" type="Script" id=4] 7 | 8 | [node name="Demo3D" type="Spatial"] 9 | 10 | [node name="WanderingNode3D" type="Spatial" parent="."] 11 | script = ExtResource( 2 ) 12 | 13 | [node name="Trail3D" type="ImmediateGeometry" parent="WanderingNode3D"] 14 | script = ExtResource( 1 ) 15 | length = 5.0 16 | max_radius = 0.25 17 | density_lengthwise = 50 18 | density_around = 20 19 | 20 | [node name="Camera" type="Camera" parent="."] 21 | transform = Transform( 1, 0, 0, 0, 0.866025, 0.5, 0, -0.5, 0.866025, 0, 2, 8 ) 22 | 23 | [node name="TestVectorDisplay3D" type="Spatial" parent="."] 24 | transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0.5, 0, 0.5 ) 25 | script = ExtResource( 4 ) 26 | pool_vector3_array = PoolVector3Array( 1, 0, 2, 2, 0, 1 ) 27 | 28 | [node name="VectorDisplay3D" type="Node" parent="TestVectorDisplay3D"] 29 | script = ExtResource( 3 ) 30 | variable_name = "pool_vector3_array" 31 | -------------------------------------------------------------------------------- /demo/scripts/demo.gd: -------------------------------------------------------------------------------- 1 | extends Node 2 | 3 | func _ready(): 4 | # Array2D 5 | var array_2d = Array2D.new() 6 | array_2d.resize(3, 4) 7 | array_2d.set_cell(2, 3, "Array2D test") 8 | print(array_2d.get_cell(2, 3)) 9 | 10 | # BitFlag 11 | var bit_flag_dict = {"a": 1, "b": 2} 12 | var bit_flag = BitFlag.new(bit_flag_dict) 13 | bit_flag.a = true 14 | print(bit_flag.get_active_keys()) 15 | 16 | # Bitset 17 | var bitset = Bitset.new(5) 18 | bitset.set_bit(2, true) 19 | bitset.set_bit(3, true) 20 | bitset.print_bits(false) 21 | -------------------------------------------------------------------------------- /demo/scripts/test_vector_display.gd: -------------------------------------------------------------------------------- 1 | extends Node 2 | 3 | export(Vector2) var vector2 = Vector2(100, 200) 4 | export(Vector3) var vector3 = Vector3(1, 2, 3) 5 | export(PoolVector2Array) var pool_vector2_array = PoolVector2Array() 6 | export(PoolVector3Array) var pool_vector3_array = PoolVector3Array() 7 | export(Array) var array = [Vector2(100, 100), Vector3(0, 1, 2)] 8 | -------------------------------------------------------------------------------- /demo/scripts/wandering_node_2d.gd: -------------------------------------------------------------------------------- 1 | extends Node2D 2 | 3 | var velocity = Vector2() 4 | onready var center = position 5 | 6 | func _ready(): 7 | randomize() 8 | 9 | 10 | func _physics_process(_delta): 11 | velocity += Vector2(randf() - 0.5, randf() - 0.5) 12 | if velocity.length_squared() > 10: 13 | velocity *= 0.99 14 | position += velocity 15 | position = position.linear_interpolate(center, 0.03).round() 16 | -------------------------------------------------------------------------------- /demo/scripts/wandering_node_3d.gd: -------------------------------------------------------------------------------- 1 | extends Spatial 2 | 3 | var velocity = Vector3() 4 | onready var center = transform.origin 5 | 6 | func _ready(): 7 | randomize() 8 | 9 | 10 | func _physics_process(_delta): 11 | velocity += Vector3(randf() * 2 - 1, randf() - 0.5, randf() - 0.5) * 0.05 12 | if velocity.length_squared() > 1: 13 | velocity *= 0.99 14 | transform.origin += velocity 15 | transform.origin = transform.origin.linear_interpolate(center, 0.125) 16 | -------------------------------------------------------------------------------- /docs/use_cases.md: -------------------------------------------------------------------------------- 1 | # Use Cases 2 | 3 | 1. Have you ever wanted to have an overrideable print method in Objects? 4 | - Variant.to_string(value) 5 | 2. Have you ever wanted to get a type as a string, regardless of whether it's an Object (return `get_class()`), or a built-in, e.g. int (return "int")? 6 | - Variant.get_type(value) 7 | 3. Have you ever wanted multiple scripts per Node? 8 | - CallbackDelegator node + Behavior resources 9 | - Supply a "base_type" script or click to initialize the default one. 10 | - This CallbackDelegator has a private property of a new "ResourceSet" type; it's a collection of unique-script resource instances housing types which... 11 | 1. can see the CallbackDelegator node as an 'owner' property. 12 | 2. can be toggled on and off as an 'enabled' property. 13 | 3. receive Node notification callbacks that have been delegated to them by the CallbackDelegator node. 14 | - This is much more efficient than simply having a large number of child nodes. 15 | 1. Resources are more lightweight than scripts. 16 | 2. One can directly save and load Resources from the filesystem. 17 | 3. Better than nodes, one can expand each resource into its own sub-inspector within a single Inspector "view", viewing all the data at once. 18 | 4. Have you ever wanted to wrap an engine type, script type, or scene type under a single API? 19 | - ClassType 20 | - It stores a reference to a *type*. This means it can represent "Node" engine class, "MyNode" script class, anonymous scripts and anonymous scenes equally. 21 | - ClassType.new() functions with being... 22 | - the string name of the class. 23 | - a reference to the script or scene. 24 | - a path to the script or scene. 25 | - an instance of an example object. 26 | - Supplying a Script or PackedScene will revert to the previous cases 27 | - Providing a Node that is the root of a scene will load that scene (since that is an approximation of the Node's type) 28 | - Providing an Object with a script will load that script (since that is the Object's type) 29 | - Providing an Object without a script will set the ClassType.name to be the Object.get_class() value 30 | - `.instance()` returns an instance of the type 31 | - `.get_type_class()` returns the string name of the type 32 | - `.get_type_parent()` returns a ClassType representing the base class of the type. 33 | - Anonymous script vs. named script doesn't matter. Uses `Script.get_base_script()`. 34 | - Also fetches base scene from a PackedScene instance 35 | - If there are no more inherited scenes, a PackedScene will defer first to the root node's script, then to the engine type of the root Node. 36 | - `.get_inheritors_list()` returns a list of the types' derived types. 37 | - The `res` property is the script or scene associated with the type. Therefore, one can get a script class by name with `ClassType.new("MyType").res`. 38 | 5. Have you ever wanted to search through your project folder and identify files which match certain criteria? 39 | - FileSearch 40 | - Has a variety of .search_(p_param, p_starting_directory = "res://", p_do_a_recursive_search = true) methods 41 | 6. Have you ever wanted to create singletons that exist even in the editor context? 42 | - Singletons 43 | - This Reference type stores a static database of all Reference-derived types which can be fetched by their script. 44 | - `Singletons.fetch(MyType)` will return an instance of MyType and ensure that only one instance of it is managed by the Singletons database. 45 | - Can implement one's own static method which converts `Singletons.fetch(MyType)` to `MyType.fetch()`. 46 | 6. Have you ever wanted to send signals to child nodes, resources or even references but you don't want to define all the possible signals in the instance? 47 | - MessageDispatcher 48 | - Allows any object to register a function that handles a specific message_type. 49 | - Emit a message on the dispatcher and it sends it to all relevant handlers or discards it if no handlers were registered. 50 | 7. Have you ever wanted to create a flat gradient with hard transitions? 51 | - DiscreteGradientTexture 52 | - It works like the GradientTexture but ignores the color interpolation of the gradient. 53 | -------------------------------------------------------------------------------- /format.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Loops through all text files tracked by Git. 4 | git grep -zIl '' | 5 | while IFS= read -rd '' f; do 6 | # Exclude csproj and hdr files. 7 | if [[ $f == *"csproj" ]]; then 8 | continue 9 | elif [[ $f == *"hdr" ]]; then 10 | continue 11 | fi 12 | # Ensures that files are UTF-8 formatted. 13 | recode UTF-8 $f 2> /dev/null 14 | # Ensures that files have LF line endings. 15 | dos2unix $f 2> /dev/null 16 | # Ensures that files do not contain a BOM. 17 | sed -i '1s/^\xEF\xBB\xBF//' "$f" 18 | # Ensures that files end with newline characters. 19 | tail -c1 < "$f" | read -r _ || echo >> "$f"; 20 | done 21 | 22 | git diff > patch.patch 23 | FILESIZE=$(stat -c%s patch.patch) 24 | MAXSIZE=5 25 | 26 | # If no patch has been generated all is OK, clean up, and exit. 27 | if (( FILESIZE < MAXSIZE )); then 28 | printf "Files in this commit comply with the formatting rules.\n" 29 | rm -f patch.patch 30 | exit 0 31 | fi 32 | 33 | # A patch has been created, notify the user, clean up, and exit. 34 | printf "\n*** The following differences were found between the code " 35 | printf "and the formatting rules:\n\n" 36 | cat patch.patch 37 | printf "\n*** Aborting, please fix your commit(s) with 'git commit --amend' or 'git rebase -i '\n" 38 | rm -f patch.patch 39 | exit 1 40 | -------------------------------------------------------------------------------- /icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/willnationsdev/godot-next/3b2949fce032bf827ce4fadb7e70f8872b635144/icon.png -------------------------------------------------------------------------------- /icon.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/icon.png-487276ed1e3a0c39cad0279d744ee560.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://icon.png" 13 | dest_files=[ "res://.import/icon.png-487276ed1e3a0c39cad0279d744ee560.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | stream=false 32 | size_limit=0 33 | detect_3d=true 34 | svg/scale=1.0 35 | -------------------------------------------------------------------------------- /licenses/LICENSE_Inflector.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) .NET Foundation and Contributors 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 13 | all 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 21 | THE SOFTWARE. 22 | 23 | ============================================================================== 24 | 25 | Inflector (https://github.com/srkirkland/Inflector) 26 | The MIT License (MIT) 27 | Copyright (c) 2013 Scott Kirkland 28 | 29 | ============================================================================== 30 | 31 | ByteSize (https://github.com/omar/ByteSize) 32 | The MIT License (MIT) 33 | Copyright (c) 2013-2014 Omar Khudeira (http://omar.io) 34 | 35 | ============================================================================== 36 | -------------------------------------------------------------------------------- /project.godot: -------------------------------------------------------------------------------- 1 | ; Engine configuration file. 2 | ; It's best edited using the editor UI and not directly, 3 | ; since the parameters that go here are not all obvious. 4 | ; 5 | ; Format: 6 | ; [section] ; section goes between [] 7 | ; param=value ; assign values to parameters 8 | 9 | config_version=4 10 | 11 | _global_script_classes=[ { 12 | "base": "Reference", 13 | "class": "Array2D", 14 | "language": "GDScript", 15 | "path": "res://addons/godot-next/references/array_2d.gd" 16 | }, { 17 | "base": "Resource", 18 | "class": "Behavior", 19 | "language": "GDScript", 20 | "path": "res://addons/godot-next/resources/behavior.gd" 21 | }, { 22 | "base": "Reference", 23 | "class": "BitFlag", 24 | "language": "GDScript", 25 | "path": "res://addons/godot-next/references/bit_flag.gd" 26 | }, { 27 | "base": "Reference", 28 | "class": "Bitset", 29 | "language": "GDScript", 30 | "path": "res://addons/godot-next/references/bitset.gd" 31 | }, { 32 | "base": "Reference", 33 | "class": "CSVFile", 34 | "language": "GDScript", 35 | "path": "res://addons/godot-next/references/csv_file.gd" 36 | }, { 37 | "base": "Node", 38 | "class": "CallbackDelegator", 39 | "language": "GDScript", 40 | "path": "res://addons/godot-next/nodes/callback_delegator.gd" 41 | }, { 42 | "base": "Reference", 43 | "class": "ClassType", 44 | "language": "GDScript", 45 | "path": "res://addons/godot-next/references/class_type.gd" 46 | }, { 47 | "base": "TabContainer", 48 | "class": "Cycle", 49 | "language": "GDScript", 50 | "path": "res://addons/godot-next/gui/cycle.gd" 51 | }, { 52 | "base": "Label", 53 | "class": "DebugLabel", 54 | "language": "GDScript", 55 | "path": "res://addons/godot-next/gui/debug_label.gd" 56 | }, { 57 | "base": "ImageTexture", 58 | "class": "DiscreteGradientTexture", 59 | "language": "GDScript", 60 | "path": "res://addons/godot-next/resources/discrete_gradient_texture.gd" 61 | }, { 62 | "base": "Reference", 63 | "class": "EditorTools", 64 | "language": "GDScript", 65 | "path": "res://addons/godot-next/global/editor_tools.gd" 66 | }, { 67 | "base": "Reference", 68 | "class": "FileSearch", 69 | "language": "GDScript", 70 | "path": "res://addons/godot-next/global/file_search.gd" 71 | }, { 72 | "base": "Reference", 73 | "class": "FileSystemLink", 74 | "language": "GDScript", 75 | "path": "res://addons/godot-next/global/file_system_link.gd" 76 | }, { 77 | "base": "CollisionShape2D", 78 | "class": "Geometry2D", 79 | "language": "GDScript", 80 | "path": "res://addons/godot-next/2d/geometry_2d.gd" 81 | }, { 82 | "base": "Reference", 83 | "class": "Icons", 84 | "language": "GDScript", 85 | "path": "res://addons/godot-next/singletons/icons.gd" 86 | }, { 87 | "base": "Reference", 88 | "class": "Inflector", 89 | "language": "GDScript", 90 | "path": "res://addons/godot-next/references/inflector.gd" 91 | }, { 92 | "base": "Reference", 93 | "class": "InspectorControls", 94 | "language": "GDScript", 95 | "path": "res://addons/godot-next/global/inspector_controls.gd" 96 | }, { 97 | "base": "Reference", 98 | "class": "MessageDispatcher", 99 | "language": "GDScript", 100 | "path": "res://addons/godot-next/references/message_dispatcher.gd" 101 | }, { 102 | "base": "Node", 103 | "class": "MessageDispatcherWrapper", 104 | "language": "GDScript", 105 | "path": "res://addons/godot-next/nodes/message_dispatcher_wrapper.gd" 106 | }, { 107 | "base": "Resource", 108 | "class": "PhysicsLayers", 109 | "language": "GDScript", 110 | "path": "res://addons/godot-next/global/physics_layers.gd" 111 | }, { 112 | "base": "Reference", 113 | "class": "ProjectTools", 114 | "language": "GDScript", 115 | "path": "res://addons/godot-next/global/project_tools.gd" 116 | }, { 117 | "base": "Reference", 118 | "class": "PropertyInfo", 119 | "language": "GDScript", 120 | "path": "res://addons/godot-next/references/property_info.gd" 121 | }, { 122 | "base": "Reference", 123 | "class": "PropertyInfoFactory", 124 | "language": "GDScript", 125 | "path": "res://addons/godot-next/references/property_info_factory.gd" 126 | }, { 127 | "base": "ResourceCollection", 128 | "class": "ResourceArray", 129 | "language": "GDScript", 130 | "path": "res://addons/godot-next/resources/resource_collections/resource_array.gd" 131 | }, { 132 | "base": "Resource", 133 | "class": "ResourceCollection", 134 | "language": "GDScript", 135 | "path": "res://addons/godot-next/resources/resource_collections/resource_collection.gd" 136 | }, { 137 | "base": "ResourceCollection", 138 | "class": "ResourceSet", 139 | "language": "GDScript", 140 | "path": "res://addons/godot-next/resources/resource_collections/resource_set.gd" 141 | }, { 142 | "base": "Reference", 143 | "class": "Singletons", 144 | "language": "GDScript", 145 | "path": "res://addons/godot-next/global/singletons.gd" 146 | }, { 147 | "base": "Line2D", 148 | "class": "Trail2D", 149 | "language": "GDScript", 150 | "path": "res://addons/godot-next/2d/trail_2d.gd" 151 | }, { 152 | "base": "ImmediateGeometry", 153 | "class": "Trail3D", 154 | "language": "GDScript", 155 | "path": "res://addons/godot-next/3d/trail_3d.gd" 156 | }, { 157 | "base": "Reference", 158 | "class": "TweenSequence", 159 | "language": "GDScript", 160 | "path": "res://addons/godot-next/references/tween_sequence/tween_sequence.gd" 161 | }, { 162 | "base": "Reference", 163 | "class": "Tweener", 164 | "language": "GDScript", 165 | "path": "res://addons/godot-next/references/tween_sequence/tweener.gd" 166 | }, { 167 | "base": "VBoxContainer", 168 | "class": "VBoxItemList", 169 | "language": "GDScript", 170 | "path": "res://addons/godot-next/gui/v_box_item_list.gd" 171 | }, { 172 | "base": "Reference", 173 | "class": "Variant", 174 | "language": "GDScript", 175 | "path": "res://addons/godot-next/global/variant.gd" 176 | }, { 177 | "base": "Node", 178 | "class": "VectorDisplay2D", 179 | "language": "GDScript", 180 | "path": "res://addons/godot-next/2d/vector_display_2d.gd" 181 | }, { 182 | "base": "Node", 183 | "class": "VectorDisplay3D", 184 | "language": "GDScript", 185 | "path": "res://addons/godot-next/3d/vector_display_3d.gd" 186 | } ] 187 | _global_script_class_icons={ 188 | "Array2D": "", 189 | "Behavior": "", 190 | "BitFlag": "", 191 | "Bitset": "", 192 | "CSVFile": "", 193 | "CallbackDelegator": "", 194 | "ClassType": "", 195 | "Cycle": "res://addons/godot-next/icons/icon_cycle.svg", 196 | "DebugLabel": "", 197 | "DiscreteGradientTexture": "", 198 | "EditorTools": "", 199 | "FileSearch": "", 200 | "FileSystemLink": "", 201 | "Geometry2D": "res://addons/godot-next/icons/icon_geometry_2d.svg", 202 | "Icons": "", 203 | "Inflector": "", 204 | "InspectorControls": "", 205 | "MessageDispatcher": "", 206 | "MessageDispatcherWrapper": "", 207 | "PhysicsLayers": "", 208 | "ProjectTools": "", 209 | "PropertyInfo": "", 210 | "PropertyInfoFactory": "", 211 | "ResourceArray": "", 212 | "ResourceCollection": "", 213 | "ResourceSet": "", 214 | "Singletons": "", 215 | "Trail2D": "res://addons/godot-next/icons/icon_trail_2d.svg", 216 | "Trail3D": "res://addons/godot-next/icons/icon_trail_3d.svg", 217 | "TweenSequence": "", 218 | "Tweener": "", 219 | "VBoxItemList": "res://addons/godot-next/icons/icon_v_box_item_list.svg", 220 | "Variant": "", 221 | "VectorDisplay2D": "", 222 | "VectorDisplay3D": "" 223 | } 224 | 225 | [application] 226 | 227 | config/name="Godot Node Extensions" 228 | config/description="Godot Node Extensions, AKA Godot NExt. 229 | Contains many script classes that extend Godot with new functionality." 230 | run/main_scene="res://demo/demo.tscn" 231 | config/icon="res://icon.png" 232 | 233 | [editor_plugins] 234 | 235 | enabled=PoolStringArray( "godot-next" ) 236 | 237 | [rendering] 238 | 239 | quality/driver/driver_name="GLES2" 240 | vram_compression/import_etc=true 241 | vram_compression/import_etc2=false 242 | environment/default_environment="res://default_env.tres" 243 | --------------------------------------------------------------------------------