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);