@@ -67,6 +67,11 @@ public class LocoCruiseControl : IDisposable
6767 private MethodInfo _tryGetPortMI ;
6868 private PropertyInfo _portValueProp ;
6969
70+ // DM3 cache for simFlow.TryGetPort
71+ private SimController _dm3SimCtrl ;
72+ private MethodInfo _dm3TryGetPortMI ;
73+ private PropertyInfo _dm3PortValueProp ;
74+
7075 // ────────────────────────────────────────────────────────────────────
7176
7277 public LocoCruiseControl ( ILocomotiveRemoteControl remoteControl , TrainCar car = null )
@@ -176,24 +181,126 @@ protected float MaintainSpeed(float targetAcceleration, float dt, float speed, f
176181 }
177182
178183 if ( error < - 3.0f || TargetSpeed < Mathf . Epsilon )
179- remoteControl . UpdateIndependentBrake ( 0.3f * error * - 1.0f * dt ) ;
180- else if ( remoteControl . GetTargetIndependentBrake ( ) > Mathf . Epsilon )
181- remoteControl . UpdateIndependentBrake ( - 30.0f * dt ) ;
184+ remoteControl . UpdateBrake ( 0.3f * error * - 1.0f * dt ) ;
185+ else if ( remoteControl . GetTargetBrake ( ) > Mathf . Epsilon )
186+ remoteControl . UpdateBrake ( - 30.0f * dt ) ;
182187
183188 remoteControl . UpdateThrottle ( ThrottleCurveFactor ( remoteControl . GetTargetThrottle ( ) , controlValue > 0.0f ) * controlValue ) ;
184189
185190 return targetAcceleration ;
186191 }
187192
193+ private float GetDM3Rpm ( )
194+ {
195+ try
196+ {
197+ if ( _dm3SimCtrl == null )
198+ _dm3SimCtrl = trainCar ? . SimController ;
199+
200+ if ( _dm3SimCtrl ? . simFlow == null )
201+ return - 1f ;
202+
203+ if ( _dm3TryGetPortMI == null )
204+ _dm3TryGetPortMI = _dm3SimCtrl . simFlow . GetType ( )
205+ . GetMethod ( "TryGetPort" , BindingFlags . Instance | BindingFlags . Public ) ;
206+
207+ if ( _dm3TryGetPortMI == null )
208+ return - 1f ;
209+
210+ var args = new object [ ] { "de.RPM" , null , true } ;
211+ if ( ! ( bool ) _dm3TryGetPortMI . Invoke ( _dm3SimCtrl . simFlow , args ) || args [ 1 ] == null )
212+ return - 1f ;
213+
214+ if ( _dm3PortValueProp == null )
215+ _dm3PortValueProp = args [ 1 ] . GetType ( ) . GetProperty ( "Value" ) ;
216+
217+ return _dm3PortValueProp != null
218+ ? ( float ) _dm3PortValueProp . GetValue ( args [ 1 ] )
219+ : - 1f ;
220+ }
221+ catch
222+ {
223+ return - 1f ;
224+ }
225+ }
226+
227+ private struct DM3GearPosition
228+ {
229+ public int GearA ;
230+ public int GearB ;
231+
232+ public DM3GearPosition ( int gearA , int gearB )
233+ {
234+ GearA = gearA ;
235+ GearB = gearB ;
236+ }
237+
238+ public override string ToString ( )
239+ {
240+ return $ "{ GearA } ,{ GearB } ";
241+ }
242+ }
243+
244+ private int _currentDM3GearIndex = 0 ;
245+
246+ private static readonly DM3GearPosition [ ] DM3_GEAR_SEQUENCE =
247+ {
248+ new DM3GearPosition ( 1 , 1 ) ,
249+ new DM3GearPosition ( 1 , 2 ) ,
250+ new DM3GearPosition ( 2 , 1 ) ,
251+ new DM3GearPosition ( 2 , 2 ) ,
252+ new DM3GearPosition ( 3 , 1 ) ,
253+ // new DM3GearPosition(1, 3), // intentionally skipped
254+ new DM3GearPosition ( 3 , 2 ) ,
255+ new DM3GearPosition ( 2 , 3 ) ,
256+ new DM3GearPosition ( 3 , 3 ) ,
257+ } ;
258+
259+ private bool ShiftDM3Gear ( int direction )
260+ {
261+ var controls = GetInteriorControls ( ) ;
262+ if ( controls == null )
263+ return false ;
264+
265+ int targetIndex = _currentDM3GearIndex + direction ;
266+
267+ DM3GearPosition current = DM3_GEAR_SEQUENCE [ _currentDM3GearIndex ] ;
268+ DM3GearPosition target = DM3_GEAR_SEQUENCE [ targetIndex ] ;
269+
270+ if ( target . GearA > current . GearA )
271+ controls . MoveScrollable ( InteriorControlsManager . ControlType . GearboxA , 1 ) ;
272+ else if ( target . GearA < current . GearA )
273+ controls . MoveScrollable ( InteriorControlsManager . ControlType . GearboxA , - 1 ) ;
274+
275+ if ( target . GearB > current . GearB )
276+ controls . MoveScrollable ( InteriorControlsManager . ControlType . GearboxB , 1 ) ;
277+ else if ( target . GearB < current . GearB )
278+ controls . MoveScrollable ( InteriorControlsManager . ControlType . GearboxB , - 1 ) ;
279+
280+ _currentDM3GearIndex = targetIndex ;
281+ return true ;
282+ }
283+
188284 // ── DM3 gear management ──────────────────────────────────────────────
189285 // Returns true while a shift is in progress (PID caller should skip output).
190286 private bool HandleDM3GearShift ( float dt )
191287 {
192- float rpm = _indicators ? . engineRpm ? . Value ?? 0f ;
288+ float rpm = GetDM3Rpm ( ) ;
289+ if ( rpm < 0f )
290+ return false ;
291+
193292 float now = Time . time ;
194293
195294 if ( _awaitingShift )
196295 {
296+ int targetIndex = _currentDM3GearIndex + _pendingShiftDir ;
297+ if ( targetIndex < 0 || targetIndex >= DM3_GEAR_SEQUENCE . Length )
298+ {
299+ _lastGearRpm = rpm ;
300+ _awaitingShift = false ;
301+ return false ; // Dont shift if already on min or max gear
302+ }
303+
197304 // Zero throttle and wait for RPM to settle before moving the lever
198305 remoteControl . UpdateThrottle ( - 100f ) ;
199306
@@ -203,14 +310,13 @@ private bool HandleDM3GearShift(float dt)
203310 var controls = GetInteriorControls ( ) ;
204311 if ( controls != null )
205312 {
206- controls . MoveScrollable ( InteriorControlsManager . ControlType . GearboxA , _pendingShiftDir ) ;
207- controls . MoveScrollable ( InteriorControlsManager . ControlType . GearboxB , _pendingShiftDir ) ;
313+ ShiftDM3Gear ( _pendingShiftDir ) ;
208314#if DEBUG
209- Terminal . Log ( $ "DM3: gear lever moved { ( _pendingShiftDir > 0 ? "up" : "down" ) } (RPM { rpm : 0} )") ;
315+ Terminal . Log ( $ "DM3: shifted to { DM3_GEAR_SEQUENCE [ _currentDM3GearIndex ] } (RPM { rpm : 0} )") ;
210316#endif
317+ _awaitingShift = false ;
318+ _lastShiftTime = now ;
211319 }
212- _awaitingShift = false ;
213- _lastShiftTime = now ;
214320 }
215321
216322 _lastGearRpm = rpm ;
0 commit comments