using Godot; using System.Collections.Generic; using System; namespace Rokojori { [Tool] [GlobalClass,Icon("res://addons/rokojori_action_library/Icons/SensorManager.svg")] public partial class SensorManager: Node { [Export] public bool initializeOnReady = true; [Export] public Sensor[] sensors = []; [Export] public SensorGroup[] sensorGroups = []; [Export] public bool processSensors = true; [Export] public Node[] autoScanForSensors = []; [Export] public bool separateMouseAndKeyboardTracking = false; [Export] public Action onActiveDeviceChange; public readonly EventSlot _onActiveDeviceChange = new EventSlot(); [ExportGroup("Read Only")] [Export] public SensorDevice[] deviceList =[]; [Export] public float[] deviceLastInputTimeStamp = []; [ExportGroup("Testing")] [Export] public SensorDevice testingLastActiveDevice; [Export] public bool showRegistratedSensors = true; [Export] public Sensor[] registratedSensors = []; List runners = new List(); List inputers = new List(); Dictionary sensorToRunner = new Dictionary(); KeyboardDevice keyboardDevice = new KeyboardDevice(); MouseDevice mouseDevice = new MouseDevice(); GamePadDevice gamePadDevice = new GamePadDevice(); MultiSensorDevice mouseKeyboardDevice = new MultiSensorDevice(); DateTime _startTime; public bool isMouseKeyboardLastActive => lastActiveDevice == mouseKeyboardDevice || lastActiveDevice == mouseDevice || lastActiveDevice == keyboardDevice; public bool isControllerLastActive => lastActiveDevice == gamePadDevice; public SensorDevice lastActiveDevice { get { if ( Engine.IsEditorHint() ) { return null; } if ( testingLastActiveDevice != null ) { return testingLastActiveDevice; } var highest = Lists.IndexOfHighestValue( Lists.From( deviceLastInputTimeStamp ), t => t ); if ( highest == -1 ) { return null; } return deviceList[ highest ]; } } void UpdateDevice( SensorDevice d ) { if ( Engine.IsEditorHint() ) { return; } var lastActive = lastActiveDevice; var index = Arrays.IndexOf( deviceList, d ); if ( index == -1 ) { deviceList = Arrays.AddEntry( deviceList, d ); index = deviceList.Length - 1; deviceLastInputTimeStamp = Arrays.AddEntry( deviceLastInputTimeStamp, 0 ); } deviceLastInputTimeStamp[ index ] = (float) ( DateTime.Now - _startTime ).TotalSeconds; if ( lastActive == lastActiveDevice ) { return; } Action.Trigger( onActiveDeviceChange ); _onActiveDeviceChange.DispatchEvent( null ); } public void UpdateLastActiveDevice( Sensor sensor, int index ) { if ( Engine.IsEditorHint() ) { return; } if ( keyboardDevice.ContainsSensor( sensor ) ) { UpdateDevice( separateMouseAndKeyboardTracking ? keyboardDevice : mouseKeyboardDevice ); return; } if ( mouseDevice.ContainsSensor( sensor ) ) { UpdateDevice( separateMouseAndKeyboardTracking ? mouseDevice : mouseKeyboardDevice ); return; } if ( gamePadDevice.ContainsSensor( sensor ) ) { var device = Arrays.Find( deviceList, d => d is GamePadDevice gpd && gpd.deviceIndex == index ); if ( device == null ) { var gpd = new GamePadDevice(); gpd.deviceIndex = index; gpd.deviceName = Input.GetJoyName( index ); device = gpd; } UpdateDevice( device ); } } // public override void _Ready() // { // if ( Engine.IsEditorHint() ) // { // return; // } // //this.LogInfo( "" ); // _startTime = DateTime.Now; // mouseKeyboardDevice.devices = new SensorDevice[]{ mouseDevice, keyboardDevice }; // if ( ! initializeOnReady ) // { // return; // } // CreateRunners(); // } public void Initialize() { _startTime = DateTime.Now; mouseKeyboardDevice.devices = new SensorDevice[]{ mouseDevice, keyboardDevice }; CreateRunners(); } public override void _Input( InputEvent ev ) { if ( Engine.IsEditorHint() ) { return; } //this.LogInfo( "" ); inputers.ForEach( ( inp )=> { inp._Input( ev ); } ); } public override void _Process( double delta ) { // //this.LogInfo( "" ); if ( Engine.IsEditorHint() || ! processSensors ) { return; } runners.ForEach( r => r.Update( (float) delta ) ); } public void Register( Sensor s, SensorInputHandler sih ) { if ( Engine.IsEditorHint() ) { return; } //this.LogInfo( "Register", s ); if ( ! sensorToRunner.ContainsKey( s ) ) { return; } var sensorRunner = sensorToRunner[ s ]; if ( sensorRunner.listeners.Contains( sih ) ) { return; } sensorRunner.listeners.Add( sih ); } public void Unregister( Sensor s, SensorInputHandler sih ) { if ( Engine.IsEditorHint() ) { return; } //this.LogInfo( "Unregister", s ); var sensorRunner = sensorToRunner[ s ]; sensorRunner.listeners.Remove( sih ); } public static void Register( SensorInputHandler handler, params Sensor[] sensors ) { if ( Engine.IsEditorHint() ) { return; } //RJLog.Log( "Register", sensors); var sm = Unique.Get(); if ( sm == null ) { Nodes.ForEachInScene( n => n.LogInfo() ); } foreach ( var s in sensors ) { if ( s == null ) { continue; } ( handler as Node ).LogInfo( "Registrating", HierarchyName.Of( (Node)handler) ); sm.Register( s, handler ); } } public static void Unregister( SensorInputHandler handler, params Sensor[] sensors ) { if ( Engine.IsEditorHint() ) { return; } //RJLog.Log( "Unregister", sensors); var sm = Unique.Get(); foreach ( var s in sensors ) { if ( s == null ) { continue; } ( handler as Node ).LogInfo( "Unregistrating" ); sm.Unregister( s, handler ); } } HashSet sensorsSet = new HashSet(); void AddSensor( Sensor s ) { if ( Engine.IsEditorHint() ) { return; } //this.LogInfo( "AddSensor", s); if ( s == null || sensorsSet.Contains( s ) ) { return; } // //this.LogInfo( "Including:", HierarchyName.Of( s ) ); AddSensorsFrom( s ); sensorsSet.Add( s ); runners.Add( new SensorRunner( s ) ); if ( showRegistratedSensors ) { registratedSensors = Arrays.AddEntry( registratedSensors, s ); } } HashSet _scannedObjects = new HashSet(); bool IsIgnoreType( object obj ) { var ignoreTypes = new List() { typeof( GpuParticles3D ), typeof( MeshInstance3D ), typeof( Node3D ), typeof( CollisionShape3D ), typeof( OmniLight3D ), typeof( DirectionalLight3D ), typeof( WorldEnvironment ), typeof( Area3D ), typeof( CharacterBody3D ), typeof( AudioStreamPlayer3D ), typeof( ReflectionProbe ) }; return ignoreTypes.IndexOf( obj.GetType() ) != -1; } void AddSensorsFrom( object obj ) { if ( Engine.IsEditorHint() ) { return; } //this.LogInfo( "AddSensorsFrom", obj ); try { if ( obj == null || _scannedObjects.Contains( obj ) ) { return; } _scannedObjects.Add( obj ); if ( IsIgnoreType( obj ) ) { return; } var sensors = ReflectionHelper.GetDataMemberValues( obj ); if ( sensors.Count > 0 ) { if ( obj is Node n ) { n.LogInfo( sensors ); } else if ( obj is Resource r ) { r.LogInfo( sensors ); } } sensors.ForEach( s => { AddSensor( s ); } ); var sensorArrays = ReflectionHelper.GetDataMemberValues( obj ); sensorArrays.ForEach( s => { for ( int i = 0; i < s.Length; i++ ) { AddSensor( s[ i ] ); } } ); var resources = ReflectionHelper.GetDataMemberValues( obj ); resources.ForEach( r => AddSensorsFrom( r ) ); } catch( System.Exception e ) { this.LogError( e ); } } void CreateRunners() { if ( Engine.IsEditorHint() ) { return; } //this.LogInfo( "CreateRunners" ); if ( sensors == null ) { sensors = new Sensor[]{}; } if ( sensorGroups == null ) { sensorGroups = new SensorGroup[]{}; } foreach ( var s in sensors ) { AddSensor( s ); } foreach ( var g in sensorGroups ) { foreach ( var s in g.sensors ) { AddSensor( s ); } } foreach ( var n in autoScanForSensors ) { Nodes.ForEach( n, AddSensorsFrom ); } // if ( autoScanParent ) // { // Nodes.ForEach( GetParent(), AddSensorsFrom ); // } runners.ForEach( r => { sensorToRunner[ r.sensor ] = r; if ( r.sensor is iOnInputSensor oi ) { inputers.Add( oi ); } } ); // this.LogInfo( "Created runners:", runners.Count ); } } }