From 113db9dcfba9721640859d8ea1dbe2e1b8c72854 Mon Sep 17 00:00:00 2001 From: Katy Fox Date: Wed, 7 Jul 2021 10:32:51 -0400 Subject: [PATCH 01/20] Add game scripts DLL project, LocoSimType --- CLL_GameScripts/CLL_GameScripts.csproj | 49 +++++ .../CarJSONKeys.cs | 5 +- CLL_GameScripts/CustomCarEnums.cs | 60 ++++++ CLL_GameScripts/Properties/AssemblyInfo.cs | 36 ++++ DVCustomCarLoader.sln | 13 +- DVCustomCarLoader/CustomCar.cs | 6 + DVCustomCarLoader/CustomCarManager.cs | 191 ++++++++---------- DVCustomCarLoader/DVCustomCarLoader.csproj | 7 +- 8 files changed, 257 insertions(+), 110 deletions(-) create mode 100644 CLL_GameScripts/CLL_GameScripts.csproj rename {DVCustomCarLoader => CLL_GameScripts}/CarJSONKeys.cs (89%) create mode 100644 CLL_GameScripts/CustomCarEnums.cs create mode 100644 CLL_GameScripts/Properties/AssemblyInfo.cs diff --git a/CLL_GameScripts/CLL_GameScripts.csproj b/CLL_GameScripts/CLL_GameScripts.csproj new file mode 100644 index 00000000..a5897ee3 --- /dev/null +++ b/CLL_GameScripts/CLL_GameScripts.csproj @@ -0,0 +1,49 @@ + + + + + Debug + AnyCPU + {C191C5AC-990E-4CA6-9847-342B9CD92465} + Library + Properties + CLL_GameScripts + CLL_GameScripts + v4.8 + 512 + true + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/DVCustomCarLoader/CarJSONKeys.cs b/CLL_GameScripts/CarJSONKeys.cs similarity index 89% rename from DVCustomCarLoader/CarJSONKeys.cs rename to CLL_GameScripts/CarJSONKeys.cs index 209edace..cff19e04 100644 --- a/DVCustomCarLoader/CarJSONKeys.cs +++ b/CLL_GameScripts/CarJSONKeys.cs @@ -1,10 +1,8 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Text; -using System.Threading.Tasks; -namespace DVCustomCarLoader +namespace CLL_GameScripts { public class CarJSONKeys { @@ -12,6 +10,7 @@ public class CarJSONKeys public const string PREFAB_NAME = "carPrefabName"; public const string IDENTIFIER = "identifier"; public const string CAR_TYPE = "carType"; + public const string SIM_TYPE = "simType"; public const string REPLACE_FRONT_BOGIE = "frontBogieReplacement"; public const string FRONT_BOGIE_PARAMS = "frontBogieParams"; diff --git a/CLL_GameScripts/CustomCarEnums.cs b/CLL_GameScripts/CustomCarEnums.cs new file mode 100644 index 00000000..db600af8 --- /dev/null +++ b/CLL_GameScripts/CustomCarEnums.cs @@ -0,0 +1,60 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CLL_GameScripts +{ + /// + /// The underlying type of cars. + /// + public enum BaseTrainCarType + { + NotSet = 0, + LocoShunter = 10, + LocoSteamHeavy = 20, + Tender = 21, + LocoSteamHeavyBlue = 22, + TenderBlue = 23, + LocoRailbus = 30, + LocoDiesel = 40, + FlatbedEmpty = 200, + FlatbedStakes = 201, + FlatbedMilitary = 202, + AutorackRed = 250, + AutorackBlue = 251, + AutorackGreen = 252, + AutorackYellow = 253, + TankOrange = 300, + TankWhite = 301, + TankYellow = 302, + TankBlue = 303, + TankChrome = 304, + TankBlack = 305, + BoxcarBrown = 400, + BoxcarGreen = 401, + BoxcarPink = 402, + BoxcarRed = 403, + BoxcarMilitary = 404, + RefrigeratorWhite = 450, + HopperBrown = 500, + HopperTeal = 501, + HopperYellow = 502, + PassengerRed = 600, + PassengerGreen = 601, + PassengerBlue = 602, + HandCar = 700, + NuclearFlask = 800 + } + + /// + /// The loco simulation type to use with the car. + /// + public enum LocoSimType + { + None = 0, + DieselElectric = 1, + Steam = 2 + } +} diff --git a/CLL_GameScripts/Properties/AssemblyInfo.cs b/CLL_GameScripts/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..8b11e506 --- /dev/null +++ b/CLL_GameScripts/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("CLL_GameScripts")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("CLL_GameScripts")] +[assembly: AssemblyCopyright("Copyright © 2021")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("c191c5ac-990e-4ca6-9847-342b9cd92465")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/DVCustomCarLoader.sln b/DVCustomCarLoader.sln index 71989133..42109ae8 100644 --- a/DVCustomCarLoader.sln +++ b/DVCustomCarLoader.sln @@ -1,10 +1,12 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.26228.4 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.31424.327 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DVCustomCarLoader", "DVCustomCarLoader\DVCustomCarLoader.csproj", "{1380FDD7-0144-49AA-A2CD-BA1EB02EE306}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CLL_GameScripts", "CLL_GameScripts\CLL_GameScripts.csproj", "{C191C5AC-990E-4CA6-9847-342B9CD92465}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -15,8 +17,15 @@ Global {1380FDD7-0144-49AA-A2CD-BA1EB02EE306}.Debug|Any CPU.Build.0 = Debug|Any CPU {1380FDD7-0144-49AA-A2CD-BA1EB02EE306}.Release|Any CPU.ActiveCfg = Release|Any CPU {1380FDD7-0144-49AA-A2CD-BA1EB02EE306}.Release|Any CPU.Build.0 = Release|Any CPU + {C191C5AC-990E-4CA6-9847-342B9CD92465}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C191C5AC-990E-4CA6-9847-342B9CD92465}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C191C5AC-990E-4CA6-9847-342B9CD92465}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C191C5AC-990E-4CA6-9847-342B9CD92465}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {F7B6969D-CDE5-4E9E-9692-AD6C962E8DBF} + EndGlobalSection EndGlobal diff --git a/DVCustomCarLoader/CustomCar.cs b/DVCustomCarLoader/CustomCar.cs index 2174987e..9113e325 100644 --- a/DVCustomCarLoader/CustomCar.cs +++ b/DVCustomCarLoader/CustomCar.cs @@ -3,6 +3,7 @@ using UnityEngine; using HarmonyLib; using Object = UnityEngine.Object; +using CLL_GameScripts; namespace DVCustomCarLoader { @@ -18,6 +19,11 @@ public class CustomCar /// public TrainCarType BaseCarType = TrainCarType.FlatbedEmpty; + /// + /// The locomotive type of this car + /// + public LocoSimType SimType = LocoSimType.None; + /// /// The base prefab that will be duplicated from. /// diff --git a/DVCustomCarLoader/CustomCarManager.cs b/DVCustomCarLoader/CustomCarManager.cs index bb8810ce..c4dfe84d 100644 --- a/DVCustomCarLoader/CustomCarManager.cs +++ b/DVCustomCarLoader/CustomCarManager.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.IO; using UnityEngine; +using CLL_GameScripts; namespace DVCustomCarLoader { @@ -9,7 +10,7 @@ public class CustomCarManager : MonoBehaviour { public List CustomCarsToSpawn; - private Dictionary SpawnedCustomCarIds = new Dictionary(); + private readonly Dictionary SpawnedCustomCarIds = new Dictionary(); public bool TryGetCustomCarId( TrainCar trainCar, out string id ) { @@ -57,97 +58,24 @@ public void Setup() //Load JSON JSONObject jsonFile = new JSONObject(jsonText); - + +#if DEBUG //Print JSON file for debug. Main.ModEntry.Logger.Log($"{jsonFile.ToString(true)}"); - +#endif + if (jsonFile.keys.Count>0) { - try + CustomCar newCar = CreateCustomCar(directory, jsonFile); + + if( newCar != null ) { - var assetBundleName = jsonFile["assetBundleName"].str; - var assetBundlePath = Path.Combine(directory, assetBundleName); - - if (File.Exists(assetBundlePath)) - { - Main.ModEntry.Logger.Log( - $"Loading AssetBundle: {assetBundleName} at path {assetBundlePath}"); - - //Try to load asset bundle. - var assetBundle = AssetBundle.LoadFromFile(assetBundlePath); - - if (assetBundle == null) - { - Debug.Log($"Failed to load AssetBundle: {assetBundleName}"); - break; - } - - Main.ModEntry.Logger.Log( - $"Successfully loaded asset bundle. Bundle name is {assetBundleName}"); - - //Try to get car prefab from asset bundle - var prefabName = jsonFile["carPrefabName"].str; - - GameObject carPrefab = assetBundle.LoadAsset(prefabName); - - //Unload assetbundle to free up memory. - assetBundle.Unload(false); - - if( carPrefab != null ) - { - - Main.ModEntry.Logger.Log( - $"Successfully loaded prefab from asset bundle. Prefab name is {carPrefab.name}"); - - var newCar = new CustomCar() - { - CarPrefab = carPrefab, - identifier = jsonFile["identifier"].str, - BaseCarType = (TrainCarType)jsonFile["carType"].i - }; - - //Bogies - // Custom Front Bogie - jsonFile.GetField(out newCar.HasCustomFrontBogie, CarJSONKeys.REPLACE_FRONT_BOGIE, false); - if( newCar.HasCustomFrontBogie ) - { - if( jsonFile.GetField(CarJSONKeys.FRONT_BOGIE_PARAMS) is JSONObject fbs ) - { - newCar.FrontBogieConfig = CustomBogieParams.FromJSON(fbs); - } - } - - // Custom Rear Bogie - jsonFile.GetField(out newCar.HasCustomRearBogie, CarJSONKeys.REPLACE_REAR_BOGIE, false); - if( newCar.HasCustomRearBogie ) - { - if( jsonFile.GetField(CarJSONKeys.REAR_BOGIE_PARAMS) is JSONObject rbs ) - { - newCar.RearBogieConfig = CustomBogieParams.FromJSON(rbs); - } - } - - newCar.FinalizePrefab(); - - CustomCarsToSpawn.Add(newCar); - - Main.ModEntry.Logger.Log( - $"Successfully added new car to spawn list: {newCar.identifier}"); - } - else - { - break; - } - } - else - { - break; - } + CustomCarsToSpawn.Add(newCar); + Main.ModEntry.Logger.Log($"Successfully added new car to spawn list: {newCar.identifier}"); } - catch (Exception e) + else { - Main.ModEntry.Logger.Error(e.ToString()); - break; + Main.ModEntry.Logger.Error($"Failed to load custom car from {directory}"); } } else @@ -159,26 +87,81 @@ public void Setup() } } - /* - public void Setup() + private static CustomCar CreateCustomCar( string directory, JSONObject jsonFile ) { - CustomCarsToSpawn = new List(); - - var primitive = GameObject.CreatePrimitive(PrimitiveType.Cube); - primitive.transform.localScale = new Vector3(3.17f, 5.71f, 28.71f); - primitive.SetActive(false); - DontDestroyOnLoad(primitive); - - CustomCarsToSpawn.Add(new CustomCar + try + { + var assetBundleName = jsonFile[CarJSONKeys.BUNDLE_NAME].str; + var assetBundlePath = Path.Combine(directory, assetBundleName); + + if( File.Exists(assetBundlePath) ) + { + Main.ModEntry.Logger.Log( + $"Loading AssetBundle: {assetBundleName} at path {assetBundlePath}"); + + //Try to load asset bundle. + var assetBundle = AssetBundle.LoadFromFile(assetBundlePath); + + if( assetBundle == null ) + { + Debug.Log($"Failed to load AssetBundle: {assetBundleName}"); + return null; + } + + Main.ModEntry.Logger.Log($"Successfully loaded asset bundle. Bundle name is {assetBundleName}"); + + //Try to get car prefab from asset bundle + var prefabName = jsonFile[CarJSONKeys.PREFAB_NAME].str; + + GameObject carPrefab = assetBundle.LoadAsset(prefabName); + + //Unload assetbundle to free up memory. + assetBundle.Unload(false); + + if( carPrefab != null ) + { + Main.ModEntry.Logger.Log($"Successfully loaded prefab from asset bundle. Prefab name is {carPrefab.name}"); + + var newCar = new CustomCar() + { + CarPrefab = carPrefab, + identifier = jsonFile[CarJSONKeys.IDENTIFIER].str, + BaseCarType = (TrainCarType)jsonFile[CarJSONKeys.CAR_TYPE].i, + SimType = (LocoSimType)jsonFile[CarJSONKeys.SIM_TYPE].i + }; + + //Bogies + // Custom Front Bogie + jsonFile.GetField(out newCar.HasCustomFrontBogie, CarJSONKeys.REPLACE_FRONT_BOGIE, false); + if( newCar.HasCustomFrontBogie ) + { + if( jsonFile.GetField(CarJSONKeys.FRONT_BOGIE_PARAMS) is JSONObject fbs ) + { + newCar.FrontBogieConfig = CustomBogieParams.FromJSON(fbs); + } + } + + // Custom Rear Bogie + jsonFile.GetField(out newCar.HasCustomRearBogie, CarJSONKeys.REPLACE_REAR_BOGIE, false); + if( newCar.HasCustomRearBogie ) + { + if( jsonFile.GetField(CarJSONKeys.REAR_BOGIE_PARAMS) is JSONObject rbs ) + { + newCar.RearBogieConfig = CustomBogieParams.FromJSON(rbs); + } + } + + newCar.FinalizePrefab(); + return newCar; + } + } + } + catch( Exception e ) { - identifier = "Autorack_Concept", - CarPrefab = primitive, - TrainCarType = TrainCarType.FlatbedEmpty, - FrontCouplerPosition = new Vector3(0.0f, 1.05f, 14.324f), - RearCouplerPosition = new Vector3(0.0f, 1.05f, -14.324f), - FrontBogiePosition = new Vector3(0.0f, 0.0f, 10.493f), - RearBogiePosition = new Vector3(0.0f, 0.0f, -10.493f) - }); - }*/ + Main.ModEntry.Logger.Error(e.ToString()); + } + + return null; + } } } \ No newline at end of file diff --git a/DVCustomCarLoader/DVCustomCarLoader.csproj b/DVCustomCarLoader/DVCustomCarLoader.csproj index 901c421c..4d54f5c4 100644 --- a/DVCustomCarLoader/DVCustomCarLoader.csproj +++ b/DVCustomCarLoader/DVCustomCarLoader.csproj @@ -71,7 +71,6 @@ - @@ -84,5 +83,11 @@ + + + {c191c5ac-990e-4ca6-9847-342b9cd92465} + CLL_GameScripts + + \ No newline at end of file From b1b75e0503096318cd360fa6bf456294070dd767 Mon Sep 17 00:00:00 2001 From: Katy Fox Date: Sat, 10 Jul 2021 22:51:17 -0400 Subject: [PATCH 02/20] Fix name of game script DLL --- .../CCL_GameScripts.csproj | 8 +++- .../CarJSONKeys.cs | 2 +- .../CustomCarEnums.cs | 2 +- .../Properties/AssemblyInfo.cs | 4 +- CCL_GameScripts/SimParamsBase.cs | 15 ++++++++ CCL_GameScripts/SimParamsDiesel.cs | 37 +++++++++++++++++++ DVCustomCarLoader.sln | 2 +- 7 files changed, 63 insertions(+), 7 deletions(-) rename CLL_GameScripts/CLL_GameScripts.csproj => CCL_GameScripts/CCL_GameScripts.csproj (83%) rename {CLL_GameScripts => CCL_GameScripts}/CarJSONKeys.cs (96%) rename {CLL_GameScripts => CCL_GameScripts}/CustomCarEnums.cs (98%) rename {CLL_GameScripts => CCL_GameScripts}/Properties/AssemblyInfo.cs (93%) create mode 100644 CCL_GameScripts/SimParamsBase.cs create mode 100644 CCL_GameScripts/SimParamsDiesel.cs diff --git a/CLL_GameScripts/CLL_GameScripts.csproj b/CCL_GameScripts/CCL_GameScripts.csproj similarity index 83% rename from CLL_GameScripts/CLL_GameScripts.csproj rename to CCL_GameScripts/CCL_GameScripts.csproj index a5897ee3..713dcabb 100644 --- a/CLL_GameScripts/CLL_GameScripts.csproj +++ b/CCL_GameScripts/CCL_GameScripts.csproj @@ -7,8 +7,8 @@ {C191C5AC-990E-4CA6-9847-342B9CD92465} Library Properties - CLL_GameScripts - CLL_GameScripts + CCL_GameScripts + CCL_GameScripts v4.8 512 true @@ -39,11 +39,15 @@ + + + + \ No newline at end of file diff --git a/CLL_GameScripts/CarJSONKeys.cs b/CCL_GameScripts/CarJSONKeys.cs similarity index 96% rename from CLL_GameScripts/CarJSONKeys.cs rename to CCL_GameScripts/CarJSONKeys.cs index cff19e04..bccaaee7 100644 --- a/CLL_GameScripts/CarJSONKeys.cs +++ b/CCL_GameScripts/CarJSONKeys.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Linq; -namespace CLL_GameScripts +namespace CCL_GameScripts { public class CarJSONKeys { diff --git a/CLL_GameScripts/CustomCarEnums.cs b/CCL_GameScripts/CustomCarEnums.cs similarity index 98% rename from CLL_GameScripts/CustomCarEnums.cs rename to CCL_GameScripts/CustomCarEnums.cs index db600af8..ac18fa80 100644 --- a/CLL_GameScripts/CustomCarEnums.cs +++ b/CCL_GameScripts/CustomCarEnums.cs @@ -4,7 +4,7 @@ using System.Text; using System.Threading.Tasks; -namespace CLL_GameScripts +namespace CCL_GameScripts { /// /// The underlying type of cars. diff --git a/CLL_GameScripts/Properties/AssemblyInfo.cs b/CCL_GameScripts/Properties/AssemblyInfo.cs similarity index 93% rename from CLL_GameScripts/Properties/AssemblyInfo.cs rename to CCL_GameScripts/Properties/AssemblyInfo.cs index 8b11e506..afdefe2d 100644 --- a/CLL_GameScripts/Properties/AssemblyInfo.cs +++ b/CCL_GameScripts/Properties/AssemblyInfo.cs @@ -5,11 +5,11 @@ // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information // associated with an assembly. -[assembly: AssemblyTitle("CLL_GameScripts")] +[assembly: AssemblyTitle("CCL_GameScripts")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("CLL_GameScripts")] +[assembly: AssemblyProduct("CCL_GameScripts")] [assembly: AssemblyCopyright("Copyright © 2021")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] diff --git a/CCL_GameScripts/SimParamsBase.cs b/CCL_GameScripts/SimParamsBase.cs new file mode 100644 index 00000000..0c1f10b7 --- /dev/null +++ b/CCL_GameScripts/SimParamsBase.cs @@ -0,0 +1,15 @@ +using System.Collections; +using UnityEngine; + +namespace CCL_GameScripts +{ + public abstract class SimParamsBase : MonoBehaviour + { + // default values from diesel + [Header("Basic")] + public float MaxSpeed = 120f; + public float SandCapacity = 200f; + public float SandValveSpeed = 10f; + public float SandMaxFlow = 5f; + } +} \ No newline at end of file diff --git a/CCL_GameScripts/SimParamsDiesel.cs b/CCL_GameScripts/SimParamsDiesel.cs new file mode 100644 index 00000000..633d4f51 --- /dev/null +++ b/CCL_GameScripts/SimParamsDiesel.cs @@ -0,0 +1,37 @@ +using System.Collections; +using UnityEngine; + +namespace CCL_GameScripts +{ + public class SimParamsDiesel : SimParamsBase + { + [Header("Throttle")] + public float ThrottleUpRate = 2f; + public float ThrottleDownRate = 2f; + + [Header("Heat Management (°C)")] + public float ColdEnginePowerFactor = 0.8f; + + public float PassiveTempLoss = 5.5f; + public float IdleTempGain = 5; + public float IdleMaxTemp = 52; + + public float TempGainPerRpm = 8; + public float MinTemp = 30; + public float MaxTemp = 120; + public float MaxPowerTemp = 75; + + [Header("Fuel (L)")] + public float FuelCapacity = 6000; + public float FuelConsumptionBase = 35; + public float FuelConsumptionMax = 1; + public float FuelConsumptionMin = 0.025f; + + public float PerformanceDropDamageLevel = 0.5f; + + [Header("Lubrication (L)")] + public float OilCapacity = 500; + public float OilConsumptionEngineRpm = 1; + //public float OilConsumptionWheels = 0.12f; + } +} \ No newline at end of file diff --git a/DVCustomCarLoader.sln b/DVCustomCarLoader.sln index 42109ae8..7b929a92 100644 --- a/DVCustomCarLoader.sln +++ b/DVCustomCarLoader.sln @@ -5,7 +5,7 @@ VisualStudioVersion = 16.0.31424.327 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DVCustomCarLoader", "DVCustomCarLoader\DVCustomCarLoader.csproj", "{1380FDD7-0144-49AA-A2CD-BA1EB02EE306}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CLL_GameScripts", "CLL_GameScripts\CLL_GameScripts.csproj", "{C191C5AC-990E-4CA6-9847-342B9CD92465}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CCL_GameScripts", "CCL_GameScripts\CCL_GameScripts.csproj", "{C191C5AC-990E-4CA6-9847-342B9CD92465}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution From 27ee3fa8d869c0a7e008f4753313a97147e1b009 Mon Sep 17 00:00:00 2001 From: Katy Fox Date: Sat, 10 Jul 2021 22:59:28 -0400 Subject: [PATCH 03/20] Add a bunch of generic loco components --- DVCustomCarLoader/CustomCar.cs | 2 +- DVCustomCarLoader/CustomCarManager.cs | 2 +- DVCustomCarLoader/DVCustomCarLoader.csproj | 16 +- DVCustomCarLoader/Extensions.cs | 19 ++ DVCustomCarLoader/LocoComponentManager.cs | 33 +++ .../LocoComponents/CustomLocoController.cs | 17 ++ .../CustomLocoControllerDiesel.cs | 219 +++++++++++++++ .../LocoComponents/CustomLocoPitStopParams.cs | 53 ++++ .../LocoComponents/CustomLocoSimDiesel.cs | 256 ++++++++++++++++++ .../LocoComponents/CustomLocoSimulation.cs | 86 ++++++ .../DamageControllerCustomDiesel.cs | 120 ++++++++ .../DamageControllerCustomLoco.cs | 15 + .../LocoComponents/DebtTrackerCustomLoco.cs | 80 ++++++ .../LocoComponents/IServicePenaltyProvider.cs | 73 +++++ DVCustomCarLoader/Main.cs | 12 +- 15 files changed, 997 insertions(+), 6 deletions(-) create mode 100644 DVCustomCarLoader/Extensions.cs create mode 100644 DVCustomCarLoader/LocoComponentManager.cs create mode 100644 DVCustomCarLoader/LocoComponents/CustomLocoController.cs create mode 100644 DVCustomCarLoader/LocoComponents/CustomLocoControllerDiesel.cs create mode 100644 DVCustomCarLoader/LocoComponents/CustomLocoPitStopParams.cs create mode 100644 DVCustomCarLoader/LocoComponents/CustomLocoSimDiesel.cs create mode 100644 DVCustomCarLoader/LocoComponents/CustomLocoSimulation.cs create mode 100644 DVCustomCarLoader/LocoComponents/DamageControllerCustomDiesel.cs create mode 100644 DVCustomCarLoader/LocoComponents/DamageControllerCustomLoco.cs create mode 100644 DVCustomCarLoader/LocoComponents/DebtTrackerCustomLoco.cs create mode 100644 DVCustomCarLoader/LocoComponents/IServicePenaltyProvider.cs diff --git a/DVCustomCarLoader/CustomCar.cs b/DVCustomCarLoader/CustomCar.cs index 9113e325..bc2bce4a 100644 --- a/DVCustomCarLoader/CustomCar.cs +++ b/DVCustomCarLoader/CustomCar.cs @@ -3,7 +3,7 @@ using UnityEngine; using HarmonyLib; using Object = UnityEngine.Object; -using CLL_GameScripts; +using CCL_GameScripts; namespace DVCustomCarLoader { diff --git a/DVCustomCarLoader/CustomCarManager.cs b/DVCustomCarLoader/CustomCarManager.cs index c4dfe84d..9131c132 100644 --- a/DVCustomCarLoader/CustomCarManager.cs +++ b/DVCustomCarLoader/CustomCarManager.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.IO; using UnityEngine; -using CLL_GameScripts; +using CCL_GameScripts; namespace DVCustomCarLoader { diff --git a/DVCustomCarLoader/DVCustomCarLoader.csproj b/DVCustomCarLoader/DVCustomCarLoader.csproj index 4d54f5c4..88753ca6 100644 --- a/DVCustomCarLoader/DVCustomCarLoader.csproj +++ b/DVCustomCarLoader/DVCustomCarLoader.csproj @@ -37,6 +37,7 @@ ..\..\..\..\..\Program Files\Steam\steamapps\common\Derail Valley\DerailValley_Data\Managed\Assembly-CSharp.dll + ..\..\..\..\..\Program Files\Steam\steamapps\common\Derail Valley\DerailValley_Data\Managed\DV.Utils.dll @@ -76,17 +77,28 @@ + + + + + + + + + + + - + {c191c5ac-990e-4ca6-9847-342b9cd92465} - CLL_GameScripts + CCL_GameScripts diff --git a/DVCustomCarLoader/Extensions.cs b/DVCustomCarLoader/Extensions.cs new file mode 100644 index 00000000..0178bb74 --- /dev/null +++ b/DVCustomCarLoader/Extensions.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace DVCustomCarLoader +{ + public static class Extensions + { + public static bool IsEnvironmental( this ResourceType type ) + { + return + (type == ResourceType.EnvironmentDamageCargo) || + (type == ResourceType.EnvironmentDamageFuel) || + (type == ResourceType.EnvironmentDamageCoal); + } + } +} diff --git a/DVCustomCarLoader/LocoComponentManager.cs b/DVCustomCarLoader/LocoComponentManager.cs new file mode 100644 index 00000000..6d9deeeb --- /dev/null +++ b/DVCustomCarLoader/LocoComponentManager.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using DV.MultipleUnit; +using UnityEngine; + +namespace DVCustomCarLoader +{ + public static class LocoComponentManager + { + // Order to add components: + // - Simulation + // - SimulationEvents + // - DamageController + // - MultipleUnitModule + // - LocoController + + public static void AddDieselSimulation( GameObject prefab ) + { + GameObject basePrefab = CarTypes.GetCarPrefab(TrainCarType.LocoDiesel); + + prefab.AddComponent(); + prefab.AddComponent(); + prefab.AddComponent(); + //prefab.AddComponent(); + + var controller = prefab.AddComponent(); + var baseController = basePrefab.GetComponent(); + controller.tractionTorqueCurve = baseController.tractionTorqueCurve; + controller.brakePowerCurve = baseController.brakePowerCurve; + } + } +} diff --git a/DVCustomCarLoader/LocoComponents/CustomLocoController.cs b/DVCustomCarLoader/LocoComponents/CustomLocoController.cs new file mode 100644 index 00000000..3cd29a2a --- /dev/null +++ b/DVCustomCarLoader/LocoComponents/CustomLocoController.cs @@ -0,0 +1,17 @@ +using System.Collections; +using UnityEngine; + +namespace DVCustomCarLoader.LocoComponents +{ + public abstract class CustomLocoController : LocoControllerBase + { + + } + + public abstract class CustomLocoController : CustomLocoController + where TSim : CustomLocoSimulation + { + protected TSim sim; + protected DebtTrackerCustomLoco locoDebt; + } +} \ No newline at end of file diff --git a/DVCustomCarLoader/LocoComponents/CustomLocoControllerDiesel.cs b/DVCustomCarLoader/LocoComponents/CustomLocoControllerDiesel.cs new file mode 100644 index 00000000..2519ace0 --- /dev/null +++ b/DVCustomCarLoader/LocoComponents/CustomLocoControllerDiesel.cs @@ -0,0 +1,219 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using DV; +using DV.ServicePenalty; +using UnityEngine; + +namespace DVCustomCarLoader.LocoComponents +{ + public class CustomLocoControllerDiesel : CustomLocoController + { + public bool Backlight { get; set; } + public bool FanOn { get; set; } + + private DamageControllerCustomDiesel damageController; + private DieselLocoSimulationEvents eventController; + + public AnimationCurve tractionTorqueCurve; + + public float EngineRPM => sim.engineRPM.value; + public float EngineRPMGauge => (sim.engineOn) ? (12.5f + sim.engineRPM.value * 100f) : 0f; + public float EngineTemp => sim.engineTemp.value; + + public bool EngineRunning + { + get => sim.engineOn; + set + { + if( value != sim.engineOn ) + { + sim.engineOn = value; + if( train.brakeSystem ) + { + train.brakeSystem.compressorRunning = value; + } + eventController.EngineRunningChanged.Invoke(value); + } + } + } + + public float FuelLevel => sim.fuel.value; + public float OilLevel => sim.oil.value; + public float SandLevel => sim.sand.value; + public bool SandersOn => sim.sandOn; + + public override float GetSandersFlow() + { + if( sim.sand.value <= 0f ) + { + return 0f; + } + return sim.sandFlow.value; + } + + public void SetSandersFlow( float value ) + { + sim.sandFlow.SetValue(value); + } + + public override void SetSanders( float value ) + { + sim.sandOn = (value > 0f); + base.SetSanders(value); + } + + public override void SetReverser( float position ) + { + if( targetThrottle > 0.05f ) + { + return; + } + if( position < 0f ) + { + position = -1f; + } + else if( position > 0f ) + { + position = 1f; + } + else + { + position = 0f; + } + base.SetReverser(position); + } + + public override void SetThrottle( float throttleLever ) + { + base.SetThrottle(throttleLever); + } + + + protected override void Awake() + { + base.Awake(); + sim = GetComponent(); + damageController = GetComponent(); + eventController = GetComponent(); + CarVisitChecker carVisitChecker = gameObject.AddComponent(); + carVisitChecker.Initialize(train); + + // TODO: MU, save state + //MultipleUnitModule component = base.GetComponent(); + //base.gameObject.AddComponent().Initialize(sim, damageController, this, carVisitChecker, component); + + train.LogicCarInitialized += OnLogicCarInitialized; + } + + + public override float GetTractionForce() + { + float num = (sim.engineRPM.value > 0f) ? tractionTorqueCurve.Evaluate(GetSpeedKmH() / sim.engineRPM.value) : 0f; + float num2 = (Mathf.Sign(GetForwardSpeed() * reverser) > 0f) ? num : 1f; + return sim.engineRPM.value * num2 * tractionTorqueMult; + } + + private void OnDisable() + { + SetupListeners(false); + } + + private void OnEnable() + { + SetupListeners(true); + } + + private void OnEngineTempChanged( LocoSimulationEvents.Amount amount ) + { + if( amount == LocoSimulationEvents.Amount.Full ) + { + EngineRunning = false; + } + } + + private void OnFuelChanged( LocoSimulationEvents.Amount amount ) + { + if( amount == LocoSimulationEvents.Amount.Depleted ) + { + EngineRunning = false; + } + } + + private void OnLocoDestroyed( TrainCar train ) + { + train.OnDestroyCar -= OnLocoDestroyed; + if( !train.playerSpawnedCar ) + { + SingletonBehaviour.Instance.StageLocoDebtOnLocoDestroy(locoDebt); + } + } + + private void OnLogicCarInitialized() + { + train.LogicCarInitialized -= OnLogicCarInitialized; + + if( !train.playerSpawnedCar ) + { + locoDebt = new DebtTrackerCustomLoco(train.ID, train.carType, this, damageController, sim); + SingletonBehaviour.Instance.RegisterLocoDebtTracker(locoDebt); + } + + train.OnDestroyCar += OnLocoDestroyed; + gameObject.AddComponent().Initialize(sim, damageController); + } + + public override void SetNeutralState() + { + EngineRunning = false; + SetSanders(0f); + SetSandersFlow(0f); + SetThrottle(0f); + SetReverser(0f); + SetBrake(0f); + SetIndependentBrake(1f); + } + + + private void SetupListeners( bool on ) + { + eventController.FuelChanged.Manage(new Action(OnFuelChanged), on); + eventController.EngineTempChanged.Manage(new Action(OnEngineTempChanged), on); + } + + //protected override bool ShouldSwitchToTrainBrakeOnStart() + //{ + // return LicenseManager.IsGeneralLicenseAcquired(GeneralLicenseType.DE6) && base.ShouldSwitchToTrainBrakeOnStart(); + //} + + protected override void Start() + { + base.Start(); + if( !VRManager.IsVREnabled() ) + { + gameObject.AddComponent().control = this; + } + } + + public override void Update() + { + base.Update(); + UpdateSimSpeed(); + UpdateSimThrottle(); + } + + private void UpdateSimSpeed() + { + sim.speed.SetValue(GetSpeedKmH()); + sim.goingForward = (GetForwardSpeed() > 0f); + } + + private void UpdateSimThrottle() + { + sim.throttle.SetValue(sim.engineOn ? throttle : 0f); + sim.throttleToTargetDiff.SetValue(sim.engineOn ? (throttle - targetThrottle) : 0f); + } + } +} diff --git a/DVCustomCarLoader/LocoComponents/CustomLocoPitStopParams.cs b/DVCustomCarLoader/LocoComponents/CustomLocoPitStopParams.cs new file mode 100644 index 00000000..877cd1c8 --- /dev/null +++ b/DVCustomCarLoader/LocoComponents/CustomLocoPitStopParams.cs @@ -0,0 +1,53 @@ +using System.Collections; +using System.Linq; +using UnityEngine; + +namespace DVCustomCarLoader.LocoComponents +{ + public class CustomLocoPitStopParams : CarPitStopParametersBase + { + private DamageControllerCustomLoco dmgController; + private CustomLocoSimulation sim; + + private PitStopRefillable[] refillables; + + public void Initialize( CustomLocoSimulation sim, DamageControllerCustomLoco dmg ) + { + this.sim = sim; + dmgController = dmg; + InitPitStopParameters(); + } + + protected override void InitPitStopParameters() + { + refillables = + sim.GetPitStopParameters() + .Concat(dmgController.GetPitStopParameters()) + .ToArray(); + + carPitStopParameters = refillables.ToDictionary( + r => r.ResourceType, + r => r.parameterData); + } + + protected override void RefreshParameters() + { + foreach( var refillable in refillables ) + { + refillable.RefreshLevel(); + } + } + + public override void UpdateCarPitStopParameter( ResourceType parameter, float changeAmount ) + { + foreach( var refillable in refillables ) + { + if( refillable.ResourceType == parameter ) + { + refillable.UpdateLevel(changeAmount); + return; + } + } + } + } +} \ No newline at end of file diff --git a/DVCustomCarLoader/LocoComponents/CustomLocoSimDiesel.cs b/DVCustomCarLoader/LocoComponents/CustomLocoSimDiesel.cs new file mode 100644 index 00000000..ba74af87 --- /dev/null +++ b/DVCustomCarLoader/LocoComponents/CustomLocoSimDiesel.cs @@ -0,0 +1,256 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using DV.JObjectExtstensions; +using Newtonsoft.Json.Linq; +using UnityEngine; +using CCL_GameScripts; +using DV.ServicePenalty; + +namespace DVCustomCarLoader.LocoComponents +{ + public class CustomLocoSimDiesel : CustomLocoSimulation, IServicePenaltyProvider + { + public float TotalFuelConsumed { get; private set; } + + //public SimParamsDiesel simParams; + private DamageControllerDiesel dmgController; + + // Engine + public bool engineOn; + public bool goingForward; + + public SimComponent speed; + public SimComponent throttle = new SimComponent("Throttle", 0f, 1f, 1f, 0f); + public SimComponent throttlePower = new SimComponent("ThrottlePower", 0f, 1f, 0.1f, 0f); + /// throttle - targetThrottle + public SimComponent throttleToTargetDiff = new SimComponent("ThrottleTargetDifference", -1f, 1f, 1f, 0f); + + public SimComponent engineRPM = new SimComponent("EngineRPM", 0f, 1f, 0.01f, 0f); + public SimComponent engineTemp = new SimComponent("EngineTemperature", 30f, 120f, 18f, 30f); + + public SimComponent fuel; + public SimComponent oil; + + private const string TOTAL_FUEL_CONSUMED_SAVE_KEY = "fuelConsumed"; + + public override IEnumerable GetDebtComponents() + { + return base.GetDebtComponents() + .Concat(new[] + { + new DebtTrackingInfo(this, new DebtComponent(0, ResourceType.EnvironmentDamageFuel)), + new DebtTrackingInfo(this, new DebtComponent(fuel.value, ResourceType.Fuel)), + new DebtTrackingInfo(this, new DebtComponent(oil.value, ResourceType.Oil)) + }); + } + + public override void ResetDebt( DebtComponent debt ) + { + switch( debt.type ) + { + case ResourceType.Fuel: + debt.ResetComponent(fuel.value); + break; + + case ResourceType.Oil: + debt.ResetComponent(oil.value); + break; + + case ResourceType.EnvironmentDamageFuel: + debt.ResetComponent(TotalFuelConsumed); + break; + + default: + base.ResetDebt(debt); + break; + } + } + + public override void UpdateDebtValue( DebtComponent debt ) + { + switch( debt.type ) + { + case ResourceType.Fuel: + debt.UpdateEndValue(fuel.value); + break; + + case ResourceType.Oil: + debt.UpdateEndValue(oil.value); + break; + + case ResourceType.EnvironmentDamageFuel: + debt.UpdateStartValue(TotalFuelConsumed); + break; + + default: + base.UpdateDebtValue(debt); + break; + } + } + + public override IEnumerable GetPitStopParameters() + { + return base.GetPitStopParameters() + .Concat(new[] + { + new PitStopRefillable(this, ResourceType.Oil, oil), + new PitStopRefillable(this, ResourceType.Fuel, fuel) + }); + } + + public override JObject GetComponentsSaveData() + { + JObject jobject = new JObject(); + SimComponent.SaveComponentState(fuel, jobject); + SimComponent.SaveComponentState(oil, jobject); + SimComponent.SaveComponentState(sand, jobject); + SimComponent.SaveComponentState(engineTemp, jobject); + jobject.SetFloat("fuelConsumed", TotalFuelConsumed); + return jobject; + } + + protected override void InitComponents() + { + base.InitComponents(); + dmgController = GetComponent(); + + speed = new SimComponent("Speed", 0f, simParams.MaxSpeed, 1f, 0f); + fuel = new SimComponent("Fuel", 0f, simParams.FuelCapacity, 1200f, simParams.FuelCapacity); + oil = new SimComponent("Oil", 0f, simParams.OilCapacity, 100f, simParams.OilCapacity); + + components = new SimComponent[] + { + fuel, + oil, + sand, + sandFlow, + engineTemp, + engineRPM, + throttlePower, + throttle, + throttleToTargetDiff, + speed + }; + } + + public override void LoadComponentsState( JObject stateData ) + { + SimComponent.LoadComponentState(fuel, stateData); + SimComponent.LoadComponentState(oil, stateData); + SimComponent.LoadComponentState(sand, stateData); + SimComponent.LoadComponentState(engineTemp, stateData); + + float? fuelConsumed = stateData.GetFloat(TOTAL_FUEL_CONSUMED_SAVE_KEY); + if( fuelConsumed != null ) + { + TotalFuelConsumed = fuelConsumed.Value; + return; + } + + Main.Error("No load data for fuelConsumed found!"); + } + + public override void ResetRefillableSimulationParams() + { + fuel.SetValue(fuel.max); + oil.SetValue(oil.max); + sand.SetValue(sand.max); + } + + public override void ResetFuelConsumption() + { + TotalFuelConsumed = 0f; + } + + private void SimulateEngineRPM( float delta ) + { + float percentWarm = Mathf.InverseLerp(engineTemp.min, simParams.MaxPowerTemp, engineTemp.value); + float warmupFactor = Mathf.Lerp(simParams.ColdEnginePowerFactor, 1f, percentWarm); + + float healthFactor = 1f - Mathf.InverseLerp(simParams.PerformanceDropDamageLevel, 1f, dmgController.engine.DamagePercentage); + engineRPM.SetNextValue(throttlePower.value * throttle.value * warmupFactor * healthFactor); + float throttleTgtDiff = Mathf.Round(throttleToTargetDiff.value * 1000f) / 1000f; + + // if tgt > throttle then throttleTgtDiff < 0 + + // throttle down (tgt < current) + if( (throttleTgtDiff > 0f) && (throttlePower.value > 0f) ) + { + throttlePower.AddNextValue(-1f * Mathf.Abs(throttleTgtDiff) * simParams.ThrottleDownRate * delta); + return; + } + + // throttle up (tgt >= current) + if( (throttleTgtDiff <= 0f) && (throttlePower.value < 1f) ) + { + throttlePower.AddNextValue((1f - Mathf.Abs(throttleTgtDiff)) * simParams.ThrottleUpRate * delta); + } + } + + private void SimulateEngineTemp( float delta ) + { + if( engineRPM.value > 0f && engineOn ) + { + engineTemp.AddNextValue(engineRPM.value * simParams.TempGainPerRpm * delta); + } + if( engineOn && engineTemp.value < 52f ) + { + engineTemp.AddNextValue(simParams.IdleTempGain * delta); + } + if( engineTemp.value > engineTemp.min ) + { + engineTemp.AddNextValue(simParams.PassiveTempLoss * delta); + } + } + + private void SimulateFuel( float delta ) + { + if( engineOn && fuel.value > 0f ) + { + float multiplier = Mathf.Lerp( + simParams.FuelConsumptionMin, + simParams.FuelConsumptionMax, + engineRPM.value); + float rate = multiplier * simParams.FuelConsumptionBase * delta; + + TotalFuelConsumed += rate; + fuel.AddNextValue(-1f * rate); + } + } + + private void SimulateOil( float delta ) + { + if( engineRPM.value > 0f && oil.value > 0f ) + { + oil.AddNextValue(-1f * engineRPM.value * simParams.OilConsumptionEngineRpm * delta); + } + } + + private void SimulateSand( float delta ) + { + if( (sandOn && sand.value > 0f && sandFlow.value < sandFlow.max) || ((!sandOn || sand.value == 0f) && sandFlow.value > sandFlow.min) ) + { + int num = (sandOn && sand.value > 0f) ? 1 : -1; + sandFlow.AddNextValue(num * simParams.SandValveSpeed * delta); + } + if( sandFlow.value > 0f && sand.value > 0f ) + { + sand.AddNextValue(-1f * sandFlow.value * simParams.SandMaxFlow * delta); + } + } + + protected override void SimulateTick( float delta ) + { + InitNextValues(); + SimulateFuel(delta); + SimulateOil(delta); + SimulateSand(delta); + SimulateEngineRPM(delta); + SimulateEngineTemp(delta); + SetValuesToNextValues(); + } + } +} diff --git a/DVCustomCarLoader/LocoComponents/CustomLocoSimulation.cs b/DVCustomCarLoader/LocoComponents/CustomLocoSimulation.cs new file mode 100644 index 00000000..ba974e3e --- /dev/null +++ b/DVCustomCarLoader/LocoComponents/CustomLocoSimulation.cs @@ -0,0 +1,86 @@ +using System; +using System.Collections.Generic; +using CCL_GameScripts; +using DV.ServicePenalty; + +namespace DVCustomCarLoader.LocoComponents +{ + public abstract class CustomLocoSimulation : LocoSimulation, IServicePenaltyProvider + { + public abstract IEnumerable GetDebtComponents(); + public abstract void ResetDebt( DebtComponent debt ); + public abstract void UpdateDebtValue( DebtComponent debt ); + public virtual void ResetFuelConsumption() { } + public abstract IEnumerable GetPitStopParameters(); + public abstract void ChangePitStopLevel( ResourceType type, float changeAmount ); + public abstract float GetPitStopLevel( ResourceType type ); + } + + public abstract class CustomLocoSimulation : CustomLocoSimulation + where TParams : SimParamsBase + { + public TParams simParams; + + // Sanders + public bool sandOn; + public SimComponent sand; + public SimComponent sandFlow = new SimComponent("SandFlow", 0f, 1f, 0.1f, 0f); + + protected override void InitComponents() + { + simParams = GetComponent(); + sand = new SimComponent("Sand", 0f, simParams.SandCapacity, 40f, simParams.SandCapacity); + } + + public override IEnumerable GetDebtComponents() + { + return new[] + { + new DebtTrackingInfo(this, new DebtComponent(sand.value, ResourceType.Sand)) + }; + } + + public override void ResetDebt( DebtComponent debt ) + { + if( debt.type == ResourceType.Sand ) + { + debt.ResetComponent(sand.value); + } + } + + public override void UpdateDebtValue( DebtComponent debt ) + { + if( debt.type == ResourceType.Sand ) + { + debt.UpdateEndValue(sand.value); + } + } + + public override IEnumerable GetPitStopParameters() + { + return new[] + { + new PitStopRefillable(this, ResourceType.Sand, sand) + }; + } + + public override void ChangePitStopLevel( ResourceType type, float changeAmount ) + { + if( type == ResourceType.Sand ) + { + sand.AddValue(changeAmount); + } + } + + public override float GetPitStopLevel( ResourceType type ) + { + if( type == ResourceType.Sand ) + { + return sand.value; + } + + Main.Warning("Tried to get pit stop value this loco sim doesn't have"); + return 0; + } + } +} \ No newline at end of file diff --git a/DVCustomCarLoader/LocoComponents/DamageControllerCustomDiesel.cs b/DVCustomCarLoader/LocoComponents/DamageControllerCustomDiesel.cs new file mode 100644 index 00000000..5a8a23c4 --- /dev/null +++ b/DVCustomCarLoader/LocoComponents/DamageControllerCustomDiesel.cs @@ -0,0 +1,120 @@ +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using DV.ServicePenalty; +using UnityEngine; + +namespace DVCustomCarLoader.LocoComponents +{ + public class DamageControllerCustomDiesel : DamageControllerCustomLoco + { + [Header("Additional damage types")] + public TrainDamage engine; + + #region Debt Tracking + + public override IEnumerable GetDebtComponents() + { + return new[] + { + new DebtTrackingInfo(this, new DebtComponent(bodyDamage.EffectiveHealthPercentage100Notation, ResourceType.Car_DMG)), + new DebtTrackingInfo(this, new DebtComponent(wheels.HealthPercentage100Notation, ResourceType.Wheels_DMG)), + new DebtTrackingInfo(this, new DebtComponent(engine.HealthPercentage100Notation, ResourceType.Engine_DMG)) + }; + } + + public override void ResetDebt( DebtComponent debt ) + { + switch( debt.type ) + { + case ResourceType.Car_DMG: + debt.ResetComponent(bodyDamage.EffectiveHealthPercentage100Notation); + break; + + case ResourceType.Wheels_DMG: + debt.ResetComponent(wheels.HealthPercentage100Notation); + break; + + case ResourceType.Engine_DMG: + debt.ResetComponent(engine.HealthPercentage100Notation); + break; + } + } + + public override void UpdateDebtValue( DebtComponent debt ) + { + switch( debt.type ) + { + case ResourceType.Car_DMG: + debt.UpdateEndValue(bodyDamage.EffectiveHealthPercentage100Notation); + break; + + case ResourceType.Wheels_DMG: + debt.UpdateEndValue(wheels.HealthPercentage100Notation); + break; + + case ResourceType.Engine_DMG: + debt.UpdateEndValue(engine.HealthPercentage100Notation); + break; + } + } + + #endregion + + #region Pit Stop Parameters + + public override IEnumerable GetPitStopParameters() + { + return new[] + { + new PitStopRefillable(this, ResourceType.Engine_DMG, engine.HealthPercentage100Notation, 100), + new PitStopRefillable(this, ResourceType.Car_DMG, bodyDamage.EffectiveHealthPercentage100Notation, 100), + new PitStopRefillable(this, ResourceType.Wheels_DMG, wheels.HealthPercentage100Notation, 100), + }; + } + + public override float GetPitStopLevel( ResourceType type ) + { + switch( type ) + { + case ResourceType.Car_DMG: + return bodyDamage.EffectiveHealthPercentage100Notation; + case ResourceType.Wheels_DMG: + return wheels.HealthPercentage100Notation; + case ResourceType.Engine_DMG: + return engine.HealthPercentage100Notation; + default: + Main.Warning("Tried to get pit stop value this loco damage ctrl doesn't have"); + return 0; + } + } + + public override void ChangePitStopLevel( ResourceType type, float changeAmount ) + { + switch( type ) + { + case ResourceType.Car_DMG: + bodyDamage.RepairCarEffectivePercentage(changeAmount / 100f); + //if( bodyDamage.DamagePercentage < 0.05f ) + //{ + // // Repair windows + //} + break; + + case ResourceType.Wheels_DMG: + wheels.RepairDamagePercentage(changeAmount / 100f); + break; + + case ResourceType.Engine_DMG: + engine.RepairDamagePercentage(changeAmount / 100f); + break; + + default: + Main.Warning("Trying to refill/repair something that is not part of this loco"); + break; + } + } + + #endregion + } +} \ No newline at end of file diff --git a/DVCustomCarLoader/LocoComponents/DamageControllerCustomLoco.cs b/DVCustomCarLoader/LocoComponents/DamageControllerCustomLoco.cs new file mode 100644 index 00000000..6f644f0a --- /dev/null +++ b/DVCustomCarLoader/LocoComponents/DamageControllerCustomLoco.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; +using DV.ServicePenalty; + +namespace DVCustomCarLoader.LocoComponents +{ + public abstract class DamageControllerCustomLoco : DamageController, IServicePenaltyProvider + { + public abstract IEnumerable GetDebtComponents(); + public abstract void ResetDebt( DebtComponent debt ); + public abstract void UpdateDebtValue( DebtComponent debt ); + public abstract IEnumerable GetPitStopParameters(); + public abstract float GetPitStopLevel( ResourceType type ); + public abstract void ChangePitStopLevel( ResourceType type, float changeAmount ); + } +} \ No newline at end of file diff --git a/DVCustomCarLoader/LocoComponents/DebtTrackerCustomLoco.cs b/DVCustomCarLoader/LocoComponents/DebtTrackerCustomLoco.cs new file mode 100644 index 00000000..3c1c85df --- /dev/null +++ b/DVCustomCarLoader/LocoComponents/DebtTrackerCustomLoco.cs @@ -0,0 +1,80 @@ +using System.Collections; +using System.Linq; +using DV.Logic.Job; +using DV.ServicePenalty; +using UnityEngine; + +namespace DVCustomCarLoader.LocoComponents +{ + public class DebtTrackerCustomLoco : LocoDebtTrackerBase + { + private CustomLocoController locoController; + private DamageControllerCustomLoco damageController; + private CustomLocoSimulation simulation; + private DebtTrackingInfo[] debtTrackers; + + public DebtTrackerCustomLoco( + string id, TrainCarType carType, + CustomLocoController ctrl, + DamageControllerCustomLoco dmg, CustomLocoSimulation sim ) + { + locoController = ctrl; + damageController = dmg; + simulation = sim; + + debtTrackers = + damageController.GetDebtComponents() + .Concat(simulation.GetDebtComponents()) + .ToArray(); + + debtData = new CarDebtData(id, carType, InitializeDebtComponents(), CargoType.None); + } + + public override DebtComponent[] InitializeDebtComponents() + { + return debtTrackers.Select(dt => dt.Debt).ToArray(); + } + + public override bool IsDebtOnlyEnvironmental() + { + bool foundEnviron = false; + bool foundOther = false; + + foreach( var debt in debtData.GetTrackedDebts() ) + { + if( debt.StartToEndDiff <= 0f ) continue; + + if( debt.type.IsEnvironmental() ) foundEnviron = true; + else foundOther = true; + } + + return foundEnviron && !foundOther; + } + + public override void ResetState() + { + TurnOffDebtSources(); + damageController.RepairAll(); + simulation.ResetRefillableSimulationParams(); + simulation.ResetFuelConsumption(); + + foreach( var tracker in debtTrackers ) + { + tracker.ResetDebt(); + } + } + + public override void TurnOffDebtSources() + { + locoController.SetNeutralState(); + } + + public override void UpdateDebtValues() + { + foreach( var tracker in debtTrackers ) + { + tracker.UpdateDebtValue(); + } + } + } +} \ No newline at end of file diff --git a/DVCustomCarLoader/LocoComponents/IServicePenaltyProvider.cs b/DVCustomCarLoader/LocoComponents/IServicePenaltyProvider.cs new file mode 100644 index 00000000..f21d6d7b --- /dev/null +++ b/DVCustomCarLoader/LocoComponents/IServicePenaltyProvider.cs @@ -0,0 +1,73 @@ +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using CCL_GameScripts; +using DV.ServicePenalty; +using UnityEngine; + +namespace DVCustomCarLoader.LocoComponents +{ + public interface IServicePenaltyProvider + { + IEnumerable GetDebtComponents(); + void ResetDebt( DebtComponent debt ); + void UpdateDebtValue( DebtComponent debt ); + + IEnumerable GetPitStopParameters(); + float GetPitStopLevel( ResourceType type ); + void ChangePitStopLevel( ResourceType type, float changeAmount ); + } + + public struct DebtTrackingInfo + { + public IServicePenaltyProvider Provider; + public DebtComponent Debt; + + public DebtTrackingInfo( IServicePenaltyProvider provider, DebtComponent debt ) + { + Provider = provider; + Debt = debt; + } + + public void ResetDebt() + { + Provider.UpdateDebtValue(Debt); + } + + public void UpdateDebtValue() + { + Provider.ResetDebt(Debt); + } + } + + public struct PitStopRefillable + { + public IServicePenaltyProvider Provider; + public ResourceType ResourceType; + public LocoParameterData parameterData; + + public PitStopRefillable( IServicePenaltyProvider provider, ResourceType type, float current, float max ) + { + Provider = provider; + ResourceType = type; + parameterData = new LocoParameterData(current, max); + } + + public PitStopRefillable( IServicePenaltyProvider provider, ResourceType type, SimComponent simComp ) + { + Provider = provider; + ResourceType = type; + parameterData = new LocoParameterData(simComp.value, simComp.max); + } + + public void RefreshLevel() + { + parameterData.value = Provider.GetPitStopLevel(ResourceType); + } + + public void UpdateLevel( float changeAmount ) + { + Provider.ChangePitStopLevel(ResourceType, changeAmount); + } + } +} \ No newline at end of file diff --git a/DVCustomCarLoader/Main.cs b/DVCustomCarLoader/Main.cs index e498c457..a0940fc5 100644 --- a/DVCustomCarLoader/Main.cs +++ b/DVCustomCarLoader/Main.cs @@ -35,9 +35,17 @@ private static bool Load(UnityModManager.ModEntry modEntry) public static CommsRadioCustomCarManager CommsRadioCustomCarManager; public static UnityModManager.ModEntry ModEntry; public static bool Enabled; - } - internal static class AppQuitWatcher + #region Logging + + public static void Log( string msg ) => ModEntry.Logger.Log(msg); + public static void Warning( string msg ) => ModEntry.Logger.Warning(msg); + public static void Error( string msg ) => ModEntry.Logger.Error(msg); + + #endregion + } + + internal static class AppQuitWatcher { public static bool isQuitting { get; private set; } = false; From f74a2a85658e1bf5185fbe9328e612a8d8532831 Mon Sep 17 00:00:00 2001 From: Katy Fox Date: Sun, 11 Jul 2021 16:28:29 -0400 Subject: [PATCH 04/20] Add a bunch of custom loco components --- CCL_GameScripts/CCL_GameScripts.csproj | 3 + CCL_GameScripts/CarJSONKeys.cs | 1 - CCL_GameScripts/CustomCarEnums.cs | 12 +- CCL_GameScripts/SimParamsDiesel.cs | 51 ++++- CCL_GameScripts/SimParamsEditor.cs | 24 +++ DVCustomCarLoader/CustomCar.cs | 5 - DVCustomCarLoader/CustomCarManager.cs | 1 - DVCustomCarLoader/DVCustomCarLoader.csproj | 2 + DVCustomCarLoader/LocoComponentManager.cs | 9 +- .../LocoComponents/CustomDieselSimEvents.cs | 156 ++++++++++++++ .../CustomLocoControllerDiesel.cs | 8 +- .../LocoComponents/CustomLocoSimDiesel.cs | 200 ++++++++++++------ .../LocoComponents/CustomLocoSimEvents.cs | 27 +++ .../LocoComponents/CustomLocoSimulation.cs | 57 ----- 14 files changed, 411 insertions(+), 145 deletions(-) create mode 100644 CCL_GameScripts/SimParamsEditor.cs create mode 100644 DVCustomCarLoader/LocoComponents/CustomDieselSimEvents.cs create mode 100644 DVCustomCarLoader/LocoComponents/CustomLocoSimEvents.cs diff --git a/CCL_GameScripts/CCL_GameScripts.csproj b/CCL_GameScripts/CCL_GameScripts.csproj index 713dcabb..8cbad3f2 100644 --- a/CCL_GameScripts/CCL_GameScripts.csproj +++ b/CCL_GameScripts/CCL_GameScripts.csproj @@ -39,8 +39,10 @@ + + @@ -48,6 +50,7 @@ + \ No newline at end of file diff --git a/CCL_GameScripts/CarJSONKeys.cs b/CCL_GameScripts/CarJSONKeys.cs index bccaaee7..4efb2774 100644 --- a/CCL_GameScripts/CarJSONKeys.cs +++ b/CCL_GameScripts/CarJSONKeys.cs @@ -10,7 +10,6 @@ public class CarJSONKeys public const string PREFAB_NAME = "carPrefabName"; public const string IDENTIFIER = "identifier"; public const string CAR_TYPE = "carType"; - public const string SIM_TYPE = "simType"; public const string REPLACE_FRONT_BOGIE = "frontBogieReplacement"; public const string FRONT_BOGIE_PARAMS = "frontBogieParams"; diff --git a/CCL_GameScripts/CustomCarEnums.cs b/CCL_GameScripts/CustomCarEnums.cs index ac18fa80..6e083992 100644 --- a/CCL_GameScripts/CustomCarEnums.cs +++ b/CCL_GameScripts/CustomCarEnums.cs @@ -51,10 +51,10 @@ public enum BaseTrainCarType /// /// The loco simulation type to use with the car. /// - public enum LocoSimType - { - None = 0, - DieselElectric = 1, - Steam = 2 - } + //public enum LocoSimType + //{ + // None = 0, + // DieselElectric = 1, + // Steam = 2 + //} } diff --git a/CCL_GameScripts/SimParamsDiesel.cs b/CCL_GameScripts/SimParamsDiesel.cs index 633d4f51..5e557dd3 100644 --- a/CCL_GameScripts/SimParamsDiesel.cs +++ b/CCL_GameScripts/SimParamsDiesel.cs @@ -1,5 +1,6 @@ using System.Collections; using UnityEngine; +using UnityEditor; namespace CCL_GameScripts { @@ -13,12 +14,14 @@ public class SimParamsDiesel : SimParamsBase public float ColdEnginePowerFactor = 0.8f; public float PassiveTempLoss = 5.5f; + public bool HasForwardRadiator = false; + public float ForwardMovementTempLoss = 0; public float IdleTempGain = 5; public float IdleMaxTemp = 52; public float TempGainPerRpm = 8; - public float MinTemp = 30; - public float MaxTemp = 120; + //public float MinTemp = 30; + //public float MaxTemp = 120; public float MaxPowerTemp = 75; [Header("Fuel (L)")] @@ -33,5 +36,49 @@ public class SimParamsDiesel : SimParamsBase public float OilCapacity = 500; public float OilConsumptionEngineRpm = 1; //public float OilConsumptionWheels = 0.12f; + + public void ApplyDE6Defaults() + { + ThrottleUpRate = 2; + ThrottleDownRate = 2; + ColdEnginePowerFactor = 0.8f; + PassiveTempLoss = -5.5f; + HasForwardRadiator = false; + ForwardMovementTempLoss = 0; + IdleTempGain = 5; + IdleMaxTemp = 52; + TempGainPerRpm = 8; + MaxPowerTemp = 75; + + FuelCapacity = 6000; + FuelConsumptionBase = 35; + FuelConsumptionMax = 1; + FuelConsumptionMin = 0.025f; + PerformanceDropDamageLevel = 0.5f; + OilCapacity = 500; + OilConsumptionEngineRpm = 1; + } + + public void ApplyShunterDefaults() + { + ThrottleUpRate = 2; + ThrottleDownRate = 2; + ColdEnginePowerFactor = 0.8f; + PassiveTempLoss = -4; + HasForwardRadiator = true; + ForwardMovementTempLoss = -3; + IdleTempGain = 5; + IdleMaxTemp = 52; + TempGainPerRpm = 12; + MaxPowerTemp = 75; + + FuelCapacity = 2000; + FuelConsumptionBase = 15; + FuelConsumptionMax = 1; + FuelConsumptionMin = 0.025f; + PerformanceDropDamageLevel = 0.5f; + OilCapacity = 100; + OilConsumptionEngineRpm = 0.3f; + } } } \ No newline at end of file diff --git a/CCL_GameScripts/SimParamsEditor.cs b/CCL_GameScripts/SimParamsEditor.cs new file mode 100644 index 00000000..e76f822f --- /dev/null +++ b/CCL_GameScripts/SimParamsEditor.cs @@ -0,0 +1,24 @@ +using UnityEditor; +using UnityEngine; + +namespace CCL_GameScripts +{ + [CustomEditor(typeof(SimParamsDiesel))] + public class SimParamsEditor : Editor + { + public override void OnInspectorGUI() + { + DrawDefaultInspector(); + + var script = (SimParamsDiesel)target; + if( GUILayout.Button("Apply DE6 Defaults") ) + { + script.ApplyDE6Defaults(); + } + if( GUILayout.Button("Apply Shunter Defaults") ) + { + script.ApplyShunterDefaults(); + } + } + } +} \ No newline at end of file diff --git a/DVCustomCarLoader/CustomCar.cs b/DVCustomCarLoader/CustomCar.cs index bc2bce4a..5fc59ba8 100644 --- a/DVCustomCarLoader/CustomCar.cs +++ b/DVCustomCarLoader/CustomCar.cs @@ -19,11 +19,6 @@ public class CustomCar /// public TrainCarType BaseCarType = TrainCarType.FlatbedEmpty; - /// - /// The locomotive type of this car - /// - public LocoSimType SimType = LocoSimType.None; - /// /// The base prefab that will be duplicated from. /// diff --git a/DVCustomCarLoader/CustomCarManager.cs b/DVCustomCarLoader/CustomCarManager.cs index 9131c132..1b361fcc 100644 --- a/DVCustomCarLoader/CustomCarManager.cs +++ b/DVCustomCarLoader/CustomCarManager.cs @@ -127,7 +127,6 @@ private static CustomCar CreateCustomCar( string directory, JSONObject jsonFile CarPrefab = carPrefab, identifier = jsonFile[CarJSONKeys.IDENTIFIER].str, BaseCarType = (TrainCarType)jsonFile[CarJSONKeys.CAR_TYPE].i, - SimType = (LocoSimType)jsonFile[CarJSONKeys.SIM_TYPE].i }; //Bogies diff --git a/DVCustomCarLoader/DVCustomCarLoader.csproj b/DVCustomCarLoader/DVCustomCarLoader.csproj index 88753ca6..8b03226b 100644 --- a/DVCustomCarLoader/DVCustomCarLoader.csproj +++ b/DVCustomCarLoader/DVCustomCarLoader.csproj @@ -78,8 +78,10 @@ + + diff --git a/DVCustomCarLoader/LocoComponentManager.cs b/DVCustomCarLoader/LocoComponentManager.cs index 6d9deeeb..b44fc9fb 100644 --- a/DVCustomCarLoader/LocoComponentManager.cs +++ b/DVCustomCarLoader/LocoComponentManager.cs @@ -3,6 +3,7 @@ using System.Linq; using DV.MultipleUnit; using UnityEngine; +using DVCustomCarLoader.LocoComponents; namespace DVCustomCarLoader { @@ -19,12 +20,12 @@ public static void AddDieselSimulation( GameObject prefab ) { GameObject basePrefab = CarTypes.GetCarPrefab(TrainCarType.LocoDiesel); - prefab.AddComponent(); - prefab.AddComponent(); - prefab.AddComponent(); + prefab.AddComponent(); + prefab.AddComponent(); + prefab.AddComponent(); //prefab.AddComponent(); - var controller = prefab.AddComponent(); + var controller = prefab.AddComponent(); var baseController = basePrefab.GetComponent(); controller.tractionTorqueCurve = baseController.tractionTorqueCurve; controller.brakePowerCurve = baseController.brakePowerCurve; diff --git a/DVCustomCarLoader/LocoComponents/CustomDieselSimEvents.cs b/DVCustomCarLoader/LocoComponents/CustomDieselSimEvents.cs new file mode 100644 index 00000000..19c43080 --- /dev/null +++ b/DVCustomCarLoader/LocoComponents/CustomDieselSimEvents.cs @@ -0,0 +1,156 @@ +using System.Collections; +using UnityEngine; + +namespace DVCustomCarLoader.LocoComponents +{ + public class CustomDieselSimEvents : + CustomLocoSimEvents + { + protected const float ENGINE_OVERHEAT_CHECK_PERIOD = 1f; + protected const float ENGINE_TEMP_CHECK_PERIOD = 3f; + protected const float FUEL_OIL_DMG_CHECK_PERIOD = 5f; + + protected const float OVERHEAT_TIME_LIMIT_MAX = 25f; + protected const float OVERHEAT_TIME_LIMIT_MIN = 15f; + + protected const float LOW_ENGINE_TEMP_THRESHOLD = 90f; + protected const float MID_ENGINE_TEMP_THRESHOLD = 105f; + + protected float LowFuelThreshold; + + protected float LowOilThreshold; + protected float MidOilThreshold; + + protected float LowSandThreshold; + + protected float EngineDamageThreshold; + + private Coroutine OverheatCheckCoroutine; + + protected override void InitThresholds() + { + LowFuelThreshold = sim.fuel.max / 4f; + + LowOilThreshold = sim.oil.max / 4f; + MidOilThreshold = sim.oil.max / 2f; + + LowSandThreshold = sim.sand.max / 5f; + + EngineDamageThreshold = sim.simParams.PerformanceDropDamageLevel; + } + + protected override void Start() + { + base.Start(); + + Fuel = GetAmount(sim.fuel.value, LowFuelThreshold); + Oil = GetAmount(sim.oil.value, LowOilThreshold, MidOilThreshold); + Sand = GetAmount(sim.sand.value, LowSandThreshold); + EngineTemp = GetAmount(sim.engineTemp.value, LOW_ENGINE_TEMP_THRESHOLD, MID_ENGINE_TEMP_THRESHOLD); + EngineDamage = GetAmount(dmgController.engine.DamagePercentage, EngineDamageThreshold); + } + + protected void OnDisable() + { + StopAllCoroutines(); + } + + protected void OnEnable() + { + StartCoroutine(CheckFuelOilDmgState()); + StartCoroutine(CheckEngineTemp()); + StartCoroutine(CheckWheelslip(0.5f)); + StartCoroutine(CheckCouplingIntegrity(2f)); + } + + private IEnumerator CheckEngineTemp() + { + WaitForSeconds timeout = WaitFor.Seconds(ENGINE_TEMP_CHECK_PERIOD); + while( true ) + { + yield return timeout; + + Amount curTempLvl = GetAmount(sim.engineTemp.value, LOW_ENGINE_TEMP_THRESHOLD, MID_ENGINE_TEMP_THRESHOLD); + if( EngineTemp != curTempLvl ) + { + EngineTemp = curTempLvl; + EngineTempChanged.Invoke(curTempLvl); + } + + if( curTempLvl == Amount.High && OverheatCheckCoroutine == null ) + { + OverheatCheckCoroutine = StartCoroutine(OverHeatCheck()); + } + } + + //yield break; + } + + private IEnumerator OverHeatCheck() + { + WaitForSeconds waitTimeout = WaitFor.Seconds(ENGINE_OVERHEAT_CHECK_PERIOD); + + float timeOnHighTemp = 0f; + float overheatTimeLimit = Random.Range(OVERHEAT_TIME_LIMIT_MIN, OVERHEAT_TIME_LIMIT_MAX); + + while( timeOnHighTemp < overheatTimeLimit ) + { + yield return waitTimeout; + if( EngineTemp < Amount.High ) + { + OverheatCheckCoroutine = null; + yield break; + } + timeOnHighTemp += ENGINE_OVERHEAT_CHECK_PERIOD; + } + + EngineTempChanged.Invoke(Amount.Full); + OverheatCheckCoroutine = null; + + yield break; + } + + private IEnumerator CheckFuelOilDmgState() + { + WaitForSeconds waitTimeout = WaitFor.Seconds(FUEL_OIL_DMG_CHECK_PERIOD); + + while( true ) + { + yield return waitTimeout; + + Amount newFuelLevel = GetAmount(sim.fuel.value, LowFuelThreshold); + if( Fuel != newFuelLevel ) + { + Fuel = newFuelLevel; + if( (newFuelLevel == Amount.Depleted && sim.engineOn) || newFuelLevel != Amount.Depleted ) + { + FuelChanged.Invoke(newFuelLevel); + } + } + + Amount newOilLevel = GetAmount(sim.oil.value, LowOilThreshold, MidOilThreshold); + if( Oil != newOilLevel ) + { + Oil = newOilLevel; + OilChanged.Invoke(newOilLevel); + } + + Amount newSandLevel = GetAmount(sim.sand.value, LowSandThreshold); + if( Sand != newSandLevel ) + { + Sand = newSandLevel; + SandChanged.Invoke(newSandLevel); + } + + Amount newDamageLevel = GetAmount(dmgController.engine.DamagePercentage, EngineDamageThreshold); + if( EngineDamage != newDamageLevel ) + { + EngineDamage = newDamageLevel; + EngineDamageChanged.Invoke(newDamageLevel); + } + } + + //yield break; + } + } +} \ No newline at end of file diff --git a/DVCustomCarLoader/LocoComponents/CustomLocoControllerDiesel.cs b/DVCustomCarLoader/LocoComponents/CustomLocoControllerDiesel.cs index 2519ace0..60609f47 100644 --- a/DVCustomCarLoader/LocoComponents/CustomLocoControllerDiesel.cs +++ b/DVCustomCarLoader/LocoComponents/CustomLocoControllerDiesel.cs @@ -15,7 +15,7 @@ public class CustomLocoControllerDiesel : CustomLocoController(); damageController = GetComponent(); - eventController = GetComponent(); + eventController = GetComponent(); CarVisitChecker carVisitChecker = gameObject.AddComponent(); carVisitChecker.Initialize(train); @@ -179,8 +179,8 @@ public override void SetNeutralState() private void SetupListeners( bool on ) { - eventController.FuelChanged.Manage(new Action(OnFuelChanged), on); - eventController.EngineTempChanged.Manage(new Action(OnEngineTempChanged), on); + eventController.FuelChanged.Manage(OnFuelChanged, on); + eventController.EngineTempChanged.Manage(OnEngineTempChanged, on); } //protected override bool ShouldSwitchToTrainBrakeOnStart() diff --git a/DVCustomCarLoader/LocoComponents/CustomLocoSimDiesel.cs b/DVCustomCarLoader/LocoComponents/CustomLocoSimDiesel.cs index ba74af87..a77e4f6d 100644 --- a/DVCustomCarLoader/LocoComponents/CustomLocoSimDiesel.cs +++ b/DVCustomCarLoader/LocoComponents/CustomLocoSimDiesel.cs @@ -18,6 +18,11 @@ public class CustomLocoSimDiesel : CustomLocoSimulation, IServi //public SimParamsDiesel simParams; private DamageControllerDiesel dmgController; + // Sanders + public bool sandOn; + public SimComponent sand; + public SimComponent sandFlow = new SimComponent("SandFlow", 0f, 1f, 0.1f, 0f); + // Engine public bool engineOn; public bool goingForward; @@ -36,70 +41,6 @@ public class CustomLocoSimDiesel : CustomLocoSimulation, IServi private const string TOTAL_FUEL_CONSUMED_SAVE_KEY = "fuelConsumed"; - public override IEnumerable GetDebtComponents() - { - return base.GetDebtComponents() - .Concat(new[] - { - new DebtTrackingInfo(this, new DebtComponent(0, ResourceType.EnvironmentDamageFuel)), - new DebtTrackingInfo(this, new DebtComponent(fuel.value, ResourceType.Fuel)), - new DebtTrackingInfo(this, new DebtComponent(oil.value, ResourceType.Oil)) - }); - } - - public override void ResetDebt( DebtComponent debt ) - { - switch( debt.type ) - { - case ResourceType.Fuel: - debt.ResetComponent(fuel.value); - break; - - case ResourceType.Oil: - debt.ResetComponent(oil.value); - break; - - case ResourceType.EnvironmentDamageFuel: - debt.ResetComponent(TotalFuelConsumed); - break; - - default: - base.ResetDebt(debt); - break; - } - } - - public override void UpdateDebtValue( DebtComponent debt ) - { - switch( debt.type ) - { - case ResourceType.Fuel: - debt.UpdateEndValue(fuel.value); - break; - - case ResourceType.Oil: - debt.UpdateEndValue(oil.value); - break; - - case ResourceType.EnvironmentDamageFuel: - debt.UpdateStartValue(TotalFuelConsumed); - break; - - default: - base.UpdateDebtValue(debt); - break; - } - } - - public override IEnumerable GetPitStopParameters() - { - return base.GetPitStopParameters() - .Concat(new[] - { - new PitStopRefillable(this, ResourceType.Oil, oil), - new PitStopRefillable(this, ResourceType.Fuel, fuel) - }); - } public override JObject GetComponentsSaveData() { @@ -117,6 +58,7 @@ protected override void InitComponents() base.InitComponents(); dmgController = GetComponent(); + sand = new SimComponent("Sand", 0f, simParams.SandCapacity, 40f, simParams.SandCapacity); speed = new SimComponent("Speed", 0f, simParams.MaxSpeed, 1f, 0f); fuel = new SimComponent("Fuel", 0f, simParams.FuelCapacity, 1200f, simParams.FuelCapacity); oil = new SimComponent("Oil", 0f, simParams.OilCapacity, 100f, simParams.OilCapacity); @@ -196,13 +138,23 @@ private void SimulateEngineTemp( float delta ) { engineTemp.AddNextValue(engineRPM.value * simParams.TempGainPerRpm * delta); } - if( engineOn && engineTemp.value < 52f ) + if( engineOn && engineTemp.value < simParams.IdleMaxTemp ) { engineTemp.AddNextValue(simParams.IdleTempGain * delta); } if( engineTemp.value > engineTemp.min ) { engineTemp.AddNextValue(simParams.PassiveTempLoss * delta); + + // check forward radiator + if( simParams.HasForwardRadiator && goingForward ) + { + if( !engineOn || (engineTemp.value > simParams.IdleMaxTemp) ) + { + // reduce to idle temp (engine On) or 0 (off) + engineTemp.AddNextValue((speed.value / speed.max) * simParams.ForwardMovementTempLoss * delta); + } + } } } @@ -252,5 +204,123 @@ protected override void SimulateTick( float delta ) SimulateEngineTemp(delta); SetValuesToNextValues(); } + + #region IServicePenaltyProvider + + public override IEnumerable GetDebtComponents() + { + return new[] + { + new DebtTrackingInfo(this, new DebtComponent(0, ResourceType.EnvironmentDamageFuel)), + new DebtTrackingInfo(this, new DebtComponent(fuel.value, ResourceType.Fuel)), + new DebtTrackingInfo(this, new DebtComponent(sand.value, ResourceType.Sand)), + new DebtTrackingInfo(this, new DebtComponent(oil.value, ResourceType.Oil)) + }; + } + + public override void ResetDebt( DebtComponent debt ) + { + switch( debt.type ) + { + case ResourceType.Sand: + debt.ResetComponent(sand.value); + break; + + case ResourceType.Fuel: + debt.ResetComponent(fuel.value); + break; + + case ResourceType.Oil: + debt.ResetComponent(oil.value); + break; + + case ResourceType.EnvironmentDamageFuel: + debt.ResetComponent(TotalFuelConsumed); + break; + + default: + Main.Warning("Tried to reset debt value this loco sim doesn't have"); + break; + } + } + + public override void UpdateDebtValue( DebtComponent debt ) + { + switch( debt.type ) + { + case ResourceType.Sand: + debt.UpdateEndValue(sand.value); + break; + + case ResourceType.Fuel: + debt.UpdateEndValue(fuel.value); + break; + + case ResourceType.Oil: + debt.UpdateEndValue(oil.value); + break; + + case ResourceType.EnvironmentDamageFuel: + debt.UpdateStartValue(TotalFuelConsumed); + break; + + default: + Main.Warning("Tried to update debt value this loco sim doesn't have"); + break; + } + } + + public override IEnumerable GetPitStopParameters() + { + return new[] + { + new PitStopRefillable(this, ResourceType.Oil, oil), + new PitStopRefillable(this, ResourceType.Fuel, fuel), + new PitStopRefillable(this, ResourceType.Sand, sand) + }; + } + + public override float GetPitStopLevel( ResourceType type ) + { + switch( type ) + { + case ResourceType.Sand: + return sand.value; + + case ResourceType.Fuel: + return fuel.value; + + case ResourceType.Oil: + return oil.value; + + default: + Main.Warning("Tried to get pit stop value this loco sim doesn't have"); + return 0; + } + } + + public override void ChangePitStopLevel( ResourceType type, float changeAmount ) + { + switch( type ) + { + case ResourceType.Sand: + sand.AddValue(changeAmount); + break; + + case ResourceType.Fuel: + fuel.AddValue(changeAmount); + break; + + case ResourceType.Oil: + oil.AddValue(changeAmount); + break; + + default: + Main.Warning("Trying to refill/repair something that is not part of this loco"); + break; + } + } + + #endregion } } diff --git a/DVCustomCarLoader/LocoComponents/CustomLocoSimEvents.cs b/DVCustomCarLoader/LocoComponents/CustomLocoSimEvents.cs new file mode 100644 index 00000000..80ef544f --- /dev/null +++ b/DVCustomCarLoader/LocoComponents/CustomLocoSimEvents.cs @@ -0,0 +1,27 @@ +using System.Collections; +using UnityEngine; + +namespace DVCustomCarLoader.LocoComponents +{ + public abstract class CustomLocoSimEvents : LocoSimulationEvents + where TDmg : DamageControllerCustomLoco + where TSim : CustomLocoSimulation + { + protected TDmg dmgController; + protected TSim sim; + + protected override void Awake() + { + base.Awake(); + sim = GetComponent(); + dmgController = GetComponent(); + } + + protected virtual void Start() + { + InitThresholds(); + } + + protected abstract void InitThresholds(); + } +} \ No newline at end of file diff --git a/DVCustomCarLoader/LocoComponents/CustomLocoSimulation.cs b/DVCustomCarLoader/LocoComponents/CustomLocoSimulation.cs index ba974e3e..ec642580 100644 --- a/DVCustomCarLoader/LocoComponents/CustomLocoSimulation.cs +++ b/DVCustomCarLoader/LocoComponents/CustomLocoSimulation.cs @@ -21,66 +21,9 @@ public abstract class CustomLocoSimulation : CustomLocoSimulation { public TParams simParams; - // Sanders - public bool sandOn; - public SimComponent sand; - public SimComponent sandFlow = new SimComponent("SandFlow", 0f, 1f, 0.1f, 0f); - protected override void InitComponents() { simParams = GetComponent(); - sand = new SimComponent("Sand", 0f, simParams.SandCapacity, 40f, simParams.SandCapacity); - } - - public override IEnumerable GetDebtComponents() - { - return new[] - { - new DebtTrackingInfo(this, new DebtComponent(sand.value, ResourceType.Sand)) - }; - } - - public override void ResetDebt( DebtComponent debt ) - { - if( debt.type == ResourceType.Sand ) - { - debt.ResetComponent(sand.value); - } - } - - public override void UpdateDebtValue( DebtComponent debt ) - { - if( debt.type == ResourceType.Sand ) - { - debt.UpdateEndValue(sand.value); - } - } - - public override IEnumerable GetPitStopParameters() - { - return new[] - { - new PitStopRefillable(this, ResourceType.Sand, sand) - }; - } - - public override void ChangePitStopLevel( ResourceType type, float changeAmount ) - { - if( type == ResourceType.Sand ) - { - sand.AddValue(changeAmount); - } - } - - public override float GetPitStopLevel( ResourceType type ) - { - if( type == ResourceType.Sand ) - { - return sand.value; - } - - Main.Warning("Tried to get pit stop value this loco sim doesn't have"); - return 0; } } } \ No newline at end of file From 7ccf92344ed9420a2004a0dcbe983d56d9dd8ff0 Mon Sep 17 00:00:00 2001 From: Katy Fox Date: Mon, 12 Jul 2021 13:39:05 -0400 Subject: [PATCH 05/20] Set up damage controllers --- CCL_GameScripts/CustomCarEnums.cs | 10 - CCL_GameScripts/DamageConfigBasic.cs | 64 +++++++ CCL_GameScripts/DamageConfigDiesel.cs | 94 ++++++++++ CCL_GameScripts/DamageConfigEditor.cs | 24 +++ CCL_GameScripts/SimParamsBase.cs | 38 ++++ CCL_GameScripts/SimParamsDiesel.cs | 42 +++++ DVCustomCarLoader/CustomCar.cs | 9 +- DVCustomCarLoader/LocoComponentManager.cs | 14 ++ .../LocoComponents/CustomDieselSimEvents.cs | 2 +- .../LocoComponents/CustomLocoController.cs | 61 +++++- .../CustomLocoControllerDiesel.cs | 55 ++---- .../LocoComponents/CustomLocoSimEvents.cs | 9 +- .../DamageControllerCustomDiesel.cs | 175 +++++++++++++++++- .../DamageControllerCustomLoco.cs | 68 +++++++ 14 files changed, 602 insertions(+), 63 deletions(-) create mode 100644 CCL_GameScripts/DamageConfigBasic.cs create mode 100644 CCL_GameScripts/DamageConfigDiesel.cs create mode 100644 CCL_GameScripts/DamageConfigEditor.cs diff --git a/CCL_GameScripts/CustomCarEnums.cs b/CCL_GameScripts/CustomCarEnums.cs index 6e083992..58ae5648 100644 --- a/CCL_GameScripts/CustomCarEnums.cs +++ b/CCL_GameScripts/CustomCarEnums.cs @@ -47,14 +47,4 @@ public enum BaseTrainCarType HandCar = 700, NuclearFlask = 800 } - - /// - /// The loco simulation type to use with the car. - /// - //public enum LocoSimType - //{ - // None = 0, - // DieselElectric = 1, - // Steam = 2 - //} } diff --git a/CCL_GameScripts/DamageConfigBasic.cs b/CCL_GameScripts/DamageConfigBasic.cs new file mode 100644 index 00000000..b5ebb22b --- /dev/null +++ b/CCL_GameScripts/DamageConfigBasic.cs @@ -0,0 +1,64 @@ +using System.Collections; +using UnityEngine; + +namespace CCL_GameScripts +{ + public class DamageConfigBasic : MonoBehaviour + { + // defaults from DE6 + [Header("Body Damage (HP/s)")] + public float BodyHitpoints = 5600f; + public float BodyCollisionResistance = 25f; + public float BodyCollisionMultiplier = 1f; + public float BodyFireResistance = 7.5f; + public float BodyFireMultiplier = 1f; + public float DamageTolerance = 0.01f; + + [Header("Wheel Damage (HP/s)")] + public float WheelHitpoints = 2000f; + //public float BrakingDamageMultiplier = 0.33f; + //public float BogieStressDPS = 0.03f; + //public float WheelslipDPS = 18f; + //public float WheelCollisionMultiplier = 0.01f; + //public float WheelFireMultiplier = 0.01f; + public AnimationCurve BrakeSpeedDamageCurve; + + public DamageConfigBasic() + { + // DE6 Defaults + BrakeSpeedDamageCurve = + new AnimationCurve( + new Keyframe(0, 0, 0, 0, 0.3333f, 0.3333f), + new Keyframe(2, 0, 0, 0, 0.3333f, 0.3333f), + new Keyframe(7.5f, 0.125f, 0.0285f, 0.0285f, 0.3333f, 0.3333f), + new Keyframe(29, 0.6706f, 0.0151f, 0.0151f, 0.3333f, 0.3333f), + new Keyframe(100, 1, 0, 0, 0.3333f, 0.3333f)) + { + preWrapMode = WrapMode.ClampForever, + postWrapMode = WrapMode.ClampForever + }; + } + + public void ApplyDefaults() + { + BodyHitpoints = 5600f; + BodyCollisionResistance = 25f; + BodyCollisionMultiplier = 1f; + BodyFireResistance = 7.5f; + BodyFireMultiplier = 1f; + DamageTolerance = 0.01f; + + BrakeSpeedDamageCurve = + new AnimationCurve( + new Keyframe(0, 0, 0, 0, 0.3333f, 0.3333f), + new Keyframe(2, 0, 0, 0, 0.3333f, 0.3333f), + new Keyframe(7.5f, 0.125f, 0.0285f, 0.0285f, 0.3333f, 0.3333f), + new Keyframe(29, 0.6706f, 0.0151f, 0.0151f, 0.3333f, 0.3333f), + new Keyframe(100, 1, 0, 0, 0.3333f, 0.3333f)) + { + preWrapMode = WrapMode.ClampForever, + postWrapMode = WrapMode.ClampForever + }; + } + } +} \ No newline at end of file diff --git a/CCL_GameScripts/DamageConfigDiesel.cs b/CCL_GameScripts/DamageConfigDiesel.cs new file mode 100644 index 00000000..bda61f71 --- /dev/null +++ b/CCL_GameScripts/DamageConfigDiesel.cs @@ -0,0 +1,94 @@ +using System; +using UnityEngine; + +namespace CCL_GameScripts +{ + public class DamageConfigDiesel : DamageConfigBasic + { + [Header("Engine Damage (HP/s)")] + public float EngineHitpoints = 4000f; + public float ColdEngineDPS = 0.1f; + public float ColdEngineRPMThreshold = 0.5f; + public float ColdEngineTempThreshold = 45f; + + public float EngineStartDamage = 10f; + public float EngineRunningDPS = 0.05f; + public float EngineNoOilDPS = 30f; + + public float EngineCollisionMultiplier = 0.15f; + public float EngineFireMultiplier = 0.1f; + public float CollisionShutoffThreshold = 100f; + + [Header("Engine Failures")] + public float RandomShutoffCheckPeriod = 10f; + [Header("chance = mult * (damage - threshold)")] + public float EngineFailureThreshold = 0.8f; + public float ShutoffChanceMultiplier = 4f; + + public void ApplyDE6Defaults() + { + // base + ApplyDefaults(); + + BodyHitpoints = 5600f; + BodyCollisionResistance = 25f; + BodyCollisionMultiplier = 1f; + BodyFireResistance = 7.5f; + BodyFireMultiplier = 1f; + DamageTolerance = 0.01f; + + WheelHitpoints = 2000f; + + // diesel + EngineHitpoints = 4000f; + ColdEngineDPS = 0.1f; + ColdEngineRPMThreshold = 0.5f; + ColdEngineTempThreshold = 45f; + + EngineStartDamage = 10f; + EngineRunningDPS = 0.05f; + EngineNoOilDPS = 30f; + + EngineCollisionMultiplier = 0.15f; + EngineFireMultiplier = 0.1f; + CollisionShutoffThreshold = 100f; + + RandomShutoffCheckPeriod = 10f; + EngineFailureThreshold = 0.8f; + ShutoffChanceMultiplier = 4f; + } + + public void ApplyShunterDefaults() + { + // base + ApplyDefaults(); + + BodyHitpoints = 5600f; + BodyCollisionResistance = 25f; + BodyCollisionMultiplier = 1f; + BodyFireResistance = 7.5f; + BodyFireMultiplier = 1f; + DamageTolerance = 0.01f; + + WheelHitpoints = 1000f; + + // diesel + EngineHitpoints = 4000f; + ColdEngineDPS = 0.1f; + ColdEngineRPMThreshold = 0.5f; + ColdEngineTempThreshold = 45f; + + EngineStartDamage = 10f; + EngineRunningDPS = 0.05f; + EngineNoOilDPS = 30f; + + EngineCollisionMultiplier = 0.15f; + EngineFireMultiplier = 0.1f; + CollisionShutoffThreshold = 100f; + + RandomShutoffCheckPeriod = 10f; + EngineFailureThreshold = 0.8f; + ShutoffChanceMultiplier = 4f; + } + } +} diff --git a/CCL_GameScripts/DamageConfigEditor.cs b/CCL_GameScripts/DamageConfigEditor.cs new file mode 100644 index 00000000..50b71726 --- /dev/null +++ b/CCL_GameScripts/DamageConfigEditor.cs @@ -0,0 +1,24 @@ +using UnityEditor; +using UnityEngine; + +namespace CCL_GameScripts +{ + [CustomEditor(typeof(DamageConfigDiesel))] + public class DamageConfigEditor : Editor + { + public override void OnInspectorGUI() + { + DrawDefaultInspector(); + + var script = (DamageConfigDiesel)target; + if( GUILayout.Button("Apply DE6 Defaults") ) + { + script.ApplyDE6Defaults(); + } + if( GUILayout.Button("Apply Shunter Defaults") ) + { + script.ApplyShunterDefaults(); + } + } + } +} \ No newline at end of file diff --git a/CCL_GameScripts/SimParamsBase.cs b/CCL_GameScripts/SimParamsBase.cs index 0c1f10b7..00a3bbe8 100644 --- a/CCL_GameScripts/SimParamsBase.cs +++ b/CCL_GameScripts/SimParamsBase.cs @@ -3,13 +3,51 @@ namespace CCL_GameScripts { + public enum LocoParamsType + { + None = 0, + DieselElectric = 1, + Steam = 2 + } + public abstract class SimParamsBase : MonoBehaviour { + [HideInInspector] + public abstract LocoParamsType SimType { get; } + // default values from diesel [Header("Basic")] public float MaxSpeed = 120f; public float SandCapacity = 200f; public float SandValveSpeed = 10f; public float SandMaxFlow = 5f; + + [Header("Physics Curves")] + public AnimationCurve BrakePowerCurve; + public AnimationCurve TractionTorqueCurve; + + public SimParamsBase() + { + // DE6 defaults + BrakePowerCurve = + new AnimationCurve( + new Keyframe(0, 0, 0.3512f, 0.3512f, 0.3333f, 0.1033f), + new Keyframe(1, 1, 1.7677f, 1.7677f, 0.0627f, 0.3333f)) + { + preWrapMode = WrapMode.ClampForever, + postWrapMode = WrapMode.ClampForever + }; + + TractionTorqueCurve = + new AnimationCurve( + new Keyframe(0, 1, 0, 0, 0.3333f, 0.3333f), + new Keyframe(15, 1, 0, 0, 0.3333f, 0.3333f), + new Keyframe(98.8f, 0.4813f, -0.0087f, -0.0087f, 0.1278f, 0.3589f), + new Keyframe(120, 0, -0.0247f, -0.0247f, 0.2221f, 0.3333f)) + { + preWrapMode = WrapMode.ClampForever, + postWrapMode = WrapMode.ClampForever + }; + } } } \ No newline at end of file diff --git a/CCL_GameScripts/SimParamsDiesel.cs b/CCL_GameScripts/SimParamsDiesel.cs index 5e557dd3..7398da0d 100644 --- a/CCL_GameScripts/SimParamsDiesel.cs +++ b/CCL_GameScripts/SimParamsDiesel.cs @@ -6,6 +6,8 @@ namespace CCL_GameScripts { public class SimParamsDiesel : SimParamsBase { + public override LocoParamsType SimType => LocoParamsType.DieselElectric; + [Header("Throttle")] public float ThrottleUpRate = 2f; public float ThrottleDownRate = 2f; @@ -37,6 +39,7 @@ public class SimParamsDiesel : SimParamsBase public float OilConsumptionEngineRpm = 1; //public float OilConsumptionWheels = 0.12f; + public void ApplyDE6Defaults() { ThrottleUpRate = 2; @@ -57,6 +60,26 @@ public void ApplyDE6Defaults() PerformanceDropDamageLevel = 0.5f; OilCapacity = 500; OilConsumptionEngineRpm = 1; + + BrakePowerCurve = + new AnimationCurve( + new Keyframe(0, 0, 0.3512f, 0.3512f, 0.3333f, 0.1033f), + new Keyframe(1, 1, 1.7677f, 1.7677f, 0.0627f, 0.3333f)) + { + preWrapMode = WrapMode.ClampForever, + postWrapMode = WrapMode.ClampForever + }; + + TractionTorqueCurve = + new AnimationCurve( + new Keyframe(0, 1, 0, 0, 0.3333f, 0.3333f), + new Keyframe(15, 1, 0, 0, 0.3333f, 0.3333f), + new Keyframe(98.8f, 0.4813f, -0.0087f, -0.0087f, 0.1278f, 0.3589f), + new Keyframe(120, 0, -0.0247f, -0.0247f, 0.2221f, 0.3333f)) + { + preWrapMode = WrapMode.ClampForever, + postWrapMode = WrapMode.ClampForever + }; } public void ApplyShunterDefaults() @@ -79,6 +102,25 @@ public void ApplyShunterDefaults() PerformanceDropDamageLevel = 0.5f; OilCapacity = 100; OilConsumptionEngineRpm = 0.3f; + + BrakePowerCurve = + new AnimationCurve( + new Keyframe(0, 0, 0.2744f, 0.2744f, 0.3333f, 0.0561f), + new Keyframe(1, 1, 1.6481f, 1.6481f, 0.0607f, 0.3333f)) + { + preWrapMode = WrapMode.ClampForever, + postWrapMode = WrapMode.ClampForever + }; + + TractionTorqueCurve = + new AnimationCurve( + new Keyframe(0, 1, 0, 0, 0.3333f, 0.3333f), + new Keyframe(35, 1, 0, 0, 0.3333f, 0.3333f), + new Keyframe(80, 0, 0, 0, 0.3333f, 0.3333f)) + { + preWrapMode = WrapMode.ClampForever, + postWrapMode = WrapMode.ClampForever + }; } } } \ No newline at end of file diff --git a/DVCustomCarLoader/CustomCar.cs b/DVCustomCarLoader/CustomCar.cs index 5fc59ba8..a5431ba9 100644 --- a/DVCustomCarLoader/CustomCar.cs +++ b/DVCustomCarLoader/CustomCar.cs @@ -333,10 +333,13 @@ public void FinalizePrefab() newCar.wheelRadius = baseCar.wheelRadius; newCar.carType = BaseCarType; - CarPrefab = newFab; + var simParams = newFab.GetComponent(); + if( simParams ) + { + LocoComponentManager.AddLocoSimulation(newFab, simParams); + } - string pn = CarPrefab == null ? "null" : "notnull"; - Main.ModEntry.Logger.Log($"New prefab for {identifier} is {pn}"); + CarPrefab = newFab; Main.ModEntry.Logger.Log($"Finalized prefab for {identifier}"); } diff --git a/DVCustomCarLoader/LocoComponentManager.cs b/DVCustomCarLoader/LocoComponentManager.cs index b44fc9fb..32fe1ef8 100644 --- a/DVCustomCarLoader/LocoComponentManager.cs +++ b/DVCustomCarLoader/LocoComponentManager.cs @@ -4,11 +4,25 @@ using DV.MultipleUnit; using UnityEngine; using DVCustomCarLoader.LocoComponents; +using CCL_GameScripts; namespace DVCustomCarLoader { public static class LocoComponentManager { + public static void AddLocoSimulation( GameObject prefab, SimParamsBase simParams ) + { + switch( simParams.SimType ) + { + case LocoParamsType.DieselElectric: + AddDieselSimulation(prefab); + break; + + default: + break; + } + } + // Order to add components: // - Simulation // - SimulationEvents diff --git a/DVCustomCarLoader/LocoComponents/CustomDieselSimEvents.cs b/DVCustomCarLoader/LocoComponents/CustomDieselSimEvents.cs index 19c43080..a44723fc 100644 --- a/DVCustomCarLoader/LocoComponents/CustomDieselSimEvents.cs +++ b/DVCustomCarLoader/LocoComponents/CustomDieselSimEvents.cs @@ -4,7 +4,7 @@ namespace DVCustomCarLoader.LocoComponents { public class CustomDieselSimEvents : - CustomLocoSimEvents + CustomLocoSimEvents { protected const float ENGINE_OVERHEAT_CHECK_PERIOD = 1f; protected const float ENGINE_TEMP_CHECK_PERIOD = 3f; diff --git a/DVCustomCarLoader/LocoComponents/CustomLocoController.cs b/DVCustomCarLoader/LocoComponents/CustomLocoController.cs index 3cd29a2a..e71928ed 100644 --- a/DVCustomCarLoader/LocoComponents/CustomLocoController.cs +++ b/DVCustomCarLoader/LocoComponents/CustomLocoController.cs @@ -1,17 +1,72 @@ using System.Collections; +using DV; +using DV.ServicePenalty; using UnityEngine; namespace DVCustomCarLoader.LocoComponents { public abstract class CustomLocoController : LocoControllerBase { - + public AnimationCurve tractionTorqueCurve; + + protected DebtTrackerCustomLoco locoDebt; + protected CarVisitChecker carVisitChecker; } - public abstract class CustomLocoController : CustomLocoController + public abstract class CustomLocoController : CustomLocoController where TSim : CustomLocoSimulation + where TDmg : DamageControllerCustomLoco + where TEvents : CustomLocoSimEvents { protected TSim sim; - protected DebtTrackerCustomLoco locoDebt; + protected TDmg damageController; + protected TEvents eventController; + + protected override void Awake() + { + base.Awake(); + sim = GetComponent(); + damageController = GetComponent(); + eventController = GetComponent(); + + var simParams = GetComponent(); + if( simParams ) + { + brakePowerCurve = simParams.BrakePowerCurve; + tractionTorqueCurve = simParams.TractionTorqueCurve; + } + else + { + Main.Error($"Sim parameters not found for this loco {train?.ID}"); + } + + carVisitChecker = gameObject.AddComponent(); + carVisitChecker.Initialize(train); + + train.LogicCarInitialized += OnLogicCarInitialized; + } + + protected virtual void OnLogicCarInitialized() + { + train.LogicCarInitialized -= OnLogicCarInitialized; + + if( !train.playerSpawnedCar ) + { + locoDebt = new DebtTrackerCustomLoco(train.ID, train.carType, this, damageController, sim); + SingletonBehaviour.Instance.RegisterLocoDebtTracker(locoDebt); + } + + train.OnDestroyCar += OnLocoDestroyed; + gameObject.AddComponent().Initialize(sim, damageController); + } + + protected virtual void OnLocoDestroyed( TrainCar train ) + { + train.OnDestroyCar -= OnLocoDestroyed; + if( !train.playerSpawnedCar ) + { + SingletonBehaviour.Instance.StageLocoDebtOnLocoDestroy(locoDebt); + } + } } } \ No newline at end of file diff --git a/DVCustomCarLoader/LocoComponents/CustomLocoControllerDiesel.cs b/DVCustomCarLoader/LocoComponents/CustomLocoControllerDiesel.cs index 60609f47..95c4cbeb 100644 --- a/DVCustomCarLoader/LocoComponents/CustomLocoControllerDiesel.cs +++ b/DVCustomCarLoader/LocoComponents/CustomLocoControllerDiesel.cs @@ -9,16 +9,15 @@ namespace DVCustomCarLoader.LocoComponents { - public class CustomLocoControllerDiesel : CustomLocoController + public class CustomLocoControllerDiesel : + CustomLocoController< + CustomLocoSimDiesel, + DamageControllerCustomDiesel, + CustomDieselSimEvents> { public bool Backlight { get; set; } public bool FanOn { get; set; } - private DamageControllerCustomDiesel damageController; - private CustomDieselSimEvents eventController; - - public AnimationCurve tractionTorqueCurve; - public float EngineRPM => sim.engineRPM.value; public float EngineRPMGauge => (sim.engineOn) ? (12.5f + sim.engineRPM.value * 100f) : 0f; public float EngineTemp => sim.engineTemp.value; @@ -92,21 +91,14 @@ public override void SetThrottle( float throttleLever ) } - protected override void Awake() - { - base.Awake(); - sim = GetComponent(); - damageController = GetComponent(); - eventController = GetComponent(); - CarVisitChecker carVisitChecker = gameObject.AddComponent(); - carVisitChecker.Initialize(train); + //protected override void Awake() + //{ + // base.Awake(); - // TODO: MU, save state - //MultipleUnitModule component = base.GetComponent(); - //base.gameObject.AddComponent().Initialize(sim, damageController, this, carVisitChecker, component); - - train.LogicCarInitialized += OnLogicCarInitialized; - } + // // TODO: MU, save state + // //MultipleUnitModule component = base.GetComponent(); + // //base.gameObject.AddComponent().Initialize(sim, damageController, this, carVisitChecker, component); + //} public override float GetTractionForce() @@ -142,29 +134,6 @@ private void OnFuelChanged( LocoSimulationEvents.Amount amount ) } } - private void OnLocoDestroyed( TrainCar train ) - { - train.OnDestroyCar -= OnLocoDestroyed; - if( !train.playerSpawnedCar ) - { - SingletonBehaviour.Instance.StageLocoDebtOnLocoDestroy(locoDebt); - } - } - - private void OnLogicCarInitialized() - { - train.LogicCarInitialized -= OnLogicCarInitialized; - - if( !train.playerSpawnedCar ) - { - locoDebt = new DebtTrackerCustomLoco(train.ID, train.carType, this, damageController, sim); - SingletonBehaviour.Instance.RegisterLocoDebtTracker(locoDebt); - } - - train.OnDestroyCar += OnLocoDestroyed; - gameObject.AddComponent().Initialize(sim, damageController); - } - public override void SetNeutralState() { EngineRunning = false; diff --git a/DVCustomCarLoader/LocoComponents/CustomLocoSimEvents.cs b/DVCustomCarLoader/LocoComponents/CustomLocoSimEvents.cs index 80ef544f..715e8416 100644 --- a/DVCustomCarLoader/LocoComponents/CustomLocoSimEvents.cs +++ b/DVCustomCarLoader/LocoComponents/CustomLocoSimEvents.cs @@ -3,9 +3,14 @@ namespace DVCustomCarLoader.LocoComponents { - public abstract class CustomLocoSimEvents : LocoSimulationEvents - where TDmg : DamageControllerCustomLoco + public abstract class CustomLocoSimEvents : LocoSimulationEvents + { + + } + + public abstract class CustomLocoSimEvents : CustomLocoSimEvents where TSim : CustomLocoSimulation + where TDmg : DamageControllerCustomLoco { protected TDmg dmgController; protected TSim sim; diff --git a/DVCustomCarLoader/LocoComponents/DamageControllerCustomDiesel.cs b/DVCustomCarLoader/LocoComponents/DamageControllerCustomDiesel.cs index 5a8a23c4..675aa275 100644 --- a/DVCustomCarLoader/LocoComponents/DamageControllerCustomDiesel.cs +++ b/DVCustomCarLoader/LocoComponents/DamageControllerCustomDiesel.cs @@ -1,15 +1,188 @@ using System.Collections; using System.Collections.Generic; using System.Linq; +using CCL_GameScripts; +using DV.JObjectExtstensions; using DV.ServicePenalty; +using Newtonsoft.Json.Linq; using UnityEngine; namespace DVCustomCarLoader.LocoComponents { - public class DamageControllerCustomDiesel : DamageControllerCustomLoco + public class DamageControllerCustomDiesel : DamageControllerCustomLoco< + CustomLocoControllerDiesel, + CustomDieselSimEvents, + DamageConfigDiesel> { + protected const string ENGINE_HP_SAVE_KEY = "engineHP"; + [Header("Additional damage types")] public TrainDamage engine; + protected Coroutine randomShutoffCoroutine = null; + + protected virtual void OnEnable() + { + SetupListeners(true); + } + + protected virtual void OnDisable() + { + SetupListeners(false); + } + + protected virtual void SetupListeners( bool on ) + { + eventController.EngineRunningChanged.Manage(OnEngineRunningChanged, on); + } + + public override void RepairAll() + { + base.RepairAll(); + engine.RepairDamage(engine.fullHitPoints - engine.currentHitPoints); + } + + public override void IgnoreDamage( bool set ) + { + base.IgnoreDamage(set); + engine.IgnoreDamage(set); + } + + #region Damage Checks + + protected override void DamagesUpdate() + { + base.DamagesUpdate(); + + float delta = Time.deltaTime; + + // check cold start damage + if( (locoController.EngineTemp < config.ColdEngineTempThreshold) && + (locoController.EngineRPM > config.ColdEngineRPMThreshold) ) + { + ApplyDamage(engine, locoController.EngineRPM * config.ColdEngineDPS * delta); + } + + // running damage + if( locoController.EngineRunning ) + { + float curRPM = locoController.EngineRPM; + + // normal wear & tear + if( curRPM > 0.0001f ) + { + ApplyDamage(engine, curRPM * config.EngineRunningDPS * delta); + } + + // no oil + if( locoController.OilLevel == 0 ) + { + float adjRPM = Mathf.Lerp(0.3f, 1f, curRPM); + ApplyDamage(engine, adjRPM * config.EngineNoOilDPS * delta); + } + + // check for random shutoff + if( (engine.DamagePercentage >= config.EngineFailureThreshold) && (randomShutoffCoroutine == null) ) + { + randomShutoffCoroutine = StartCoroutine(RandomEngineShutoff()); + } + } + } + + protected IEnumerator RandomEngineShutoff() + { + WaitForSeconds waitTimeout = WaitFor.Seconds(config.RandomShutoffCheckPeriod); + + float threshold = config.EngineFailureThreshold; + while( locoController.EngineRunning && (engine.DamagePercentage > threshold) ) + { + if( Random.value <= (config.ShutoffChanceMultiplier * (engine.DamagePercentage - threshold)) ) + { + locoController.EngineRunning = false; + break; + } + yield return waitTimeout; + } + + randomShutoffCoroutine = null; + yield break; + } + + protected override void OnCollisionDamage( float colDamage, Vector3 forceDirection ) + { + base.OnCollisionDamage(colDamage, forceDirection); + + float engineDamage = bodyDamage.GetModifiedCollisionDamage(colDamage) * config.EngineCollisionMultiplier; + + if( engineDamage > 0 ) + { + ApplyDamage(engine, engineDamage); + + if( engineDamage > config.CollisionShutoffThreshold ) + { + locoController.EngineRunning = false; + } + } + } + + protected override void OnFireDamage( float timeInFire ) + { + base.OnFireDamage(timeInFire); + + float engineDamage = bodyDamage.GetModifiedFireDamage(timeInFire) * config.EngineFireMultiplier; + + if( engineDamage > 0 ) + { + ApplyDamage(engine, engineDamage); + } + } + + private void OnEngineRunningChanged( bool engineTurnedOn ) + { + if( engineTurnedOn ) + { + ApplyDamage(engine, config.EngineStartDamage); + } + } + + #endregion + + #region Data Overrides + + protected override void InitializeTrainDamages() + { + base.InitializeTrainDamages(); + + engine.fullHitPoints = config.EngineHitpoints; + if( engine.fullHitPoints == 0f ) + { + Main.Error("TrainDamage[engine].fullHitPoints is set to invalid value 0! Overriding to 1000"); + engine.fullHitPoints = 1000f; + } + engine.SetCurrentHitPoints(engine.fullHitPoints); + } + + public override JObject GetDamageSaveData() + { + JObject saveData = base.GetDamageSaveData(); + saveData.SetFloat(ENGINE_HP_SAVE_KEY, engine.currentHitPoints); + return saveData; + } + + public override void LoadDamagesState( JObject stateData ) + { + base.LoadDamagesState(stateData); + float? engineHp = stateData.GetFloat(ENGINE_HP_SAVE_KEY); + if( engineHp.HasValue ) + { + engine.SetCurrentHitPoints(engineHp.Value); + } + else + { + Main.Error("No load data for engineHP found!"); + } + } + + #endregion #region Debt Tracking diff --git a/DVCustomCarLoader/LocoComponents/DamageControllerCustomLoco.cs b/DVCustomCarLoader/LocoComponents/DamageControllerCustomLoco.cs index 6f644f0a..e0f18b42 100644 --- a/DVCustomCarLoader/LocoComponents/DamageControllerCustomLoco.cs +++ b/DVCustomCarLoader/LocoComponents/DamageControllerCustomLoco.cs @@ -1,5 +1,8 @@ using System.Collections.Generic; +using System.Reflection; +using CCL_GameScripts; using DV.ServicePenalty; +using HarmonyLib; namespace DVCustomCarLoader.LocoComponents { @@ -11,5 +14,70 @@ public abstract class DamageControllerCustomLoco : DamageController, IServicePen public abstract IEnumerable GetPitStopParameters(); public abstract float GetPitStopLevel( ResourceType type ); public abstract void ChangePitStopLevel( ResourceType type, float changeAmount ); + + + private static readonly FieldInfo carDamagePropsField = + AccessTools.Field(typeof(CarDamageModel), "carDamageProperties"); + + protected void SetBodyDamageProps( CarDamageProperties value ) + { + carDamagePropsField.SetValue(bodyDamage, value); + } + } + + public abstract class DamageControllerCustomLoco : DamageControllerCustomLoco + where TCtrl : CustomLocoController + where TEvents : CustomLocoSimEvents + where TConfig : DamageConfigBasic + { + protected TCtrl locoController; + protected TEvents eventController; + protected TConfig config; + + protected override void Awake() + { + controller = locoController = GetComponent(); + eventController = GetComponent(); + config = GetComponent(); + InitializeTrainDamages(); + } + + protected override void Start() + { + base.Start(); + ApplyBodyParameters(); + } + + protected override void InitializeTrainDamages() + { + wheels.fullHitPoints = config.WheelHitpoints; + if( wheels.fullHitPoints == 0f ) + { + Main.Error("TrainDamage[wheels].fullHitPoints is set to invalid value 0! Overriding to 1000"); + wheels.fullHitPoints = 1000f; + } + wheels.SetCurrentHitPoints(wheels.fullHitPoints); + } + + protected void ApplyBodyParameters() + { + if( bodyDamage ) + { + var props = new CarDamageProperties( + config.BodyHitpoints, + config.BodyCollisionResistance, + config.BodyCollisionMultiplier, + config.BodyFireResistance, + config.BodyFireMultiplier, + config.DamageTolerance + ); + + SetBodyDamageProps(props); + } + else + { + Main.Warning("Body damage properties not found!"); + } + } } } \ No newline at end of file From 81c51a75d47356a750b0f12138b6f7d6fea9e4a1 Mon Sep 17 00:00:00 2001 From: Katy Fox Date: Mon, 12 Jul 2021 14:12:11 -0400 Subject: [PATCH 06/20] get damage props to actually compile --- CCL_GameScripts/CCL_GameScripts.csproj | 3 +++ CCL_GameScripts/DamageConfigDiesel.cs | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CCL_GameScripts/CCL_GameScripts.csproj b/CCL_GameScripts/CCL_GameScripts.csproj index 8cbad3f2..cb9f880b 100644 --- a/CCL_GameScripts/CCL_GameScripts.csproj +++ b/CCL_GameScripts/CCL_GameScripts.csproj @@ -47,6 +47,9 @@ + + + diff --git a/CCL_GameScripts/DamageConfigDiesel.cs b/CCL_GameScripts/DamageConfigDiesel.cs index bda61f71..4587e6bb 100644 --- a/CCL_GameScripts/DamageConfigDiesel.cs +++ b/CCL_GameScripts/DamageConfigDiesel.cs @@ -73,7 +73,7 @@ public void ApplyShunterDefaults() WheelHitpoints = 1000f; // diesel - EngineHitpoints = 4000f; + EngineHitpoints = 1000f; ColdEngineDPS = 0.1f; ColdEngineRPMThreshold = 0.5f; ColdEngineTempThreshold = 45f; From bfa7db79e222bbd92a01137c5cb6677963652ab2 Mon Sep 17 00:00:00 2001 From: Katy Fox Date: Mon, 12 Jul 2021 22:57:33 -0400 Subject: [PATCH 07/20] Fix NRE errors at car spawn --- CCL_GameScripts/SimParamsBase.cs | 18 +++++++++++ CCL_GameScripts/SimParamsDiesel.cs | 24 +++++++++++++++ DVCustomCarLoader/AudioPatches.cs | 19 ++++++++++++ DVCustomCarLoader/DVCustomCarLoader.csproj | 1 + DVCustomCarLoader/LocoComponentManager.cs | 30 ++++++++++++++----- .../LocoComponents/CustomLocoSimDiesel.cs | 6 ++-- .../LocoComponents/CustomLocoSimulation.cs | 8 ++++- .../DamageControllerCustomDiesel.cs | 17 +++++++---- .../DamageControllerCustomLoco.cs | 24 +++++++++++---- 9 files changed, 125 insertions(+), 22 deletions(-) create mode 100644 DVCustomCarLoader/AudioPatches.cs diff --git a/CCL_GameScripts/SimParamsBase.cs b/CCL_GameScripts/SimParamsBase.cs index 00a3bbe8..c7cd964c 100644 --- a/CCL_GameScripts/SimParamsBase.cs +++ b/CCL_GameScripts/SimParamsBase.cs @@ -26,6 +26,14 @@ public abstract class SimParamsBase : MonoBehaviour public AnimationCurve BrakePowerCurve; public AnimationCurve TractionTorqueCurve; + [Header("Drivers")] + public bool PreventWheelslip = false; + public float FrictionCoefficient = 0.25f; + [Range(1f, 10f)] + public float SandCoefficient = 1.5f; + public float SlopeCoefficientMultiplier = 2f; + public AnimationCurve WheelslipToFrictionModifier; + public SimParamsBase() { // DE6 defaults @@ -48,6 +56,16 @@ public SimParamsBase() preWrapMode = WrapMode.ClampForever, postWrapMode = WrapMode.ClampForever }; + + WheelslipToFrictionModifier = + new AnimationCurve( + new Keyframe(0, 0.4f, 0, 0, 0.3333f, 0.3333f), + new Keyframe(0.802f, 0.271f, -0.516f, -0.516f, 0.105f, 0.267f), + new Keyframe(1, 0.005f, -0.008f, -0.008f, 0.3333f, 0.3333f)) + { + preWrapMode = WrapMode.ClampForever, + postWrapMode = WrapMode.ClampForever + }; } } } \ No newline at end of file diff --git a/CCL_GameScripts/SimParamsDiesel.cs b/CCL_GameScripts/SimParamsDiesel.cs index 7398da0d..0f931408 100644 --- a/CCL_GameScripts/SimParamsDiesel.cs +++ b/CCL_GameScripts/SimParamsDiesel.cs @@ -39,6 +39,10 @@ public class SimParamsDiesel : SimParamsBase public float OilConsumptionEngineRpm = 1; //public float OilConsumptionWheels = 0.12f; + public SimParamsDiesel() + { + ApplyDE6Defaults(); + } public void ApplyDE6Defaults() { @@ -80,6 +84,16 @@ public void ApplyDE6Defaults() preWrapMode = WrapMode.ClampForever, postWrapMode = WrapMode.ClampForever }; + + WheelslipToFrictionModifier = + new AnimationCurve( + new Keyframe(0, 0.4f, 0, 0, 0.3333f, 0.3333f), + new Keyframe(0.802f, 0.271f, -0.516f, -0.516f, 0.105f, 0.267f), + new Keyframe(1, 0.005f, -0.008f, -0.008f, 0.3333f, 0.3333f)) + { + preWrapMode = WrapMode.ClampForever, + postWrapMode = WrapMode.ClampForever + }; } public void ApplyShunterDefaults() @@ -121,6 +135,16 @@ public void ApplyShunterDefaults() preWrapMode = WrapMode.ClampForever, postWrapMode = WrapMode.ClampForever }; + + WheelslipToFrictionModifier = + new AnimationCurve( + new Keyframe(0, 0.4f, 0, 0, 0.3333f, 0.3333f), + new Keyframe(0.565f, 0.262f, -0.516f, -0.516f, 0.105f, 0.267f), + new Keyframe(1, 0.005f, -0.008f, -0.008f, 0.3333f, 0.3333f)) + { + preWrapMode = WrapMode.ClampForever, + postWrapMode = WrapMode.ClampForever + }; } } } \ No newline at end of file diff --git a/DVCustomCarLoader/AudioPatches.cs b/DVCustomCarLoader/AudioPatches.cs new file mode 100644 index 00000000..2dc2f0a8 --- /dev/null +++ b/DVCustomCarLoader/AudioPatches.cs @@ -0,0 +1,19 @@ +using System; +using CCL_GameScripts; +using DVCustomCarLoader.LocoComponents; +using HarmonyLib; + +namespace DVCustomCarLoader +{ + [HarmonyPatch(typeof(TrainCar), "InitAudio")] + public static class TrainCar_InitAudioPatch + { + [HarmonyPriority(Priority.First)] + static bool Prefix( TrainCar __instance ) + { + var simParams = __instance.gameObject.GetComponent(); + if( simParams ) return false; + return true; + } + } +} diff --git a/DVCustomCarLoader/DVCustomCarLoader.csproj b/DVCustomCarLoader/DVCustomCarLoader.csproj index 8b03226b..a1769d0d 100644 --- a/DVCustomCarLoader/DVCustomCarLoader.csproj +++ b/DVCustomCarLoader/DVCustomCarLoader.csproj @@ -72,6 +72,7 @@ + diff --git a/DVCustomCarLoader/LocoComponentManager.cs b/DVCustomCarLoader/LocoComponentManager.cs index 32fe1ef8..fd98596c 100644 --- a/DVCustomCarLoader/LocoComponentManager.cs +++ b/DVCustomCarLoader/LocoComponentManager.cs @@ -15,7 +15,7 @@ public static void AddLocoSimulation( GameObject prefab, SimParamsBase simParams switch( simParams.SimType ) { case LocoParamsType.DieselElectric: - AddDieselSimulation(prefab); + AddDieselSimulation(prefab, (SimParamsDiesel)simParams); break; default: @@ -30,19 +30,35 @@ public static void AddLocoSimulation( GameObject prefab, SimParamsBase simParams // - MultipleUnitModule // - LocoController - public static void AddDieselSimulation( GameObject prefab ) + public static void AddDieselSimulation( GameObject prefab, SimParamsDiesel simParams ) { - GameObject basePrefab = CarTypes.GetCarPrefab(TrainCarType.LocoDiesel); + var dmgConfig = prefab.GetComponent(); + if( !dmgConfig ) + { + Main.Error($"Loco prefab {prefab.name} is missing diesel damage config, skipping sim setup"); + return; + } + + var drivingForce = prefab.AddComponent(); + ApplyDrivingForceParams(drivingForce, simParams); prefab.AddComponent(); prefab.AddComponent(); prefab.AddComponent(); //prefab.AddComponent(); + var locoController = prefab.AddComponent(); + locoController.drivingForce = drivingForce; - var controller = prefab.AddComponent(); - var baseController = basePrefab.GetComponent(); - controller.tractionTorqueCurve = baseController.tractionTorqueCurve; - controller.brakePowerCurve = baseController.brakePowerCurve; + Main.Log($"Added diesel electric simulation to {prefab.name}"); + } + + private static void ApplyDrivingForceParams( DrivingForce driver, SimParamsBase simParams ) + { + driver.frictionCoeficient = simParams.FrictionCoefficient; + driver.preventWheelslip = simParams.PreventWheelslip; + driver.sandCoefMax = simParams.SandCoefficient; + driver.slopeCoeficientMultiplier = simParams.SlopeCoefficientMultiplier; + driver.wheelslipToFrictionModifierCurve = simParams.WheelslipToFrictionModifier; } } } diff --git a/DVCustomCarLoader/LocoComponents/CustomLocoSimDiesel.cs b/DVCustomCarLoader/LocoComponents/CustomLocoSimDiesel.cs index a77e4f6d..21319a11 100644 --- a/DVCustomCarLoader/LocoComponents/CustomLocoSimDiesel.cs +++ b/DVCustomCarLoader/LocoComponents/CustomLocoSimDiesel.cs @@ -11,12 +11,13 @@ namespace DVCustomCarLoader.LocoComponents { - public class CustomLocoSimDiesel : CustomLocoSimulation, IServicePenaltyProvider + public class CustomLocoSimDiesel : + CustomLocoSimulation { public float TotalFuelConsumed { get; private set; } //public SimParamsDiesel simParams; - private DamageControllerDiesel dmgController; + //private DamageControllerCustomDiesel dmgController; // Sanders public bool sandOn; @@ -56,7 +57,6 @@ public override JObject GetComponentsSaveData() protected override void InitComponents() { base.InitComponents(); - dmgController = GetComponent(); sand = new SimComponent("Sand", 0f, simParams.SandCapacity, 40f, simParams.SandCapacity); speed = new SimComponent("Speed", 0f, simParams.MaxSpeed, 1f, 0f); diff --git a/DVCustomCarLoader/LocoComponents/CustomLocoSimulation.cs b/DVCustomCarLoader/LocoComponents/CustomLocoSimulation.cs index ec642580..d8322b15 100644 --- a/DVCustomCarLoader/LocoComponents/CustomLocoSimulation.cs +++ b/DVCustomCarLoader/LocoComponents/CustomLocoSimulation.cs @@ -16,14 +16,20 @@ public virtual void ResetFuelConsumption() { } public abstract float GetPitStopLevel( ResourceType type ); } - public abstract class CustomLocoSimulation : CustomLocoSimulation + public abstract class CustomLocoSimulation : CustomLocoSimulation where TParams : SimParamsBase + where TDmg : DamageControllerCustomLoco { public TParams simParams; + public TDmg dmgController; protected override void InitComponents() { simParams = GetComponent(); + if( !simParams ) Main.Error($"Missing {typeof(TParams).Name} on {gameObject.name}"); + + dmgController = GetComponent(); + if( !dmgController ) Main.Error($"Missing DamageControllerDiesel on {gameObject.name}"); } } } \ No newline at end of file diff --git a/DVCustomCarLoader/LocoComponents/DamageControllerCustomDiesel.cs b/DVCustomCarLoader/LocoComponents/DamageControllerCustomDiesel.cs index 675aa275..a732efa9 100644 --- a/DVCustomCarLoader/LocoComponents/DamageControllerCustomDiesel.cs +++ b/DVCustomCarLoader/LocoComponents/DamageControllerCustomDiesel.cs @@ -152,13 +152,20 @@ protected override void InitializeTrainDamages() { base.InitializeTrainDamages(); - engine.fullHitPoints = config.EngineHitpoints; - if( engine.fullHitPoints == 0f ) + if( engine == null ) { - Main.Error("TrainDamage[engine].fullHitPoints is set to invalid value 0! Overriding to 1000"); - engine.fullHitPoints = 1000f; + engine = new TrainDamage(config.EngineHitpoints); + } + else + { + engine.fullHitPoints = config.EngineHitpoints; + if( engine.fullHitPoints == 0f ) + { + Main.Error("TrainDamage[engine].fullHitPoints is set to invalid value 0! Overriding to 1000"); + engine.fullHitPoints = 1000f; + } + engine.SetCurrentHitPoints(engine.fullHitPoints); } - engine.SetCurrentHitPoints(engine.fullHitPoints); } public override JObject GetDamageSaveData() diff --git a/DVCustomCarLoader/LocoComponents/DamageControllerCustomLoco.cs b/DVCustomCarLoader/LocoComponents/DamageControllerCustomLoco.cs index e0f18b42..7717e9a7 100644 --- a/DVCustomCarLoader/LocoComponents/DamageControllerCustomLoco.cs +++ b/DVCustomCarLoader/LocoComponents/DamageControllerCustomLoco.cs @@ -15,7 +15,6 @@ public abstract class DamageControllerCustomLoco : DamageController, IServicePen public abstract float GetPitStopLevel( ResourceType type ); public abstract void ChangePitStopLevel( ResourceType type, float changeAmount ); - private static readonly FieldInfo carDamagePropsField = AccessTools.Field(typeof(CarDamageModel), "carDamageProperties"); @@ -37,8 +36,14 @@ public abstract class DamageControllerCustomLoco : Damage protected override void Awake() { controller = locoController = GetComponent(); + if( !locoController ) Main.Error($"Missing {typeof(TCtrl).Name} on {gameObject.name}"); + eventController = GetComponent(); + if( !eventController ) Main.Error($"Missing {typeof(TEvents).Name} on {gameObject.name}"); + config = GetComponent(); + if( !config ) Main.Error($"Missing {typeof(TConfig).Name} on {gameObject.name}"); + InitializeTrainDamages(); } @@ -50,13 +55,20 @@ protected override void Start() protected override void InitializeTrainDamages() { - wheels.fullHitPoints = config.WheelHitpoints; - if( wheels.fullHitPoints == 0f ) + if( wheels == null ) + { + wheels = new TrainDamage(config.WheelHitpoints); + } + else { - Main.Error("TrainDamage[wheels].fullHitPoints is set to invalid value 0! Overriding to 1000"); - wheels.fullHitPoints = 1000f; + wheels.fullHitPoints = config.WheelHitpoints; + if( wheels.fullHitPoints == 0f ) + { + Main.Error("TrainDamage[wheels].fullHitPoints is set to invalid value 0! Overriding to 1000"); + wheels.fullHitPoints = 1000f; + } + wheels.SetCurrentHitPoints(wheels.fullHitPoints); } - wheels.SetCurrentHitPoints(wheels.fullHitPoints); } protected void ApplyBodyParameters() From 3491b61ae364749b7757e3a81d0e715c81fe327c Mon Sep 17 00:00:00 2001 From: Katy Fox Date: Tue, 13 Jul 2021 09:11:23 -0400 Subject: [PATCH 08/20] Move more scripts into CCL_GameScripts --- .../Editor/MethodButtonAttributeDrawer.cs | 67 ++ .../Attributes/Editor/ReadOnlyDrawer.cs | 26 + .../Attributes/MethodButtonAttribute.cs | 25 + .../Attributes/ReadOnlyAttribute.cs | 6 + CCL_GameScripts/BogieSetup.cs | 35 ++ CCL_GameScripts/CCL_GameScripts.csproj | 22 +- CCL_GameScripts/Editor/AddLocoParams.cs | 100 +++ CCL_GameScripts/Editor/CreateAssetBundles.cs | 1 + CCL_GameScripts/Editor/DVSTUDIOExtensions.cs | 23 + .../{ => Editor}/DamageConfigEditor.cs | 0 CCL_GameScripts/Editor/ExportTrainCar.cs | 570 ++++++++++++++++++ CCL_GameScripts/Editor/FBXMeshExtractor.cs | 74 +++ CCL_GameScripts/Editor/FindMissingScripts.cs | 81 +++ .../{ => Editor}/SimParamsEditor.cs | 0 .../JSONObject/JSONObject.cs | 32 + CCL_GameScripts/JSONObject/LICENSE.md | 19 + .../JSONObject/VectorTemplates.cs | 0 CCL_GameScripts/JSONObject/readme.md | 192 ++++++ CCL_GameScripts/TrainCarSetup.cs | 131 ++++ DVCustomCarLoader/AudioPatches.cs | 19 - DVCustomCarLoader/DVCustomCarLoader.csproj | 4 +- DVCustomCarLoader/ModPatches.cs | 42 ++ 22 files changed, 1444 insertions(+), 25 deletions(-) create mode 100644 CCL_GameScripts/Attributes/Editor/MethodButtonAttributeDrawer.cs create mode 100644 CCL_GameScripts/Attributes/Editor/ReadOnlyDrawer.cs create mode 100644 CCL_GameScripts/Attributes/MethodButtonAttribute.cs create mode 100644 CCL_GameScripts/Attributes/ReadOnlyAttribute.cs create mode 100644 CCL_GameScripts/BogieSetup.cs create mode 100644 CCL_GameScripts/Editor/AddLocoParams.cs create mode 100644 CCL_GameScripts/Editor/CreateAssetBundles.cs create mode 100644 CCL_GameScripts/Editor/DVSTUDIOExtensions.cs rename CCL_GameScripts/{ => Editor}/DamageConfigEditor.cs (100%) create mode 100644 CCL_GameScripts/Editor/ExportTrainCar.cs create mode 100644 CCL_GameScripts/Editor/FBXMeshExtractor.cs create mode 100644 CCL_GameScripts/Editor/FindMissingScripts.cs rename CCL_GameScripts/{ => Editor}/SimParamsEditor.cs (100%) rename {DVCustomCarLoader => CCL_GameScripts}/JSONObject/JSONObject.cs (97%) create mode 100644 CCL_GameScripts/JSONObject/LICENSE.md rename {DVCustomCarLoader => CCL_GameScripts}/JSONObject/VectorTemplates.cs (100%) create mode 100644 CCL_GameScripts/JSONObject/readme.md create mode 100644 CCL_GameScripts/TrainCarSetup.cs delete mode 100644 DVCustomCarLoader/AudioPatches.cs create mode 100644 DVCustomCarLoader/ModPatches.cs diff --git a/CCL_GameScripts/Attributes/Editor/MethodButtonAttributeDrawer.cs b/CCL_GameScripts/Attributes/Editor/MethodButtonAttributeDrawer.cs new file mode 100644 index 00000000..7ed51086 --- /dev/null +++ b/CCL_GameScripts/Attributes/Editor/MethodButtonAttributeDrawer.cs @@ -0,0 +1,67 @@ +using UnityEngine; +using UnityEditor; +using System.Reflection; + +[CustomPropertyDrawer(typeof(MethodButtonAttribute))] +public class MethodButtonAttributeDrawer : PropertyDrawer +{ + private int buttonCount; + private readonly float buttonHeight = EditorGUIUtility.singleLineHeight * 2; + private MethodButtonAttribute attr; + + public override void OnGUI(Rect position, SerializedProperty editorFoldout, GUIContent label) + { + if (editorFoldout.name.Equals("editorFoldout") == false) + { + LogErrorMessage(editorFoldout); + return; + } + + buttonCount = 0; + + Rect foldoutRect = new Rect(position.x, position.y, position.width, 5 + buttonHeight); + + editorFoldout.boolValue = EditorGUI.Foldout(foldoutRect, editorFoldout.boolValue, "Buttons", true); + + if (editorFoldout.boolValue) + { + buttonCount++; + + attr = (MethodButtonAttribute)base.attribute; + + foreach (var name in attr.MethodNames) + { + buttonCount++; + + Rect buttonRect = new Rect(position.x, position.y + ((1 + buttonHeight) * (buttonCount - 1)), position.width, buttonHeight - 1); + if (GUI.Button(buttonRect, name)) + { + InvokeMethod(editorFoldout, name); + } + } + } + } + + public override float GetPropertyHeight(SerializedProperty property, GUIContent label) + { + return EditorGUI.GetPropertyHeight(property, label, true) + (buttonHeight) * (buttonCount); + } + + private void InvokeMethod(SerializedProperty property, string name) + { + Object target = property.serializedObject.targetObject; + target.GetType().GetMethod(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly).Invoke(target, null); + } + + private void LogErrorMessage(SerializedProperty editorFoldout) + { + Debug.LogError("Possible improper usage of method button attribute!"); +#if NET_4_6 + Debug.LogError($"Got field name: {editorFoldout.name}, Expected: editorFoldout"); + Debug.LogError($"Please see {"Usage"} at {"https://github.com/GlassToeStudio/UnityMethodButtonAttribute/blob/master/README.md"}"); +#else + Debug.LogError(string.Format("Got field name: {0}, Expected: editorFoldout", editorFoldout.name)); + Debug.LogError("Please see \"Usage\" at \"https://github.com/GlassToeStudio/UnityMethodButtonAttribute/blob/master/README.md \""); +#endif + } +} diff --git a/CCL_GameScripts/Attributes/Editor/ReadOnlyDrawer.cs b/CCL_GameScripts/Attributes/Editor/ReadOnlyDrawer.cs new file mode 100644 index 00000000..737ea909 --- /dev/null +++ b/CCL_GameScripts/Attributes/Editor/ReadOnlyDrawer.cs @@ -0,0 +1,26 @@ +using UnityEngine; +using UnityEditor; +/// +/// This class contain custom drawer for ReadOnly attribute. +/// +[CustomPropertyDrawer(typeof(ReadOnlyAttribute))] +public class ReadOnlyDrawer : PropertyDrawer +{ + /// + /// Unity method for drawing GUI in Editor + /// + /// Position. + /// Property. + /// Label. + public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) + { + // Saving previous GUI enabled value + var previousGUIState = GUI.enabled; + // Disabling edit for property + GUI.enabled = false; + // Drawing Property + EditorGUI.PropertyField(position, property, label); + // Setting old GUI enabled value + GUI.enabled = previousGUIState; + } +} \ No newline at end of file diff --git a/CCL_GameScripts/Attributes/MethodButtonAttribute.cs b/CCL_GameScripts/Attributes/MethodButtonAttribute.cs new file mode 100644 index 00000000..2366d630 --- /dev/null +++ b/CCL_GameScripts/Attributes/MethodButtonAttribute.cs @@ -0,0 +1,25 @@ +using UnityEngine; + +/// +/// Usage: +/// #if UNITY_EDITOR +/// [MethodButton("MethodName1", "MethodName2", "MethodNameN")] // Must match a the method name! +/// [SerializeField] private bool showButtons; // this bool is mandatory! +/// #endif +/// +public class MethodButtonAttribute : PropertyAttribute +{ + /// + /// Array of different method names for which a button will be displayed. + /// "MethodOne", MethodTwo" ... "MethodN" + /// + public string[] MethodNames { get; private set; } + + /// + /// Default constructor. + /// + public MethodButtonAttribute(params string[] args) + { + MethodNames = args; + } +} \ No newline at end of file diff --git a/CCL_GameScripts/Attributes/ReadOnlyAttribute.cs b/CCL_GameScripts/Attributes/ReadOnlyAttribute.cs new file mode 100644 index 00000000..5f37abdf --- /dev/null +++ b/CCL_GameScripts/Attributes/ReadOnlyAttribute.cs @@ -0,0 +1,6 @@ +using UnityEngine; +/// +/// Read Only attribute. +/// Attribute is use only to mark ReadOnly properties. +/// +public class ReadOnlyAttribute : PropertyAttribute { } \ No newline at end of file diff --git a/CCL_GameScripts/BogieSetup.cs b/CCL_GameScripts/BogieSetup.cs new file mode 100644 index 00000000..899293cc --- /dev/null +++ b/CCL_GameScripts/BogieSetup.cs @@ -0,0 +1,35 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +namespace CCL_GameScripts +{ + public class BogieSetup : MonoBehaviour + { + [Header("Axles")] + [Tooltip("Number of axles, used for calculating joint sounds")] + [Range(0, 10)] + public int AxleCount = 2; + + [Tooltip("Distance between 2 consecutive axles on a bogie")] + [Range(0, 10)] + public float AxleSeparation = 1.5f; + + [Header("Physics")] + [Range(0, 100000)] + public float BrakingForcePerBar = 10000f; + + [Range(0, 0.02f)] + public float RollingResistanceCoefficient = 0.004f; + + public JSONObject GetJSON() + { + var repr = new JSONObject(); + repr.AddField("axleCount", AxleCount); + repr.AddField("axleSeparation", AxleSeparation); + repr.AddField("brakingForcePerBar", BrakingForcePerBar); + repr.AddField("rollingResistance", RollingResistanceCoefficient); + return repr; + } + } +} \ No newline at end of file diff --git a/CCL_GameScripts/CCL_GameScripts.csproj b/CCL_GameScripts/CCL_GameScripts.csproj index cb9f880b..769c049c 100644 --- a/CCL_GameScripts/CCL_GameScripts.csproj +++ b/CCL_GameScripts/CCL_GameScripts.csproj @@ -18,7 +18,7 @@ full false bin\Debug\ - DEBUG;TRACE + TRACE;DEBUG;UNITY_EDITOR prompt 4 @@ -41,19 +41,35 @@ + + + + + + + - + + + + + + + + + - + + \ No newline at end of file diff --git a/CCL_GameScripts/Editor/AddLocoParams.cs b/CCL_GameScripts/Editor/AddLocoParams.cs new file mode 100644 index 00000000..4f80efd8 --- /dev/null +++ b/CCL_GameScripts/Editor/AddLocoParams.cs @@ -0,0 +1,100 @@ +using System.Collections; +using System.Collections.Generic; +using CCL_GameScripts; +using UnityEditor; +using UnityEngine; + +public class AddLocoParams : EditorWindow +{ + #region Static + + private static AddLocoParams window = null; + + [InitializeOnLoadMethod] + static void Init() + { + TrainCarSetup.LaunchLocoSetupWindow = ShowWindow; + } + + static void ShowWindow( TrainCarSetup trainCarSetup ) + { + window = GetWindow(); + window.trainScript = trainCarSetup; + } + + #endregion + + #region Instance + + private TrainCarSetup trainScript; + private LocoSimTemplate LocoType; + + private void ResetWindow() + { + LocoType = LocoSimTemplate.Shunter; + } + + private void OnGUI() + { + GUILayout.BeginVertical(); + LocoType = (LocoSimTemplate)EditorGUILayout.EnumPopup("Template Loco type:", LocoType); + + GUILayout.Space(20); + GUILayout.BeginHorizontal(); + + if( GUILayout.Button("Cancel") ) + { + Close(); + return; + } + + if( GUILayout.Button("Apply Template") ) + { + ApplyTemplate(); + Close(); + return; + } + + GUILayout.EndHorizontal(); + + GUILayout.EndVertical(); + } + + private void ApplyTemplate() + { + var obj = trainScript.gameObject; + + if( (LocoType == LocoSimTemplate.DE6) || (LocoType == LocoSimTemplate.Shunter) ) + { + SimParamsDiesel simParams = obj.GetComponent(); + if( !simParams ) + { + simParams = obj.AddComponent(); + } + + DamageConfigDiesel dmgConfig = obj.GetComponent(); + if( !dmgConfig ) + { + dmgConfig = obj.AddComponent(); + } + + if( LocoType == LocoSimTemplate.DE6 ) + { + simParams.ApplyDE6Defaults(); + dmgConfig.ApplyDE6Defaults(); + } + else + { + simParams.ApplyShunterDefaults(); + dmgConfig.ApplyShunterDefaults(); + } + } + } + + #endregion +} + +public enum LocoSimTemplate +{ + Shunter, DE6, SH282 +} diff --git a/CCL_GameScripts/Editor/CreateAssetBundles.cs b/CCL_GameScripts/Editor/CreateAssetBundles.cs new file mode 100644 index 00000000..5f282702 --- /dev/null +++ b/CCL_GameScripts/Editor/CreateAssetBundles.cs @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/CCL_GameScripts/Editor/DVSTUDIOExtensions.cs b/CCL_GameScripts/Editor/DVSTUDIOExtensions.cs new file mode 100644 index 00000000..4cce4e41 --- /dev/null +++ b/CCL_GameScripts/Editor/DVSTUDIOExtensions.cs @@ -0,0 +1,23 @@ + + public static class DVSTUDIOExtensions + { + /// + /// Clears out an entire folder. BE CAREFUL WHEN USING. + /// + /// + public static void Empty(this System.IO.DirectoryInfo directory) + { + foreach(System.IO.FileInfo file in directory.GetFiles()) file.Delete(); + foreach(System.IO.DirectoryInfo subDirectory in directory.GetDirectories()) subDirectory.Delete(true); + } + + /// + /// Returns a full path with filename without an extension. + /// + /// + /// + public static string GetFullPathWithoutExtension(string path) + { + return System.IO.Path.Combine(System.IO.Path.GetDirectoryName(path), System.IO.Path.GetFileNameWithoutExtension(path)); + } + } \ No newline at end of file diff --git a/CCL_GameScripts/DamageConfigEditor.cs b/CCL_GameScripts/Editor/DamageConfigEditor.cs similarity index 100% rename from CCL_GameScripts/DamageConfigEditor.cs rename to CCL_GameScripts/Editor/DamageConfigEditor.cs diff --git a/CCL_GameScripts/Editor/ExportTrainCar.cs b/CCL_GameScripts/Editor/ExportTrainCar.cs new file mode 100644 index 00000000..f1a3a1b2 --- /dev/null +++ b/CCL_GameScripts/Editor/ExportTrainCar.cs @@ -0,0 +1,570 @@ +using System.Collections.Generic; +using System.IO; +using System.Linq; +using UnityEditor; +using UnityEngine; +using UnityEditor.Build; +using UnityEditor.Build.Reporting; +using CCL_GameScripts; + +/// +/// This window will export a train car for use in Derail Valley. +/// It writes a JSON file with the accompanied assetbundle. +/// +public class ExportTrainCar : EditorWindow +{ + + #region Internal Data + + private static ExportTrainCar window = null; + + private GUIStyle _boxStyle; + private GUIStyle BoxStyle => _boxStyle ?? GUI.skin.box; + + private TrainCarSetup _trainCarSetup; + + #endregion + + private enum state + { + Main, + Settings, + Export + } + + /// + /// The current state of the menu. + /// + private state State = state.Main; + + #region Exported Train Car Data + + private bool loadedCar = false; + + /// + /// The identifier for this car. + /// + private string identifier = "Custom Car"; + + /// + /// The underlying type of this car. + /// + private BaseTrainCarType TrainCarType = BaseTrainCarType.FlatbedEmpty; + + #region Positions + + ////Couplers + //private Vector3 FrontCouplerPosition; + //private Vector3 RearCouplerPosition; + + ////Chains + //private Vector3 FrontChainPosition; + //private Vector3 RearChainPosition; + + ////Hoses + //private Vector3 FrontHosePosition; + //private Vector3 RearHosePosition; + + ////Buffers + //private Vector3 FrontBufferPosition; + //private Vector3 RearBufferPosition; + + ////Bogies + //private Vector3 FrontBogiePosition; + //private Vector3 RearBogiePosition; + + ////Name plates + //private Vector3 SidePlate1Position; + //private Vector3 SidePlate2Position; + + #endregion + + #endregion + + [InitializeOnLoadMethod] + static void Init() + { + TrainCarSetup.LaunchExportWindow = ShowWindow; + } + + static void ShowWindow(TrainCarSetup trainCarSetup) + { + // Get existing open window or if none, make a new one: + window = GetWindow(); + + #region Set Internal Data + + if (window._boxStyle == null) + { + window._boxStyle = new GUIStyle {normal = {textColor = Color.white}}; + } + + //Reset window when we open it (just in case) + window.ResetWindow(); + + //Set train car + window._trainCarSetup = trainCarSetup; + + #endregion + + window.Show(); + } + + void OnGUI() + { + switch (State) + { + #region State.Main + + case state.Main: + GUILayout.BeginVertical("box"); + GUILayout.Box("Train Car Tools", BoxStyle); + GUILayout.BeginHorizontal("box"); + + if (_trainCarSetup == null) + { + GUI.enabled = false; + EditorGUILayout.LabelField( + "This button is disabled because a TrainCarSetup script couldn't be found!"); + } + + if (GUILayout.Button("Prepare TrainCar for Export")) + { + State = state.Settings; + } + + GUI.enabled = true; + + GUILayout.BeginVertical(); + + EditorStyles.label.wordWrap = true; + EditorGUILayout.LabelField( + "This tool will prepare a TrainCar for export. You'll be able to choose various settings to setup your car before exporting."); + + GUILayout.EndVertical(); + GUILayout.EndHorizontal(); + GUILayout.EndVertical(); + break; + + #endregion + + #region State.Settings + + case state.Settings: + + GUILayout.BeginVertical("box"); + GUILayout.Box("Prepare TrainCar for Export", BoxStyle); + + EditorStyles.label.wordWrap = true; + EditorGUILayout.LabelField( + "Select your TrainCar prefab. It will be shown in the list below. ONLY ONE TRAIN CAR IS SUPPORTED AT THIS TIME. Selecting more than one prefab will cause unwanted results."); + + EditorGUILayout.LabelField("", GUI.skin.horizontalSlider); + + GUILayout.BeginVertical(); + + identifier = EditorGUILayout.TextField("Identifier:", identifier); + + TrainCarType = (BaseTrainCarType) EditorGUILayout.EnumPopup("Type of Car:", TrainCarType); + + #region Positions + + //EditorStyles.label.wordWrap = true; + // EditorGUILayout.LabelField( + // "The values below are not editable because they are retrieved directly from the TrainCar script. They are only shown to you for evaluation." + + // "If you believe these values are not correct, you should check to make sure everything is correctly in position."); + + // GUILayout.Space(30); + + // GUI.enabled = false; + + // //Couplers + // FrontCouplerPosition = EditorGUILayout.Vector3Field("Front Coupler Position:", FrontCouplerPosition); + // RearCouplerPosition = EditorGUILayout.Vector3Field("Rear Coupler Position:", RearCouplerPosition); + + // //Chains + // FrontChainPosition = EditorGUILayout.Vector3Field("Front Chain Position:", FrontChainPosition); + // RearChainPosition = EditorGUILayout.Vector3Field("Rear Chain Position:", RearChainPosition); + + // FrontHosePosition = EditorGUILayout.Vector3Field("Front Hose Position:", FrontHosePosition); + // RearHosePosition = EditorGUILayout.Vector3Field("Rear Hose Position:", RearHosePosition); + + // //Buffers + // FrontBufferPosition = EditorGUILayout.Vector3Field("Front Buffer Position:", FrontBufferPosition); + // RearBufferPosition = EditorGUILayout.Vector3Field("Rear Buffer Position:", RearBufferPosition); + + // //Bogies + // FrontBogiePosition = EditorGUILayout.Vector3Field("Front Bogie Position:", FrontBogiePosition); + // RearBogiePosition = EditorGUILayout.Vector3Field("Rear Bogie Position:", RearBogiePosition); + + #endregion + + EditorGUILayout.LabelField("", GUI.skin.horizontalSlider); + + if (_trainCarSetup != null) + { + EditorGUILayout.ObjectField("Selected TrainCar: ", _trainCarSetup.gameObject, typeof(GameObject), + false); + } + else + { + EditorStyles.label.wordWrap = true; + EditorGUILayout.LabelField("No TrainCar found!."); + } + + GUI.enabled = true; + + var lastColor = GUI.backgroundColor; + GUI.backgroundColor = Color.green; + + GUI.enabled = _trainCarSetup != null; + if (GUILayout.Button("Finalize TrainCar settings.")) + { + State = state.Export; + } + + GUI.enabled = true; + + GUI.backgroundColor = lastColor; + + GUILayout.EndVertical(); + + GUILayout.EndVertical(); + break; + + #endregion + + #region State.Export + + case state.Export: + GUILayout.BeginVertical("box"); // +1 + GUILayout.Box("Export Train Car", BoxStyle); + GUILayout.BeginHorizontal("box"); // +2 + + if (GUILayout.Button("Export Train Car")) + { + + //Extra check for null. + if (_trainCarSetup == null) + { + EditorUtility.DisplayDialog("ERROR", "TrainCarSetup script is null!", "Ok"); + return; + } + + if (EditorUtility.DisplayDialog("Confirmation", + $"You are about to export your TrainCar named {identifier}, are you sure you want to proceed?", + "Yes", "No")) + { + + //Basically we store two paths that can potentially have the game existing in it. + var cPath = Path.Combine("C:/Program Files/Steam/steamapps/common/Derail Valley/Mods/DVCustomCarLoader/Cars"); + var xPath = Path.Combine("X:/Program Files/Steam/steamapps/common/Derail Valley/Mods/DVCustomCarLoader/Cars"); + var ExistAtC = Directory.Exists(cPath); + var ExistAtX = Directory.Exists(xPath); + + //We check both paths to see if they exist. If not, we just open at the users desktop. + var assetBundleFullpath = EditorUtility.SaveFolderPanel( + "Export Car", + ExistAtC ? cPath : (ExistAtX ? xPath : System.Environment.GetFolderPath(System.Environment.SpecialFolder.Desktop)), + identifier); + + if (assetBundleFullpath.Length != 0) + { + + DirectoryInfo directory = new DirectoryInfo(assetBundleFullpath); + + //Prompt to clear folder before exporting. + if (directory.GetFiles().Length > 0 || directory.GetDirectories().Length>0) + { + if (EditorUtility.DisplayDialog("Clear Folder", + "The directory you selected isn't empty, would you like to clear the folder before proceeding? \n \n WARNING: THIS WILL DELETE EVERYTHING IN THE FOLDER.", + "Clear Folder", + "Cancel")) + { + //DANGEROUS METHOD, DONT USE WITHOUT CONFIDENCE + directory.Empty(); + } + } + + var carFullPath = Path.Combine(assetBundleFullpath, "car.json"); + + Debug.Log($"Exporting assetBundle to: {assetBundleFullpath}"); + + #region Build Asset Bundles + + //Build assetBundle. + //[OLD METHOD] Not used because it builds ALL asset bundles in the project. + //BuildPipeline.BuildAssetBundles (assetBundleDirectory, BuildAssetBundleOptions.None, BuildTarget.StandaloneWindows64); + + //Correct + var trainCarBuild = _trainCarSetup.gameObject; + var trainCarOriginalObject = PrefabUtility.GetCorrespondingObjectFromSource(trainCarBuild); + var trainCarAssetBundleName = AssetImporter + .GetAtPath(AssetDatabase.GetAssetPath(trainCarOriginalObject)).assetBundleName; + var processedBundles = new HashSet(); + var trainCarBundleBuild = AssetBundleBuildHelper + .GetBuildsForPaths(processedBundles, trainCarOriginalObject).ToArray(); + + if (trainCarOriginalObject == null) + { + Debug.LogError("You must make your TrainCar a prefab before attempting to export it!"); + return; + } + + if (trainCarBundleBuild.Length <= 0) + { + Debug.LogError("Failed to create build for AssetBundle."); + } + + BuildPipeline.BuildAssetBundles(assetBundleFullpath, trainCarBundleBuild, + BuildAssetBundleOptions.None, BuildTarget.StandaloneWindows64); + + Debug.Log($"Finished AssetBundle build for car: {identifier}."); + + #endregion + + #region Create car.json file. + + ExportCarSettings(trainCarAssetBundleName, carFullPath); + + #endregion + + //Goto folder when finished building. + if(EditorUtility.DisplayDialog("Finished Build", $"Finished building car {identifier} to path ({assetBundleFullpath}). Would you like to open the build folder?", "Yes", "No")) + { + EditorUtility.RevealInFinder(assetBundleFullpath); + } + + //Close the window when we are done building. + //Disable because people might potentially want to export again due to an error or something. + //Close(); + + } + } + else + { + return; + } + + } + + GUILayout.BeginVertical(); // +3 + + EditorStyles.label.wordWrap = true; + EditorGUILayout.LabelField( + "This button will open a window that allows you to select a folder to export your car. " + + "If an assetBundle already exists with the name you type, it will be written over." + + "If a car's settings file already exists, it will be written over." + + "Be sure to name your assetBundle appropriately as special characters/symbols can cause problems."); + + GUILayout.EndVertical(); // -3 + GUILayout.EndHorizontal(); // -2 + + GUILayout.BeginVertical("box"); // +2 + //GUILayout.BeginHorizontal(); + + EditorStyles.label.wordWrap = true; + EditorGUILayout.LabelField( + //How to export + "[HOW TO EXPORT] \n \n" + + "-The tool will attempt to find your Derail Valley installation. If the tool can't find your install path, navigate to it manually. \n" + + "-Once you are in the installation path, navigate to Mods/DVCustomCarLoader/Cars \n" + + "-When you are in the Cars folder, create a new folder for your new car, name doesn't matter. (Example: UP Autorack Yellow) \n" + + "-After creating that new folder, make sure to select it and click 'Save'. \n \n" + + + //How to avoid errors + "[HOW TO AVOID ERRORS] \n \n" + + "-Do not type any special characters in your folder to avoid Windows path problems. \n" + + "-Do not overwrite files if you don't want something potentially going wrong. \n" + + "-If any warnings or errors pop up within Unity, do not continue saving your file."); + + //GUILayout.EndHorizontal(); + GUILayout.EndVertical(); // -2 + + //GUILayout.EndVertical(); // -1 + break; + + #endregion + } + + + if (State != state.Main) + { + EditorGUILayout.LabelField("", GUI.skin.horizontalSlider); + + switch (State) + { + case state.Main: + if (GUILayout.Button("Close")) + { + ResetWindow(); + Close(); + } + break; + + case state.Settings: + if (GUILayout.Button("Back")) + { + ResetWindow(); + } + break; + + case state.Export: + if (GUILayout.Button("Back")) + { + State = state.Settings; + } + break; + } + } + } + + private void ExportCarSettings( string assetBundleName, string outFilePath ) + { + //Create master JSONObject + JSONObject jsonfile = new JSONObject(); + + jsonfile.AddField(CarJSONKeys.BUNDLE_NAME, assetBundleName); + jsonfile.AddField(CarJSONKeys.PREFAB_NAME, _trainCarSetup.gameObject.name); + jsonfile.AddField(CarJSONKeys.IDENTIFIER, identifier); + jsonfile.AddField(CarJSONKeys.CAR_TYPE, (int)TrainCarType); + + //Bogies + jsonfile.AddField(CarJSONKeys.REPLACE_FRONT_BOGIE, _trainCarSetup.ReplaceFrontBogie); + if( _trainCarSetup.ReplaceFrontBogie ) + { + if( _trainCarSetup.FrontBogie.GetComponent() is BogieSetup fbs ) + { + jsonfile.AddField(CarJSONKeys.FRONT_BOGIE_PARAMS, fbs.GetJSON()); + } + } + + jsonfile.AddField(CarJSONKeys.REPLACE_REAR_BOGIE, _trainCarSetup.ReplaceRearBogie); + if( _trainCarSetup.ReplaceRearBogie ) + { + if( _trainCarSetup.RearBogie.GetComponent() is BogieSetup rbs ) + { + jsonfile.AddField(CarJSONKeys.REAR_BOGIE_PARAMS, rbs.GetJSON()); + } + } + + //Create JSON file. + var fullJson = jsonfile.ToString(true); + + //Write data to JSON file + using( StreamWriter newTask = new StreamWriter(outFilePath, false) ) + { + newTask.Write(fullJson); + } + } + + private void ResetWindow() + { + //Set state to main when + State = state.Main; + + //Reset internal data + _trainCarSetup = null; + + //Reset loaded car so we can get data from it again. + loadedCar = false; + + #region Reset Exported Train Car Data + + //Reset identifier + identifier = "Custom Car"; + + //Reset TrainCar type. + TrainCarType = BaseTrainCarType.FlatbedEmpty; + + #region Positions + + ////Couplers + //FrontCouplerPosition = Vector3.zero; + //RearCouplerPosition = Vector3.zero; + + ////Chains + //FrontChainPosition = Vector3.zero; + //RearChainPosition = Vector3.zero; + + //FrontHosePosition = Vector3.zero; + //RearHosePosition = Vector3.zero; + + ////Buffers + //FrontBufferPosition = Vector3.zero; + //RearBufferPosition = Vector3.zero; + + ////Bogies + //FrontBogiePosition = Vector3.zero; + //RearBogiePosition = Vector3.zero; + + ////Name plates + //SidePlate1Position = Vector3.zero; + //SidePlate2Position = Vector3.zero; + + #endregion + + #endregion + } +} + + +#region Build Helper + +public static class AssetBundleBuildHelper +{ + public static List GetBuildsForPaths(HashSet processedBundles , params Object[] assets ) + { + List assetBundleBuilds = new List(); + + // Get asset bundle names from selection + foreach (var o in assets) + { + var assetPath = AssetDatabase.GetAssetPath(o); + var importer = AssetImporter.GetAtPath(assetPath); + + if (importer == null) + { + continue; + } + + // Get asset bundle name & variant + var assetBundleName = importer.assetBundleName; + var assetBundleVariant = importer.assetBundleVariant; + var assetBundleFullName = string.IsNullOrEmpty(assetBundleVariant) ? assetBundleName : assetBundleName + "." + assetBundleVariant; + + // Only process assetBundleFullName once. No need to add it again. + if (processedBundles.Contains(assetBundleFullName)) + { + continue; + } + + processedBundles.Add(assetBundleFullName); + + AssetBundleBuild build = new AssetBundleBuild(); + + build.assetBundleName = assetBundleName; + build.assetBundleVariant = assetBundleVariant; + build.assetNames = AssetDatabase.GetAssetPathsFromAssetBundle(assetBundleFullName); + + assetBundleBuilds.Add(build); + } + + return assetBundleBuilds; + } +} + +class MyCustomBuildProcessor : IPostprocessBuildWithReport +{ + public int callbackOrder + { + get { return 0; } + } + + public void OnPostprocessBuild(BuildReport report) + { + Debug.Log("Finished building asset bundles"); + } +} + +#endregion \ No newline at end of file diff --git a/CCL_GameScripts/Editor/FBXMeshExtractor.cs b/CCL_GameScripts/Editor/FBXMeshExtractor.cs new file mode 100644 index 00000000..45e7daa3 --- /dev/null +++ b/CCL_GameScripts/Editor/FBXMeshExtractor.cs @@ -0,0 +1,74 @@ +using UnityEngine; +using UnityEditor; + +public class FBXMeshExtractor +{ + private static string _progressTitle = "Extracting Meshes"; + private static string _sourceExtension = ".fbx"; + private static string _targetExtension = ".asset"; + + + [MenuItem("Assets/Extract Meshes", validate = true)] + private static bool ExtractMeshesMenuItemValidate() + { + for( int i = 0; i < Selection.objects.Length; i++ ) + { + if( !AssetDatabase.GetAssetPath(Selection.objects[i]).EndsWith(_sourceExtension) ) + return false; + } + return true; + } + + [MenuItem("Assets/Extract Meshes")] + private static void ExtractMeshesMenuItem() + { + EditorUtility.DisplayProgressBar(_progressTitle, "", 0); + for( int i = 0; i < Selection.objects.Length; i++ ) + { + EditorUtility.DisplayProgressBar(_progressTitle, Selection.objects[i].name, (float)i / (Selection.objects.Length - 1)); + ExtractMeshes(Selection.objects[i]); + } + EditorUtility.ClearProgressBar(); + } + + private static void ExtractMeshes( Object selectedObject ) + { + //Create Folder Hierarchy + string selectedObjectPath = AssetDatabase.GetAssetPath(selectedObject); + string parentfolderPath = selectedObjectPath.Substring(0, selectedObjectPath.Length - (selectedObject.name.Length + 5)); + string objectFolderName = selectedObject.name; + string objectFolderPath = parentfolderPath + "/" + objectFolderName; + string meshFolderName = "Meshes"; + string meshFolderPath = objectFolderPath + "/" + meshFolderName; + + if( !AssetDatabase.IsValidFolder(objectFolderPath) ) + { + AssetDatabase.CreateFolder(parentfolderPath, objectFolderName); + + if( !AssetDatabase.IsValidFolder(meshFolderPath) ) + { + AssetDatabase.CreateFolder(objectFolderPath, meshFolderName); + } + } + + //Create Meshes + Object[] objects = AssetDatabase.LoadAllAssetsAtPath(selectedObjectPath); + + for( int i = 0; i < objects.Length; i++ ) + { + if( objects[i] is Mesh ) + { + EditorUtility.DisplayProgressBar(_progressTitle, selectedObject.name + " : " + objects[i].name, (float)i / (objects.Length - 1)); + + Mesh mesh = Object.Instantiate(objects[i]) as Mesh; + + AssetDatabase.CreateAsset(mesh, meshFolderPath + "/" + objects[i].name + _targetExtension); + } + } + + //Cleanup + AssetDatabase.MoveAsset(selectedObjectPath, objectFolderPath + "/" + selectedObject.name + _sourceExtension); + AssetDatabase.SaveAssets(); + AssetDatabase.Refresh(); + } +} diff --git a/CCL_GameScripts/Editor/FindMissingScripts.cs b/CCL_GameScripts/Editor/FindMissingScripts.cs new file mode 100644 index 00000000..cc5b283f --- /dev/null +++ b/CCL_GameScripts/Editor/FindMissingScripts.cs @@ -0,0 +1,81 @@ +using System; +using System.Collections.Generic; +using UnityEngine; +using UnityEditor; + +/// +/// Find missing scripts in object. +/// Optionally delete them. +/// +public class FindMissingScripts : EditorWindow +{ + static int go_count = 0, components_count = 0, missing_count = 0; + private bool deleteMissingScripts = false; + + [MenuItem("Tools/Find Missing Scripts")] + public static void ShowWindow() + { + var window = (FindMissingScripts)EditorWindow.GetWindow(typeof(FindMissingScripts)); + window.deleteMissingScripts = false; + } + + public void OnGUI() + { + if (GUILayout.Button("Find Missing Scripts in selected GameObjects")) + { + FindInSelected(deleteMissingScripts); + } + + deleteMissingScripts = GUILayout.Toggle(deleteMissingScripts, "Delete Missing Scripts"); + + } + private static void FindInSelected(bool delete) + { + GameObject[] go = Selection.gameObjects; + go_count = 0; + components_count = 0; + missing_count = 0; + foreach (GameObject g in go) + { + FindInGO(g, delete); + } + Debug.Log(string.Format("Searched {0} GameObjects, {1} components, found {2} missing", go_count, components_count, missing_count)); + } + + private static void FindInGO(GameObject g, bool delete) + { + go_count++; + Component[] components = g.GetComponents(); + for (int i = 0; i < components.Length; i++) + { + components_count++; + if (components[i] == null) + { + missing_count++; + string s = g.name; + Transform t = g.transform; + while (t.parent != null) + { + s = t.parent.name +"/"+s; + t = t.parent; + } + + Debug.Log ($"{s} has an empty script attached in position: {i}", g); + } + } + + //delete scripts + if (delete) + { + var num = GameObjectUtility.RemoveMonoBehavioursWithMissingScript(g); + Debug.Log($"Deleted {num} script on GameObject {g.name}"); + } + + // Now recurse through each child GO (if there are any): + foreach (Transform childT in g.transform) + { + //Debug.Log("Searching " + childT.name + " " ); + FindInGO(childT.gameObject, delete); + } + } +} \ No newline at end of file diff --git a/CCL_GameScripts/SimParamsEditor.cs b/CCL_GameScripts/Editor/SimParamsEditor.cs similarity index 100% rename from CCL_GameScripts/SimParamsEditor.cs rename to CCL_GameScripts/Editor/SimParamsEditor.cs diff --git a/DVCustomCarLoader/JSONObject/JSONObject.cs b/CCL_GameScripts/JSONObject/JSONObject.cs similarity index 97% rename from DVCustomCarLoader/JSONObject/JSONObject.cs rename to CCL_GameScripts/JSONObject/JSONObject.cs index 4240975e..4b0f245f 100644 --- a/DVCustomCarLoader/JSONObject/JSONObject.cs +++ b/CCL_GameScripts/JSONObject/JSONObject.cs @@ -3,8 +3,10 @@ #define USEFLOAT //Use floats for numbers instead of doubles (enable if you're getting too many significant digits in string output) //#define POOLING //Currently using a build setting for this one (also it's experimental) +#if UNITY_2 || UNITY_3 || UNITY_4 || UNITY_5 || UNITY_5_3_OR_NEWER using UnityEngine; using Debug = UnityEngine.Debug; +#endif using System.Diagnostics; using System.Collections; using System.Collections.Generic; @@ -274,7 +276,11 @@ void Parse(string str, int maxDepth = -2, bool storeExcessLevels = false, bool s if(strict) { if(str[0] != '[' && str[0] != '{') { type = Type.NULL; +#if UNITY_2 || UNITY_3 || UNITY_4 || UNITY_5 || UNITY_5_3_OR_NEWER Debug.LogWarning +#else + Debug.WriteLine +#endif ("Improper (strict) JSON formatting. First character must be [ or {"); return; } @@ -359,7 +365,11 @@ void Parse(string str, int maxDepth = -2, bool storeExcessLevels = false, bool s type = Type.NUMBER; } catch(System.FormatException) { type = Type.NULL; +#if UNITY_2 || UNITY_3 || UNITY_4 || UNITY_5 || UNITY_5_3_OR_NEWER Debug.LogWarning +#else + Debug.WriteLine +#endif ("improper JSON formatting:" + str); } return; @@ -690,7 +700,11 @@ static void MergeRecur(JSONObject left, JSONObject right) { } } else if(left.type == Type.ARRAY && right.type == Type.ARRAY) { if(right.Count > left.Count) { +#if UNITY_2 || UNITY_3 || UNITY_4 || UNITY_5 || UNITY_5_3_OR_NEWER Debug.LogError +#else + Debug.WriteLine +#endif ("Cannot merge arrays when right object has more elements"); return; } @@ -745,7 +759,11 @@ public IEnumerable PrintAsync(bool pretty = false) { IEnumerable StringifyAsync(int depth, StringBuilder builder, bool pretty = false) { //Convert the JSONObject into a string //Profiler.BeginSample("JSONprint"); if(depth++ > MAX_DEPTH) { +#if UNITY_2 || UNITY_3 || UNITY_4 || UNITY_5 || UNITY_5_3_OR_NEWER Debug.Log +#else + Debug.WriteLine +#endif ("reached max depth!"); yield break; } @@ -887,7 +905,11 @@ IEnumerable StringifyAsync(int depth, StringBuilder builder, bool pretty = false void Stringify(int depth, StringBuilder builder, bool pretty = false) { //Convert the JSONObject into a string //Profiler.BeginSample("JSONprint"); if(depth++ > MAX_DEPTH) { +#if UNITY_2 || UNITY_3 || UNITY_4 || UNITY_5 || UNITY_5_3_OR_NEWER Debug.Log +#else + Debug.WriteLine +#endif ("reached max depth!"); return; } @@ -1013,6 +1035,7 @@ void Stringify(int depth, StringBuilder builder, bool pretty = false) { //Conver //Profiler.EndSample(); } #endregion +#if UNITY_2 || UNITY_3 || UNITY_4 || UNITY_5 || UNITY_5_3_OR_NEWER public static implicit operator WWWForm(JSONObject obj) { WWWForm form = new WWWForm(); for(int i = 0; i < obj.list.Count; i++) { @@ -1026,6 +1049,7 @@ public static implicit operator WWWForm(JSONObject obj) { } return form; } +#endif public JSONObject this[int index] { get { if(list.Count > index) return list[index]; @@ -1060,14 +1084,22 @@ public Dictionary ToDictionary() { case Type.NUMBER: result.Add(keys[i], val.n.ToString(CultureInfo.InvariantCulture)); break; case Type.BOOL: result.Add(keys[i], val.b.ToString(CultureInfo.InvariantCulture)); break; default: +#if UNITY_2 || UNITY_3 || UNITY_4 || UNITY_5 || UNITY_5_3_OR_NEWER Debug.LogWarning +#else + Debug.WriteLine +#endif ("Omitting object: " + keys[i] + " in dictionary conversion"); break; } } return result; } +#if UNITY_2 || UNITY_3 || UNITY_4 || UNITY_5 || UNITY_5_3_OR_NEWER Debug.Log +#else + Debug.WriteLine +#endif ("Tried to turn non-Object JSONObject into a dictionary"); return null; } diff --git a/CCL_GameScripts/JSONObject/LICENSE.md b/CCL_GameScripts/JSONObject/LICENSE.md new file mode 100644 index 00000000..e0f1994a --- /dev/null +++ b/CCL_GameScripts/JSONObject/LICENSE.md @@ -0,0 +1,19 @@ +Copyright (c) 2010-2019 Matt Schoen + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/DVCustomCarLoader/JSONObject/VectorTemplates.cs b/CCL_GameScripts/JSONObject/VectorTemplates.cs similarity index 100% rename from DVCustomCarLoader/JSONObject/VectorTemplates.cs rename to CCL_GameScripts/JSONObject/VectorTemplates.cs diff --git a/CCL_GameScripts/JSONObject/readme.md b/CCL_GameScripts/JSONObject/readme.md new file mode 100644 index 00000000..ea3588ea --- /dev/null +++ b/CCL_GameScripts/JSONObject/readme.md @@ -0,0 +1,192 @@ +# Author + + Matt Schoen of [Defective Studios](http://www.defectivestudios.com) + + +# Intro + +I came across the need to send structured data to and from a server on one of my projects, and figured it would be worth my while to use JSON. When I looked into the issue, I tried a few of the C# implementations listed on http://json.org, but found them to be too complicated to work with and expand upon. So, I've written a very simple JSONObject class, which can be generically used to encode/decode data into a simple container. This page assumes that you know what JSON is, and how it works. It's rather simple, just go to json.org for a visual description of the encoding format. + +As an aside, this class is pretty central to the AssetCloud content management system, from Defective Studios. + +> **Update:** The code has been updated to version 1.4 to incorporate user-submitted patches and bug reports. This fixes issues dealing with whitespace in the format, as well as empty arrays and objects, and escaped quotes within strings. + + +# Usage + +Users should not have to modify the JSONObject class themselves, and must follow the very simple proceedures outlined below: + +Sample data (in JSON format): +```JSON +{ + "TestObject": { + "SomeText": "Blah", + "SomeObject": { + "SomeNumber": 42, + "SomeBool": true, + "SomeNull": null + }, + + "SomeEmptyObject": { }, + "SomeEmptyArray": [ ], + "EmbeddedObject": "{\"field\":\"Value with \\\"escaped quotes\\\"\"}" + } +} +``` + +## Features + +* Decode JSON-formatted strings into a usable data structure +* Encode structured data into a JSON-formatted string +* Interoperable with `Dictionary` and `WWWForm` +* Optimized `parse`/`stringify` functions -- minimal (unavoidable) garbage creation +* Asynchronous `stringify` function for serializing lots of data without frame drops +* `MaxDepth` parsing will skip over nested data that you don't need +* Special (non-compliant) `Baked` object type can store stringified data within parsed objects +* Copy to new `JSONObject` +* Merge with another `JSONObject` (experimental) +* Random access (with `int` or `string`) +* `ToString()` returns JSON data with optional "pretty" flag to include newlines and tabs +* Switch between double and float for numeric storage depending on level of precision needed (and to ensure that numbers are parsed/stringified correctly) +* Supports `Infinity` and `NaN` values +* `JSONTemplates` static class provides serialization functions for common classes like `Vector3`, `Matrix4x4` +* Object pool implementation (experimental) +* Handy `JSONChecker` window to test parsing on sample data + +It should be pretty obvious what this parser can and cannot do. If anyone reading this is a JSON buff (is there such a thing?) please feel free to expand and modify the parser to be more compliant. Currently I am using the .NET `System.Convert` namespace functions for parsing the data itself. It parses strings and numbers, which was all that I needed of it, but unless the formatting is supported by `System.Convert`, it may not incorporate all proper JSON strings. Also, having never written a JSON parser before, I don't doubt that I could improve the efficiency or correctness of the parser. It serves my purpose, and hopefully will help you with your project! Let me know if you make any improvements :) + +Also, you JSON buffs (really, who would admit to being a JSON buff...) might also notice from my feature list that this thing isn't exactly to specifications. Here is where it differs: +* "a string" is considered valid JSON. There is an optional "strict" parameter to the parser which will bomb out on such input, in case that matters to you. +* The `Baked` mode is totally made up. +* The `MaxDepth` parsing is totally made up. +* `NaN` and `Infinity` aren't officially supported by JSON ([http://stackoverflow.com/questions/1423081/json-left-out-infinity-and-nan-json-status-in-ecmascript read more] about this issue... I lol'd @ the first comment on the first answer) +* I have no idea about edge cases in my parsing strategy. I have been using this code for about 3 years now and have only had to modify the parser because other people's use cases (still valid JSON) didn't parse correctly. In my experience, anything that this code generates is parsed just fine. + + +## Encoding + +Encoding is something of a hard-coded process. This is because I have no idea what your data is! It would be great if this were some sort of interface for taking an entire class and encoding it's number/string fields, but it's not. I've come up with a few clever ways of using loops and/or recursive methods to cut down of the amount of code I have to write when I use this tool, but they're pretty project-specific. + +Note: This section used to be WRONG! And now it's OLD! Will update later... this will all still work, but there are now a couple of ways to skin this cat. + +```C# +// Note: your data can only be numbers and strings. +// This is not a solution for object serialization +// or anything like that. +JSONObject j = new JSONObject(JSONObject.Type.OBJECT); +// number +j.AddField("field1", 0.5); +// string +j.AddField("field2", "sampletext"); +// array +JSONObject arr = new JSONObject(JSONObject.Type.ARRAY); +j.AddField("field3", arr); + +arr.Add(1); +arr.Add(2); +arr.Add(3); + +string encodedString = j.print(); +``` + +NEW! The constructor, Add, and AddField functions now support a nested delegate structure. This is useful if you need to create a nested JSONObject in a single line. For example: + + +```C# +DoRequest(URL, new JSONObject(delegate(JSONObject request) { + request.AddField("sort", delegate(JSONObject sort) { + sort.AddField("_timestamp", "desc"); + }); + request.AddField("query", new JSONObject(delegate(JSONObject query) { + query.AddField("match_all", JSONObject.obj); + })); + request.AddField("fields", delegate(JSONObject fields) { + fields.Add("_timestamp"); + }); +}).ToString()); +``` + + +## Decoding + +Decoding is much simpler on the input end, and again, what you do with the `JSONObject` will vary on a per-project basis. One of the more complicated way to extract the data is with a recursive function, as drafted below. Calling the constructor with a properly formatted JSON string will return the root object (or array) containing all of its children, in one neat reference! The data is in a public `ArrayList` called `list`, with a matching key list (called `keys`!) if the root is an `Object`. If that's confusing, take a glance over the following code and the `print()` method in the `JSONObject` class. If there is an error in the JSON formatting (or if there's an error with my code!) the debug console will read "improper JSON formatting". + +```C# +string encodedString = "{\"field1\": 0.5,\"field2\": \"sampletext\",\"field3\": [1,2,3]}"; +JSONObject j = new JSONObject(encodedString); +accessData(j); +//access data (and print it) +void accessData(JSONObject obj){ + switch(obj.type){ + case JSONObject.Type.OBJECT: + for(int i = 0; i < obj.list.Count; i++){ + string key = (string)obj.keys[i]; + JSONObject j = (JSONObject)obj.list[i]; + Debug.Log(key); + accessData(j); + } + break; + case JSONObject.Type.ARRAY: + foreach(JSONObject j in obj.list){ + accessData(j); + } + break; + case JSONObject.Type.STRING: + Debug.Log(obj.str); + break; + case JSONObject.Type.NUMBER: + Debug.Log(obj.n); + break; + case JSONObject.Type.BOOL: + Debug.Log(obj.b); + break; + case JSONObject.Type.NULL: + Debug.Log("NULL"); + break; + + } +} +``` + +NEW! Decoding now also supports a delegate format which will automatically check if a field exists before processing the data, providing an optional parameter for an OnFieldNotFound response. For example: + +```C# +new JSONObject(data); +list.GetField("hits", delegate(JSONObject hits) { + hits.GetField("hits", delegate(JSONObject hits2) { + foreach (JSONObject gameSession in hits2.list) { + Debug.Log(gameSession); + } + }); +}, delegate(string name) { //"name" will be equal to the name of the missing field. In this case, "hits" + Debug.LogWarning("no game sessions"); +}); +``` + +## Not So New! `(O(n))` Random access! + +I've added a string and int [] index to the class, so you can now retrieve data as such (from above): + +```C# +JSONObject arr = obj["field3"]; +Debug.log(arr[2].n); //Should ouptut "3" +``` + +## Change Log + +### v1.4 +Big update! + +* Better GC performance. Enough of that garbage! + * Remaining culprits are internal garbage from `StringBuilder.Append`/`AppendFormat`, `String.Substring`, `List.Add`/`GrowIfNeeded`, `Single.ToString` +* Added asynchronous `Stringily` function for serializing large amounts of data at runtime without frame drops +* Added `Baked` type +* Added `MaxDepth` to parsing function +* Various cleanup refactors recommended by ReSharper + +### v1.3.2 +* Added support for `NaN` +* Added strict mode to fail on purpose for improper formatting. Right now this just means that if the parse string doesn't start with `[` or `{`, it will print a warning and return a `null` `JSONObject`. +* Changed `infinity` and `NaN` implementation to use `float` and `double` instead of `Mathf` +* Handles empty objects/arrays better +* Added a flag to print and `ToString` to turn on/off pretty print. The define on top is now an override to system-wide disable diff --git a/CCL_GameScripts/TrainCarSetup.cs b/CCL_GameScripts/TrainCarSetup.cs new file mode 100644 index 00000000..0c4103c5 --- /dev/null +++ b/CCL_GameScripts/TrainCarSetup.cs @@ -0,0 +1,131 @@ +using System.Collections.Generic; +using UnityEditor; +using UnityEngine; + +namespace CCL_GameScripts +{ + /// + /// Holds all the references for setup of a TrainCar. + /// + public class TrainCarSetup : MonoBehaviour + { + + #region Internal + + public delegate void BringUpWindowDelegate( TrainCarSetup trainCarSetup ); + + public static BringUpWindowDelegate LaunchExportWindow; + public static BringUpWindowDelegate LaunchLocoSetupWindow; + + [ContextMenu("Prepare Car for Export")] + private void BringUpExportWindow() + { + LaunchExportWindow?.Invoke(this); + } + + [ContextMenu("Add Locomotive Parameters")] + private void BringUpLocoSetup() + { + LaunchLocoSetupWindow?.Invoke(this); + } + + #endregion + + + [Header("Bogies")] + public Transform FrontBogie; + public Transform RearBogie; + public bool ReplaceFrontBogie; + public bool ReplaceRearBogie; + public CapsuleCollider FrontBogieCollider; + public CapsuleCollider RearBogieCollider; + + //[Header("Couplers")] + //public Transform FrontCouplerRig; + //public Transform RearCouplerRig; + +#if UNITY_EDITOR + + #region Helpers + [MethodButton(nameof(CreateAssetBundleForTrainCar), nameof(AlignBogieColliders))] + [SerializeField] private bool editorFoldout; + + public void CreateAssetBundleForTrainCar() + { + string assetPath = AssetDatabase.GetAssetPath(PrefabUtility.GetCorrespondingObjectFromSource(gameObject)); + + if (string.IsNullOrEmpty(assetPath)) + { + Debug.LogError("Asset path is null! Make sure the TrainCar is a prefab!"); + return; + } + + //Change name of asset bundle to this GameObject. + AssetImporter.GetAtPath(assetPath).SetAssetBundleNameAndVariant(name, ""); + + //Remove unused assetBundle names. + AssetDatabase.RemoveUnusedAssetBundleNames(); + + EditorUtility.DisplayDialog("Created AssetBundle", + $"An AssetBundle with the name {name} was created successfully.", "OK"); + } + + /// + /// This will properly align the bogie colliders to the bogie along the x and z axes. + /// + public void AlignBogieColliders() + { + List objectsToUndo = new List(); + + if( FrontBogieCollider ) + { + var frontCenter = FrontBogieCollider.center; + frontCenter = new Vector3(0, frontCenter.y, FrontBogie.localPosition.z); + FrontBogieCollider.center = frontCenter; + objectsToUndo.Add(FrontBogieCollider.transform); + } + + if( RearBogieCollider ) + { + var rearCenter = RearBogieCollider.center; + rearCenter = new Vector3(0, rearCenter.y, RearBogie.localPosition.z); + RearBogieCollider.center = rearCenter; + objectsToUndo.Add(RearBogieCollider.transform); + } + + Undo.RecordObjects(objectsToUndo.ToArray(), "Undo Align Bogies"); + } + + #endregion + + #region Gizmos + + //private void OnDrawGizmos() + //{ + // #region Coupler Gizmos + // if (FrontCoupler != null) Gizmos.DrawWireCube(FrontCoupler.position, new Vector3(0.3f, 0.3f, 0.3f)); + // if (RearCoupler != null) Gizmos.DrawWireCube(RearCoupler.position, new Vector3(0.3f, 0.3f, 0.3f)); + // #endregion + + // #region Bogie Gizmos + + // //if (FrontBogieCollider != null) + // //{ + // // var frontBogiePos = FrontBogieCollider.transform.position + FrontBogieCollider.transform.TransformPoint(FrontBogieCollider.bounds.center); + // // Gizmos.DrawWireCube(frontBogiePos, FrontBogieCollider.bounds.size); + // //} + + // //if (RearBogieCollider != null) + // //{ + // // var rearBogiePos = RearBogieCollider.transform.position + RearBogieCollider.transform.TransformPoint(RearBogieCollider.bounds.center); + // // Gizmos.DrawWireCube(rearBogiePos, RearBogieCollider.bounds.size); + // //} + + // #endregion + //} + + #endregion + +#endif + } +} \ No newline at end of file diff --git a/DVCustomCarLoader/AudioPatches.cs b/DVCustomCarLoader/AudioPatches.cs deleted file mode 100644 index 2dc2f0a8..00000000 --- a/DVCustomCarLoader/AudioPatches.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System; -using CCL_GameScripts; -using DVCustomCarLoader.LocoComponents; -using HarmonyLib; - -namespace DVCustomCarLoader -{ - [HarmonyPatch(typeof(TrainCar), "InitAudio")] - public static class TrainCar_InitAudioPatch - { - [HarmonyPriority(Priority.First)] - static bool Prefix( TrainCar __instance ) - { - var simParams = __instance.gameObject.GetComponent(); - if( simParams ) return false; - return true; - } - } -} diff --git a/DVCustomCarLoader/DVCustomCarLoader.csproj b/DVCustomCarLoader/DVCustomCarLoader.csproj index a1769d0d..3c77269b 100644 --- a/DVCustomCarLoader/DVCustomCarLoader.csproj +++ b/DVCustomCarLoader/DVCustomCarLoader.csproj @@ -72,7 +72,7 @@ - + @@ -89,8 +89,6 @@ - - diff --git a/DVCustomCarLoader/ModPatches.cs b/DVCustomCarLoader/ModPatches.cs new file mode 100644 index 00000000..9b497509 --- /dev/null +++ b/DVCustomCarLoader/ModPatches.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using CCL_GameScripts; +using DVCustomCarLoader.LocoComponents; +using HarmonyLib; + +namespace DVCustomCarLoader +{ + [HarmonyPatch(typeof(TrainCar), "InitAudio")] + public static class TrainCar_InitAudioPatch + { + [HarmonyPriority(Priority.First)] + static bool Prefix( TrainCar __instance ) + { + var simParams = __instance.gameObject.GetComponent(); + if( simParams ) return false; + return true; + } + } + + [HarmonyPatch] + public static class LocoLights_Patch + { + static MethodBase TargetMethod() + { + Type trainCarPatch = AccessTools.TypeByName("TrainCar_Start_Patch"); + if( trainCarPatch != null ) + { + return AccessTools.Method(trainCarPatch, "DoCreate", new[] { typeof(TrainCar) }); + } + return null; + } + + static bool Prefix( TrainCar car ) + { + var simParams = car.gameObject.GetComponent(); + if( simParams ) return false; + return true; + } + } +} From a30ffb6299c74bc0c34ff5ba1b2d20cbc6a4d615 Mon Sep 17 00:00:00 2001 From: Katy Fox Date: Tue, 13 Jul 2021 12:56:39 -0400 Subject: [PATCH 09/20] Custom locos working with keyboard input --- DVCustomCarLoader/CarPartConstants.cs | 2 +- DVCustomCarLoader/CustomCar.cs | 3 +- DVCustomCarLoader/CustomCarManager.cs | 5 +++ .../CustomLocoControllerDiesel.cs | 12 +++---- DVCustomCarLoader/Main.cs | 31 ++++++++++++++----- DVCustomCarLoader/ModPatches.cs | 27 ++++++++++++---- 6 files changed, 57 insertions(+), 23 deletions(-) diff --git a/DVCustomCarLoader/CarPartConstants.cs b/DVCustomCarLoader/CarPartConstants.cs index 94b760d3..89f3f6c1 100644 --- a/DVCustomCarLoader/CarPartConstants.cs +++ b/DVCustomCarLoader/CarPartConstants.cs @@ -11,7 +11,7 @@ public static class CarPartNames public const string BUFFER_PLATE_REAR = "HookPlate_R"; public static readonly string[] BUFFER_FRONT_PADS = { "Buffer_FL", "Buffer_FR" }; public static readonly string[] BUFFER_REAR_PADS = { "Buffer_RL", "Buffer_RR" }; - public const string BUFFER_CHAIN_RIG = "BuffersAndChainRig"; // same name front and rear + public static readonly string[] BUFFER_CHAIN_RIGS = { "BuffersAndChainRig", "BuffersAndChainRigMU" }; // same name front and rear // Collider parts public const string COLLIDERS_ROOT = "[colliders]"; diff --git a/DVCustomCarLoader/CustomCar.cs b/DVCustomCarLoader/CustomCar.cs index a5431ba9..d540817b 100644 --- a/DVCustomCarLoader/CustomCar.cs +++ b/DVCustomCarLoader/CustomCar.cs @@ -88,7 +88,7 @@ public void FinalizePrefab() Transform child = bufferRoot.transform.GetChild(i); string childName = child.name.Trim(); - if( CarPartNames.BUFFER_CHAIN_RIG.Equals(childName) ) + if( CarPartNames.BUFFER_CHAIN_RIGS.Contains(childName) ) { // front or rear chain rig // determine whether front or rear chain rig: +z is front @@ -340,6 +340,7 @@ public void FinalizePrefab() } CarPrefab = newFab; + CarPrefab.name = identifier; Main.ModEntry.Logger.Log($"Finalized prefab for {identifier}"); } diff --git a/DVCustomCarLoader/CustomCarManager.cs b/DVCustomCarLoader/CustomCarManager.cs index 1b361fcc..3f7d6c0e 100644 --- a/DVCustomCarLoader/CustomCarManager.cs +++ b/DVCustomCarLoader/CustomCarManager.cs @@ -12,6 +12,11 @@ public class CustomCarManager : MonoBehaviour private readonly Dictionary SpawnedCustomCarIds = new Dictionary(); + public bool IsRegisteredCustomCar( TrainCar trainCar ) + { + return SpawnedCustomCarIds.ContainsKey(trainCar); + } + public bool TryGetCustomCarId( TrainCar trainCar, out string id ) { return SpawnedCustomCarIds.TryGetValue(trainCar, out id); diff --git a/DVCustomCarLoader/LocoComponents/CustomLocoControllerDiesel.cs b/DVCustomCarLoader/LocoComponents/CustomLocoControllerDiesel.cs index 95c4cbeb..ef4c678e 100644 --- a/DVCustomCarLoader/LocoComponents/CustomLocoControllerDiesel.cs +++ b/DVCustomCarLoader/LocoComponents/CustomLocoControllerDiesel.cs @@ -85,16 +85,10 @@ public override void SetReverser( float position ) base.SetReverser(position); } - public override void SetThrottle( float throttleLever ) - { - base.SetThrottle(throttleLever); - } - - //protected override void Awake() //{ // base.Awake(); - + // // TODO: MU, save state // //MultipleUnitModule component = base.GetComponent(); // //base.gameObject.AddComponent().Initialize(sim, damageController, this, carVisitChecker, component); @@ -162,7 +156,9 @@ protected override void Start() base.Start(); if( !VRManager.IsVREnabled() ) { - gameObject.AddComponent().control = this; + var keyboardCtrl = gameObject.AddComponent(); + keyboardCtrl.control = this; + Main.Log("Added keyboard input to car"); } } diff --git a/DVCustomCarLoader/Main.cs b/DVCustomCarLoader/Main.cs index a0940fc5..e36609ab 100644 --- a/DVCustomCarLoader/Main.cs +++ b/DVCustomCarLoader/Main.cs @@ -1,5 +1,6 @@ using System; using System.Reflection; +using DVCustomCarLoader.LocoComponents; using HarmonyLib; using UnityEngine; using UnityModManagerNet; @@ -9,13 +10,19 @@ namespace DVCustomCarLoader { public static class Main { - private static bool Load(UnityModManager.ModEntry modEntry) + public static CustomCarManager CustomCarManagerInstance; + public static CommsRadioCustomCarManager CommsRadioCustomCarManager; + public static UnityModManager.ModEntry ModEntry; + public static bool Enabled; + + public static bool Load(UnityModManager.ModEntry modEntry) { var harmony = new Harmony(modEntry.Info.Id); harmony.PatchAll(Assembly.GetExecutingAssembly()); + LocoLights_Patch.TryCreatePatch(harmony); - Main.Enabled = modEntry.Enabled; - Main.ModEntry = modEntry; + Enabled = modEntry.Enabled; + ModEntry = modEntry; //Create sky manager instance. ModEntry.Logger.Log("Creating CustomCarManager"); @@ -28,13 +35,23 @@ private static bool Load(UnityModManager.ModEntry modEntry) CustomCarManagerInstance = nsmgr.AddComponent(); CustomCarManagerInstance.Setup(); + PlayerManager.CarChanged += OnCarChanged; + return true; } - public static CustomCarManager CustomCarManagerInstance; - public static CommsRadioCustomCarManager CommsRadioCustomCarManager; - public static UnityModManager.ModEntry ModEntry; - public static bool Enabled; + private static void OnCarChanged( TrainCar newCar ) + { + if( newCar && CustomCarManagerInstance.IsRegisteredCustomCar(newCar) ) + { + // diesel autostart + var locoController = newCar.gameObject.GetComponent(); + if( locoController && !locoController.EngineRunning ) + { + locoController.EngineRunning = true; + } + } + } #region Logging diff --git a/DVCustomCarLoader/ModPatches.cs b/DVCustomCarLoader/ModPatches.cs index 9b497509..8bfc87da 100644 --- a/DVCustomCarLoader/ModPatches.cs +++ b/DVCustomCarLoader/ModPatches.cs @@ -19,17 +19,32 @@ static bool Prefix( TrainCar __instance ) } } - [HarmonyPatch] public static class LocoLights_Patch { - static MethodBase TargetMethod() + public static void TryCreatePatch( Harmony harmony ) { - Type trainCarPatch = AccessTools.TypeByName("TrainCar_Start_Patch"); - if( trainCarPatch != null ) + try { - return AccessTools.Method(trainCarPatch, "DoCreate", new[] { typeof(TrainCar) }); + Type trainCarPatch = AccessTools.TypeByName("LocoLightsMod.TrainCar_Start_Patch"); + if( trainCarPatch != null ) + { + var target = AccessTools.Method(trainCarPatch, "DoCreate", new[] { typeof(TrainCar) }); + var prefix = AccessTools.Method(typeof(LocoLights_Patch), "Prefix"); + + harmony.Patch(target, new HarmonyMethod(prefix)); + } + else + { + Main.Log("Loco Lights traincar patch not found, skipping"); + } + } + catch( Exception ex ) + { + Main.Log("Not creating Loco Lights patch"); +#if DEBUG + Main.ModEntry.Logger.LogException(ex); +#endif } - return null; } static bool Prefix( TrainCar car ) From a4e5fafb05cb553dbe168907a4c19f60f9b3f7de Mon Sep 17 00:00:00 2001 From: Katy Fox Date: Tue, 13 Jul 2021 19:32:26 -0400 Subject: [PATCH 10/20] Spawn interiors for locos --- CCL_GameScripts/CCL_GameScripts.csproj | 1 + CCL_GameScripts/CabInputSetup.cs | 13 ++ CCL_GameScripts/TrainCarSetup.cs | 82 +++++---- DVCustomCarLoader/CustomCar.cs | 22 +++ DVCustomCarLoader/DVCustomCarLoader.csproj | 1 + DVCustomCarLoader/LocoComponentManager.cs | 27 +++ .../LocoComponents/CustomCabInput.cs | 166 ++++++++++++++++++ 7 files changed, 270 insertions(+), 42 deletions(-) create mode 100644 CCL_GameScripts/CabInputSetup.cs create mode 100644 DVCustomCarLoader/LocoComponents/CustomCabInput.cs diff --git a/CCL_GameScripts/CCL_GameScripts.csproj b/CCL_GameScripts/CCL_GameScripts.csproj index 769c049c..e9886e0d 100644 --- a/CCL_GameScripts/CCL_GameScripts.csproj +++ b/CCL_GameScripts/CCL_GameScripts.csproj @@ -52,6 +52,7 @@ + diff --git a/CCL_GameScripts/CabInputSetup.cs b/CCL_GameScripts/CabInputSetup.cs new file mode 100644 index 00000000..a87a06e0 --- /dev/null +++ b/CCL_GameScripts/CabInputSetup.cs @@ -0,0 +1,13 @@ +using System.Collections; +using UnityEngine; + +namespace CCL_GameScripts +{ + public class CabInputSetup : MonoBehaviour + { + public GameObject Brake; + public GameObject IndependentBrake; + public GameObject Reverser; + public GameObject Throttle; + } +} \ No newline at end of file diff --git a/CCL_GameScripts/TrainCarSetup.cs b/CCL_GameScripts/TrainCarSetup.cs index 0c4103c5..ec9971b5 100644 --- a/CCL_GameScripts/TrainCarSetup.cs +++ b/CCL_GameScripts/TrainCarSetup.cs @@ -32,7 +32,6 @@ private void BringUpLocoSetup() #endregion - [Header("Bogies")] public Transform FrontBogie; public Transform RearBogie; public bool ReplaceFrontBogie; @@ -40,62 +39,61 @@ private void BringUpLocoSetup() public CapsuleCollider FrontBogieCollider; public CapsuleCollider RearBogieCollider; - //[Header("Couplers")] - //public Transform FrontCouplerRig; - //public Transform RearCouplerRig; + public GameObject InteriorPrefab; #if UNITY_EDITOR #region Helpers - [MethodButton(nameof(CreateAssetBundleForTrainCar), nameof(AlignBogieColliders))] - [SerializeField] private bool editorFoldout; - public void CreateAssetBundleForTrainCar() - { - string assetPath = AssetDatabase.GetAssetPath(PrefabUtility.GetCorrespondingObjectFromSource(gameObject)); + [MethodButton(nameof(CreateAssetBundleForTrainCar), nameof(AlignBogieColliders))] + [SerializeField] private bool editorFoldout; - if (string.IsNullOrEmpty(assetPath)) + public void CreateAssetBundleForTrainCar() { - Debug.LogError("Asset path is null! Make sure the TrainCar is a prefab!"); - return; - } + string assetPath = AssetDatabase.GetAssetPath(PrefabUtility.GetCorrespondingObjectFromSource(gameObject)); + + if (string.IsNullOrEmpty(assetPath)) + { + Debug.LogError("Asset path is null! Make sure the TrainCar is a prefab!"); + return; + } - //Change name of asset bundle to this GameObject. - AssetImporter.GetAtPath(assetPath).SetAssetBundleNameAndVariant(name, ""); + //Change name of asset bundle to this GameObject. + AssetImporter.GetAtPath(assetPath).SetAssetBundleNameAndVariant(name, ""); - //Remove unused assetBundle names. - AssetDatabase.RemoveUnusedAssetBundleNames(); + //Remove unused assetBundle names. + AssetDatabase.RemoveUnusedAssetBundleNames(); - EditorUtility.DisplayDialog("Created AssetBundle", - $"An AssetBundle with the name {name} was created successfully.", "OK"); - } - - /// - /// This will properly align the bogie colliders to the bogie along the x and z axes. - /// - public void AlignBogieColliders() - { - List objectsToUndo = new List(); - - if( FrontBogieCollider ) - { - var frontCenter = FrontBogieCollider.center; - frontCenter = new Vector3(0, frontCenter.y, FrontBogie.localPosition.z); - FrontBogieCollider.center = frontCenter; - objectsToUndo.Add(FrontBogieCollider.transform); + EditorUtility.DisplayDialog("Created AssetBundle", + $"An AssetBundle with the name {name} was created successfully.", "OK"); } - if( RearBogieCollider ) + /// + /// This will properly align the bogie colliders to the bogie along the x and z axes. + /// + public void AlignBogieColliders() { - var rearCenter = RearBogieCollider.center; - rearCenter = new Vector3(0, rearCenter.y, RearBogie.localPosition.z); - RearBogieCollider.center = rearCenter; - objectsToUndo.Add(RearBogieCollider.transform); + List objectsToUndo = new List(); + + if( FrontBogieCollider ) + { + var frontCenter = FrontBogieCollider.center; + frontCenter = new Vector3(0, frontCenter.y, FrontBogie.localPosition.z); + FrontBogieCollider.center = frontCenter; + objectsToUndo.Add(FrontBogieCollider.transform); + } + + if( RearBogieCollider ) + { + var rearCenter = RearBogieCollider.center; + rearCenter = new Vector3(0, rearCenter.y, RearBogie.localPosition.z); + RearBogieCollider.center = rearCenter; + objectsToUndo.Add(RearBogieCollider.transform); + } + + Undo.RecordObjects(objectsToUndo.ToArray(), "Undo Align Bogies"); } - Undo.RecordObjects(objectsToUndo.ToArray(), "Undo Align Bogies"); - } - #endregion #region Gizmos diff --git a/DVCustomCarLoader/CustomCar.cs b/DVCustomCarLoader/CustomCar.cs index d540817b..20894587 100644 --- a/DVCustomCarLoader/CustomCar.cs +++ b/DVCustomCarLoader/CustomCar.cs @@ -24,6 +24,8 @@ public class CustomCar /// public GameObject CarPrefab; + public GameObject InteriorPrefab; + //Bogies public bool HasCustomFrontBogie = false; public bool HasCustomRearBogie = false; @@ -337,6 +339,26 @@ public void FinalizePrefab() if( simParams ) { LocoComponentManager.AddLocoSimulation(newFab, simParams); + + var carSetup = newFab.GetComponent(); + if( carSetup ) + { + if( carSetup.InteriorPrefab ) + { + GameObject interiorFab = Object.Instantiate(carSetup.InteriorPrefab, null); + interiorFab.SetActive(false); + Object.DontDestroyOnLoad(interiorFab); + + LocoComponentManager.SetupCabInput(interiorFab); + newCar.interiorPrefab = interiorFab; + + InteriorPrefab = interiorFab; + } + } + else + { + Main.Warning("TrainCarSetup not found"); + } } CarPrefab = newFab; diff --git a/DVCustomCarLoader/DVCustomCarLoader.csproj b/DVCustomCarLoader/DVCustomCarLoader.csproj index 3c77269b..91fc9ab9 100644 --- a/DVCustomCarLoader/DVCustomCarLoader.csproj +++ b/DVCustomCarLoader/DVCustomCarLoader.csproj @@ -72,6 +72,7 @@ + diff --git a/DVCustomCarLoader/LocoComponentManager.cs b/DVCustomCarLoader/LocoComponentManager.cs index fd98596c..f83ad54f 100644 --- a/DVCustomCarLoader/LocoComponentManager.cs +++ b/DVCustomCarLoader/LocoComponentManager.cs @@ -5,6 +5,7 @@ using UnityEngine; using DVCustomCarLoader.LocoComponents; using CCL_GameScripts; +using HarmonyLib; namespace DVCustomCarLoader { @@ -60,5 +61,31 @@ private static void ApplyDrivingForceParams( DrivingForce driver, SimParamsBase driver.slopeCoeficientMultiplier = simParams.SlopeCoefficientMultiplier; driver.wheelslipToFrictionModifierCurve = simParams.WheelslipToFrictionModifier; } + + public static void SetupCabInput( GameObject interior ) + { + var cabParams = interior.GetComponent(); + if( cabParams ) + { + interior.AddComponent(); + } + else + { + Main.Warning("Loco has an interior prefab, but no cab input setup"); + } + } + } + + [HarmonyPatch(typeof(TrainCar), "LoadInterior")] + public static class TrainCar_LoadInterior_Patch + { + public static void Postfix( GameObject ___loadedInterior ) + { + if( !___loadedInterior.activeSelf ) + { + ___loadedInterior.gameObject.SetActive(true); + Main.Log($"Activating interior on {___loadedInterior.gameObject.name}"); + } + } } } diff --git a/DVCustomCarLoader/LocoComponents/CustomCabInput.cs b/DVCustomCarLoader/LocoComponents/CustomCabInput.cs new file mode 100644 index 00000000..c4104c0c --- /dev/null +++ b/DVCustomCarLoader/LocoComponents/CustomCabInput.cs @@ -0,0 +1,166 @@ +using System.Collections; +using CCL_GameScripts; +using DV.CabControls; +using UnityEngine; + +namespace DVCustomCarLoader.LocoComponents +{ + public class CustomCabInput : CabInput + { + protected CustomLocoController locoController; + protected CabInputSetup config; + + //public GameObject brake; + //public GameObject independentBrake; + //public GameObject reverser; + //public GameObject throttle; + + public bool reverserSnap; + + protected ControlImplBase brakeControl; + protected ControlImplBase independentBrakeControl; + protected ControlImplBase reverserControl; + protected ControlImplBase throttleControl; + + protected bool initialized; + + protected float prevControllerBrake; + protected float prevControllerIndependentBrake; + protected float prevControllerReverser; + protected float prevControllerThrottle; + + private IEnumerator Init() + { + yield return null; + yield return null; + + var car = TrainCar.Resolve(gameObject); + if( car == null || !car ) + { + Main.Error($"Couldn't find TrainCar for interior {gameObject.name}"); + yield break; + } + + locoController = car.GetComponent(); + config = gameObject.GetComponent(); + + if( !locoController ) + { + Main.Error("Couldn't find custom lococontroller"); + yield break; + } + + if( !config ) + { + Main.Error("Couldn't find cab input setup"); + yield break; + } + + if( config.Brake ) + { + brakeControl = config.Brake.GetComponent(); + brakeControl.SetValue(locoController.targetBrake); + } + + if( config.IndependentBrake ) + { + independentBrakeControl = config.IndependentBrake.GetComponent(); + independentBrakeControl.SetValue(locoController.targetIndependentBrake); + } + + if( config.Throttle ) + { + throttleControl = config.Throttle.GetComponent(); + throttleControl.SetValue(locoController.targetThrottle); + } + + if( config.Reverser ) + { + reverserControl = config.Reverser.GetComponent(); + reverserControl.SetValue((locoController.reverser + 1f) / 2f); + } + + yield return WaitFor.SecondsRealtime(1f); + + if( brakeControl ) + { + brakeControl.ValueChanged += (ValueChangedEventArgs e) => + locoController.SetBrake(e.newValue); + } + + if( independentBrakeControl ) + { + independentBrakeControl.ValueChanged += (ValueChangedEventArgs e) => + locoController.SetIndependentBrake(e.newValue); + } + + if( throttleControl ) + { + throttleControl.ValueChanged += (ValueChangedEventArgs e) => + locoController.SetThrottle(e.newValue); + } + + if( reverserControl ) + { + if( reverserSnap ) + { + + } + reverserControl.ValueChanged += (ValueChangedEventArgs e) => + locoController.SetReverser(Mathf.RoundToInt(e.newValue * 2f) - 1); + } + + initialized = true; + yield break; + } + + protected virtual void OnDestroy() + { + if( locoController ) + { + locoController.SetThrottle(throttleControl.Value); + } + } + + protected virtual void OnEnable() + { + StartCoroutine(Init()); + } + + protected virtual void Update() + { + if( !initialized ) + { + return; + } + + float targetThrottle = locoController.targetThrottle; + if( targetThrottle != prevControllerThrottle && !throttleControl.IsGrabbedOrHoverScrolled() ) + { + throttleControl.SetValue(targetThrottle); + prevControllerThrottle = targetThrottle; + } + + float targetBrake = locoController.targetBrake; + if( targetBrake != prevControllerBrake && !brakeControl.IsGrabbedOrHoverScrolled() ) + { + brakeControl.SetValue(targetBrake); + prevControllerBrake = targetBrake; + } + + float targetIndependentBrake = locoController.targetIndependentBrake; + if( targetIndependentBrake != prevControllerIndependentBrake && !independentBrakeControl.IsGrabbedOrHoverScrolled() ) + { + independentBrakeControl.SetValue(targetIndependentBrake); + prevControllerIndependentBrake = targetIndependentBrake; + } + + float curReverser = locoController.reverser; + if( curReverser != prevControllerReverser && !reverserControl.IsGrabbedOrHoverScrolled() ) + { + reverserControl.SetValue((curReverser + 1f) / 2f); + prevControllerReverser = curReverser; + } + } + } +} \ No newline at end of file From 549916ebabb881a2365bef7d03ce51a7c8c89297 Mon Sep 17 00:00:00 2001 From: Katy Fox Date: Fri, 16 Jul 2021 15:25:29 -0400 Subject: [PATCH 11/20] Adjust script button drawing --- .../Editor/MethodButtonAttributeDrawer.cs | 22 ++- CCL_GameScripts/Editor/AddLocoParams.cs | 2 +- CCL_GameScripts/Editor/ExportTrainCar.cs | 159 +++++------------- CCL_GameScripts/TrainCarSetup.cs | 19 ++- 4 files changed, 75 insertions(+), 127 deletions(-) diff --git a/CCL_GameScripts/Attributes/Editor/MethodButtonAttributeDrawer.cs b/CCL_GameScripts/Attributes/Editor/MethodButtonAttributeDrawer.cs index 7ed51086..cfc7a86a 100644 --- a/CCL_GameScripts/Attributes/Editor/MethodButtonAttributeDrawer.cs +++ b/CCL_GameScripts/Attributes/Editor/MethodButtonAttributeDrawer.cs @@ -1,12 +1,13 @@ using UnityEngine; using UnityEditor; using System.Reflection; +using System.Text.RegularExpressions; [CustomPropertyDrawer(typeof(MethodButtonAttribute))] public class MethodButtonAttributeDrawer : PropertyDrawer { private int buttonCount; - private readonly float buttonHeight = EditorGUIUtility.singleLineHeight * 2; + private readonly float buttonHeight = EditorGUIUtility.singleLineHeight * 1.2f; private MethodButtonAttribute attr; public override void OnGUI(Rect position, SerializedProperty editorFoldout, GUIContent label) @@ -27,14 +28,16 @@ public override void OnGUI(Rect position, SerializedProperty editorFoldout, GUIC { buttonCount++; - attr = (MethodButtonAttribute)base.attribute; + attr = (MethodButtonAttribute)attribute; foreach (var name in attr.MethodNames) { buttonCount++; Rect buttonRect = new Rect(position.x, position.y + ((1 + buttonHeight) * (buttonCount - 1)), position.width, buttonHeight - 1); - if (GUI.Button(buttonRect, name)) + + string buttonText = SplitCamelCase(name); + if (GUI.Button(buttonRect, buttonText)) { InvokeMethod(editorFoldout, name); } @@ -42,6 +45,19 @@ public override void OnGUI(Rect position, SerializedProperty editorFoldout, GUIC } } + private static string SplitCamelCase( string str ) + { + return Regex.Replace( + Regex.Replace( + str, + @"(\P{Ll})(\P{Ll}\p{Ll})", + "$1 $2" + ), + @"(\p{Ll})(\P{Ll})", + "$1 $2" + ); + } + public override float GetPropertyHeight(SerializedProperty property, GUIContent label) { return EditorGUI.GetPropertyHeight(property, label, true) + (buttonHeight) * (buttonCount); diff --git a/CCL_GameScripts/Editor/AddLocoParams.cs b/CCL_GameScripts/Editor/AddLocoParams.cs index 4f80efd8..12e90222 100644 --- a/CCL_GameScripts/Editor/AddLocoParams.cs +++ b/CCL_GameScripts/Editor/AddLocoParams.cs @@ -16,7 +16,7 @@ static void Init() TrainCarSetup.LaunchLocoSetupWindow = ShowWindow; } - static void ShowWindow( TrainCarSetup trainCarSetup ) + public static void ShowWindow( TrainCarSetup trainCarSetup ) { window = GetWindow(); window.trainScript = trainCarSetup; diff --git a/CCL_GameScripts/Editor/ExportTrainCar.cs b/CCL_GameScripts/Editor/ExportTrainCar.cs index f1a3a1b2..6d1d0db6 100644 --- a/CCL_GameScripts/Editor/ExportTrainCar.cs +++ b/CCL_GameScripts/Editor/ExportTrainCar.cs @@ -27,7 +27,7 @@ public class ExportTrainCar : EditorWindow private enum state { - Main, + //Main, Settings, Export } @@ -35,16 +35,16 @@ private enum state /// /// The current state of the menu. /// - private state State = state.Main; + private state State = state.Settings; #region Exported Train Car Data - private bool loadedCar = false; + //private bool loadedCar = false; /// /// The identifier for this car. /// - private string identifier = "Custom Car"; + private string Identifier = "Custom Car"; /// /// The underlying type of this car. @@ -87,7 +87,7 @@ static void Init() TrainCarSetup.LaunchExportWindow = ShowWindow; } - static void ShowWindow(TrainCarSetup trainCarSetup) + public static void ShowWindow(TrainCarSetup trainCarSetup) { // Get existing open window or if none, make a new one: window = GetWindow(); @@ -114,8 +114,8 @@ void OnGUI() { switch (State) { - #region State.Main - + #region State.Main + /* case state.Main: GUILayout.BeginVertical("box"); GUILayout.Box("Train Car Tools", BoxStyle); @@ -145,12 +145,12 @@ void OnGUI() GUILayout.EndHorizontal(); GUILayout.EndVertical(); break; + */ + #endregion - #endregion - - #region State.Settings + #region State.Settings - case state.Settings: + case state.Settings: GUILayout.BeginVertical("box"); GUILayout.Box("Prepare TrainCar for Export", BoxStyle); @@ -163,42 +163,9 @@ void OnGUI() GUILayout.BeginVertical(); - identifier = EditorGUILayout.TextField("Identifier:", identifier); - + Identifier = EditorGUILayout.TextField("Identifier:", Identifier); TrainCarType = (BaseTrainCarType) EditorGUILayout.EnumPopup("Type of Car:", TrainCarType); - #region Positions - - //EditorStyles.label.wordWrap = true; - // EditorGUILayout.LabelField( - // "The values below are not editable because they are retrieved directly from the TrainCar script. They are only shown to you for evaluation." + - // "If you believe these values are not correct, you should check to make sure everything is correctly in position."); - - // GUILayout.Space(30); - - // GUI.enabled = false; - - // //Couplers - // FrontCouplerPosition = EditorGUILayout.Vector3Field("Front Coupler Position:", FrontCouplerPosition); - // RearCouplerPosition = EditorGUILayout.Vector3Field("Rear Coupler Position:", RearCouplerPosition); - - // //Chains - // FrontChainPosition = EditorGUILayout.Vector3Field("Front Chain Position:", FrontChainPosition); - // RearChainPosition = EditorGUILayout.Vector3Field("Rear Chain Position:", RearChainPosition); - - // FrontHosePosition = EditorGUILayout.Vector3Field("Front Hose Position:", FrontHosePosition); - // RearHosePosition = EditorGUILayout.Vector3Field("Rear Hose Position:", RearHosePosition); - - // //Buffers - // FrontBufferPosition = EditorGUILayout.Vector3Field("Front Buffer Position:", FrontBufferPosition); - // RearBufferPosition = EditorGUILayout.Vector3Field("Rear Buffer Position:", RearBufferPosition); - - // //Bogies - // FrontBogiePosition = EditorGUILayout.Vector3Field("Front Bogie Position:", FrontBogiePosition); - // RearBogiePosition = EditorGUILayout.Vector3Field("Rear Bogie Position:", RearBogiePosition); - - #endregion - EditorGUILayout.LabelField("", GUI.skin.horizontalSlider); if (_trainCarSetup != null) @@ -234,9 +201,9 @@ void OnGUI() #endregion - #region State.Export + #region State.Export - case state.Export: + case state.Export: GUILayout.BeginVertical("box"); // +1 GUILayout.Box("Export Train Car", BoxStyle); GUILayout.BeginHorizontal("box"); // +2 @@ -252,7 +219,7 @@ void OnGUI() } if (EditorUtility.DisplayDialog("Confirmation", - $"You are about to export your TrainCar named {identifier}, are you sure you want to proceed?", + $"You are about to export your TrainCar named {Identifier}, are you sure you want to proceed?", "Yes", "No")) { @@ -266,7 +233,7 @@ void OnGUI() var assetBundleFullpath = EditorUtility.SaveFolderPanel( "Export Car", ExistAtC ? cPath : (ExistAtX ? xPath : System.Environment.GetFolderPath(System.Environment.SpecialFolder.Desktop)), - identifier); + Identifier); if (assetBundleFullpath.Length != 0) { @@ -319,7 +286,7 @@ void OnGUI() BuildPipeline.BuildAssetBundles(assetBundleFullpath, trainCarBundleBuild, BuildAssetBundleOptions.None, BuildTarget.StandaloneWindows64); - Debug.Log($"Finished AssetBundle build for car: {identifier}."); + Debug.Log($"Finished AssetBundle build for car: {Identifier}."); #endregion @@ -330,15 +297,13 @@ void OnGUI() #endregion //Goto folder when finished building. - if(EditorUtility.DisplayDialog("Finished Build", $"Finished building car {identifier} to path ({assetBundleFullpath}). Would you like to open the build folder?", "Yes", "No")) + if(EditorUtility.DisplayDialog("Finished Build", $"Finished building car {Identifier} to path ({assetBundleFullpath}). Would you like to open the build folder?", "Yes", "No")) { EditorUtility.RevealInFinder(assetBundleFullpath); } //Close the window when we are done building. - //Disable because people might potentially want to export again due to an error or something. - //Close(); - + Close(); } } else @@ -388,35 +353,23 @@ void OnGUI() } - if (State != state.Main) - { - EditorGUILayout.LabelField("", GUI.skin.horizontalSlider); - - switch (State) - { - case state.Main: - if (GUILayout.Button("Close")) - { - ResetWindow(); - Close(); - } - break; - - case state.Settings: - if (GUILayout.Button("Back")) - { - ResetWindow(); - } - break; + EditorGUILayout.LabelField("", GUI.skin.horizontalSlider); - case state.Export: - if (GUILayout.Button("Back")) - { - State = state.Settings; - } - break; - } - } + if( State == state.Settings ) + { + if( GUILayout.Button("Close") ) + { + ResetWindow(); + Close(); + } + } + else + { + if (GUILayout.Button("Back")) + { + State = state.Settings; + } + } } private void ExportCarSettings( string assetBundleName, string outFilePath ) @@ -426,7 +379,7 @@ private void ExportCarSettings( string assetBundleName, string outFilePath ) jsonfile.AddField(CarJSONKeys.BUNDLE_NAME, assetBundleName); jsonfile.AddField(CarJSONKeys.PREFAB_NAME, _trainCarSetup.gameObject.name); - jsonfile.AddField(CarJSONKeys.IDENTIFIER, identifier); + jsonfile.AddField(CarJSONKeys.IDENTIFIER, Identifier); jsonfile.AddField(CarJSONKeys.CAR_TYPE, (int)TrainCarType); //Bogies @@ -461,50 +414,14 @@ private void ExportCarSettings( string assetBundleName, string outFilePath ) private void ResetWindow() { //Set state to main when - State = state.Main; + State = state.Settings; //Reset internal data _trainCarSetup = null; - //Reset loaded car so we can get data from it again. - loadedCar = false; - - #region Reset Exported Train Car Data - //Reset identifier - identifier = "Custom Car"; - - //Reset TrainCar type. + Identifier = "Custom Car"; TrainCarType = BaseTrainCarType.FlatbedEmpty; - - #region Positions - - ////Couplers - //FrontCouplerPosition = Vector3.zero; - //RearCouplerPosition = Vector3.zero; - - ////Chains - //FrontChainPosition = Vector3.zero; - //RearChainPosition = Vector3.zero; - - //FrontHosePosition = Vector3.zero; - //RearHosePosition = Vector3.zero; - - ////Buffers - //FrontBufferPosition = Vector3.zero; - //RearBufferPosition = Vector3.zero; - - ////Bogies - //FrontBogiePosition = Vector3.zero; - //RearBogiePosition = Vector3.zero; - - ////Name plates - //SidePlate1Position = Vector3.zero; - //SidePlate2Position = Vector3.zero; - - #endregion - - #endregion } } diff --git a/CCL_GameScripts/TrainCarSetup.cs b/CCL_GameScripts/TrainCarSetup.cs index ec9971b5..dc1d005f 100644 --- a/CCL_GameScripts/TrainCarSetup.cs +++ b/CCL_GameScripts/TrainCarSetup.cs @@ -45,8 +45,13 @@ private void BringUpLocoSetup() #region Helpers - [MethodButton(nameof(CreateAssetBundleForTrainCar), nameof(AlignBogieColliders))] - [SerializeField] private bool editorFoldout; + [MethodButton( + nameof(CreateAssetBundleForTrainCar), + nameof(AlignBogieColliders), + nameof(AddLocoSimulation), + nameof(ExportCar))] + [SerializeField] + private bool editorFoldout = true; public void CreateAssetBundleForTrainCar() { @@ -94,6 +99,16 @@ public void AlignBogieColliders() Undo.RecordObjects(objectsToUndo.ToArray(), "Undo Align Bogies"); } + private void ExportCar() + { + ExportTrainCar.ShowWindow(this); + } + + private void AddLocoSimulation() + { + AddLocoParams.ShowWindow(this); + } + #endregion #region Gizmos From ab161ec54427a32ef404d77b952dca3c665bf95e Mon Sep 17 00:00:00 2001 From: Katy Fox Date: Fri, 16 Jul 2021 20:12:36 -0400 Subject: [PATCH 12/20] Custom cab inputs --- CCL_GameScripts/CCL_GameScripts.csproj | 2 + .../CabControls/ControlSetupBase.cs | 18 +++ CCL_GameScripts/CabControls/LeverSetup.cs | 147 ++++++++++++++++++ DVCustomCarLoader/CustomCar.cs | 1 + DVCustomCarLoader/DVCustomCarLoader.csproj | 2 + DVCustomCarLoader/LocoComponentManager.cs | 6 + .../LocoComponents/CabControlCreator.cs | 66 ++++++++ .../LocoComponents/CustomCabInput.cs | 8 +- .../DamageControllerCustomDiesel.cs | 10 ++ .../DamageControllerCustomLoco.cs | 1 + 10 files changed, 257 insertions(+), 4 deletions(-) create mode 100644 CCL_GameScripts/CabControls/ControlSetupBase.cs create mode 100644 CCL_GameScripts/CabControls/LeverSetup.cs create mode 100644 DVCustomCarLoader/LocoComponents/CabControlCreator.cs diff --git a/CCL_GameScripts/CCL_GameScripts.csproj b/CCL_GameScripts/CCL_GameScripts.csproj index e9886e0d..871f1de5 100644 --- a/CCL_GameScripts/CCL_GameScripts.csproj +++ b/CCL_GameScripts/CCL_GameScripts.csproj @@ -52,6 +52,8 @@ + + diff --git a/CCL_GameScripts/CabControls/ControlSetupBase.cs b/CCL_GameScripts/CabControls/ControlSetupBase.cs new file mode 100644 index 00000000..70d8695f --- /dev/null +++ b/CCL_GameScripts/CabControls/ControlSetupBase.cs @@ -0,0 +1,18 @@ +using System.Collections.Generic; +using UnityEngine; + +namespace CCL_GameScripts.CabControls +{ + [DisallowMultipleComponent] + public abstract class ControlSetupBase : MonoBehaviour + { + public abstract CabControlType ControlType { get; } + public GameObject[] InteractionColliders; + } + + public enum CabControlType + { + Lever, + Button + } +} \ No newline at end of file diff --git a/CCL_GameScripts/CabControls/LeverSetup.cs b/CCL_GameScripts/CabControls/LeverSetup.cs new file mode 100644 index 00000000..f8c8d491 --- /dev/null +++ b/CCL_GameScripts/CabControls/LeverSetup.cs @@ -0,0 +1,147 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; + +namespace CCL_GameScripts.CabControls +{ + public class LeverSetup : ControlSetupBase + { + public override CabControlType ControlType => CabControlType.Lever; + + [Header("Lever")] + public bool UseNotches = true; // useSteppedJoint + public int Notches = 20; + public bool InvertDirection = false; + + [Header("Hinge Joint")] + public Vector3 JointAxis = Vector3.up; + //public bool UseLimits = true; + public float JointLimitMin = 0; + public float JointLimitMax = 90; + //public bool UseSpring = true; + public float JointSpring = 100f; + public float JointDamper = 10; + + [Header("Rigidbody")] + public float RigidbodyMass = 30; + public float RigidbodyDrag = 8; + //public float RigidbodyAngularDrag = 0; + + [Header("NonVR")] + public float HoverScrollMagnitude = 4; + public float ScrollWheelSpring = 50; + public GameObject StaticInteractionArea = null; + + [Header("VR")] + public float MaxAppliedForce = float.PositiveInfinity; + public float PullingForceMultiplier = 1; + [InspectorName("Interaction Point (optional)")] + public Transform InteractionPoint = null; + public bool VibrateAtLimit = false; + + // TODO: Audio + +#if UNITY_EDITOR + [MethodButton( + nameof(IndependentBrakeDefaults), + nameof(TrainBrakeDefaults), + nameof(DieselThrottleDefaults), + nameof(DieselReverserDefaults))] + [SerializeField] + private bool editorFoldout; + + public void IndependentBrakeDefaults() + { + UseNotches = true; + Notches = 20; + InvertDirection = false; + + JointAxis = Vector3.up; + JointLimitMin = -61; + JointLimitMax = 0; + JointSpring = 100f; + JointDamper = 10; + + RigidbodyMass = 30; + RigidbodyDrag = 8; + + HoverScrollMagnitude = 4; + ScrollWheelSpring = 50; + + MaxAppliedForce = float.PositiveInfinity; + PullingForceMultiplier = 1; + VibrateAtLimit = false; + } + + public void TrainBrakeDefaults() + { + UseNotches = true; + Notches = 20; + InvertDirection = false; + + JointAxis = Vector3.up; + JointLimitMin = 0; + JointLimitMax = 72; + JointSpring = 100f; + JointDamper = 10; + + RigidbodyMass = 30; + RigidbodyDrag = 8; + + HoverScrollMagnitude = 4; + ScrollWheelSpring = 50; + + MaxAppliedForce = float.PositiveInfinity; + PullingForceMultiplier = 1; + VibrateAtLimit = false; + } + + public void DieselThrottleDefaults() + { + UseNotches = true; + Notches = 8; + InvertDirection = false; + + JointAxis = Vector3.up; + JointLimitMin = -52; + JointLimitMax = 0; + JointSpring = 100f; + JointDamper = 0; + + RigidbodyMass = 30; + RigidbodyDrag = 16; + + HoverScrollMagnitude = 1; + ScrollWheelSpring = 100; + + MaxAppliedForce = float.PositiveInfinity; + PullingForceMultiplier = 1; + VibrateAtLimit = false; + } + + public void DieselReverserDefaults() + { + UseNotches = true; + Notches = 3; + InvertDirection = false; + + JointAxis = Vector3.up; + JointLimitMin = -25; + JointLimitMax = 65; + JointSpring = 25; + JointDamper = 2; + + RigidbodyMass = 30; + RigidbodyDrag = 0; + + HoverScrollMagnitude = 1; + ScrollWheelSpring = 8; + + MaxAppliedForce = float.PositiveInfinity; + PullingForceMultiplier = 1; + VibrateAtLimit = false; + } +#endif + } +} diff --git a/DVCustomCarLoader/CustomCar.cs b/DVCustomCarLoader/CustomCar.cs index 20894587..67cf9967 100644 --- a/DVCustomCarLoader/CustomCar.cs +++ b/DVCustomCarLoader/CustomCar.cs @@ -349,6 +349,7 @@ public void FinalizePrefab() interiorFab.SetActive(false); Object.DontDestroyOnLoad(interiorFab); + interiorFab.SetLayersRecursive("Interactable"); LocoComponentManager.SetupCabInput(interiorFab); newCar.interiorPrefab = interiorFab; diff --git a/DVCustomCarLoader/DVCustomCarLoader.csproj b/DVCustomCarLoader/DVCustomCarLoader.csproj index 91fc9ab9..496db371 100644 --- a/DVCustomCarLoader/DVCustomCarLoader.csproj +++ b/DVCustomCarLoader/DVCustomCarLoader.csproj @@ -38,6 +38,7 @@ ..\..\..\..\..\Program Files\Steam\steamapps\common\Derail Valley\DerailValley_Data\Managed\Assembly-CSharp.dll + ..\..\..\..\..\Program Files\Steam\steamapps\common\Derail Valley\DerailValley_Data\Managed\DV.Utils.dll @@ -72,6 +73,7 @@ + diff --git a/DVCustomCarLoader/LocoComponentManager.cs b/DVCustomCarLoader/LocoComponentManager.cs index f83ad54f..ae12b613 100644 --- a/DVCustomCarLoader/LocoComponentManager.cs +++ b/DVCustomCarLoader/LocoComponentManager.cs @@ -6,6 +6,7 @@ using DVCustomCarLoader.LocoComponents; using CCL_GameScripts; using HarmonyLib; +using CCL_GameScripts.CabControls; namespace DVCustomCarLoader { @@ -67,6 +68,11 @@ public static void SetupCabInput( GameObject interior ) var cabParams = interior.GetComponent(); if( cabParams ) { + foreach( var control in interior.GetComponentsInChildren() ) + { + CabControlCreator.Create(control); + } + interior.AddComponent(); } else diff --git a/DVCustomCarLoader/LocoComponents/CabControlCreator.cs b/DVCustomCarLoader/LocoComponents/CabControlCreator.cs new file mode 100644 index 00000000..78dd51b4 --- /dev/null +++ b/DVCustomCarLoader/LocoComponents/CabControlCreator.cs @@ -0,0 +1,66 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using CCL_GameScripts.CabControls; +using DV.CabControls.Spec; + +namespace DVCustomCarLoader.LocoComponents +{ + public static class CabControlCreator + { + public static void Create( ControlSetupBase control ) + { + switch( control.ControlType ) + { + case CabControlType.Lever: + CreateLever((LeverSetup)control); + break; + + default: + Main.Warning($"Unknown cab control type on {control.gameObject.name}"); + break; + } + } + + private static void ApplyBaseConfig( ControlSetupBase setup, ControlSpec spec ) + { + spec.colliderGameObjects = setup.InteractionColliders.ToArray(); + } + + private static void CreateLever( LeverSetup setup ) + { + var spec = setup.gameObject.AddComponent(); + ApplyBaseConfig(setup, spec); + + spec.useSteppedJoint = setup.UseNotches; + spec.notches = setup.Notches; + spec.invertDirection = setup.InvertDirection; + + spec.jointAxis = setup.JointAxis; + spec.jointLimitMin = setup.JointLimitMin; + spec.jointLimitMax = setup.JointLimitMax; + spec.jointSpring = setup.JointSpring; + spec.jointDamper = setup.JointDamper; + + spec.rigidbodyMass = setup.RigidbodyMass; + spec.rigidbodyDrag = setup.RigidbodyDrag; + + spec.scrollWheelHoverScroll = setup.HoverScrollMagnitude; + spec.scrollWheelSpring = setup.ScrollWheelSpring; + + spec.maxForceAppliedMagnitude = setup.MaxAppliedForce; + spec.pullingForceMultiplier = setup.PullingForceMultiplier; + spec.interactionPoint = setup.InteractionPoint; + spec.limitVibration = setup.VibrateAtLimit; + + if( setup.StaticInteractionArea ) + { + spec.nonVrStaticInteractionArea = setup.StaticInteractionArea.AddComponent(); + } + + UnityEngine.Object.Destroy(setup); + } + } +} diff --git a/DVCustomCarLoader/LocoComponents/CustomCabInput.cs b/DVCustomCarLoader/LocoComponents/CustomCabInput.cs index c4104c0c..447e6a24 100644 --- a/DVCustomCarLoader/LocoComponents/CustomCabInput.cs +++ b/DVCustomCarLoader/LocoComponents/CustomCabInput.cs @@ -59,25 +59,25 @@ private IEnumerator Init() if( config.Brake ) { brakeControl = config.Brake.GetComponent(); - brakeControl.SetValue(locoController.targetBrake); + if( brakeControl ) brakeControl.SetValue(locoController.targetBrake); } if( config.IndependentBrake ) { independentBrakeControl = config.IndependentBrake.GetComponent(); - independentBrakeControl.SetValue(locoController.targetIndependentBrake); + if( independentBrakeControl ) independentBrakeControl.SetValue(locoController.targetIndependentBrake); } if( config.Throttle ) { throttleControl = config.Throttle.GetComponent(); - throttleControl.SetValue(locoController.targetThrottle); + if( throttleControl ) throttleControl.SetValue(locoController.targetThrottle); } if( config.Reverser ) { reverserControl = config.Reverser.GetComponent(); - reverserControl.SetValue((locoController.reverser + 1f) / 2f); + if( reverserControl ) reverserControl.SetValue((locoController.reverser + 1f) / 2f); } yield return WaitFor.SecondsRealtime(1f); diff --git a/DVCustomCarLoader/LocoComponents/DamageControllerCustomDiesel.cs b/DVCustomCarLoader/LocoComponents/DamageControllerCustomDiesel.cs index a732efa9..5b696d64 100644 --- a/DVCustomCarLoader/LocoComponents/DamageControllerCustomDiesel.cs +++ b/DVCustomCarLoader/LocoComponents/DamageControllerCustomDiesel.cs @@ -245,6 +245,16 @@ public override void UpdateDebtValue( DebtComponent debt ) public override IEnumerable GetPitStopParameters() { + if( (engine == null) || (wheels == null) || (bodyDamage == null) || !bodyDamage ) + { + bool eng = (engine != null); + bool whl = (wheels != null); + bool body = (bodyDamage != null) && bodyDamage; + + Main.Error($"DamageControllerCustomDiesel: engine={eng}, wheels={whl}, body={body}"); + return Enumerable.Empty(); + } + return new[] { new PitStopRefillable(this, ResourceType.Engine_DMG, engine.HealthPercentage100Notation, 100), diff --git a/DVCustomCarLoader/LocoComponents/DamageControllerCustomLoco.cs b/DVCustomCarLoader/LocoComponents/DamageControllerCustomLoco.cs index 7717e9a7..783df1a4 100644 --- a/DVCustomCarLoader/LocoComponents/DamageControllerCustomLoco.cs +++ b/DVCustomCarLoader/LocoComponents/DamageControllerCustomLoco.cs @@ -85,6 +85,7 @@ protected void ApplyBodyParameters() ); SetBodyDamageProps(props); + Main.Log("Applied body damage properties"); } else { From f3e5da988d512fd349e5eec7dd3ced47390978ab Mon Sep 17 00:00:00 2001 From: Katy Fox Date: Sat, 17 Jul 2021 15:19:16 -0400 Subject: [PATCH 13/20] add ComponentInitSpec auto-creator --- CCL_GameScripts/CCL_GameScripts.csproj | 5 +- .../CabControls/ControlSetupBase.cs | 4 +- CCL_GameScripts/CabControls/LeverSetup.cs | 72 +++++++++- CCL_GameScripts/ComponentInitSpec.cs | 130 ++++++++++++++++++ CCL_GameScripts/TrainCarSetup.cs | 3 - DVCustomCarLoader/DVCustomCarLoader.csproj | 3 +- .../LocoComponents/CabControlCreator.cs | 66 --------- .../LocoComponentManager.cs | 15 +- 8 files changed, 218 insertions(+), 80 deletions(-) create mode 100644 CCL_GameScripts/ComponentInitSpec.cs delete mode 100644 DVCustomCarLoader/LocoComponents/CabControlCreator.cs rename DVCustomCarLoader/{ => LocoComponents}/LocoComponentManager.cs (88%) diff --git a/CCL_GameScripts/CCL_GameScripts.csproj b/CCL_GameScripts/CCL_GameScripts.csproj index 871f1de5..0c2c3a6d 100644 --- a/CCL_GameScripts/CCL_GameScripts.csproj +++ b/CCL_GameScripts/CCL_GameScripts.csproj @@ -18,7 +18,7 @@ full false bin\Debug\ - TRACE;DEBUG;UNITY_EDITOR + TRACE;DEBUG prompt 4 @@ -53,9 +53,12 @@ + + + diff --git a/CCL_GameScripts/CabControls/ControlSetupBase.cs b/CCL_GameScripts/CabControls/ControlSetupBase.cs index 70d8695f..f777d253 100644 --- a/CCL_GameScripts/CabControls/ControlSetupBase.cs +++ b/CCL_GameScripts/CabControls/ControlSetupBase.cs @@ -4,9 +4,11 @@ namespace CCL_GameScripts.CabControls { [DisallowMultipleComponent] - public abstract class ControlSetupBase : MonoBehaviour + public abstract class ControlSetupBase : ComponentInitSpec { public abstract CabControlType ControlType { get; } + + [ProxyField("colliderGameObjects")] public GameObject[] InteractionColliders; } diff --git a/CCL_GameScripts/CabControls/LeverSetup.cs b/CCL_GameScripts/CabControls/LeverSetup.cs index f8c8d491..a7aae537 100644 --- a/CCL_GameScripts/CabControls/LeverSetup.cs +++ b/CCL_GameScripts/CabControls/LeverSetup.cs @@ -7,42 +7,111 @@ namespace CCL_GameScripts.CabControls { public class LeverSetup : ControlSetupBase { + protected override string TargetTypeName => "DV.CabControls.Spec.Lever"; + protected override bool DestroyAfterCreation => true; public override CabControlType ControlType => CabControlType.Lever; [Header("Lever")] + [ProxyField("useSteppedJoint")] public bool UseNotches = true; // useSteppedJoint + [ProxyField("notches")] public int Notches = 20; + [ProxyField("invertDirection")] public bool InvertDirection = false; [Header("Hinge Joint")] + [ProxyField("jointAxis")] public Vector3 JointAxis = Vector3.up; //public bool UseLimits = true; + [ProxyField("jointLimitMin")] public float JointLimitMin = 0; + [ProxyField("jointLimitMax")] public float JointLimitMax = 90; //public bool UseSpring = true; + [ProxyField("jointSpring")] public float JointSpring = 100f; + [ProxyField("jointDamper")] public float JointDamper = 10; [Header("Rigidbody")] + [ProxyField("rigidbodyMass")] public float RigidbodyMass = 30; + [ProxyField("rigidbodyDrag")] public float RigidbodyDrag = 8; //public float RigidbodyAngularDrag = 0; [Header("NonVR")] + [ProxyField("scrollWheelHoverScroll")] public float HoverScrollMagnitude = 4; + [ProxyField("scrollWheelSpring")] public float ScrollWheelSpring = 50; + [ProxyComponent("nonVrStaticInteractionArea", "StaticInteractionArea")] public GameObject StaticInteractionArea = null; [Header("VR")] + [ProxyField("maxForceAppliedMagnitude")] public float MaxAppliedForce = float.PositiveInfinity; + [ProxyField("pullingForceMultiplier")] public float PullingForceMultiplier = 1; + [InspectorName("Interaction Point (optional)")] + [ProxyField("interactionPoint")] public Transform InteractionPoint = null; + [ProxyField("limitVibration")] public bool VibrateAtLimit = false; // TODO: Audio -#if UNITY_EDITOR + protected const float GIZMO_RADIUS = 0.1f; + protected const int GIZMO_SEGMENTS = 40; + protected static readonly Color START_COLOR = new Color(0, 0, 0.65f); + protected static readonly Color END_COLOR = new Color(0, 0.65f, 0); + + private void OnDrawGizmosSelected() + { + Color startColor = InvertDirection ? END_COLOR : START_COLOR; + Color endColor = InvertDirection ? START_COLOR : END_COLOR; + + if( UseNotches ) + { + // draw ray segments + for( int i = 0; i <= Notches; i++ ) + { + Color segmentColor = Color.Lerp(startColor, endColor, (float)i / Notches); + Vector3 rayVector = Quaternion.AngleAxis( + Mathf.Lerp(JointLimitMin, JointLimitMax, (float)i / Notches), JointAxis) + * Vector3.forward * GIZMO_RADIUS; + rayVector = transform.TransformPoint(rayVector); + + Debug.DrawLine(transform.position, rayVector, segmentColor, 0, false); + } + } + else + { + // draw semi-circle + Vector3 lastVector = transform.parent.position; + for( int i = 0; i <= GIZMO_SEGMENTS; i++ ) + { + Color segmentColor = Color.Lerp(startColor, endColor, (float)i / Notches); + Vector3 nextVector = Quaternion.AngleAxis( + Mathf.Lerp(JointLimitMin, JointLimitMax, (float)i / GIZMO_SEGMENTS), JointAxis) + * Vector3.forward * GIZMO_RADIUS; + nextVector = transform.TransformPoint(nextVector); + + if( i == 0 || i == GIZMO_SEGMENTS ) + { + Debug.DrawLine(transform.position, nextVector, segmentColor, 0, false); + } + else if( i != 0 ) + { + Debug.DrawLine(lastVector, nextVector, segmentColor, 0f, false); + } + + lastVector = nextVector; + } + } + } + [MethodButton( nameof(IndependentBrakeDefaults), nameof(TrainBrakeDefaults), @@ -142,6 +211,5 @@ public void DieselReverserDefaults() PullingForceMultiplier = 1; VibrateAtLimit = false; } -#endif } } diff --git a/CCL_GameScripts/ComponentInitSpec.cs b/CCL_GameScripts/ComponentInitSpec.cs new file mode 100644 index 00000000..ebb4a82f --- /dev/null +++ b/CCL_GameScripts/ComponentInitSpec.cs @@ -0,0 +1,130 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using UnityEngine; + +namespace CCL_GameScripts +{ + public abstract class ComponentInitSpec : MonoBehaviour + { + protected abstract string TargetTypeName { get; } + protected abstract bool DestroyAfterCreation { get; } + + // use dependency injection so that this class can be used in Unity without references to Harmony or car loader + public object CreateRealComponent( Func findTypeFunc, Action logAction = null ) + { + // *All* the reflection + if( logAction == null ) logAction = Debug.LogWarning; + Type sourceType = GetType(); + Type targetType = findTypeFunc(TargetTypeName); + + if( targetType == null ) + { + logAction($"Target of spec {sourceType.Name} ({TargetTypeName}) not found!"); + return null; + } + + var realComp = gameObject.AddComponent(targetType); + if( realComp == null || !realComp ) + { + logAction($"Failed to instantiate component of type {TargetTypeName} (from spec {sourceType.Name})"); + return null; + } + + foreach( FieldInfo sourceField in sourceType.GetFields() ) + { + var proxies = sourceField.GetCustomAttributes().OfType(); + foreach( var proxy in proxies ) + { + FieldInfo targetField = targetType.GetField(proxy.TargetName); + if( targetField != null ) + { + Type assignValueType; + object assignValue; + + if( proxy is ProxyComponentAttribute proxyComp ) + { + // create a component on the source field gameobject + if( !typeof(GameObject).Equals(sourceField.FieldType) ) + { + // can't create a component on non-gameobject + logAction($"{sourceType.Name}.{sourceField.Name} is not of type GameObject, can't proxy a component on it!"); + continue; + } + + // get the root object from the source field + GameObject componentParent = sourceField.GetValue(this) as GameObject; + if( componentParent == null || !componentParent ) + { + logAction($"{sourceType.Name}.{sourceField.Name} is null, can't proxy a component on it!"); + continue; + } + + // create the actual component that will be sent to the target + assignValueType = findTypeFunc(proxyComp.ComponentType); + if( (assignValueType != null) && typeof(Component).IsAssignableFrom(assignValueType) ) + { + assignValue = componentParent.AddComponent(assignValueType); + } + else + { + logAction($"{sourceType.Name}.{sourceField.Name} component type {proxyComp.ComponentType} not found, or not a component"); + continue; + } + } + else + { + // direct assignment + assignValueType = sourceField.FieldType; + assignValue = sourceField.GetValue(this); + } + + if( targetField.FieldType.IsAssignableFrom(assignValueType) ) + { + targetField.SetValue(realComp, assignValue); + } + else + { + logAction($"Proxy {targetType.Name}.{proxy.TargetName} is not assignable from {sourceType.Name}.{sourceField.Name}"); + } + } + else + { + logAction($"From spec type {sourceType.Name} - target {proxy.TargetName} not found on {targetType.Name}"); + } + } + } + + if( DestroyAfterCreation ) + { + Destroy(this); + } + + return realComp; + } + } + + [AttributeUsage(AttributeTargets.Field)] + public class ProxyFieldAttribute : Attribute + { + public string TargetName; + + public ProxyFieldAttribute( string proxyField ) + { + TargetName = proxyField; + } + } + + [AttributeUsage(AttributeTargets.Field)] + public class ProxyComponentAttribute : ProxyFieldAttribute + { + public string ComponentType; + + public ProxyComponentAttribute( string proxyField, string compName ) : + base(proxyField) + { + ComponentType = compName; + } + } +} diff --git a/CCL_GameScripts/TrainCarSetup.cs b/CCL_GameScripts/TrainCarSetup.cs index dc1d005f..b7c2302f 100644 --- a/CCL_GameScripts/TrainCarSetup.cs +++ b/CCL_GameScripts/TrainCarSetup.cs @@ -41,7 +41,6 @@ private void BringUpLocoSetup() public GameObject InteriorPrefab; -#if UNITY_EDITOR #region Helpers @@ -138,7 +137,5 @@ private void AddLocoSimulation() //} #endregion - -#endif } } \ No newline at end of file diff --git a/DVCustomCarLoader/DVCustomCarLoader.csproj b/DVCustomCarLoader/DVCustomCarLoader.csproj index 496db371..cf9d87dd 100644 --- a/DVCustomCarLoader/DVCustomCarLoader.csproj +++ b/DVCustomCarLoader/DVCustomCarLoader.csproj @@ -73,8 +73,8 @@ - + @@ -92,7 +92,6 @@ - diff --git a/DVCustomCarLoader/LocoComponents/CabControlCreator.cs b/DVCustomCarLoader/LocoComponents/CabControlCreator.cs deleted file mode 100644 index 78dd51b4..00000000 --- a/DVCustomCarLoader/LocoComponents/CabControlCreator.cs +++ /dev/null @@ -1,66 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using CCL_GameScripts.CabControls; -using DV.CabControls.Spec; - -namespace DVCustomCarLoader.LocoComponents -{ - public static class CabControlCreator - { - public static void Create( ControlSetupBase control ) - { - switch( control.ControlType ) - { - case CabControlType.Lever: - CreateLever((LeverSetup)control); - break; - - default: - Main.Warning($"Unknown cab control type on {control.gameObject.name}"); - break; - } - } - - private static void ApplyBaseConfig( ControlSetupBase setup, ControlSpec spec ) - { - spec.colliderGameObjects = setup.InteractionColliders.ToArray(); - } - - private static void CreateLever( LeverSetup setup ) - { - var spec = setup.gameObject.AddComponent(); - ApplyBaseConfig(setup, spec); - - spec.useSteppedJoint = setup.UseNotches; - spec.notches = setup.Notches; - spec.invertDirection = setup.InvertDirection; - - spec.jointAxis = setup.JointAxis; - spec.jointLimitMin = setup.JointLimitMin; - spec.jointLimitMax = setup.JointLimitMax; - spec.jointSpring = setup.JointSpring; - spec.jointDamper = setup.JointDamper; - - spec.rigidbodyMass = setup.RigidbodyMass; - spec.rigidbodyDrag = setup.RigidbodyDrag; - - spec.scrollWheelHoverScroll = setup.HoverScrollMagnitude; - spec.scrollWheelSpring = setup.ScrollWheelSpring; - - spec.maxForceAppliedMagnitude = setup.MaxAppliedForce; - spec.pullingForceMultiplier = setup.PullingForceMultiplier; - spec.interactionPoint = setup.InteractionPoint; - spec.limitVibration = setup.VibrateAtLimit; - - if( setup.StaticInteractionArea ) - { - spec.nonVrStaticInteractionArea = setup.StaticInteractionArea.AddComponent(); - } - - UnityEngine.Object.Destroy(setup); - } - } -} diff --git a/DVCustomCarLoader/LocoComponentManager.cs b/DVCustomCarLoader/LocoComponents/LocoComponentManager.cs similarity index 88% rename from DVCustomCarLoader/LocoComponentManager.cs rename to DVCustomCarLoader/LocoComponents/LocoComponentManager.cs index ae12b613..5e17b64f 100644 --- a/DVCustomCarLoader/LocoComponentManager.cs +++ b/DVCustomCarLoader/LocoComponents/LocoComponentManager.cs @@ -68,11 +68,7 @@ public static void SetupCabInput( GameObject interior ) var cabParams = interior.GetComponent(); if( cabParams ) { - foreach( var control in interior.GetComponentsInChildren() ) - { - CabControlCreator.Create(control); - } - + CreateComponentsFromProxies(interior); interior.AddComponent(); } else @@ -80,6 +76,15 @@ public static void SetupCabInput( GameObject interior ) Main.Warning("Loco has an interior prefab, but no cab input setup"); } } + + public static void CreateComponentsFromProxies( GameObject root ) + { + var allInitSpecs = root.GetComponentsInChildren(); + foreach( var compSpec in allInitSpecs ) + { + compSpec.CreateRealComponent(AccessTools.TypeByName, Main.Warning); + } + } } [HarmonyPatch(typeof(TrainCar), "LoadInterior")] From 00a5e13919513e5363be9f561b20952bd7560f40 Mon Sep 17 00:00:00 2001 From: Katy Fox Date: Sun, 18 Jul 2021 18:38:41 -0400 Subject: [PATCH 14/20] CopiedCabControls --- CCL_GameScripts/CCL_GameScripts.csproj | 4 +- CCL_GameScripts/CabControls/CabInputSetup.cs | 39 +++++++ .../CabControls/CopiedCabControl.cs | 21 ++++ CCL_GameScripts/CabControls/CopiedLever.cs | 108 ++++++++++++++++++ CCL_GameScripts/CabControls/LeverSetup.cs | 12 +- CCL_GameScripts/CabInputSetup.cs | 13 --- .../LocoComponents/CustomCabInput.cs | 2 +- .../LocoComponents/LocoComponentManager.cs | 59 +++++++++- 8 files changed, 236 insertions(+), 22 deletions(-) create mode 100644 CCL_GameScripts/CabControls/CabInputSetup.cs create mode 100644 CCL_GameScripts/CabControls/CopiedCabControl.cs create mode 100644 CCL_GameScripts/CabControls/CopiedLever.cs delete mode 100644 CCL_GameScripts/CabInputSetup.cs diff --git a/CCL_GameScripts/CCL_GameScripts.csproj b/CCL_GameScripts/CCL_GameScripts.csproj index 0c2c3a6d..6c67d00c 100644 --- a/CCL_GameScripts/CCL_GameScripts.csproj +++ b/CCL_GameScripts/CCL_GameScripts.csproj @@ -52,11 +52,13 @@ + + + - diff --git a/CCL_GameScripts/CabControls/CabInputSetup.cs b/CCL_GameScripts/CabControls/CabInputSetup.cs new file mode 100644 index 00000000..521829e0 --- /dev/null +++ b/CCL_GameScripts/CabControls/CabInputSetup.cs @@ -0,0 +1,39 @@ +using System.Collections; +using UnityEngine; + +namespace CCL_GameScripts.CabControls +{ + public class CabInputSetup : MonoBehaviour + { + public GameObject Brake; + public GameObject IndependentBrake; + public GameObject Reverser; + public GameObject Throttle; + + public void SetInputObject( CabInputType inputType, GameObject controlRoot ) + { + switch( inputType ) + { + case CabInputType.TrainBrake: + Brake = controlRoot; + break; + + case CabInputType.IndependentBrake: + IndependentBrake = controlRoot; + break; + + case CabInputType.Reverser: + Reverser = controlRoot; + break; + + case CabInputType.Throttle: + Throttle = controlRoot; + break; + + default: + Debug.LogWarning("Tried to set input of unsupported type"); + break; + } + } + } +} \ No newline at end of file diff --git a/CCL_GameScripts/CabControls/CopiedCabControl.cs b/CCL_GameScripts/CabControls/CopiedCabControl.cs new file mode 100644 index 00000000..bd553266 --- /dev/null +++ b/CCL_GameScripts/CabControls/CopiedCabControl.cs @@ -0,0 +1,21 @@ +using System.Collections; +using UnityEngine; + +namespace CCL_GameScripts.CabControls +{ + public abstract class CopiedCabControl : MonoBehaviour + { + public CabInputType InputBinding; + + public abstract (BaseTrainCarType, string) GetSourceObject(); + } + + public enum CabInputType + { + IndependentBrake, + TrainBrake, + Throttle, + Reverser, + Horn + } +} \ No newline at end of file diff --git a/CCL_GameScripts/CabControls/CopiedLever.cs b/CCL_GameScripts/CabControls/CopiedLever.cs new file mode 100644 index 00000000..014b2b7a --- /dev/null +++ b/CCL_GameScripts/CabControls/CopiedLever.cs @@ -0,0 +1,108 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; + +namespace CCL_GameScripts.CabControls +{ + public class CopiedLever : CopiedCabControl + { + protected static readonly (BaseTrainCarType, string)[] TargetObjects = + new [] + { + (BaseTrainCarType.LocoShunter, "C independent_brake_lever"), + (BaseTrainCarType.LocoShunter, "C train_brake_lever"), + (BaseTrainCarType.LocoShunter, "C throttle"), + (BaseTrainCarType.LocoShunter, "C reverser"), + (BaseTrainCarType.LocoShunter, "C horn"), + + (BaseTrainCarType.LocoDiesel, "C independent_brake_lever"), + (BaseTrainCarType.LocoDiesel, "C train_brake_lever"), + (BaseTrainCarType.LocoDiesel, "C throttle"), + (BaseTrainCarType.LocoDiesel, "C reverser"), + (BaseTrainCarType.LocoDiesel, "C horn"), + }; + + protected static readonly LeverGizmoInfo[] GizmoData = + new[] + { + new LeverGizmoInfo(-61, 0, 20), + new LeverGizmoInfo(0, 72, 20), + new LeverGizmoInfo(-52, 1, 8, true), + new LeverGizmoInfo(-25, 65, 3, true), + new LeverGizmoInfo(-27, 27, 3), + + new LeverGizmoInfo(-88.429f, 0, 20, true), + new LeverGizmoInfo(-90, 0, 20, true), + new LeverGizmoInfo(-78.815f, 0, 8, true), + new LeverGizmoInfo(-65.2f, 0, 3, true), + new LeverGizmoInfo(-12, 12, 3), + }; + + public CopiedLeverType LeverType; + + public override (BaseTrainCarType, string) GetSourceObject() + { + return TargetObjects[(int)LeverType]; + } + + protected const float GIZMO_RADIUS = 0.1f; + protected const int GIZMO_SEGMENTS = 40; + protected static readonly Color START_COLOR = new Color(0.65f, 0, 0); + protected static readonly Color END_COLOR = new Color(0, 0.65f, 0); + + private void OnDrawGizmosSelected() + { + var gizmo = GizmoData[(int)LeverType]; + + Color startColor = gizmo.Inverted ? END_COLOR : START_COLOR; + Color endColor = gizmo.Inverted ? START_COLOR : END_COLOR; + + // draw ray segments + for( int i = 0; i <= gizmo.Notches; i++ ) + { + Color segmentColor = Color.Lerp(startColor, endColor, (float)i / gizmo.Notches); + Vector3 rayVector = Quaternion.AngleAxis( + Mathf.Lerp(gizmo.LimitMin, gizmo.LimitMax, (float)i / gizmo.Notches), Vector3.up) + * Vector3.forward * GIZMO_RADIUS; + rayVector = transform.TransformPoint(rayVector); + + Gizmos.color = segmentColor; + Gizmos.DrawLine(transform.position, rayVector); + } + } + + protected class LeverGizmoInfo + { + public float LimitMin; + public float LimitMax; + public int Notches; + public bool Inverted; + + public LeverGizmoInfo( float min, float max, int notches, bool invert = false ) + { + LimitMin = min; + LimitMax = max; + Notches = notches; + Inverted = invert; + } + } + } + + public enum CopiedLeverType + { + IndependentBrakeShunter, + TrainBrakeShunter, + ThrottleShunter, + ReverserShunter, + HornShunter, + + IndependentBrakeDE6, + TrainBrakeDE6, + ThrottleDE6, + ReverserDE6, + HornDE6, + } +} diff --git a/CCL_GameScripts/CabControls/LeverSetup.cs b/CCL_GameScripts/CabControls/LeverSetup.cs index a7aae537..e9307726 100644 --- a/CCL_GameScripts/CabControls/LeverSetup.cs +++ b/CCL_GameScripts/CabControls/LeverSetup.cs @@ -64,7 +64,7 @@ public class LeverSetup : ControlSetupBase protected const float GIZMO_RADIUS = 0.1f; protected const int GIZMO_SEGMENTS = 40; - protected static readonly Color START_COLOR = new Color(0, 0, 0.65f); + protected static readonly Color START_COLOR = new Color(0.65f, 0, 0); protected static readonly Color END_COLOR = new Color(0, 0.65f, 0); private void OnDrawGizmosSelected() @@ -77,13 +77,13 @@ private void OnDrawGizmosSelected() // draw ray segments for( int i = 0; i <= Notches; i++ ) { - Color segmentColor = Color.Lerp(startColor, endColor, (float)i / Notches); + Gizmos.color = Color.Lerp(startColor, endColor, (float)i / Notches); Vector3 rayVector = Quaternion.AngleAxis( Mathf.Lerp(JointLimitMin, JointLimitMax, (float)i / Notches), JointAxis) * Vector3.forward * GIZMO_RADIUS; rayVector = transform.TransformPoint(rayVector); - Debug.DrawLine(transform.position, rayVector, segmentColor, 0, false); + Gizmos.DrawLine(transform.position, rayVector); } } else @@ -92,7 +92,7 @@ private void OnDrawGizmosSelected() Vector3 lastVector = transform.parent.position; for( int i = 0; i <= GIZMO_SEGMENTS; i++ ) { - Color segmentColor = Color.Lerp(startColor, endColor, (float)i / Notches); + Gizmos.color = Color.Lerp(startColor, endColor, (float)i / Notches); Vector3 nextVector = Quaternion.AngleAxis( Mathf.Lerp(JointLimitMin, JointLimitMax, (float)i / GIZMO_SEGMENTS), JointAxis) * Vector3.forward * GIZMO_RADIUS; @@ -100,11 +100,11 @@ private void OnDrawGizmosSelected() if( i == 0 || i == GIZMO_SEGMENTS ) { - Debug.DrawLine(transform.position, nextVector, segmentColor, 0, false); + Gizmos.DrawLine(transform.position, nextVector); } else if( i != 0 ) { - Debug.DrawLine(lastVector, nextVector, segmentColor, 0f, false); + Gizmos.DrawLine(lastVector, nextVector); } lastVector = nextVector; diff --git a/CCL_GameScripts/CabInputSetup.cs b/CCL_GameScripts/CabInputSetup.cs deleted file mode 100644 index a87a06e0..00000000 --- a/CCL_GameScripts/CabInputSetup.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System.Collections; -using UnityEngine; - -namespace CCL_GameScripts -{ - public class CabInputSetup : MonoBehaviour - { - public GameObject Brake; - public GameObject IndependentBrake; - public GameObject Reverser; - public GameObject Throttle; - } -} \ No newline at end of file diff --git a/DVCustomCarLoader/LocoComponents/CustomCabInput.cs b/DVCustomCarLoader/LocoComponents/CustomCabInput.cs index 447e6a24..9879f4bc 100644 --- a/DVCustomCarLoader/LocoComponents/CustomCabInput.cs +++ b/DVCustomCarLoader/LocoComponents/CustomCabInput.cs @@ -1,5 +1,5 @@ using System.Collections; -using CCL_GameScripts; +using CCL_GameScripts.CabControls; using DV.CabControls; using UnityEngine; diff --git a/DVCustomCarLoader/LocoComponents/LocoComponentManager.cs b/DVCustomCarLoader/LocoComponents/LocoComponentManager.cs index 5e17b64f..dfe42d78 100644 --- a/DVCustomCarLoader/LocoComponents/LocoComponentManager.cs +++ b/DVCustomCarLoader/LocoComponents/LocoComponentManager.cs @@ -25,6 +25,9 @@ public static void AddLocoSimulation( GameObject prefab, SimParamsBase simParams } } + private static Dictionary InteriorPrefabCache = + new Dictionary(); + // Order to add components: // - Simulation // - SimulationEvents @@ -69,7 +72,8 @@ public static void SetupCabInput( GameObject interior ) if( cabParams ) { CreateComponentsFromProxies(interior); - interior.AddComponent(); + CreateCopiedControls(interior, cabParams); + var cabInput = interior.AddComponent(); } else { @@ -85,6 +89,59 @@ public static void CreateComponentsFromProxies( GameObject root ) compSpec.CreateRealComponent(AccessTools.TypeByName, Main.Warning); } } + + public static void CreateCopiedControls( GameObject root, CabInputSetup cabSetup ) + { + var allCopySpecs = root.GetComponentsInChildren(); + foreach( var copySpec in allCopySpecs ) + { + GameObject parent = copySpec.gameObject; + (BaseTrainCarType carType, string sourceObjName) = copySpec.GetSourceObject(); + + GameObject sourceInterior = GetTrainCarInterior((TrainCarType)carType); + if( !sourceInterior ) continue; + + Transform sourceChild = sourceInterior.transform.Find(sourceObjName); + if( sourceChild ) + { + GameObject newControl = UnityEngine.Object.Instantiate(sourceChild.gameObject, parent.transform); + newControl.transform.localPosition = Vector3.zero; + newControl.transform.localRotation = Quaternion.identity; + cabSetup.SetInputObject(copySpec.InputBinding, newControl); + } + } + } + + public static GameObject GetTrainCarInterior( TrainCarType carType ) + { + if( !InteriorPrefabCache.TryGetValue(carType, out GameObject interior) ) + { + var prefab = CarTypes.GetCarPrefab(carType); + if( !prefab ) + { + Main.Error($"CarType {carType} has missing prefab"); + return null; + } + + TrainCar car = prefab.GetComponent(); + if( !car ) + { + Main.Warning($"Couldn't find TrainCar on carType {prefab.name}"); + return null; + } + + if( car.interiorPrefab == null || !car.interiorPrefab ) + { + Main.Warning($"TrainCar on carType {prefab.name} doesn't have an interiorPrefab assigned"); + return null; + } + + interior = car.interiorPrefab; + InteriorPrefabCache.Add(carType, interior); + } + + return interior; + } } [HarmonyPatch(typeof(TrainCar), "LoadInterior")] From 4d36b9dbc067f0475651c09ada924904771438db Mon Sep 17 00:00:00 2001 From: Katy Fox Date: Tue, 20 Jul 2021 19:58:53 -0400 Subject: [PATCH 15/20] Add custom indicators --- CCL_GameScripts/CCL_GameScripts.csproj | 2 + CCL_GameScripts/CabControls/CabIOTypes.cs | 22 +++++ .../CabControls/ControlSetupBase.cs | 2 + .../CabControls/CopiedCabControl.cs | 15 +-- CCL_GameScripts/CabControls/CopiedGauge.cs | 97 +++++++++++++++++++ CCL_GameScripts/CabControls/CopiedLever.cs | 2 +- CCL_GameScripts/CabControls/GaugeSetup.cs | 56 +++++++++++ .../CabControls/IndicatorSetupBase.cs | 22 +++++ CCL_GameScripts/CabControls/LeverSetup.cs | 2 +- CCL_GameScripts/Editor/FBXMeshExtractor.cs | 9 +- CCL_GameScripts/SimParamsDiesel.cs | 3 + DVCustomCarLoader/CustomCar.cs | 3 +- DVCustomCarLoader/DVCustomCarLoader.csproj | 1 + .../LocoComponents/CustomCabIndicators.cs | 58 +++++++++++ .../LocoComponents/CustomCabInput.cs | 5 - .../LocoComponents/CustomLocoController.cs | 25 ++++- .../CustomLocoControllerDiesel.cs | 69 +++++++++---- .../LocoComponents/LocoComponentManager.cs | 79 +++++++++++++-- 18 files changed, 419 insertions(+), 53 deletions(-) create mode 100644 CCL_GameScripts/CabControls/CabIOTypes.cs create mode 100644 CCL_GameScripts/CabControls/CopiedGauge.cs create mode 100644 CCL_GameScripts/CabControls/GaugeSetup.cs create mode 100644 CCL_GameScripts/CabControls/IndicatorSetupBase.cs create mode 100644 DVCustomCarLoader/LocoComponents/CustomCabIndicators.cs diff --git a/CCL_GameScripts/CCL_GameScripts.csproj b/CCL_GameScripts/CCL_GameScripts.csproj index 6c67d00c..796ffa28 100644 --- a/CCL_GameScripts/CCL_GameScripts.csproj +++ b/CCL_GameScripts/CCL_GameScripts.csproj @@ -53,8 +53,10 @@ + + diff --git a/CCL_GameScripts/CabControls/CabIOTypes.cs b/CCL_GameScripts/CabControls/CabIOTypes.cs new file mode 100644 index 00000000..c31635dd --- /dev/null +++ b/CCL_GameScripts/CabControls/CabIOTypes.cs @@ -0,0 +1,22 @@ +namespace CCL_GameScripts.CabControls +{ + public enum CabInputType + { + IndependentBrake, + TrainBrake, + Throttle, + Reverser, + Horn + } + + public enum CabIndicatorType + { + BrakePipe, + BrakeReservoir, + EngineTemp, + Fuel, + Oil, + Sand, + Speed + } +} \ No newline at end of file diff --git a/CCL_GameScripts/CabControls/ControlSetupBase.cs b/CCL_GameScripts/CabControls/ControlSetupBase.cs index f777d253..16886b1e 100644 --- a/CCL_GameScripts/CabControls/ControlSetupBase.cs +++ b/CCL_GameScripts/CabControls/ControlSetupBase.cs @@ -8,6 +8,8 @@ public abstract class ControlSetupBase : ComponentInitSpec { public abstract CabControlType ControlType { get; } + public CabInputType InputBinding; + [ProxyField("colliderGameObjects")] public GameObject[] InteractionColliders; } diff --git a/CCL_GameScripts/CabControls/CopiedCabControl.cs b/CCL_GameScripts/CabControls/CopiedCabControl.cs index bd553266..a8bbe293 100644 --- a/CCL_GameScripts/CabControls/CopiedCabControl.cs +++ b/CCL_GameScripts/CabControls/CopiedCabControl.cs @@ -5,17 +5,18 @@ namespace CCL_GameScripts.CabControls { public abstract class CopiedCabControl : MonoBehaviour { - public CabInputType InputBinding; + public bool ReplaceThisObject = false; public abstract (BaseTrainCarType, string) GetSourceObject(); } - public enum CabInputType + public abstract class CopiedCabInput : CopiedCabControl + { + public CabInputType InputBinding; + } + + public abstract class CopiedCabIndicator : CopiedCabControl { - IndependentBrake, - TrainBrake, - Throttle, - Reverser, - Horn + public CabIndicatorType OutputBinding; } } \ No newline at end of file diff --git a/CCL_GameScripts/CabControls/CopiedGauge.cs b/CCL_GameScripts/CabControls/CopiedGauge.cs new file mode 100644 index 00000000..1d45419f --- /dev/null +++ b/CCL_GameScripts/CabControls/CopiedGauge.cs @@ -0,0 +1,97 @@ +using System.Collections; +using UnityEngine; + +namespace CCL_GameScripts.CabControls +{ + public class CopiedGauge : CopiedCabIndicator + { + protected static readonly (BaseTrainCarType, string)[] TargetObjects = + new[] + { + (BaseTrainCarType.LocoShunter, "I brake_pipe_meter"), + (BaseTrainCarType.LocoShunter, "I brake_aux_res_meter"), + (BaseTrainCarType.LocoShunter, "I speedometer"), + (BaseTrainCarType.LocoShunter, "C dashboard indicators controller/I engine_temp_meter"), + (BaseTrainCarType.LocoShunter, "C dashboard indicators controller/I sand_meter"), + (BaseTrainCarType.LocoShunter, "C dashboard indicators controller/I fuel_meter"), + (BaseTrainCarType.LocoShunter, "C dashboard indicators controller/I oil_meter"), + }; + + protected static readonly GaugeGizmoInfo[] GizmoData = + new[] + { + new GaugeGizmoInfo(-200, 20, 0.025f), + new GaugeGizmoInfo(-200, 20, 0.025f), + new GaugeGizmoInfo(-226, 46, 0.05f), + + new GaugeGizmoInfo(-225, 45, 0.045f), + new GaugeGizmoInfo(-225, 45, 0.025f), + new GaugeGizmoInfo(-225, 45, 0.025f), + new GaugeGizmoInfo(-225, 45, 0.025f), + }; + + public CopiedGaugeType GaugeType; + + public override (BaseTrainCarType, string) GetSourceObject() + { + return TargetObjects[(int)GaugeType]; + } + + public CopiedGauge() + { + ReplaceThisObject = true; + } + + protected const int GIZMO_SEGMENTS = 40; + protected static readonly Color START_COLOR = new Color(0.65f, 0, 0); + protected static readonly Color END_COLOR = new Color(0, 0.65f, 0); + + private void OnDrawGizmosSelected() + { + var gizmo = GizmoData[(int)GaugeType]; + + Vector3 lastVector = transform.position; + for( int i = 0; i <= GIZMO_SEGMENTS; i++ ) + { + Gizmos.color = Color.Lerp(START_COLOR, END_COLOR, (float)i / GIZMO_SEGMENTS); + Vector3 nextVector = Quaternion.AngleAxis( + Mathf.Lerp(gizmo.LimitMin, gizmo.LimitMax, (float)i / GIZMO_SEGMENTS), Vector3.up) + * Vector3.right * gizmo.Radius; + nextVector = transform.TransformPoint(nextVector); + + Gizmos.DrawLine(transform.position, nextVector); + if( i != 0 ) + { + Gizmos.DrawLine(lastVector, nextVector); + } + + lastVector = nextVector; + } + } + + protected class GaugeGizmoInfo + { + public float LimitMin; + public float LimitMax; + public float Radius; + + public GaugeGizmoInfo( float min, float max, float radius ) + { + LimitMin = min; + LimitMax = max; + Radius = radius; + } + } + } + + public enum CopiedGaugeType + { + DE2BrakePipeMeter, + DE2BrakeAuxResMeter, + DE2Speedometer, + DE2EngineTempMeter, + DE2SandMeter, + DE2FuelMeter, + DE2OilMeter + } +} \ No newline at end of file diff --git a/CCL_GameScripts/CabControls/CopiedLever.cs b/CCL_GameScripts/CabControls/CopiedLever.cs index 014b2b7a..92f7ef36 100644 --- a/CCL_GameScripts/CabControls/CopiedLever.cs +++ b/CCL_GameScripts/CabControls/CopiedLever.cs @@ -7,7 +7,7 @@ namespace CCL_GameScripts.CabControls { - public class CopiedLever : CopiedCabControl + public class CopiedLever : CopiedCabInput { protected static readonly (BaseTrainCarType, string)[] TargetObjects = new [] diff --git a/CCL_GameScripts/CabControls/GaugeSetup.cs b/CCL_GameScripts/CabControls/GaugeSetup.cs new file mode 100644 index 00000000..5548d835 --- /dev/null +++ b/CCL_GameScripts/CabControls/GaugeSetup.cs @@ -0,0 +1,56 @@ +using System.Collections; +using UnityEngine; + +namespace CCL_GameScripts.CabControls +{ + public class GaugeSetup : IndicatorSetupBase + { + protected override string TargetTypeName => "IndicatorGauge"; + public override IndicatorType IndicatorType => IndicatorType.Gauge; + + [ProxyField("unclamped")] + public bool Unclamped = false; + [ProxyField("minAngle")] + public float MinAngle = -180; + [ProxyField("maxAngle")] + public float MaxAngle = 180; + + [ProxyField("rotationAxis")] + public Vector3 RotationAxis = Vector3.forward; + [ProxyField("needle")] + public Transform Needle; + + protected const float GIZMO_RADIUS = 0.1f; + protected const int GIZMO_SEGMENTS = 20; + protected static readonly Color START_COLOR = new Color(0, 0, 0.65f); + protected static readonly Color END_COLOR = new Color(0.65f, 0, 0); + + private void OnDrawGizmosSelected() + { + if( !Needle ) + { + return; + } + + Vector3 lastVector = Vector3.zero; + for( int i = 0; i <= GIZMO_SEGMENTS; i++ ) + { + Color segmentColor = Color.Lerp(START_COLOR, END_COLOR, (float)i / GIZMO_SEGMENTS); + Vector3 nextVector = Quaternion.AngleAxis(Mathf.Lerp(MinAngle, MaxAngle, (float)i / GIZMO_SEGMENTS), RotationAxis) * Vector3.right * GIZMO_RADIUS; + nextVector = transform.TransformPoint(nextVector); + + if( i == 0 || i == GIZMO_SEGMENTS ) + { + Debug.DrawLine(Needle.position, nextVector, segmentColor, 0f, false); + } + + if( i != 0 ) + { + Debug.DrawLine(lastVector, nextVector, segmentColor, 0f, false); + } + + lastVector = nextVector; + } + } + } +} \ No newline at end of file diff --git a/CCL_GameScripts/CabControls/IndicatorSetupBase.cs b/CCL_GameScripts/CabControls/IndicatorSetupBase.cs new file mode 100644 index 00000000..0d08531b --- /dev/null +++ b/CCL_GameScripts/CabControls/IndicatorSetupBase.cs @@ -0,0 +1,22 @@ +using UnityEngine; + +namespace CCL_GameScripts.CabControls +{ + public abstract class IndicatorSetupBase : ComponentInitSpec + { + protected override bool DestroyAfterCreation => true; + public abstract IndicatorType IndicatorType { get; } + + public CabIndicatorType OutputBinding; + + [ProxyField("minValue")] + public float MinValue = 0; + [ProxyField("maxValue")] + public float MaxValue = 1; + } + + public enum IndicatorType + { + Gauge + } +} \ No newline at end of file diff --git a/CCL_GameScripts/CabControls/LeverSetup.cs b/CCL_GameScripts/CabControls/LeverSetup.cs index e9307726..aecbfe73 100644 --- a/CCL_GameScripts/CabControls/LeverSetup.cs +++ b/CCL_GameScripts/CabControls/LeverSetup.cs @@ -89,7 +89,7 @@ private void OnDrawGizmosSelected() else { // draw semi-circle - Vector3 lastVector = transform.parent.position; + Vector3 lastVector = transform.position; for( int i = 0; i <= GIZMO_SEGMENTS; i++ ) { Gizmos.color = Color.Lerp(startColor, endColor, (float)i / Notches); diff --git a/CCL_GameScripts/Editor/FBXMeshExtractor.cs b/CCL_GameScripts/Editor/FBXMeshExtractor.cs index 45e7daa3..4acc68d4 100644 --- a/CCL_GameScripts/Editor/FBXMeshExtractor.cs +++ b/CCL_GameScripts/Editor/FBXMeshExtractor.cs @@ -38,17 +38,10 @@ private static void ExtractMeshes( Object selectedObject ) string parentfolderPath = selectedObjectPath.Substring(0, selectedObjectPath.Length - (selectedObject.name.Length + 5)); string objectFolderName = selectedObject.name; string objectFolderPath = parentfolderPath + "/" + objectFolderName; - string meshFolderName = "Meshes"; - string meshFolderPath = objectFolderPath + "/" + meshFolderName; if( !AssetDatabase.IsValidFolder(objectFolderPath) ) { AssetDatabase.CreateFolder(parentfolderPath, objectFolderName); - - if( !AssetDatabase.IsValidFolder(meshFolderPath) ) - { - AssetDatabase.CreateFolder(objectFolderPath, meshFolderName); - } } //Create Meshes @@ -62,7 +55,7 @@ private static void ExtractMeshes( Object selectedObject ) Mesh mesh = Object.Instantiate(objects[i]) as Mesh; - AssetDatabase.CreateAsset(mesh, meshFolderPath + "/" + objects[i].name + _targetExtension); + AssetDatabase.CreateAsset(mesh, objectFolderPath + "/" + objects[i].name + _targetExtension); } } diff --git a/CCL_GameScripts/SimParamsDiesel.cs b/CCL_GameScripts/SimParamsDiesel.cs index 0f931408..38ebda4d 100644 --- a/CCL_GameScripts/SimParamsDiesel.cs +++ b/CCL_GameScripts/SimParamsDiesel.cs @@ -39,6 +39,9 @@ public class SimParamsDiesel : SimParamsBase public float OilConsumptionEngineRpm = 1; //public float OilConsumptionWheels = 0.12f; + [Header("Other Equipment")] + public float AirCompressorRate = 0.8f; + public SimParamsDiesel() { ApplyDE6Defaults(); diff --git a/DVCustomCarLoader/CustomCar.cs b/DVCustomCarLoader/CustomCar.cs index 67cf9967..2e57467a 100644 --- a/DVCustomCarLoader/CustomCar.cs +++ b/DVCustomCarLoader/CustomCar.cs @@ -349,8 +349,9 @@ public void FinalizePrefab() interiorFab.SetActive(false); Object.DontDestroyOnLoad(interiorFab); + LocoComponentManager.SetupCabComponents(interiorFab); interiorFab.SetLayersRecursive("Interactable"); - LocoComponentManager.SetupCabInput(interiorFab); + newCar.interiorPrefab = interiorFab; InteriorPrefab = interiorFab; diff --git a/DVCustomCarLoader/DVCustomCarLoader.csproj b/DVCustomCarLoader/DVCustomCarLoader.csproj index cf9d87dd..fccc4c74 100644 --- a/DVCustomCarLoader/DVCustomCarLoader.csproj +++ b/DVCustomCarLoader/DVCustomCarLoader.csproj @@ -73,6 +73,7 @@ + diff --git a/DVCustomCarLoader/LocoComponents/CustomCabIndicators.cs b/DVCustomCarLoader/LocoComponents/CustomCabIndicators.cs new file mode 100644 index 00000000..c529701c --- /dev/null +++ b/DVCustomCarLoader/LocoComponents/CustomCabIndicators.cs @@ -0,0 +1,58 @@ +using UnityEngine; +using CCL_GameScripts.CabControls; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace DVCustomCarLoader.LocoComponents +{ + public class CustomCabIndicators : MonoBehaviour + { + protected CustomLocoController locoController; + + public IndicatorInfo[] Indicators; + + //public void AddIndicators( IEnumerable<(CabIndicatorType, Indicator)> comps ) + //{ + // if( Indicators == null ) Indicators = new List(); + + // Indicators.AddRange(comps.Select(c => new IndicatorInfo(c.Item1, c.Item2))); + //} + + protected virtual void Start() + { + locoController = TrainCar.Resolve(gameObject).GetComponent(); + + Indicators = GetComponentsInChildren(); + + Main.Log($"CustomCabIndicators Start - {Indicators.Length} indicators"); + foreach( var indicator in Indicators ) + { + indicator.GetValue = locoController.GetIndicatorFunc(indicator.Type); + } + } + + protected virtual void Update() + { + foreach( var indicator in Indicators ) + { + indicator.Indicator.value = indicator.GetValue(); + } + } + } + + public class IndicatorInfo : MonoBehaviour + { + public CabIndicatorType Type; + public Indicator Indicator; + + [NonSerialized] + public Func GetValue; + + public void Initialize( CabIndicatorType type, Indicator indicator ) + { + Type = type; + Indicator = indicator; + } + } +} \ No newline at end of file diff --git a/DVCustomCarLoader/LocoComponents/CustomCabInput.cs b/DVCustomCarLoader/LocoComponents/CustomCabInput.cs index 9879f4bc..14aa549e 100644 --- a/DVCustomCarLoader/LocoComponents/CustomCabInput.cs +++ b/DVCustomCarLoader/LocoComponents/CustomCabInput.cs @@ -10,11 +10,6 @@ public class CustomCabInput : CabInput protected CustomLocoController locoController; protected CabInputSetup config; - //public GameObject brake; - //public GameObject independentBrake; - //public GameObject reverser; - //public GameObject throttle; - public bool reverserSnap; protected ControlImplBase brakeControl; diff --git a/DVCustomCarLoader/LocoComponents/CustomLocoController.cs b/DVCustomCarLoader/LocoComponents/CustomLocoController.cs index e71928ed..c355ffff 100644 --- a/DVCustomCarLoader/LocoComponents/CustomLocoController.cs +++ b/DVCustomCarLoader/LocoComponents/CustomLocoController.cs @@ -1,4 +1,6 @@ -using System.Collections; +using System; +using System.Collections; +using CCL_GameScripts.CabControls; using DV; using DV.ServicePenalty; using UnityEngine; @@ -11,6 +13,27 @@ public abstract class CustomLocoController : LocoControllerBase protected DebtTrackerCustomLoco locoDebt; protected CarVisitChecker carVisitChecker; + + public float GetBrakePipePressure() => train.brakeSystem.brakePipePressure; + public float GetBrakeResPressure() => train.brakeSystem.mainReservoirPressure; + + public virtual Func GetIndicatorFunc( CabIndicatorType indicatedType ) + { + switch( indicatedType ) + { + case CabIndicatorType.BrakePipe: + return GetBrakePipePressure; + + case CabIndicatorType.BrakeReservoir: + return GetBrakeResPressure; + + case CabIndicatorType.Speed: + return GetSpeedKmH; + + default: + return () => 0; + } + } } public abstract class CustomLocoController : CustomLocoController diff --git a/DVCustomCarLoader/LocoComponents/CustomLocoControllerDiesel.cs b/DVCustomCarLoader/LocoComponents/CustomLocoControllerDiesel.cs index ef4c678e..de487a63 100644 --- a/DVCustomCarLoader/LocoComponents/CustomLocoControllerDiesel.cs +++ b/DVCustomCarLoader/LocoComponents/CustomLocoControllerDiesel.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Text; using System.Threading.Tasks; +using CCL_GameScripts.CabControls; using DV; using DV.ServicePenalty; using UnityEngine; @@ -44,7 +45,28 @@ public bool EngineRunning public float SandLevel => sim.sand.value; public bool SandersOn => sim.sandOn; - public override float GetSandersFlow() + public override Func GetIndicatorFunc( CabIndicatorType indicatedType ) + { + switch( indicatedType ) + { + case CabIndicatorType.Fuel: + return () => FuelLevel; + + case CabIndicatorType.Oil: + return () => OilLevel; + + case CabIndicatorType.Sand: + return () => SandLevel; + + case CabIndicatorType.EngineTemp: + return () => EngineTemp; + + default: + return base.GetIndicatorFunc(indicatedType); + } + } + + public override float GetSandersFlow() { if( sim.sand.value <= 0f ) { @@ -85,16 +107,6 @@ public override void SetReverser( float position ) base.SetReverser(position); } - //protected override void Awake() - //{ - // base.Awake(); - - // // TODO: MU, save state - // //MultipleUnitModule component = base.GetComponent(); - // //base.gameObject.AddComponent().Initialize(sim, damageController, this, carVisitChecker, component); - //} - - public override float GetTractionForce() { float num = (sim.engineRPM.value > 0f) ? tractionTorqueCurve.Evaluate(GetSpeedKmH() / sim.engineRPM.value) : 0f; @@ -102,7 +114,18 @@ public override float GetTractionForce() return sim.engineRPM.value * num2 * tractionTorqueMult; } - private void OnDisable() + #region Events + + //protected override void Awake() + //{ + // base.Awake(); + + // // TODO: MU, save state + // //MultipleUnitModule component = base.GetComponent(); + // //base.gameObject.AddComponent().Initialize(sim, damageController, this, carVisitChecker, component); + //} + + private void OnDisable() { SetupListeners(false); } @@ -146,12 +169,12 @@ private void SetupListeners( bool on ) eventController.EngineTempChanged.Manage(OnEngineTempChanged, on); } - //protected override bool ShouldSwitchToTrainBrakeOnStart() - //{ - // return LicenseManager.IsGeneralLicenseAcquired(GeneralLicenseType.DE6) && base.ShouldSwitchToTrainBrakeOnStart(); - //} + //protected override bool ShouldSwitchToTrainBrakeOnStart() + //{ + // return LicenseManager.IsGeneralLicenseAcquired(GeneralLicenseType.DE6) && base.ShouldSwitchToTrainBrakeOnStart(); + //} - protected override void Start() + protected override void Start() { base.Start(); if( !VRManager.IsVREnabled() ) @@ -160,9 +183,15 @@ protected override void Start() keyboardCtrl.control = this; Main.Log("Added keyboard input to car"); } + + train.brakeSystem.compressorProductionRate = sim.simParams.AirCompressorRate; } - public override void Update() + #endregion + + #region Update Loop + + public override void Update() { base.Update(); UpdateSimSpeed(); @@ -180,5 +209,7 @@ private void UpdateSimThrottle() sim.throttle.SetValue(sim.engineOn ? throttle : 0f); sim.throttleToTargetDiff.SetValue(sim.engineOn ? (throttle - targetThrottle) : 0f); } - } + + #endregion + } } diff --git a/DVCustomCarLoader/LocoComponents/LocoComponentManager.cs b/DVCustomCarLoader/LocoComponents/LocoComponentManager.cs index dfe42d78..4adf1e34 100644 --- a/DVCustomCarLoader/LocoComponents/LocoComponentManager.cs +++ b/DVCustomCarLoader/LocoComponents/LocoComponentManager.cs @@ -25,7 +25,7 @@ public static void AddLocoSimulation( GameObject prefab, SimParamsBase simParams } } - private static Dictionary InteriorPrefabCache = + private static readonly Dictionary InteriorPrefabCache = new Dictionary(); // Order to add components: @@ -44,6 +44,10 @@ public static void AddDieselSimulation( GameObject prefab, SimParamsDiesel simPa return; } + // add brake compressor, rate is set in controller start + var train = prefab.GetComponent(); + train.hasCompressor = simParams.AirCompressorRate > 0; + var drivingForce = prefab.AddComponent(); ApplyDrivingForceParams(drivingForce, simParams); @@ -66,12 +70,25 @@ private static void ApplyDrivingForceParams( DrivingForce driver, SimParamsBase driver.wheelslipToFrictionModifierCurve = simParams.WheelslipToFrictionModifier; } - public static void SetupCabInput( GameObject interior ) + public static void SetupCabComponents( GameObject interior ) { var cabParams = interior.GetComponent(); + if( !cabParams ) + { + Main.Log("Added Cab Input Setup"); + cabParams = interior.AddComponent(); + } + + var indicatorControl = interior.GetComponent(); + if( !indicatorControl ) + { + Main.Log("Added Cab Indicator Controller"); + indicatorControl = interior.AddComponent(); + } + if( cabParams ) { - CreateComponentsFromProxies(interior); + CreateComponentsFromProxies(interior, cabParams); CreateCopiedControls(interior, cabParams); var cabInput = interior.AddComponent(); } @@ -81,21 +98,37 @@ public static void SetupCabInput( GameObject interior ) } } - public static void CreateComponentsFromProxies( GameObject root ) + public static void CreateComponentsFromProxies( GameObject root, CabInputSetup inputSetup ) { var allInitSpecs = root.GetComponentsInChildren(); + foreach( var compSpec in allInitSpecs ) { - compSpec.CreateRealComponent(AccessTools.TypeByName, Main.Warning); + GameObject controlObject = compSpec.gameObject; + if( compSpec is ControlSetupBase control ) + { + inputSetup.SetInputObject(control.InputBinding, controlObject); + } + + object realComp = compSpec.CreateRealComponent(AccessTools.TypeByName, Main.Warning); + + if( (compSpec is IndicatorSetupBase indicatorSpec) && (realComp is GameObject spawnedObj) ) + { + var realIndicator = spawnedObj.GetComponent(); + var indicatorInfo = spawnedObj.AddComponent(); + indicatorInfo.Type = indicatorSpec.OutputBinding; + indicatorInfo.Indicator = realIndicator; + } } } public static void CreateCopiedControls( GameObject root, CabInputSetup cabSetup ) { var allCopySpecs = root.GetComponentsInChildren(); + foreach( var copySpec in allCopySpecs ) { - GameObject parent = copySpec.gameObject; + GameObject copierAttachedObject = copySpec.gameObject; (BaseTrainCarType carType, string sourceObjName) = copySpec.GetSourceObject(); GameObject sourceInterior = GetTrainCarInterior((TrainCarType)carType); @@ -104,10 +137,36 @@ public static void CreateCopiedControls( GameObject root, CabInputSetup cabSetup Transform sourceChild = sourceInterior.transform.Find(sourceObjName); if( sourceChild ) { - GameObject newControl = UnityEngine.Object.Instantiate(sourceChild.gameObject, parent.transform); - newControl.transform.localPosition = Vector3.zero; - newControl.transform.localRotation = Quaternion.identity; - cabSetup.SetInputObject(copySpec.InputBinding, newControl); + GameObject newControl; + if( copySpec.ReplaceThisObject ) + { + newControl = UnityEngine.Object.Instantiate(sourceChild.gameObject, copierAttachedObject.transform.parent); + newControl.transform.localPosition = copierAttachedObject.transform.localPosition; + newControl.transform.localRotation = copierAttachedObject.transform.localRotation; + } + else + { + newControl = UnityEngine.Object.Instantiate(sourceChild.gameObject, copierAttachedObject.transform); + newControl.transform.localPosition = Vector3.zero; + newControl.transform.localRotation = Quaternion.identity; + } + + if( copySpec is CopiedCabInput input ) + { + cabSetup.SetInputObject(input.InputBinding, newControl); + } + else if( copySpec is CopiedCabIndicator indicator ) + { + var realIndicator = newControl.GetComponentInChildren(true); + var indicatorInfo = realIndicator.gameObject.AddComponent(); + indicatorInfo.Type = indicator.OutputBinding; + indicatorInfo.Indicator = realIndicator; + } + + if( copySpec.ReplaceThisObject ) + { + UnityEngine.Object.Destroy(copierAttachedObject); + } } } } From cb721f6978e188dd92a241539d9099663f1a1056 Mon Sep 17 00:00:00 2001 From: Katy Fox Date: Sun, 25 Jul 2021 19:02:08 -0400 Subject: [PATCH 16/20] Copied lamp, lamp controller, TrainCarSetup modifications --- CCL_GameScripts/CCL_GameScripts.csproj | 1 + CCL_GameScripts/CabControls/CabIOTypes.cs | 27 +++- CCL_GameScripts/CabControls/CopiedLamp.cs | 82 ++++++++++ CCL_GameScripts/CabControls/CopiedLever.cs | 7 +- CCL_GameScripts/CabControls/LeverSetup.cs | 12 +- CCL_GameScripts/TrainCarSetup.cs | 27 +++- DVCustomCarLoader/CustomCar.cs | 56 ++++--- DVCustomCarLoader/CustomCarManager.cs | 7 +- DVCustomCarLoader/DVCustomCarLoader.csproj | 2 + .../LocoComponents/CustomCabIndicators.cs | 7 - .../LocoComponents/CustomLampController.cs | 141 ++++++++++++++++++ .../LocoComponents/CustomLocoSimEvents.cs | 30 ++++ .../LocoComponents/LocoComponentManager.cs | 30 ++++ 13 files changed, 386 insertions(+), 43 deletions(-) create mode 100644 CCL_GameScripts/CabControls/CopiedLamp.cs create mode 100644 DVCustomCarLoader/LocoComponents/CustomLampController.cs diff --git a/CCL_GameScripts/CCL_GameScripts.csproj b/CCL_GameScripts/CCL_GameScripts.csproj index 796ffa28..70f346f3 100644 --- a/CCL_GameScripts/CCL_GameScripts.csproj +++ b/CCL_GameScripts/CCL_GameScripts.csproj @@ -57,6 +57,7 @@ + diff --git a/CCL_GameScripts/CabControls/CabIOTypes.cs b/CCL_GameScripts/CabControls/CabIOTypes.cs index c31635dd..bc2c1837 100644 --- a/CCL_GameScripts/CabControls/CabIOTypes.cs +++ b/CCL_GameScripts/CabControls/CabIOTypes.cs @@ -17,6 +17,31 @@ public enum CabIndicatorType Fuel, Oil, Sand, - Speed + Speed, + } + + public enum SimEventType + { + Couplers, + EngineDamage, + EngineTemp, + Fuel, + Oil, + Sand, + Wheelslip, + } + + public enum SimThresholdDirection + { + Above, Below + } + + public enum SimAmount + { + Depleted = 0, + Low = 1, + Mid = 2, + High = 3, + Full = 4 } } \ No newline at end of file diff --git a/CCL_GameScripts/CabControls/CopiedLamp.cs b/CCL_GameScripts/CabControls/CopiedLamp.cs new file mode 100644 index 00000000..4e29bc92 --- /dev/null +++ b/CCL_GameScripts/CabControls/CopiedLamp.cs @@ -0,0 +1,82 @@ +using System.Collections; +using UnityEngine; + +namespace CCL_GameScripts.CabControls +{ + public class CopiedLamp : CopiedCabControl + { + public enum CopiedLampType + { + DE2SmallRed, + DE2SmallBlue, + DE2SmallYellow, + DE2LargeRed, + DE2LargeGreen + } + + protected static readonly (BaseTrainCarType, string)[] TargetObjects = + new[] + { + (BaseTrainCarType.LocoShunter, "I brake_aux_res_lamp"), + (BaseTrainCarType.LocoShunter, "C dashboard buttons controller/I fan_lamp"), + (BaseTrainCarType.LocoShunter, "C dashboard buttons controller/I deploy_sand_lamp"), + (BaseTrainCarType.LocoShunter, "C dashboard indicators controller/I service_engine_lamp"), + (BaseTrainCarType.LocoShunter, "C dashboard buttons controller/I power_fuse_lamp"), + }; + + protected static readonly (float, Color)[] GizmoData = + new[] + { + (0.011f, new Color(0.6f, 0, 0)), + (0.01f, new Color(0, 0, 0.6f)), + (0.009f, new Color(0.6f, 0.6f, 0)), + (0.02f, new Color(0.6f, 0, 0)), + (0.02f, new Color(0, 0.6f, 0)), + }; + + + public SimEventType SimBinding; + public CopiedLampType LampType; + + [Header("Simulation Binding")] + public SimThresholdDirection ThresholdDirection; + public SimAmount SolidThreshold; + public bool UseBlinkMode; + public SimAmount BlinkThreshold; + + public override (BaseTrainCarType, string) GetSourceObject() + { + return TargetObjects[(int)LampType]; + } + + public CopiedLamp() + { + ReplaceThisObject = true; + } + + protected const int GIZMO_SEGMENTS = 40; + //protected static readonly Color GIZMO_COLOR = new Color(0.65f, 0, 0); + + private void OnDrawGizmosSelected() + { + (float radius, Color color) = GizmoData[(int)LampType]; + + Vector3 lastVector = transform.position; + for( int i = 0; i <= GIZMO_SEGMENTS; i++ ) + { + Gizmos.color = color; + Vector3 nextVector = Quaternion.AngleAxis( + Mathf.Lerp(0, 360, (float)i / GIZMO_SEGMENTS), Vector3.forward) + * Vector3.right * radius; + nextVector = transform.TransformPoint(nextVector); + + if( i != 0 ) + { + Gizmos.DrawLine(lastVector, nextVector); + } + + lastVector = nextVector; + } + } + } +} \ No newline at end of file diff --git a/CCL_GameScripts/CabControls/CopiedLever.cs b/CCL_GameScripts/CabControls/CopiedLever.cs index 92f7ef36..0f722898 100644 --- a/CCL_GameScripts/CabControls/CopiedLever.cs +++ b/CCL_GameScripts/CabControls/CopiedLever.cs @@ -60,12 +60,13 @@ private void OnDrawGizmosSelected() Color startColor = gizmo.Inverted ? END_COLOR : START_COLOR; Color endColor = gizmo.Inverted ? START_COLOR : END_COLOR; + float rayCount = gizmo.Notches - 1; // draw ray segments - for( int i = 0; i <= gizmo.Notches; i++ ) + for( int i = 0; i <= rayCount; i++ ) { - Color segmentColor = Color.Lerp(startColor, endColor, (float)i / gizmo.Notches); + Color segmentColor = Color.Lerp(startColor, endColor, i / rayCount); Vector3 rayVector = Quaternion.AngleAxis( - Mathf.Lerp(gizmo.LimitMin, gizmo.LimitMax, (float)i / gizmo.Notches), Vector3.up) + Mathf.Lerp(gizmo.LimitMin, gizmo.LimitMax, i / rayCount), Vector3.up) * Vector3.forward * GIZMO_RADIUS; rayVector = transform.TransformPoint(rayVector); diff --git a/CCL_GameScripts/CabControls/LeverSetup.cs b/CCL_GameScripts/CabControls/LeverSetup.cs index aecbfe73..eed85208 100644 --- a/CCL_GameScripts/CabControls/LeverSetup.cs +++ b/CCL_GameScripts/CabControls/LeverSetup.cs @@ -72,14 +72,16 @@ private void OnDrawGizmosSelected() Color startColor = InvertDirection ? END_COLOR : START_COLOR; Color endColor = InvertDirection ? START_COLOR : END_COLOR; - if( UseNotches ) + if( UseNotches && (Notches > 0) ) { + float rayCount = Notches - 1; + // draw ray segments - for( int i = 0; i <= Notches; i++ ) + for( int i = 0; i <= rayCount; i++ ) { - Gizmos.color = Color.Lerp(startColor, endColor, (float)i / Notches); + Gizmos.color = Color.Lerp(startColor, endColor, i / rayCount); Vector3 rayVector = Quaternion.AngleAxis( - Mathf.Lerp(JointLimitMin, JointLimitMax, (float)i / Notches), JointAxis) + Mathf.Lerp(JointLimitMin, JointLimitMax, i / rayCount), JointAxis) * Vector3.forward * GIZMO_RADIUS; rayVector = transform.TransformPoint(rayVector); @@ -92,7 +94,7 @@ private void OnDrawGizmosSelected() Vector3 lastVector = transform.position; for( int i = 0; i <= GIZMO_SEGMENTS; i++ ) { - Gizmos.color = Color.Lerp(startColor, endColor, (float)i / Notches); + Gizmos.color = Color.Lerp(startColor, endColor, (float)i / GIZMO_SEGMENTS); Vector3 nextVector = Quaternion.AngleAxis( Mathf.Lerp(JointLimitMin, JointLimitMax, (float)i / GIZMO_SEGMENTS), JointAxis) * Vector3.forward * GIZMO_RADIUS; diff --git a/CCL_GameScripts/TrainCarSetup.cs b/CCL_GameScripts/TrainCarSetup.cs index b7c2302f..c37c6a9a 100644 --- a/CCL_GameScripts/TrainCarSetup.cs +++ b/CCL_GameScripts/TrainCarSetup.cs @@ -7,7 +7,7 @@ namespace CCL_GameScripts /// /// Holds all the references for setup of a TrainCar. /// - public class TrainCarSetup : MonoBehaviour + public class TrainCarSetup : ComponentInitSpec { #region Internal @@ -31,7 +31,13 @@ private void BringUpLocoSetup() #endregion + #region TrainCar Init Spec + protected override string TargetTypeName => "TrainCar"; + + protected override bool DestroyAfterCreation => false; + + [Header("Bogie Replacement")] public Transform FrontBogie; public Transform RearBogie; public bool ReplaceFrontBogie; @@ -39,9 +45,26 @@ private void BringUpLocoSetup() public CapsuleCollider FrontBogieCollider; public CapsuleCollider RearBogieCollider; + [Header("Bogie Physics")] + public bool OverridePhysics = false; + + [ProxyField("wheelRadius")] + public float WheelRadius = 0.459f; + + [Range(0, 1)] + [ProxyField("bogieMassRatio")] + public float MassRatioPerBogie = 0.5f; + + [ProxyField("bogieSpring")] + public float BogieSpring = 200; + + [ProxyField("bogieDamping")] + public float BogieDamping = 5; + public GameObject InteriorPrefab; - + #endregion + #region Helpers [MethodButton( diff --git a/DVCustomCarLoader/CustomCar.cs b/DVCustomCarLoader/CustomCar.cs index 2e57467a..22aac9b6 100644 --- a/DVCustomCarLoader/CustomCar.cs +++ b/DVCustomCarLoader/CustomCar.cs @@ -36,7 +36,7 @@ public class CustomCar public Vector3 FrontCouplerPosition; public Vector3 RearCouplerPosition; - public void FinalizePrefab() + public bool FinalizePrefab() { Main.ModEntry.Logger.Log($"Augmenting prefab for {identifier}"); @@ -44,6 +44,13 @@ public void FinalizePrefab() newFab.SetActive(false); Object.DontDestroyOnLoad(newFab); + TrainCarSetup carSetup = newFab.GetComponent(); + if( !carSetup ) + { + Main.Error($"Prefab {CarPrefab.name} for {identifier} has no TrainCarSetup!"); + return false; + } + GameObject basePrefab = CarTypes.GetCarPrefab(BaseCarType); TrainCar baseCar = basePrefab.GetComponent(); @@ -325,14 +332,24 @@ public void FinalizePrefab() #endregion // Setup new car script - TrainCar newCar = newFab.AddComponent(); + var newCar = carSetup.CreateRealComponent(AccessTools.TypeByName, Main.Warning) as TrainCar; + if( !newCar ) + { + Main.Warning("Couldn't create TrainCar component"); + Object.Destroy(newFab); + return false; + } // setup traincar properties - newCar.bogieDamping = baseCar.bogieDamping; - newCar.bogieMassRatio = baseCar.bogieMassRatio; - newCar.bogieSpring = baseCar.bogieSpring; - newCar.totalMass = baseCar.totalMass; - newCar.wheelRadius = baseCar.wheelRadius; + if( !carSetup.OverridePhysics ) + { + newCar.bogieDamping = baseCar.bogieDamping; + newCar.bogieMassRatio = baseCar.bogieMassRatio; + newCar.bogieSpring = baseCar.bogieSpring; + newCar.totalMass = baseCar.totalMass; + newCar.wheelRadius = baseCar.wheelRadius; + } + newCar.carType = BaseCarType; var simParams = newFab.GetComponent(); @@ -340,26 +357,18 @@ public void FinalizePrefab() { LocoComponentManager.AddLocoSimulation(newFab, simParams); - var carSetup = newFab.GetComponent(); - if( carSetup ) + if( carSetup.InteriorPrefab ) { - if( carSetup.InteriorPrefab ) - { - GameObject interiorFab = Object.Instantiate(carSetup.InteriorPrefab, null); - interiorFab.SetActive(false); - Object.DontDestroyOnLoad(interiorFab); + GameObject interiorFab = Object.Instantiate(carSetup.InteriorPrefab, null); + interiorFab.SetActive(false); + Object.DontDestroyOnLoad(interiorFab); - LocoComponentManager.SetupCabComponents(interiorFab); - interiorFab.SetLayersRecursive("Interactable"); + LocoComponentManager.SetupCabComponents(interiorFab); + interiorFab.SetLayersRecursive("Interactable"); - newCar.interiorPrefab = interiorFab; + newCar.interiorPrefab = interiorFab; - InteriorPrefab = interiorFab; - } - } - else - { - Main.Warning("TrainCarSetup not found"); + InteriorPrefab = interiorFab; } } @@ -367,6 +376,7 @@ public void FinalizePrefab() CarPrefab.name = identifier; Main.ModEntry.Logger.Log($"Finalized prefab for {identifier}"); + return true; } private static Delegate[] carSpawnedDelegates = null; diff --git a/DVCustomCarLoader/CustomCarManager.cs b/DVCustomCarLoader/CustomCarManager.cs index 3f7d6c0e..8b2c924c 100644 --- a/DVCustomCarLoader/CustomCarManager.cs +++ b/DVCustomCarLoader/CustomCarManager.cs @@ -155,8 +155,11 @@ private static CustomCar CreateCustomCar( string directory, JSONObject jsonFile } } - newCar.FinalizePrefab(); - return newCar; + if( newCar.FinalizePrefab() ) + { + return newCar; + } + else return null; } } } diff --git a/DVCustomCarLoader/DVCustomCarLoader.csproj b/DVCustomCarLoader/DVCustomCarLoader.csproj index fccc4c74..c1d5d7d5 100644 --- a/DVCustomCarLoader/DVCustomCarLoader.csproj +++ b/DVCustomCarLoader/DVCustomCarLoader.csproj @@ -43,6 +43,7 @@ ..\..\..\..\..\Program Files\Steam\steamapps\common\Derail Valley\DerailValley_Data\Managed\DV.Utils.dll + False @@ -75,6 +76,7 @@ + diff --git a/DVCustomCarLoader/LocoComponents/CustomCabIndicators.cs b/DVCustomCarLoader/LocoComponents/CustomCabIndicators.cs index c529701c..a6525c1a 100644 --- a/DVCustomCarLoader/LocoComponents/CustomCabIndicators.cs +++ b/DVCustomCarLoader/LocoComponents/CustomCabIndicators.cs @@ -12,13 +12,6 @@ public class CustomCabIndicators : MonoBehaviour public IndicatorInfo[] Indicators; - //public void AddIndicators( IEnumerable<(CabIndicatorType, Indicator)> comps ) - //{ - // if( Indicators == null ) Indicators = new List(); - - // Indicators.AddRange(comps.Select(c => new IndicatorInfo(c.Item1, c.Item2))); - //} - protected virtual void Start() { locoController = TrainCar.Resolve(gameObject).GetComponent(); diff --git a/DVCustomCarLoader/LocoComponents/CustomLampController.cs b/DVCustomCarLoader/LocoComponents/CustomLampController.cs new file mode 100644 index 00000000..3bf9d9c6 --- /dev/null +++ b/DVCustomCarLoader/LocoComponents/CustomLampController.cs @@ -0,0 +1,141 @@ +using System; +using System.Collections; +using CCL_GameScripts.CabControls; +using DV.Util.EventWrapper; +using UnityEngine; + +namespace DVCustomCarLoader.LocoComponents +{ + public class CustomLampController : MonoBehaviour + { + protected CustomLocoSimEvents simEvents; + public DashboardLampRelay[] Relays; + + protected virtual void Start() + { + simEvents = TrainCar.Resolve(gameObject).GetComponent(); + + Relays = GetComponentsInChildren(); + + Main.Log($"CustomDashboardLamps Start - {Relays.Length} lamps"); + foreach( var lampController in Relays ) + { + object changeEvent = simEvents.GetEvent(lampController.SimBinding); + if( changeEvent != null ) + { + lampController.SetupListener(changeEvent); + } + } + } + } + + public class DashboardLampRelay : MonoBehaviour + { + protected static AudioClip WarningSound; + + public SimEventType SimBinding; + public LampControl Lamp; + + public SimThresholdDirection ThresholdDirection; + public LocoSimulationEvents.Amount SolidThreshold; + public bool UseBlinkMode; + public LocoSimulationEvents.Amount BlinkThreshold; + + private void OnAmountChanged( LocoSimulationEvents.Amount amount ) + { + if( ThresholdDirection == SimThresholdDirection.Above ) + { + if( UseBlinkMode && (amount >= BlinkThreshold) ) + { + UpdateLampState(LampControl.LampState.Blinking, true); + } + else if( amount >= SolidThreshold ) + { + UpdateLampState(LampControl.LampState.On, true); + } + else + { + UpdateLampState(LampControl.LampState.Off, false); + } + } + else + { + // below threshold + if( UseBlinkMode && (amount <= BlinkThreshold) ) + { + UpdateLampState(LampControl.LampState.Blinking, true); + } + else if( amount <= SolidThreshold ) + { + UpdateLampState(LampControl.LampState.On, true); + } + else + { + UpdateLampState(LampControl.LampState.Off, false); + } + } + } + + private void OnBoolChanged( bool newValue ) + { + // below, true -> false + // below, false -> true + // above, true -> true + // above, false -> false + + if( (ThresholdDirection == SimThresholdDirection.Below) ^ newValue ) + { + UpdateLampState(UseBlinkMode ? LampControl.LampState.Blinking : LampControl.LampState.On, true); + } + else + { + UpdateLampState(LampControl.LampState.Off, false); + } + } + + private void OnCouplingChanged( LocoSimulationEvents.CouplingIntegrityInfo integrityInfo ) + { + LampControl.LampState state = (integrityInfo == LocoSimulationEvents.CouplingIntegrityInfo.OK) ? + LampControl.LampState.Off : + LampControl.LampState.Blinking; + + UpdateLampState(state, true); + } + + private void UpdateLampState( LampControl.LampState state, bool playWarningSound = true ) + { + if( playWarningSound && (state == LampControl.LampState.On || state == LampControl.LampState.Blinking) ) + { + WarningSound.Play(Lamp.transform.position, 1f, 1f, 0f, 1f, 500f, default(AudioSourceCurves), AudioManager.e.cabGroup, Lamp.transform); + } + + Lamp.SetLampState(state); + } + + public void Initialize( SimEventType type, LampControl lamp ) + { + SimBinding = type; + Lamp = lamp; + } + + public void SetupListener( object toAttach ) + { + if( toAttach is event_ amountEvent ) + { + amountEvent.Manage(OnAmountChanged, true); + } + else if( toAttach is event_ coupleEvent ) + { + coupleEvent.Manage(OnCouplingChanged, true); + } + else if( toAttach is event_ boolEvent ) + { + boolEvent.Manage(OnBoolChanged, true); + } + else + { + Main.Warning($"DashboardLampRelay - unknown sim event type {toAttach.GetType().Name}"); + } + } + } +} \ No newline at end of file diff --git a/DVCustomCarLoader/LocoComponents/CustomLocoSimEvents.cs b/DVCustomCarLoader/LocoComponents/CustomLocoSimEvents.cs index 715e8416..000481f9 100644 --- a/DVCustomCarLoader/LocoComponents/CustomLocoSimEvents.cs +++ b/DVCustomCarLoader/LocoComponents/CustomLocoSimEvents.cs @@ -1,11 +1,41 @@ using System.Collections; +using CCL_GameScripts.CabControls; using UnityEngine; namespace DVCustomCarLoader.LocoComponents { public abstract class CustomLocoSimEvents : LocoSimulationEvents { + /// Gets an event_<T> from the controller + public virtual object GetEvent( SimEventType indicatorType ) + { + switch( indicatorType ) + { + case SimEventType.Fuel: + return FuelChanged; + + case SimEventType.Oil: + return OilChanged; + + case SimEventType.Sand: + return SandChanged; + + case SimEventType.EngineTemp: + return EngineTempChanged; + case SimEventType.EngineDamage: + return EngineDamageChanged; + + case SimEventType.Wheelslip: + return WheelslipChanged; + + case SimEventType.Couplers: + return CouplingIntegrityChanged; + + default: + return null; + } + } } public abstract class CustomLocoSimEvents : CustomLocoSimEvents diff --git a/DVCustomCarLoader/LocoComponents/LocoComponentManager.cs b/DVCustomCarLoader/LocoComponents/LocoComponentManager.cs index 4adf1e34..5f3c5719 100644 --- a/DVCustomCarLoader/LocoComponents/LocoComponentManager.cs +++ b/DVCustomCarLoader/LocoComponents/LocoComponentManager.cs @@ -7,6 +7,7 @@ using CCL_GameScripts; using HarmonyLib; using CCL_GameScripts.CabControls; +using DV.CabControls.Spec; namespace DVCustomCarLoader { @@ -153,6 +154,23 @@ public static void CreateCopiedControls( GameObject root, CabInputSetup cabSetup if( copySpec is CopiedCabInput input ) { + // copy interaction area + var realControlSpec = newControl.GetComponentInChildren(true); + + // try to find interaction area parent + var iAreaField = AccessTools.Field(realControlSpec.GetType(), "nonVrStaticInteractionArea"); + if( iAreaField != null ) + { + var iArea = iAreaField.GetValue(realControlSpec) as StaticInteractionArea; + if( iArea ) + { + GameObject newIAObj = UnityEngine.Object.Instantiate(iArea.gameObject, newControl.transform.parent); + iArea = newIAObj.GetComponent(); + iAreaField.SetValue(realControlSpec, iArea); + Main.Log("Instantiated static interaction area"); + } + } + cabSetup.SetInputObject(input.InputBinding, newControl); } else if( copySpec is CopiedCabIndicator indicator ) @@ -162,6 +180,18 @@ public static void CreateCopiedControls( GameObject root, CabInputSetup cabSetup indicatorInfo.Type = indicator.OutputBinding; indicatorInfo.Indicator = realIndicator; } + else if( copySpec is CopiedLamp lamp ) + { + var realLamp = newControl.GetComponentInChildren(true); + var lampRelay = newControl.gameObject.AddComponent(); + lampRelay.SimBinding = lamp.SimBinding; + lampRelay.Lamp = realLamp; + + lampRelay.ThresholdDirection = lamp.ThresholdDirection; + lampRelay.SolidThreshold = (LocoSimulationEvents.Amount)lamp.SolidThreshold; + lampRelay.UseBlinkMode = lamp.UseBlinkMode; + lampRelay.BlinkThreshold = (LocoSimulationEvents.Amount)lamp.BlinkThreshold; + } if( copySpec.ReplaceThisObject ) { From 68e5d5bb469415b135f92a18c6b38edaee47336e Mon Sep 17 00:00:00 2001 From: Katy Fox Date: Sun, 25 Jul 2021 19:50:22 -0400 Subject: [PATCH 17/20] Add mu module init --- DVCustomCarLoader/DVCustomCarLoader.csproj | 2 +- .../LocoComponents/LocoComponentManager.cs | 26 ++++++++++++++++++- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/DVCustomCarLoader/DVCustomCarLoader.csproj b/DVCustomCarLoader/DVCustomCarLoader.csproj index c1d5d7d5..0171abb8 100644 --- a/DVCustomCarLoader/DVCustomCarLoader.csproj +++ b/DVCustomCarLoader/DVCustomCarLoader.csproj @@ -76,7 +76,7 @@ - + diff --git a/DVCustomCarLoader/LocoComponents/LocoComponentManager.cs b/DVCustomCarLoader/LocoComponents/LocoComponentManager.cs index 5f3c5719..adc6e27f 100644 --- a/DVCustomCarLoader/LocoComponents/LocoComponentManager.cs +++ b/DVCustomCarLoader/LocoComponents/LocoComponentManager.cs @@ -55,8 +55,32 @@ public static void AddDieselSimulation( GameObject prefab, SimParamsDiesel simPa prefab.AddComponent(); prefab.AddComponent(); prefab.AddComponent(); - //prefab.AddComponent(); var locoController = prefab.AddComponent(); + + // setup multiple unit module + var muHoses = prefab.GetComponentsInChildren(); + if( muHoses.Length == 0 ) + { + Main.Warning($"Could not find MU hose adapters for diesel engine"); + } + else + { + var muModule = prefab.AddComponent(); + foreach( var muHoseAdapter in muHoses ) + { + Vector3 hoseOffset = prefab.transform.InverseTransformPoint(muHoseAdapter.transform.position); + if( hoseOffset.z > 0 ) + { + // front coupling + muModule.frontCableAdapter = muHoseAdapter; + } + else + { + muModule.rearCableAdapter = muHoseAdapter; + } + } + } + locoController.drivingForce = drivingForce; Main.Log($"Added diesel electric simulation to {prefab.name}"); From dfe7581b3e4815d76cf84b272f27a8777b50bc80 Mon Sep 17 00:00:00 2001 From: Katy Fox Date: Fri, 13 Aug 2021 13:45:48 -0400 Subject: [PATCH 18/20] Add custom TrainCarType injection --- CCL_GameScripts/CustomCarEnums.cs | 19 ++ CCL_GameScripts/Editor/ExportTrainCar.cs | 70 +---- CCL_GameScripts/SimParamsBase.cs | 9 + CCL_GameScripts/TrainCarSetup.cs | 5 + DVCustomCarLoader/CarTypeInjector.cs | 211 +++++++++++++++ DVCustomCarLoader/CustomCar.cs | 14 + DVCustomCarLoader/CustomCarManager.cs | 10 +- DVCustomCarLoader/DVCustomCarLoader.csproj | 4 + .../CustomDieselReverserLock.cs | 93 +++++++ .../LocoComponents/CustomLocoAudio.cs | 52 ++++ .../LocoComponents/CustomLocoAudioDiesel.cs | 249 ++++++++++++++++++ .../LocoComponents/LocoComponentManager.cs | 21 +- DVCustomCarLoader/ModPatches.cs | 22 +- DVCustomCarLoader/SaveLoadPatches.cs | 23 +- 14 files changed, 708 insertions(+), 94 deletions(-) create mode 100644 DVCustomCarLoader/CarTypeInjector.cs create mode 100644 DVCustomCarLoader/LocoComponents/CustomDieselReverserLock.cs create mode 100644 DVCustomCarLoader/LocoComponents/CustomLocoAudio.cs create mode 100644 DVCustomCarLoader/LocoComponents/CustomLocoAudioDiesel.cs diff --git a/CCL_GameScripts/CustomCarEnums.cs b/CCL_GameScripts/CustomCarEnums.cs index 58ae5648..56c4dc2d 100644 --- a/CCL_GameScripts/CustomCarEnums.cs +++ b/CCL_GameScripts/CustomCarEnums.cs @@ -47,4 +47,23 @@ public enum BaseTrainCarType HandCar = 700, NuclearFlask = 800 } + + public enum BaseCargoContainerType + { + None, + Hopper, + TankerOil, + TankerGas, + TankerChem, + Flatcar, + FlatcarStakes, + Boxcar, + Gondola, + Refrigerator, + Cars, + Passengers, + MilitaryBoxcar, + NuclearFlask, + MilitaryFlatcar + } } diff --git a/CCL_GameScripts/Editor/ExportTrainCar.cs b/CCL_GameScripts/Editor/ExportTrainCar.cs index 6d1d0db6..aeaeaba6 100644 --- a/CCL_GameScripts/Editor/ExportTrainCar.cs +++ b/CCL_GameScripts/Editor/ExportTrainCar.cs @@ -37,49 +37,6 @@ private enum state /// private state State = state.Settings; - #region Exported Train Car Data - - //private bool loadedCar = false; - - /// - /// The identifier for this car. - /// - private string Identifier = "Custom Car"; - - /// - /// The underlying type of this car. - /// - private BaseTrainCarType TrainCarType = BaseTrainCarType.FlatbedEmpty; - - #region Positions - - ////Couplers - //private Vector3 FrontCouplerPosition; - //private Vector3 RearCouplerPosition; - - ////Chains - //private Vector3 FrontChainPosition; - //private Vector3 RearChainPosition; - - ////Hoses - //private Vector3 FrontHosePosition; - //private Vector3 RearHosePosition; - - ////Buffers - //private Vector3 FrontBufferPosition; - //private Vector3 RearBufferPosition; - - ////Bogies - //private Vector3 FrontBogiePosition; - //private Vector3 RearBogiePosition; - - ////Name plates - //private Vector3 SidePlate1Position; - //private Vector3 SidePlate2Position; - - #endregion - - #endregion [InitializeOnLoadMethod] static void Init() @@ -157,14 +114,14 @@ void OnGUI() EditorStyles.label.wordWrap = true; EditorGUILayout.LabelField( - "Select your TrainCar prefab. It will be shown in the list below. ONLY ONE TRAIN CAR IS SUPPORTED AT THIS TIME. Selecting more than one prefab will cause unwanted results."); + "Confirm the settings for your train car before proceeding"); EditorGUILayout.LabelField("", GUI.skin.horizontalSlider); GUILayout.BeginVertical(); - Identifier = EditorGUILayout.TextField("Identifier:", Identifier); - TrainCarType = (BaseTrainCarType) EditorGUILayout.EnumPopup("Type of Car:", TrainCarType); + EditorGUILayout.LabelField("Identifier:", _trainCarSetup.Identifier); + EditorGUILayout.LabelField("Car Underlying Type:", _trainCarSetup.BaseCarType.ToString()); EditorGUILayout.LabelField("", GUI.skin.horizontalSlider); @@ -219,7 +176,7 @@ void OnGUI() } if (EditorUtility.DisplayDialog("Confirmation", - $"You are about to export your TrainCar named {Identifier}, are you sure you want to proceed?", + $"You are about to export your TrainCar named {_trainCarSetup.Identifier}, are you sure you want to proceed?", "Yes", "No")) { @@ -233,7 +190,7 @@ void OnGUI() var assetBundleFullpath = EditorUtility.SaveFolderPanel( "Export Car", ExistAtC ? cPath : (ExistAtX ? xPath : System.Environment.GetFolderPath(System.Environment.SpecialFolder.Desktop)), - Identifier); + _trainCarSetup.Identifier); if (assetBundleFullpath.Length != 0) { @@ -286,18 +243,15 @@ void OnGUI() BuildPipeline.BuildAssetBundles(assetBundleFullpath, trainCarBundleBuild, BuildAssetBundleOptions.None, BuildTarget.StandaloneWindows64); - Debug.Log($"Finished AssetBundle build for car: {Identifier}."); + Debug.Log($"Finished AssetBundle build for car: {_trainCarSetup.Identifier}."); #endregion - #region Create car.json file. - + // Create car.json file. ExportCarSettings(trainCarAssetBundleName, carFullPath); - #endregion - //Goto folder when finished building. - if(EditorUtility.DisplayDialog("Finished Build", $"Finished building car {Identifier} to path ({assetBundleFullpath}). Would you like to open the build folder?", "Yes", "No")) + if(EditorUtility.DisplayDialog("Finished Build", $"Finished building car {_trainCarSetup.Identifier} to path ({assetBundleFullpath}). Would you like to open the build folder?", "Yes", "No")) { EditorUtility.RevealInFinder(assetBundleFullpath); } @@ -379,8 +333,8 @@ private void ExportCarSettings( string assetBundleName, string outFilePath ) jsonfile.AddField(CarJSONKeys.BUNDLE_NAME, assetBundleName); jsonfile.AddField(CarJSONKeys.PREFAB_NAME, _trainCarSetup.gameObject.name); - jsonfile.AddField(CarJSONKeys.IDENTIFIER, Identifier); - jsonfile.AddField(CarJSONKeys.CAR_TYPE, (int)TrainCarType); + jsonfile.AddField(CarJSONKeys.IDENTIFIER, _trainCarSetup.Identifier); + jsonfile.AddField(CarJSONKeys.CAR_TYPE, (int)_trainCarSetup.BaseCarType); //Bogies jsonfile.AddField(CarJSONKeys.REPLACE_FRONT_BOGIE, _trainCarSetup.ReplaceFrontBogie); @@ -418,10 +372,6 @@ private void ResetWindow() //Reset internal data _trainCarSetup = null; - - //Reset identifier - Identifier = "Custom Car"; - TrainCarType = BaseTrainCarType.FlatbedEmpty; } } diff --git a/CCL_GameScripts/SimParamsBase.cs b/CCL_GameScripts/SimParamsBase.cs index c7cd964c..42843675 100644 --- a/CCL_GameScripts/SimParamsBase.cs +++ b/CCL_GameScripts/SimParamsBase.cs @@ -10,6 +10,14 @@ public enum LocoParamsType Steam = 2 } + public enum LocoRequiredLicense + { + None = 0, + DE2 = 1, + DE6 = 2, + Steam = 3, + } + public abstract class SimParamsBase : MonoBehaviour { [HideInInspector] @@ -17,6 +25,7 @@ public abstract class SimParamsBase : MonoBehaviour // default values from diesel [Header("Basic")] + public LocoRequiredLicense RequiredLicense = LocoRequiredLicense.None; public float MaxSpeed = 120f; public float SandCapacity = 200f; public float SandValveSpeed = 10f; diff --git a/CCL_GameScripts/TrainCarSetup.cs b/CCL_GameScripts/TrainCarSetup.cs index c37c6a9a..ec61ccd6 100644 --- a/CCL_GameScripts/TrainCarSetup.cs +++ b/CCL_GameScripts/TrainCarSetup.cs @@ -37,6 +37,11 @@ private void BringUpLocoSetup() protected override bool DestroyAfterCreation => false; + [Header("Basic")] + public string Identifier = "My New Car"; + public BaseTrainCarType BaseCarType; + public BaseCargoContainerType CargoClass = BaseCargoContainerType.None; + [Header("Bogie Replacement")] public Transform FrontBogie; public Transform RearBogie; diff --git a/DVCustomCarLoader/CarTypeInjector.cs b/DVCustomCarLoader/CarTypeInjector.cs new file mode 100644 index 00000000..ef8de092 --- /dev/null +++ b/DVCustomCarLoader/CarTypeInjector.cs @@ -0,0 +1,211 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using CCL_GameScripts; +using DV.Logic.Job; +using HarmonyLib; +using UnityEngine; + +namespace DVCustomCarLoader +{ + public static class CarTypeInjector + { + public const int CUSTOM_TYPE_OFFSET = 0x4000_0000; + public const int CUSTOM_TYPE_MASK = CUSTOM_TYPE_OFFSET - 1; + public const int CUSTOM_TYPE_MAX_VALUE = CUSTOM_TYPE_OFFSET + CUSTOM_TYPE_MASK; + + public const TrainCarType MissingCustomType = (TrainCarType)(-1); + + private static readonly Dictionary idToCarType = new Dictionary(StringComparer.CurrentCultureIgnoreCase); + private static readonly Dictionary idToCustomCar = new Dictionary(StringComparer.CurrentCultureIgnoreCase); + private static readonly Dictionary carTypeToCustomCar = new Dictionary(); + + public static bool TryGetCarTypeById( string id, out TrainCarType type ) => idToCarType.TryGetValue(id, out type); + public static bool TryGetCustomCarByType( TrainCarType carType, out CustomCar car ) => carTypeToCustomCar.TryGetValue(carType, out car); + public static bool TryGetCustomCarById( string id, out CustomCar car ) => idToCustomCar.TryGetValue(id, out car); + + public static bool IsCustomTypeRegistered( TrainCarType carType ) => carTypeToCustomCar.ContainsKey(carType); + public static bool IsCustomTypeRegistered( string identifier ) => idToCarType.ContainsKey(identifier); + + public static bool IsInCustomRange( TrainCarType carType ) => (int)carType >= CUSTOM_TYPE_OFFSET; + + // Reflected fields + private static readonly HashSet locomotivesMap; + private static readonly HashSet multipleUnitLocos; + private static readonly Dictionary CarTypeToContainerType; + + static CarTypeInjector() + { + locomotivesMap = AccessTools.Field(typeof(CarTypes), "locomotivesMap")?.GetValue(null) as HashSet; + if( locomotivesMap == null ) + { + Main.Error("Failed to get CarTypes.locomotivesMap"); + } + + multipleUnitLocos = AccessTools.Field(typeof(CarTypes), "multipleUnitLocos")?.GetValue(null) as HashSet; + if( multipleUnitLocos == null ) + { + Main.Error("Failed to get CarTypes.multipleUnitLocos"); + } + + CarTypeToContainerType = AccessTools.Field(typeof(CargoTypes), nameof(CargoTypes.CarTypeToContainerType))?.GetValue(null) + as Dictionary; + if( CarTypeToContainerType == null ) + { + Main.Error("Failed to get CargoTypes.CarTypeToContainerType"); + } + } + + private static TrainCarType GenerateUniqueCarType( string identifier ) + { + int hash = identifier.GetHashCode(); + hash = (hash & CUSTOM_TYPE_MASK) + CUSTOM_TYPE_OFFSET; + + // find an untaken TrainCarType + for( int searchWatchdog = CUSTOM_TYPE_MASK; searchWatchdog >= 0; searchWatchdog-- ) + { + TrainCarType candidateType = (TrainCarType)hash; + if( !IsCustomTypeRegistered(candidateType) ) + { + return candidateType; + } + + // move up by 1 and try again + hash += 1; + if( hash > CUSTOM_TYPE_MAX_VALUE ) hash = CUSTOM_TYPE_OFFSET; + } + + Main.Error("No available custom car types, something is VERY wrong"); + return TrainCarType.NotSet; + } + + public static TrainCarType RegisterCustomCarType( CustomCar car ) + { + string identifier = car.identifier.ToLower(); + + TrainCarType carType = GenerateUniqueCarType(identifier); + idToCarType.Add(identifier, carType); + carTypeToCustomCar.Add(carType, car); + idToCustomCar.Add(identifier, car); + + var trainCar = car.CarPrefab.GetComponent(); + trainCar.carType = carType; + + car.CarType = carType; + + InjectCarTypesData(car); + + return carType; + } + + private static void InjectCarTypesData( CustomCar car ) + { + if( car.LocoType != LocoParamsType.None ) + { + locomotivesMap?.Add(car.CarType); + + if( car.LocoType == LocoParamsType.DieselElectric ) + { + multipleUnitLocos?.Add(car.CarType); + } + } + + CarTypeToContainerType.Add(car.CarType, car.CargoClass); + } + } + + [HarmonyPatch(typeof(CarTypes), nameof(CarTypes.GetCarPrefab))] + public static class CarTypes_GetCarPrefab_Patch + { + public static bool Prefix( TrainCarType carType, ref GameObject __result ) + { + if( CarTypeInjector.IsInCustomRange(carType) ) + { + if( CarTypeInjector.TryGetCustomCarByType(carType, out CustomCar car) ) + { + __result = car.CarPrefab; + } + else + { + __result = null; + } + return false; + } + + return true; + } + } + + [HarmonyPatch(typeof(CarTypes), nameof(CarTypes.DisplayName))] + public static class CarTypes_DisplayName_Patch + { + public static bool Prefix( TrainCarType carType, ref string __result ) + { + if( CarTypeInjector.IsInCustomRange(carType) ) + { + if( CarTypeInjector.TryGetCustomCarByType(carType, out CustomCar car) ) + { + __result = car.identifier; + } + else + { + __result = null; + } + return false; + } + + return true; + } + } + + [HarmonyPatch(typeof(CarTypes))] + public static class CarTypes_LicenseCheck_Patches + { + private static bool DoesCarNeedLicense( TrainCarType carType, LocoRequiredLicense license ) + { + if( CarTypeInjector.TryGetCustomCarByType(carType, out CustomCar car) ) + { + return (car.RequiredLicense == license); + } + else return false; + } + + [HarmonyPrefix] + [HarmonyPatch(nameof(CarTypes.IsSteamLocomotive))] + public static bool IsSteamLocomotive( TrainCarType carType, ref bool __result ) + { + if( CarTypeInjector.IsInCustomRange(carType) ) + { + __result = DoesCarNeedLicense(carType, LocoRequiredLicense.Steam); + return false; + } + return true; + } + + [HarmonyPrefix] + [HarmonyPatch(nameof(CarTypes.IsShunterLocomotive))] + public static bool IsShunterLocomotive( TrainCarType carType, ref bool __result ) + { + if( CarTypeInjector.IsInCustomRange(carType) ) + { + __result = DoesCarNeedLicense(carType, LocoRequiredLicense.DE2); + return false; + } + return true; + } + + [HarmonyPrefix] + [HarmonyPatch(nameof(CarTypes.IsDieselLocomotive))] + public static bool IsDieselLocomotive( TrainCarType carType, ref bool __result ) + { + if( CarTypeInjector.IsInCustomRange(carType) ) + { + __result = DoesCarNeedLicense(carType, LocoRequiredLicense.DE6); + return false; + } + return true; + } + } +} diff --git a/DVCustomCarLoader/CustomCar.cs b/DVCustomCarLoader/CustomCar.cs index 22aac9b6..0846a74d 100644 --- a/DVCustomCarLoader/CustomCar.cs +++ b/DVCustomCarLoader/CustomCar.cs @@ -4,6 +4,7 @@ using HarmonyLib; using Object = UnityEngine.Object; using CCL_GameScripts; +using DV.Logic.Job; namespace DVCustomCarLoader { @@ -14,6 +15,11 @@ public class CustomCar /// public string identifier = "Custom Car"; + /// + /// Generated type enum for this car. + /// + public TrainCarType CarType = TrainCarType.NotSet; + /// /// The underlying type of this car. /// @@ -35,6 +41,10 @@ public class CustomCar //Couplers public Vector3 FrontCouplerPosition; public Vector3 RearCouplerPosition; + + public LocoParamsType LocoType { get; protected set; } + public LocoRequiredLicense RequiredLicense { get; protected set; } + public CargoContainerType CargoClass { get; protected set; } public bool FinalizePrefab() { @@ -341,6 +351,8 @@ public bool FinalizePrefab() } // setup traincar properties + CargoClass = (CargoContainerType)carSetup.CargoClass; + if( !carSetup.OverridePhysics ) { newCar.bogieDamping = baseCar.bogieDamping; @@ -356,6 +368,8 @@ public bool FinalizePrefab() if( simParams ) { LocoComponentManager.AddLocoSimulation(newFab, simParams); + LocoType = simParams.SimType; + RequiredLicense = simParams.RequiredLicense; if( carSetup.InteriorPrefab ) { diff --git a/DVCustomCarLoader/CustomCarManager.cs b/DVCustomCarLoader/CustomCarManager.cs index 8b2c924c..9c00de34 100644 --- a/DVCustomCarLoader/CustomCarManager.cs +++ b/DVCustomCarLoader/CustomCarManager.cs @@ -157,7 +157,15 @@ private static CustomCar CreateCustomCar( string directory, JSONObject jsonFile if( newCar.FinalizePrefab() ) { - return newCar; + if( CarTypeInjector.RegisterCustomCarType(newCar) != TrainCarType.NotSet ) + { + return newCar; + } + else + { + // TODO: Destroy failed car? + return null; + } } else return null; } diff --git a/DVCustomCarLoader/DVCustomCarLoader.csproj b/DVCustomCarLoader/DVCustomCarLoader.csproj index 0171abb8..d62f0376 100644 --- a/DVCustomCarLoader/DVCustomCarLoader.csproj +++ b/DVCustomCarLoader/DVCustomCarLoader.csproj @@ -74,9 +74,13 @@ + + + + diff --git a/DVCustomCarLoader/LocoComponents/CustomDieselReverserLock.cs b/DVCustomCarLoader/LocoComponents/CustomDieselReverserLock.cs new file mode 100644 index 00000000..5b15f6de --- /dev/null +++ b/DVCustomCarLoader/LocoComponents/CustomDieselReverserLock.cs @@ -0,0 +1,93 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using DV.CabControls.NonVR; +using UnityEngine; + +namespace DVCustomCarLoader.LocoComponents +{ + public class CustomDieselReverserLock : MonoBehaviour + { + private const float DRIVING_REVERSER_DRAG = 82f; + private const float DRIVING_REVERSER_ANGULAR_DRAG = 1000f; + + private CustomLocoControllerDiesel controller; + private Rigidbody rb; + private GrabHandlerHingeJoint nonVrGrab; + private LeverNonVR nonVrLever; + + private float normalDrag; + private float normalAngularDrag; + private bool reverserBlocked; + private bool initCompleted; + + private IEnumerator Start() + { + controller = TrainCar.Resolve(gameObject).GetComponent(); + + while( (rb = GetComponent()) == null ) + { + yield return WaitFor.SecondsRealtime(0.5f); + } + + nonVrGrab = GetComponent(); + nonVrLever = GetComponent(); + + normalAngularDrag = rb.angularDrag; + normalDrag = rb.drag; + initCompleted = true; + + yield break; + } + + private void SetBlocked( bool blocked ) + { + if( blocked == reverserBlocked ) + { + return; + } + + if( blocked ) + { + if( nonVrGrab ) + { + nonVrGrab.SetMovingDisabled(true); + } + + rb.drag = DRIVING_REVERSER_DRAG; + rb.angularDrag = DRIVING_REVERSER_ANGULAR_DRAG; + } + else + { + // not blocked + if( nonVrGrab ) + { + nonVrGrab.SetMovingDisabled(false); + } + + rb.drag = normalDrag; + rb.angularDrag = normalAngularDrag; + } + + if( nonVrLever ) + { + nonVrLever.IsScrollingBlocked = blocked; + } + + reverserBlocked = blocked; + } + + private void Update() + { + if( !initCompleted ) + { + return; + } + + SetBlocked(controller.targetThrottle > 0.05f && controller.EngineRunning); + } + } +} diff --git a/DVCustomCarLoader/LocoComponents/CustomLocoAudio.cs b/DVCustomCarLoader/LocoComponents/CustomLocoAudio.cs new file mode 100644 index 00000000..34a77808 --- /dev/null +++ b/DVCustomCarLoader/LocoComponents/CustomLocoAudio.cs @@ -0,0 +1,52 @@ +using System.Collections; +using UnityEngine; + +namespace DVCustomCarLoader.LocoComponents +{ + public abstract class CustomLocoAudio : LocoTrainAudio + { + } + + public abstract class CustomLocoAudio : CustomLocoAudio + where TCtrl : CustomLocoController + where TEvent : CustomLocoSimEvents + where TDmg : DamageControllerCustomLoco + { + protected TCtrl customLocoController; + protected TEvent simEvents; + protected TDmg customDmgController; + + protected override void SetupLocoLogic( TrainCar car ) + { + customLocoController = car.GetComponent(); + locoController = customLocoController; + + simEvents = car.GetComponent(); + + customDmgController = car.GetComponent(); + dmgController = customDmgController; + } + + protected override void UnsetLocoLogic() + { + customLocoController = null; + locoController = null; + customDmgController = null; + dmgController = null; + simEvents = null; + } + } + + public static class TrainComponentPool_RequestAudio_Patch + { + public static bool Prefix( TrainCar car ) + { + if( Main.CustomCarManagerInstance.IsRegisteredCustomCar(car) ) + { + return false; + } + + return true; + } + } +} \ No newline at end of file diff --git a/DVCustomCarLoader/LocoComponents/CustomLocoAudioDiesel.cs b/DVCustomCarLoader/LocoComponents/CustomLocoAudioDiesel.cs new file mode 100644 index 00000000..770db7a0 --- /dev/null +++ b/DVCustomCarLoader/LocoComponents/CustomLocoAudioDiesel.cs @@ -0,0 +1,249 @@ +using System.Collections; +using UnityEngine; + +namespace DVCustomCarLoader.LocoComponents +{ + public class CustomLocoAudioDiesel : + CustomLocoAudio< + CustomLocoControllerDiesel, + CustomDieselSimEvents, + DamageControllerCustomDiesel> + { + // Engine/Traction Sounds + public Transform playEngineAt; + + public LayeredAudio engineAudio; + public LayeredAudio enginePistonAudio; + public LayeredAudio electricMotorAudio; + + public LayeredAudio engineDamageAudio; + public AnimationCurve engineDamageToMasterVolumeCurve; + + public AudioClip engineOnClip; + public AudioClip engineOffClip; + + public float prevLocoEngineRpm; + public float enginePistonTargetVolume; + public float neutralEnginePistonVolume = 0.4f; + + // Reverser Lever + public AudioClip[] reverserClips; + public Transform playReverserAt; + public float reverserVolume = 1f; + public float reverserPitch = 1f; + + // Sanders + public Transform playSandAt; + public LayeredAudio sandAudio; + + // Horn + public LayeredAudio hornAudio; + + // Engine loop + protected bool IsEngineVolumeFadeActive; + protected Coroutine engineAudioCoro; + + #region Setup & Teardown + + protected override void SetupLocoLogic( TrainCar car ) + { + base.SetupLocoLogic(car); + + simEvents.EngineRunningChanged.Register(OnEngineStateChanged); + } + + protected override void UnsetLocoLogic() + { + if( simEvents != null ) + { + simEvents.EngineRunningChanged.Unregister(OnEngineStateChanged); + } + + if( engineAudioCoro != null ) + { + StopCoroutine(engineAudioCoro); + } + + base.UnsetLocoLogic(); + } + + public override void SetupForCar( TrainCar car ) + { + base.SetupForCar(car); + + // Setup Horn + } + + protected override void ReturnToPool() + { + UnsetLocoLogic(); + base.ReturnToPool(); + } + + protected override void ResetAllAudio() + { + base.ResetAllAudio(); + float newVolume = customLocoController.EngineRunning ? 1 : 0; + + if( engineAudio != null ) + { + engineAudio.Reset(); + engineAudio.masterVolume = newVolume; + } + if( enginePistonAudio != null ) + { + enginePistonAudio.Reset(); + enginePistonAudio.masterVolume = newVolume; + } + if( electricMotorAudio != null ) + { + electricMotorAudio.Reset(); + electricMotorAudio.masterVolume = newVolume; + } + if( engineDamageAudio != null ) + { + engineDamageAudio.Reset(); + engineDamageAudio.masterVolume = 0f; + } + if( sandAudio != null ) + { + sandAudio.Reset(); + } + if( hornAudio != null ) + { + hornAudio.Reset(); + } + } + + #endregion + + #region Game Audio Triggers + + protected override void Update() + { + base.Update(); + + if( !timeFlow || !Car ) return; + + float engineRPM = customLocoController.EngineRPM; + engineAudio.Set(engineRPM); + enginePistonAudio.Set(engineRPM); + electricMotorAudio.Set(locoController.GetSpeedKmH() / 100f); + + if( engineDamageAudio ) + { + engineDamageAudio.Set(engineRPM); + engineDamageAudio.masterVolume = + engineDamageToMasterVolumeCurve.Evaluate(customDmgController.engine.DamagePercentage); + } + + if( sandAudio ) + { + sandAudio.Set(locoController.GetSandersFlow()); + } + + if( customLocoController.EngineRunning && !IsEngineVolumeFadeActive ) + { + float deltaRPM = engineRPM - prevLocoEngineRpm; + if( deltaRPM > -0.0001f ) + { + enginePistonTargetVolume = deltaRPM * 1000f; + + if( enginePistonTargetVolume < neutralEnginePistonVolume ) + { + enginePistonTargetVolume = neutralEnginePistonVolume; + } + } + else + { + enginePistonTargetVolume = 0f; + } + + enginePistonTargetVolume = Mathf.Clamp01(enginePistonTargetVolume); + + // Ramp piston volume up/down to target + if( enginePistonAudio.masterVolume < enginePistonTargetVolume ) + { + enginePistonAudio.masterVolume += 0.001f + (enginePistonAudio.masterVolume / 16f); + } + else if( enginePistonAudio.masterVolume > enginePistonTargetVolume ) + { + enginePistonAudio.masterVolume -= enginePistonAudio.masterVolume / 32f; + } + + enginePistonAudio.masterVolume = Mathf.Clamp01(enginePistonAudio.masterVolume); + prevLocoEngineRpm = engineRPM; + } + } + + protected void PlayReverser() + { + if( reverserClips != null && reverserClips.Length != 0 ) + { + Transform playAt = playReverserAt ? playReverserAt : transform; + reverserClips.Play(playAt.position, reverserVolume, 1f, 0f, 1f, 500f, default(AudioSourceCurves), AudioManager.e.cabGroup, playAt); + } + } + + #endregion + + #region Engine Loop + + private void OnEngineStateChanged( bool state ) + { + if( engineAudioCoro != null ) + { + StopCoroutine(engineAudioCoro); + } + engineAudioCoro = StartCoroutine(EngineStartupShutdown(state)); + } + + private IEnumerator EngineStartupShutdown( bool engineTurnedOn ) + { + IsEngineVolumeFadeActive = true; + + if( engineTurnedOn ) + { + engineOnClip.Play(playEngineAt.position, 1f, 1f, 0f, 1f, 500f, default(AudioSourceCurves), null, playEngineAt); + } + else + { + engineOffClip.Play(playEngineAt.position, 1f, 1f, 0f, 1f, 500f, default(AudioSourceCurves), null, playEngineAt); + } + + float onOffDelay = engineTurnedOn ? (engineOnClip.length * 0.15f) : (engineOffClip.length * 0.1f); + yield return WaitFor.Seconds(onOffDelay); + + float startTime = Time.realtimeSinceStartup; + float duration = engineTurnedOn ? 2f : 1f; + + float startEngineVolume = engineAudio.masterVolume; + float startPistonVolume = enginePistonAudio.masterVolume; + float startElectricVolume = electricMotorAudio.masterVolume; + + int endEngineVolume = engineTurnedOn ? 1 : 0; + float endPistonVolume = engineTurnedOn ? neutralEnginePistonVolume : 0f; + + // Ramp engine volumes up/down + while( true ) + { + float durationPercent = (Time.realtimeSinceStartup - startTime) / duration; + engineAudio.masterVolume = Mathf.Lerp(startEngineVolume, endEngineVolume, durationPercent); + enginePistonAudio.masterVolume = Mathf.Lerp(startPistonVolume, endPistonVolume, durationPercent); + electricMotorAudio.masterVolume = Mathf.Lerp(startElectricVolume, endEngineVolume, durationPercent); + + if( durationPercent >= 1f ) + { + break; + } + yield return null; + } + + IsEngineVolumeFadeActive = false; + engineAudioCoro = null; + yield break; + } + + #endregion + } +} \ No newline at end of file diff --git a/DVCustomCarLoader/LocoComponents/LocoComponentManager.cs b/DVCustomCarLoader/LocoComponents/LocoComponentManager.cs index adc6e27f..20102081 100644 --- a/DVCustomCarLoader/LocoComponents/LocoComponentManager.cs +++ b/DVCustomCarLoader/LocoComponents/LocoComponentManager.cs @@ -58,7 +58,7 @@ public static void AddDieselSimulation( GameObject prefab, SimParamsDiesel simPa var locoController = prefab.AddComponent(); // setup multiple unit module - var muHoses = prefab.GetComponentsInChildren(); + var muHoses = prefab.GetComponentsInChildren(true); if( muHoses.Length == 0 ) { Main.Warning($"Could not find MU hose adapters for diesel engine"); @@ -79,6 +79,8 @@ public static void AddDieselSimulation( GameObject prefab, SimParamsDiesel simPa muModule.rearCableAdapter = muHoseAdapter; } } + + Main.Log("Added multiple unit module"); } locoController.drivingForce = drivingForce; @@ -195,6 +197,23 @@ public static void CreateCopiedControls( GameObject root, CabInputSetup cabSetup } } + // handle special case of reverser limiter (if copied directly, causes errors) + if( copySpec is CopiedLever lever ) + { + if( lever.LeverType == CopiedLeverType.ReverserShunter ) + { + var reverserLock = newControl.GetComponentInChildren(); + if( reverserLock ) UnityEngine.Object.Destroy(reverserLock); + newControl.AddComponent(); + } + else if( lever.LeverType == CopiedLeverType.ReverserDE6 ) + { + var reverserLock = newControl.GetComponentInChildren(); + if( reverserLock ) UnityEngine.Object.Destroy(reverserLock); + newControl.AddComponent(); + } + } + cabSetup.SetInputObject(input.InputBinding, newControl); } else if( copySpec is CopiedCabIndicator indicator ) diff --git a/DVCustomCarLoader/ModPatches.cs b/DVCustomCarLoader/ModPatches.cs index 8bfc87da..bd4d5a6d 100644 --- a/DVCustomCarLoader/ModPatches.cs +++ b/DVCustomCarLoader/ModPatches.cs @@ -7,17 +7,17 @@ namespace DVCustomCarLoader { - [HarmonyPatch(typeof(TrainCar), "InitAudio")] - public static class TrainCar_InitAudioPatch - { - [HarmonyPriority(Priority.First)] - static bool Prefix( TrainCar __instance ) - { - var simParams = __instance.gameObject.GetComponent(); - if( simParams ) return false; - return true; - } - } + //[HarmonyPatch(typeof(TrainCar), "InitAudio")] + //public static class TrainCar_InitAudioPatch + //{ + // [HarmonyPriority(Priority.First)] + // static bool Prefix( TrainCar __instance ) + // { + // var simParams = __instance.gameObject.GetComponent(); + // if( simParams ) return false; + // return true; + // } + //} public static class LocoLights_Patch { diff --git a/DVCustomCarLoader/SaveLoadPatches.cs b/DVCustomCarLoader/SaveLoadPatches.cs index 673f8a45..d6621f67 100644 --- a/DVCustomCarLoader/SaveLoadPatches.cs +++ b/DVCustomCarLoader/SaveLoadPatches.cs @@ -28,7 +28,7 @@ public static void Postfix( TrainCar car, ref JObject __result ) } } } - + [HarmonyPatch(typeof(CarsSaveManager), "InstantiateCar")] public static class CarsSaveManager_InstantiateCar_Patch { @@ -46,8 +46,7 @@ public static bool Prefix( JObject carData, RailTrack[] tracks, ref TrainCar __r return true; } - CustomCar customCarType = Main.CustomCarManagerInstance.CustomCarsToSpawn.Find(cc => cc.identifier == customType); - if( customCarType == null ) + if( !CarTypeInjector.TryGetCustomCarById(customType, out CustomCar customCarType) ) { Main.ModEntry.Logger.Warning($"Found a saved custom car of unknown type ({customType}), skipping this car"); __result = null; @@ -132,23 +131,5 @@ public static bool Prefix( JObject carData, RailTrack[] tracks, ref TrainCar __r __result = trainCar; return false; } - - //public static void Postfix( JObject carData, TrainCar __result ) - //{ - // string customType = carData.GetString(SaveConstants.CUSTOM_CAR_KEY); - // if( customType != null ) - // { - // CustomCar match = Main.CustomCarManagerInstance.CustomCarsToSpawn.Find(cc => cc.identifier == customType); - // if( match != null ) - // { - // //match.Spawn(__result); - // Main.ModEntry.Logger.Warning($"Reverting {match.identifier} to its base type"); - // } - // else - // { - // Main.ModEntry.Logger.Warning("Tried to instantiate a custom car of unknown type, reverting to its base type"); - // } - // } - //} } } From 4a9131fef001127069f27e266f907575d01d9bc9 Mon Sep 17 00:00:00 2001 From: Katy Fox Date: Sun, 15 Aug 2021 16:37:45 -0400 Subject: [PATCH 19/20] staticify CustomCarManager, get loco audio working --- CCL_GameScripts/ComponentInitSpec.cs | 10 +- CCL_GameScripts/SimParamsBase.cs | 11 ++ CCL_GameScripts/SimParamsDiesel.cs | 4 + DVCustomCarLoader/CarTypeInjector.cs | 111 ++++++++++++++++++ .../CommsRadioController_Awake_Patch.cs | 2 +- .../CommsRadioCustomCarManager.cs | 10 +- DVCustomCarLoader/CustomCar.cs | 20 ++-- DVCustomCarLoader/CustomCarManager.cs | 21 ++-- .../LocoComponents/CustomLocoAudio.cs | 96 ++++++++++++--- .../LocoComponents/CustomLocoAudioDiesel.cs | 35 ++++-- DVCustomCarLoader/Main.cs | 10 +- DVCustomCarLoader/SaveLoadPatches.cs | 2 +- 12 files changed, 270 insertions(+), 62 deletions(-) diff --git a/CCL_GameScripts/ComponentInitSpec.cs b/CCL_GameScripts/ComponentInitSpec.cs index ebb4a82f..26f237b3 100644 --- a/CCL_GameScripts/ComponentInitSpec.cs +++ b/CCL_GameScripts/ComponentInitSpec.cs @@ -37,7 +37,9 @@ public object CreateRealComponent( Func findTypeFunc, Action(); foreach( var proxy in proxies ) { - FieldInfo targetField = targetType.GetField(proxy.TargetName); + string targetName = proxy.TargetName ?? sourceField.Name; + FieldInfo targetField = targetType.GetField(targetName); + if( targetField != null ) { Type assignValueType; @@ -86,12 +88,12 @@ public object CreateRealComponent( Func findTypeFunc, Action LocoParamsType.DieselElectric; + [Header("Audio")] + public bool UseBigDieselAudio = false; + public override LocoAudioBasis AudioType => UseBigDieselAudio ? LocoAudioBasis.DE6 : LocoAudioBasis.DE2; + [Header("Throttle")] public float ThrottleUpRate = 2f; public float ThrottleDownRate = 2f; diff --git a/DVCustomCarLoader/CarTypeInjector.cs b/DVCustomCarLoader/CarTypeInjector.cs index ef8de092..91f4935e 100644 --- a/DVCustomCarLoader/CarTypeInjector.cs +++ b/DVCustomCarLoader/CarTypeInjector.cs @@ -5,6 +5,7 @@ using System.Threading.Tasks; using CCL_GameScripts; using DV.Logic.Job; +using DVCustomCarLoader.LocoComponents; using HarmonyLib; using UnityEngine; @@ -114,6 +115,102 @@ private static void InjectCarTypesData( CustomCar car ) CarTypeToContainerType.Add(car.CarType, car.CargoClass); } + + #region Audio Pooling + + private static GameObject GetPooledAudioPrefab( TrainComponentPool componentPool, TrainCarType carType ) + { + foreach( var poolData in componentPool.audioPoolReferences.poolData ) + { + if( poolData.trainCarType == carType ) + { + return poolData.audioPrefab; + } + } + + return null; + } + + private static GameObject CopyAudioPrefab( GameObject sourcePrefab ) + where TAudio : CustomLocoAudio + { + GameObject newFab = UnityEngine.Object.Instantiate(sourcePrefab, null); + newFab.SetActive(false); + UnityEngine.Object.DontDestroyOnLoad(newFab); + + var origAudio = newFab.GetComponentInChildren(); + if( origAudio ) + { + Main.Log($"Adding audio {typeof(TAudio).Name}"); + TAudio newAudio = origAudio.gameObject.AddComponent(); + newAudio.PullSettingsFromOtherAudio(origAudio); + UnityEngine.Object.DestroyImmediate(origAudio); + + // grab extra components + newAudio.carFrictionSound = newFab.GetComponentInChildren(true); + newAudio.carCollisionSounds = newFab.GetComponentInChildren(true); + newAudio.trainDerailAudio = newFab.GetComponentInChildren(true); + } + else + { + Main.Warning($"Couldn't find LocoTrainAudio on prefab {sourcePrefab.name}"); + } + + return newFab; + } + + public static void InjectLocoAudioToPool( CustomCar car, TrainComponentPool componentPool ) + { + const int LOCO_POOL_SIZE = 10; + + GameObject sourcePrefab; + GameObject newPrefab; + + switch( car.LocoAudioType ) + { + case LocoAudioBasis.DE2: + sourcePrefab = GetPooledAudioPrefab(componentPool, TrainCarType.LocoShunter); + if( sourcePrefab ) + { + newPrefab = CopyAudioPrefab(sourcePrefab); + + var newPoolData = new AudioPoolReferences.AudioPoolData() + { + trainCarType = car.CarType, + audioPrefab = newPrefab, + poolSize = LOCO_POOL_SIZE + }; + + componentPool.audioPoolReferences.poolData.Add(newPoolData); + } + else Main.Warning("Couldn't find shunter pooled audio"); + break; + + case LocoAudioBasis.DE6: + sourcePrefab = GetPooledAudioPrefab(componentPool, TrainCarType.LocoDiesel); + if( sourcePrefab ) + { + newPrefab = CopyAudioPrefab(sourcePrefab); + + var newPoolData = new AudioPoolReferences.AudioPoolData() + { + trainCarType = car.CarType, + audioPrefab = newPrefab, + poolSize = LOCO_POOL_SIZE + }; + + componentPool.audioPoolReferences.poolData.Add(newPoolData); + } + else Main.Warning("Couldn't find DE6 pooled audio"); + break; + + case LocoAudioBasis.Steam: + default: + break; + } + } + + #endregion } [HarmonyPatch(typeof(CarTypes), nameof(CarTypes.GetCarPrefab))] @@ -208,4 +305,18 @@ public static bool IsDieselLocomotive( TrainCarType carType, ref bool __result ) return true; } } + + [HarmonyPatch(typeof(TrainComponentPool), "Awake")] + public static class TrainComponentPool_Awake_Patch + { + public static void Prefix( TrainComponentPool __instance ) + { + Main.Log("Injecting custom cars into component pool"); + + foreach( CustomCar car in CustomCarManager.CustomCarTypes ) + { + CarTypeInjector.InjectLocoAudioToPool(car, __instance); + } + } + } } diff --git a/DVCustomCarLoader/CommsRadioController_Awake_Patch.cs b/DVCustomCarLoader/CommsRadioController_Awake_Patch.cs index 1124cd59..41da4a2e 100644 --- a/DVCustomCarLoader/CommsRadioController_Awake_Patch.cs +++ b/DVCustomCarLoader/CommsRadioController_Awake_Patch.cs @@ -19,7 +19,7 @@ static void Postfix(CommsRadioController __instance, ref List _ try { - if (Main.CustomCarManagerInstance.CustomCarsToSpawn.Count <= 0) + if( CustomCarManager.CustomCarTypes.Count <= 0 ) return; //Add our spawner to the comms radio diff --git a/DVCustomCarLoader/CommsRadioCustomCarManager.cs b/DVCustomCarLoader/CommsRadioCustomCarManager.cs index 31330d5a..3b72ef57 100644 --- a/DVCustomCarLoader/CommsRadioCustomCarManager.cs +++ b/DVCustomCarLoader/CommsRadioCustomCarManager.cs @@ -134,7 +134,7 @@ public void OnUse() switch (state) { case State.EnterSpawnMode: - SetCarToSpawn(Main.CustomCarManagerInstance.CustomCarsToSpawn[selectedCarTypeIndex]); + SetCarToSpawn(CustomCarManager.CustomCarTypes[selectedCarTypeIndex]); SetState(State.PickCar); break; case State.PickCar: @@ -240,9 +240,9 @@ public bool ButtonACustomAction() { case State.PickCar: selectedCarTypeIndex = selectedCarTypeIndex <= 0 - ? Main.CustomCarManagerInstance.CustomCarsToSpawn.Count - 1 + ? CustomCarManager.CustomCarTypes.Count - 1 : selectedCarTypeIndex - 1; - SetCarToSpawn(Main.CustomCarManagerInstance.CustomCarsToSpawn[selectedCarTypeIndex]); + SetCarToSpawn(CustomCarManager.CustomCarTypes[selectedCarTypeIndex]); return true; case State.PickDestination: if (!canSpawnAtPoint) return false; @@ -260,8 +260,8 @@ public bool ButtonBCustomAction() switch (state) { case State.PickCar: - selectedCarTypeIndex = (selectedCarTypeIndex + 1) % Main.CustomCarManagerInstance.CustomCarsToSpawn.Count; - SetCarToSpawn(Main.CustomCarManagerInstance.CustomCarsToSpawn[selectedCarTypeIndex]); + selectedCarTypeIndex = (selectedCarTypeIndex + 1) % CustomCarManager.CustomCarTypes.Count; + SetCarToSpawn(CustomCarManager.CustomCarTypes[selectedCarTypeIndex]); return true; case State.PickDestination: if (!canSpawnAtPoint) return false; diff --git a/DVCustomCarLoader/CustomCar.cs b/DVCustomCarLoader/CustomCar.cs index 0846a74d..84e4af59 100644 --- a/DVCustomCarLoader/CustomCar.cs +++ b/DVCustomCarLoader/CustomCar.cs @@ -41,10 +41,11 @@ public class CustomCar //Couplers public Vector3 FrontCouplerPosition; public Vector3 RearCouplerPosition; - - public LocoParamsType LocoType { get; protected set; } - public LocoRequiredLicense RequiredLicense { get; protected set; } - public CargoContainerType CargoClass { get; protected set; } + + public LocoParamsType LocoType { get; protected set; } = LocoParamsType.None; + public LocoAudioBasis LocoAudioType { get; protected set; } = LocoAudioBasis.None; + public LocoRequiredLicense RequiredLicense { get; protected set; } = LocoRequiredLicense.None; + public CargoContainerType CargoClass { get; protected set; } = CargoContainerType.None; public bool FinalizePrefab() { @@ -362,13 +363,14 @@ public bool FinalizePrefab() newCar.wheelRadius = baseCar.wheelRadius; } - newCar.carType = BaseCarType; + newCar.carType = CarType; var simParams = newFab.GetComponent(); if( simParams ) { LocoComponentManager.AddLocoSimulation(newFab, simParams); LocoType = simParams.SimType; + LocoAudioType = simParams.AudioType; RequiredLicense = simParams.RequiredLicense; if( carSetup.InteriorPrefab ) @@ -428,8 +430,8 @@ public TrainCar SpawnCar( RailTrack track, Vector3 position, Vector3 forward, bo spawnedCar.InitializeNewLogicCar(); spawnedCar.SetTrack(track, position, forward); - spawnedCar.OnDestroyCar += Main.CustomCarManagerInstance.DeregisterCar; - Main.CustomCarManagerInstance.RegisterSpawnedCar(spawnedCar, identifier); + spawnedCar.OnDestroyCar += CustomCarManager.DeregisterCar; + CustomCarManager.RegisterSpawnedCar(spawnedCar, identifier); RaiseCarSpawned(spawnedCar); return spawnedCar; @@ -474,8 +476,8 @@ public TrainCar SpawnLoadedCar( spawnedCar.rearCoupler.forceCoupleStateOnLoad = true; spawnedCar.rearCoupler.loadedCoupledState = couplerRCoupled; - spawnedCar.OnDestroyCar += Main.CustomCarManagerInstance.DeregisterCar; - Main.CustomCarManagerInstance.RegisterSpawnedCar(spawnedCar, identifier); + spawnedCar.OnDestroyCar += CustomCarManager.DeregisterCar; + CustomCarManager.RegisterSpawnedCar(spawnedCar, identifier); RaiseCarSpawned(spawnedCar); return spawnedCar; diff --git a/DVCustomCarLoader/CustomCarManager.cs b/DVCustomCarLoader/CustomCarManager.cs index 9c00de34..0f783dac 100644 --- a/DVCustomCarLoader/CustomCarManager.cs +++ b/DVCustomCarLoader/CustomCarManager.cs @@ -6,37 +6,34 @@ namespace DVCustomCarLoader { - public class CustomCarManager : MonoBehaviour + public static class CustomCarManager { - public List CustomCarsToSpawn; + public static List CustomCarTypes = new List(); - private readonly Dictionary SpawnedCustomCarIds = new Dictionary(); + private static readonly Dictionary SpawnedCustomCarIds = new Dictionary(); - public bool IsRegisteredCustomCar( TrainCar trainCar ) + public static bool IsRegisteredCustomCar( TrainCar trainCar ) { return SpawnedCustomCarIds.ContainsKey(trainCar); } - public bool TryGetCustomCarId( TrainCar trainCar, out string id ) + public static bool TryGetCustomCarId( TrainCar trainCar, out string id ) { return SpawnedCustomCarIds.TryGetValue(trainCar, out id); } - public void RegisterSpawnedCar( TrainCar car, string identifier ) + public static void RegisterSpawnedCar( TrainCar car, string identifier ) { SpawnedCustomCarIds[car] = identifier; } - public void DeregisterCar( TrainCar car ) + public static void DeregisterCar( TrainCar car ) { SpawnedCustomCarIds.Remove(car); } - public void Setup() + public static void Setup() { - - CustomCarsToSpawn = new List(); - //Load all json files string bundlePath = Path.Combine(Main.ModEntry.Path, "Cars"); @@ -75,7 +72,7 @@ public void Setup() if( newCar != null ) { - CustomCarsToSpawn.Add(newCar); + CustomCarTypes.Add(newCar); Main.ModEntry.Logger.Log($"Successfully added new car to spawn list: {newCar.identifier}"); } else diff --git a/DVCustomCarLoader/LocoComponents/CustomLocoAudio.cs b/DVCustomCarLoader/LocoComponents/CustomLocoAudio.cs index 34a77808..ea3c8da1 100644 --- a/DVCustomCarLoader/LocoComponents/CustomLocoAudio.cs +++ b/DVCustomCarLoader/LocoComponents/CustomLocoAudio.cs @@ -1,10 +1,89 @@ -using System.Collections; -using UnityEngine; +using System; +using System.Collections; +using System.Linq; +using System.Reflection; +using CCL_GameScripts; +using HarmonyLib; namespace DVCustomCarLoader.LocoComponents { public abstract class CustomLocoAudio : LocoTrainAudio { + private static readonly FieldInfo bogieAudioField = AccessTools.Field(typeof(TrainAudio), "bogieAudioControllers"); + + protected override void Awake() + { + if( bogieAudioField?.GetValue(this) is BogieAudioController[] bogieAudios ) + { + for( int i = 0; i < bogieAudios.Length; i++ ) + { + bogieAudios[i].DestroyAllSources(); + bogieAudios[i] = null; + } + } + base.Awake(); + } + + public override void SetupForCar( TrainCar car ) + { + if( !(bogieAudioField?.GetValue(this) is BogieAudioController[] bogieAudios) ) + { + Main.Log($"Pre-awaking audio {GetType()}"); + Awake(); + } + + base.SetupForCar(car); + } + + public void PullSettingsFromOtherAudio( LocoTrainAudio other ) + { + Type localType = GetType(); + Type targetType = other.GetType(); + + foreach( FieldInfo localField in localType.GetFields() ) + { + var proxies = localField.GetCustomAttributes().OfType(); + foreach( var proxy in proxies ) + { + string targetName = proxy.TargetName ?? localField.Name; + FieldInfo targetField = targetType.GetField(targetName); + + if( targetField != null ) + { + // direct assignment + if( localField.FieldType.IsAssignableFrom(targetField.FieldType) ) + { + localField.SetValue(this, targetField.GetValue(other)); + } + else + { + Main.Warning($"Proxy {localType.Name}.{localField.Name} is not assignable from {targetType.Name}.{targetName}"); + } + } + else + { + Main.Warning($"From audio type {localType.Name} - target {targetName} not found on {targetType.Name}"); + } + } + } + + // Brakes + doBrakeAudio = other.doBrakeAudio; + doBrakeAirflowAudio = other.doBrakeAirflowAudio; + brakeAudio = other.brakeAudio; + brakeSquealAudio = other.brakeSquealAudio; + brakeCylinderExhaustAudio = other.brakeCylinderExhaustAudio; + airflowAudio = other.airflowAudio; + brakeVolumeSpeedCurve = other.brakeVolumeSpeedCurve; + brakeSquealVolumeSpeedCurve = other.brakeSquealVolumeSpeedCurve; + + // Wheels + wheelslipAudio1 = other.wheelslipAudio1; + wheelslipAudio2 = other.wheelslipAudio2; + wheelDamageToMasterVolumeCurve = other.wheelDamageToMasterVolumeCurve; + wheelDamagedAudio1 = other.wheelDamagedAudio1; + wheelDamagedAudio2 = other.wheelDamagedAudio2; + } } public abstract class CustomLocoAudio : CustomLocoAudio @@ -36,17 +115,4 @@ protected override void UnsetLocoLogic() simEvents = null; } } - - public static class TrainComponentPool_RequestAudio_Patch - { - public static bool Prefix( TrainCar car ) - { - if( Main.CustomCarManagerInstance.IsRegisteredCustomCar(car) ) - { - return false; - } - - return true; - } - } } \ No newline at end of file diff --git a/DVCustomCarLoader/LocoComponents/CustomLocoAudioDiesel.cs b/DVCustomCarLoader/LocoComponents/CustomLocoAudioDiesel.cs index 770db7a0..4db3602e 100644 --- a/DVCustomCarLoader/LocoComponents/CustomLocoAudioDiesel.cs +++ b/DVCustomCarLoader/LocoComponents/CustomLocoAudioDiesel.cs @@ -1,5 +1,7 @@ using System.Collections; +using System.Linq; using UnityEngine; +using CCL_GameScripts; namespace DVCustomCarLoader.LocoComponents { @@ -10,33 +12,51 @@ public class CustomLocoAudioDiesel : DamageControllerCustomDiesel> { // Engine/Traction Sounds + [ProxyField] public Transform playEngineAt; + [ProxyField] public LayeredAudio engineAudio; + [ProxyField] public LayeredAudio enginePistonAudio; + [ProxyField] public LayeredAudio electricMotorAudio; + [ProxyField] public LayeredAudio engineDamageAudio; + [ProxyField] public AnimationCurve engineDamageToMasterVolumeCurve; + [ProxyField] public AudioClip engineOnClip; + [ProxyField] public AudioClip engineOffClip; + [ProxyField] public float prevLocoEngineRpm; + [ProxyField] public float enginePistonTargetVolume; + [ProxyField] public float neutralEnginePistonVolume = 0.4f; // Reverser Lever + [ProxyField] public AudioClip[] reverserClips; + [ProxyField] public Transform playReverserAt; + [ProxyField] public float reverserVolume = 1f; + [ProxyField] public float reverserPitch = 1f; // Sanders + [ProxyField] public Transform playSandAt; + [ProxyField] public LayeredAudio sandAudio; // Horn + [ProxyField] public LayeredAudio hornAudio; // Engine loop @@ -50,6 +70,7 @@ protected override void SetupLocoLogic( TrainCar car ) base.SetupLocoLogic(car); simEvents.EngineRunningChanged.Register(OnEngineStateChanged); + customLocoController.OnReverserChanged += PlayReverser; } protected override void UnsetLocoLogic() @@ -69,7 +90,7 @@ protected override void UnsetLocoLogic() public override void SetupForCar( TrainCar car ) { - base.SetupForCar(car); + base.SetupForCar(car); // Setup Horn } @@ -85,31 +106,31 @@ protected override void ResetAllAudio() base.ResetAllAudio(); float newVolume = customLocoController.EngineRunning ? 1 : 0; - if( engineAudio != null ) + if( engineAudio ) { engineAudio.Reset(); engineAudio.masterVolume = newVolume; } - if( enginePistonAudio != null ) + if( enginePistonAudio ) { enginePistonAudio.Reset(); enginePistonAudio.masterVolume = newVolume; } - if( electricMotorAudio != null ) + if( electricMotorAudio ) { electricMotorAudio.Reset(); electricMotorAudio.masterVolume = newVolume; } - if( engineDamageAudio != null ) + if( engineDamageAudio ) { engineDamageAudio.Reset(); engineDamageAudio.masterVolume = 0f; } - if( sandAudio != null ) + if( sandAudio ) { sandAudio.Reset(); } - if( hornAudio != null ) + if( hornAudio ) { hornAudio.Reset(); } diff --git a/DVCustomCarLoader/Main.cs b/DVCustomCarLoader/Main.cs index e36609ab..56012781 100644 --- a/DVCustomCarLoader/Main.cs +++ b/DVCustomCarLoader/Main.cs @@ -10,7 +10,6 @@ namespace DVCustomCarLoader { public static class Main { - public static CustomCarManager CustomCarManagerInstance; public static CommsRadioCustomCarManager CommsRadioCustomCarManager; public static UnityModManager.ModEntry ModEntry; public static bool Enabled; @@ -28,12 +27,7 @@ public static bool Load(UnityModManager.ModEntry modEntry) ModEntry.Logger.Log("Creating CustomCarManager"); Application.quitting += AppQuitWatcher.OnAppQuit; - - var nsmgr = new GameObject("[CustomCarManagerInstance]"); - Object.DontDestroyOnLoad(nsmgr); - nsmgr.transform.SetSiblingIndex(0); - CustomCarManagerInstance = nsmgr.AddComponent(); - CustomCarManagerInstance.Setup(); + CustomCarManager.Setup(); PlayerManager.CarChanged += OnCarChanged; @@ -42,7 +36,7 @@ public static bool Load(UnityModManager.ModEntry modEntry) private static void OnCarChanged( TrainCar newCar ) { - if( newCar && CustomCarManagerInstance.IsRegisteredCustomCar(newCar) ) + if( newCar && CustomCarManager.IsRegisteredCustomCar(newCar) ) { // diesel autostart var locoController = newCar.gameObject.GetComponent(); diff --git a/DVCustomCarLoader/SaveLoadPatches.cs b/DVCustomCarLoader/SaveLoadPatches.cs index d6621f67..f00162d2 100644 --- a/DVCustomCarLoader/SaveLoadPatches.cs +++ b/DVCustomCarLoader/SaveLoadPatches.cs @@ -21,7 +21,7 @@ public static class CarsSaveManager_GetSaveData_Patch { public static void Postfix( TrainCar car, ref JObject __result ) { - if( Main.CustomCarManagerInstance.TryGetCustomCarId(car, out string id) ) + if( CustomCarManager.TryGetCustomCarId(car, out string id) ) { // custom car detected, save its type __result.SetString(SaveConstants.CUSTOM_CAR_KEY, id); From 4f2405b05bf6857deaec71f02666c9e3380fdc79 Mon Sep 17 00:00:00 2001 From: Katy Fox Date: Mon, 16 Aug 2021 07:52:44 -0400 Subject: [PATCH 20/20] Integrate custom types to base car spawner, fix save/load --- DVCustomCarLoader/CarSpawner_Patches.cs | 140 +++++++ DVCustomCarLoader/CarTypeInjector.cs | 4 +- .../CommsRadioCarSpawner_Awake_Patch.cs | 28 -- .../CommsRadioController_Awake_Patch.cs | 52 --- .../CommsRadioCustomCarManager.cs | 376 ------------------ DVCustomCarLoader/CustomCar.cs | 88 ---- DVCustomCarLoader/CustomCarManager.cs | 42 +- DVCustomCarLoader/DVCustomCarLoader.csproj | 4 +- DVCustomCarLoader/Main.cs | 7 +- DVCustomCarLoader/SaveLoadPatches.cs | 11 +- 10 files changed, 175 insertions(+), 577 deletions(-) create mode 100644 DVCustomCarLoader/CarSpawner_Patches.cs delete mode 100644 DVCustomCarLoader/CommsRadioCarSpawner_Awake_Patch.cs delete mode 100644 DVCustomCarLoader/CommsRadioController_Awake_Patch.cs delete mode 100644 DVCustomCarLoader/CommsRadioCustomCarManager.cs diff --git a/DVCustomCarLoader/CarSpawner_Patches.cs b/DVCustomCarLoader/CarSpawner_Patches.cs new file mode 100644 index 00000000..3f35494c --- /dev/null +++ b/DVCustomCarLoader/CarSpawner_Patches.cs @@ -0,0 +1,140 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using DV; +using HarmonyLib; +using UnityEngine; + +namespace DVCustomCarLoader +{ + [HarmonyPatch(typeof(CommsRadioCarSpawner), nameof(CommsRadioCarSpawner.UpdateCarTypesToSpawn))] + public static class CommsRadioCarSpawner_UpdateCarTypesToSpawn_Patch + { + public static void Prefix( ref bool allowLocoSpawning ) + { + allowLocoSpawning = true; + } + + public static void Postfix( List ___carTypesToSpawn ) + { + var customCarTypes = CustomCarManager.CustomCarTypes.Select(car => car.CarType); + ___carTypesToSpawn.AddRange(customCarTypes); + } + } + + [HarmonyPatch(typeof(CarSpawner))] + public static class CarSpawner_Patches + { + private static Delegate[] carSpawnedDelegates = null; + + private static void RaiseCarSpawned( TrainCar car ) + { + if( carSpawnedDelegates == null ) + { + if( !(AccessTools.Field(typeof(CarSpawner), nameof(CarSpawner.CarSpawned)).GetValue(null) is MulticastDelegate mcd) ) + { + Main.ModEntry.Logger.Error("Couldn't get CarSpawner.CarSpawned delegate"); + return; + } + + carSpawnedDelegates = mcd.GetInvocationList(); + } + + var args = new object[] { car }; + foreach( Delegate d in carSpawnedDelegates ) + { + d.Method.Invoke(d.Target, args); + } + } + + [HarmonyPrefix] + [HarmonyPatch(nameof(CarSpawner.SpawnCar))] + public static bool SpawnCar( + GameObject carToSpawn, RailTrack track, Vector3 position, Vector3 forward, bool playerSpawnedCar, + ref TrainCar __result) + { + TrainCar prefabCar = carToSpawn.GetComponentInChildren(true); + if( !CarTypeInjector.IsCustomTypeRegistered(prefabCar.carType) ) + { + return true; + } + + GameObject carObj = UnityEngine.Object.Instantiate(carToSpawn); + if( !carObj.activeSelf ) + { + carObj.SetActive(true); + } + TrainCar spawnedCar = carObj.GetComponentInChildren(); + + spawnedCar.playerSpawnedCar = playerSpawnedCar; + spawnedCar.InitializeNewLogicCar(); + spawnedCar.SetTrack(track, position, forward); + + //spawnedCar.OnDestroyCar += CustomCarManager.DeregisterCar; + //CustomCarManager.RegisterSpawnedCar(spawnedCar, identifier); + + RaiseCarSpawned(spawnedCar); + + __result = spawnedCar; + return false; + } + + [HarmonyPrefix] + [HarmonyPatch(nameof(CarSpawner.SpawnLoadedCar))] + public static bool SpawnLoadedCar( + GameObject carToSpawn, + string carId, string carGuid, bool playerSpawnedCar, Vector3 position, Quaternion rotation, + bool bogie1Derailed, RailTrack bogie1Track, double bogie1PositionAlongTrack, + bool bogie2Derailed, RailTrack bogie2Track, double bogie2PositionAlongTrack, + bool couplerFCoupled, bool couplerRCoupled, + ref TrainCar __result) + { + TrainCar prefabCar = carToSpawn.GetComponentInChildren(true); + if( !CarTypeInjector.IsCustomTypeRegistered(prefabCar.carType) ) + { + return true; + } + + GameObject carObj = UnityEngine.Object.Instantiate(carToSpawn, position, rotation); + if( !carObj.activeSelf ) + { + carObj.SetActive(true); + } + + TrainCar spawnedCar = carObj.GetComponentInChildren(); + spawnedCar.playerSpawnedCar = playerSpawnedCar; + spawnedCar.InitializeExistingLogicCar(carId, carGuid); + + if( !bogie1Derailed ) + { + spawnedCar.Bogies[0].SetTrack(bogie1Track, bogie1PositionAlongTrack); + } + else + { + spawnedCar.Bogies[0].SetDerailedOnLoadFlag(true); + } + + if( !bogie2Derailed ) + { + spawnedCar.Bogies[1].SetTrack(bogie2Track, bogie2PositionAlongTrack); + } + else + { + spawnedCar.Bogies[1].SetDerailedOnLoadFlag(true); + } + + spawnedCar.frontCoupler.forceCoupleStateOnLoad = true; + spawnedCar.frontCoupler.loadedCoupledState = couplerFCoupled; + spawnedCar.rearCoupler.forceCoupleStateOnLoad = true; + spawnedCar.rearCoupler.loadedCoupledState = couplerRCoupled; + + //spawnedCar.OnDestroyCar += CustomCarManager.DeregisterCar; + //CustomCarManager.RegisterSpawnedCar(spawnedCar, identifier); + + RaiseCarSpawned(spawnedCar); + + __result = spawnedCar; + return false; + } + } +} diff --git a/DVCustomCarLoader/CarTypeInjector.cs b/DVCustomCarLoader/CarTypeInjector.cs index 91f4935e..de676067 100644 --- a/DVCustomCarLoader/CarTypeInjector.cs +++ b/DVCustomCarLoader/CarTypeInjector.cs @@ -23,9 +23,9 @@ public static class CarTypeInjector private static readonly Dictionary idToCustomCar = new Dictionary(StringComparer.CurrentCultureIgnoreCase); private static readonly Dictionary carTypeToCustomCar = new Dictionary(); - public static bool TryGetCarTypeById( string id, out TrainCarType type ) => idToCarType.TryGetValue(id, out type); + public static bool TryGetCarTypeById( string id, out TrainCarType type ) => idToCarType.TryGetValue(id.ToLower(), out type); public static bool TryGetCustomCarByType( TrainCarType carType, out CustomCar car ) => carTypeToCustomCar.TryGetValue(carType, out car); - public static bool TryGetCustomCarById( string id, out CustomCar car ) => idToCustomCar.TryGetValue(id, out car); + public static bool TryGetCustomCarById( string id, out CustomCar car ) => idToCustomCar.TryGetValue(id.ToLower(), out car); public static bool IsCustomTypeRegistered( TrainCarType carType ) => carTypeToCustomCar.ContainsKey(carType); public static bool IsCustomTypeRegistered( string identifier ) => idToCarType.ContainsKey(identifier); diff --git a/DVCustomCarLoader/CommsRadioCarSpawner_Awake_Patch.cs b/DVCustomCarLoader/CommsRadioCarSpawner_Awake_Patch.cs deleted file mode 100644 index 32de9f57..00000000 --- a/DVCustomCarLoader/CommsRadioCarSpawner_Awake_Patch.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System; -using System.Collections.Generic; -using DV; -using HarmonyLib; - -namespace DVCustomCarLoader -{ - [HarmonyPatch(typeof(CommsRadioCarSpawner), "Awake")] - public class CommsRadioCarSpawner_Awake_Patch - { - static void Postfix(CommsRadioCarSpawner __instance) - { - if (!Main.Enabled) - return; - - try - { - //Add trains to car spawner - __instance.UpdateCarTypesToSpawn(true); - - } - catch(Exception e) - { - Main.ModEntry.Logger.Error(e.ToString()); - } - } - } -} \ No newline at end of file diff --git a/DVCustomCarLoader/CommsRadioController_Awake_Patch.cs b/DVCustomCarLoader/CommsRadioController_Awake_Patch.cs deleted file mode 100644 index 41da4a2e..00000000 --- a/DVCustomCarLoader/CommsRadioController_Awake_Patch.cs +++ /dev/null @@ -1,52 +0,0 @@ -using System; -using System.Collections.Generic; -using DV; -using HarmonyLib; - -namespace DVCustomCarLoader -{ - /// - /// After CommsRadioControler finished it's Awake() call, we add our own comms mode after that. - /// - [HarmonyPatch(typeof(CommsRadioController), "Awake")] - public class CommsRadioController_Awake_Patch - { - static void Postfix(CommsRadioController __instance, ref List ___allModes, ref CommsRadioCarSpawner ___carSpawnerControl) - { - if (!Main.Enabled) - return; - - try - { - - if( CustomCarManager.CustomCarTypes.Count <= 0 ) - return; - - //Add our spawner to the comms radio - var ccm = Main.CommsRadioCustomCarManager = __instance.gameObject.AddComponent(); - - //We need to get info from car spawner because we don't have these naturally. - ccm.display = ___carSpawnerControl.display; - ccm.validMaterial = ___carSpawnerControl.validMaterial; - ccm.invalidMaterial = ___carSpawnerControl.invalidMaterial; - ccm.lcdArrow = ___carSpawnerControl.lcdArrow; - ccm.spawnModeEnterSound = ___carSpawnerControl.spawnModeEnterSound; - ccm.spawnVehicleSound = ___carSpawnerControl.spawnVehicleSound; - ccm.confirmSound = ___carSpawnerControl.confirmSound; - ccm.cancelSound = ___carSpawnerControl.cancelSound; - ccm.destinationHighlighterGO = ___carSpawnerControl.destinationHighlighterGO; - ccm.directionArrowsHighlighterGO = ___carSpawnerControl.directionArrowsHighlighterGO; - - //setup script after we add references. - ccm.Setup(); - - //Add our custom mode to comms radio - ___allModes.Add(Main.CommsRadioCustomCarManager); - } - catch(Exception e) - { - Main.ModEntry.Logger.Error(e.ToString()); - } - } - } -} diff --git a/DVCustomCarLoader/CommsRadioCustomCarManager.cs b/DVCustomCarLoader/CommsRadioCustomCarManager.cs deleted file mode 100644 index 3b72ef57..00000000 --- a/DVCustomCarLoader/CommsRadioCustomCarManager.cs +++ /dev/null @@ -1,376 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using DV; -using DV.PointSet; -using UnityEngine; - -namespace DVCustomCarLoader -{ - - /// - /// Allows use of the comms radio to spawn custom cars. - /// - public class CommsRadioCustomCarManager : MonoBehaviour, ICommsRadioMode - { - private static readonly Color laserColor = new Color32(0, 238, 255, 255); - private bool spawnWithTrackDirection = true; - private readonly List potentialTracks = new List(); - private const float POTENTIAL_TRACKS_RADIUS = 200f; - private const float MAX_DISTANCE_FROM_TRACK_POINT = 3f; - private const float TRACK_POINT_POSITION_Y_OFFSET = -1.75f; - private const float SIGNAL_RANGE = 100f; - private const float INVALID_DESTINATION_HIGHLIGHTER_DISTANCE = 20f; - private const float UPDATE_TRACKS_PERIOD = 2.5f; - private const string MODE_NAME = "CUSTOM SPAWNER"; - private const string CONFIRM_TEXT = "confirm"; - private const string CANCEL_TEXT = "cancel"; - public Transform signalOrigin; - public CommsRadioDisplay display; - public Material validMaterial; - public Material invalidMaterial; - public ArrowLCD lcdArrow; - [Header("Sounds")] public AudioClip spawnModeEnterSound; - public AudioClip spawnVehicleSound; - public AudioClip confirmSound; - public AudioClip cancelSound; - [Header("Highlighters")] public GameObject destinationHighlighterGO; - public GameObject directionArrowsHighlighterGO; - private CarDestinationHighlighter destHighlighter; - private RaycastHit hit; - private LayerMask trackMask; - private int selectedCarTypeIndex; - private CustomCar carToSpawn; - private Bounds carBounds; - private bool canSpawnAtPoint; - private RailTrack destinationTrack; - private EquiPointSet.Point? closestPointOnDestinationTrack; - private Coroutine trackUpdateCoro; - private State state; - - - - private void SetState(State newState) - { - if (state == newState) return; - state = newState; - switch (state) - { - case State.EnterSpawnMode: - SetStartingDisplay(); - lcdArrow.TurnOff(); - ButtonBehaviour = ButtonBehaviourType.Regular; - break; - case State.PickCar: - if (trackUpdateCoro == null) - trackUpdateCoro = StartCoroutine(PotentialTracksUpdateCoro()); - ButtonBehaviour = ButtonBehaviourType.Override; - CommsRadioController.PlayAudioFromRadio(spawnModeEnterSound, transform); - break; - case State.PickDestination: - ButtonBehaviour = ButtonBehaviourType.Override; - break; - } - } - - public void Setup() - { - if (!(bool) signalOrigin) - { - Debug.LogError("signalOrigin on CommsRadioCrewVehicle isn't set, using this.transform!", - this); - signalOrigin = transform; - } - - if (display == null) - Debug.LogError("display not set, can't function properly!", this); - if (validMaterial == null || - invalidMaterial == null) - Debug.LogError("Some of the required materials isn't set. Visuals won't be correct.", - this); - if (lcdArrow == null) - Debug.LogError("lcdArrow not set, can't display arrow!", this); - if (destinationHighlighterGO == null || - directionArrowsHighlighterGO == null) - Debug.LogError( - "destinationHighlighterGO or directionArrowsHighlighterGO is not set, can't function properly!!", - this); - if (spawnVehicleSound == null || - spawnModeEnterSound == null || confirmSound == null || cancelSound == null) - Debug.LogError("Not all audio clips set, some sounds won't be played!", - this); - trackMask = LayerMask.GetMask("Default"); - destHighlighter = - new CarDestinationHighlighter(destinationHighlighterGO, directionArrowsHighlighterGO); - } - - private void OnDestroy() - { - if (AppQuitWatcher.isQuitting) return; - destHighlighter.Destroy(); - destHighlighter = null; - } - - public ButtonBehaviourType ButtonBehaviour { get; private set; } - - public void Enable() - { - } - - public void Disable() - { - ClearFlags(); - trackUpdateCoro = null; - StopAllCoroutines(); - } - - public void OverrideSignalOrigin(Transform signalOrigin) - { - this.signalOrigin = signalOrigin; - } - - public void OnUse() - { - switch (state) - { - case State.EnterSpawnMode: - SetCarToSpawn(CustomCarManager.CustomCarTypes[selectedCarTypeIndex]); - SetState(State.PickCar); - break; - case State.PickCar: - if (carToSpawn == null) - { - Debug.LogError("carPrefabToSpawn is null! Something is wrong, can't spawn this car.", - this); - ClearFlags(); - break; - } - - if (canSpawnAtPoint) - { - SetState(State.PickDestination); - CommsRadioController.PlayAudioFromRadio(confirmSound, transform); - break; - } - - ClearFlags(); - CommsRadioController.PlayAudioFromRadio(cancelSound, transform); - break; - case State.PickDestination: - if (canSpawnAtPoint) - { - var forward = closestPointOnDestinationTrack.Value.forward; - if (!spawnWithTrackDirection) forward = -forward; - - var trainCar = carToSpawn.SpawnCar(destinationTrack, - (Vector3)closestPointOnDestinationTrack.Value.position + WorldMover.currentMove, - forward, true); - - if( trainCar != null) - { - SingletonBehaviour.Instance.MarkForDelete(trainCar); - CommsRadioController.PlayAudioFromCar(spawnVehicleSound, trainCar); - CommsRadioController.PlayAudioFromRadio(confirmSound, transform); - canSpawnAtPoint = false; - break; - } - - Debug.LogError("Couldn't spawn car!", carToSpawn.CarPrefab); - ClearFlags(); - break; - } - - ClearFlags(); - CommsRadioController.PlayAudioFromRadio(cancelSound, transform); - break; - } - } - - public void OnUpdate() - { - if (potentialTracks.Count == 0 || (uint) (state - 1) > 1U) return; - if (Physics.Raycast(signalOrigin.position, signalOrigin.forward, out hit, 100f, - trackMask)) - { - var point = hit.point; - foreach (var potentialTrack in potentialTracks) - { - var rangeWithYoffset = - RailTrack.GetPointWithinRangeWithYOffset(potentialTrack, point, 3f, -1.75f); - if (rangeWithYoffset.HasValue) - { - destinationTrack = potentialTrack; - var startingFromIndex = - CarSpawner.FindClosestValidPointForCarStartingFromIndex( - potentialTrack.GetPointSet().points, rangeWithYoffset.Value.index, - carBounds.extents); - var hasValue = startingFromIndex.HasValue; - closestPointOnDestinationTrack = !hasValue ? rangeWithYoffset : startingFromIndex; - canSpawnAtPoint = hasValue; - var position = (Vector3) closestPointOnDestinationTrack.Value.position + - WorldMover.currentMove; - var forward = closestPointOnDestinationTrack.Value.forward; - if (!spawnWithTrackDirection) forward *= -1f; - destHighlighter.Highlight(position, forward, carBounds, - canSpawnAtPoint ? validMaterial : invalidMaterial); - display.SetAction(canSpawnAtPoint ? "confirm" : "cancel"); - if (canSpawnAtPoint && state == State.PickDestination) - { - UpdateLCDRerailDirectionArrow(); - return; - } - - lcdArrow.TurnOff(); - return; - } - } - } - - canSpawnAtPoint = false; - destinationTrack = null; - destHighlighter.Highlight(signalOrigin.position + signalOrigin.forward * 20f, - signalOrigin.right, carBounds, invalidMaterial); - display.SetAction("cancel"); - lcdArrow.TurnOff(); - } - - public bool ButtonACustomAction() - { - switch (state) - { - case State.PickCar: - selectedCarTypeIndex = selectedCarTypeIndex <= 0 - ? CustomCarManager.CustomCarTypes.Count - 1 - : selectedCarTypeIndex - 1; - SetCarToSpawn(CustomCarManager.CustomCarTypes[selectedCarTypeIndex]); - return true; - case State.PickDestination: - if (!canSpawnAtPoint) return false; - spawnWithTrackDirection = !spawnWithTrackDirection; - return true; - default: - Debug.LogError(string.Format("Unexpected state {0}!", state), - this); - return false; - } - } - - public bool ButtonBCustomAction() - { - switch (state) - { - case State.PickCar: - selectedCarTypeIndex = (selectedCarTypeIndex + 1) % CustomCarManager.CustomCarTypes.Count; - SetCarToSpawn(CustomCarManager.CustomCarTypes[selectedCarTypeIndex]); - return true; - case State.PickDestination: - if (!canSpawnAtPoint) return false; - spawnWithTrackDirection = !spawnWithTrackDirection; - return true; - default: - Debug.LogError(string.Format("Unexpected state {0}!", state), - this); - return false; - } - } - - public void SetStartingDisplay() - { - display.SetDisplay("CUSTOM CAR SPAWNER", "Enable custom car spawner?"); - } - - public Color GetLaserBeamColor() - { - return laserColor; - } - - private void SetCarToSpawn(CustomCar car) - { - //carPrefabToSpawn = CarTypes.GetCarPrefab(car.TrainCarType); - carToSpawn = car; - - if( carToSpawn == null) - { - Debug.LogError( - $"Couldn't load car prefab: {car.identifier}! Won't be able to spawn this car.", this); - } - else - { - //var component = carPrefabToSpawn.GetComponent(); - carBounds = car.Bounds; - display.SetContent(car.identifier + "\n" + car.InterCouplerDistance.ToString("F") + "m"); - } - } - - private void UpdatePotentialTracks() - { - potentialTracks.Clear(); - var num = 1f; - while (true) - { - foreach (var allTrack in RailTrackRegistry.AllTracks) - if (RailTrack.GetPointWithinRangeWithYOffset(allTrack, transform.position, num * 200f) - .HasValue) - potentialTracks.Add(allTrack); - - if (potentialTracks.Count <= 0 && num <= 4.0) - { - Debug.LogWarning( - string.Format("No tracks in {0} radius. Expanding radius!", - (float) (num * 200.0)), this); - num += 0.2f; - } - else - { - break; - } - } - - if (potentialTracks.Count != 0) return; - Debug.LogError("No near tracks found. Can't spawn crew vehicle"); - } - - private IEnumerator PotentialTracksUpdateCoro() - { - var CommsRadioCustomCarManager = this; - var lastUpdatedTracksWorldPosition = Vector3.positiveInfinity; - while (true) - { - if ((CommsRadioCustomCarManager.transform.position - WorldMover.currentMove - - lastUpdatedTracksWorldPosition).magnitude > 100.0) - { - CommsRadioCustomCarManager.UpdatePotentialTracks(); - lastUpdatedTracksWorldPosition = - CommsRadioCustomCarManager.transform.position - WorldMover.currentMove; - } - - yield return WaitFor.Seconds(2.5f); - } - } - - private void UpdateLCDRerailDirectionArrow() - { - lcdArrow.TurnOn(Mathf.Sin( - Vector3.SignedAngle( - spawnWithTrackDirection - ? closestPointOnDestinationTrack.Value.forward - : -closestPointOnDestinationTrack.Value.forward, - signalOrigin.forward, Vector3.up) * ((float) Math.PI / 180f)) > 0.0); - } - - private void ClearFlags() - { - destinationTrack = null; - canSpawnAtPoint = false; - destHighlighter.TurnOff(); - SetState(State.EnterSpawnMode); - } - - private enum State - { - EnterSpawnMode, - PickCar, - PickDestination - } - } -} \ No newline at end of file diff --git a/DVCustomCarLoader/CustomCar.cs b/DVCustomCarLoader/CustomCar.cs index 84e4af59..22d6abbb 100644 --- a/DVCustomCarLoader/CustomCar.cs +++ b/DVCustomCarLoader/CustomCar.cs @@ -395,94 +395,6 @@ public bool FinalizePrefab() return true; } - private static Delegate[] carSpawnedDelegates = null; - - private static void RaiseCarSpawned( TrainCar car ) - { - if( carSpawnedDelegates == null ) - { - if( !(AccessTools.Field(typeof(CarSpawner), nameof(CarSpawner.CarSpawned)).GetValue(null) is MulticastDelegate mcd) ) - { - Main.ModEntry.Logger.Error("Couldn't get CarSpawner.CarSpawned delegate"); - return; - } - - carSpawnedDelegates = mcd.GetInvocationList(); - } - - var args = new object[] { car }; - foreach( Delegate d in carSpawnedDelegates ) - { - d.Method.Invoke(d.Target, args); - } - } - - public TrainCar SpawnCar( RailTrack track, Vector3 position, Vector3 forward, bool playerSpawnedCar = false ) - { - GameObject carObj = Object.Instantiate(CarPrefab); - if( !carObj.activeSelf ) - { - carObj.SetActive(true); - } - TrainCar spawnedCar = carObj.GetComponentInChildren(); - - spawnedCar.playerSpawnedCar = playerSpawnedCar; - spawnedCar.InitializeNewLogicCar(); - spawnedCar.SetTrack(track, position, forward); - - spawnedCar.OnDestroyCar += CustomCarManager.DeregisterCar; - CustomCarManager.RegisterSpawnedCar(spawnedCar, identifier); - - RaiseCarSpawned(spawnedCar); - return spawnedCar; - } - - public TrainCar SpawnLoadedCar( - string carId, string carGuid, bool playerSpawnedCar, Vector3 position, Quaternion rotation, - bool bogie1Derailed, RailTrack bogie1Track, double bogie1PositionAlongTrack, - bool bogie2Derailed, RailTrack bogie2Track, double bogie2PositionAlongTrack, - bool couplerFCoupled, bool couplerRCoupled ) - { - GameObject carObj = Object.Instantiate(CarPrefab, position, rotation); - if( !carObj.activeSelf ) - { - carObj.SetActive(true); - } - - TrainCar spawnedCar = carObj.GetComponentInChildren(); - spawnedCar.playerSpawnedCar = playerSpawnedCar; - spawnedCar.InitializeExistingLogicCar(carId, carGuid); - - if( !bogie1Derailed ) - { - spawnedCar.Bogies[0].SetTrack(bogie1Track, bogie1PositionAlongTrack); - } - else - { - spawnedCar.Bogies[0].SetDerailedOnLoadFlag(true); - } - - if( !bogie2Derailed ) - { - spawnedCar.Bogies[1].SetTrack(bogie2Track, bogie2PositionAlongTrack); - } - else - { - spawnedCar.Bogies[1].SetDerailedOnLoadFlag(true); - } - - spawnedCar.frontCoupler.forceCoupleStateOnLoad = true; - spawnedCar.frontCoupler.loadedCoupledState = couplerFCoupled; - spawnedCar.rearCoupler.forceCoupleStateOnLoad = true; - spawnedCar.rearCoupler.loadedCoupledState = couplerRCoupled; - - spawnedCar.OnDestroyCar += CustomCarManager.DeregisterCar; - CustomCarManager.RegisterSpawnedCar(spawnedCar, identifier); - - RaiseCarSpawned(spawnedCar); - return spawnedCar; - } - private float _interCouplerDistance; public float InterCouplerDistance diff --git a/DVCustomCarLoader/CustomCarManager.cs b/DVCustomCarLoader/CustomCarManager.cs index 0f783dac..dd8c02a9 100644 --- a/DVCustomCarLoader/CustomCarManager.cs +++ b/DVCustomCarLoader/CustomCarManager.cs @@ -10,27 +10,27 @@ public static class CustomCarManager { public static List CustomCarTypes = new List(); - private static readonly Dictionary SpawnedCustomCarIds = new Dictionary(); - - public static bool IsRegisteredCustomCar( TrainCar trainCar ) - { - return SpawnedCustomCarIds.ContainsKey(trainCar); - } - - public static bool TryGetCustomCarId( TrainCar trainCar, out string id ) - { - return SpawnedCustomCarIds.TryGetValue(trainCar, out id); - } - - public static void RegisterSpawnedCar( TrainCar car, string identifier ) - { - SpawnedCustomCarIds[car] = identifier; - } - - public static void DeregisterCar( TrainCar car ) - { - SpawnedCustomCarIds.Remove(car); - } + //private static readonly Dictionary SpawnedCustomCarIds = new Dictionary(); + + //public static bool IsRegisteredCustomCar( TrainCar trainCar ) + //{ + // return SpawnedCustomCarIds.ContainsKey(trainCar); + //} + + //public static bool TryGetCustomCarId( TrainCar trainCar, out string id ) + //{ + // return SpawnedCustomCarIds.TryGetValue(trainCar, out id); + //} + + //public static void RegisterSpawnedCar( TrainCar car, string identifier ) + //{ + // SpawnedCustomCarIds[car] = identifier; + //} + + //public static void DeregisterCar( TrainCar car ) + //{ + // SpawnedCustomCarIds.Remove(car); + //} public static void Setup() { diff --git a/DVCustomCarLoader/DVCustomCarLoader.csproj b/DVCustomCarLoader/DVCustomCarLoader.csproj index d62f0376..63488104 100644 --- a/DVCustomCarLoader/DVCustomCarLoader.csproj +++ b/DVCustomCarLoader/DVCustomCarLoader.csproj @@ -74,6 +74,7 @@ + @@ -83,9 +84,6 @@ - - - diff --git a/DVCustomCarLoader/Main.cs b/DVCustomCarLoader/Main.cs index 56012781..4089cd77 100644 --- a/DVCustomCarLoader/Main.cs +++ b/DVCustomCarLoader/Main.cs @@ -10,7 +10,6 @@ namespace DVCustomCarLoader { public static class Main { - public static CommsRadioCustomCarManager CommsRadioCustomCarManager; public static UnityModManager.ModEntry ModEntry; public static bool Enabled; @@ -36,7 +35,8 @@ public static bool Load(UnityModManager.ModEntry modEntry) private static void OnCarChanged( TrainCar newCar ) { - if( newCar && CustomCarManager.IsRegisteredCustomCar(newCar) ) +#if DEBUG + if( newCar && CarTypeInjector.IsCustomTypeRegistered(newCar.carType) ) { // diesel autostart var locoController = newCar.gameObject.GetComponent(); @@ -45,7 +45,8 @@ private static void OnCarChanged( TrainCar newCar ) locoController.EngineRunning = true; } } - } +#endif + } #region Logging diff --git a/DVCustomCarLoader/SaveLoadPatches.cs b/DVCustomCarLoader/SaveLoadPatches.cs index f00162d2..f382b320 100644 --- a/DVCustomCarLoader/SaveLoadPatches.cs +++ b/DVCustomCarLoader/SaveLoadPatches.cs @@ -21,10 +21,10 @@ public static class CarsSaveManager_GetSaveData_Patch { public static void Postfix( TrainCar car, ref JObject __result ) { - if( CustomCarManager.TryGetCustomCarId(car, out string id) ) + if( CarTypeInjector.TryGetCustomCarByType(car.carType, out CustomCar customCar) ) { // custom car detected, save its type - __result.SetString(SaveConstants.CUSTOM_CAR_KEY, id); + __result.SetString(SaveConstants.CUSTOM_CAR_KEY, customCar.identifier); } } } @@ -52,6 +52,8 @@ public static bool Prefix( JObject carData, RailTrack[] tracks, ref TrainCar __r __result = null; return false; } + + // proper custom type, proceed with the spawning // car info string carId = carData.GetString("id"); @@ -97,8 +99,9 @@ public static bool Prefix( JObject carData, RailTrack[] tracks, ref TrainCar __r RailTrack bogie2Track = (!bogie2Derailed) ? tracks[bogie2TrackChildIdx.Value] : null; double bogie2PositionAlongTrack = (!bogie2Derailed) ? bogie2TrackPosition.Value : 0.0; - TrainCar trainCar = customCarType.SpawnLoadedCar( - carId, carGuid, playerSpawnedCar, position.Value + WorldMover.currentMove, Quaternion.Euler(rotation.Value), + TrainCar trainCar = CarSpawner.SpawnLoadedCar( + customCarType.CarPrefab, carId, carGuid, playerSpawnedCar, + position.Value + WorldMover.currentMove, Quaternion.Euler(rotation.Value), bogie1Derailed, bogie1Track, bogie1PositionAlongTrack, bogie2Derailed, bogie2Track, bogie2PositionAlongTrack, coupledFront.Value, coupledRear.Value);