@@ -11,35 +11,33 @@ namespace ShipSaveSplicer
11
11
[ KSPAddon ( KSPAddon . Startup . TrackingStation , false ) ]
12
12
public class ShipSaveSplicer : MonoBehaviour
13
13
{
14
- public static ApplicationLauncherButton theButton ;
15
- public static bool includeCrew = false ;
16
- private static bool EventAdded = false ;
14
+ private static ApplicationLauncherButton _theButton ;
15
+ private static bool _includeCrew = false ;
16
+ private static bool _eventAdded = false ;
17
17
public void Start ( )
18
18
{
19
19
//add button to the Stock toolbar
20
- if ( ! EventAdded )
20
+ if ( ! _eventAdded )
21
21
{
22
22
GameEvents . onGUIApplicationLauncherReady . Add ( AddButton ) ;
23
- EventAdded = true ;
23
+ _eventAdded = true ;
24
24
}
25
-
26
- //AddButton();
27
25
}
28
26
29
27
public void OnDestroy ( )
30
28
{
31
- if ( theButton != null )
29
+ if ( _theButton != null )
32
30
{
33
- ApplicationLauncher . Instance . RemoveModApplication ( theButton ) ;
34
- theButton = null ;
31
+ ApplicationLauncher . Instance . RemoveModApplication ( _theButton ) ;
32
+ _theButton = null ;
35
33
}
36
34
}
37
35
38
36
public void AddButton ( )
39
37
{
40
- if ( ApplicationLauncher . Ready && theButton == null )
38
+ if ( ApplicationLauncher . Ready && _theButton == null && HighLogic . LoadedScene == GameScenes . TRACKSTATION )
41
39
{
42
- theButton = ApplicationLauncher . Instance . AddModApplication (
40
+ _theButton = ApplicationLauncher . Instance . AddModApplication (
43
41
OnClick ,
44
42
Dummy , Dummy , Dummy , Dummy , Dummy , ApplicationLauncher . AppScenes . TRACKSTATION , GameDatabase . Instance . GetTexture ( "ShipSaveSplicer/icon" , false ) ) ;
45
43
}
@@ -50,22 +48,22 @@ private void Dummy() { }
50
48
public void OnClick ( )
51
49
{
52
50
//figure out if mod+clicked
53
- //includeCrew = GameSettings.MODIFIER_KEY.GetKey(); //TODO: Reenable when fixed
54
- includeCrew = false ;
51
+ _includeCrew = GameSettings . MODIFIER_KEY . GetKey ( ) ;
52
+ // includeCrew = false;
55
53
bool ctrlHeld = Input . GetKey ( KeyCode . LeftControl ) ;
56
54
57
55
if ( Input . GetKey ( KeyCode . LeftShift ) )
58
56
{
59
57
OpenConvertWindow ( ) ; //convert ships to craft files
60
- theButton . SetFalse ( ) ;
58
+ _theButton . SetFalse ( ) ;
61
59
return ;
62
60
}
63
61
64
62
//get the selected craft
65
63
SpaceTracking trackingStation = ( SpaceTracking ) FindObjectOfType ( typeof ( SpaceTracking ) ) ;
66
64
Vessel selectedVessel = trackingStation . SelectedVessel ;
67
65
68
- if ( ctrlHeld || includeCrew ) //ctrl or modifier held
66
+ if ( ctrlHeld || _includeCrew ) //ctrl or modifier held
69
67
{
70
68
OpenImportWindow ( ) ;
71
69
}
@@ -74,14 +72,16 @@ public void OnClick()
74
72
ExportSelectedCraft ( selectedVessel ) ;
75
73
}
76
74
77
- theButton . SetFalse ( false ) ;
75
+ _theButton . SetFalse ( false ) ;
78
76
}
79
77
80
78
public void ExportSelectedCraft ( Vessel vessel )
81
79
{
82
80
CreateFolder ( ) ;
83
81
84
82
string filename = KSPUtil . ApplicationRootPath + "/Ships/export/" + HighLogic . SaveFolder + "_" + vessel . vesselName ;
83
+ Log ( $ "Exporting vessel: { vessel . vesselName } \n Exporting to file: { filename } ") ;
84
+
85
85
ConfigNode nodeToSave = new ConfigNode ( ) ;
86
86
87
87
//save vessel
@@ -116,7 +116,7 @@ public void OpenConvertWindow()
116
116
for ( int i = 0 ; i < count ; i ++ )
117
117
{
118
118
int select = i ;
119
- options [ i ] = new DialogGUIButton ( files [ i ] . Split ( '/' ) . Last ( ) , ( ) => { ConvertVessel ( files [ select ] ) ; } ) ;
119
+ options [ i ] = new DialogGUIButton ( files [ i ] . Split ( '/' ) . Last ( ) , ( ) => { ConvertToCraft ( files [ select ] ) ; } ) ;
120
120
}
121
121
options [ count ] = new DialogGUIButton ( "Close" , Dummy ) ;
122
122
string msg = "Select a vessel to convert to a .craft file." ;
@@ -125,21 +125,17 @@ public void OpenConvertWindow()
125
125
PopupDialog . SpawnPopupDialog ( new Vector2 ( 0.5f , 0.5f ) , new Vector2 ( 0.5f , 0.5f ) , a , false , HighLogic . UISkin ) ;
126
126
}
127
127
128
- public void ConvertVessel ( string name )
128
+ public void ConvertToCraft ( string name )
129
129
{
130
130
if ( System . IO . File . Exists ( name ) )
131
131
{
132
+ Log ( $ "Converting ship to craft file: { name } ") ;
132
133
ConfigNode storedNode = ConfigNode . Load ( name ) ;
133
134
134
135
ConfigNode vesselNode = storedNode . GetNode ( "VESSEL" ) ;
135
136
136
- List < string > invalidParts = InvalidParts ( vesselNode ) ;
137
- if ( invalidParts . Count > 0 ) //contains invalid parts and can't be loaded
137
+ if ( WarnOfInvalidParts ( vesselNode , false ) )
138
138
{
139
- StringBuilder msg = new StringBuilder ( "The selected vessel cannot be converted because it contains the following invalid parts (perhaps you removed a mod?):\n " ) ;
140
- foreach ( string invalid in invalidParts )
141
- msg . Append ( " " ) . AppendLine ( invalid ) ;
142
- PopupDialog . SpawnPopupDialog ( new Vector2 ( 0.5f , 0.5f ) , new Vector2 ( 0.5f , 0.5f ) , "missingPartsPopup" , "Missing Parts" , msg . ToString ( ) , "Ok" , false , HighLogic . UISkin ) ;
143
139
return ;
144
140
}
145
141
//clear out all crew on vessel
@@ -165,14 +161,13 @@ public void VesselToCraftFile(ConfigNode VesselNode)
165
161
ProtoVessel VesselToSave = HighLogic . CurrentGame . AddVessel ( VesselNode ) ;
166
162
if ( VesselToSave . vesselRef == null )
167
163
{
168
- Debug . LogError ( "Vessel reference is null!" ) ;
164
+ Log ( "Vessel reference is null!" ) ;
169
165
return ;
170
166
}
171
167
172
168
try
173
169
{
174
170
string ShipName = VesselToSave . vesselName ;
175
- // Debug.LogWarning("Saving: " + ShipName);
176
171
177
172
//Vessel FromFlight = FlightGlobals.Vessels.Find(v => v.id == VesselToSave.vesselID);
178
173
try
@@ -182,7 +177,7 @@ public void VesselToCraftFile(ConfigNode VesselNode)
182
177
catch ( Exception ex )
183
178
{
184
179
Debug . LogException ( ex ) ;
185
- Debug . Log ( "Attempting to continue." ) ;
180
+ Log ( "Attempting to continue." ) ;
186
181
}
187
182
188
183
ShipConstruct ConstructToSave = new ShipConstruct ( ShipName , "" , VesselToSave . vesselRef . Parts [ 0 ] ) ;
@@ -247,8 +242,7 @@ public void OpenImportWindow()
247
242
options [ i ] = new DialogGUIButton ( files [ i ] . Split ( '/' ) . Last ( ) , ( ) => { ImportVessel ( files [ select ] ) ; } ) ;
248
243
}
249
244
options [ count ] = new DialogGUIButton ( "Close" , Dummy ) ;
250
- string msg = "Select a vessel to import. Will " + ( includeCrew ? "" : "not " ) + "import crew members." ; // +(includeCrew ? "": " (modifier+click the SSS button to include crew)");
251
- //TODO: Reenable when fixed
245
+ string msg = string . Format ( "Select a vessel to import. Will {0}import crew.{1}" , ( _includeCrew ? string . Empty : "not " ) , ( _includeCrew ? string . Empty : "\n (modifier+click the SSS button to include crew)" ) ) ;
252
246
253
247
MultiOptionDialog a = new MultiOptionDialog ( "importPopup" , msg , "Import Vessel" , null , options ) ;
254
248
PopupDialog . SpawnPopupDialog ( new Vector2 ( 0.5f , 0.5f ) , new Vector2 ( 0.5f , 0.5f ) , a , false , HighLogic . UISkin ) ;
@@ -258,25 +252,21 @@ public void ImportVessel(string name)
258
252
{
259
253
if ( System . IO . File . Exists ( name ) )
260
254
{
255
+ Log ( $ "Importing vessel: { name } ") ;
261
256
ConfigNode storedNode = ConfigNode . Load ( name ) ;
262
257
263
258
ConfigNode vesselNode = storedNode . GetNode ( "VESSEL" ) ;
264
259
265
260
vesselNode . SetValue ( "pid" , Guid . NewGuid ( ) . ToString ( ) ) ;
266
261
267
- List < string > invalidParts = InvalidParts ( vesselNode ) ;
268
- if ( invalidParts . Count > 0 ) //contains invalid parts and can't be loaded
262
+ if ( WarnOfInvalidParts ( vesselNode , true ) )
269
263
{
270
- string msg = "The selected vessel cannot be imported because it contains the following invalid parts (perhaps you removed a mod?):\n " ;
271
- foreach ( string invalid in invalidParts )
272
- msg += " " + invalid + "\n " ;
273
- PopupDialog . SpawnPopupDialog ( new Vector2 ( 0.5f , 0.5f ) , new Vector2 ( 0.5f , 0.5f ) , "missingPartsPopup" , "Missing Parts" , msg , "Ok" , false , HighLogic . UISkin ) ;
274
264
return ;
275
265
}
276
266
267
+ List < ProtoCrewMember > crewAdded = new List < ProtoCrewMember > ( ) ;
277
268
278
-
279
- if ( ! includeCrew )
269
+ if ( ! _includeCrew )
280
270
{
281
271
//clear out all crew on vessel
282
272
StripCrew ( vesselNode ) ;
@@ -294,29 +284,35 @@ public void ImportVessel(string name)
294
284
foreach ( ConfigNode . Value crewValue in partNode . values ) //(string crewmember in partNode.GetValues("crew"))
295
285
{
296
286
if ( crewValue . name != "crew" )
287
+ {
297
288
continue ;
289
+ }
290
+
298
291
string crewmember = crewValue . value ;
299
292
//find the confignode saved with the vessel
300
293
ConfigNode crewNode = storedNode . GetNodes ( "CREW" ) ? . FirstOrDefault ( c => c . GetValue ( "name" ) == crewmember ) ;
301
294
if ( crewNode == null || crewNode . GetValue ( "type" ) != "Crew" ) //if no data or is tourist then remove from ship
302
295
{
303
296
//can't find the required data, so remove that kerbal from the ship
297
+ Log ( $ "Vessel occupant is not crew: { crewmember } ") ;
304
298
toRemove . Add ( crewmember ) ;
305
299
continue ;
306
300
}
307
301
308
302
309
303
ProtoCrewMember newCrew = new ProtoCrewMember ( HighLogic . CurrentGame . Mode , crewNode , ProtoCrewMember . KerbalType . Crew ) ;
310
- if ( HighLogic . CurrentGame . CrewRoster . Crew . FirstOrDefault ( c => c . name == crewmember ) != null ) //there's already a kerbal with that name (sadness :( )
304
+ if ( HighLogic . CurrentGame . CrewRoster . Exists ( crewmember ) ) //there's already a kerbal with that name (sadness :( )
311
305
{
312
306
//alright, rename this kerbal to a new name
313
307
string newName = RenameKerbal ( crewmember ) ;
314
308
newCrew . ChangeName ( newName ) ;
315
309
crewValue . value = newName ;
316
310
}
311
+ Log ( $ "Creating crewmember { newCrew . name } ") ;
317
312
//add the crew member to the crew roster
318
313
//the function to do this is hidden for some reason. yay!
319
314
HighLogic . CurrentGame . CrewRoster . AddCrewMember ( newCrew ) ; //no longer hidden! MORE YAY!
315
+ crewAdded . Add ( newCrew ) ;
320
316
}
321
317
foreach ( string crewmember in toRemove ) //remove all crews that shouldn't be here anymore
322
318
{
@@ -325,6 +321,7 @@ public void ImportVessel(string name)
325
321
{
326
322
if ( val . name == "crew" && val . value == crewmember )
327
323
{
324
+ Log ( $ "Removing non-valid crew member { val . value } ") ;
328
325
partNode . values . Remove ( val ) ;
329
326
break ;
330
327
}
@@ -335,7 +332,7 @@ public void ImportVessel(string name)
335
332
}
336
333
catch ( Exception ex )
337
334
{
338
- Debug . Log ( "[ShipSaveSplicer] Encountered exception while transferring crew. The exception follows. Stripping crew data." ) ;
335
+ Log ( "Encountered exception while transferring crew. The exception follows. Stripping crew data." ) ;
339
336
Debug . LogException ( ex ) ;
340
337
341
338
StripCrew ( vesselNode ) ;
@@ -349,15 +346,19 @@ public void ImportVessel(string name)
349
346
}
350
347
351
348
ProtoVessel addedVessel = HighLogic . CurrentGame . AddVessel ( vesselNode ) ;
352
- //we might have to assign the kerbals to the vessel here
353
-
354
- //related issue, in 1.2.2 (at least) we fail validation of assignments when there are two ships with the same part ids (guessing).
355
- //All I know is that if I terminate the original flight, I can import crew. Otherwise it NREs when I save.
349
+ foreach ( ProtoCrewMember crew in crewAdded )
350
+ {
351
+ Log ( $ "Assigning { crew . name } ") ;
352
+ addedVessel . AddCrew ( crew ) ;
353
+ }
354
+ //In 1.2.2+ saving fails when there are two copies of a ship with crew onboard both. Might be part ID related.
355
+ //All I know is that if I terminate the original flight, I can import crew. Otherwise it NREs when it tries to save the flight state.
356
356
}
357
357
}
358
358
359
359
private void StripCrew ( ConfigNode vesselNode )
360
360
{
361
+ Log ( "Stripping out crew information" ) ;
361
362
foreach ( ConfigNode partNode in vesselNode . GetNodes ( "PART" ) )
362
363
{
363
364
if ( partNode . HasValue ( "crew" ) )
@@ -395,7 +396,9 @@ public void CreateFolder()
395
396
{
396
397
string filename = KSPUtil . ApplicationRootPath + "/Ships/export/" ;
397
398
if ( ! System . IO . Directory . Exists ( filename ) )
399
+ {
398
400
System . IO . Directory . CreateDirectory ( filename ) ;
401
+ }
399
402
}
400
403
401
404
public List < string > InvalidParts ( ConfigNode vesselNode )
@@ -408,21 +411,51 @@ public List<string> InvalidParts(ConfigNode vesselNode)
408
411
{
409
412
string partName = PartNameFromNode ( partNode ) ;
410
413
if ( ! invalid . Contains ( partName ) && PartLoader . getPartInfoByName ( partName ) == null ) //don't add duplicates
414
+ {
411
415
invalid . Add ( partName ) ;
416
+ }
412
417
}
413
418
return invalid ;
414
419
}
415
420
421
+ public bool WarnOfInvalidParts ( ConfigNode vesselNode , bool importing )
422
+ {
423
+ List < string > invalidParts = InvalidParts ( vesselNode ) ;
424
+ if ( invalidParts . Count > 0 )
425
+ {
426
+ string action = importing ? "impoerted" : "converted" ;
427
+ StringBuilder msg = new StringBuilder ( $ "The selected vessel cannot be { action } because it contains the following invalid parts (perhaps you removed a mod?):") . AppendLine ( ) ;
428
+ foreach ( string invalid in invalidParts )
429
+ {
430
+ msg . Append ( " " ) . AppendLine ( invalid ) ;
431
+ }
432
+
433
+ PopupDialog . SpawnPopupDialog ( new Vector2 ( 0.5f , 0.5f ) , new Vector2 ( 0.5f , 0.5f ) , "missingPartsPopup" , "Missing Parts" , msg . ToString ( ) , "Ok" , false , HighLogic . UISkin ) ;
434
+ return true ;
435
+ }
436
+ return false ;
437
+ }
438
+
416
439
public string PartNameFromNode ( ConfigNode part )
417
440
{
418
441
string name = part . GetValue ( "part" ) ;
419
442
if ( name != null )
443
+ {
420
444
name = name . Split ( '_' ) [ 0 ] ;
445
+ }
421
446
else
447
+ {
422
448
name = part . GetValue ( "name" ) ;
449
+ }
450
+
423
451
return name ;
424
452
}
425
453
454
+ public void Log ( object msg )
455
+ {
456
+ Debug . Log ( "[ShipSaveSplicer] " + msg . ToString ( ) ) ;
457
+ }
458
+
426
459
/* The following is directly from Claw's InflightShipSave and credit goes to the original author */
427
460
private void CleanEditorNodes ( ConfigNode CN )
428
461
{
@@ -467,11 +500,11 @@ private void PristineNodes(ConfigNode CN)
467
500
{
468
501
string PartName = ( ( CN . GetValue ( "part" ) ) . Split ( '_' ) ) [ 0 ] ;
469
502
470
- Debug . LogWarning ( "PART: " + PartName ) ;
503
+ Log ( "PART: " + PartName ) ;
471
504
472
505
Part NewPart = PartLoader . getPartInfoByName ( PartName ) . partPrefab ;
473
506
ConfigNode NewPartCN = new ConfigNode ( ) ;
474
- Debug . LogWarning ( "New Part: " + NewPart . name ) ;
507
+ Log ( "New Part: " + NewPart . name ) ;
475
508
476
509
NewPart . InitializeModules ( ) ;
477
510
@@ -510,7 +543,7 @@ private void PristineNodes(ConfigNode CN)
510
543
}
511
544
}
512
545
/*
513
- Copyright (C) 2017 Michael Marvin
546
+ Copyright (C) 2018 Michael Marvin
514
547
515
548
This program is free software: you can redistribute it and/or modify
516
549
it under the terms of the GNU General Public License as published by
0 commit comments