ImpactForces, CameraManager BugFix

This commit is contained in:
Josef 2025-07-25 10:13:35 +02:00
parent 839b0bca39
commit b5567a0bcd
48 changed files with 927 additions and 57 deletions

View File

@ -0,0 +1,73 @@
using Godot;
using System.Collections.Generic;
namespace Rokojori
{
[Tool][GlobalClass]
public partial class VelocityToSound:Action
{
[Export]
public PhysicsBody3D velocitySource;
[Export]
public AudioStreamPlayer3D target;
[Export]
public Curve velocityToVolumeLinear;
[Export]
public Curve velocityToPitchScale;
[Export]
public Smoothing velocitySmoothing;
[Export]
public float inAirMultiply = 0f;
[Export]
public bool ensurePlaying = true;
public override void _Process( double delta )
{
if ( velocitySource == null || target == null )
{
return;
}
var velocity = Smoothing.Apply( velocitySmoothing, velocitySource.GetVelocity().Length(), (float) delta );
if ( velocitySource.IsInAir() )
{
velocity *= inAirMultiply;
}
var amp = velocityToVolumeLinear == null ? 1 : velocityToVolumeLinear.Sample( velocity );
var pitch = velocityToPitchScale == null ? 1 : velocityToPitchScale.Sample( velocity );
// if ( velocity == 0 )
// {
// this.LogInfo( "Velocity == 0" );
// }
// if ( velocity != 0 )
// {
// this.LogInfo( "Velocity != 0" );
// }
// this.LogInfo( "Vel:", (int)(velocity), "Amp:", (int)(amp * 100 ) + "%",(int) (pitch * 100) );
if ( ensurePlaying && amp > 0 && ! target.Playing )
{
target.Playing = true;
}
target.VolumeLinear = amp;
target.PitchScale = pitch;
}
}
}

View File

@ -0,0 +1 @@
uid://4o4j41hwd4ro

View File

@ -0,0 +1,51 @@
using Godot;
namespace Rokojori
{
[Tool][GlobalClass ]
public partial class MoveTowards:Node
{
[Export]
public Node3D target;
[Export]
public Node3D goal;
[Export]
public float speed = 5f;
[Export]
public Smoothing rotationSmoothing;
[Export]
public float yDirectionMultiply = 1;
public override void _Process( double delta )
{
if ( Engine.IsEditorHint() )
{
return;
}
if ( goal == null )
{
return;
}
var direction = goal.GlobalPosition - target.GlobalPosition;
direction.Y *= yDirectionMultiply;
direction = direction.Normalized() * speed * (float)delta;
target.GlobalPosition += direction;
// this.LogInfo( "Moving:", direction );
target.LookAt( goal.GlobalPosition );
var nextRotation = target.GetGlobalQuaternion();
var rotation = Smoothing.Apply( rotationSmoothing, nextRotation, (float) delta );
target.SetGlobalQuaternion( rotation );
}
}
}

View File

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

View File

@ -24,6 +24,9 @@ namespace Rokojori
[Export] [Export]
public Action onExit; public Action onExit;
public readonly EventSlot<Node> onEnteredSlot = new EventSlot<Node>();
public readonly EventSlot<Node> onInsideSlot = new EventSlot<Node>();
public readonly EventSlot<Node> onExitSlot = new EventSlot<Node>();
Dictionary<Node,System.Action> _inside = new Dictionary<Node,System.Action>(); Dictionary<Node,System.Action> _inside = new Dictionary<Node,System.Action>();
@ -83,6 +86,7 @@ namespace Rokojori
// this.LogInfo( "Selecting Enter", area.GlobalPosition, HierarchyName.Of( n ), ( (Node3D)n ).GlobalPosition ); // this.LogInfo( "Selecting Enter", area.GlobalPosition, HierarchyName.Of( n ), ( (Node3D)n ).GlobalPosition );
Action.Trigger( onEntered ); Action.Trigger( onEntered );
onEnteredSlot.DispatchEvent( n );
if ( onInside == null ) if ( onInside == null )
{ {
@ -96,7 +100,12 @@ namespace Rokojori
return; return;
} }
var callback = ()=>{ Action.Trigger( onInside ); }; var callback = ()=>
{
Action.Trigger( onInside );
onInsideSlot.DispatchEvent( n );
};
_inside[ n ] = callback; _inside[ n ] = callback;
tm.AddProcessCallback( callback ); tm.AddProcessCallback( callback );
@ -112,7 +121,7 @@ namespace Rokojori
} }
Action.Trigger( onExit ); Action.Trigger( onExit );
onExitSlot.DispatchEvent( n );
if ( ! _inside.ContainsKey( n ) ) if ( ! _inside.ContainsKey( n ) )

View File

