Camera Locking Update

This commit is contained in:
Rokojori 2026-10-07 10:29:15 +02:00
parent f472d031a8
commit e2a1171e5e
12 changed files with 306 additions and 5 deletions

View File

@ -91,12 +91,12 @@ public partial class RokojoriPlugin: EditorPlugin
// return _instance;
// }
public override void _EnablePlugin()
public void EnablePlugin()
{
this.LogInfo();
AddAutoloadSingleton( RokojoriRootAutoLoad, RokojoriRootAutoLoadPath );
EnsureHiddenProjectPath( RokojoriProjectInternalPath );
EnsureHiddenProjectPath( RokojoriSettingsPath );
EnsureHiddenProjectPath( RokojoriCachePath );
@ -147,7 +147,7 @@ public partial class RokojoriPlugin: EditorPlugin
}
public override void _DisablePlugin()
public void DisablePlugin()
{
this.LogInfo();
RemoveAutoloadSingleton( RokojoriRootAutoLoad );

View File

@ -63,11 +63,16 @@ namespace Rokojori
// return;
// }
if ( target == null )
if ( target == null || ! IsInstanceValid( target ) )
{
return;
}
if ( lockTarget != null && ! IsInstanceValid( lockTarget ) )
{
lockTarget = null;
}
// var tl = TimeLineManager.Ensure( timeLine );
// var delta = tl.delta;

View File

@ -65,6 +65,18 @@ namespace Rokojori
return;
}
if ( pointable != null && ! IsInstanceValid( pointable ) )
{
pointable = null;
if ( engageSensor.isActive && engageSensor is ActivationModeSensor ams )
{
ams.Release();
}
onLockReleased?.Trigger();
}
UpdateClosestCandidate();
var wasLocked = pointable != null;

View File

@ -0,0 +1,194 @@
#if TOOLS
using Godot;
using System.Collections.Generic;
namespace Rokojori.Tools
{
using Rokojori;
// Like Node.Duplicate(), but [Export] Node/array/list members that point outside the
// duplicated subtree (parent, siblings, cousins, ...) keep pointing at the original
// external target instead of being nulled out. Members pointing inside the subtree are
// remapped to the corresponding duplicated node, same as native duplicate already does.
public static class NodeDuplicator
{
public static Node DuplicateDeep( Node original )
{
if ( original == null )
{
return null;
}
var parent = original.GetParent();
if ( parent == null )
{
original.LogError( "Duplicate Deep needs a parent node, can't duplicate the scene root." );
return null;
}
var duplicate = original.Duplicate( (int) Node.DuplicateFlags.Default );
parent.AddChild( duplicate );
parent.MoveChild( duplicate, original.GetIndex() + 1 );
SetOwnerRecursively( duplicate, original.Owner );
RemapExternalReferences( original, duplicate );
duplicate.LogInfo( "Duplicated", original.Name, "as", duplicate.Name );
return duplicate;
}
static void SetOwnerRecursively( Node node, Node sceneOwner )
{
if ( sceneOwner == null )
{
return;
}
node.Owner = sceneOwner;
foreach ( Node child in node.GetChildren() )
{
SetOwnerRecursively( child, sceneOwner );
}
}
static void RemapExternalReferences( Node originalRoot, Node duplicateRoot )
{
var originalNodes = new List<Node>();
var duplicateNodes = new List<Node>();
NodesWalker.Get().Iterate( originalRoot, n => originalNodes.Add( n ), false );
NodesWalker.Get().Iterate( duplicateRoot, n => duplicateNodes.Add( n ), false );
var originalToDuplicate = new Dictionary<Node, Node>();
for ( var i = 0; i < originalNodes.Count && i < duplicateNodes.Count; i++ )
{
originalToDuplicate[ originalNodes[ i ] ] = duplicateNodes[ i ];
}
for ( var i = 0; i < originalNodes.Count; i++ )
{
RemapNodeExports( originalNodes[ i ], duplicateNodes[ i ], originalToDuplicate );
}
}
static void RemapNodeExports( Node original, Node duplicate, Dictionary<Node, Node> map )
{
var properties = original.GetPropertyList();
foreach ( var propertyInfo in properties )
{
var dict = (Godot.Collections.Dictionary) propertyInfo;
if ( ! IsStored( dict ) )
{
continue;
}
var name = dict[ "name" ].AsString();
if ( string.IsNullOrEmpty( name ) )
{
continue;
}
var type = (Variant.Type) dict[ "type" ].AsInt32();
if ( type == Variant.Type.Object )
{
RemapSingleProperty( original, duplicate, name, map );
}
else if ( type == Variant.Type.Array )
{
RemapArrayProperty( original, duplicate, name, map );
}
}
}
static bool IsStored( Godot.Collections.Dictionary propertyInfo )
{
if ( ! propertyInfo.ContainsKey( "usage" ) )
{
return false;
}
var usage = propertyInfo[ "usage" ].AsInt64();
return ( usage & (long) PropertyUsageFlags.Storage ) != 0;
}
static void RemapSingleProperty( Node original, Node duplicate, string name, Dictionary<Node, Node> map )
{
var value = original.Get( name );
if ( value.VariantType != Variant.Type.Object )
{
return;
}
if ( value.AsGodotObject() is not Node targetNode )
{
return;
}
var remapped = Remap( targetNode, map );
if ( remapped != targetNode )
{
duplicate.Set( name, remapped );
}
}
static void RemapArrayProperty( Node original, Node duplicate, string name, Dictionary<Node, Node> map )
{
var value = original.Get( name );
if ( value.VariantType != Variant.Type.Array )
{
return;
}
var array = value.AsGodotArray();
var changed = false;
for ( var i = 0; i < array.Count; i++ )
{
var element = array[ i ];
if ( element.VariantType != Variant.Type.Object || element.AsGodotObject() is not Node elementNode )
{
continue;
}
var remapped = Remap( elementNode, map );
if ( remapped != elementNode )
{
array[ i ] = remapped;
changed = true;
}
}
if ( changed )
{
duplicate.Set( name, array );
}
}
static Node Remap( Node originalTarget, Dictionary<Node, Node> map )
{
if ( map.TryGetValue( originalTarget, out var mapped ) )
{
return mapped;
}
return originalTarget;
}
}
}
#endif

