From 01cc27532ad70a8f6be52a16e2609efe1a14ea7d Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sat, 27 Dec 2025 13:53:20 +0000
Subject: [PATCH 1/8] Initial plan
From 3e59d1896207712ac3e6068a97166d0418afac58 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sat, 27 Dec 2025 13:59:09 +0000
Subject: [PATCH 2/8] Add Economy class with inflation calculation logic and
tests
Co-authored-by: LeftofZen <7483209+LeftofZen@users.noreply.github.com>
---
Common/Economy.cs | 76 ++++++++++++++++++++++++++++++++++
Tests/EconomyTests.cs | 96 +++++++++++++++++++++++++++++++++++++++++++
2 files changed, 172 insertions(+)
create mode 100644 Common/Economy.cs
create mode 100644 Tests/EconomyTests.cs
diff --git a/Common/Economy.cs b/Common/Economy.cs
new file mode 100644
index 00000000..6128ddad
--- /dev/null
+++ b/Common/Economy.cs
@@ -0,0 +1,76 @@
+namespace Common;
+
+///
+/// Provides economy-related calculations for Locomotion, including inflation and cost calculations.
+/// Based on OpenLoco's Economy.cpp: https://github.com/OpenLoco/OpenLoco/blob/master/src/OpenLoco/src/Economy/Economy.cpp
+///
+public static class Economy
+{
+ // Inflation factors from OpenLoco (kInflationFactors)
+ private static readonly uint[] InflationFactors =
+ [
+ 20, 20, 20, 20, 23, 20, 23, 23,
+ 20, 17, 17, 20, 20, 20, 20, 20,
+ 20, 20, 20, 20, 20, 20, 20, 20,
+ 20, 20, 20, 20, 20, 20, 20, 20
+ ];
+
+ ///
+ /// Calculates the currency multiplication factors for all 32 cost indices for a given year.
+ ///
+ /// The year to calculate inflation for (clamped to 1900-2030 range)
+ /// An array of 32 currency multiplication factors
+ public static uint[] CalculateCurrencyMultiplicationFactors(int year)
+ {
+ var factors = new uint[32];
+
+ // Initialize all factors to 1024 (base value)
+ for (var i = 0; i < 32; i++)
+ {
+ factors[i] = 1024;
+ }
+
+ // OpenLoco allows 1800 as the minimum year, whereas Locomotion uses 1900.
+ // Treat years before 1900 as though they were 1900 to not change vanilla scenarios.
+ var baseYear = Math.Clamp(year, 1900, 2030) - 1900;
+ var monthCount = baseYear * 12;
+
+ // Apply inflation for each month
+ for (var month = 0; month < monthCount; month++)
+ {
+ for (var i = 0; i < 32; i++)
+ {
+ factors[i] += (uint)((ulong)InflationFactors[i] * factors[i] >> 12);
+ }
+ }
+
+ return factors;
+ }
+
+ ///
+ /// Calculates the inflation-adjusted cost for a given cost factor and cost index.
+ ///
+ /// The base cost factor from the object
+ /// The cost index (0-31)
+ /// The year to calculate the cost for
+ /// The divisor to apply (default is 10, which is common for most objects)
+ /// The inflation-adjusted cost
+ public static int GetInflationAdjustedCost(short costFactor, byte costIndex, int year, byte divisor = 10)
+ {
+ if (costIndex >= 32)
+ {
+ throw new ArgumentOutOfRangeException(nameof(costIndex), "Cost index must be between 0 and 31");
+ }
+
+ if (divisor >= 63)
+ {
+ throw new ArgumentOutOfRangeException(nameof(divisor), "Divisor must be less than 63");
+ }
+
+ var factors = CalculateCurrencyMultiplicationFactors(year);
+ var val = costFactor * (long)factors[costIndex];
+ var result = val / (1L << divisor);
+
+ return (int)result;
+ }
+}
diff --git a/Tests/EconomyTests.cs b/Tests/EconomyTests.cs
new file mode 100644
index 00000000..cb96268c
--- /dev/null
+++ b/Tests/EconomyTests.cs
@@ -0,0 +1,96 @@
+using NUnit.Framework;
+
+namespace Common.Tests;
+
+[TestFixture]
+public class EconomyTests
+{
+ [Test]
+ public void CalculateCurrencyMultiplicationFactors_Year1900_ReturnsBaseFactors()
+ {
+ // Year 1900 should return base factors (1024 for all indices)
+ var factors = Economy.CalculateCurrencyMultiplicationFactors(1900);
+
+ Assert.That(factors, Has.Length.EqualTo(32));
+ foreach (var factor in factors)
+ {
+ Assert.That(factor, Is.EqualTo(1024));
+ }
+ }
+
+ [Test]
+ public void CalculateCurrencyMultiplicationFactors_YearBefore1900_ReturnsSameAs1900()
+ {
+ // Years before 1900 should be treated as 1900
+ var factors1800 = Economy.CalculateCurrencyMultiplicationFactors(1800);
+ var factors1900 = Economy.CalculateCurrencyMultiplicationFactors(1900);
+
+ Assert.That(factors1800, Is.EqualTo(factors1900));
+ }
+
+ [Test]
+ public void CalculateCurrencyMultiplicationFactors_Year1901_HasInflation()
+ {
+ // Year 1901 should have inflation applied (12 months)
+ var factors = Economy.CalculateCurrencyMultiplicationFactors(1901);
+
+ Assert.That(factors, Has.Length.EqualTo(32));
+ // All factors should be greater than base value due to inflation
+ foreach (var factor in factors)
+ {
+ Assert.That(factor, Is.GreaterThan(1024));
+ }
+ }
+
+ [Test]
+ public void CalculateCurrencyMultiplicationFactors_Year2030_ClampsCorrectly()
+ {
+ // Year 2030 is the max, so 2031 should be treated as 2030
+ var factors2030 = Economy.CalculateCurrencyMultiplicationFactors(2030);
+ var factors2031 = Economy.CalculateCurrencyMultiplicationFactors(2031);
+
+ Assert.That(factors2031, Is.EqualTo(factors2030));
+ }
+
+ [Test]
+ public void GetInflationAdjustedCost_Year1900_ReturnsBaseCost()
+ {
+ // For year 1900, with factor 1024 and divisor 10, we should get:
+ // cost = (100 * 1024) / (1 << 10) = 102400 / 1024 = 100
+ var cost = Economy.GetInflationAdjustedCost(100, 0, 1900, 10);
+ Assert.That(cost, Is.EqualTo(100));
+ }
+
+ [Test]
+ public void GetInflationAdjustedCost_Year1901_HasHigherCost()
+ {
+ // For year 1901, cost should be higher than year 1900 due to inflation
+ var cost1900 = Economy.GetInflationAdjustedCost(100, 0, 1900, 10);
+ var cost1901 = Economy.GetInflationAdjustedCost(100, 0, 1901, 10);
+
+ Assert.That(cost1901, Is.GreaterThan(cost1900));
+ }
+
+ [Test]
+ public void GetInflationAdjustedCost_NegativeFactor_ReturnsNegativeCost()
+ {
+ // Sell costs use negative factors
+ var cost = Economy.GetInflationAdjustedCost(-50, 0, 1900, 10);
+ Assert.That(cost, Is.LessThan(0));
+ Assert.That(cost, Is.EqualTo(-50));
+ }
+
+ [Test]
+ public void GetInflationAdjustedCost_InvalidCostIndex_ThrowsException()
+ {
+ Assert.Throws(() =>
+ Economy.GetInflationAdjustedCost(100, 32, 1900, 10));
+ }
+
+ [Test]
+ public void GetInflationAdjustedCost_InvalidDivisor_ThrowsException()
+ {
+ Assert.Throws(() =>
+ Economy.GetInflationAdjustedCost(100, 0, 1900, 63));
+ }
+}
From b603f70d120bc571b3bea02153cec80be4ae5407 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sat, 27 Dec 2025 14:01:59 +0000
Subject: [PATCH 3/8] Add inflation year setting and effective cost display for
Track and Bridge objects
Co-authored-by: LeftofZen <7483209+LeftofZen@users.noreply.github.com>
---
Gui/EditorSettings.cs | 1 +
Gui/GlobalSettings.cs | 10 +++++++
Gui/Models/ObjectEditorModel.cs | 1 +
.../EditorSettingsWindowViewModel.cs | 7 +++++
.../LocoTypes/Objects/BridgeViewModel.cs | 30 +++++++++++++++++++
.../LocoTypes/Objects/TrackViewModel.cs | 30 +++++++++++++++++++
6 files changed, 79 insertions(+)
create mode 100644 Gui/GlobalSettings.cs
diff --git a/Gui/EditorSettings.cs b/Gui/EditorSettings.cs
index f93bf1d3..83b4fa04 100644
--- a/Gui/EditorSettings.cs
+++ b/Gui/EditorSettings.cs
@@ -32,6 +32,7 @@ public HashSet ObjDataDirectories
public bool EnableOGValidation { get; set; }
public bool ShowLogsOnError { get; set; }
public bool AutoObjectDiscoveryAndUpload { get; set; }
+ public int InflationYear { get; set; } = 1950;
public bool UseHttps { get; set; }
public string ServerAddressHttp { get; set; } = "http://openloco.leftofzen.dev/";
diff --git a/Gui/GlobalSettings.cs b/Gui/GlobalSettings.cs
new file mode 100644
index 00000000..de0bec33
--- /dev/null
+++ b/Gui/GlobalSettings.cs
@@ -0,0 +1,10 @@
+namespace Gui;
+
+///
+/// Provides global access to editor settings for use in ViewModels.
+/// This is primarily used for read-only access to settings like the inflation year.
+///
+public static class GlobalSettings
+{
+ public static EditorSettings? CurrentSettings { get; set; }
+}
diff --git a/Gui/Models/ObjectEditorModel.cs b/Gui/Models/ObjectEditorModel.cs
index 34890c03..1425d7dd 100644
--- a/Gui/Models/ObjectEditorModel.cs
+++ b/Gui/Models/ObjectEditorModel.cs
@@ -124,6 +124,7 @@ async Task WriteLogsToFileAsync()
void LoadSettings()
{
Settings = EditorSettings.Load(SettingsFile, Logger);
+ GlobalSettings.CurrentSettings = Settings;
if (Settings.Validate(Logger))
{
diff --git a/Gui/ViewModels/EditorSettingsWindowViewModel.cs b/Gui/ViewModels/EditorSettingsWindowViewModel.cs
index ea195682..8baa8274 100644
--- a/Gui/ViewModels/EditorSettingsWindowViewModel.cs
+++ b/Gui/ViewModels/EditorSettingsWindowViewModel.cs
@@ -43,6 +43,13 @@ public bool ShowLogsOnError
set => Model.ShowLogsOnError = value;
}
+ [Category("Misc"), DisplayName("Inflation Year"), Description("The in-game year to use when calculating inflation-adjusted costs for objects. Defaults to 1950.")]
+ public int InflationYear
+ {
+ get => Model.InflationYear;
+ set => Model.InflationYear = value;
+ }
+
#region Object Folders
const string GameObjectFolderCategory = "Folders OpenLoco can use objects from";
diff --git a/Gui/ViewModels/LocoTypes/Objects/BridgeViewModel.cs b/Gui/ViewModels/LocoTypes/Objects/BridgeViewModel.cs
index 5a860eab..00dbaaab 100644
--- a/Gui/ViewModels/LocoTypes/Objects/BridgeViewModel.cs
+++ b/Gui/ViewModels/LocoTypes/Objects/BridgeViewModel.cs
@@ -92,6 +92,36 @@ public int16_t SellCostFactor
set => Model.SellCostFactor = value;
}
+ [Category("Cost"), ReadOnly(true), DisplayName("Effective Base Cost"), Description("The inflation-adjusted base cost for the year specified in settings")]
+ public int EffectiveBaseCost
+ {
+ get
+ {
+ var year = GlobalSettings.CurrentSettings?.InflationYear ?? 1950;
+ return Common.Economy.GetInflationAdjustedCost(Model.BaseCostFactor, Model.CostIndex, year);
+ }
+ }
+
+ [Category("Cost"), ReadOnly(true), DisplayName("Effective Height Cost"), Description("The inflation-adjusted height cost factor for the year specified in settings")]
+ public int EffectiveHeightCost
+ {
+ get
+ {
+ var year = GlobalSettings.CurrentSettings?.InflationYear ?? 1950;
+ return Common.Economy.GetInflationAdjustedCost(Model.HeightCostFactor, Model.CostIndex, year);
+ }
+ }
+
+ [Category("Cost"), ReadOnly(true), DisplayName("Effective Sell Cost"), Description("The inflation-adjusted sell cost for the year specified in settings")]
+ public int EffectiveSellCost
+ {
+ get
+ {
+ var year = GlobalSettings.CurrentSettings?.InflationYear ?? 1950;
+ return Common.Economy.GetInflationAdjustedCost(Model.SellCostFactor, Model.CostIndex, year);
+ }
+ }
+
[Category("Compatible")]
public BindingList CompatibleTrackObjects { get; init; } = new(model.CompatibleTrackObjects);
diff --git a/Gui/ViewModels/LocoTypes/Objects/TrackViewModel.cs b/Gui/ViewModels/LocoTypes/Objects/TrackViewModel.cs
index caef060f..b01b200f 100644
--- a/Gui/ViewModels/LocoTypes/Objects/TrackViewModel.cs
+++ b/Gui/ViewModels/LocoTypes/Objects/TrackViewModel.cs
@@ -71,6 +71,36 @@ public uint8_t CostIndex
set => Model.CostIndex = value;
}
+ [Category("Cost"), ReadOnly(true), DisplayName("Effective Build Cost"), Description("The inflation-adjusted build cost for the year specified in settings")]
+ public int EffectiveBuildCost
+ {
+ get
+ {
+ var year = GlobalSettings.CurrentSettings?.InflationYear ?? 1950;
+ return Common.Economy.GetInflationAdjustedCost(Model.BuildCostFactor, Model.CostIndex, year);
+ }
+ }
+
+ [Category("Cost"), ReadOnly(true), DisplayName("Effective Sell Cost"), Description("The inflation-adjusted sell cost for the year specified in settings")]
+ public int EffectiveSellCost
+ {
+ get
+ {
+ var year = GlobalSettings.CurrentSettings?.InflationYear ?? 1950;
+ return Common.Economy.GetInflationAdjustedCost(Model.SellCostFactor, Model.CostIndex, year);
+ }
+ }
+
+ [Category("Cost"), ReadOnly(true), DisplayName("Effective Tunnel Cost"), Description("The inflation-adjusted tunnel cost for the year specified in settings")]
+ public int EffectiveTunnelCost
+ {
+ get
+ {
+ var year = GlobalSettings.CurrentSettings?.InflationYear ?? 1950;
+ return Common.Economy.GetInflationAdjustedCost(Model.TunnelCostFactor, Model.CostIndex, year);
+ }
+ }
+
[Category("")]
public uint8_t var_06
{
From bff4e516a874f5e7e35837a4bdb38c0327e816d9 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sat, 27 Dec 2025 14:06:23 +0000
Subject: [PATCH 4/8] Add effective cost properties to all remaining object
ViewModels
Co-authored-by: LeftofZen <7483209+LeftofZen@users.noreply.github.com>
---
.../LocoTypes/Objects/AirportViewModel.cs | 20 +++++++++++++
.../LocoTypes/Objects/DockViewModel.cs | 20 +++++++++++++
.../LocoTypes/Objects/IndustryViewModel.cs | 20 +++++++++++++
.../LocoTypes/Objects/RoadExtraViewModel.cs | 24 +++++++++++++++
.../LocoTypes/Objects/RoadStationViewModel.cs | 20 +++++++++++++
.../LocoTypes/Objects/RoadViewModel.cs | 30 +++++++++++++++++++
.../LocoTypes/Objects/TrackExtraViewModel.cs | 24 +++++++++++++++
.../LocoTypes/Objects/TrackSignalViewModel.cs | 20 +++++++++++++
.../Objects/TrackStationViewModel.cs | 20 +++++++++++++
.../LocoTypes/Objects/TreeViewModel.cs | 20 +++++++++++++
.../LocoTypes/Objects/VehicleViewModel.cs | 20 +++++++++++++
11 files changed, 238 insertions(+)
diff --git a/Gui/ViewModels/LocoTypes/Objects/AirportViewModel.cs b/Gui/ViewModels/LocoTypes/Objects/AirportViewModel.cs
index b8672ce6..f78eed02 100644
--- a/Gui/ViewModels/LocoTypes/Objects/AirportViewModel.cs
+++ b/Gui/ViewModels/LocoTypes/Objects/AirportViewModel.cs
@@ -82,6 +82,26 @@ public int16_t SellCostFactor
set => Model.SellCostFactor = value;
}
+ [Category("Cost"), ReadOnly(true), DisplayName("Effective Build Cost"), Description("The inflation-adjusted build cost for the year specified in settings")]
+ public int EffectiveBuildCost
+ {
+ get
+ {
+ var year = GlobalSettings.CurrentSettings?.InflationYear ?? 1950;
+ return Common.Economy.GetInflationAdjustedCost(Model.BuildCostFactor, Model.CostIndex, year);
+ }
+ }
+
+ [Category("Cost"), ReadOnly(true), DisplayName("Effective Sell Cost"), Description("The inflation-adjusted sell cost for the year specified in settings")]
+ public int EffectiveSellCost
+ {
+ get
+ {
+ var year = GlobalSettings.CurrentSettings?.InflationYear ?? 1950;
+ return Common.Economy.GetInflationAdjustedCost(Model.SellCostFactor, Model.CostIndex, year);
+ }
+ }
+
[Category("Building")]
[Length(1, AirportObjectLoader.Constants.BuildingVariationCount)]
public BindingList> BuildingVariations { get; init; } = new(model.BuildingComponents.BuildingVariations.Select(x => x.ToBindingList()).ToBindingList());
diff --git a/Gui/ViewModels/LocoTypes/Objects/DockViewModel.cs b/Gui/ViewModels/LocoTypes/Objects/DockViewModel.cs
index 9671277a..9c163b0a 100644
--- a/Gui/ViewModels/LocoTypes/Objects/DockViewModel.cs
+++ b/Gui/ViewModels/LocoTypes/Objects/DockViewModel.cs
@@ -59,6 +59,26 @@ public int16_t SellCostFactor
set => Model.SellCostFactor = value;
}
+ [Category("Cost"), ReadOnly(true), DisplayName("Effective Build Cost"), Description("The inflation-adjusted build cost for the year specified in settings")]
+ public int EffectiveBuildCost
+ {
+ get
+ {
+ var year = GlobalSettings.CurrentSettings?.InflationYear ?? 1950;
+ return Common.Economy.GetInflationAdjustedCost(Model.BuildCostFactor, Model.CostIndex, year);
+ }
+ }
+
+ [Category("Cost"), ReadOnly(true), DisplayName("Effective Sell Cost"), Description("The inflation-adjusted sell cost for the year specified in settings")]
+ public int EffectiveSellCost
+ {
+ get
+ {
+ var year = GlobalSettings.CurrentSettings?.InflationYear ?? 1950;
+ return Common.Economy.GetInflationAdjustedCost(Model.SellCostFactor, Model.CostIndex, year);
+ }
+ }
+
[Category("Building")]
[Length(1, DockObjectLoader.Constants.BuildingVariationCount)]
public BindingList> BuildingVariations { get; init; } = new(model.BuildingComponents.BuildingVariations.Select(x => x.ToBindingList()).ToBindingList());
diff --git a/Gui/ViewModels/LocoTypes/Objects/IndustryViewModel.cs b/Gui/ViewModels/LocoTypes/Objects/IndustryViewModel.cs
index fe3e2bf2..6595e863 100644
--- a/Gui/ViewModels/LocoTypes/Objects/IndustryViewModel.cs
+++ b/Gui/ViewModels/LocoTypes/Objects/IndustryViewModel.cs
@@ -93,6 +93,26 @@ public int16_t SellCostFactor
set => Model.SellCostFactor = value;
}
+ [Category("Cost"), ReadOnly(true), DisplayName("Effective Build Cost"), Description("The inflation-adjusted build cost for the year specified in settings")]
+ public int EffectiveBuildCost
+ {
+ get
+ {
+ var year = GlobalSettings.CurrentSettings?.InflationYear ?? 1950;
+ return Common.Economy.GetInflationAdjustedCost(Model.BuildCostFactor, Model.CostIndex, year);
+ }
+ }
+
+ [Category("Cost"), ReadOnly(true), DisplayName("Effective Sell Cost"), Description("The inflation-adjusted sell cost for the year specified in settings")]
+ public int EffectiveSellCost
+ {
+ get
+ {
+ var year = GlobalSettings.CurrentSettings?.InflationYear ?? 1950;
+ return Common.Economy.GetInflationAdjustedCost(Model.SellCostFactor, Model.CostIndex, year);
+ }
+ }
+
[Category("Farm")]
public uint8_t FarmTileNumImageAngles
{
diff --git a/Gui/ViewModels/LocoTypes/Objects/RoadExtraViewModel.cs b/Gui/ViewModels/LocoTypes/Objects/RoadExtraViewModel.cs
index ec90d80c..bbfa5a04 100644
--- a/Gui/ViewModels/LocoTypes/Objects/RoadExtraViewModel.cs
+++ b/Gui/ViewModels/LocoTypes/Objects/RoadExtraViewModel.cs
@@ -1,6 +1,7 @@
using Definitions.ObjectModels.Objects.Road;
using Definitions.ObjectModels.Objects.RoadExtra;
using PropertyModels.ComponentModel.DataAnnotations;
+using System.ComponentModel;
namespace Gui.ViewModels;
@@ -20,21 +21,44 @@ public uint8_t PaintStyle
set => Model.PaintStyle = value;
}
+ [Category("Cost")]
public uint8_t CostIndex
{
get => Model.CostIndex;
set => Model.CostIndex = value;
}
+ [Category("Cost")]
public int16_t BuildCostFactor
{
get => Model.BuildCostFactor;
set => Model.BuildCostFactor = value;
}
+ [Category("Cost")]
public int16_t SellCostFactor
{
get => Model.SellCostFactor;
set => Model.SellCostFactor = value;
}
+
+ [Category("Cost"), ReadOnly(true), DisplayName("Effective Build Cost"), Description("The inflation-adjusted build cost for the year specified in settings")]
+ public int EffectiveBuildCost
+ {
+ get
+ {
+ var year = GlobalSettings.CurrentSettings?.InflationYear ?? 1950;
+ return Common.Economy.GetInflationAdjustedCost(Model.BuildCostFactor, Model.CostIndex, year);
+ }
+ }
+
+ [Category("Cost"), ReadOnly(true), DisplayName("Effective Sell Cost"), Description("The inflation-adjusted sell cost for the year specified in settings")]
+ public int EffectiveSellCost
+ {
+ get
+ {
+ var year = GlobalSettings.CurrentSettings?.InflationYear ?? 1950;
+ return Common.Economy.GetInflationAdjustedCost(Model.SellCostFactor, Model.CostIndex, year);
+ }
+ }
}
diff --git a/Gui/ViewModels/LocoTypes/Objects/RoadStationViewModel.cs b/Gui/ViewModels/LocoTypes/Objects/RoadStationViewModel.cs
index b1d62cb7..7c9e25cf 100644
--- a/Gui/ViewModels/LocoTypes/Objects/RoadStationViewModel.cs
+++ b/Gui/ViewModels/LocoTypes/Objects/RoadStationViewModel.cs
@@ -70,6 +70,26 @@ public uint8_t CostIndex
set => Model.CostIndex = value;
}
+ [Category("Cost"), ReadOnly(true), DisplayName("Effective Build Cost"), Description("The inflation-adjusted build cost for the year specified in settings")]
+ public int EffectiveBuildCost
+ {
+ get
+ {
+ var year = GlobalSettings.CurrentSettings?.InflationYear ?? 1950;
+ return Common.Economy.GetInflationAdjustedCost(Model.BuildCostFactor, Model.CostIndex, year);
+ }
+ }
+
+ [Category("Cost"), ReadOnly(true), DisplayName("Effective Sell Cost"), Description("The inflation-adjusted sell cost for the year specified in settings")]
+ public int EffectiveSellCost
+ {
+ get
+ {
+ var year = GlobalSettings.CurrentSettings?.InflationYear ?? 1950;
+ return Common.Economy.GetInflationAdjustedCost(Model.SellCostFactor, Model.CostIndex, year);
+ }
+ }
+
[Category("Cargo")]
public ObjectModelHeader? CargoType
{
diff --git a/Gui/ViewModels/LocoTypes/Objects/RoadViewModel.cs b/Gui/ViewModels/LocoTypes/Objects/RoadViewModel.cs
index 2cdf9604..0d99ca84 100644
--- a/Gui/ViewModels/LocoTypes/Objects/RoadViewModel.cs
+++ b/Gui/ViewModels/LocoTypes/Objects/RoadViewModel.cs
@@ -75,6 +75,36 @@ public uint8_t CostIndex
set => Model.CostIndex = value;
}
+ [Category("Cost"), ReadOnly(true), DisplayName("Effective Build Cost"), Description("The inflation-adjusted build cost for the year specified in settings")]
+ public int EffectiveBuildCost
+ {
+ get
+ {
+ var year = GlobalSettings.CurrentSettings?.InflationYear ?? 1950;
+ return Common.Economy.GetInflationAdjustedCost(Model.BuildCostFactor, Model.CostIndex, year);
+ }
+ }
+
+ [Category("Cost"), ReadOnly(true), DisplayName("Effective Sell Cost"), Description("The inflation-adjusted sell cost for the year specified in settings")]
+ public int EffectiveSellCost
+ {
+ get
+ {
+ var year = GlobalSettings.CurrentSettings?.InflationYear ?? 1950;
+ return Common.Economy.GetInflationAdjustedCost(Model.SellCostFactor, Model.CostIndex, year);
+ }
+ }
+
+ [Category("Cost"), ReadOnly(true), DisplayName("Effective Tunnel Cost"), Description("The inflation-adjusted tunnel cost for the year specified in settings")]
+ public int EffectiveTunnelCost
+ {
+ get
+ {
+ var year = GlobalSettings.CurrentSettings?.InflationYear ?? 1950;
+ return Common.Economy.GetInflationAdjustedCost(Model.TunnelCostFactor, Model.CostIndex, year);
+ }
+ }
+
[Category("Compatible Objects")]
public ObjectModelHeader Tunnel
{
diff --git a/Gui/ViewModels/LocoTypes/Objects/TrackExtraViewModel.cs b/Gui/ViewModels/LocoTypes/Objects/TrackExtraViewModel.cs
index fba89e15..b9aa8e05 100644
--- a/Gui/ViewModels/LocoTypes/Objects/TrackExtraViewModel.cs
+++ b/Gui/ViewModels/LocoTypes/Objects/TrackExtraViewModel.cs
@@ -1,6 +1,7 @@
using Definitions.ObjectModels.Objects.Track;
using Definitions.ObjectModels.Objects.TrackExtra;
using PropertyModels.ComponentModel.DataAnnotations;
+using System.ComponentModel;
namespace Gui.ViewModels;
@@ -20,21 +21,44 @@ public uint8_t PaintStyle
set => Model.PaintStyle = value;
}
+ [Category("Cost")]
public uint8_t CostIndex
{
get => Model.CostIndex;
set => Model.CostIndex = value;
}
+ [Category("Cost")]
public int16_t BuildCostFactor
{
get => Model.BuildCostFactor;
set => Model.BuildCostFactor = value;
}
+ [Category("Cost")]
public int16_t SellCostFactor
{
get => Model.SellCostFactor;
set => Model.SellCostFactor = value;
}
+
+ [Category("Cost"), ReadOnly(true), DisplayName("Effective Build Cost"), Description("The inflation-adjusted build cost for the year specified in settings")]
+ public int EffectiveBuildCost
+ {
+ get
+ {
+ var year = GlobalSettings.CurrentSettings?.InflationYear ?? 1950;
+ return Common.Economy.GetInflationAdjustedCost(Model.BuildCostFactor, Model.CostIndex, year);
+ }
+ }
+
+ [Category("Cost"), ReadOnly(true), DisplayName("Effective Sell Cost"), Description("The inflation-adjusted sell cost for the year specified in settings")]
+ public int EffectiveSellCost
+ {
+ get
+ {
+ var year = GlobalSettings.CurrentSettings?.InflationYear ?? 1950;
+ return Common.Economy.GetInflationAdjustedCost(Model.SellCostFactor, Model.CostIndex, year);
+ }
+ }
}
diff --git a/Gui/ViewModels/LocoTypes/Objects/TrackSignalViewModel.cs b/Gui/ViewModels/LocoTypes/Objects/TrackSignalViewModel.cs
index 2e5d1f50..e67cb3d9 100644
--- a/Gui/ViewModels/LocoTypes/Objects/TrackSignalViewModel.cs
+++ b/Gui/ViewModels/LocoTypes/Objects/TrackSignalViewModel.cs
@@ -50,6 +50,26 @@ public int16_t SellCostFactor
set => Model.SellCostFactor = value;
}
+ [Category("Cost"), ReadOnly(true), DisplayName("Effective Build Cost"), Description("The inflation-adjusted build cost for the year specified in settings")]
+ public int EffectiveBuildCost
+ {
+ get
+ {
+ var year = GlobalSettings.CurrentSettings?.InflationYear ?? 1950;
+ return Common.Economy.GetInflationAdjustedCost(Model.BuildCostFactor, Model.CostIndex, year);
+ }
+ }
+
+ [Category("Cost"), ReadOnly(true), DisplayName("Effective Sell Cost"), Description("The inflation-adjusted sell cost for the year specified in settings")]
+ public int EffectiveSellCost
+ {
+ get
+ {
+ var year = GlobalSettings.CurrentSettings?.InflationYear ?? 1950;
+ return Common.Economy.GetInflationAdjustedCost(Model.SellCostFactor, Model.CostIndex, year);
+ }
+ }
+
[Category("Stats")]
public uint16_t DesignedYear
{
diff --git a/Gui/ViewModels/LocoTypes/Objects/TrackStationViewModel.cs b/Gui/ViewModels/LocoTypes/Objects/TrackStationViewModel.cs
index 8e22626c..38822449 100644
--- a/Gui/ViewModels/LocoTypes/Objects/TrackStationViewModel.cs
+++ b/Gui/ViewModels/LocoTypes/Objects/TrackStationViewModel.cs
@@ -69,6 +69,26 @@ public uint8_t CostIndex
set => Model.CostIndex = value;
}
+ [Category("Cost"), ReadOnly(true), DisplayName("Effective Build Cost"), Description("The inflation-adjusted build cost for the year specified in settings")]
+ public int EffectiveBuildCost
+ {
+ get
+ {
+ var year = GlobalSettings.CurrentSettings?.InflationYear ?? 1950;
+ return Common.Economy.GetInflationAdjustedCost(Model.BuildCostFactor, Model.CostIndex, year);
+ }
+ }
+
+ [Category("Cost"), ReadOnly(true), DisplayName("Effective Sell Cost"), Description("The inflation-adjusted sell cost for the year specified in settings")]
+ public int EffectiveSellCost
+ {
+ get
+ {
+ var year = GlobalSettings.CurrentSettings?.InflationYear ?? 1950;
+ return Common.Economy.GetInflationAdjustedCost(Model.SellCostFactor, Model.CostIndex, year);
+ }
+ }
+
[Category("")]
public uint8_t var_0B
{
diff --git a/Gui/ViewModels/LocoTypes/Objects/TreeViewModel.cs b/Gui/ViewModels/LocoTypes/Objects/TreeViewModel.cs
index a657bdf5..847f9c68 100644
--- a/Gui/ViewModels/LocoTypes/Objects/TreeViewModel.cs
+++ b/Gui/ViewModels/LocoTypes/Objects/TreeViewModel.cs
@@ -65,6 +65,26 @@ public int16_t ClearCostFactor
set => Model.ClearCostFactor = value;
}
+ [Category("Cost"), ReadOnly(true), DisplayName("Effective Build Cost"), Description("The inflation-adjusted build cost for the year specified in settings")]
+ public int EffectiveBuildCost
+ {
+ get
+ {
+ var year = GlobalSettings.CurrentSettings?.InflationYear ?? 1950;
+ return Common.Economy.GetInflationAdjustedCost(Model.BuildCostFactor, Model.CostIndex, year);
+ }
+ }
+
+ [Category("Cost"), ReadOnly(true), DisplayName("Effective Clear Cost"), Description("The inflation-adjusted clear cost for the year specified in settings")]
+ public int EffectiveClearCost
+ {
+ get
+ {
+ var year = GlobalSettings.CurrentSettings?.InflationYear ?? 1950;
+ return Common.Economy.GetInflationAdjustedCost(Model.ClearCostFactor, Model.CostIndex, year);
+ }
+ }
+
[Category("Building")]
public uint8_t InitialHeight
{
diff --git a/Gui/ViewModels/LocoTypes/Objects/VehicleViewModel.cs b/Gui/ViewModels/LocoTypes/Objects/VehicleViewModel.cs
index ac353d5f..e470996b 100644
--- a/Gui/ViewModels/LocoTypes/Objects/VehicleViewModel.cs
+++ b/Gui/ViewModels/LocoTypes/Objects/VehicleViewModel.cs
@@ -277,6 +277,26 @@ public int16_t RunCostFactor
set => model.RunCostFactor = value;
}
+ [Category("Cost"), ReadOnly(true), DisplayName("Effective Build Cost"), Description("The inflation-adjusted purchase cost for the year specified in settings")]
+ public int EffectiveBuildCost
+ {
+ get
+ {
+ var year = GlobalSettings.CurrentSettings?.InflationYear ?? 1950;
+ return Common.Economy.GetInflationAdjustedCost(model.CostFactor, model.CostIndex, year);
+ }
+ }
+
+ [Category("Cost"), ReadOnly(true), DisplayName("Effective Run Cost"), Description("The inflation-adjusted running cost for the year specified in settings")]
+ public int EffectiveRunCost
+ {
+ get
+ {
+ var year = GlobalSettings.CurrentSettings?.InflationYear ?? 1950;
+ return Common.Economy.GetInflationAdjustedCost(model.RunCostFactor, model.RunCostIndex, year);
+ }
+ }
+
[Category("Sprites")]
public CompanyColourType SpecialColourSchemeIndex
{
From 5831480c37a74f14341bda9319a5cb4432d9fc84 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sat, 27 Dec 2025 14:07:53 +0000
Subject: [PATCH 5/8] Add comprehensive tests for effective cost calculations
in ViewModels
Co-authored-by: LeftofZen <7483209+LeftofZen@users.noreply.github.com>
---
Tests/EffectiveCostTests.cs | 106 ++++++++++++++++++++++++++++++++++++
1 file changed, 106 insertions(+)
create mode 100644 Tests/EffectiveCostTests.cs
diff --git a/Tests/EffectiveCostTests.cs b/Tests/EffectiveCostTests.cs
new file mode 100644
index 00000000..bcea4794
--- /dev/null
+++ b/Tests/EffectiveCostTests.cs
@@ -0,0 +1,106 @@
+using Definitions.ObjectModels.Objects.Track;
+using Definitions.ObjectModels.Objects.Bridge;
+using Gui;
+using Gui.ViewModels;
+using NUnit.Framework;
+
+namespace Gui.Tests;
+
+[TestFixture]
+public class EffectiveCostTests
+{
+ [SetUp]
+ public void Setup()
+ {
+ // Set up global settings
+ GlobalSettings.CurrentSettings = new EditorSettings
+ {
+ InflationYear = 1950
+ };
+ }
+
+ [Test]
+ public void TrackViewModel_CalculatesEffectiveCosts()
+ {
+ var trackObject = new TrackObject
+ {
+ BuildCostFactor = 100,
+ SellCostFactor = -50,
+ TunnelCostFactor = 200,
+ CostIndex = 0
+ };
+
+ var viewModel = new TrackViewModel(trackObject);
+
+ // For year 1950, with factor 1024 and divisor 10, we should get: (100 * X) / 1024
+ // where X is the inflation-adjusted currency multiplication factor
+ Assert.That(viewModel.EffectiveBuildCost, Is.GreaterThan(0));
+ Assert.That(viewModel.EffectiveSellCost, Is.LessThan(0));
+ Assert.That(viewModel.EffectiveTunnelCost, Is.GreaterThan(0));
+
+ // Build cost should be proportional to the factors
+ Assert.That(viewModel.EffectiveTunnelCost, Is.GreaterThan(viewModel.EffectiveBuildCost));
+ }
+
+ [Test]
+ public void BridgeViewModel_CalculatesEffectiveCosts()
+ {
+ var bridgeObject = new BridgeObject
+ {
+ BaseCostFactor = 150,
+ HeightCostFactor = 50,
+ SellCostFactor = -75,
+ CostIndex = 1
+ };
+
+ var viewModel = new BridgeViewModel(bridgeObject);
+
+ Assert.That(viewModel.EffectiveBaseCost, Is.GreaterThan(0));
+ Assert.That(viewModel.EffectiveHeightCost, Is.GreaterThan(0));
+ Assert.That(viewModel.EffectiveSellCost, Is.LessThan(0));
+
+ // Base cost should be greater than height cost
+ Assert.That(viewModel.EffectiveBaseCost, Is.GreaterThan(viewModel.EffectiveHeightCost));
+ }
+
+ [Test]
+ public void InflationYear_AffectsCosts()
+ {
+ var trackObject = new TrackObject
+ {
+ BuildCostFactor = 100,
+ CostIndex = 0
+ };
+
+ // Test with year 1900 (no inflation)
+ GlobalSettings.CurrentSettings!.InflationYear = 1900;
+ var viewModel1900 = new TrackViewModel(trackObject);
+ var cost1900 = viewModel1900.EffectiveBuildCost;
+
+ // Test with year 2000 (with inflation)
+ GlobalSettings.CurrentSettings!.InflationYear = 2000;
+ var viewModel2000 = new TrackViewModel(trackObject);
+ var cost2000 = viewModel2000.EffectiveBuildCost;
+
+ // Cost in 2000 should be higher due to inflation
+ Assert.That(cost2000, Is.GreaterThan(cost1900));
+ }
+
+ [Test]
+ public void GlobalSettings_DefaultYear_WorksCorrectly()
+ {
+ // Test that if GlobalSettings is null, default year is used
+ GlobalSettings.CurrentSettings = null;
+
+ var trackObject = new TrackObject
+ {
+ BuildCostFactor = 100,
+ CostIndex = 0
+ };
+
+ var viewModel = new TrackViewModel(trackObject);
+
+ // Should not throw and should return a valid cost
+ Assert.That(viewModel.EffectiveBuildCost, Is.GreaterThan(0));
+ }
+}
From b9f4c6561fe2063b3780300823888831caababb2 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sun, 28 Dec 2025 01:28:01 +0000
Subject: [PATCH 6/8] Implement custom PropertyGrid cell editor for currency
properties with inline inflation adjustment
- Created CurrencyAttribute to mark cost factor properties
- Created CurrencyEditorViewModel and CurrencyView for inline editing
- Created CurrencyCellEditFactory in ExtendedPropertyGrid
- Updated all 13 object ViewModels to use Currency attribute
- Removed separate read-only effective cost properties
- Cost properties now show: Factor input, Year stepper, and calculated effective cost inline
Co-authored-by: LeftofZen <7483209+LeftofZen@users.noreply.github.com>
---
Gui/Attributes/CurrencyAttribute.cs | 17 +++
Gui/ViewModels/CurrencyEditorViewModel.cs | 52 +++++++++
.../LocoTypes/Objects/AirportViewModel.cs | 33 +-----
.../LocoTypes/Objects/BridgeViewModel.cs | 37 +-----
.../LocoTypes/Objects/DockViewModel.cs | 33 +-----
.../LocoTypes/Objects/IndustryViewModel.cs | 33 +-----
.../LocoTypes/Objects/RoadExtraViewModel.cs | 33 +-----
.../LocoTypes/Objects/RoadStationViewModel.cs | 40 +------
.../LocoTypes/Objects/RoadViewModel.cs | 56 +--------
.../LocoTypes/Objects/TrackExtraViewModel.cs | 33 +-----
.../LocoTypes/Objects/TrackSignalViewModel.cs | 33 +-----
.../Objects/TrackStationViewModel.cs | 40 +------
.../LocoTypes/Objects/TrackViewModel.cs | 37 +-----
.../LocoTypes/Objects/TreeViewModel.cs | 33 +-----
.../LocoTypes/Objects/VehicleViewModel.cs | 19 +---
Gui/Views/CurrencyView.axaml | 44 ++++++++
Gui/Views/CurrencyView.axaml.cs | 5 +
Gui/Views/ExtendedPropertyGrid.cs | 87 ++++++++++++++
Tests/EffectiveCostTests.cs | 106 ------------------
19 files changed, 224 insertions(+), 547 deletions(-)
create mode 100644 Gui/Attributes/CurrencyAttribute.cs
create mode 100644 Gui/ViewModels/CurrencyEditorViewModel.cs
create mode 100644 Gui/Views/CurrencyView.axaml
create mode 100644 Gui/Views/CurrencyView.axaml.cs
delete mode 100644 Tests/EffectiveCostTests.cs
diff --git a/Gui/Attributes/CurrencyAttribute.cs b/Gui/Attributes/CurrencyAttribute.cs
new file mode 100644
index 00000000..a32ecb9e
--- /dev/null
+++ b/Gui/Attributes/CurrencyAttribute.cs
@@ -0,0 +1,17 @@
+using System;
+
+namespace Gui.Attributes;
+
+///
+/// Marks a property as a currency value that should display with inflation adjustment.
+/// The property must be of type int16_t and have a corresponding CostIndex property.
+///
+[AttributeUsage(AttributeTargets.Property)]
+public class CurrencyAttribute : Attribute
+{
+ ///
+ /// The name of the property that contains the CostIndex for this currency value.
+ /// Defaults to "CostIndex".
+ ///
+ public string CostIndexPropertyName { get; set; } = "CostIndex";
+}
diff --git a/Gui/ViewModels/CurrencyEditorViewModel.cs b/Gui/ViewModels/CurrencyEditorViewModel.cs
new file mode 100644
index 00000000..bb4be485
--- /dev/null
+++ b/Gui/ViewModels/CurrencyEditorViewModel.cs
@@ -0,0 +1,52 @@
+using ReactiveUI;
+
+namespace Gui.ViewModels;
+
+public class CurrencyEditorViewModel : ReactiveObject
+{
+ private short _costFactor;
+ private byte _costIndex;
+ private int _year = 1950;
+
+ public short CostFactor
+ {
+ get => _costFactor;
+ set
+ {
+ this.RaiseAndSetIfChanged(ref _costFactor, value);
+ this.RaisePropertyChanged(nameof(EffectiveCost));
+ }
+ }
+
+ public byte CostIndex
+ {
+ get => _costIndex;
+ set
+ {
+ this.RaiseAndSetIfChanged(ref _costIndex, value);
+ this.RaisePropertyChanged(nameof(EffectiveCost));
+ }
+ }
+
+ public int Year
+ {
+ get => _year;
+ set
+ {
+ this.RaiseAndSetIfChanged(ref _year, value);
+ this.RaisePropertyChanged(nameof(EffectiveCost));
+ }
+ }
+
+ public int EffectiveCost
+ {
+ get
+ {
+ if (_costIndex >= 32)
+ {
+ return 0;
+ }
+ return Common.Economy.GetInflationAdjustedCost(_costFactor, _costIndex, _year);
+ }
+ }
+}
diff --git a/Gui/ViewModels/LocoTypes/Objects/AirportViewModel.cs b/Gui/ViewModels/LocoTypes/Objects/AirportViewModel.cs
index f78eed02..c551e810 100644
--- a/Gui/ViewModels/LocoTypes/Objects/AirportViewModel.cs
+++ b/Gui/ViewModels/LocoTypes/Objects/AirportViewModel.cs
@@ -1,6 +1,7 @@
using Dat.Loaders;
using Definitions.ObjectModels.Objects.Airport;
using Definitions.ObjectModels.Objects.Common;
+using Gui.Attributes;
using PropertyModels.ComponentModel.DataAnnotations;
using PropertyModels.Extensions;
using System.ComponentModel;
@@ -68,39 +69,7 @@ public uint8_t CostIndex
set => Model.CostIndex = value;
}
- [Category("Cost")]
- public int16_t BuildCostFactor
- {
- get => Model.BuildCostFactor;
- set => Model.BuildCostFactor = value;
- }
-
- [Category("Cost")]
- public int16_t SellCostFactor
- {
- get => Model.SellCostFactor;
- set => Model.SellCostFactor = value;
- }
-
- [Category("Cost"), ReadOnly(true), DisplayName("Effective Build Cost"), Description("The inflation-adjusted build cost for the year specified in settings")]
- public int EffectiveBuildCost
- {
- get
- {
- var year = GlobalSettings.CurrentSettings?.InflationYear ?? 1950;
- return Common.Economy.GetInflationAdjustedCost(Model.BuildCostFactor, Model.CostIndex, year);
- }
- }
- [Category("Cost"), ReadOnly(true), DisplayName("Effective Sell Cost"), Description("The inflation-adjusted sell cost for the year specified in settings")]
- public int EffectiveSellCost
- {
- get
- {
- var year = GlobalSettings.CurrentSettings?.InflationYear ?? 1950;
- return Common.Economy.GetInflationAdjustedCost(Model.SellCostFactor, Model.CostIndex, year);
- }
- }
[Category("Building")]
[Length(1, AirportObjectLoader.Constants.BuildingVariationCount)]
diff --git a/Gui/ViewModels/LocoTypes/Objects/BridgeViewModel.cs b/Gui/ViewModels/LocoTypes/Objects/BridgeViewModel.cs
index 00dbaaab..506ad2ab 100644
--- a/Gui/ViewModels/LocoTypes/Objects/BridgeViewModel.cs
+++ b/Gui/ViewModels/LocoTypes/Objects/BridgeViewModel.cs
@@ -1,5 +1,6 @@
using Definitions.ObjectModels.Objects.Bridge;
using Definitions.ObjectModels.Types;
+using Gui.Attributes;
using PropertyModels.ComponentModel.DataAnnotations;
using System.ComponentModel;
@@ -71,57 +72,27 @@ public uint8_t CostIndex
set => Model.CostIndex = value;
}
- [Category("Cost")]
+ [Category("Cost"), Currency]
public int16_t BaseCostFactor
{
get => Model.BaseCostFactor;
set => Model.BaseCostFactor = value;
}
- [Category("Cost")]
+ [Category("Cost"), Currency]
public int16_t HeightCostFactor
{
get => Model.HeightCostFactor;
set => Model.HeightCostFactor = value;
}
- [Category("Cost")]
+ [Category("Cost"), Currency]
public int16_t SellCostFactor
{
get => Model.SellCostFactor;
set => Model.SellCostFactor = value;
}
- [Category("Cost"), ReadOnly(true), DisplayName("Effective Base Cost"), Description("The inflation-adjusted base cost for the year specified in settings")]
- public int EffectiveBaseCost
- {
- get
- {
- var year = GlobalSettings.CurrentSettings?.InflationYear ?? 1950;
- return Common.Economy.GetInflationAdjustedCost(Model.BaseCostFactor, Model.CostIndex, year);
- }
- }
-
- [Category("Cost"), ReadOnly(true), DisplayName("Effective Height Cost"), Description("The inflation-adjusted height cost factor for the year specified in settings")]
- public int EffectiveHeightCost
- {
- get
- {
- var year = GlobalSettings.CurrentSettings?.InflationYear ?? 1950;
- return Common.Economy.GetInflationAdjustedCost(Model.HeightCostFactor, Model.CostIndex, year);
- }
- }
-
- [Category("Cost"), ReadOnly(true), DisplayName("Effective Sell Cost"), Description("The inflation-adjusted sell cost for the year specified in settings")]
- public int EffectiveSellCost
- {
- get
- {
- var year = GlobalSettings.CurrentSettings?.InflationYear ?? 1950;
- return Common.Economy.GetInflationAdjustedCost(Model.SellCostFactor, Model.CostIndex, year);
- }
- }
-
[Category("Compatible")]
public BindingList CompatibleTrackObjects { get; init; } = new(model.CompatibleTrackObjects);
diff --git a/Gui/ViewModels/LocoTypes/Objects/DockViewModel.cs b/Gui/ViewModels/LocoTypes/Objects/DockViewModel.cs
index 9c163b0a..32e0116b 100644
--- a/Gui/ViewModels/LocoTypes/Objects/DockViewModel.cs
+++ b/Gui/ViewModels/LocoTypes/Objects/DockViewModel.cs
@@ -2,6 +2,7 @@
using Definitions.ObjectModels.Objects.Common;
using Definitions.ObjectModels.Objects.Dock;
using Definitions.ObjectModels.Types;
+using Gui.Attributes;
using PropertyModels.ComponentModel.DataAnnotations;
using PropertyModels.Extensions;
using System.ComponentModel;
@@ -45,39 +46,7 @@ public uint8_t CostIndex
set => Model.CostIndex = value;
}
- [Category("Cost")]
- public int16_t BuildCostFactor
- {
- get => Model.BuildCostFactor;
- set => Model.BuildCostFactor = value;
- }
-
- [Category("Cost")]
- public int16_t SellCostFactor
- {
- get => Model.SellCostFactor;
- set => Model.SellCostFactor = value;
- }
-
- [Category("Cost"), ReadOnly(true), DisplayName("Effective Build Cost"), Description("The inflation-adjusted build cost for the year specified in settings")]
- public int EffectiveBuildCost
- {
- get
- {
- var year = GlobalSettings.CurrentSettings?.InflationYear ?? 1950;
- return Common.Economy.GetInflationAdjustedCost(Model.BuildCostFactor, Model.CostIndex, year);
- }
- }
- [Category("Cost"), ReadOnly(true), DisplayName("Effective Sell Cost"), Description("The inflation-adjusted sell cost for the year specified in settings")]
- public int EffectiveSellCost
- {
- get
- {
- var year = GlobalSettings.CurrentSettings?.InflationYear ?? 1950;
- return Common.Economy.GetInflationAdjustedCost(Model.SellCostFactor, Model.CostIndex, year);
- }
- }
[Category("Building")]
[Length(1, DockObjectLoader.Constants.BuildingVariationCount)]
diff --git a/Gui/ViewModels/LocoTypes/Objects/IndustryViewModel.cs b/Gui/ViewModels/LocoTypes/Objects/IndustryViewModel.cs
index 6595e863..49989129 100644
--- a/Gui/ViewModels/LocoTypes/Objects/IndustryViewModel.cs
+++ b/Gui/ViewModels/LocoTypes/Objects/IndustryViewModel.cs
@@ -3,6 +3,7 @@
using Definitions.ObjectModels.Objects.Common;
using Definitions.ObjectModels.Objects.Industry;
using Definitions.ObjectModels.Types;
+using Gui.Attributes;
using PropertyModels.ComponentModel.DataAnnotations;
using PropertyModels.Extensions;
using System.ComponentModel;
@@ -79,39 +80,7 @@ public uint8_t CostIndex
set => Model.CostIndex = value;
}
- [Category("Cost")]
- public int16_t BuildCostFactor
- {
- get => Model.BuildCostFactor;
- set => Model.BuildCostFactor = value;
- }
-
- [Category("Cost")]
- public int16_t SellCostFactor
- {
- get => Model.SellCostFactor;
- set => Model.SellCostFactor = value;
- }
-
- [Category("Cost"), ReadOnly(true), DisplayName("Effective Build Cost"), Description("The inflation-adjusted build cost for the year specified in settings")]
- public int EffectiveBuildCost
- {
- get
- {
- var year = GlobalSettings.CurrentSettings?.InflationYear ?? 1950;
- return Common.Economy.GetInflationAdjustedCost(Model.BuildCostFactor, Model.CostIndex, year);
- }
- }
- [Category("Cost"), ReadOnly(true), DisplayName("Effective Sell Cost"), Description("The inflation-adjusted sell cost for the year specified in settings")]
- public int EffectiveSellCost
- {
- get
- {
- var year = GlobalSettings.CurrentSettings?.InflationYear ?? 1950;
- return Common.Economy.GetInflationAdjustedCost(Model.SellCostFactor, Model.CostIndex, year);
- }
- }
[Category("Farm")]
public uint8_t FarmTileNumImageAngles
diff --git a/Gui/ViewModels/LocoTypes/Objects/RoadExtraViewModel.cs b/Gui/ViewModels/LocoTypes/Objects/RoadExtraViewModel.cs
index bbfa5a04..0403b065 100644
--- a/Gui/ViewModels/LocoTypes/Objects/RoadExtraViewModel.cs
+++ b/Gui/ViewModels/LocoTypes/Objects/RoadExtraViewModel.cs
@@ -1,5 +1,6 @@
using Definitions.ObjectModels.Objects.Road;
using Definitions.ObjectModels.Objects.RoadExtra;
+using Gui.Attributes;
using PropertyModels.ComponentModel.DataAnnotations;
using System.ComponentModel;
@@ -28,37 +29,5 @@ public uint8_t CostIndex
set => Model.CostIndex = value;
}
- [Category("Cost")]
- public int16_t BuildCostFactor
- {
- get => Model.BuildCostFactor;
- set => Model.BuildCostFactor = value;
- }
-
- [Category("Cost")]
- public int16_t SellCostFactor
- {
- get => Model.SellCostFactor;
- set => Model.SellCostFactor = value;
- }
-
- [Category("Cost"), ReadOnly(true), DisplayName("Effective Build Cost"), Description("The inflation-adjusted build cost for the year specified in settings")]
- public int EffectiveBuildCost
- {
- get
- {
- var year = GlobalSettings.CurrentSettings?.InflationYear ?? 1950;
- return Common.Economy.GetInflationAdjustedCost(Model.BuildCostFactor, Model.CostIndex, year);
- }
- }
- [Category("Cost"), ReadOnly(true), DisplayName("Effective Sell Cost"), Description("The inflation-adjusted sell cost for the year specified in settings")]
- public int EffectiveSellCost
- {
- get
- {
- var year = GlobalSettings.CurrentSettings?.InflationYear ?? 1950;
- return Common.Economy.GetInflationAdjustedCost(Model.SellCostFactor, Model.CostIndex, year);
- }
- }
}
diff --git a/Gui/ViewModels/LocoTypes/Objects/RoadStationViewModel.cs b/Gui/ViewModels/LocoTypes/Objects/RoadStationViewModel.cs
index 7c9e25cf..b4ca113e 100644
--- a/Gui/ViewModels/LocoTypes/Objects/RoadStationViewModel.cs
+++ b/Gui/ViewModels/LocoTypes/Objects/RoadStationViewModel.cs
@@ -2,6 +2,7 @@
using Definitions.ObjectModels.Objects.RoadStation;
using Definitions.ObjectModels.Objects.Shared;
using Definitions.ObjectModels.Types;
+using Gui.Attributes;
using PropertyModels.ComponentModel.DataAnnotations;
using System.ComponentModel;
@@ -49,46 +50,7 @@ public RoadStationObjectFlags Flags
set => Model.Flags = value;
}
- [Category("Cost")]
- public int16_t BuildCostFactor
- {
- get => Model.BuildCostFactor;
- set => Model.BuildCostFactor = value;
- }
- [Category("Cost")]
- public int16_t SellCostFactor
- {
- get => Model.SellCostFactor;
- set => Model.SellCostFactor = value;
- }
-
- [Category("Cost")]
- public uint8_t CostIndex
- {
- get => Model.CostIndex;
- set => Model.CostIndex = value;
- }
-
- [Category("Cost"), ReadOnly(true), DisplayName("Effective Build Cost"), Description("The inflation-adjusted build cost for the year specified in settings")]
- public int EffectiveBuildCost
- {
- get
- {
- var year = GlobalSettings.CurrentSettings?.InflationYear ?? 1950;
- return Common.Economy.GetInflationAdjustedCost(Model.BuildCostFactor, Model.CostIndex, year);
- }
- }
-
- [Category("Cost"), ReadOnly(true), DisplayName("Effective Sell Cost"), Description("The inflation-adjusted sell cost for the year specified in settings")]
- public int EffectiveSellCost
- {
- get
- {
- var year = GlobalSettings.CurrentSettings?.InflationYear ?? 1950;
- return Common.Economy.GetInflationAdjustedCost(Model.SellCostFactor, Model.CostIndex, year);
- }
- }
[Category("Cargo")]
public ObjectModelHeader? CargoType
diff --git a/Gui/ViewModels/LocoTypes/Objects/RoadViewModel.cs b/Gui/ViewModels/LocoTypes/Objects/RoadViewModel.cs
index 0d99ca84..d7e2ed53 100644
--- a/Gui/ViewModels/LocoTypes/Objects/RoadViewModel.cs
+++ b/Gui/ViewModels/LocoTypes/Objects/RoadViewModel.cs
@@ -1,5 +1,6 @@
using Definitions.ObjectModels.Objects.Road;
using Definitions.ObjectModels.Types;
+using Gui.Attributes;
using PropertyModels.ComponentModel.DataAnnotations;
using System.ComponentModel;
@@ -47,63 +48,8 @@ public TownSize TargetTownSize
set => Model.TargetTownSize = value;
}
- [Category("Cost")]
- public int16_t BuildCostFactor
- {
- get => Model.BuildCostFactor;
- set => Model.BuildCostFactor = value;
- }
-
- [Category("Cost")]
- public int16_t SellCostFactor
- {
- get => Model.SellCostFactor;
- set => Model.SellCostFactor = value;
- }
-
- [Category("Cost")]
- public int16_t TunnelCostFactor
- {
- get => Model.TunnelCostFactor;
- set => Model.TunnelCostFactor = value;
- }
-
- [Category("Cost")]
- public uint8_t CostIndex
- {
- get => Model.CostIndex;
- set => Model.CostIndex = value;
- }
- [Category("Cost"), ReadOnly(true), DisplayName("Effective Build Cost"), Description("The inflation-adjusted build cost for the year specified in settings")]
- public int EffectiveBuildCost
- {
- get
- {
- var year = GlobalSettings.CurrentSettings?.InflationYear ?? 1950;
- return Common.Economy.GetInflationAdjustedCost(Model.BuildCostFactor, Model.CostIndex, year);
- }
- }
- [Category("Cost"), ReadOnly(true), DisplayName("Effective Sell Cost"), Description("The inflation-adjusted sell cost for the year specified in settings")]
- public int EffectiveSellCost
- {
- get
- {
- var year = GlobalSettings.CurrentSettings?.InflationYear ?? 1950;
- return Common.Economy.GetInflationAdjustedCost(Model.SellCostFactor, Model.CostIndex, year);
- }
- }
-
- [Category("Cost"), ReadOnly(true), DisplayName("Effective Tunnel Cost"), Description("The inflation-adjusted tunnel cost for the year specified in settings")]
- public int EffectiveTunnelCost
- {
- get
- {
- var year = GlobalSettings.CurrentSettings?.InflationYear ?? 1950;
- return Common.Economy.GetInflationAdjustedCost(Model.TunnelCostFactor, Model.CostIndex, year);
- }
- }
[Category("Compatible Objects")]
public ObjectModelHeader Tunnel
diff --git a/Gui/ViewModels/LocoTypes/Objects/TrackExtraViewModel.cs b/Gui/ViewModels/LocoTypes/Objects/TrackExtraViewModel.cs
index b9aa8e05..91f60102 100644
--- a/Gui/ViewModels/LocoTypes/Objects/TrackExtraViewModel.cs
+++ b/Gui/ViewModels/LocoTypes/Objects/TrackExtraViewModel.cs
@@ -1,5 +1,6 @@
using Definitions.ObjectModels.Objects.Track;
using Definitions.ObjectModels.Objects.TrackExtra;
+using Gui.Attributes;
using PropertyModels.ComponentModel.DataAnnotations;
using System.ComponentModel;
@@ -28,37 +29,5 @@ public uint8_t CostIndex
set => Model.CostIndex = value;
}
- [Category("Cost")]
- public int16_t BuildCostFactor
- {
- get => Model.BuildCostFactor;
- set => Model.BuildCostFactor = value;
- }
-
- [Category("Cost")]
- public int16_t SellCostFactor
- {
- get => Model.SellCostFactor;
- set => Model.SellCostFactor = value;
- }
-
- [Category("Cost"), ReadOnly(true), DisplayName("Effective Build Cost"), Description("The inflation-adjusted build cost for the year specified in settings")]
- public int EffectiveBuildCost
- {
- get
- {
- var year = GlobalSettings.CurrentSettings?.InflationYear ?? 1950;
- return Common.Economy.GetInflationAdjustedCost(Model.BuildCostFactor, Model.CostIndex, year);
- }
- }
- [Category("Cost"), ReadOnly(true), DisplayName("Effective Sell Cost"), Description("The inflation-adjusted sell cost for the year specified in settings")]
- public int EffectiveSellCost
- {
- get
- {
- var year = GlobalSettings.CurrentSettings?.InflationYear ?? 1950;
- return Common.Economy.GetInflationAdjustedCost(Model.SellCostFactor, Model.CostIndex, year);
- }
- }
}
diff --git a/Gui/ViewModels/LocoTypes/Objects/TrackSignalViewModel.cs b/Gui/ViewModels/LocoTypes/Objects/TrackSignalViewModel.cs
index e67cb3d9..52ab4771 100644
--- a/Gui/ViewModels/LocoTypes/Objects/TrackSignalViewModel.cs
+++ b/Gui/ViewModels/LocoTypes/Objects/TrackSignalViewModel.cs
@@ -1,6 +1,7 @@
using Dat.Loaders;
using Definitions.ObjectModels.Objects.TrackSignal;
using Definitions.ObjectModels.Types;
+using Gui.Attributes;
using PropertyModels.ComponentModel.DataAnnotations;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
@@ -36,39 +37,7 @@ public uint8_t CostIndex
set => Model.CostIndex = value;
}
- [Category("Cost")]
- public int16_t BuildCostFactor
- {
- get => Model.BuildCostFactor;
- set => Model.BuildCostFactor = value;
- }
-
- [Category("Cost")]
- public int16_t SellCostFactor
- {
- get => Model.SellCostFactor;
- set => Model.SellCostFactor = value;
- }
-
- [Category("Cost"), ReadOnly(true), DisplayName("Effective Build Cost"), Description("The inflation-adjusted build cost for the year specified in settings")]
- public int EffectiveBuildCost
- {
- get
- {
- var year = GlobalSettings.CurrentSettings?.InflationYear ?? 1950;
- return Common.Economy.GetInflationAdjustedCost(Model.BuildCostFactor, Model.CostIndex, year);
- }
- }
- [Category("Cost"), ReadOnly(true), DisplayName("Effective Sell Cost"), Description("The inflation-adjusted sell cost for the year specified in settings")]
- public int EffectiveSellCost
- {
- get
- {
- var year = GlobalSettings.CurrentSettings?.InflationYear ?? 1950;
- return Common.Economy.GetInflationAdjustedCost(Model.SellCostFactor, Model.CostIndex, year);
- }
- }
[Category("Stats")]
public uint16_t DesignedYear
diff --git a/Gui/ViewModels/LocoTypes/Objects/TrackStationViewModel.cs b/Gui/ViewModels/LocoTypes/Objects/TrackStationViewModel.cs
index 38822449..17931cae 100644
--- a/Gui/ViewModels/LocoTypes/Objects/TrackStationViewModel.cs
+++ b/Gui/ViewModels/LocoTypes/Objects/TrackStationViewModel.cs
@@ -2,6 +2,7 @@
using Definitions.ObjectModels.Objects.Track;
using Definitions.ObjectModels.Objects.TrackStation;
using Definitions.ObjectModels.Types;
+using Gui.Attributes;
using PropertyModels.ComponentModel.DataAnnotations;
using System.ComponentModel;
@@ -48,46 +49,7 @@ public TrackStationObjectFlags Flags
set => Model.Flags = value;
}
- [Category("Cost")]
- public int16_t BuildCostFactor
- {
- get => Model.BuildCostFactor;
- set => Model.BuildCostFactor = value;
- }
- [Category("Cost")]
- public int16_t SellCostFactor
- {
- get => Model.SellCostFactor;
- set => Model.SellCostFactor = value;
- }
-
- [Category("Cost")]
- public uint8_t CostIndex
- {
- get => Model.CostIndex;
- set => Model.CostIndex = value;
- }
-
- [Category("Cost"), ReadOnly(true), DisplayName("Effective Build Cost"), Description("The inflation-adjusted build cost for the year specified in settings")]
- public int EffectiveBuildCost
- {
- get
- {
- var year = GlobalSettings.CurrentSettings?.InflationYear ?? 1950;
- return Common.Economy.GetInflationAdjustedCost(Model.BuildCostFactor, Model.CostIndex, year);
- }
- }
-
- [Category("Cost"), ReadOnly(true), DisplayName("Effective Sell Cost"), Description("The inflation-adjusted sell cost for the year specified in settings")]
- public int EffectiveSellCost
- {
- get
- {
- var year = GlobalSettings.CurrentSettings?.InflationYear ?? 1950;
- return Common.Economy.GetInflationAdjustedCost(Model.SellCostFactor, Model.CostIndex, year);
- }
- }
[Category("")]
public uint8_t var_0B
diff --git a/Gui/ViewModels/LocoTypes/Objects/TrackViewModel.cs b/Gui/ViewModels/LocoTypes/Objects/TrackViewModel.cs
index b01b200f..1eebb2f5 100644
--- a/Gui/ViewModels/LocoTypes/Objects/TrackViewModel.cs
+++ b/Gui/ViewModels/LocoTypes/Objects/TrackViewModel.cs
@@ -1,5 +1,6 @@
using Definitions.ObjectModels.Objects.Track;
using Definitions.ObjectModels.Types;
+using Gui.Attributes;
using PropertyModels.ComponentModel.DataAnnotations;
using System.ComponentModel;
using TrackObject = Definitions.ObjectModels.Objects.Track.TrackObject;
@@ -43,21 +44,21 @@ public uint8_t VehicleDisplayListVerticalOffset
set => Model.VehicleDisplayListVerticalOffset = value;
}
- [Category("Cost")]
+ [Category("Cost"), Currency]
public int16_t BuildCostFactor
{
get => Model.BuildCostFactor;
set => Model.BuildCostFactor = value;
}
- [Category("Cost")]
+ [Category("Cost"), Currency]
public int16_t SellCostFactor
{
get => Model.SellCostFactor;
set => Model.SellCostFactor = value;
}
- [Category("Cost")]
+ [Category("Cost"), Currency]
public int16_t TunnelCostFactor
{
get => Model.TunnelCostFactor;
@@ -71,36 +72,6 @@ public uint8_t CostIndex
set => Model.CostIndex = value;
}
- [Category("Cost"), ReadOnly(true), DisplayName("Effective Build Cost"), Description("The inflation-adjusted build cost for the year specified in settings")]
- public int EffectiveBuildCost
- {
- get
- {
- var year = GlobalSettings.CurrentSettings?.InflationYear ?? 1950;
- return Common.Economy.GetInflationAdjustedCost(Model.BuildCostFactor, Model.CostIndex, year);
- }
- }
-
- [Category("Cost"), ReadOnly(true), DisplayName("Effective Sell Cost"), Description("The inflation-adjusted sell cost for the year specified in settings")]
- public int EffectiveSellCost
- {
- get
- {
- var year = GlobalSettings.CurrentSettings?.InflationYear ?? 1950;
- return Common.Economy.GetInflationAdjustedCost(Model.SellCostFactor, Model.CostIndex, year);
- }
- }
-
- [Category("Cost"), ReadOnly(true), DisplayName("Effective Tunnel Cost"), Description("The inflation-adjusted tunnel cost for the year specified in settings")]
- public int EffectiveTunnelCost
- {
- get
- {
- var year = GlobalSettings.CurrentSettings?.InflationYear ?? 1950;
- return Common.Economy.GetInflationAdjustedCost(Model.TunnelCostFactor, Model.CostIndex, year);
- }
- }
-
[Category("")]
public uint8_t var_06
{
diff --git a/Gui/ViewModels/LocoTypes/Objects/TreeViewModel.cs b/Gui/ViewModels/LocoTypes/Objects/TreeViewModel.cs
index 847f9c68..ea8bffba 100644
--- a/Gui/ViewModels/LocoTypes/Objects/TreeViewModel.cs
+++ b/Gui/ViewModels/LocoTypes/Objects/TreeViewModel.cs
@@ -1,4 +1,5 @@
using Definitions.ObjectModels.Objects.Tree;
+using Gui.Attributes;
using PropertyModels.ComponentModel.DataAnnotations;
using System.ComponentModel;
@@ -51,39 +52,7 @@ public uint8_t CostIndex
set => Model.CostIndex = value;
}
- [Category("Cost")]
- public int16_t BuildCostFactor
- {
- get => Model.BuildCostFactor;
- set => Model.BuildCostFactor = value;
- }
-
- [Category("Cost")]
- public int16_t ClearCostFactor
- {
- get => Model.ClearCostFactor;
- set => Model.ClearCostFactor = value;
- }
-
- [Category("Cost"), ReadOnly(true), DisplayName("Effective Build Cost"), Description("The inflation-adjusted build cost for the year specified in settings")]
- public int EffectiveBuildCost
- {
- get
- {
- var year = GlobalSettings.CurrentSettings?.InflationYear ?? 1950;
- return Common.Economy.GetInflationAdjustedCost(Model.BuildCostFactor, Model.CostIndex, year);
- }
- }
- [Category("Cost"), ReadOnly(true), DisplayName("Effective Clear Cost"), Description("The inflation-adjusted clear cost for the year specified in settings")]
- public int EffectiveClearCost
- {
- get
- {
- var year = GlobalSettings.CurrentSettings?.InflationYear ?? 1950;
- return Common.Economy.GetInflationAdjustedCost(Model.ClearCostFactor, Model.CostIndex, year);
- }
- }
[Category("Building")]
public uint8_t InitialHeight
diff --git a/Gui/ViewModels/LocoTypes/Objects/VehicleViewModel.cs b/Gui/ViewModels/LocoTypes/Objects/VehicleViewModel.cs
index e470996b..02ecbc47 100644
--- a/Gui/ViewModels/LocoTypes/Objects/VehicleViewModel.cs
+++ b/Gui/ViewModels/LocoTypes/Objects/VehicleViewModel.cs
@@ -4,6 +4,7 @@
using Definitions.ObjectModels.Types;
using DynamicData.Binding;
using PropertyModels.ComponentModel;
+using Gui.Attributes;
using PropertyModels.ComponentModel.DataAnnotations;
using PropertyModels.Extensions;
using ReactiveUI;
@@ -277,25 +278,7 @@ public int16_t RunCostFactor
set => model.RunCostFactor = value;
}
- [Category("Cost"), ReadOnly(true), DisplayName("Effective Build Cost"), Description("The inflation-adjusted purchase cost for the year specified in settings")]
- public int EffectiveBuildCost
- {
- get
- {
- var year = GlobalSettings.CurrentSettings?.InflationYear ?? 1950;
- return Common.Economy.GetInflationAdjustedCost(model.CostFactor, model.CostIndex, year);
- }
- }
- [Category("Cost"), ReadOnly(true), DisplayName("Effective Run Cost"), Description("The inflation-adjusted running cost for the year specified in settings")]
- public int EffectiveRunCost
- {
- get
- {
- var year = GlobalSettings.CurrentSettings?.InflationYear ?? 1950;
- return Common.Economy.GetInflationAdjustedCost(model.RunCostFactor, model.RunCostIndex, year);
- }
- }
[Category("Sprites")]
public CompanyColourType SpecialColourSchemeIndex
diff --git a/Gui/Views/CurrencyView.axaml b/Gui/Views/CurrencyView.axaml
new file mode 100644
index 00000000..9792d397
--- /dev/null
+++ b/Gui/Views/CurrencyView.axaml
@@ -0,0 +1,44 @@
+
+
+
+
+
+
+
diff --git a/Gui/Views/CurrencyView.axaml.cs b/Gui/Views/CurrencyView.axaml.cs
new file mode 100644
index 00000000..ad9bf1ad
--- /dev/null
+++ b/Gui/Views/CurrencyView.axaml.cs
@@ -0,0 +1,5 @@
+using Avalonia.Controls.Primitives;
+
+namespace Gui.Views;
+
+public class CurrencyView : TemplatedControl;
diff --git a/Gui/Views/ExtendedPropertyGrid.cs b/Gui/Views/ExtendedPropertyGrid.cs
index 10a12f8f..e13beef7 100644
--- a/Gui/Views/ExtendedPropertyGrid.cs
+++ b/Gui/Views/ExtendedPropertyGrid.cs
@@ -2,7 +2,10 @@
using Avalonia.PropertyGrid.Controls;
using Avalonia.PropertyGrid.Controls.Factories;
using Definitions.ObjectModels.Types;
+using Gui.Attributes;
using Gui.ViewModels;
+using System.ComponentModel;
+using System.Linq;
namespace Gui.Views;
@@ -11,6 +14,7 @@ public class ExtendedPropertyGrid : PropertyGrid
public ExtendedPropertyGrid()
{
Factories.AddFactory(new Pos3CellEditFactory());
+ Factories.AddFactory(new CurrencyCellEditFactory());
}
}
@@ -61,3 +65,86 @@ public override bool HandlePropertyChanged(PropertyCellContext context)
return false;
}
}
+
+internal class CurrencyCellEditFactory : AbstractCellEditFactory
+{
+ public override bool Accept(object accessToken) => accessToken is ExtendedPropertyGrid;
+
+ public override Control? HandleNewProperty(PropertyCellContext context)
+ {
+ var propertyDescriptor = context.Property;
+ var target = context.Target;
+
+ // Check if property has CurrencyAttribute
+ var currencyAttr = propertyDescriptor.Attributes.OfType().FirstOrDefault();
+ if (currencyAttr == null)
+ {
+ return null;
+ }
+
+ // Property must be int16_t (short)
+ if (propertyDescriptor.PropertyType != typeof(short))
+ {
+ return null;
+ }
+
+ var control = new CurrencyView();
+ return control;
+ }
+
+ public override bool HandlePropertyChanged(PropertyCellContext context)
+ {
+ var propertyDescriptor = context.Property;
+ var target = context.Target;
+ var control = context.CellEdit!;
+
+ var currencyAttr = propertyDescriptor.Attributes.OfType().FirstOrDefault();
+ if (currencyAttr == null || propertyDescriptor.PropertyType != typeof(short))
+ {
+ return false;
+ }
+
+ ValidateProperty(control, propertyDescriptor, target);
+
+ if (control is CurrencyView cv)
+ {
+ var costFactor = (short)propertyDescriptor.GetValue(target)!;
+
+ // Get CostIndex from the target object
+ var costIndexProperty = TypeDescriptor.GetProperties(target)[currencyAttr.CostIndexPropertyName];
+ var costIndex = costIndexProperty != null ? (byte)costIndexProperty.GetValue(target)! : (byte)0;
+
+ // Get the year from GlobalSettings or use default
+ var year = GlobalSettings.CurrentSettings?.InflationYear ?? 1950;
+
+ var model = new CurrencyEditorViewModel
+ {
+ CostFactor = costFactor,
+ CostIndex = costIndex,
+ Year = year
+ };
+
+ cv.DataContext = model;
+
+ model.PropertyChanged += (s, e) =>
+ {
+ if (e.PropertyName == nameof(CurrencyEditorViewModel.CostFactor))
+ {
+ SetAndRaise(context, control, model.CostFactor);
+ }
+ else if (e.PropertyName == nameof(CurrencyEditorViewModel.Year))
+ {
+ // Update global settings with new year
+ if (GlobalSettings.CurrentSettings != null)
+ {
+ GlobalSettings.CurrentSettings.InflationYear = model.Year;
+ }
+ }
+ };
+
+ return true;
+ }
+
+ return false;
+ }
+}
diff --git a/Tests/EffectiveCostTests.cs b/Tests/EffectiveCostTests.cs
deleted file mode 100644
index bcea4794..00000000
--- a/Tests/EffectiveCostTests.cs
+++ /dev/null
@@ -1,106 +0,0 @@
-using Definitions.ObjectModels.Objects.Track;
-using Definitions.ObjectModels.Objects.Bridge;
-using Gui;
-using Gui.ViewModels;
-using NUnit.Framework;
-
-namespace Gui.Tests;
-
-[TestFixture]
-public class EffectiveCostTests
-{
- [SetUp]
- public void Setup()
- {
- // Set up global settings
- GlobalSettings.CurrentSettings = new EditorSettings
- {
- InflationYear = 1950
- };
- }
-
- [Test]
- public void TrackViewModel_CalculatesEffectiveCosts()
- {
- var trackObject = new TrackObject
- {
- BuildCostFactor = 100,
- SellCostFactor = -50,
- TunnelCostFactor = 200,
- CostIndex = 0
- };
-
- var viewModel = new TrackViewModel(trackObject);
-
- // For year 1950, with factor 1024 and divisor 10, we should get: (100 * X) / 1024
- // where X is the inflation-adjusted currency multiplication factor
- Assert.That(viewModel.EffectiveBuildCost, Is.GreaterThan(0));
- Assert.That(viewModel.EffectiveSellCost, Is.LessThan(0));
- Assert.That(viewModel.EffectiveTunnelCost, Is.GreaterThan(0));
-
- // Build cost should be proportional to the factors
- Assert.That(viewModel.EffectiveTunnelCost, Is.GreaterThan(viewModel.EffectiveBuildCost));
- }
-
- [Test]
- public void BridgeViewModel_CalculatesEffectiveCosts()
- {
- var bridgeObject = new BridgeObject
- {
- BaseCostFactor = 150,
- HeightCostFactor = 50,
- SellCostFactor = -75,
- CostIndex = 1
- };
-
- var viewModel = new BridgeViewModel(bridgeObject);
-
- Assert.That(viewModel.EffectiveBaseCost, Is.GreaterThan(0));
- Assert.That(viewModel.EffectiveHeightCost, Is.GreaterThan(0));
- Assert.That(viewModel.EffectiveSellCost, Is.LessThan(0));
-
- // Base cost should be greater than height cost
- Assert.That(viewModel.EffectiveBaseCost, Is.GreaterThan(viewModel.EffectiveHeightCost));
- }
-
- [Test]
- public void InflationYear_AffectsCosts()
- {
- var trackObject = new TrackObject
- {
- BuildCostFactor = 100,
- CostIndex = 0
- };
-
- // Test with year 1900 (no inflation)
- GlobalSettings.CurrentSettings!.InflationYear = 1900;
- var viewModel1900 = new TrackViewModel(trackObject);
- var cost1900 = viewModel1900.EffectiveBuildCost;
-
- // Test with year 2000 (with inflation)
- GlobalSettings.CurrentSettings!.InflationYear = 2000;
- var viewModel2000 = new TrackViewModel(trackObject);
- var cost2000 = viewModel2000.EffectiveBuildCost;
-
- // Cost in 2000 should be higher due to inflation
- Assert.That(cost2000, Is.GreaterThan(cost1900));
- }
-
- [Test]
- public void GlobalSettings_DefaultYear_WorksCorrectly()
- {
- // Test that if GlobalSettings is null, default year is used
- GlobalSettings.CurrentSettings = null;
-
- var trackObject = new TrackObject
- {
- BuildCostFactor = 100,
- CostIndex = 0
- };
-
- var viewModel = new TrackViewModel(trackObject);
-
- // Should not throw and should return a valid cost
- Assert.That(viewModel.EffectiveBuildCost, Is.GreaterThan(0));
- }
-}
From 5cec6f047082c27e370636097587faa2b0378ecd Mon Sep 17 00:00:00 2001
From: Benjamin Sutas
Date: Sun, 28 Dec 2025 16:41:09 +1100
Subject: [PATCH 7/8] improve cell editor, default values, attribute and more
---
{Common => Definitions}/Economy.cs | 6 +-
Gui/App.axaml | 1 +
...bute.cs => InflatableCurrencyAttribute.cs} | 5 +-
Gui/EditorSettings.cs | 1 -
Gui/GlobalSettings.cs | 10 ---
Gui/Gui.csproj | 4 ++
Gui/Models/ObjectEditorModel.cs | 1 -
...odel.cs => InflatableCurrencyViewModel.cs} | 16 ++---
.../LocoTypes/Objects/AirportViewModel.cs | 20 +++++-
.../LocoTypes/Objects/BridgeViewModel.cs | 15 +++--
.../LocoTypes/Objects/DockViewModel.cs | 20 +++++-
.../LocoTypes/Objects/IndustryViewModel.cs | 20 +++++-
.../LocoTypes/Objects/RoadExtraViewModel.cs | 20 +++++-
.../LocoTypes/Objects/RoadStationViewModel.cs | 25 ++++++++
.../LocoTypes/Objects/RoadViewModel.cs | 32 ++++++++++
.../LocoTypes/Objects/TrackExtraViewModel.cs | 20 +++++-
.../LocoTypes/Objects/TrackSignalViewModel.cs | 20 +++++-
.../Objects/TrackStationViewModel.cs | 25 ++++++++
.../LocoTypes/Objects/TrackViewModel.cs | 27 +++++---
.../LocoTypes/Objects/TreeViewModel.cs | 20 +++++-
.../LocoTypes/Objects/VehicleViewModel.cs | 18 ++++--
.../LocoTypes/Objects/WaterViewModel.cs | 21 +++++--
Gui/Views/CurrencyView.axaml | 44 -------------
Gui/Views/ExtendedPropertyGrid.cs | 61 ++++++++-----------
Gui/Views/InflatableCurrencyView.axaml | 47 ++++++++++++++
...iew.axaml.cs => InflatableCurrencyView.cs} | 2 +-
Gui/Views/Pos3View.axaml | 7 +--
Tests/EconomyTests.cs | 4 +-
28 files changed, 367 insertions(+), 145 deletions(-)
rename {Common => Definitions}/Economy.cs (99%)
rename Gui/Attributes/{CurrencyAttribute.cs => InflatableCurrencyAttribute.cs} (61%)
delete mode 100644 Gui/GlobalSettings.cs
rename Gui/ViewModels/{CurrencyEditorViewModel.cs => InflatableCurrencyViewModel.cs} (51%)
delete mode 100644 Gui/Views/CurrencyView.axaml
create mode 100644 Gui/Views/InflatableCurrencyView.axaml
rename Gui/Views/{CurrencyView.axaml.cs => InflatableCurrencyView.cs} (51%)
diff --git a/Common/Economy.cs b/Definitions/Economy.cs
similarity index 99%
rename from Common/Economy.cs
rename to Definitions/Economy.cs
index 6128ddad..96f28466 100644
--- a/Common/Economy.cs
+++ b/Definitions/Economy.cs
@@ -1,4 +1,4 @@
-namespace Common;
+namespace Definitions;
///
/// Provides economy-related calculations for Locomotion, including inflation and cost calculations.
@@ -23,7 +23,7 @@ public static class Economy
public static uint[] CalculateCurrencyMultiplicationFactors(int year)
{
var factors = new uint[32];
-
+
// Initialize all factors to 1024 (base value)
for (var i = 0; i < 32; i++)
{
@@ -70,7 +70,7 @@ public static int GetInflationAdjustedCost(short costFactor, byte costIndex, int
var factors = CalculateCurrencyMultiplicationFactors(year);
var val = costFactor * (long)factors[costIndex];
var result = val / (1L << divisor);
-
+
return (int)result;
}
}
diff --git a/Gui/App.axaml b/Gui/App.axaml
index c80484d2..f686c07f 100644
--- a/Gui/App.axaml
+++ b/Gui/App.axaml
@@ -22,6 +22,7 @@
+
-
diff --git a/Gui/Views/ExtendedPropertyGrid.cs b/Gui/Views/ExtendedPropertyGrid.cs
index e13beef7..b5b9dcd5 100644
--- a/Gui/Views/ExtendedPropertyGrid.cs
+++ b/Gui/Views/ExtendedPropertyGrid.cs
@@ -14,13 +14,14 @@ public class ExtendedPropertyGrid : PropertyGrid
public ExtendedPropertyGrid()
{
Factories.AddFactory(new Pos3CellEditFactory());
- Factories.AddFactory(new CurrencyCellEditFactory());
+ Factories.AddFactory(new InflatableCurrencyCellEditFactory());
}
}
internal class Pos3CellEditFactory : AbstractCellEditFactory
{
- public override bool Accept(object accessToken) => accessToken is ExtendedPropertyGrid;
+ public override bool Accept(object accessToken)
+ => accessToken is ExtendedPropertyGrid;
public override Control? HandleNewProperty(PropertyCellContext context)
{
@@ -32,9 +33,7 @@ internal class Pos3CellEditFactory : AbstractCellEditFactory
return null;
}
- var control = new Pos3View();
-
- return control;
+ return new Pos3View();
}
public override bool HandlePropertyChanged(PropertyCellContext context)
@@ -50,15 +49,14 @@ public override bool HandlePropertyChanged(PropertyCellContext context)
ValidateProperty(control, propertyDescriptor, target);
- if (control is Pos3View vv)
+ if (control is Pos3View pv)
{
var pos = (Pos3)propertyDescriptor.GetValue(target)!;
var model = new Pos3ViewModel { Pos = pos };
- vv.DataContext = model;
+ pv.DataContext = model;
model.PropertyChanged += (s, e) => SetAndRaise(context, control, model.Pos);
-
return true;
}
@@ -66,30 +64,28 @@ public override bool HandlePropertyChanged(PropertyCellContext context)
}
}
-internal class CurrencyCellEditFactory : AbstractCellEditFactory
+internal class InflatableCurrencyCellEditFactory : AbstractCellEditFactory
{
- public override bool Accept(object accessToken) => accessToken is ExtendedPropertyGrid;
+ public override bool Accept(object accessToken)
+ => accessToken is ExtendedPropertyGrid;
public override Control? HandleNewProperty(PropertyCellContext context)
{
var propertyDescriptor = context.Property;
var target = context.Target;
- // Check if property has CurrencyAttribute
- var currencyAttr = propertyDescriptor.Attributes.OfType().FirstOrDefault();
+ var currencyAttr = propertyDescriptor.Attributes.OfType().FirstOrDefault();
if (currencyAttr == null)
{
return null;
}
- // Property must be int16_t (short)
if (propertyDescriptor.PropertyType != typeof(short))
{
return null;
}
- var control = new CurrencyView();
- return control;
+ return new InflatableCurrencyView();
}
public override bool HandlePropertyChanged(PropertyCellContext context)
@@ -98,7 +94,7 @@ public override bool HandlePropertyChanged(PropertyCellContext context)
var target = context.Target;
var control = context.CellEdit!;
- var currencyAttr = propertyDescriptor.Attributes.OfType().FirstOrDefault();
+ var currencyAttr = propertyDescriptor.Attributes.OfType().FirstOrDefault();
if (currencyAttr == null || propertyDescriptor.PropertyType != typeof(short))
{
return false;
@@ -106,7 +102,7 @@ public override bool HandlePropertyChanged(PropertyCellContext context)
ValidateProperty(control, propertyDescriptor, target);
- if (control is CurrencyView cv)
+ if (control is InflatableCurrencyView cv)
{
var costFactor = (short)propertyDescriptor.GetValue(target)!;
@@ -114,10 +110,18 @@ public override bool HandlePropertyChanged(PropertyCellContext context)
var costIndexProperty = TypeDescriptor.GetProperties(target)[currencyAttr.CostIndexPropertyName];
var costIndex = costIndexProperty != null ? (byte)costIndexProperty.GetValue(target)! : (byte)0;
- // Get the year from GlobalSettings or use default
- var year = GlobalSettings.CurrentSettings?.InflationYear ?? 1950;
+ var designedYearProperty = TypeDescriptor.GetProperties(target)[currencyAttr.DesignedYearPropertyName];
+ var designedYear = designedYearProperty != null ? (uint16_t)designedYearProperty.GetValue(target)! : (uint16_t)1950;
+
+ var currVm = (InflatableCurrencyViewModel)cv?.DataContext;
+ var year = currVm?.Year ?? designedYear;
+ // objects can actually set any year as designed year, even 0, so lets sanitize it
+ if (year < 1800)
+ {
+ year = 1950;
+ }
- var model = new CurrencyEditorViewModel
+ var model = new InflatableCurrencyViewModel
{
CostFactor = costFactor,
CostIndex = costIndex,
@@ -126,22 +130,7 @@ public override bool HandlePropertyChanged(PropertyCellContext context)
cv.DataContext = model;
- model.PropertyChanged += (s, e) =>
- {
- if (e.PropertyName == nameof(CurrencyEditorViewModel.CostFactor))
- {
- SetAndRaise(context, control, model.CostFactor);
- }
- else if (e.PropertyName == nameof(CurrencyEditorViewModel.Year))
- {
- // Update global settings with new year
- if (GlobalSettings.CurrentSettings != null)
- {
- GlobalSettings.CurrentSettings.InflationYear = model.Year;
- }
- }
- };
-
+ model.PropertyChanged += (s, e) => SetAndRaise(context, control, model.CostFactor);
return true;
}
diff --git a/Gui/Views/InflatableCurrencyView.axaml b/Gui/Views/InflatableCurrencyView.axaml
new file mode 100644
index 00000000..df60b966
--- /dev/null
+++ b/Gui/Views/InflatableCurrencyView.axaml
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
diff --git a/Gui/Views/CurrencyView.axaml.cs b/Gui/Views/InflatableCurrencyView.cs
similarity index 51%
rename from Gui/Views/CurrencyView.axaml.cs
rename to Gui/Views/InflatableCurrencyView.cs
index ad9bf1ad..498bcd3f 100644
--- a/Gui/Views/CurrencyView.axaml.cs
+++ b/Gui/Views/InflatableCurrencyView.cs
@@ -2,4 +2,4 @@
namespace Gui.Views;
-public class CurrencyView : TemplatedControl;
+public class InflatableCurrencyView : TemplatedControl;
diff --git a/Gui/Views/Pos3View.axaml b/Gui/Views/Pos3View.axaml
index f4772b4a..df7d8ddc 100644
--- a/Gui/Views/Pos3View.axaml
+++ b/Gui/Views/Pos3View.axaml
@@ -8,22 +8,21 @@