@ -10,7 +10,10 @@ namespace Rokojori
[Export] [Export]
public AudioStreamPlayer3D player; public AudioStreamPlayer3D player;
[Export]
public Duration overdrivePreventionDuration;
float _lastSoundPlayed;
[ExportGroup("Randomize Playback Position")] [ExportGroup("Randomize Playback Position")]
[Export] [Export]
public bool randomizePlaybackPosition = false; public bool randomizePlaybackPosition = false;
@ -53,6 +56,17 @@ namespace Rokojori
protected override void _OnTrigger() protected override void _OnTrigger()
{ {
if ( overdrivePreventionDuration != null )
{
var now = TimeLine.osTime;
var elapsed = now - _lastSoundPlayed;
if ( elapsed < overdrivePreventionDuration.GetDurationInSeconds() )
{
return;
}
}
var offset = 0f; var offset = 0f;
var player = generatePools ? GetFreePlayer() : this.player; var player = generatePools ? GetFreePlayer() : this.player;
@ -77,7 +91,7 @@ namespace Rokojori
var start = tl.position; var start = tl.position;
TimeLineManager.ScheduleSpanIn( durationPerSound.timeLine, 0, durationPerSound.GetDurationInSeconds(), TimeLineManager.ScheduleSpanIn( durationPerSound.timeLine, 0, durationPerSound.GetDurationInSeconds() / player.PitchScale,
( span, type )=> ( span, type )=>
{ {
var timeNow = tl.position; var timeNow = tl.position;

View File

@ -61,13 +61,18 @@ namespace Rokojori
} }
else else
{ {
this.LogInfo( "Removing tick:", _eventID );
if ( _eventID == -1 ) if ( _eventID == -1 )
{ {
return; return;
} }
TimeLineManager.RemoveEvent( timeLine, _eventID ); TimeLineManager.RemoveEvent( timeLine, _eventID );
_eventID = -1; _eventID = -1;
} }

View File

@ -0,0 +1,27 @@
using Godot;
using System.Collections;
using System.Collections.Generic;
using Godot.Collections;
namespace Rokojori
{
[GlobalClass,Tool]
public partial class SetCollisionShape:Action
{
[Export]
public CollisionShape3D shape3D;
[Export]
public bool disabled = false;
protected override void _OnTrigger()
{
if ( shape3D == null )
{
return;
}
shape3D.Disabled = disabled;
}
}
}

View File

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

View File

@ -0,0 +1,27 @@
using Godot;
using System.Collections.Generic;
namespace Rokojori
{
[Tool][GlobalClass]
public partial class RemoveChildNodes : Action
{
[Export]
public Node target;
[Export]
public bool queue = true;
protected override void _OnTrigger()
{
if ( target == null )
{
return;
}
target.DestroyChildren( true, queue );
}
}
}

View File

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

View File

@ -0,0 +1,66 @@
using Godot;
namespace Rokojori
{
[Tool]
[GlobalClass ]
public partial class CountNumber : SequenceAction
{
[Export]
public UIText target;
[Export]
public int startValue = 0;
[Export]
public int endValue = 100;
int _actionID = -1;
int _animationID = -1;
[Export]
public Duration duration;
protected override void _OnTrigger()
{
if ( target == null )
{
return;
}
if ( _actionID != -1 )
{
CancelAction( _actionID );
}
var _id = DispatchStart();
_actionID = _id;
_animationID = TimeLineManager.ScheduleSpanWith( duration,
( s, t ) =>
{
if ( _animationID != s.id )
{
return;
}
var number = startValue + s.phase * ( endValue - startValue );
var intNumber = Mathf.RoundToInt( number );
target.Set( intNumber + "" );
if ( TimeLineSpanUpdateType.End == t )
{
target.Set( endValue + "" );
DispatchEnd( _id );
}
}
).id;
}
}
}

View File

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

View File

@ -0,0 +1,100 @@
using System;
using Godot;
namespace Rokojori
{
[Tool]
[GlobalClass ]
public partial class TweenFloat:SequenceAction, Animator
{
[Export]
public GodotObject target;
[Export]
public string targetMember;
[Export]
public float endValue;
[Export]
public Duration duration;
[Export]
public Curve curve;
public void OnAnimatorStart(){}
public void OnAnimatorEnd(){}
public void OnAnimatorCancel(){}
int _actionID = -1;
int _timeID = -1;
protected override void _OnTrigger()
{
this.LogInfo( "Started Float Tween" );
if ( Engine.IsEditorHint() )
{
return;
}
if ( _actionID != -1 )
{
CancelAction( _actionID );
}
_actionID = DispatchStart();
var startValue = ReflectionHelper.GetValue<float>( target, targetMember );
AnimationManager.StartAnimation( this, target, targetMember );
// this.LogInfo( "Get Float Tween", startValue );
_timeID = TimeLineManager.ScheduleSpanWith( duration,
( span, type )=>
{
// this.LogInfo( "Update Float Tween", startValue );
if ( span.id != _timeID )
{
return;
}
if ( ! AnimationManager.IsAnimating( this, target, targetMember ) )
{
return;
}
var phase = span.phase;
if ( curve != null )
{
phase = curve.Sample( phase );
}
var value = Mathf.Lerp( startValue, endValue, phase );
// this.LogInfo( "Updating Float Tween", "phase:", phase, "value:", value, target );
ReflectionHelper.SetValue( target, targetMember, value );
if ( type == TimeLineSpanUpdateType.End )
{
// this.LogInfo( "End Float Tween", endValue );
target._Set( targetMember, endValue );
AnimationManager.EndAnimation( this, target, targetMember );
DispatchEnd( _actionID );
_actionID = -1;
_timeID = -1;
}
}
).id;
}
}
}

View File

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

View File

@ -43,35 +43,33 @@ namespace Rokojori
public class AnimationManager public class AnimationManager
{ {
static MultiMap<Node,string,Animator> _animatingNodes = new MultiMap<Node,string,Animator>(); static MultiMap<GodotObject,string,Animator> _animatingObjects = new MultiMap<GodotObject,string,Animator>();
static MultiMap<Resource,string,Animator> _animatingResources = new MultiMap<Resource,string,Animator>();
public static Animator GetAnimator( GodotObject go, string memberName )
public static Animator GetAnimator( Node node, AnimationMember member )
{ {
return _animatingNodes.Get( node, member.name ); return _animatingObjects.Get( go, memberName );
} }
public static Animator GetAnimator( Material material, ShaderPropertyName propertyName ) public static Animator GetAnimator( Material material, ShaderPropertyName propertyName )
{ {
return _animatingResources.Get( material, propertyName.propertyName ); return GetAnimator( material, propertyName.propertyName );
} }
public static bool IsAnimating( Animator animator, Node node, AnimationMember member ) public static bool IsAnimating( Animator animator, GodotObject go, string memberName )
{ {
return GetAnimator( node, member ) == animator; return GetAnimator( go, memberName ) == animator;
} }
public static bool IsAnimating( Animator animator, Material material, ShaderPropertyName propertyName ) public static bool IsAnimating( Animator animator, Material material, ShaderPropertyName propertyName )
{ {
return GetAnimator( material, propertyName ) == animator; return IsAnimating( animator, material, propertyName.propertyName );
} }
public static bool IsAnimating( Animator animator, Node node, params AnimationMember[] members ) public static bool IsAnimating( Animator animator, Node node, params AnimationMember[] members )
{ {
for ( int i = 0; i < members.Length; i++ ) for ( int i = 0; i < members.Length; i++ )
{ {
if ( ! IsAnimating( animator, node, members[ i ] ) ) if ( ! IsAnimating( animator, node, members[ i ].name ) )
{ {
return false; return false;
} }
@ -93,16 +91,28 @@ namespace Rokojori
return nodes.Length > 0; return nodes.Length > 0;
} }
public static void StartAnimation( Animator animator, Node node, AnimationMember member ) public static void StartAnimation( Animator animator, GodotObject go, string member )
{ {
var activeAnimator = GetAnimator( node, member ); var activeAnimator = GetAnimator( go, member );
if ( activeAnimator != null ) if ( activeAnimator != null )
{ {
activeAnimator.OnAnimatorCancel(); activeAnimator.OnAnimatorCancel();
} }
_animatingNodes.Set( node, member.name, animator ); _animatingObjects.Set( go, member, animator );
}
public static void StartAnimation( Animator animator, Node node, AnimationMember member )
{
var activeAnimator = GetAnimator( node, member.name );
if ( activeAnimator != null )
{
activeAnimator.OnAnimatorCancel();
}
_animatingObjects.Set( node, member.name, animator );
} }
@ -115,14 +125,14 @@ namespace Rokojori
activeAnimator.OnAnimatorCancel(); activeAnimator.OnAnimatorCancel();
} }
_animatingResources.Set( material, shaderPropertyName.propertyName, animator ); _animatingObjects.Set( material, shaderPropertyName.propertyName, animator );
} }
public static void StartAnimation( Animator animator, Node node, params AnimationMember[] members ) public static void StartAnimation( Animator animator, Node node, params AnimationMember[] members )
{ {
for ( int i = 0; i < members.Length; i++ ) for ( int i = 0; i < members.Length; i++ )
{ {
StartAnimation( animator, node, members[ i ] ); StartAnimation( animator, node, members[ i ].name );
} }
} }
@ -134,9 +144,20 @@ namespace Rokojori
} }
} }
public static void EndAnimation( Animator animator, GodotObject go, string member )
{
var activeAnimator = GetAnimator( go, member );
if ( activeAnimator != null )
{
activeAnimator.OnAnimatorCancel();
}
}
public static void EndAnimation( Animator animator, Node node, AnimationMember member ) public static void EndAnimation( Animator animator, Node node, AnimationMember member )
{ {
var activeAnimator = GetAnimator( node, member ); var activeAnimator = GetAnimator( node, member.name );
if ( activeAnimator != null ) if ( activeAnimator != null )
{ {
@ -158,7 +179,7 @@ namespace Rokojori
{ {
for ( int i = 0; i < members.Length; i++ ) for ( int i = 0; i < members.Length; i++ )
{ {
EndAnimation( animator, node, members[ i ] ); EndAnimation( animator, node, members[ i ].name );
} }
} }

View File

@ -40,7 +40,7 @@ timeline = ExtResource("3_h08aw")
smooth = true smooth = true
smoothingStrength = 0.325 smoothingStrength = 0.325
positionShake = Vector3(0.075, 0, 0.075) positionShake = Vector3(0.075, 0, 0.075)
globalPosition = true globalPosition = false
repeatAndFlipFirstPosition = true repeatAndFlipFirstPosition = true
rotationShake = Vector3(1, 1, 20) rotationShake = Vector3(1, 1, 20)
globalRotation = false globalRotation = false

View File

@ -42,7 +42,7 @@ namespace Rokojori
var start = timeline.position; var start = timeline.position;
var end = start + animations.GetMaxDuration( _randomizations ); var end = start + animations.GetMaxDuration( _randomizations );
var sequenceID = DispatchStart(); var actionID = DispatchStart();
foreach ( var c in animations.curves ) foreach ( var c in animations.curves )
{ {
@ -52,6 +52,18 @@ namespace Rokojori
TimeLineManager.ScheduleSpanIn( timeline, 0, duration, TimeLineManager.ScheduleSpanIn( timeline, 0, duration,
( span, type )=> ( span, type )=>
{ {
if ( actionID == -1 )
{
return;
}
if ( ! IsInstanceValid( target ) )
{
DispatchCancelled( actionID );
actionID = -1;
return;
}
var timeNow = timeline.position; var timeNow = timeline.position;
var elapsed = timeNow - start; var elapsed = timeNow - start;
@ -74,7 +86,7 @@ namespace Rokojori
AnimationManager.EndAnimation( c, target, c.animationMember ); AnimationManager.EndAnimation( c, target, c.animationMember );
} }
DispatchEnd( sequenceID ); DispatchEnd( actionID );
} }
} }
); );

View File

@ -516,12 +516,12 @@ namespace Rokojori
} }
public static void DestroyChildren( this Node parent, bool includeInternal = false ) public static void DestroyChildren( this Node parent, bool includeInternal = false, bool queue = true )
{ {
RemoveAndDeleteChildren( parent, includeInternal ); RemoveAndDeleteChildren( parent, includeInternal );
} }
public static void RemoveAndDeleteChildren( Node parent, bool includeInternal = false ) public static void RemoveAndDeleteChildren( Node parent, bool includeInternal = false, bool queue = true )
{ {
if ( parent == null ) if ( parent == null )
{ {
@ -534,7 +534,15 @@ namespace Rokojori
{ {
var node = parent.GetChild( i, includeInternal ); var node = parent.GetChild( i, includeInternal );
parent.RemoveChild( node ); parent.RemoveChild( node );
node.QueueFree();
if ( ! queue )
{
node.Free();
}
else
{
node.QueueFree();
}
} }
} }
@ -547,7 +555,7 @@ namespace Rokojori
parent.RemoveChild( node ); parent.RemoveChild( node );
} }
node.LogInfo( "Freeing queued:", queue, node); // node.LogInfo( "Freeing queued:", queue, node);
if ( queue ) if ( queue )
{ {

View File

@ -0,0 +1,28 @@
using Godot;
using System.Collections;
using System.Collections.Generic;
using Godot.Collections;
namespace Rokojori
{
[Tool]
[GlobalClass]
public partial class SetCharacterControllerAction:Action
{
[Export]
public CharacterControllerAction characterControllerAction;
[Export]
public bool enabled = true;
protected override void _OnTrigger()
{
if ( characterControllerAction == null )
{
return;
}
characterControllerAction.enabled = enabled;
}
}
}

View File

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

View File

@ -0,0 +1,53 @@
using Godot;
using System.Collections;
using System.Collections.Generic;
using Godot.Collections;
namespace Rokojori
{
[Tool]
[GlobalClass, Icon("res://addons/rokojori_action_library/Icons/CCGravity.svg")]
public partial class AddImpactForce:Action
{
[Export]
public ImpactForce impactForce;
[Export]
public ImpactForces target;
[Export]
public OnCollision collisionDirectionSource;
protected override void _OnTrigger()
{
this.LogInfo( TimeLine.osTime );
if ( target == null || impactForce == null )
{
return;
}
var colliders = collisionDirectionSource.GetNodesInside().FilterType<Node,Node3D>();
var force = (ImpactForce) impactForce.Duplicate();
if ( colliders.Count == 0 )
{
force.direction = target.controller.body.GlobalForward() * -1;
}
else
{
var positions = colliders.Map( c => c.GlobalPosition );
var center = Math3D.ComputeAverage( positions );
force.direction = target.controller.body.GlobalPosition - center;
}
force.direction = force.direction * new Vector3( 1, 0, 1 );
this.LogInfo( force.direction );
target.AddImpactForce( force );
}
}
}

View File

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

View File

@ -87,7 +87,22 @@ namespace Rokojori
{ {
this.delta = (float) delta; this.delta = (float) delta;
var container = actionsContainer == null ? this : actionsContainer; var container = actionsContainer == null ? this : actionsContainer;
Nodes.ForEachDirectChild<CharacterControllerAction>( container, Action.Trigger ); Nodes.ForEachDirectChild<CharacterControllerAction>(
container,
( c ) =>
{
if (
! c.enabled ||
c.condition != null && ! c.condition.Evaluate() ||
c.sceneCondition != null && ! c.sceneCondition.Evaluate()
)
{
return;
}
Action.Trigger( c );
}
);
} }
} }
} }

