public static void ParseEffectDefinition(VehicleEffectsDefinition.Vehicle vehicleDef, VehicleEffectsDefinition.Effect effectDef, List <VehicleInfo.Effect> effectsList, ref HashSet <string> parseErrors) { if (effectDef?.Name == null) { parseErrors.Add(vehicleDef.Name + " - Effect name missing."); return; } string effectName = (effectDef.Replacment == null) ? effectDef.Name : effectDef.Replacment; effectName += (effectDef.Base != null) ? " - " + effectDef.Base : ""; var effectPrefab = FindEffect(effectName); if (effectPrefab == null) { //This effect is either a 'special' effect or simply non existent if (effectName.StartsWith("Vehicle Effect Wrapper")) { // Wrapper for effects if (effectDef.Base != null) { var baseEffect = FindEffect(effectDef.Base); if (baseEffect != null) { effectPrefab = CustomVehicleEffect.CreateEffect(effectDef, baseEffect); if (effectPrefab == null) { parseErrors.Add(vehicleDef.Name + ": An error occured trying to create a custom effect. Check debug log for details."); return; } } else { // Try fallback if (effectDef.Fallback != null) { Logging.Log(vehicleDef.Name + ": trying to use fallback " + effectDef.Fallback + " for " + effectName); if (effectDef.Fallback.StartsWith("None")) { // None specified as fallback, just exit return; } else if (effectDef.Fallback.StartsWith("Remove") && effectDef.Replacment != null) { // Remove the existing effect effectName = "Remove"; effectPrefab = null; } else { baseEffect = FindEffect(effectDef.Fallback); if (baseEffect != null) { effectPrefab = CustomVehicleEffect.CreateEffect(effectDef, baseEffect); if (effectPrefab == null) { parseErrors.Add(vehicleDef.Name + ": An error occured trying to create a custom effect. Check debug log for details."); return; } } else { parseErrors.Add(vehicleDef.Name + ": Vehicle Effect Wrapper fallback " + effectDef.Fallback + " was needed for " + effectDef.Base + " but not loaded either!"); } } } else { parseErrors.Add(vehicleDef.Name + ": Vehicle Effect Wrapper base effect " + effectDef.Base + " was not loaded and no fallback was specified!"); return; } } } else { parseErrors.Add(vehicleDef.Name + ": Vehicle Effect Wrapper requires a base effect but it wasn't given"); return; } } else if (effectName.StartsWith("Vehicle Effect Multi")) { // Custom MultiEffect defined in the xml if (effectDef.SubEffects != null) { List <MultiEffect.SubEffect> loadedSubEffects = new List <MultiEffect.SubEffect>(); foreach (var subEffect in effectDef.SubEffects) { var dummyList = new List <VehicleInfo.Effect>(); ParseEffectDefinition(vehicleDef, subEffect.Effect, dummyList, ref parseErrors); if (dummyList.Count > 0) { loadedSubEffects.Add(new MultiEffect.SubEffect() { m_effect = dummyList[0].m_effect, m_endTime = subEffect.EndTime, m_startTime = subEffect.StartTime, m_probability = subEffect.Probability }); } } effectPrefab = CustomVehicleMultiEffect.CreateEffect(effectDef, loadedSubEffects.ToArray(), effectDef.Duration, effectDef.UseSimulationTime); } else { parseErrors.Add(vehicleDef.Name + " Vehicle Effect Multi with no sub effects!"); return; } } else if (effectName.StartsWith("None") || effectName.StartsWith("Remove")) { // Not a real effect, but keywords used for removal of an existing effect. // 'None' included for < 1.2c compatibility effectName = "Remove"; effectPrefab = null; } else { // Try fallback if (effectDef.Fallback != null) { Logging.Log(vehicleDef.Name + ": trying to use fallback " + effectDef.Fallback + " for " + effectName); if (effectDef.Fallback.StartsWith("None")) { // None specified as fallback, just exit return; } else if (effectDef.Fallback.StartsWith("Remove") && effectDef.Replacment != null) { // Remove the existing effect effectName = "Remove"; effectPrefab = null; } else { effectPrefab = FindEffect(effectDef.Fallback); if (effectPrefab == null) { parseErrors.Add(vehicleDef.Name + " requested non-existing effect " + effectName + " and fallback " + effectDef.Fallback + " could not be found either!"); return; } } } else { parseErrors.Add(vehicleDef.Name + " requested non-existing effect " + effectName + " and no fallback was specified"); return; } } } if (effectPrefab == null && !effectName.Equals("Remove")) { parseErrors.Add(vehicleDef.Name + " - Effect with name " + effectDef.Name + " not loaded."); return; } var vehicleInfoEffect = new VehicleInfo.Effect { m_effect = effectPrefab, m_parkedFlagsForbidden = VehicleParked.Flags.Created, m_parkedFlagsRequired = VehicleParked.Flags.None, m_vehicleFlagsForbidden = (Vehicle.Flags)effectDef.ForbiddenFlags, m_vehicleFlagsRequired = ((Vehicle.Flags)effectDef.RequiredFlags) | Vehicle.Flags.Created }; if (effectDef.Replacment == null) { effectsList.Add(vehicleInfoEffect); } else { int indexToRemove = -1; for (int i = 0; i < effectsList.Count; i++) { if (effectsList[i].m_effect.name.Equals(effectDef.Name)) { if (effectDef.Replacment.Equals("Remove")) { indexToRemove = i; break; } effectsList[i] = vehicleInfoEffect; break; } } if (indexToRemove >= 0) { effectsList.RemoveAt(indexToRemove); } } }
private void UpdateVehicleEffects() { eventVehicleUpdateStart?.Invoke(); try { effectPlacementRequests = new List <VehicleEffectsDefinition.Vehicle>(); vehicleEffectsDefParseErrors = new HashSet <string>(); var lockedVehicles = new HashSet <VehicleInfo>(); var checkedPaths = new List <string>(); var loadedDefinitions = new List <VehicleEffectsDefinition>(); var definitionPackages = new Dictionary <VehicleEffectsDefinition, string>(); // Load definitions from mod folders foreach (var current in ColossalFramework.Plugins.PluginManager.instance.GetPluginsInfo()) { if (current.isEnabled) { var vehicleEffectsDefPath = Path.Combine(current.modPath, filename); // skip files which were already parsed if (checkedPaths.Contains(vehicleEffectsDefPath)) { continue; } checkedPaths.Add(vehicleEffectsDefPath); if (!File.Exists(vehicleEffectsDefPath)) { continue; } VehicleEffectsDefinition vehicleEffectsDef = null; var xmlSerializer = new XmlSerializer(typeof(VehicleEffectsDefinition)); try { using (var streamReader = new System.IO.StreamReader(vehicleEffectsDefPath)) { vehicleEffectsDef = xmlSerializer.Deserialize(streamReader) as VehicleEffectsDefinition; } } catch (Exception e) { Logging.LogException(e); vehicleEffectsDefParseErrors.Add(current.name + " - " + e.Message); continue; } Logging.Log("Loading definition from Mod " + current.name); vehicleEffectsDef.LoadedFromMod = true; loadedDefinitions.Add(vehicleEffectsDef); definitionPackages.Add(vehicleEffectsDef, current.name); } } // Load definitions from prefabs for (uint i = 0; i < PrefabCollection <VehicleInfo> .LoadedCount(); i++) { var prefab = PrefabCollection <VehicleInfo> .GetLoaded(i); if (prefab == null) { continue; } // search for VehicleEffectsDefinition.xml var asset = PackageManager.FindAssetByName(prefab.name); var crpPath = asset?.package?.packagePath; if (crpPath == null) { continue; } var vehicleEffectsDefPath = Path.Combine(Path.GetDirectoryName(crpPath) ?? "", filename); // skip files which were already parsed if (checkedPaths.Contains(vehicleEffectsDefPath)) { continue; } checkedPaths.Add(vehicleEffectsDefPath); if (!File.Exists(vehicleEffectsDefPath)) { continue; } VehicleEffectsDefinition vehicleEffectsDef = null; var xmlSerializer = new XmlSerializer(typeof(VehicleEffectsDefinition)); try { using (var streamReader = new System.IO.StreamReader(vehicleEffectsDefPath)) { vehicleEffectsDef = xmlSerializer.Deserialize(streamReader) as VehicleEffectsDefinition; } } catch (Exception e) { Logging.LogException(e); vehicleEffectsDefParseErrors.Add(asset.package.packageName + " - " + e.Message); continue; } loadedDefinitions.Add(vehicleEffectsDef); definitionPackages.Add(vehicleEffectsDef, asset.package.packageName); } // Apply the effects foreach (var vehicleEffectsDef in loadedDefinitions) { if (vehicleEffectsDef?.Vehicles == null || vehicleEffectsDef.Vehicles.Count == 0) { vehicleEffectsDefParseErrors.Add(definitionPackages[vehicleEffectsDef] + " - vehicleEffectDef is null or empty."); continue; } foreach (var vehicleDef in vehicleEffectsDef.Vehicles) { ParseVehicleDefinition(vehicleDef, definitionPackages[vehicleEffectsDef], ref changes, ref vehicleEffectsDefParseErrors, ignoreModVehicleParseErrors ? vehicleEffectsDef.LoadedFromMod : false); // Mods can be used as packs, not all vehicles may be loaded so we surpress vehicle parse errors } } } catch (Exception e) { Logging.LogException(e); } if (vehicleEffectsDefParseErrors?.Count > 0 && showParseErrors) { var errorMessage = vehicleEffectsDefParseErrors.Aggregate("Error while parsing vehicle effect definition file(s). Assets will work but may have effects missing. Contact the author of the asset(s). \n" + "List of errors:\n", (current, error) => current + (error + '\n')); UIView.library.ShowModal <ExceptionPanel>("ExceptionPanel").SetMessage("Vehicle Effects", errorMessage, false); } vehicleEffectsDefParseErrors = null; hasChangedPrefabs = true; eventVehicleUpdateFinished?.Invoke(); }
/// <summary> /// Applies a vehicle definition vehicle entry. /// </summary> /// <param name="vehicleDef">Vehicle definition to parse.</param> /// <param name="packageName">Package name used in error messages.</param> /// <param name="backup">Used to store original effect array.</param> /// <param name="parseErrors">HashSet to add parse errors to.</param> /// <param name="noParseErrors">Optional: If true no vehicle related parse errors are given.</param> /// <param name="vehicleDefPrefab">Optional: VehicleInfo to apply definition to.</param> public static void ParseVehicleDefinition(VehicleEffectsDefinition.Vehicle vehicleDef, string packageName, ref Dictionary <VehicleInfo, VehicleInfo.Effect[]> backup, ref HashSet <string> parseErrors, bool noParseErrors = false, VehicleInfo vehicleDefPrefab = null) { if (vehicleDef?.Name == null) { if (!noParseErrors) { parseErrors.Add(packageName + " - Vehicle name missing."); } return; } if (vehicleDefPrefab == null) { vehicleDefPrefab = FindVehicle(vehicleDef.Name, packageName); } if (vehicleDefPrefab == null) { if (!noParseErrors) { parseErrors.Add(packageName + " - Vehicle with name " + vehicleDef.Name + " not loaded."); } return; } if (vehicleDef.Effects == null || vehicleDef.Effects.Count == 0) { if (!noParseErrors) { parseErrors.Add(packageName + " - No effects specified for " + vehicleDef.Name + "."); } return; } if (vehicleDef.ApplyToTrailersOnly) { HashSet <VehicleInfo> trailers = new HashSet <VehicleInfo>(); foreach (var trailer in vehicleDefPrefab.m_trailers) { if (!trailers.Contains(trailer.m_info)) { trailers.Add(trailer.m_info); } } foreach (var trailer in trailers) { var trailerDef = new VehicleEffectsDefinition.Vehicle(); trailerDef.Name = trailer.name; trailerDef.Effects = vehicleDef.Effects; ParseVehicleDefinition(trailerDef, packageName, ref backup, ref parseErrors, noParseErrors, trailer); } return; } // Backup default effects array if (!backup.ContainsKey(vehicleDefPrefab)) { var effects = new VehicleInfo.Effect[vehicleDefPrefab.m_effects.Length]; vehicleDefPrefab.m_effects.CopyTo(effects, 0); backup.Add(vehicleDefPrefab, effects); } var vehicleEffectsList = new List <VehicleInfo.Effect>(); vehicleEffectsList.AddRange(vehicleDefPrefab.m_effects); foreach (var effectDef in vehicleDef.Effects) { ParseEffectDefinition(vehicleDef, effectDef, vehicleEffectsList, ref parseErrors); } // Remove and log null effects for (int i = vehicleEffectsList.Count - 1; i >= 0; i--) { if (vehicleEffectsList[i].m_effect == null) { Logging.LogWarning("Found effect that is NULL for vehicle " + vehicleDef.Name + " at index " + i + ", removing."); vehicleEffectsList.RemoveAt(i); } } // Update the VehicleInfo with the new effects vehicleDefPrefab.m_effects = vehicleEffectsList.ToArray(); // taken from VehicleInfo.InitializePrefab if (vehicleDefPrefab.m_effects != null) { for (int j = 0; j < vehicleDefPrefab.m_effects.Length; j++) { if (vehicleDefPrefab.m_effects[j].m_effect != null) { vehicleDefPrefab.m_effects[j].m_effect.InitializeEffect(); } } } }
public void ReloadVehicleEffects() { Logging.Log("Reloading Vehicle Effects"); ResetVehicleEffects(); UpdateVehicleEffects(); }