20
20
using System ;
21
21
using System . Collections . Generic ;
22
22
using System . IO ;
23
+ using System . Diagnostics ;
23
24
using System . Linq ;
24
25
using System . Windows . Forms ;
26
+ using LibGit2Sharp ;
25
27
using Microsoft . Xna . Framework ;
26
28
using Microsoft . Xna . Framework . Graphics ;
27
29
using Orts . Simulation ;
30
32
using ORTS . Common ;
31
33
using ORTS . Common . Input ;
32
34
using ORTS . Settings ;
35
+ using static System . Windows . Forms . VisualStyles . VisualStyleElement ;
36
+ using Orts . Formats . Msts ;
37
+ using SharpFont ;
38
+ using static Orts . Viewer3D . WebServices . TrainCarOperationsWebpage . OperationsSend ;
39
+ using SharpDX . Direct3D9 ;
33
40
34
41
namespace Orts . Viewer3D . Popups
35
42
{
@@ -62,6 +69,14 @@ public class HelpWindow : Window
62
69
StreamWriter wDbfEval ; //Debrief eval
63
70
public static float DbfEvalDistanceTravelled = 0 ; //Debrief eval
64
71
72
+ // for Train Info tab
73
+ private Train LastPlayerTrain = null ;
74
+ private int LastPlayerTrainCarCount = 0 ;
75
+ private static Texture2D TrainInfoSpriteSheet = null ;
76
+ //private struct CarInfo { public float MassKg; public bool IsEngine; }
77
+ //private CarInfo[] CarMass = null; // TODO: Does not need to persist beyond method. Use List<T>(size) instead.
78
+ private class CarInfo { public readonly float MassKg ; public readonly bool IsEngine ; public CarInfo ( float mass , bool isEng ) { MassKg = mass ; IsEngine = isEng ; } }
79
+
65
80
List < TabData > Tabs = new List < TabData > ( ) ;
66
81
int ActiveTab ;
67
82
@@ -74,7 +89,7 @@ public void LogSeparator(int nCols)
74
89
}
75
90
76
91
public HelpWindow ( WindowManager owner )
77
- : base ( owner , Window . DecorationSize . X + owner . TextFontDefault . Height * 37 , Window . DecorationSize . Y + owner . TextFontDefault . Height * 24 , Viewer . Catalog . GetString ( "Help" ) )
92
+ : base ( owner , Window . DecorationSize . X + owner . TextFontDefault . Height * 42 , Window . DecorationSize . Y + owner . TextFontDefault . Height * 24 , Viewer . Catalog . GetString ( "Help" ) )
78
93
{
79
94
Tabs . Add ( new TabData ( Tab . KeyboardShortcuts , Viewer . Catalog . GetString ( "Key Commands" ) , ( cl ) =>
80
95
{
@@ -576,6 +591,22 @@ owner.Viewer.Simulator.PlayerLocomotive is MSTSLocomotive &&
576
591
scrollbox . Add ( new TextFlow ( scrollbox . RemainingWidth , ( ( MSTSLocomotive ) owner . Viewer . Simulator . PlayerLocomotive ) . EngineOperatingProcedures ) ) ;
577
592
}
578
593
} ) ) ;
594
+ Tabs . Add ( new TabData ( Tab . TrainInfo , Viewer . Catalog . GetString ( "Train Info" ) , ( cl ) =>
595
+ {
596
+ var labelWidth = cl . TextHeight * 11 ;
597
+ var scrollbox = cl . AddLayoutScrollboxVertical ( cl . RemainingWidth ) ;
598
+ if ( Owner . Viewer . PlayerTrain != null )
599
+ {
600
+ string name = Owner . Viewer . PlayerTrain . Name ;
601
+ if ( ! String . IsNullOrEmpty ( Owner . Viewer . Simulator . conFileName ) )
602
+ name += " (" + Viewer . Catalog . GetString ( "created from" ) + " " + Path . GetFileName ( Owner . Viewer . Simulator . conFileName ) + ")" ;
603
+ var line = scrollbox . AddLayoutHorizontalLineOfText ( ) ;
604
+ line . Add ( new Label ( labelWidth , line . RemainingHeight , Viewer . Catalog . GetString ( "Name:" ) , LabelAlignment . Left ) ) ;
605
+ line . Add ( new Label ( line . RemainingWidth , line . RemainingHeight , name , LabelAlignment . Left ) ) ;
606
+
607
+ if ( Owner . Viewer . PlayerTrain . Cars != null ) { AddAggregatedTrainInfo ( Owner . Viewer . PlayerTrain , scrollbox , labelWidth ) ; }
608
+ }
609
+ } ) ) ;
579
610
}
580
611
581
612
private void ReportEvaluation ( WindowManager owner , ControlLayout cl , TrainCar locomotive , int nmissedstation , string labeltext , int noverspeedcoupling , int dbfstationstopsremaining , Train playerTrain , int colWidth , Label indicator , bool lcurvespeeddependent , bool lbreakcouplers , int ndbfEvalTaskAccomplished )
@@ -1152,6 +1183,198 @@ private void writeline()
1152
1183
if ( ! lDebriefEvalFile ) wDbfEval . WriteLine ( ) ;
1153
1184
}
1154
1185
1186
+ /// <summary>
1187
+ /// Add aggregated info to the Train Info layout.
1188
+ /// Loops through the train cars and aggregates the info.
1189
+ /// </summary>
1190
+ private void AddAggregatedTrainInfo ( Train playerTrain , ControlLayout scrollbox , int labelWidth )
1191
+ {
1192
+ string numEngines = "" ; // using "+" between DPU sets
1193
+ int numCars = 0 ;
1194
+ int numAxles = 0 ;
1195
+ var massKg = playerTrain . MassKg ;
1196
+ string sectionMass = "" ; // using " + " between wagon sets
1197
+ var lengthM = playerTrain . Length ;
1198
+ var maxSpeedMps = playerTrain . TrainMaxSpeedMpS ;
1199
+ float totPowerW = 0f ;
1200
+ string sectionTractiveForce = "" ; // using " + " between DPU sets
1201
+ float maxBrakeForceN = 0f ;
1202
+ float lowestCouplerStrengthN = 9.999e8f ; // impossible high force
1203
+ float lowestDerailForceN = 9.999e8f ; // impossible high force
1204
+
1205
+ int engCount = 0 ; string countSeparator = "" ; // when set, indicates that subsequent engines are in a separate block
1206
+ float engForceN = 0f ; string forceSeparator = "" ; // when set, indicates that subsequent engines are in a separate block
1207
+ float wagMassKg = 0f ; string massSeparator = "" ; // when set, indicates that subsequent engines are in a separate block
1208
+ int numOperativeBrakes = 0 ;
1209
+ bool isMetric = false ; bool isUK = false ; // isImperial* does not seem to be used in simulation
1210
+
1211
+ if ( TrainInfoSpriteSheet == null ) { TrainInfoSpriteSheet = SharedTextureManager . Get ( Owner . Viewer . RenderProcess . GraphicsDevice , Path . Combine ( Owner . Viewer . ContentPath , "TrainInfoSprites.png" ) ) ; }
1212
+ const int spriteWidth = 6 ; const int spriteHeight = 26 ;
1213
+ var carInfoList = new List < CarInfo > ( playerTrain . Cars . Count ) ;
1214
+
1215
+ foreach ( var car in playerTrain . Cars )
1216
+ {
1217
+ // ignore (legacy) EOT
1218
+ if ( car . WagonType == TrainCar . WagonTypes . EOT || car . CarLengthM < 1.1f ) { continue ; }
1219
+
1220
+ var wag = car is MSTSWagon ? ( MSTSWagon ) car : null ;
1221
+ var eng = car is MSTSLocomotive ? ( MSTSLocomotive ) car : null ;
1222
+
1223
+ var isEng = ( car . WagonType == TrainCar . WagonTypes . Engine && eng != null && eng . MaxForceN > 25000 ) ; // count legacy driving trailers as wagons
1224
+
1225
+ if ( car . IsMetric ) { isMetric = true ; } ; if ( car . IsUK ) { isUK = true ; }
1226
+
1227
+ if ( isEng )
1228
+ {
1229
+ engCount ++ ;
1230
+ numAxles += eng . LocoNumDrvAxles + eng . GetWagonNumAxles ( ) ;
1231
+ totPowerW += eng . MaxPowerW ;
1232
+ engForceN += eng . MaxForceN ;
1233
+
1234
+ // hanlde transition from wagons to engines
1235
+ if ( wagMassKg > 0 )
1236
+ {
1237
+ sectionMass += massSeparator + FormatStrings . FormatLargeMass ( wagMassKg , isMetric , isUK ) ;
1238
+ wagMassKg = 0f ; massSeparator = " + " ;
1239
+ }
1240
+ }
1241
+ else if ( wag != null )
1242
+ {
1243
+ numCars ++ ;
1244
+ numAxles += wag . GetWagonNumAxles ( ) ;
1245
+ wagMassKg += wag . MassKG ;
1246
+
1247
+ // handle transition from engines to wagons
1248
+ if ( engCount > 0 || ( engCount == 0 && numCars == 0 ) )
1249
+ {
1250
+ numEngines += countSeparator + engCount . ToString ( ) ;
1251
+ engCount = 0 ; countSeparator = "+" ;
1252
+ sectionTractiveForce += forceSeparator + FormatStrings . FormatForce ( engForceN , isMetric ) ;
1253
+ engForceN = 0f ; forceSeparator = " + " ;
1254
+ }
1255
+ }
1256
+
1257
+ // wag and eng
1258
+ if ( wag != null )
1259
+ {
1260
+ maxBrakeForceN += wag . MaxBrakeForceN ;
1261
+ var couplerStrength = GetMinCouplerStrenght ( wag ) ;
1262
+ if ( couplerStrength < lowestCouplerStrengthN ) { lowestCouplerStrengthN = couplerStrength ; }
1263
+ var derailForce = GetDerailForce ( wag ) ;
1264
+ if ( derailForce < lowestDerailForceN ) { lowestDerailForceN = derailForce ; }
1265
+ if ( wag . MaxBrakeForceN > 0 ) { numOperativeBrakes ++ ; }
1266
+
1267
+ carInfoList . Add ( new CarInfo ( wag . MassKG , isEng ) ) ;
1268
+ }
1269
+ }
1270
+
1271
+ if ( engCount > 0 ) { numEngines = numEngines + countSeparator + engCount . ToString ( ) ; }
1272
+ if ( String . IsNullOrEmpty ( numEngines ) ) { numEngines = "0" ; }
1273
+ if ( engForceN > 0 ) { sectionTractiveForce += forceSeparator + FormatStrings . FormatForce ( engForceN , isMetric ) ; }
1274
+ if ( wagMassKg > 0 ) { sectionMass += massSeparator + FormatStrings . FormatLargeMass ( wagMassKg , isMetric , isUK ) ; }
1275
+
1276
+ var line = scrollbox . AddLayoutHorizontalLineOfText ( ) ;
1277
+ line . Add ( new Label ( labelWidth , line . RemainingHeight , Viewer . Catalog . GetString ( "Number of Engines:" ) , LabelAlignment . Left ) ) ;
1278
+ line . Add ( new Label ( line . RemainingWidth , line . RemainingHeight , numEngines , LabelAlignment . Left ) ) ;
1279
+
1280
+ line = scrollbox . AddLayoutHorizontalLineOfText ( ) ;
1281
+ line . Add ( new Label ( labelWidth , line . RemainingHeight , Viewer . Catalog . GetString ( "Number of Cars:" ) , LabelAlignment . Left ) ) ;
1282
+ line . Add ( new Label ( line . RemainingWidth , line . RemainingHeight , numCars . ToString ( ) , LabelAlignment . Left ) ) ;
1283
+
1284
+ line = scrollbox . AddLayoutHorizontalLineOfText ( ) ;
1285
+ line . Add ( new Label ( labelWidth , line . RemainingHeight , Viewer . Catalog . GetString ( "Number of Axles:" ) , LabelAlignment . Left ) ) ;
1286
+ line . Add ( new Label ( line . RemainingWidth , line . RemainingHeight , numAxles . ToString ( ) , LabelAlignment . Left ) ) ;
1287
+
1288
+ line = scrollbox . AddLayoutHorizontalLineOfText ( ) ;
1289
+ line . Add ( new Label ( labelWidth , line . RemainingHeight , Viewer . Catalog . GetString ( "Total Weight:" ) , LabelAlignment . Left ) ) ;
1290
+ string massValue = FormatStrings . FormatLargeMass ( massKg , isMetric , isUK ) + " (" + sectionMass + ")" ;
1291
+ line . Add ( new Label ( line . RemainingWidth , line . RemainingHeight , massValue , LabelAlignment . Left ) ) ;
1292
+
1293
+ line = scrollbox . AddLayoutHorizontalLineOfText ( ) ;
1294
+ line . Add ( new Label ( labelWidth , line . RemainingHeight , Viewer . Catalog . GetString ( "Total Length:" ) , LabelAlignment . Left ) ) ;
1295
+ line . Add ( new Label ( line . RemainingWidth , line . RemainingHeight , FormatStrings . FormatShortDistanceDisplay ( lengthM , isMetric ) , LabelAlignment . Left ) ) ;
1296
+
1297
+ line = scrollbox . AddLayoutHorizontalLineOfText ( ) ;
1298
+ line . Add ( new Label ( labelWidth , line . RemainingHeight , Viewer . Catalog . GetString ( "Maximum Speed:" ) , LabelAlignment . Left ) ) ;
1299
+ line . Add ( new Label ( line . RemainingWidth , line . RemainingHeight , FormatStrings . FormatSpeedLimit ( maxSpeedMps , isMetric ) , LabelAlignment . Left ) ) ;
1300
+
1301
+ scrollbox . AddHorizontalSeparator ( ) ;
1302
+
1303
+ line = scrollbox . AddLayoutHorizontalLineOfText ( ) ;
1304
+ line . Add ( new Label ( labelWidth , line . RemainingHeight , Viewer . Catalog . GetString ( "Total Power:" ) , LabelAlignment . Left ) ) ;
1305
+ line . Add ( new Label ( line . RemainingWidth , line . RemainingHeight , FormatStrings . FormatPower ( totPowerW , isMetric , false , false ) , LabelAlignment . Left ) ) ;
1306
+
1307
+ line = scrollbox . AddLayoutHorizontalLineOfText ( ) ;
1308
+ line . Add ( new Label ( labelWidth , line . RemainingHeight , Viewer . Catalog . GetString ( "Max Tractive Effort:" ) , LabelAlignment . Left ) ) ;
1309
+ line . Add ( new Label ( line . RemainingWidth , line . RemainingHeight , sectionTractiveForce , LabelAlignment . Left ) ) ;
1310
+
1311
+ if ( ! isMetric )
1312
+ {
1313
+ float hpt = massKg > 0f ? W . ToHp ( totPowerW ) / Kg . ToTUS ( massKg ) : 0f ;
1314
+ line = scrollbox . AddLayoutHorizontalLineOfText ( ) ;
1315
+ line . Add ( new Label ( labelWidth , line . RemainingHeight , Viewer . Catalog . GetString ( "Horespower per Ton:" ) , LabelAlignment . Left ) ) ;
1316
+ line . Add ( new Label ( line . RemainingWidth , line . RemainingHeight , string . Format ( "{0:0.0}" , hpt ) , LabelAlignment . Left ) ) ;
1317
+
1318
+ float tpob = numOperativeBrakes > 0 ? Kg . ToTUS ( massKg ) / numOperativeBrakes : 0 ;
1319
+ line = scrollbox . AddLayoutHorizontalLineOfText ( ) ;
1320
+ line . Add ( new Label ( labelWidth , line . RemainingHeight , Viewer . Catalog . GetString ( "Tons per Operative Brake:" ) , LabelAlignment . Left ) ) ;
1321
+ line . Add ( new Label ( line . RemainingWidth , line . RemainingHeight , string . Format ( "{0:0}" , tpob ) , LabelAlignment . Left ) ) ;
1322
+ }
1323
+
1324
+ line = scrollbox . AddLayoutHorizontalLineOfText ( ) ;
1325
+ line . Add ( new Label ( labelWidth , line . RemainingHeight , Viewer . Catalog . GetString ( "Lowest Coupler Strength:" ) , LabelAlignment . Left ) ) ;
1326
+ line . Add ( new Label ( line . RemainingWidth , line . RemainingHeight , FormatStrings . FormatForce ( lowestCouplerStrengthN , isMetric ) , LabelAlignment . Left ) ) ;
1327
+
1328
+ line = scrollbox . AddLayoutHorizontalLineOfText ( ) ;
1329
+ line . Add ( new Label ( labelWidth , line . RemainingHeight , Viewer . Catalog . GetString ( "Lowest Derail Force:" ) , LabelAlignment . Left ) ) ;
1330
+ line . Add ( new Label ( line . RemainingWidth , line . RemainingHeight , FormatStrings . FormatForce ( lowestDerailForceN , isMetric ) , LabelAlignment . Left ) ) ;
1331
+
1332
+ scrollbox . AddHorizontalSeparator ( ) ;
1333
+
1334
+ // weight graph
1335
+ line = scrollbox . AddLayoutHorizontalLineOfText ( ) ;
1336
+ line . Add ( new Label ( line . RemainingWidth , line . RemainingHeight , Viewer . Catalog . GetString ( "Car Weight (front on left):" ) , LabelAlignment . Left ) ) ;
1337
+ scrollbox . AddSpace ( scrollbox . RemainingWidth , 2 ) ;
1338
+ var hscrollbox = scrollbox . AddLayoutScrollboxHorizontal ( SystemInformation . HorizontalScrollBarHeight + spriteHeight + 2 ) ;
1339
+ var vbox = hscrollbox . AddLayoutVertical ( carInfoList . Count * spriteWidth + 2 ) ;
1340
+ var weightbox = vbox . AddLayoutHorizontal ( spriteHeight + 2 ) ;
1341
+ foreach ( var car in carInfoList )
1342
+ {
1343
+ int spriteIdx = ( int ) Math . Floor ( car . MassKg / 15000f ) ;
1344
+ if ( spriteIdx < 0 ) { spriteIdx = 0 ; } else if ( spriteIdx > 11 ) { spriteIdx = 11 ; }
1345
+ var image = new Image ( spriteWidth , spriteHeight ) ;
1346
+ if ( car . IsEngine ) { image . Source = new Rectangle ( spriteIdx * spriteWidth , 0 , spriteWidth , spriteHeight ) ; }
1347
+ else { image . Source = new Rectangle ( spriteIdx * spriteWidth , spriteHeight , spriteWidth , spriteHeight ) ; }
1348
+ image . Texture = TrainInfoSpriteSheet ;
1349
+ weightbox . Add ( image ) ;
1350
+ }
1351
+ }
1352
+
1353
+ /// <summary>
1354
+ /// Get the lowest coupler strength for a car.
1355
+ /// </summary>
1356
+ private float GetMinCouplerStrenght ( MSTSWagon wag )
1357
+ {
1358
+ float couplerStrength = 1e10f ; // default from TrainCar.GetCouplerBreak2N()
1359
+ if ( wag . Couplers . Count > 1 && wag . Couplers [ 1 ] . Break2N < couplerStrength ) { couplerStrength = wag . Couplers [ 1 ] . Break2N ; }
1360
+ else if ( wag . Couplers . Count > 0 && wag . Couplers [ 0 ] . Break2N < couplerStrength ) { couplerStrength = wag . Couplers [ 0 ] . Break2N ; }
1361
+ if ( couplerStrength < 99f ) { couplerStrength = 1e10f ; } // use default if near zero
1362
+ return couplerStrength ;
1363
+ }
1364
+
1365
+ /// <summary>
1366
+ /// Calculate the lowest force it takes to derail the car (on a curve).
1367
+ /// It is equivalent to the vertical force at the wheel.
1368
+ /// </summary>
1369
+ private float GetDerailForce ( MSTSWagon wag )
1370
+ {
1371
+ // see TotalWagonVerticalDerailForceN in TrainCar.cs
1372
+ float derailForce = 2.0e5f ; // 45k lbf on wheel
1373
+ int numWheels = wag . GetWagonNumAxles ( ) * 2 ;
1374
+ if ( numWheels > 2 ) { derailForce = wag . MassKG / numWheels * wag . GetGravitationalAccelerationMpS2 ( ) ; }
1375
+ return derailForce ;
1376
+ }
1377
+
1155
1378
public override void TabAction ( )
1156
1379
{
1157
1380
ActiveTab = ( ActiveTab + 1 ) % Tabs . Count ;
@@ -1195,6 +1418,7 @@ enum Tab
1195
1418
ActivityEvaluation ,
1196
1419
TimetableBriefing ,
1197
1420
LocomotiveProcedures ,
1421
+ TrainInfo ,
1198
1422
}
1199
1423
1200
1424
class TabData
@@ -1253,6 +1477,14 @@ public override void PrepareFrame(ElapsedTime elapsedTime, bool updateFull)
1253
1477
Layout ( ) ;
1254
1478
}
1255
1479
}
1480
+ else if ( Tabs [ ActiveTab ] . Tab == Tab . TrainInfo && Owner . Viewer . PlayerTrain != null )
1481
+ {
1482
+ if ( LastPlayerTrain == null || Owner . Viewer . PlayerTrain != LastPlayerTrain || LastPlayerTrainCarCount != Owner . Viewer . PlayerTrain . Cars . Count )
1483
+ {
1484
+ LastPlayerTrain = Owner . Viewer . PlayerTrain ; LastPlayerTrainCarCount = Owner . Viewer . PlayerTrain . Cars . Count ;
1485
+ Layout ( ) ;
1486
+ }
1487
+ }
1256
1488
if ( this . ActivityUpdated == true ) //true value is set in ActivityWindow.cs
1257
1489
{
1258
1490
this . ActivityUpdated = false ;
0 commit comments