View File

@ -9,9 +9,20 @@ namespace Rokojori
[GlobalClass, Icon("res://addons/rokojori_action_library/Icons/CharacterControllerAction.svg")] [GlobalClass, Icon("res://addons/rokojori_action_library/Icons/CharacterControllerAction.svg")]
public partial class CharacterControllerAction:Action public partial class CharacterControllerAction:Action
{ {
[Export]
public bool enabled = true;
[Export] [Export]
public CharacterController controller; public CharacterController controller;
[Export]
public Condition condition;
[Export]
public SceneCondition sceneCondition;
public CharacterBody3D body => controller.body; public CharacterBody3D body => controller.body;
public void AddVelocity( Vector3 velocity ) public void AddVelocity( Vector3 velocity )

View File

@ -0,0 +1,32 @@
using Godot;
using System.Collections;
using System.Collections.Generic;
using Godot.Collections;
namespace Rokojori
{
[Tool]
[GlobalClass, Icon("res://addons/rokojori_action_library/Icons/CCGravity.svg")]
public partial class ImpactForce:Resource
{
[Export]
public Curve forceOverTime;
[Export]
public float maxForce;
[Export]
public Curve freezeOverTime;
[Export]
public float duration;
[Export]
public Vector3 direction;
}
}

View File

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

View File

@ -0,0 +1,70 @@
using Godot;
using System.Collections;
using System.Collections.Generic;
using Godot.Collections;
namespace Rokojori
{
[Tool]
[GlobalClass, Icon("res://addons/rokojori_action_library/Icons/CCGravity.svg")]
public partial class ImpactForces:CharacterControllerAction
{
List<ImpactForce> _forces = new List<ImpactForce>();
List<float> _forcePosition = new List<float>();
public void AddImpactForce( ImpactForce impactForce )
{
_forces.Add( impactForce );
_forcePosition.Add( 0 );
}
protected override void _OnTrigger()
{
if ( _forces.Count == 0 )
{
return;
}
var combinedForce = Vector3.Zero;
var removals = new List<int>();
var currentVelocity = body.Velocity * controller.delta;
var maxFreeze = 1f;
for ( int i = 0; i < _forces.Count; i++ )
{
var position = _forcePosition[ i ];
var duration = _forces[ i ].duration;
if ( position > duration )
{
position = duration;
removals.Add( i );
}
var force = _forces[ i ].forceOverTime.Sample( position ) * _forces[ i ].maxForce;
var freeze = 1f - _forces[ i ].freezeOverTime.Sample( position );
maxFreeze = Mathf.Min( freeze, maxFreeze );
combinedForce += force * _forces[ i ].direction;
_forcePosition[ i ] += controller.delta;
}
_forces.RemoveIncreasingSortedIndices( removals );
_forcePosition.RemoveIncreasingSortedIndices( removals );
// this.LogInfo( combinedForce );
SetVelocity( combinedForce + currentVelocity * maxFreeze );
}
}
}

View File

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

View File

@ -47,12 +47,18 @@ namespace Rokojori
bool needsToRelease = false; bool needsToRelease = false;
bool inAir = false; bool inAir = false;
float airStart = 0;
public bool IsJumping() public bool IsJumping()
{ {
return inAir; return inAir;
} }
public float GetAirTime()
{
return airStart == -1 ? 0 : ( TimeLine.osTime - airStart );
}
protected override void _OnTrigger() protected override void _OnTrigger()
{ {
if ( body.IsOnFloor() ) if ( body.IsOnFloor() )
@ -60,6 +66,7 @@ namespace Rokojori
if ( inAir ) if ( inAir )
{ {
inAir = false; inAir = false;
airStart = -1;
} }
if ( jumpPressing ) if ( jumpPressing )
@ -91,6 +98,7 @@ namespace Rokojori
jumpPressing = true; jumpPressing = true;
jumpedDuration = 0; jumpedDuration = 0;
inAir = true; inAir = true;
airStart = TimeLine.osTime;
Trigger( onJump ); Trigger( onJump );

View File

@ -497,6 +497,26 @@ namespace Rokojori
return Curve( y, y ); return Curve( y, y );
} }
public static float CurveStartPosition( this Curve c )
{
if ( c.PointCount == 0 )
{
return 0;
}
return c.GetPointPosition( 0 ).X;
}
public static float CurveEndPosition( this Curve c )
{
if ( c.PointCount == 0 )
{
return 0;
}
return c.GetPointPosition( c.PointCount - 1 ).X;
}
public static float CurveAngle( Curve c, float t, float samplingRange = 0.01f ) public static float CurveAngle( Curve c, float t, float samplingRange = 0.01f )
{ {
var x0 = Mathf.Max( t - samplingRange, 0 ); var x0 = Mathf.Max( t - samplingRange, 0 );

View File

@ -0,0 +1,59 @@
using Godot;
using System.Collections.Generic;
namespace Rokojori
{
public static class PhysicsBodies
{
public static bool IsInAir( this PhysicsBody3D physicsBody3D )
{
if ( physicsBody3D is RigidBody3D rb)
{
return false;
}
if ( physicsBody3D is CharacterBody3D cb)
{
return ! cb.IsOnFloor();
}
if ( physicsBody3D is PhysicalBone3D pb )
{
return false;
}
if ( physicsBody3D is StaticBody3D sb )
{
return false;
}
return false;
}
public static Vector3 GetVelocity( this PhysicsBody3D physicsBody3D )
{
if ( physicsBody3D is RigidBody3D rb)
{
return rb.LinearVelocity;
}
if ( physicsBody3D is CharacterBody3D cb)
{
return cb.Velocity;
}
if ( physicsBody3D is PhysicalBone3D pb )
{
return pb.LinearVelocity;
}
if ( physicsBody3D is StaticBody3D sb )
{
return sb.ConstantLinearVelocity;
}
return Vector3.Zero;
}
}
}

View File

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

View File

@ -7,18 +7,19 @@ layout( rgba16f, set = 0, binding = 0)
uniform image2D color_image; uniform image2D color_image;
layout(push_constant, std430) layout(push_constant, std430)
uniform Params uniform Parameters
{ {
vec2 raster_size; vec2 raster_size;
vec2 reserved; float amount;
float method;
} params; } parameters;
void main() void main()
{ {
ivec2 uv = ivec2( gl_GlobalInvocationID.xy ); ivec2 uv = ivec2( gl_GlobalInvocationID.xy );
ivec2 size = ivec2( params.raster_size ); ivec2 size = ivec2( parameters.raster_size );
if ( uv.x >= size.x || uv.y >= size.y ) if ( uv.x >= size.x || uv.y >= size.y )
{ {
@ -27,9 +28,12 @@ void main()
vec4 color = imageLoad( color_image, uv ); vec4 color = imageLoad( color_image, uv );
float gray = color.r * 0.2125 + color.g * 0.7154 + color.b * 0.0721; float gray = color.r * 0.2 + color.g * 0.7 + color.b * 0.1;
float avg = ( color.r + color.g + color.b ) / 3.0;
color.rgb = vec3( gray * 0.5); gray = mix( gray, avg, parameters.method );
color.rgb = mix( color.rgb, vec3( gray ), parameters.amount );
imageStore( color_image, uv, color ); imageStore( color_image, uv, color );
} }

View File

@ -11,6 +11,12 @@ namespace Rokojori
{ {
public static readonly string shaderPath = Path( "GreyScale/GrayScaleShader.glsl" ); public static readonly string shaderPath = Path( "GreyScale/GrayScaleShader.glsl" );
[Export( PropertyHint.Range, "0,1" )]
public float amount;
[Export( PropertyHint.Range, "0,1" )]
public float luminanceMethod;
protected override void OnConfigure() protected override void OnConfigure()
{ {
@ -23,7 +29,8 @@ namespace Rokojori
{ {
constants.Set( constants.Set(
(Vector2)context.internalSize, (Vector2)context.internalSize,
Vector2.Zero amount,
luminanceMethod
); );
} }

View File

@ -19,6 +19,13 @@ namespace Rokojori
[Export] [Export]
public Action onEnd; public Action onEnd;
[Export]
public bool onlyWhenNotConsumed = true;
[Export]
public bool consumeEvent = true;
[ExportGroup("Condition")] [ExportGroup("Condition")]
[Export] [Export]
public Condition condition; public Condition condition;
@ -26,13 +33,18 @@ namespace Rokojori
[Export] [Export]
public SceneCondition sceneCondition; public SceneCondition sceneCondition;
public override void _Process( double delta) public override void _Process( double delta )
{ {
if ( sensor == null ) if ( sensor == null )
{ {
return; return;
} }
if ( onlyWhenNotConsumed && sensor.consumed )
{
return;
}
if ( condition != null && ! condition.Evaluate() ) if ( condition != null && ! condition.Evaluate() )
{ {
return; return;
@ -44,21 +56,37 @@ namespace Rokojori
} }
if ( sensor.isDown ) if ( sensor.isDown )
{ {
Action.Trigger( onStart ); Action.Trigger( onStart );
if ( consumeEvent && onStart != null )
{
sensor.Consume();
return;
}
} }
if ( sensor.isHold ) if ( sensor.isHold )
{ {
Action.Trigger( onActive ); Action.Trigger( onActive );
if ( consumeEvent && onActive != null )
{
sensor.Consume();
return;
}
} }
if ( sensor.isUp ) if ( sensor.isUp )
{ {
Action.Trigger( onActive ); Action.Trigger( onEnd );
if ( consumeEvent && onEnd != null )
{
sensor.Consume();
return;
}
} }
} }

View File

@ -25,6 +25,8 @@ namespace Rokojori
protected float _activeTreshold = 0.5f; protected float _activeTreshold = 0.5f;
protected bool _consumed = false;
public void ProcessSensor( SensorRunner runner, float delta ) public void ProcessSensor( SensorRunner runner, float delta )
{ {
_wasActive = _active; _wasActive = _active;
@ -50,6 +52,14 @@ namespace Rokojori
public virtual bool WasActive( int device ) => isUp; public virtual bool WasActive( int device ) => isUp;
public virtual bool IsActive( int device ) => isUp; public virtual bool IsActive( int device ) => isUp;
public bool consumed => _consumed;
public void Consume()
{
_consumed = true;
}
protected virtual void UpdateValue() protected virtual void UpdateValue()
{ {
@ -59,12 +69,14 @@ namespace Rokojori
{ {
_active = activeState; _active = activeState;
_value = activeState ? 1 : 0; _value = activeState ? 1 : 0;
_consumed = false;
} }
protected void SetFloatValue( float value ) protected void SetFloatValue( float value )
{ {
_value = value; _value = value;
_active = value > _activeTreshold; _active = value > _activeTreshold;
_consumed = false;
} }
public virtual List<InputIcon> GetInputIcons() public virtual List<InputIcon> GetInputIcons()

View File

@ -96,12 +96,18 @@ vec3 adjustHSL( vec3 color, vec3 hslAdjustment )
} }
vec3 SRGBtoLINEAR( vec3 sRGB )
vec3 toLinear( vec3 sRGB )
{ {
return mix( pow( (sRGB + vec3( 0.055 )) * ( 1.0 / ( 1.0 + 0.055 )),vec3( 2.4 )),sRGB * ( 1.0 / 12.92 ),lessThan( sRGB,vec3( 0.04045 )) ); return mix( pow( (sRGB + vec3( 0.055 )) * ( 1.0 / ( 1.0 + 0.055 )),vec3( 2.4 )),sRGB * ( 1.0 / 12.92 ),lessThan( sRGB,vec3( 0.04045 )) );
} }
vec3 LINEARtoSRGB( vec3 linear )
{
vec3 color = linear;
const vec3 a = vec3(0.055f);
return mix((vec3(1.0f) + a) * pow(color.rgb, vec3(1.0f / 2.4f)) - a, 12.92f * color.rgb, lessThan(color.rgb, vec3(0.0031308f)));
}
float modPolarDegrees( float value ) float modPolarDegrees( float value )
{ {
return mod( value + 180.0, 360.0 ) - 180.0; return mod( value + 180.0, 360.0 ) - 180.0;

View File

@ -8,6 +8,18 @@ namespace Rokojori
{ {
public static class Text public static class Text
{ {
public static string WithLeadingZeros( int value, int minDigits = 2 )
{
var label = value + "";
while ( label.Length < minDigits )
{
label = "0" + label;
}
return label;
}
public static string EscapeAsPathForCommandLine( this string path ) public static string EscapeAsPathForCommandLine( this string path )
{ {
var escaped = path.Replace( "\"", "\\\"" ); var escaped = path.Replace( "\"", "\\\"" );

View File

@ -19,5 +19,17 @@ namespace Rokojori
{ {
return 0; return 0;
} }
public static string GetTimerLabel( float seconds )
{
var secondsAmount = seconds % 60;
var minutesAmount = ( seconds - secondsAmount ) / 60f;
var secondsLabel = Text.WithLeadingZeros( Mathf.CeilToInt( secondsAmount ) );
var minutesLabel = Text.WithLeadingZeros( Mathf.CeilToInt( minutesAmount ) );
return minutesLabel + ":" + secondsLabel;
}
} }
} }

View File

@ -12,6 +12,8 @@ namespace Rokojori
{ {
public static void RemoveEvent( TimeLine timeline, int id ) public static void RemoveEvent( TimeLine timeline, int id )
{ {
RJLog.Log( "Removing tick:", timeline, id );
timeline = TimeLineManager.Ensure( timeline ); timeline = TimeLineManager.Ensure( timeline );
var runner = timeline.runner; var runner = timeline.runner;
@ -38,7 +40,7 @@ namespace Rokojori
return runner._ScheduleEvent( position, eventID, false, callback ); return runner._ScheduleEvent( position, eventID, false, callback );
} }
public static TimeLineEvent ScheduleEventIn( TimeLine timeline, float offset, Action<TimeLineEvent> callback ) public static TimeLineEvent ScheduleEventIn( TimeLine timeline, float offset, Action<TimeLineEvent> callback )
{ {
timeline = TimeLineManager.Ensure( timeline ); timeline = TimeLineManager.Ensure( timeline );
var runner = timeline.runner; var runner = timeline.runner;
@ -48,6 +50,11 @@ namespace Rokojori
return runner._ScheduleEvent( position, eventID, false, callback ); return runner._ScheduleEvent( position, eventID, false, callback );
} }
public static TimeLineEvent ScheduleEventWith( Duration duration, Action<TimeLineEvent> callback )
{
return ScheduleEventIn( duration.timeLine, duration.GetDurationInSeconds(), callback );
}
public static TimeLineEvent ScheduleLoopEvent( TimeLine timeline, float duration, float offset, Action<TimeLineEvent> callback ) public static TimeLineEvent ScheduleLoopEvent( TimeLine timeline, float duration, float offset, Action<TimeLineEvent> callback )
{ {
timeline = TimeLineManager.Ensure( timeline ); timeline = TimeLineManager.Ensure( timeline );
@ -68,7 +75,12 @@ namespace Rokojori
return runner._ScheduleSpan( start, end, spanID, false, callback ); return runner._ScheduleSpan( start, end, spanID, false, callback );
} }
public static TimeLineSpan ScheduleSpanIn( TimeLine timeline, float delay, float duration, Action<TimeLineSpan,TimeLineSpanUpdateType> callback ) public static TimeLineSpan ScheduleSpanWith( Duration duration, Action<TimeLineSpan,TimeLineSpanUpdateType> callback )
{
return ScheduleSpanIn( duration.timeLine, 0, duration.GetDurationInSeconds(), callback );
}
public static TimeLineSpan ScheduleSpanIn( TimeLine timeline, float delay, float duration, Action<TimeLineSpan,TimeLineSpanUpdateType> callback )
{ {
timeline = TimeLineManager.Ensure( timeline ); timeline = TimeLineManager.Ensure( timeline );
var runner = timeline.runner; var runner = timeline.runner;

View File

@ -129,11 +129,14 @@ namespace Rokojori
void ProcessEvents() void ProcessEvents()
{ {
if ( requestedRemovals.Count > 0 ) if ( requestedRemovalIDs.Count > 0 )
{ {
requestedRemovals.Sort(); requestedRemovalIDs.Sort();
Lists.RemoveIncreasingSortedIndices( events, requestedRemovals );
requestedRemovals.Clear(); RJLog.Log( "Removing:", requestedRemovalIDs, "From:", events );
//Lists.RemoveIncreasingSortedIndices( events, requestedRemovalIDs );
events = events.Filter( e => ! requestedRemovalIDs.Contains( e.id ) );
requestedRemovalIDs.Clear();
} }
List<int> eventRemovals = null; List<int> eventRemovals = null;
@ -257,11 +260,13 @@ namespace Rokojori
return list; return list;
} }
List<int> requestedRemovals = new List<int>(); List<int> requestedRemovalIDs = new List<int>();
public void RemoveEvent( int eventID ) public void RemoveEvent( int eventID )
{ {
requestedRemovals.Add( eventID ); requestedRemovalIDs.Add( eventID );
RJLog.Log( "requestedRemovals add:", eventID );
} }
public TimeLineCallback _ScheduleCallback( Action<TimeLineCallback> callback, int eventID ) public TimeLineCallback _ScheduleCallback( Action<TimeLineCallback> callback, int eventID )

View File

@ -515,11 +515,19 @@ namespace Rokojori
return list[ list.Count - 1 - index ]; return list[ list.Count - 1 - index ];
} }
public static void RemoveIncreasingSortedIndices<T>( List<T> list, List<int> increasinglySortedRemovals ) public static void RemoveIncreasingSortedIndices<T>( this List<T> list, List<int> increasinglySortedRemovals )
{ {
for ( var i = increasinglySortedRemovals.Count - 1; i >= 0; i-- ) for ( var i = increasinglySortedRemovals.Count - 1; i >= 0; i-- )
{ {
var index = increasinglySortedRemovals[ i ]; var index = increasinglySortedRemovals[ i ];
if ( index < 0 || index >= list.Count )
{
// RJLog.Log( "Invalid index in removals:", index, list.Count, increasinglySortedRemovals );
continue;
}
// RJLog.Log( "requestedRemovals remove:", index );
list.RemoveAt( index ); list.RemoveAt( index );
} }
} }

View File

@ -336,6 +336,22 @@ namespace Rokojori
return list; return list;
} }
public static T GetValue<T>( object instance, string name )
{
var mi = GetDataMemberInfo( instance, name );
var value = GetDataMemberValue<T>( instance, mi );
return value;
}
public static void SetValue<T>( object instance, string name, T value )
{
var mi = GetDataMemberInfo( instance, name );
SetDataMemberValue<T>( instance, mi, value );
}
public static List<T> GetDataMemberValues<T>( object instance ) public static List<T> GetDataMemberValues<T>( object instance )
{ {
var infos = GetDataMemberInfos<T>( instance ); var infos = GetDataMemberInfos<T>( instance );
@ -408,6 +424,21 @@ namespace Rokojori
return default(T); return default(T);
} }
public static void SetDataMemberValue<T>( object instance, MemberInfo info, T value )
{
if ( info is FieldInfo fieldInfo )
{
fieldInfo.SetValue( instance, value );
}
if ( info is PropertyInfo propertyInfo )
{
propertyInfo.SetValue( instance, value );
}
}
public static T GetDataMemberValue<T>( object instance, string memberName, BindingFlags flags = ReflectionHelper.defaultBindings ) public static T GetDataMemberValue<T>( object instance, string memberName, BindingFlags flags = ReflectionHelper.defaultBindings )
{ {
return GetDataMemberValue<T>( instance, GetDataMemberInfo( instance, memberName, flags ) ); return GetDataMemberValue<T>( instance, GetDataMemberInfo( instance, memberName, flags ) );

View File

@ -21,6 +21,11 @@ namespace Rokojori
set { _locale = value; UpdateLocalization(); } set { _locale = value; UpdateLocalization(); }
} }
public void Set( string value )
{
locale = LocaleText.Create( value );
}
[Export] [Export]
public bool refreshText public bool refreshText
{ {

View File

@ -56,7 +56,7 @@ namespace Rokojori
if ( resolvedSlot == null ) if ( resolvedSlot == null )
{ {
// this.LogError( "No camera slot found" ); this.LogError( "No camera slot found" );
return; return;
} }

View File

@ -185,7 +185,7 @@ namespace Rokojori
return; return;
} }
SetSingleCamera( cam ); SetSingleCamera( cam, delta );
} }
else else
@ -194,8 +194,10 @@ namespace Rokojori
} }
} }
void SetSingleCamera( VirtualCamera3DSlot c ) void SetSingleCamera( VirtualCamera3DSlot c, double delta )
{ {
c.Update( delta, this );
var rotation = c.GetCameraRotation(); var rotation = c.GetCameraRotation();
if ( ! rotation.IsFinite() || rotation.Length() == 0 ) if ( ! rotation.IsFinite() || rotation.Length() == 0 )

View File

@ -36,11 +36,15 @@ namespace Rokojori
{ {
if ( cameraEffectRunner.isFinished ) if ( cameraEffectRunner.isFinished )
{ {
this.LogInfo( "finished" );
cameraEffectRunner = null; cameraEffectRunner = null;
} }
else else
{ {
this.LogInfo( "time:", cameraEffectRunner.timePosition, cameraEffectRunner.effect.maxDuration );
cameraEffectRunner.Update(); cameraEffectRunner.Update();
} }
} }
@ -69,6 +73,8 @@ namespace Rokojori
var offset = camera.GetGlobalOffset( cameraEffectRunner.position ); var offset = camera.GetGlobalOffset( cameraEffectRunner.position );
this.LogInfo( "Cam Off", offset );
return camera.GetCameraPosition() + offset; return camera.GetCameraPosition() + offset;
} }