Skip to content

Instantly share code, notes, and snippets.

@ronyx69
Created July 16, 2018 15:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ronyx69/68d57f77e721dabefaefa864cc29616b to your computer and use it in GitHub Desktop.
Save ronyx69/68d57f77e721dabefaefa864cc29616b to your computer and use it in GitHub Desktop.
Source code for the Detail mod.
using ColossalFramework.IO;
using ColossalFramework.UI;
using ICities;
using System;
using System.IO;
using System.Reflection;
using System.Xml.Serialization;
using UnityEngine;
namespace Detail
{
public class HotkeyBehaviour : MonoBehaviour
{
void Update()
{
if (Input.GetKey(KeyCode.LeftShift) && Input.GetKey(KeyCode.LeftAlt) && Input.GetKeyDown(KeyCode.D))
{
var settings = DetailMod.Settings;
if (settings.distance == 0f) settings.distance = 1f;
else settings.distance = 0f;
DetailMod.UpdateDistanceSliderFromSettings();
Detail.SetRenderPropertiesAll();
}
}
}
public class DetailMod : IUserMod
{
public string Name => "Detail";
public string Description => "Allows colorable vegetation and custom render distance for props.";
private static Configuration settings;
public static Configuration Settings
{
get
{
if (settings == null)
{
settings = Configuration.LoadConfiguration();
if (settings == null)
{
settings = new Configuration();
Configuration.SaveConfiguration();
}
}
return settings;
}
set
{
settings = value;
}
}
#region UserInterface
private float sliderWidth = 715f;
private float sliderHeight = 16f;
private string toolTipText = "Hold SHIFT to drag all sliders";
private UISlider redSlider;
private UISlider greenSlider;
private UISlider blueSlider;
private UISlider brightnessSlider;
private UISlider colorSlider;
private static UISlider distanceSlider;
private UIButton matchButton;
private static void UpdateSlider(UISlider slider, float value)
{
slider.value = value;
var rootPanel = slider.parent as UIPanel;
var label = rootPanel.Find<UILabel>("Label");
label.text = value.ToString();
}
public static void UpdateDistanceSliderFromSettings()
{
distanceSlider.value = settings.distance;
}
public static void UpdateSlider2(UISlider slider, float value)
{
slider.value = value;
}
public void OnSettingsUI(UIHelperBase helper)
{
UIHelperBase mainGroup = helper.AddGroup(" Detail");
distanceSlider = (UISlider)mainGroup.AddSlider("Render Distance", 0f, 1f, 0.1f, Settings.distance, (f) =>
{
distanceSlider.tooltipBox.isVisible = true;
Settings.distance = f;
UpdateSlider2(distanceSlider, f);
Detail.SetRenderPropertiesAll();
});
distanceSlider.color = Color.gray;
distanceSlider.scrollWheelAmount = 0.1f;
distanceSlider.width = sliderWidth;
distanceSlider.height = sliderHeight;
UpdateSlider2(distanceSlider, Settings.distance);
mainGroup.AddSpace(32);
UIHelperBase sliderGroup = helper.AddGroup(" Vegetation Color");
redSlider = (UISlider)sliderGroup.AddSlider(Settings.VegetationColor.r.ToString(), 0f, 255f, 1f, Settings.VegetationColor.r, (f) =>
{
if (Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift))
{
var difference = f - Settings.VegetationColor.r;
var green = Settings.VegetationColor.g;
var blue = Settings.VegetationColor.b;
if (blue + difference >= 0 && blue + difference <= 255)
{
UpdateSlider(blueSlider, blue + difference);
Settings.VegetationColor.b = blue + difference;
}
if (green + difference >= 0 && green + difference <= 255)
{
UpdateSlider(greenSlider, green + difference);
Settings.VegetationColor.g = green + difference;
}
redSlider.tooltipBox.isVisible = false;
}
else redSlider.tooltipBox.isVisible = true;
Settings.VegetationColor.r = f;
UpdateSlider(redSlider, f);
Detail.ApplyColors();
});
redSlider.color = Color.Lerp(Color.red, Color.white, 0.25f);
redSlider.tooltip = toolTipText;
redSlider.scrollWheelAmount = 1f;
redSlider.width = sliderWidth;
redSlider.height = sliderHeight;
UpdateSlider(redSlider, Settings.VegetationColor.r);
greenSlider = (UISlider)sliderGroup.AddSlider(Settings.VegetationColor.g.ToString(), 0f, 255f, 1f, Settings.VegetationColor.g, (f) =>
{
if (Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift))
{
var difference = f - Settings.VegetationColor.g;
var red = Settings.VegetationColor.r;
var blue = Settings.VegetationColor.b;
if (red + difference >= 0 && red + difference <= 255)
{
UpdateSlider(redSlider, red + difference);
Settings.VegetationColor.r = red + difference;
}
if (blue + difference >= 0 && blue + difference <= 255)
{
UpdateSlider(blueSlider, blue + difference);
Settings.VegetationColor.b = blue + difference;
}
greenSlider.tooltipBox.isVisible = false;
}
else greenSlider.tooltipBox.isVisible = true;
greenSlider.RefreshTooltip();
Settings.VegetationColor.g = f;
UpdateSlider(greenSlider, f);
Detail.ApplyColors();
});
greenSlider.color = Color.Lerp(Color.green, Color.white, 0.25f);
greenSlider.tooltip = toolTipText;
greenSlider.scrollWheelAmount = 1f;
greenSlider.width = sliderWidth;
greenSlider.height = sliderHeight;
UpdateSlider(greenSlider, Settings.VegetationColor.g);
blueSlider = (UISlider)sliderGroup.AddSlider(Settings.VegetationColor.b.ToString(), 0f, 255f, 1f, Settings.VegetationColor.b, (f) =>
{
if (Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift))
{
var difference = f - Settings.VegetationColor.b;
var red = Settings.VegetationColor.r;
var green = Settings.VegetationColor.g;
if (red + difference >= 0 && red + difference <= 255)
{
UpdateSlider(redSlider, red + difference);
Settings.VegetationColor.r = red + difference;
}
if (green + difference >= 0 && green + difference <= 255)
{
UpdateSlider(greenSlider, green + difference);
Settings.VegetationColor.g = green + difference;
}
blueSlider.tooltipBox.isVisible = false;
}
else blueSlider.tooltipBox.isVisible = true;
blueSlider.RefreshTooltip();
Settings.VegetationColor.b = f;
UpdateSlider(blueSlider, f);
Detail.ApplyColors();
});
blueSlider.color = Color.Lerp(Color.blue, Color.white, 0.25f);
blueSlider.tooltip = toolTipText;
blueSlider.scrollWheelAmount = 1f;
blueSlider.width = sliderWidth;
blueSlider.height = sliderHeight;
UpdateSlider(blueSlider, Settings.VegetationColor.b);
sliderGroup.AddSpace(24);
matchButton = (UIButton)sliderGroup.AddButton("Auto Match Grass", () =>
{
Detail.MatchColor();
UpdateSlider(redSlider, Settings.VegetationColor.r);
UpdateSlider(greenSlider, Settings.VegetationColor.g);
UpdateSlider(blueSlider, Settings.VegetationColor.b);
});
sliderGroup.AddSpace(32);
brightnessSlider = (UISlider)sliderGroup.AddSlider("Brightness Randomness", 0f, 1f, 0.1f, Settings.randomBrightness, (f) =>
{
brightnessSlider.tooltipBox.isVisible = true;
Settings.randomBrightness = f;
UpdateSlider2(brightnessSlider, f);
Detail.ApplyColors();
});
brightnessSlider.color = Color.gray;
brightnessSlider.scrollWheelAmount = 0.1f;
brightnessSlider.width = sliderWidth;
brightnessSlider.height = sliderHeight;
UpdateSlider2(brightnessSlider, Settings.randomBrightness);
sliderGroup.AddSpace(8);
colorSlider = (UISlider)sliderGroup.AddSlider("Color Randomness", 0f, 1f, 0.1f, Settings.randomColor, (f) =>
{
colorSlider.tooltipBox.isVisible = true;
Settings.randomColor = f;
UpdateSlider2(colorSlider, f);
Detail.ApplyColors();
});
colorSlider.color = Color.gray;
colorSlider.scrollWheelAmount = 0.1f;
colorSlider.width = sliderWidth;
colorSlider.height = sliderHeight;
UpdateSlider2(colorSlider, Settings.randomColor);
//add hotkey behaviour
var uiHelper = helper as UIHelper;
var uiComponent = uiHelper.self as UIComponent;
uiComponent.gameObject.AddComponent<HotkeyBehaviour>();
}
#endregion
}
public class Configuration
{
[XmlIgnore]
private static readonly string configurationPath = Path.Combine(DataLocation.localApplicationData, "Detail.xml");
public Color VegetationColor = new Color(255f, 255f, 255f, 1f);
public float randomBrightness = 0.5f;
public float randomColor = 0.5f;
public float distance = 1f;
public bool AutoDeployed = false;
public Configuration() { }
public void OnPreSerialize() { }
public void OnPostDeserialize() { }
public static void SaveConfiguration()
{
var fileName = configurationPath;
var config = DetailMod.Settings;
var serializer = new XmlSerializer(typeof(Configuration));
using (var writer = new StreamWriter(fileName))
{
config.OnPreSerialize();
serializer.Serialize(writer, config);
}
}
public static Configuration LoadConfiguration()
{
var fileName = configurationPath;
var serializer = new XmlSerializer(typeof(Configuration));
try
{
using (var reader = new StreamReader(fileName))
{
var config = serializer.Deserialize(reader) as Configuration;
return config;
}
}
catch (Exception ex)
{
Debug.Log(string.Format("[Detail]: Error Parsing {0}: {1}", fileName, ex.Message.ToString()));
return null;
}
}
}
public class DetailLoading : LoadingExtensionBase
{
public override void OnLevelLoaded(LoadMode mode)
{
base.OnLevelLoaded(mode);
UIView.library.Get<UIPanel>("OptionsPanel").eventVisibilityChanged += (c, i) => Detail.SetRenderPropertiesAll();
UIView.GetAView().FindUIComponent<UIDropDown>("LevelOfDetail").eventSelectedIndexChanged += (c, i) => Detail.SetRenderPropertiesAll();
}
public override void OnLevelUnloading()
{
base.OnLevelUnloading();
Detail.Loaded = false;
UIView.library.Get<UIPanel>("OptionsPanel").eventVisibilityChanged -= (c, i) => Detail.SetRenderPropertiesAll();
UIView.GetAView().FindUIComponent<UIDropDown>("LevelOfDetail").eventSelectedIndexChanged -= (c, i) => Detail.SetRenderPropertiesAll();
}
}
public class Detail
{
public static bool Loaded;
internal static void SetRenderProperties(PropInfo prefab)
{
string[] values = prefab.m_material.name.Split(' ');
prefab.m_lodRenderDistance = Convert.ToSingle(values[1]) * DetailMod.Settings.distance;
prefab.m_maxRenderDistance = Convert.ToSingle(values[2]) * DetailMod.Settings.distance;
}
internal static void SetRenderProperties(TreeInfo prefab)
{
prefab.m_renderMaterial.SetTexture("_MainTex", prefab.m_material.GetTexture("_MainTex"));
prefab.m_renderMaterial.SetTexture("_XYCAMap", prefab.m_material.GetTexture("_XYCAMap"));
}
internal static void SetRenderPropertiesAll()
{
for (uint i = 0; i < PrefabCollection<PropInfo>.LoadedCount(); i++)
{
var prefab = PrefabCollection<PropInfo>.GetLoaded(i);
if (prefab == null) continue;
if (prefab.m_material.name.Contains("DetailMod_")) SetRenderProperties(prefab);
}
for (uint i = 0; i < PrefabCollection<TreeInfo>.LoadedCount(); i++)
{
var prefab = PrefabCollection<TreeInfo>.GetLoaded(i);
if (prefab == null) continue;
if (prefab.m_material.name.Contains("DetailMod_Tree")) SetRenderProperties(prefab);
}
}
internal static void ApplyProperties()
{
SetRenderPropertiesAll();
ApplyColors();
}
internal static void MatchColor()
{
var correction = 0.53f; var emphasis = 6;
var tex = UnityEngine.Object.FindObjectOfType<TerrainProperties>().m_grassDiffuse;
var texColors = tex.GetPixels32(); var total = texColors.Length;
var r = 0; var g = 0; var b = 0;
for (var i = 0; i < total; i++)
{
r += texColors[i].r; g += texColors[i].g; b += texColors[i].b;
}
r = r / total; g = g / total; b = b / total;
if (r > g && r > b)
{
r = r + emphasis;
if (g > b) g = g + Mathf.RoundToInt(emphasis / 2f);
else if (b > g) b = b + Mathf.RoundToInt(emphasis / 2f);
}
else if (g > r && g > b)
{
g = g + emphasis;
if (r > b) r = r + Mathf.RoundToInt(emphasis / 2f);
else if (b > r) b = b + Mathf.RoundToInt(emphasis / 2f);
}
else if (b > r && b > g)
{
b = b + emphasis;
if (r > g) r = r + Mathf.RoundToInt(emphasis / 2f);
else if (g > r) g = g + Mathf.RoundToInt(emphasis / 2f);
}
DetailMod.Settings.VegetationColor.r = Mathf.RoundToInt(Mathf.Pow((r / 255f), correction) * 255);
DetailMod.Settings.VegetationColor.g = Mathf.RoundToInt(Mathf.Pow((g / 255f), correction) * 255);
DetailMod.Settings.VegetationColor.b = Mathf.RoundToInt(Mathf.Pow((b / 255f), correction) * 255);
ApplyColors();
}
internal static void ApplyColors()
{
var rgb = DetailMod.Settings.VegetationColor;
var randomBrightness = DetailMod.Settings.randomBrightness;
var randomColor = DetailMod.Settings.randomColor;
Color colord = new Color(
Mathf.Pow((rgb.r / 255f), (1.0f)),
Mathf.Pow((rgb.g / 255f), (1.0f)),
Mathf.Pow((rgb.b / 255f), (1.0f)));
Color color0 = new Color(
Mathf.Pow((rgb.r / 255f), (1.0f + (randomBrightness * 0.2f))),
Mathf.Pow((rgb.g / 255f), (1.0f + (randomBrightness * 0.2f) - (randomColor * 0.1f))),
Mathf.Pow((rgb.b / 255f), (1.0f + (randomBrightness * 0.2f))));
Color color1 = new Color(
Mathf.Pow((rgb.r / 255f), (1.0f + (randomBrightness * 0.1f) - (randomColor * 0.05f))),
Mathf.Pow((rgb.g / 255f), (1.0f + (randomBrightness * 0.1f))),
Mathf.Pow((rgb.b / 255f), (1.0f + (randomBrightness * 0.1f) + (randomColor * 0.05f))));
Color color2 = new Color(
Mathf.Pow((rgb.r / 255f), (1.0f - (randomBrightness * 0.1f) + (randomColor * 0.05f))),
Mathf.Pow((rgb.g / 255f), (1.0f - (randomBrightness * 0.1f))),
Mathf.Pow((rgb.b / 255f), (1.0f - (randomBrightness * 0.1f) - (randomColor * 0.05f))));
Color color3 = new Color(
Mathf.Pow((rgb.r / 255f), (1.0f - (randomBrightness * 0.2f))),
Mathf.Pow((rgb.g / 255f), (1.0f - (randomBrightness * 0.2f) + (randomColor * 0.1f))),
Mathf.Pow((rgb.b / 255f), (1.0f - (randomBrightness * 0.2f))));
for (uint i = 0; i < PrefabCollection<PropInfo>.LoadedCount(); i++)
{
var prefab = PrefabCollection<PropInfo>.GetLoaded(i);
if (prefab == null) continue;
if (prefab.m_material.name.Contains("DetailMod_Vegetation"))
{
prefab.m_color0 = color0; prefab.m_color1 = color1;
prefab.m_color2 = color2; prefab.m_color3 = color3;
}
}
for (uint i = 0; i < PrefabCollection<TreeInfo>.LoadedCount(); i++)
{
var prefab = PrefabCollection<TreeInfo>.GetLoaded(i);
if (prefab == null) continue;
if (prefab.m_material.name.Contains("DetailMod_Tree"))
{
prefab.m_defaultColor = colord;
prefab.m_material.color = colord;
prefab.m_renderMaterial.color = colord;
prefab.m_maxBrightness = 1.0f + (randomBrightness * 0.2f);
prefab.m_minBrightness = 1.0f - (randomBrightness * 0.2f);
}
}
Configuration.SaveConfiguration();
SetRenderPropertiesAll();
}
}
public class DisableBlur : ThreadingExtensionBase
{
private UIComponent component;
public override void OnUpdate(float realTimeDelta, float simulationTimeDelta)
{
if (!Detail.Loaded && LoadingManager.instance.m_loadingComplete)
{
Detail.ApplyProperties();
Detail.Loaded = true;
}
if (component == null) component = UIView.library.Get("OptionsPanel");
if (component != null && component.isVisible)
{
UITextureSprite uITextureSprite = Util.ReadPrivate<DesaturationControl, UITextureSprite>(UnityEngine.Object.FindObjectOfType<DesaturationControl>(), "m_Target");
if (uITextureSprite.opacity != 0f)
{
uITextureSprite.opacity = 0f;
Util.WritePrivate<DesaturationControl, UITextureSprite>(UnityEngine.Object.FindObjectOfType<DesaturationControl>(), "m_Target", uITextureSprite);
}
}
}
}
public static class Util
{
public static Q ReadPrivate<T, Q>(T o, string fieldName)
{
FieldInfo[] fields = typeof(T).GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
FieldInfo fieldInfo = null;
FieldInfo[] array = fields;
for (int i = 0; i < array.Length; i++)
{
FieldInfo fieldInfo2 = array[i];
if (fieldInfo2.Name == fieldName)
{
fieldInfo = fieldInfo2;
break;
}
}
return (Q)((object)fieldInfo.GetValue(o));
}
public static void WritePrivate<T, Q>(T o, string fieldName, object value)
{
FieldInfo[] fields = typeof(T).GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
FieldInfo fieldInfo = null;
FieldInfo[] array = fields;
for (int i = 0; i < array.Length; i++)
{
FieldInfo fieldInfo2 = array[i];
if (fieldInfo2.Name == fieldName)
{
fieldInfo = fieldInfo2;
break;
}
}
fieldInfo.SetValue(o, value);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment