mirror of
https://github.com/Relintai/broken_seals.git
synced 2025-02-01 14:37:01 +01:00
Also added gdpose and godot-plugin-refresher addons.
This commit is contained in:
parent
32648325e0
commit
847640d35b
4
game/addons/.gitignore
vendored
4
game/addons/.gitignore
vendored
@ -3,4 +3,8 @@
|
|||||||
!module_manager/**
|
!module_manager/**
|
||||||
!Godoxel
|
!Godoxel
|
||||||
!Godoxel/**
|
!Godoxel/**
|
||||||
|
!godot-plugin-refresher
|
||||||
|
!godot-plugin-refresher/**
|
||||||
|
!gdpose
|
||||||
|
!gdpose/**
|
||||||
!addon_versions
|
!addon_versions
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
Godotxel 6e3b61c2887e51c767d0c2417e0b293a35f904ac from https://github.com/aaronfranke/GraphicsEditor
|
Godotxel 6e3b61c2887e51c767d0c2417e0b293a35f904ac from https://github.com/aaronfranke/GraphicsEditor
|
||||||
|
gdpose ba80c648b04d5dc416f69d1d3bac280a2e02b3a0 from https://github.com/BastiaanOlij/gdpose
|
||||||
|
godot-plugin-refresher e2b12f448b9fc112fd0f4acdc6de8df6180b464e from https://github.com/godot-extended-libraries/godot-plugin-refresher
|
||||||
|
|
||||||
|
21
game/addons/gdpose/LICENSE
Normal file
21
game/addons/gdpose/LICENSE
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2020 Bastiaan Olij
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
23
game/addons/gdpose/README.md
Normal file
23
game/addons/gdpose/README.md
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
# gdpose
|
||||||
|
Plugin to make skeletons poseable in the editor
|
||||||
|
|
||||||
|
Note: this is still a work in progress
|
||||||
|
|
||||||
|
For more info see my streams on Youtube:
|
||||||
|
- [Stream #16](https://youtu.be/LtJCLWorenc)
|
||||||
|
- [Stream #17](https://youtu.be/zFVmMPe9Skc)
|
||||||
|
|
||||||
|
# TODOs
|
||||||
|
Minimum work needed to make this more feasible:
|
||||||
|
- Add undo feature
|
||||||
|
- Improve bone selection menu
|
||||||
|
- Add option to add keyframe for selected bone
|
||||||
|
|
||||||
|
Nice to figure out:
|
||||||
|
- Make it possible to select bones by clicking on them
|
||||||
|
- Make a better widget for rotations
|
||||||
|
- Add code to determine correct direction of rotation
|
||||||
|
|
||||||
|
# License
|
||||||
|
This project is provided under an MIT license.
|
||||||
|
See LICENSE for further details.
|
32
game/addons/gdpose/SkeletonPopup.gd
Normal file
32
game/addons/gdpose/SkeletonPopup.gd
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
tool
|
||||||
|
extends MenuButton
|
||||||
|
|
||||||
|
var skeleton : Skeleton = null
|
||||||
|
|
||||||
|
func edit(new_skeleton : Skeleton):
|
||||||
|
skeleton = new_skeleton
|
||||||
|
|
||||||
|
# update our menu
|
||||||
|
var popup = get_popup()
|
||||||
|
if popup:
|
||||||
|
popup.clear()
|
||||||
|
|
||||||
|
if skeleton:
|
||||||
|
for idx in range(0, skeleton.get_bone_count()):
|
||||||
|
var parent = skeleton.get_bone_parent(idx)
|
||||||
|
if parent != -1:
|
||||||
|
var name = skeleton.get_bone_name(idx)
|
||||||
|
popup.add_item(name, idx)
|
||||||
|
|
||||||
|
func select_bone(id):
|
||||||
|
print("select bone " + str(id))
|
||||||
|
|
||||||
|
var gizmo : BoneSpatialGizmo = skeleton.gizmo
|
||||||
|
if gizmo:
|
||||||
|
gizmo.set_selected_bone(id)
|
||||||
|
skeleton.update_gizmo()
|
||||||
|
|
||||||
|
func _ready():
|
||||||
|
var popup = get_popup()
|
||||||
|
if popup:
|
||||||
|
popup.connect("id_pressed", self, "select_bone")
|
11
game/addons/gdpose/SkeletonPopup.tscn
Normal file
11
game/addons/gdpose/SkeletonPopup.tscn
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
[gd_scene load_steps=2 format=2]
|
||||||
|
|
||||||
|
[ext_resource path="res://addons/gdpose/SkeletonPopup.gd" type="Script" id=1]
|
||||||
|
|
||||||
|
[node name="SkeletonPopup" type="MenuButton"]
|
||||||
|
text = "GDPose"
|
||||||
|
items = [ "Test1", null, 0, false, false, 0, 0, null, "", false, "Test2", null, 0, false, false, 1, 0, null, "", false ]
|
||||||
|
script = ExtResource( 1 )
|
||||||
|
__meta__ = {
|
||||||
|
"_edit_use_anchors_": false
|
||||||
|
}
|
179
game/addons/gdpose/bonegizmo.gd
Normal file
179
game/addons/gdpose/bonegizmo.gd
Normal file
@ -0,0 +1,179 @@
|
|||||||
|
tool
|
||||||
|
extends EditorSpatialGizmo
|
||||||
|
|
||||||
|
class_name BoneSpatialGizmo
|
||||||
|
|
||||||
|
var offset = 0.1
|
||||||
|
var handle_dist = 0.1 # Distance of gizmo to bone origin
|
||||||
|
var selected_bone = -1 # Bone we are animating
|
||||||
|
|
||||||
|
var is_posing = false
|
||||||
|
var start_pose = Transform()
|
||||||
|
var start_point = Vector2()
|
||||||
|
|
||||||
|
var handle_idx = Array() # Handles we've added
|
||||||
|
|
||||||
|
const handle_rot_x = -2
|
||||||
|
const handle_rot_y = -3
|
||||||
|
const handle_rot_z = -4
|
||||||
|
|
||||||
|
func set_selected_bone(idx):
|
||||||
|
var skeleton : Skeleton = get_spatial_node()
|
||||||
|
if !skeleton:
|
||||||
|
return
|
||||||
|
|
||||||
|
if idx >= 0 and idx < skeleton.get_bone_count():
|
||||||
|
selected_bone = idx
|
||||||
|
else:
|
||||||
|
selected_bone = -1
|
||||||
|
|
||||||
|
func get_handle_name(index):
|
||||||
|
var skeleton : Skeleton = get_spatial_node()
|
||||||
|
if !skeleton:
|
||||||
|
return "No skeleton"
|
||||||
|
|
||||||
|
var idx = handle_idx[index]
|
||||||
|
if idx == handle_rot_x:
|
||||||
|
return "Rotate X"
|
||||||
|
elif idx == handle_rot_y:
|
||||||
|
return "Rotate Y"
|
||||||
|
elif idx == handle_rot_z:
|
||||||
|
return "Rotate Z"
|
||||||
|
else:
|
||||||
|
return "huh???"
|
||||||
|
|
||||||
|
func get_handle_value(index):
|
||||||
|
var skeleton : Skeleton = get_spatial_node()
|
||||||
|
if !skeleton:
|
||||||
|
return "No skeleton"
|
||||||
|
|
||||||
|
var idx = handle_idx[index]
|
||||||
|
if selected_bone >= 0:
|
||||||
|
return skeleton.get_bone_pose(selected_bone)
|
||||||
|
else:
|
||||||
|
return "No selected bone"
|
||||||
|
|
||||||
|
func set_handle(index, camera, point):
|
||||||
|
var idx = handle_idx[index]
|
||||||
|
var skeleton : Skeleton = get_spatial_node()
|
||||||
|
if !skeleton:
|
||||||
|
return "No skeleton"
|
||||||
|
|
||||||
|
if !is_posing:
|
||||||
|
start_point = point
|
||||||
|
start_pose = skeleton.get_bone_pose(selected_bone)
|
||||||
|
is_posing = true
|
||||||
|
print("start posing " + str(start_pose))
|
||||||
|
return
|
||||||
|
|
||||||
|
var moved = point - start_point
|
||||||
|
var distance = moved.x + moved.y
|
||||||
|
|
||||||
|
# calculate our new transform (in local space)
|
||||||
|
var new_pose : Transform = start_pose
|
||||||
|
if idx == handle_rot_x:
|
||||||
|
# rotate around X
|
||||||
|
new_pose = new_pose.rotated(start_pose.basis.x.normalized(), distance * 0.01)
|
||||||
|
elif idx == handle_rot_y:
|
||||||
|
# rotate around Y
|
||||||
|
new_pose = new_pose.rotated(start_pose.basis.y.normalized(), distance * 0.01)
|
||||||
|
elif idx == handle_rot_z:
|
||||||
|
# rotate around Z
|
||||||
|
new_pose = new_pose.rotated(start_pose.basis.z.normalized(), distance * 0.01)
|
||||||
|
|
||||||
|
skeleton.set_bone_pose(selected_bone, new_pose)
|
||||||
|
skeleton.update_gizmo()
|
||||||
|
|
||||||
|
func commit_handle(index, restore, cancel = false):
|
||||||
|
# var idx = handle_idx[index]
|
||||||
|
|
||||||
|
print("Commit")
|
||||||
|
is_posing = false
|
||||||
|
|
||||||
|
var skeleton : Skeleton = get_spatial_node()
|
||||||
|
if !skeleton:
|
||||||
|
return
|
||||||
|
|
||||||
|
if selected_bone == -1:
|
||||||
|
return
|
||||||
|
|
||||||
|
if (cancel):
|
||||||
|
skeleton.set_bone_pose(selected_bone, restore)
|
||||||
|
|
||||||
|
func redraw():
|
||||||
|
clear()
|
||||||
|
|
||||||
|
var skeleton : Skeleton = get_spatial_node()
|
||||||
|
if !skeleton:
|
||||||
|
return
|
||||||
|
|
||||||
|
var lines_material = get_plugin().get_material("skeleton", self)
|
||||||
|
var selected_material = get_plugin().get_material("selected", self)
|
||||||
|
var handles_material = get_plugin().get_material("handles", self)
|
||||||
|
var handles = PoolVector3Array()
|
||||||
|
handle_idx.clear()
|
||||||
|
|
||||||
|
# loop through our bones
|
||||||
|
for idx in range(0, skeleton.get_bone_count()):
|
||||||
|
var parent = skeleton.get_bone_parent(idx)
|
||||||
|
if parent != -1:
|
||||||
|
var lines = PoolVector3Array()
|
||||||
|
var parent_transform = skeleton.get_bone_global_pose(parent)
|
||||||
|
var bone_transform = skeleton.get_bone_global_pose(idx)
|
||||||
|
|
||||||
|
var parent_pos = parent_transform.origin
|
||||||
|
var bone_pos = bone_transform.origin
|
||||||
|
var delta = bone_pos - parent_pos
|
||||||
|
var length = delta.length()
|
||||||
|
|
||||||
|
var p1 = parent_pos + (delta * offset) + parent_transform.basis.x * length * offset
|
||||||
|
var p2 = parent_pos + (delta * offset) + parent_transform.basis.z * length * offset
|
||||||
|
var p3 = parent_pos + (delta * offset) - parent_transform.basis.x * length * offset
|
||||||
|
var p4 = parent_pos + (delta * offset) - parent_transform.basis.z * length * offset
|
||||||
|
|
||||||
|
lines.push_back(parent_pos)
|
||||||
|
lines.push_back(p1)
|
||||||
|
lines.push_back(p1)
|
||||||
|
lines.push_back(bone_pos)
|
||||||
|
|
||||||
|
lines.push_back(parent_pos)
|
||||||
|
lines.push_back(p2)
|
||||||
|
lines.push_back(p2)
|
||||||
|
lines.push_back(bone_pos)
|
||||||
|
|
||||||
|
lines.push_back(parent_pos)
|
||||||
|
lines.push_back(p3)
|
||||||
|
lines.push_back(p3)
|
||||||
|
lines.push_back(bone_pos)
|
||||||
|
|
||||||
|
lines.push_back(parent_pos)
|
||||||
|
lines.push_back(p4)
|
||||||
|
lines.push_back(p4)
|
||||||
|
lines.push_back(bone_pos)
|
||||||
|
|
||||||
|
lines.push_back(p1)
|
||||||
|
lines.push_back(p2)
|
||||||
|
lines.push_back(p2)
|
||||||
|
lines.push_back(p3)
|
||||||
|
lines.push_back(p3)
|
||||||
|
lines.push_back(p4)
|
||||||
|
lines.push_back(p4)
|
||||||
|
lines.push_back(p1)
|
||||||
|
|
||||||
|
if parent == selected_bone:
|
||||||
|
add_lines(lines, selected_material, false)
|
||||||
|
else:
|
||||||
|
add_lines(lines, lines_material, false)
|
||||||
|
|
||||||
|
if idx == selected_bone:
|
||||||
|
handles.push_back(bone_pos + bone_transform.basis.x * handle_dist)
|
||||||
|
handle_idx.push_back(handle_rot_x)
|
||||||
|
|
||||||
|
handles.push_back(bone_pos + bone_transform.basis.y * handle_dist)
|
||||||
|
handle_idx.push_back(handle_rot_y)
|
||||||
|
|
||||||
|
handles.push_back(bone_pos + bone_transform.basis.z * handle_dist)
|
||||||
|
handle_idx.push_back(handle_rot_z)
|
||||||
|
|
||||||
|
if handles.size() > 0:
|
||||||
|
add_handles(handles, handles_material)
|
22
game/addons/gdpose/bonegizmoplugin.gd
Normal file
22
game/addons/gdpose/bonegizmoplugin.gd
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
tool
|
||||||
|
extends EditorSpatialGizmoPlugin
|
||||||
|
|
||||||
|
const BoneGizmo = preload("res://addons/gdpose/bonegizmo.gd")
|
||||||
|
|
||||||
|
func get_name():
|
||||||
|
return "BoneGizmo"
|
||||||
|
|
||||||
|
func get_priority():
|
||||||
|
return 100
|
||||||
|
|
||||||
|
func create_gizmo(spatial):
|
||||||
|
if spatial is Skeleton:
|
||||||
|
return BoneGizmo.new()
|
||||||
|
else:
|
||||||
|
return null
|
||||||
|
|
||||||
|
func _init():
|
||||||
|
create_material("skeleton", Color(0.6, 0.6, 0.0))
|
||||||
|
create_material("selected", Color(1.0, 1.0, 0.0))
|
||||||
|
create_handle_material("handles")
|
||||||
|
|
7
game/addons/gdpose/plugin.cfg
Normal file
7
game/addons/gdpose/plugin.cfg
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
[plugin]
|
||||||
|
|
||||||
|
name="gdpose"
|
||||||
|
description="Pose editor"
|
||||||
|
author="Bastiaan Olij"
|
||||||
|
version="1"
|
||||||
|
script="plugin.gd"
|
39
game/addons/gdpose/plugin.gd
Normal file
39
game/addons/gdpose/plugin.gd
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
tool
|
||||||
|
extends EditorPlugin
|
||||||
|
|
||||||
|
const BoneGizmoPlugin = preload("res://addons/gdpose/bonegizmoplugin.gd")
|
||||||
|
const SkeletonPopup = preload("res://addons/gdpose/SkeletonPopup.tscn")
|
||||||
|
|
||||||
|
var skeleton_popup_instance
|
||||||
|
var bone_gizmo = BoneGizmoPlugin.new()
|
||||||
|
|
||||||
|
func get_plugin_name():
|
||||||
|
return "GD Pose Plugin"
|
||||||
|
|
||||||
|
func _enter_tree():
|
||||||
|
# Initialization of the plugin goes here.
|
||||||
|
add_spatial_gizmo_plugin(bone_gizmo)
|
||||||
|
|
||||||
|
# add our menu
|
||||||
|
skeleton_popup_instance = SkeletonPopup.instance()
|
||||||
|
add_control_to_container(EditorPlugin.CONTAINER_SPATIAL_EDITOR_MENU, skeleton_popup_instance)
|
||||||
|
skeleton_popup_instance.visible = false
|
||||||
|
|
||||||
|
func _exit_tree():
|
||||||
|
remove_spatial_gizmo_plugin(bone_gizmo)
|
||||||
|
if skeleton_popup_instance:
|
||||||
|
remove_control_from_container(EditorPlugin.CONTAINER_SPATIAL_EDITOR_MENU, skeleton_popup_instance)
|
||||||
|
skeleton_popup_instance.queue_free()
|
||||||
|
skeleton_popup_instance = null
|
||||||
|
|
||||||
|
func make_visible(visible):
|
||||||
|
if skeleton_popup_instance:
|
||||||
|
skeleton_popup_instance.visible = visible
|
||||||
|
|
||||||
|
func handles(object):
|
||||||
|
return object is Skeleton
|
||||||
|
|
||||||
|
func edit(object):
|
||||||
|
var skeleton : Skeleton = object
|
||||||
|
if skeleton_popup_instance:
|
||||||
|
skeleton_popup_instance.edit(skeleton)
|
21
game/addons/godot-plugin-refresher/LICENSE
Normal file
21
game/addons/godot-plugin-refresher/LICENSE
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2018 Will Nations
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
24
game/addons/godot-plugin-refresher/README.md
Normal file
24
game/addons/godot-plugin-refresher/README.md
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
# godot-plugin-refresher
|
||||||
|
|
||||||
|
This plugin simplifies plugin development for those wanting to make tools for the Godot Editor.
|
||||||
|
|
||||||
|
It adds a dropdown and refresh button to the toolbar. *Other* plugins' directory names will show up in the dropdown, and clicking the refresh button will simply toggle the plugin off and then back on.
|
||||||
|
|
||||||
|
This makes it much easier to iterate on a single plugin since rather than having to...
|
||||||
|
|
||||||
|
1. Click Project Settings.
|
||||||
|
2. Go to Plugins tab (first time).
|
||||||
|
3. Find the desired plugin.
|
||||||
|
4. Click the dropdown.
|
||||||
|
5. Select the opposite option.
|
||||||
|
6. Click the dropdown again.
|
||||||
|
7. Click the original option.
|
||||||
|
8. Close the Project Settings.
|
||||||
|
|
||||||
|
You instead just...
|
||||||
|
|
||||||
|
1. Click the dropdown (first time).
|
||||||
|
2. Select your WIP plugin (first time).
|
||||||
|
3. Click the refresh button.
|
||||||
|
|
||||||
|
Please consider starring the repo if you like the project and let me know if you have any feedback for bugs / feature improvements in the Issues. If you'd like to support my work, please [send tips to my Kofi](https://ko-fi.com/willnationsdev). Cheers!
|
7
game/addons/godot-plugin-refresher/plugin.cfg
Normal file
7
game/addons/godot-plugin-refresher/plugin.cfg
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
[plugin]
|
||||||
|
|
||||||
|
name="Godot Plugin Refresher"
|
||||||
|
description="A toolbar addition to facilitate toggling off/on a selected plugin."
|
||||||
|
author="willnationsdev"
|
||||||
|
version="1.0"
|
||||||
|
script="plugin_refresher_plugin.gd"
|
53
game/addons/godot-plugin-refresher/plugin_refresher.gd
Normal file
53
game/addons/godot-plugin-refresher/plugin_refresher.gd
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
tool
|
||||||
|
extends HBoxContainer
|
||||||
|
|
||||||
|
signal request_refresh_plugin(p_name)
|
||||||
|
signal confirm_refresh_plugin(p_name)
|
||||||
|
|
||||||
|
onready var options = $OptionButton
|
||||||
|
|
||||||
|
func _ready():
|
||||||
|
$RefreshButton.icon = get_icon('Reload', 'EditorIcons')
|
||||||
|
|
||||||
|
func update_items(p_plugins):
|
||||||
|
if not options:
|
||||||
|
return
|
||||||
|
options.clear()
|
||||||
|
var plugin_dirs = p_plugins.keys()
|
||||||
|
for idx in plugin_dirs.size():
|
||||||
|
var plugin_dirname = plugin_dirs[idx]
|
||||||
|
var plugin_name = p_plugins[plugin_dirname]
|
||||||
|
options.add_item(plugin_name, idx)
|
||||||
|
options.set_item_metadata(idx, plugin_dirname)
|
||||||
|
|
||||||
|
func select_plugin(p_name):
|
||||||
|
if not options:
|
||||||
|
return
|
||||||
|
if p_name == null or p_name.empty():
|
||||||
|
return
|
||||||
|
|
||||||
|
for idx in options.get_item_count():
|
||||||
|
var plugin = options.get_item_metadata(idx)
|
||||||
|
if plugin == p_name:
|
||||||
|
options.selected = options.get_item_id(idx)
|
||||||
|
break
|
||||||
|
|
||||||
|
func _on_RefreshButton_pressed():
|
||||||
|
if options.selected == -1:
|
||||||
|
return # nothing selected
|
||||||
|
|
||||||
|
var plugin = options.get_item_metadata(options.selected)
|
||||||
|
if not plugin or plugin.empty():
|
||||||
|
return
|
||||||
|
emit_signal("request_refresh_plugin", plugin)
|
||||||
|
|
||||||
|
func show_warning(p_name):
|
||||||
|
$ConfirmationDialog.dialog_text = """
|
||||||
|
Plugin `%s` is currently disabled.\n
|
||||||
|
Do you want to enable it now?
|
||||||
|
""" % [p_name]
|
||||||
|
$ConfirmationDialog.popup_centered()
|
||||||
|
|
||||||
|
func _on_ConfirmationDialog_confirmed():
|
||||||
|
var plugin = options.get_item_metadata(options.selected)
|
||||||
|
emit_signal('confirm_refresh_plugin', plugin)
|
32
game/addons/godot-plugin-refresher/plugin_refresher.tscn
Normal file
32
game/addons/godot-plugin-refresher/plugin_refresher.tscn
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
[gd_scene load_steps=2 format=2]
|
||||||
|
|
||||||
|
[ext_resource path="res://addons/godot-plugin-refresher/plugin_refresher.gd" type="Script" id=1]
|
||||||
|
|
||||||
|
[node name="HBoxContainer" type="HBoxContainer"]
|
||||||
|
margin_right = 40.0
|
||||||
|
margin_bottom = 40.0
|
||||||
|
script = ExtResource( 1 )
|
||||||
|
|
||||||
|
[node name="VSeparator" type="VSeparator" parent="."]
|
||||||
|
margin_right = 4.0
|
||||||
|
margin_bottom = 40.0
|
||||||
|
|
||||||
|
[node name="OptionButton" type="OptionButton" parent="."]
|
||||||
|
margin_left = 8.0
|
||||||
|
margin_right = 158.0
|
||||||
|
margin_bottom = 40.0
|
||||||
|
rect_min_size = Vector2( 150, 0 )
|
||||||
|
|
||||||
|
[node name="RefreshButton" type="ToolButton" parent="."]
|
||||||
|
margin_left = 162.0
|
||||||
|
margin_right = 174.0
|
||||||
|
margin_bottom = 40.0
|
||||||
|
|
||||||
|
[node name="ConfirmationDialog" type="ConfirmationDialog" parent="."]
|
||||||
|
margin_right = 278.0
|
||||||
|
margin_bottom = 110.0
|
||||||
|
rect_min_size = Vector2( 300, 70 )
|
||||||
|
window_title = "Plugin Refresher"
|
||||||
|
dialog_autowrap = true
|
||||||
|
[connection signal="pressed" from="RefreshButton" to="." method="_on_RefreshButton_pressed"]
|
||||||
|
[connection signal="confirmed" from="ConfirmationDialog" to="." method="_on_ConfirmationDialog_confirmed"]
|
125
game/addons/godot-plugin-refresher/plugin_refresher_plugin.gd
Normal file
125
game/addons/godot-plugin-refresher/plugin_refresher_plugin.gd
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
tool
|
||||||
|
extends EditorPlugin
|
||||||
|
|
||||||
|
const ADDONS_PATH = "res://addons/"
|
||||||
|
const PLUGIN_CONFIG_DIR = 'plugins/plugin_refresher'
|
||||||
|
const PLUGIN_CONFIG = 'settings.cfg'
|
||||||
|
const SETTINGS = 'settings'
|
||||||
|
const SETTING_RECENT = 'recently_used'
|
||||||
|
|
||||||
|
var plugin_config = ConfigFile.new()
|
||||||
|
var refresher
|
||||||
|
|
||||||
|
func _enter_tree():
|
||||||
|
refresher = preload("plugin_refresher.tscn").instance()
|
||||||
|
add_control_to_container(CONTAINER_TOOLBAR, refresher)
|
||||||
|
|
||||||
|
# Watch whether any plugin is changed, added or removed on the filesystem
|
||||||
|
var efs = get_editor_interface().get_resource_filesystem()
|
||||||
|
efs.connect("filesystem_changed", self, "_on_filesystem_changed")
|
||||||
|
|
||||||
|
refresher.connect("request_refresh_plugin", self, "_on_request_refresh_plugin")
|
||||||
|
refresher.connect("confirm_refresh_plugin", self, "_on_confirm_refresh_plugin")
|
||||||
|
|
||||||
|
_reload_plugins_list()
|
||||||
|
_load_settings()
|
||||||
|
|
||||||
|
func _exit_tree():
|
||||||
|
remove_control_from_container(CONTAINER_TOOLBAR, refresher)
|
||||||
|
refresher.free()
|
||||||
|
|
||||||
|
func _reload_plugins_list():
|
||||||
|
var refresher_dir = get_plugin_path().get_file()
|
||||||
|
var plugins = {}
|
||||||
|
var origins = {}
|
||||||
|
|
||||||
|
var dir = Directory.new()
|
||||||
|
dir.open(ADDONS_PATH)
|
||||||
|
dir.list_dir_begin(true, true)
|
||||||
|
var file = dir.get_next()
|
||||||
|
while file:
|
||||||
|
var addon_dir = ADDONS_PATH.plus_file(file)
|
||||||
|
if dir.dir_exists(addon_dir) and file != refresher_dir:
|
||||||
|
var display_name = file
|
||||||
|
var plugin_config_path = addon_dir.plus_file("plugin.cfg")
|
||||||
|
if not dir.file_exists(plugin_config_path):
|
||||||
|
continue # not a plugin
|
||||||
|
var plugin_cfg = ConfigFile.new()
|
||||||
|
plugin_cfg.load(plugin_config_path)
|
||||||
|
display_name = plugin_cfg.get_value("plugin", "name", file)
|
||||||
|
if not display_name in origins:
|
||||||
|
origins[display_name] = [file]
|
||||||
|
else:
|
||||||
|
origins[display_name].append(file)
|
||||||
|
plugins[file] = display_name
|
||||||
|
file = dir.get_next()
|
||||||
|
|
||||||
|
# Specify the exact plugin name in parenthesis in case of naming collisions.
|
||||||
|
for display_name in origins:
|
||||||
|
var plugin_names = origins[display_name]
|
||||||
|
if plugin_names.size() > 1:
|
||||||
|
for n in plugin_names:
|
||||||
|
plugins[n] = "%s (%s)" % [display_name, n]
|
||||||
|
|
||||||
|
refresher.update_items(plugins)
|
||||||
|
|
||||||
|
func _load_settings():
|
||||||
|
var path = get_config_path()
|
||||||
|
|
||||||
|
var fs = Directory.new()
|
||||||
|
if not fs.file_exists(path):
|
||||||
|
# Create new if running for the first time
|
||||||
|
var config = ConfigFile.new()
|
||||||
|
fs.make_dir_recursive(path.get_base_dir())
|
||||||
|
config.save(path)
|
||||||
|
else:
|
||||||
|
plugin_config.load(path)
|
||||||
|
|
||||||
|
func _save_settings():
|
||||||
|
plugin_config.save(get_config_path())
|
||||||
|
|
||||||
|
func get_config_path():
|
||||||
|
var dir = get_editor_interface().get_editor_settings().get_project_settings_dir()
|
||||||
|
var home = dir.plus_file(PLUGIN_CONFIG_DIR)
|
||||||
|
var path = home.plus_file(PLUGIN_CONFIG)
|
||||||
|
|
||||||
|
return path
|
||||||
|
|
||||||
|
func _on_filesystem_changed():
|
||||||
|
if refresher:
|
||||||
|
_reload_plugins_list()
|
||||||
|
refresher.select_plugin(get_recent_plugin())
|
||||||
|
|
||||||
|
func get_recent_plugin():
|
||||||
|
if not plugin_config.has_section_key(SETTINGS, SETTING_RECENT):
|
||||||
|
return null # not saved yet
|
||||||
|
|
||||||
|
var recent = plugin_config.get_value(SETTINGS, SETTING_RECENT)
|
||||||
|
return recent
|
||||||
|
|
||||||
|
func _on_request_refresh_plugin(p_name):
|
||||||
|
assert(not p_name.empty())
|
||||||
|
|
||||||
|
var disabled = not get_editor_interface().is_plugin_enabled(p_name)
|
||||||
|
if disabled:
|
||||||
|
refresher.show_warning(p_name)
|
||||||
|
else:
|
||||||
|
refresh_plugin(p_name)
|
||||||
|
|
||||||
|
func _on_confirm_refresh_plugin(p_name):
|
||||||
|
refresh_plugin(p_name)
|
||||||
|
|
||||||
|
func get_plugin_path():
|
||||||
|
return get_script().resource_path.get_base_dir()
|
||||||
|
|
||||||
|
func refresh_plugin(p_name):
|
||||||
|
print("Refreshing plugin: ", p_name)
|
||||||
|
|
||||||
|
var enabled = get_editor_interface().is_plugin_enabled(p_name)
|
||||||
|
if enabled: # can only disable an active plugin
|
||||||
|
get_editor_interface().set_plugin_enabled(p_name, false)
|
||||||
|
|
||||||
|
get_editor_interface().set_plugin_enabled(p_name, true)
|
||||||
|
|
||||||
|
plugin_config.set_value(SETTINGS, SETTING_RECENT, p_name)
|
||||||
|
_save_settings()
|
Loading…
Reference in New Issue
Block a user