View File

@ -0,0 +1 @@
uid://bigysvcanctvn

View File

@ -0,0 +1,24 @@
#if TOOLS
using Godot;
namespace Rokojori.Tools
{
using Rokojori;
[Tool]
public partial class RJ_EditorBridge : RefCounted
{
public void LogHello( Node node )
{
node.LogInfo( "Hello" );
}
public void DuplicateDeep( Node node )
{
NodeDuplicator.DuplicateDeep( node );
}
}
}
#endif

View File

@ -0,0 +1 @@
uid://b83jwjb1m4uae

View File

@ -0,0 +1,21 @@
@tool
extends EditorContextMenuPlugin
const EditorBridgeScript = preload( "res://addons/rokojori_action_library/Tools/Editor/RJ_EditorBridge.cs" )
var bridge = EditorBridgeScript.new()
func _popup_menu( paths:PackedStringArray ) -> void:
add_context_menu_item( "Log Hello", _on_log_hello )
add_context_menu_item( "Duplicate Deep", _on_duplicate_deep )
func _on_log_hello( nodes:Array ) -> void:
for node in nodes:
bridge.LogHello( node )
func _on_duplicate_deep( nodes:Array ) -> void:
for node in nodes:
bridge.DuplicateDeep( node )

View File

@ -0,0 +1 @@
uid://bgopgmdisn1df

View File

@ -4,4 +4,4 @@ name="Rokojori Action Library"
description="Library for actions, assets and effects"
author="Rokojori"
version="0.6"
script="RokojoriPlugin.cs"
script="plugin.gd"

41
plugin.gd Normal file
View File

@ -0,0 +1,41 @@
@tool
extends EditorPlugin
const RokojoriPluginScript = preload( "res://addons/rokojori_action_library/RokojoriPlugin.cs" )
const NodeContextMenuScript = preload( "res://addons/rokojori_action_library/Tools/Editor/RJ_NodeContextMenu.gd" )
var csPlugin
var nodeContextMenu
func _ensure_cs_plugin() -> void:
if csPlugin == null:
csPlugin = RokojoriPluginScript.new()
add_child( csPlugin )
func _enter_tree() -> void:
_ensure_cs_plugin()
nodeContextMenu = NodeContextMenuScript.new()
add_context_menu_plugin( EditorContextMenuPlugin.CONTEXT_SLOT_SCENE_TREE, nodeContextMenu )
func _exit_tree() -> void:
if nodeContextMenu:
remove_context_menu_plugin( nodeContextMenu )
nodeContextMenu = null
if csPlugin:
csPlugin.queue_free()
csPlugin = null
func _enable_plugin() -> void:
_ensure_cs_plugin()
csPlugin.EnablePlugin()
func _disable_plugin() -> void:
if csPlugin:
csPlugin.DisablePlugin()

1
plugin.gd.uid Normal file
View File

@ -0,0 +1 @@
uid://c3fvpug0mdv5l