Skip to content

Instantly share code, notes, and snippets.

@boformer
Created November 13, 2015 17:32
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save boformer/31ec55ce97c040bbc89a to your computer and use it in GitHub Desktop.
Save boformer/31ec55ce97c040bbc89a to your computer and use it in GitHub Desktop.
Sub-Buildings Enabler Mod for Cities: Skylines
using ColossalFramework.IO;
using ColossalFramework.Packaging;
using ColossalFramework.UI;
using ICities;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Xml.Serialization;
using UnityEngine;
namespace SubBuildingsEnabler
{
public class SubBuildingsEnablerMod : LoadingExtensionBase, IUserMod
{
public string Name
{
get { return "Sub-Buildings Enabler"; }
}
public string Description
{
get { return "Adds support for large custom buildings made of multiple parts (e.g. airports with multiple gates)"; }
}
public override void OnLevelLoaded(LoadMode mode)
{
base.OnLevelLoaded(mode);
// Don't load in asset editor (saving asset with sub-buildings causes error)
if (mode == LoadMode.NewAsset || mode == LoadMode.LoadAsset) return;
var subBuildingsDefParseErrors = new HashSet<string>();
var checkedPaths = new List<string>();
for (uint i = 0; i < PrefabCollection<BuildingInfo>.LoadedCount(); i++)
{
var prefab = PrefabCollection<BuildingInfo>.GetLoaded(i);
if (prefab == null) continue;
// search for SubBuildingsEnabler.xml
var asset = PackageManager.FindAssetByName(prefab.name);
if (asset == null || asset.package == null) continue;
var crpPath = asset.package.packagePath;
if (crpPath == null) continue;
var subBuildingsDefPath = Path.Combine(Path.GetDirectoryName(crpPath), "SubBuildingsDefinition.xml");
// skip files which were already parsed
if (checkedPaths.Contains(subBuildingsDefPath)) continue;
checkedPaths.Add(subBuildingsDefPath);
if (!File.Exists(subBuildingsDefPath)) continue;
SubBuildingsDefinition subBuildingsDef = null;
var xmlSerializer = new XmlSerializer(typeof(SubBuildingsDefinition));
try
{
using (StreamReader streamReader = new System.IO.StreamReader(subBuildingsDefPath))
{
subBuildingsDef = xmlSerializer.Deserialize(streamReader) as SubBuildingsDefinition;
}
}
catch (Exception e)
{
Debug.LogException(e);
subBuildingsDefParseErrors.Add(asset.package.packageName + " - " + e.Message);
continue;
}
if (subBuildingsDef == null || subBuildingsDef.Buildings == null || subBuildingsDef.Buildings.Count == 0)
{
subBuildingsDefParseErrors.Add(asset.package.packageName + " - subBuildingsDef is null or empty.");
continue;
}
foreach (var parentBuildingDef in subBuildingsDef.Buildings)
{
if (parentBuildingDef == null || parentBuildingDef.Name == null)
{
subBuildingsDefParseErrors.Add(asset.package.packageName + " - Building name missing.");
continue;
}
var parentBuildingPrefab = FindPrefab(parentBuildingDef.Name, asset.package.packageName);
if (parentBuildingPrefab == null)
{
subBuildingsDefParseErrors.Add(asset.package.packageName + " - Building with name " + parentBuildingDef.Name + " not loaded.");
continue;
}
if (parentBuildingDef.SubBuildings == null || parentBuildingDef.SubBuildings.Count == 0)
{
subBuildingsDefParseErrors.Add(asset.package.packageName + " - No sub buildings specified for " + parentBuildingDef.Name + ".");
continue;
}
var subBuildings = new List<BuildingInfo.SubInfo>();
foreach (var subBuildingDef in parentBuildingDef.SubBuildings)
{
if (subBuildingDef == null || subBuildingDef.Name == null)
{
subBuildingsDefParseErrors.Add(parentBuildingDef.Name + " - Sub-building name missing.");
continue;
}
var subBuildingPrefab = FindPrefab(subBuildingDef.Name, asset.package.packageName);
if (subBuildingPrefab == null)
{
subBuildingsDefParseErrors.Add(parentBuildingDef.Name + " - Sub-building with name " + subBuildingDef.Name + " not loaded.");
continue;
}
var subBuilding = new BuildingInfo.SubInfo
{
m_buildingInfo = subBuildingPrefab,
m_position = new Vector3(subBuildingDef.PosX, subBuildingDef.PosY, subBuildingDef.PosZ),
m_angle = subBuildingDef.Angle,
m_fixedHeight = subBuildingDef.FixedHeight
};
subBuildings.Add(subBuilding);
// this is usually done in the InitializePrefab method
if (subBuildingDef.FixedHeight && !parentBuildingPrefab.m_fixedHeight) parentBuildingPrefab.m_fixedHeight = true;
}
if (subBuildings.Count == 0)
{
subBuildingsDefParseErrors.Add("No sub buildings specified for " + parentBuildingDef.Name + ".");
continue;
}
parentBuildingPrefab.m_subBuildings = subBuildings.ToArray();
}
}
if (subBuildingsDefParseErrors.Count > 0)
{
var errorMessage = "Error while parsing sub-building definition file(s). Contact the author of the assets. \n"
+ "List of errors:\n";
foreach (var error in subBuildingsDefParseErrors) errorMessage += error + '\n';
UIView.library.ShowModal<ExceptionPanel>("ExceptionPanel").SetMessage("Sub-Buildings Enabler", errorMessage, true);
}
}
private BuildingInfo FindPrefab(string prefabName, string packageName)
{
var prefab = PrefabCollection<BuildingInfo>.FindLoaded(prefabName);
if (prefab == null) prefab = PrefabCollection<BuildingInfo>.FindLoaded(prefabName + "_Data");
if (prefab == null) prefab = PrefabCollection<BuildingInfo>.FindLoaded(PathEscaper.Escape(prefabName) + "_Data");
if (prefab == null) prefab = PrefabCollection<BuildingInfo>.FindLoaded(packageName + "." + prefabName + "_Data");
if (prefab == null) prefab = PrefabCollection<BuildingInfo>.FindLoaded(packageName + "." + PathEscaper.Escape(prefabName) + "_Data");
return prefab;
}
}
public class SubBuildingsDefinition
{
public List<Building> Buildings { get; set; }
public SubBuildingsDefinition()
{
Buildings = new List<Building>();
}
public class Building
{
[XmlAttribute("name"), DefaultValue(null)]
public string Name { get; set; }
public List<SubBuilding> SubBuildings { get; set; }
public Building()
{
SubBuildings = new List<SubBuilding>();
}
}
public class SubBuilding
{
[XmlAttribute("name"), DefaultValue(null)]
public string Name { get; set; }
[XmlAttribute("pos-x"), DefaultValue(0f)]
public float PosX { get; set; }
[XmlAttribute("pos-y"), DefaultValue(0f)]
public float PosY { get; set; }
[XmlAttribute("pos-z"), DefaultValue(0f)]
public float PosZ { get; set; }
[XmlAttribute("angle"), DefaultValue(0f)]
public float Angle { get; set; }
[XmlAttribute("fixed-height"), DefaultValue(true)]
public bool FixedHeight { get; set; }
public SubBuilding()
{
FixedHeight = true;
}
}
/*
public static void Save(SubBuildingsDefinition instance)
{
if (instance != null)
{
var configPath = "SubBuildingsEnabler.xml";
XmlSerializer xmlSerializer = new XmlSerializer(typeof(SubBuildingsDefinition));
XmlSerializerNamespaces noNamespaces = new XmlSerializerNamespaces();
noNamespaces.Add("", "");
try
{
using (StreamWriter streamWriter = new System.IO.StreamWriter(configPath))
{
xmlSerializer.Serialize(streamWriter, instance, noNamespaces);
}
}
catch (Exception e)
{
Debug.LogException(e);
}
}
}
*/
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment