diff --git a/src/RealTime/Core/RealTimeCore.cs b/src/RealTime/Core/RealTimeCore.cs
index 6648a7246d7bd6116513718f7be2a83091411bc6..02e28d914ed56ea00fe3f99812c5cd7a8ded8da3 100644
--- a/src/RealTime/Core/RealTimeCore.cs
+++ b/src/RealTime/Core/RealTimeCore.cs
@@ -143,6 +143,17 @@ namespace RealTime.Core
var result = new RealTimeCore(timeAdjustment, customTimeBar, eventManager, patcher);
eventManager.EventsChanged += result.CityEventsChanged;
+
+ var statistics = new Statistics(timeInfo, localizationProvider);
+ if (statistics.Initialize())
+ {
+ statistics.RefreshUnits();
+ }
+ else
+ {
+ statistics = null;
+ }
+
SimulationHandler.NewDay += result.CityEventsChanged;
SimulationHandler.TimeAdjustment = timeAdjustment;
@@ -152,6 +163,7 @@ namespace RealTime.Core
SimulationHandler.Buildings = BuildingAIPatches.RealTimeAI;
SimulationHandler.Buildings.UpdateFrameDuration();
SimulationHandler.Buildings.InitializeLightState();
+ SimulationHandler.Statistics = statistics;
AwakeSleepSimulation.Install(configProvider.Configuration);
@@ -205,6 +217,8 @@ namespace RealTime.Core
SimulationHandler.WeatherInfo = null;
SimulationHandler.Buildings = null;
SimulationHandler.CitizenProcessor = null;
+ SimulationHandler.Statistics?.Close();
+ SimulationHandler.Statistics = null;
isEnabled = false;
}
diff --git a/src/RealTime/Localization/Constants.cs b/src/RealTime/Localization/Constants.cs
index 908c5080c10d03e8a5ab8f2f9e74979e5a8168a8..ed3c0e781ed83aef9c51062d86eeb4fd0f23344e 100644
--- a/src/RealTime/Localization/Constants.cs
+++ b/src/RealTime/Localization/Constants.cs
@@ -20,7 +20,16 @@ namespace RealTime.Localization
/// The XML item key attribute name.
public const string XmlKeyAttribute = "id";
+ /// The XML translation node name.
+ public const string XmlTranslationNodeName = "translation";
+
/// The XML item value attribute name.
public const string XmlValueAttribute = "value";
+
+ /// The XML override node name.
+ public const string XmlOverrideNodeName = "overrides";
+
+ /// The XML override node 'type' attribute name.
+ public const string XmlOverrideTypeAttribute = "type";
}
}
\ No newline at end of file
diff --git a/src/RealTime/Localization/ILocalizationProvider.cs b/src/RealTime/Localization/ILocalizationProvider.cs
index d9dbc24042060815493db2e9213fa8806c53ee6f..95f514a78783da015dee3478aac5dba645938c5e 100644
--- a/src/RealTime/Localization/ILocalizationProvider.cs
+++ b/src/RealTime/Localization/ILocalizationProvider.cs
@@ -4,6 +4,7 @@
namespace RealTime.Localization
{
+ using System.Collections.Generic;
using System.Globalization;
///
@@ -18,5 +19,12 @@ namespace RealTime.Localization
/// The value ID.
/// The translated string value or the placeholder text on failure.
string Translate(string id);
+
+ /// Gets a dictionary representing the game's translations that should be overridden
+ /// by this mod. Can return null.
+ /// The overridden translations type string.
+ /// A map of key-value pairs for translations to override, or null.
+ /// Thrown when the argument is null.
+ IDictionary GetOverriddenTranslations(string type);
}
}
\ No newline at end of file
diff --git a/src/RealTime/Localization/LocalizationProvider.cs b/src/RealTime/Localization/LocalizationProvider.cs
index 7b00e90060eebb88101e8423f2d9c639a3785d38..91992c3006e13b9313f9ce29a6c43eec09cb7fed 100644
--- a/src/RealTime/Localization/LocalizationProvider.cs
+++ b/src/RealTime/Localization/LocalizationProvider.cs
@@ -15,6 +15,7 @@ namespace RealTime.Localization
{
private readonly string localeStorage;
private readonly Dictionary translation = new Dictionary();
+ private readonly Dictionary> overrides = new Dictionary>();
/// Initializes a new instance of the class.
/// The root path.
@@ -72,6 +73,22 @@ namespace RealTime.Localization
return result == LoadingResult.Success;
}
+ /// Gets a dictionary representing the game's translations that should be overridden
+ /// by this mod. Can return null.
+ /// The overridden translations type string.
+ /// A map of key-value pairs for translations to override, or null.
+ /// Thrown when the argument is null.
+ public IDictionary GetOverriddenTranslations(string type)
+ {
+ if (type == null)
+ {
+ throw new ArgumentNullException(nameof(type));
+ }
+
+ overrides.TryGetValue(type, out Dictionary result);
+ return result;
+ }
+
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase", Justification = "No security issues here")]
private static string GetLocaleNameFromLanguage(string language)
{
@@ -115,6 +132,7 @@ namespace RealTime.Localization
}
translation.Clear();
+ overrides.Clear();
string path = Path.Combine(localeStorage, language + FileExtension);
if (!File.Exists(path))
@@ -132,17 +150,50 @@ namespace RealTime.Localization
foreach (XmlNode node in doc.DocumentElement.ChildNodes)
{
- translation[node.Attributes[XmlKeyAttribute].Value] = node.Attributes[XmlValueAttribute].Value;
+ switch (node.Name)
+ {
+ case XmlTranslationNodeName:
+ translation[node.Attributes[XmlKeyAttribute].Value] = node.Attributes[XmlValueAttribute].Value;
+ break;
+
+ case XmlOverrideNodeName when node.HasChildNodes:
+ ReadOverrides(node);
+ break;
+ }
}
}
catch (Exception ex)
{
Log.Error($"The 'Real Time' cannot load data from localization file '{path}', error message: {ex}");
translation.Clear();
+ overrides.Clear();
return LoadingResult.Failure;
}
return LoadingResult.Success;
}
+
+ private void ReadOverrides(XmlNode overridesNode)
+ {
+ string type = overridesNode.Attributes[XmlOverrideTypeAttribute]?.Value;
+ if (type == null)
+ {
+ return;
+ }
+
+ if (!overrides.TryGetValue(type, out Dictionary typeOverrides))
+ {
+ typeOverrides = new Dictionary();
+ overrides[type] = typeOverrides;
+ }
+
+ foreach (XmlNode node in overridesNode.ChildNodes)
+ {
+ if (node.Name == XmlTranslationNodeName)
+ {
+ typeOverrides[node.Attributes[XmlKeyAttribute].Value] = node.Attributes[XmlValueAttribute].Value;
+ }
+ }
+ }
}
}
\ No newline at end of file
diff --git a/src/RealTime/Localization/TranslationKeys.cs b/src/RealTime/Localization/TranslationKeys.cs
index b7de428f946d32b0c3dc53872cc6ffab3f83f9da..03c007ec5ff07522d8e49ae93524db0f7f18ffdb 100644
--- a/src/RealTime/Localization/TranslationKeys.cs
+++ b/src/RealTime/Localization/TranslationKeys.cs
@@ -20,5 +20,8 @@ namespace RealTime.Localization
/// The key for a 'incompatible mods found' message.
public const string IncompatibleModsFoundMessage = "IncompatibleModsFoundMessage";
+
+ /// The key for the abbreviated 'minutes' text.
+ public const string Minutes = "Minutes";
}
}
diff --git a/src/RealTime/Localization/Translations/de.xml b/src/RealTime/Localization/Translations/de.xml
index a65295b0143283664742f3fce89511fb52d5b52b..260b4fd8598e78f68e6fd487a2c2b4233cc46e47 100644
--- a/src/RealTime/Localization/Translations/de.xml
+++ b/src/RealTime/Localization/Translations/de.xml
@@ -4,6 +4,7 @@
+
@@ -80,5 +81,94 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/RealTime/Localization/Translations/en.xml b/src/RealTime/Localization/Translations/en.xml
index 7b486b3560618c70ae33f481681a38f1791c8832..685cb45b0e63e46c41711dfd82a7560d8d8e592b 100644
--- a/src/RealTime/Localization/Translations/en.xml
+++ b/src/RealTime/Localization/Translations/en.xml
@@ -4,6 +4,7 @@
+
@@ -80,5 +81,94 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/RealTime/Localization/Translations/es.xml b/src/RealTime/Localization/Translations/es.xml
index 3bf4d112352b0dea8f20d28f1a616d9fca5e1f44..0da60bbe4817b0554c271f2a6d52d417b1bc39c0 100644
--- a/src/RealTime/Localization/Translations/es.xml
+++ b/src/RealTime/Localization/Translations/es.xml
@@ -4,6 +4,7 @@
+
@@ -80,5 +81,94 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/RealTime/Localization/Translations/fr.xml b/src/RealTime/Localization/Translations/fr.xml
index 85ab630b76e8ccb45369d51c7328dad7d94dc5a7..1d61b273f70012643d9bbf1cba0f44c23d3c476a 100644
--- a/src/RealTime/Localization/Translations/fr.xml
+++ b/src/RealTime/Localization/Translations/fr.xml
@@ -4,6 +4,7 @@
+
@@ -80,5 +81,94 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/RealTime/Localization/Translations/ko.xml b/src/RealTime/Localization/Translations/ko.xml
index 3204123759e4b2f6c319e02629007471e7cfdf7b..6c627ae55b4d71c955795b77c0726999c8ccd1f5 100644
--- a/src/RealTime/Localization/Translations/ko.xml
+++ b/src/RealTime/Localization/Translations/ko.xml
@@ -4,7 +4,8 @@
-
+
+
@@ -80,5 +81,94 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/RealTime/Localization/Translations/pl.xml b/src/RealTime/Localization/Translations/pl.xml
index d9ca31fc82397da7dff7d1f32efef66a781ef8a2..4cddfc6f6d7f7b91a8081df6adcef70b0b89fb34 100644
--- a/src/RealTime/Localization/Translations/pl.xml
+++ b/src/RealTime/Localization/Translations/pl.xml
@@ -4,6 +4,7 @@
+
@@ -80,5 +81,94 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/RealTime/Localization/Translations/pt.xml b/src/RealTime/Localization/Translations/pt.xml
index ce97e47af3a7b0f69705411fe328475a1e6c722f..2e955ddc1c901a87ba067f475f8f14e3942ad215 100644
--- a/src/RealTime/Localization/Translations/pt.xml
+++ b/src/RealTime/Localization/Translations/pt.xml
@@ -4,6 +4,7 @@
+
@@ -80,5 +81,94 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/RealTime/Localization/Translations/ru.xml b/src/RealTime/Localization/Translations/ru.xml
index 4a66c77181f74ef2e9372323d4c947a665ef1b20..55dd3df3450e3f767af83c412df3d10549a1ff9b 100644
--- a/src/RealTime/Localization/Translations/ru.xml
+++ b/src/RealTime/Localization/Translations/ru.xml
@@ -4,6 +4,7 @@
+
@@ -80,5 +81,94 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/RealTime/Localization/Translations/zh.xml b/src/RealTime/Localization/Translations/zh.xml
index 4e9d73a24858574b28e35f212de75f7488204d54..c637e6a3cde098d912f92c7ea656576ee6ab7e92 100644
--- a/src/RealTime/Localization/Translations/zh.xml
+++ b/src/RealTime/Localization/Translations/zh.xml
@@ -4,6 +4,7 @@
+
@@ -80,5 +81,94 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/RealTime/RealTime.csproj b/src/RealTime/RealTime.csproj
index e0bba36440ddf16ded2bfe1d1a51f8928c0ed1c4..27346c64f9ff082e9b844f0d608a6246ea9dfaf4 100644
--- a/src/RealTime/RealTime.csproj
+++ b/src/RealTime/RealTime.csproj
@@ -139,6 +139,7 @@
+
diff --git a/src/RealTime/Simulation/SimulationHandler.cs b/src/RealTime/Simulation/SimulationHandler.cs
index 92dc4b84c45b298c45258f2903bea77cd5dd9dbe..8509065bea97d1ebae3b1a6b5b87c6ed58d78112 100644
--- a/src/RealTime/Simulation/SimulationHandler.cs
+++ b/src/RealTime/Simulation/SimulationHandler.cs
@@ -46,6 +46,9 @@ namespace RealTime.Simulation
/// Gets or sets the citizen processing class instance.
internal static CitizenProcessor CitizenProcessor { get; set; }
+ /// Gets or sets the statistics processing class instance.
+ internal static Statistics Statistics { get; set; }
+
///
/// Called before each game simulation tick. A tick contains multiple frames.
/// Performs the dispatching for this simulation phase.
@@ -70,6 +73,7 @@ namespace RealTime.Simulation
if (updateFrameLength)
{
Buildings?.UpdateFrameDuration();
+ Statistics?.RefreshUnits();
}
if (DayTimeSimulation == null || CitizenProcessor == null)
diff --git a/src/RealTime/Simulation/Statistics.cs b/src/RealTime/Simulation/Statistics.cs
new file mode 100644
index 0000000000000000000000000000000000000000..9942d7c911d34f2543b39a5a470c65404ef4c37e
--- /dev/null
+++ b/src/RealTime/Simulation/Statistics.cs
@@ -0,0 +1,260 @@
+//
+// Copyright (c) dymanoid. All rights reserved.
+//
+
+namespace RealTime.Simulation
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Reflection;
+ using ColossalFramework.Globalization;
+ using ColossalFramework.UI;
+ using RealTime.Localization;
+ using RealTime.Tools;
+ using UnityEngine;
+
+ ///
+ /// Handles the customization of the game's statistics numbers.
+ ///
+ internal sealed class Statistics
+ {
+ private const int VanillaFramesPerWeek = 3840;
+ private const string UnitPlaceholder = "{unit}";
+ private const string OverrddenTranslationType = "Units";
+ private const string CityInfoPanelName = "(Library) CityInfoPanel";
+ private const string DistrictInfoPanelName = "(Library) DistrictWorldInfoPanel";
+ private const string TouristsPanelName = "Tourists";
+ private const string TouristsLabelName = "Label";
+ private const string InfoPanelName = "InfoPanel";
+ private const string CitizensChangeLabelName = "ProjectedChange";
+ private const string IncomeLabelName = "ProjectedIncome";
+ private const string BuildingsButtonsContainer = "TSContainer";
+
+ private readonly ITimeInfo timeInfo;
+ private readonly ILocalizationProvider localizationProvider;
+ private readonly Locale customLocale;
+
+ private Locale mainLocale;
+ private UILabel cityInfoPanelTourists;
+ private UILabel districtInfoPanelTourists;
+ private UILabel labelPopulation;
+ private UILabel labelIncome;
+ private UITabContainer buildingsTabContainer;
+
+ /// Initializes a new instance of the class.
+ /// An object that provides the game's time information.
+ /// The object to get the current locale from.
+ /// Thrown when any argument is null.
+ public Statistics(ITimeInfo timeInfo, ILocalizationProvider localizationProvider)
+ {
+ this.timeInfo = timeInfo ?? throw new ArgumentNullException(nameof(timeInfo));
+ this.localizationProvider = localizationProvider ?? throw new ArgumentNullException(nameof(localizationProvider));
+
+ customLocale = new Locale();
+ }
+
+ /// Initializes this instance by preparing connections to the necessary game parts.
+ /// true on success; otherwise, false.
+ public bool Initialize()
+ {
+ if (mainLocale != null)
+ {
+ return true;
+ }
+
+ try
+ {
+ FieldInfo field = typeof(LocaleManager).GetField("m_Locale", BindingFlags.Instance | BindingFlags.NonPublic);
+ mainLocale = field.GetValue(LocaleManager.instance) as Locale;
+ }
+ catch (Exception ex)
+ {
+ Log.Warning("The 'Real Time' mod could not obtain the locale field of the LocaleManager, error message: " + ex);
+ return false;
+ }
+
+ cityInfoPanelTourists = GameObject
+ .Find(CityInfoPanelName)?
+ .GetComponent()?
+ .Find(TouristsPanelName)?
+ .Find(TouristsLabelName);
+
+ if (cityInfoPanelTourists == null)
+ {
+ Log.Warning("The 'Real Time' mod could not obtain the CityInfoPanel.Tourists.Label object");
+ }
+
+ districtInfoPanelTourists = GameObject
+ .Find(DistrictInfoPanelName)?
+ .GetComponent()?
+ .Find(TouristsPanelName)?
+ .Find(TouristsLabelName);
+
+ if (districtInfoPanelTourists == null)
+ {
+ Log.Warning("The 'Real Time' mod could not obtain the DistrictWorldInfoPanel.Tourists.Label object");
+ }
+
+ UIPanel infoPanel = UIView.Find(InfoPanelName);
+ if (infoPanel == null)
+ {
+ Log.Warning("The 'Real Time' mod could not obtain the InfoPanel object");
+ }
+ else
+ {
+ labelPopulation = infoPanel.Find(CitizensChangeLabelName);
+ if (labelPopulation == null)
+ {
+ Log.Warning("The 'Real Time' mod could not obtain the ProjectedChange object");
+ }
+
+ labelIncome = infoPanel.Find(IncomeLabelName);
+ if (labelIncome == null)
+ {
+ Log.Warning("The 'Real Time' mod could not obtain the ProjectedIncome object");
+ }
+ }
+
+ buildingsTabContainer = UIView.Find(BuildingsButtonsContainer);
+ if (buildingsTabContainer == null)
+ {
+ Log.Warning("The 'Real Time' mod could not obtain the TSContainer object");
+ }
+
+ LocaleManager.eventLocaleChanged += LocaleChanged;
+ return true;
+ }
+
+ /// Shuts down this instance.
+ public void Close()
+ {
+ LocaleManager.eventLocaleChanged -= LocaleChanged;
+ mainLocale = null;
+ cityInfoPanelTourists = null;
+ districtInfoPanelTourists = null;
+ labelIncome = null;
+ labelPopulation = null;
+ buildingsTabContainer = null;
+ }
+
+ /// Refreshes the statistics units for current game speed.
+ public void RefreshUnits()
+ {
+ if (mainLocale == null)
+ {
+ return;
+ }
+
+ var unit = TimeSpan.FromHours(VanillaFramesPerWeek * timeInfo.HoursPerFrame);
+
+ double minutes = Math.Round(unit.TotalMinutes);
+ if (minutes >= 30d)
+ {
+ minutes = Math.Round(minutes / 10d) * 10d;
+ }
+ else if (minutes >= 10d)
+ {
+ minutes = Math.Round(minutes / 5d) * 5d;
+ }
+
+ string displayUnit = $"{minutes:F0} {localizationProvider.Translate(TranslationKeys.Minutes)}";
+ if (RefreshUnits(displayUnit))
+ {
+ RefreshUI();
+ }
+ }
+
+ private static void RefreshEconomyPanel()
+ {
+ IEnumerable components = ToolsModifierControl.economyPanel?
+ .GetComponentsInChildren()?
+ .Where(c => !string.IsNullOrEmpty(c.tooltipLocaleID));
+
+ if (components == null)
+ {
+ return;
+ }
+
+ foreach (UIComponent component in components.Where(c => c is UISprite || c is UITextComponent))
+ {
+ component.tooltip = Locale.Get(component.tooltipLocaleID);
+ }
+ }
+
+ private bool RefreshUnits(string displayUnit)
+ {
+ customLocale.Reset();
+
+ IDictionary overridden = localizationProvider.GetOverriddenTranslations(OverrddenTranslationType);
+ if (overridden == null || overridden.Count == 0)
+ {
+ return false;
+ }
+
+ foreach (KeyValuePair value in overridden)
+ {
+ string translated = value.Value.Replace(UnitPlaceholder, displayUnit);
+ customLocale.AddLocalizedString(new Locale.Key { m_Identifier = value.Key }, translated);
+ }
+
+ mainLocale.Merge(null, customLocale);
+ return true;
+ }
+
+ private void RefreshUI()
+ {
+ if (labelIncome != null)
+ {
+ labelIncome.tooltip = Locale.Get(labelIncome.tooltipLocaleID);
+ }
+
+ if (labelPopulation != null)
+ {
+ labelPopulation.tooltip = Locale.Get(labelPopulation.tooltipLocaleID);
+ }
+
+ if (cityInfoPanelTourists != null)
+ {
+ cityInfoPanelTourists.text = Locale.Get(cityInfoPanelTourists.localeID);
+ }
+
+ if (districtInfoPanelTourists != null)
+ {
+ districtInfoPanelTourists.text = Locale.Get(districtInfoPanelTourists.localeID);
+ }
+
+ RefreshEconomyPanel();
+ RefreshBuildingsButtons();
+ }
+
+ private void RefreshBuildingsButtons()
+ {
+ if (buildingsTabContainer == null)
+ {
+ return;
+ }
+
+ // This creates objects on heap, but it won't cause memory pressure because it's not called
+ // in the simulation loop
+ var items = buildingsTabContainer.GetComponentsInChildren()?
+ .Select(b => new { Info = b.objectUserData as PrefabInfo, Button = b })
+ .Where(i => i.Info is BuildingInfo || i.Info is NetInfo);
+
+ if (items == null)
+ {
+ return;
+ }
+
+ foreach (var item in items)
+ {
+ item.Button.tooltip = item.Info.GetLocalizedTooltip();
+ }
+ }
+
+ private void LocaleChanged()
+ {
+ RefreshUnits();
+ }
+ }
+}
diff --git a/src/RealTime/UI/TitleBar.cs b/src/RealTime/UI/TitleBar.cs
index cbf9988c57a317923c77d4b9ab04cc591eab6bd1..e54dc88643db603ccf5fa0bf1f2b4b887478d688 100644
--- a/src/RealTime/UI/TitleBar.cs
+++ b/src/RealTime/UI/TitleBar.cs
@@ -24,11 +24,7 @@ namespace RealTime.UI
/// Gets or sets the title bar caption.
public string Caption
{
- get
- {
- return captionLabel?.text ?? caption ?? string.Empty;
- }
-
+ get => captionLabel?.text ?? caption ?? string.Empty;
set
{
caption = value;