diff --git a/ChangeLog.md b/ChangeLog.md
index 3951793..c76114c 100644
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -1 +1 @@
-- Initial release.
+- Fix occasional initialization error.
diff --git a/IBLIV.csproj b/IBLIV.csproj
index 817d9ce..b1325bd 100644
--- a/IBLIV.csproj
+++ b/IBLIV.csproj
@@ -8,7 +8,7 @@
Properties\PublishConfiguration.xml
Improved Building Level Info View
- 1.0.0
+ 1.0.1
Improvements to the Building Level info view.
Copyright © 2024
$(USERPROFILE)\Documents\Visual Studio Projects\Cities Skylines 2 Mods\My Mods\pdx_account.txt
diff --git a/Library/ilpp.pid b/Library/ilpp.pid
index b8ecd26..4f8cefa 100644
--- a/Library/ilpp.pid
+++ b/Library/ilpp.pid
@@ -1 +1 @@
-23104
\ No newline at end of file
+2104
\ No newline at end of file
diff --git a/Localization/Translation.cs b/Localization/Translation.cs
index e202f31..62c862c 100644
--- a/Localization/Translation.cs
+++ b/Localization/Translation.cs
@@ -1,4 +1,6 @@
using Colossal;
+using Colossal.Localization;
+using Game.SceneFlow;
using System;
using System.Collections.Generic;
using System.IO;
@@ -138,16 +140,19 @@ public static void Initialize()
return;
}
- // Read the lines from the translation CSV file.
- string[] lines;
+ // Read the text from the translation CSV file.
+ string fileText;
using (Stream fileStream = Assembly.GetExecutingAssembly().GetManifestResourceStream(translationFile))
{
using (StreamReader fileReader = new StreamReader(fileStream, Encoding.UTF8))
{
- lines = fileReader.ReadToEnd().Split(new string[] { "\n", "\r\n" }, StringSplitOptions.None);
+ fileText = fileReader.ReadToEnd();
}
}
+ // Split the text into lines.
+ string[] lines = fileText.Split(new string[] { "\n", "\r\n" }, StringSplitOptions.None);
+
// First line cannot be blank or a comment.
string firstLine = lines[0];
if (firstLine.Trim().Length == 0 || firstLine.StartsWith("#"))
@@ -172,11 +177,26 @@ public static void Initialize()
{
translationKeyCount.Add(translationKey, 0);
}
+
+ // Check if file text has any $$ references to game translations.
+ Dictionary gameTranslations = new Dictionary();
+ LocalizationManager localizationManager = GameManager.instance.localizationManager;
+ if (fileText.Contains("$$"))
+ {
+ // Get game translations once here instead of every time a $$ reference is encountered.
+ string currentLocaleID = localizationManager.activeLocaleId;
+ foreach (string gameLocaleID in localizationManager.GetSupportedLocales())
+ {
+ localizationManager.SetActiveLocale(gameLocaleID);
+ gameTranslations[gameLocaleID] = localizationManager.activeDictionary;
+ }
+ localizationManager.SetActiveLocale(currentLocaleID);
+ }
// Process each subsequent line.
for (int i = 1; i < lines.Length; i++)
{
- ProcessTranslationLine(lines[i], languages, temporaryTranslations, translationKeys, translationKeyCount);
+ ProcessTranslationLine(lines[i], languages, temporaryTranslations, gameTranslations, translationKeys, translationKeyCount);
}
// Each translation key must be defined.
@@ -192,7 +212,7 @@ public static void Initialize()
// All the translations were read into the languages variable just for this right here.
foreach (string languageCode in languages.Keys)
{
- Game.SceneFlow.GameManager.instance.localizationManager.AddSource(languageCode, new LocalizationSource(languages[languageCode]));
+ localizationManager.AddSource(languageCode, new LocalizationSource(languages[languageCode]));
}
}
catch(Exception ex)
@@ -267,6 +287,7 @@ private static void ProcessTranslationLine
string line,
Languages languages,
Languages temporaryTranslations,
+ Dictionary gameTranslations,
string[] translationKeys,
Dictionary translationKeyCount
)
@@ -319,10 +340,6 @@ Dictionary translationKeyCount
}
}
- // Save active locale ID.
- Colossal.Localization.LocalizationManager localizationManager = Game.SceneFlow.GameManager.instance.localizationManager;
- string savedLocaleID = localizationManager.activeLocaleId;
-
// Do each language code.
foreach (string languageCode in languages.Keys)
{
@@ -364,15 +381,22 @@ Dictionary translationKeyCount
string gameTranslationKey = translatedText.Substring(2);
// Get the game's translation for the key.
- localizationManager.SetActiveLocale(languageCode);
- if (localizationManager.activeDictionary.TryGetValue(gameTranslationKey, out string gameTranslatedText))
+ if (gameTranslations.ContainsKey(languageCode))
{
- // Use the game translated text.
- translatedText = gameTranslatedText;
+ if (gameTranslations[languageCode].TryGetValue(gameTranslationKey, out string gameTranslatedText))
+ {
+ // Use the game translated text.
+ translatedText = gameTranslatedText;
+ }
+ else
+ {
+ LogUtil.Warn($"Game translation key [{gameTranslationKey}] does not exist for language [{languageCode}].");
+ // Leave the invalid $$ reference in the translated text.
+ }
}
else
{
- LogUtil.Warn($"Game translation key [{gameTranslationKey}] does not exist for language [{languageCode}].");
+ LogUtil.Warn($"Game does not contain translations for language [{languageCode}] for translation key [{gameTranslationKey}].");
// Leave the invalid $$ reference in the translated text.
}
}
@@ -434,12 +458,6 @@ Dictionary translationKeyCount
}
}
}
-
- // Restore saved locale ID.
- if (localizationManager.activeLocaleId != savedLocaleID)
- {
- localizationManager.SetActiveLocale(savedLocaleID);
- }
}
}
diff --git a/Localization/Translation.csv b/Localization/Translation.csv
index b363b25..e113a91 100644
--- a/Localization/Translation.csv
+++ b/Localization/Translation.csv
@@ -1,9 +1,4 @@
,"en-US","de-DE","es-ES","fr-FR","it-IT","ja-JP","ko-KR","pl-PL","pt-BR","ru-RU","zh-HANS","zh-HANT"
-"# Mod title and description.",,,,,,,,,,,,
-"Title","Improved Building Level Info View","Verbesserte Gebäudeebenen-Infoansicht","Vista de información de nivel de edificio mejorada","Amélioration de la vue d'informations sur le niveau du bâtiment","Visualizzazione informazioni livello edificio migliorata","建物レベルの情報ビューの改善","개선된 건물 레벨 정보 보기","Ulepszony widok informacji na poziomie budynku","Visualização de informações do nível do edifício aprimorada","Улучшенный вид информации об уровне здания","改进了建筑等级信息视图","改進的建築層資訊視圖"
-"Description","Improvements to the Building Level info view.","Verbesserungen der Gebäudeebenen-Infoansicht.","Mejoras en la vista de información de nivel de edificio.","Améliorations apportées à la vue d'informations sur le niveau du bâtiment.","Miglioramenti alla visualizzazione informazioni livello edificio.","建物レベルの情報ビューが改善されました。","건물 레벨 정보 보기 개선.","Ulepszenia widoku informacji na poziomie budynku.","Melhorias na visualização de informações do nível do edifício.","Улучшения в виде информации об уровне здания.","改进了建筑等级信息视图。","建築物級別資訊視圖的改進。"
-,,,,,,,,,,,,
-,,,,,,,,,,,,
"# Infomode titles.",,,,,,,,,,,,
"@@BuildingLevel","$$Infoviews.INFOMODE[Level]","$$Infoviews.INFOMODE[Level]","$$Infoviews.INFOMODE[Level]","$$Infoviews.INFOMODE[Level]","$$Infoviews.INFOMODE[Level]","$$Infoviews.INFOMODE[Level]","$$Infoviews.INFOMODE[Level]","$$Infoviews.INFOMODE[Level]","$$Infoviews.INFOMODE[Level]","$$Infoviews.INFOMODE[Level]","$$Infoviews.INFOMODE[Level]","$$Infoviews.INFOMODE[Level]"
"@@Residential","$$LevelInfoPanel.RESIDENTIAL","$$LevelInfoPanel.RESIDENTIAL","$$LevelInfoPanel.RESIDENTIAL","$$LevelInfoPanel.RESIDENTIAL","$$LevelInfoPanel.RESIDENTIAL","$$LevelInfoPanel.RESIDENTIAL","$$LevelInfoPanel.RESIDENTIAL","$$LevelInfoPanel.RESIDENTIAL","$$LevelInfoPanel.RESIDENTIAL","$$LevelInfoPanel.RESIDENTIAL","$$LevelInfoPanel.RESIDENTIAL","$$LevelInfoPanel.RESIDENTIAL"
diff --git a/Localization/UITranslationKey.cs b/Localization/UITranslationKey.cs
index b6cd6f8..eadaf5a 100644
--- a/Localization/UITranslationKey.cs
+++ b/Localization/UITranslationKey.cs
@@ -3,20 +3,16 @@ namespace IBLIV
// Define UI translation keys.
public class UITranslationKey
{
- // Mod title and description.
- public const string Title = "IBLIV.Title";
- public const string Description = "IBLIV.Description";
-
// Infomode titles.
- public const string InfomodeTitleLevelResidential = "Infoviews.INFOMODE[IBLIVLevelResidential]";
- public const string InfomodeTitleLevelCommercial = "Infoviews.INFOMODE[IBLIVLevelCommercial]";
- public const string InfomodeTitleLevelIndustrial = "Infoviews.INFOMODE[IBLIVLevelIndustrial]";
- public const string InfomodeTitleLevelOffice = "Infoviews.INFOMODE[IBLIVLevelOffice]";
+ public const string InfomodeTitleLevelResidential = "Infoviews.INFOMODE[" + ModAssemblyInfo.Name + "LevelResidential]";
+ public const string InfomodeTitleLevelCommercial = "Infoviews.INFOMODE[" + ModAssemblyInfo.Name + "LevelCommercial]";
+ public const string InfomodeTitleLevelIndustrial = "Infoviews.INFOMODE[" + ModAssemblyInfo.Name + "LevelIndustrial]";
+ public const string InfomodeTitleLevelOffice = "Infoviews.INFOMODE[" + ModAssemblyInfo.Name + "LevelOffice]";
// Infomode tooltips.
- public const string InfomodeTooltipLevelResidential = "Infoviews.INFOMODE_TOOLTIP[IBLIVLevelResidential]";
- public const string InfomodeTooltipLevelCommercial = "Infoviews.INFOMODE_TOOLTIP[IBLIVLevelCommercial]";
- public const string InfomodeTooltipLevelIndustrial = "Infoviews.INFOMODE_TOOLTIP[IBLIVLevelIndustrial]";
- public const string InfomodeTooltipLevelOffice = "Infoviews.INFOMODE_TOOLTIP[IBLIVLevelOffice]";
+ public const string InfomodeTooltipLevelResidential = "Infoviews.INFOMODE_TOOLTIP[" + ModAssemblyInfo.Name + "LevelResidential]";
+ public const string InfomodeTooltipLevelCommercial = "Infoviews.INFOMODE_TOOLTIP[" + ModAssemblyInfo.Name + "LevelCommercial]";
+ public const string InfomodeTooltipLevelIndustrial = "Infoviews.INFOMODE_TOOLTIP[" + ModAssemblyInfo.Name + "LevelIndustrial]";
+ public const string InfomodeTooltipLevelOffice = "Infoviews.INFOMODE_TOOLTIP[" + ModAssemblyInfo.Name + "LevelOffice]";
}
}
diff --git a/Mod.cs b/Mod.cs
index 18e4048..cf70c1a 100644
--- a/Mod.cs
+++ b/Mod.cs
@@ -91,6 +91,8 @@ public void OnLoad(UpdateSystem updateSystem)
{
LogUtil.Exception(ex);
}
+
+ LogUtil.Info($"{nameof(Mod)}.{nameof(OnLoad)} complete.");
}
///
diff --git a/ModAssemblyInfo/ModAssemblyInfo.cs b/ModAssemblyInfo/ModAssemblyInfo.cs
index df9fc84..c7fb4c5 100644
--- a/ModAssemblyInfo/ModAssemblyInfo.cs
+++ b/ModAssemblyInfo/ModAssemblyInfo.cs
@@ -10,7 +10,7 @@ namespace IBLIV
public class ModAssemblyInfo
{
public const string Name = "IBLIV";
- public const string Version = "1.0.0";
+ public const string Version = "1.0.1";
public const string Title = "Improved Building Level Info View";
public const string Description = "Improvements to the Building Level info view.";
}
diff --git a/Properties/PublishConfiguration.xml b/Properties/PublishConfiguration.xml
index 3e58b21..5518981 100644
--- a/Properties/PublishConfiguration.xml
+++ b/Properties/PublishConfiguration.xml
@@ -53,7 +53,7 @@ The following mods were used extensively in the development of this mod:
-
+
@@ -63,7 +63,7 @@ The following mods were used extensively in the development of this mod:
-- Initial release.
+- Fix occasional initialization error.
diff --git a/Systems/BuildingLevelInfoviewSystem.cs b/Systems/BuildingLevelInfoviewSystem.cs
index 4b9bf26..01c0339 100644
--- a/Systems/BuildingLevelInfoviewSystem.cs
+++ b/Systems/BuildingLevelInfoviewSystem.cs
@@ -1,4 +1,5 @@
using Colossal.Mathematics;
+using Colossal.Serialization.Entities;
using Game;
using Game.Prefabs;
using System;
@@ -14,19 +15,42 @@ namespace IBLIV
///
public partial class BuildingLevelInfoviewSystem : GameSystemBase
{
+ // Initialization flag.
+ private bool _initialized = false;
+
///
- /// Initialize this system.
- /// All the work of updating the building level infoview is performed in OnCreate().
+ /// Called when a game is about to be loaded.
///
- protected override void OnCreate()
+ protected override void OnGamePreload(Purpose purpose, GameMode mode)
{
- base.OnCreate();
- LogUtil.Info($"{nameof(BuildingLevelInfoviewSystem)}.{nameof(OnCreate)}");
+ base.OnGamePreload(purpose, mode);
+
+ // Initialization is performed in OnGamePreload instead of OnCreate because
+ // occasionally the signature infomode prefabs were not available when this system was created.
+ // It is not known why this happened only occasionally.
+
+ // Initialization is performed in OnGamePreload instead of OnGameLoadingComplete because
+ // the custom infoview icon does not get displayed if performed in OnGameLoadingComplete.
+
+ // Skip if already initialized.
+ if (_initialized)
+ {
+ return;
+ }
+
+ // Skip if not loading a game (i.e. is editor or main menu).
+ if (mode != GameMode.Game)
+ {
+ return;
+ }
+
+ LogUtil.Info($"{nameof(BuildingLevelInfoviewSystem)}.{nameof(OnGamePreload)} initialize");
try
{
// The game's infoviews must be created first.
- // That will be the normal case, but perform the check anyway just in case.
+ // That will be the normal case by the time a game is about to be loaded.
+ // But perform the check anyway just in case.
InfoviewInitializeSystem infoviewInitializeSystem = World.DefaultGameObjectInjectionWorld.GetOrCreateSystemManaged();
if (infoviewInitializeSystem == null || infoviewInitializeSystem.infoviews.Count() == 0)
{
@@ -50,21 +74,9 @@ protected override void OnCreate()
return;
}
- // Create a new building status infomode prefab for the building level of each zone type.
- BuildingStatusInfomodePrefab infomodePrefabLevelResidential = CreateInfomodePrefab(ImprovedBuildingStatusType.LevelResidential);
- BuildingStatusInfomodePrefab infomodePrefabLevelCommercial = CreateInfomodePrefab(ImprovedBuildingStatusType.LevelCommercial);
- BuildingStatusInfomodePrefab infomodePrefabLevelIndustrial = CreateInfomodePrefab(ImprovedBuildingStatusType.LevelIndustrial);
- BuildingStatusInfomodePrefab infomodePrefabLevelOffice = CreateInfomodePrefab(ImprovedBuildingStatusType.LevelOffice);
-
- // Add the building status infomode prefabs to the prefab system.
- PrefabSystem prefabSystem = World.DefaultGameObjectInjectionWorld.GetOrCreateSystemManaged();
- prefabSystem.AddPrefab(infomodePrefabLevelResidential);
- prefabSystem.AddPrefab(infomodePrefabLevelCommercial);
- prefabSystem.AddPrefab(infomodePrefabLevelIndustrial);
- prefabSystem.AddPrefab(infomodePrefabLevelOffice);
-
// For signature buildings, the game's existing infomode prefabs are used instead of creating new infomode prefabs.
// Get the game's signature infomode prefab for each zone type.
+ PrefabSystem prefabSystem = World.DefaultGameObjectInjectionWorld.GetOrCreateSystemManaged();
BuildingStatusInfomodePrefab infomodePrefabSignatureResidential = null;
BuildingStatusInfomodePrefab infomodePrefabSignatureCommercial = null;
BuildingStatusInfomodePrefab infomodePrefabSignatureIndustrial = null;
@@ -109,6 +121,18 @@ protected override void OnCreate()
if (infomodePrefabSignatureIndustrial == null) { LogUtil.Error("Unable to find infomode prefab for Signature Industrial." ); return; }
if (infomodePrefabSignatureOffice == null) { LogUtil.Error("Unable to find infomode prefab for Signature Office." ); return; }
+ // Create a new building status infomode prefab for the building level of each zone type.
+ BuildingStatusInfomodePrefab infomodePrefabLevelResidential = CreateInfomodePrefab(ImprovedBuildingStatusType.LevelResidential);
+ BuildingStatusInfomodePrefab infomodePrefabLevelCommercial = CreateInfomodePrefab(ImprovedBuildingStatusType.LevelCommercial);
+ BuildingStatusInfomodePrefab infomodePrefabLevelIndustrial = CreateInfomodePrefab(ImprovedBuildingStatusType.LevelIndustrial);
+ BuildingStatusInfomodePrefab infomodePrefabLevelOffice = CreateInfomodePrefab(ImprovedBuildingStatusType.LevelOffice);
+
+ // Add the building status infomode prefabs to the prefab system.
+ prefabSystem.AddPrefab(infomodePrefabLevelResidential);
+ prefabSystem.AddPrefab(infomodePrefabLevelCommercial);
+ prefabSystem.AddPrefab(infomodePrefabLevelIndustrial);
+ prefabSystem.AddPrefab(infomodePrefabLevelOffice);
+
// Remove the existing infomodes from the building level infoview.
DynamicBuffer infomodesBuildingLevel = prefabSystem.GetBuffer(buildingLevelInfoviewPrafab, isReadOnly: false);
infomodesBuildingLevel.Clear();
@@ -126,6 +150,9 @@ protected override void OnCreate()
// Set a new custom icon on building level infoview.
buildingLevelInfoviewPrafab.m_IconPath = $"coui://{Mod.ImagesURI}/ImprovedBuildingLevel.svg";
+
+ // Initialized.
+ _initialized = true;
}
catch (Exception ex)
{