From 649e7e8c280269f0ef4f30486d808eb2cb219b10 Mon Sep 17 00:00:00 2001 From: RoosterDragon Date: Sat, 8 Nov 2025 12:40:56 +0000 Subject: [PATCH] Change classes that use FieldLoader to use read-only collections. --- OpenRA.Game/ExternalMods.cs | 3 +- OpenRA.Game/Exts.cs | 3 +- OpenRA.Game/FluentBundle.cs | 11 ++-- OpenRA.Game/FluentProvider.cs | 5 +- OpenRA.Game/Fonts.cs | 7 +-- OpenRA.Game/Game.cs | 3 +- OpenRA.Game/GameInformation.cs | 3 +- OpenRA.Game/GameRules/Ruleset.cs | 3 +- OpenRA.Game/GameRules/SoundInfo.cs | 33 ++++++------ OpenRA.Game/GameRules/WeaponInfo.cs | 13 ++--- OpenRA.Game/Graphics/Animation.cs | 3 +- OpenRA.Game/Graphics/ChromeProvider.cs | 6 ++- OpenRA.Game/Graphics/Palette.cs | 7 +-- OpenRA.Game/Graphics/PlatformInterfaces.cs | 2 +- OpenRA.Game/Graphics/PlayerColorRemap.cs | 6 +-- OpenRA.Game/Graphics/SpriteCache.cs | 11 ++-- OpenRA.Game/Graphics/Viewport.cs | 3 +- OpenRA.Game/HotkeyDefinition.cs | 13 ++--- OpenRA.Game/Manifest.cs | 46 +++++++++-------- OpenRA.Game/Map/Map.cs | 3 +- OpenRA.Game/Map/MapGrid.cs | 10 ++-- OpenRA.Game/Map/MapPlayers.cs | 3 +- OpenRA.Game/Map/MapPreview.cs | 30 +++++------ OpenRA.Game/Map/PlayerReference.cs | 5 +- OpenRA.Game/Map/TerrainInfo.cs | 7 +-- OpenRA.Game/MiniYaml.cs | 4 +- OpenRA.Game/Network/FluentMessage.cs | 36 ++++++------- OpenRA.Game/Network/GameServer.cs | 16 +++--- OpenRA.Game/Network/OrderManager.cs | 3 +- OpenRA.Game/Network/UnitOrders.cs | 9 ++-- OpenRA.Game/Primitives/Polygon.cs | 7 +-- OpenRA.Game/Server/Server.cs | 9 ++-- OpenRA.Game/Settings.cs | 12 +++-- OpenRA.Game/Sound/Sound.cs | 9 ++-- OpenRA.Game/Traits/TraitsInterfaces.cs | 7 +-- OpenRA.Game/Traits/World/Faction.cs | 4 +- OpenRA.Game/Widgets/Widget.cs | 7 +-- OpenRA.Game/World.cs | 3 +- .../ClassicTilesetSpecificSpriteSequence.cs | 12 +++-- OpenRA.Mods.Cnc/Graphics/ModelActorPreview.cs | 7 +-- OpenRA.Mods.Cnc/Graphics/ModelRenderable.cs | 9 ++-- OpenRA.Mods.Cnc/Graphics/UIModelRenderable.cs | 7 +-- OpenRA.Mods.Cnc/Traits/Disguise.cs | 3 +- .../PaletteEffects/LightPaletteRotator.cs | 6 ++- OpenRA.Mods.Cnc/Traits/Render/RenderVoxels.cs | 5 +- OpenRA.Mods.Cnc/Traits/Render/WithCargo.cs | 6 ++- .../Traits/Render/WithHarvesterSpriteBody.cs | 3 +- .../Render/WithLandingCraftAnimation.cs | 3 +- .../Traits/SupportPowers/DropPodsPower.cs | 6 ++- .../Traits/TransformsNearResources.cs | 3 +- .../Traits/World/JumpjetLocomotor.cs | 4 +- OpenRA.Mods.Cnc/Traits/World/ModelRenderer.cs | 13 ++--- .../Traits/World/TSEditorResourceLayer.cs | 4 +- .../Traits/World/TSResourceLayer.cs | 3 +- .../Traits/World/TSTiberiumRenderer.cs | 12 +++-- .../Traits/World/TSVeinsRenderer.cs | 3 +- .../Traits/World/WithResourceAnimation.cs | 10 ++-- .../UtilityCommands/ImportGen1MapCommand.cs | 5 +- .../UtilityCommands/RemapShpCommand.cs | 3 +- OpenRA.Mods.Cnc/Widgets/ModelWidget.cs | 9 ++-- OpenRA.Mods.Common/AIUtils.cs | 3 +- OpenRA.Mods.Common/Activities/Transform.cs | 3 +- OpenRA.Mods.Common/AssetBrowser.cs | 10 ++-- .../EditorBrushes/EditorMarkerLayerBrush.cs | 5 +- OpenRA.Mods.Common/Effects/FloatingSprite.cs | 9 ++-- .../ContentInstallerFileSystemLoader.cs | 43 ++++++++++++++-- .../FileSystem/DefaultFileSystemLoader.cs | 17 ++++++- .../Graphics/DefaultSpriteSequence.cs | 51 +++++++++---------- .../Graphics/TilesetSpecificSpriteSequence.cs | 12 +++-- OpenRA.Mods.Common/HitShapes/Polygon.cs | 5 +- OpenRA.Mods.Common/Lint/CheckChromeHotkeys.cs | 2 +- OpenRA.Mods.Common/Lint/CheckChromeLogic.cs | 3 +- .../Lint/CheckFluentReferences.cs | 5 +- OpenRA.Mods.Common/Lint/CheckFluentSyntax.cs | 3 +- OpenRA.Mods.Common/Lint/CheckInteractable.cs | 3 +- OpenRA.Mods.Common/Lint/CheckMapMetadata.cs | 3 +- OpenRA.Mods.Common/Lint/CheckPlayers.cs | 5 +- .../Lint/CheckUnknownTraitFields.cs | 3 +- .../Lint/CheckUnknownWeaponFields.cs | 3 +- .../Lint/CheckWorldAndPlayerInherits.cs | 5 +- .../MapGenerator/MapGeneratorSettings.cs | 41 ++++++++------- .../MapGenerator/Terraformer.cs | 2 +- OpenRA.Mods.Common/MapGenerator/TilingPath.cs | 7 ++- OpenRA.Mods.Common/ModContent.cs | 25 ++++----- OpenRA.Mods.Common/Projectiles/AreaBeam.cs | 7 +-- OpenRA.Mods.Common/Projectiles/Bullet.cs | 12 +++-- OpenRA.Mods.Common/Projectiles/GravityBomb.cs | 3 +- OpenRA.Mods.Common/Projectiles/Missile.cs | 5 +- OpenRA.Mods.Common/Projectiles/NukeLaunch.cs | 5 +- OpenRA.Mods.Common/Scripting/LuaScript.cs | 4 +- .../ServerTraits/LobbyCommands.cs | 7 +-- OpenRA.Mods.Common/Terrain/DefaultTerrain.cs | 48 +++++++++-------- .../Terrain/DefaultTileCache.cs | 3 +- OpenRA.Mods.Common/Terrain/TerrainInfo.cs | 11 ++-- .../Traits/AcceptsDeliveredCash.cs | 7 +-- .../Traits/AcceptsDeliveredExperience.cs | 4 +- OpenRA.Mods.Common/Traits/ActorSpawner.cs | 6 +-- OpenRA.Mods.Common/Traits/Air/Aircraft.cs | 8 +-- OpenRA.Mods.Common/Traits/AmmoPool.cs | 4 +- OpenRA.Mods.Common/Traits/Armament.cs | 5 +- .../Traits/Attack/AttackBase.cs | 3 +- .../Traits/Attack/AttackGarrisoned.cs | 15 +++--- .../Traits/Attack/AttackTurreted.cs | 3 +- OpenRA.Mods.Common/Traits/AutoTarget.cs | 14 +++-- .../Traits/BotModules/BaseBuilderBotModule.cs | 34 +++++++------ .../BotModuleLogic/BaseBuilderQueueManager.cs | 3 +- .../BotModuleLogic/MinelayerBotModule.cs | 3 +- .../BotModuleLogic/SupportPowerDecision.cs | 5 +- .../BotModules/CaptureManagerBotModule.cs | 5 +- .../Traits/BotModules/HarvesterBotModule.cs | 5 +- .../McvExpansionManagerBotModule.cs | 7 +-- .../Traits/BotModules/McvManagerBotModule.cs | 7 +-- .../Traits/BotModules/PowerDownBotManager.cs | 3 +- .../Traits/BotModules/ResourceMapBotModule.cs | 12 ++--- .../BotModules/SquadManagerBotModule.cs | 13 ++--- .../BotModules/Squads/AttackOrFleeFuzzy.cs | 1 - .../BotModules/SupportPowerBotModule.cs | 5 +- .../Traits/BotModules/UnitBuilderBotModule.cs | 12 +++-- OpenRA.Mods.Common/Traits/Buildable.cs | 7 +-- OpenRA.Mods.Common/Traits/Buildings/Bridge.cs | 10 ++-- .../Traits/Buildings/BridgeHut.cs | 6 +-- .../Traits/Buildings/BridgePlaceholder.cs | 5 +- .../Traits/Buildings/Building.cs | 14 ++--- OpenRA.Mods.Common/Traits/Buildings/Exit.cs | 3 +- .../Traits/Buildings/GivesBuildableArea.cs | 8 ++- .../Traits/Buildings/GroundLevelBridge.cs | 5 +- .../Traits/Buildings/LineBuild.cs | 3 +- .../Traits/Buildings/LineBuildNode.cs | 6 ++- .../Traits/Buildings/PlaceBuildingVariants.cs | 5 +- .../Traits/Buildings/PrimaryBuilding.cs | 4 +- .../Traits/Buildings/RallyPoint.cs | 3 +- .../Traits/Buildings/RepairableBuilding.cs | 3 +- .../Traits/Buildings/RequiresBuildableArea.cs | 4 +- .../Buildings/TransformsIntoAircraft.cs | 3 +- .../Traits/Buildings/TransformsIntoMobile.cs | 3 +- .../Buildings/TransformsIntoRepairable.cs | 3 +- OpenRA.Mods.Common/Traits/Cargo.cs | 10 ++-- OpenRA.Mods.Common/Traits/Carryall.cs | 9 ++-- .../GrantChargedConditionOnToggle.cs | 5 +- .../Conditions/GrantConditionOnAttack.cs | 6 ++- .../Conditions/GrantConditionOnBotOwner.cs | 5 +- .../Conditions/GrantConditionOnClientDock.cs | 4 +- .../Conditions/GrantConditionOnDamageState.cs | 5 +- .../Conditions/GrantConditionOnDeploy.cs | 8 +-- .../Conditions/GrantConditionOnFaction.cs | 4 +- .../Conditions/GrantConditionOnHealth.cs | 5 +- .../Conditions/GrantConditionOnHostDock.cs | 4 +- .../GrantConditionOnPrerequisite.cs | 3 +- .../Conditions/GrantConditionOnProduction.cs | 4 +- .../Conditions/GrantConditionOnTerrain.cs | 7 ++- .../Conditions/GrantConditionOnTileSet.cs | 5 +- .../Traits/Conditions/GrantRandomCondition.cs | 3 +- OpenRA.Mods.Common/Traits/Crates/Crate.cs | 3 +- .../Traits/Crates/CrateAction.cs | 7 ++- .../Traits/Crates/DuplicateUnitCrateAction.cs | 4 +- .../Traits/Crates/GiveUnitCrateAction.cs | 6 ++- OpenRA.Mods.Common/Traits/DamagedByTerrain.cs | 5 +- OpenRA.Mods.Common/Traits/DeliversCash.cs | 3 +- .../Traits/DockClientManager.cs | 3 +- .../Traits/FireProjectilesOnDeath.cs | 11 ++-- OpenRA.Mods.Common/Traits/FireWarheads.cs | 7 +-- OpenRA.Mods.Common/Traits/GainsExperience.cs | 3 +- OpenRA.Mods.Common/Traits/Harvester.cs | 3 +- OpenRA.Mods.Common/Traits/HitShape.cs | 3 +- OpenRA.Mods.Common/Traits/Husk.cs | 3 +- .../Traits/Infantry/ScaredyCat.cs | 4 +- .../Traits/Infantry/TakeCover.cs | 4 +- .../Traits/Infantry/TerrainModifiesDamage.cs | 4 +- OpenRA.Mods.Common/Traits/Interactable.cs | 15 +++--- .../Traits/IsometricSelectable.cs | 13 +++-- OpenRA.Mods.Common/Traits/KillsSelf.cs | 3 +- OpenRA.Mods.Common/Traits/MapEditorData.cs | 9 ++-- OpenRA.Mods.Common/Traits/Minelayer.cs | 3 +- OpenRA.Mods.Common/Traits/Mobile.cs | 3 +- .../Multipliers/ProductionCostMultiplier.cs | 7 +-- .../Multipliers/ProductionTimeMultiplier.cs | 7 +-- .../PaletteEffects/RotationPaletteEffect.cs | 11 ++-- .../Traits/Palettes/ColorPickerPalette.cs | 7 +-- .../Traits/Palettes/FixedColorPalette.cs | 5 +- .../Traits/Palettes/FixedPlayerColorShift.cs | 5 +- .../Traits/Palettes/IndexedPalette.cs | 7 +-- .../Traits/Palettes/IndexedPlayerPalette.cs | 9 ++-- .../Traits/Palettes/PaletteFromFile.cs | 5 +- .../Palettes/PaletteFromGimpOrJascFile.cs | 5 +- .../Traits/Palettes/PaletteFromPng.cs | 3 +- .../Traits/Palettes/PlayerColorPalette.cs | 5 +- OpenRA.Mods.Common/Traits/Parachutable.cs | 3 +- OpenRA.Mods.Common/Traits/Passenger.cs | 3 +- .../Traits/Player/BulkProductionQueue.cs | 3 +- .../Player/ClassicParallelProductionQueue.cs | 5 +- .../Traits/Player/ClassicProductionQueue.cs | 3 +- .../GrantConditionOnPrerequisiteManager.cs | 7 +-- .../Player/LobbyPrerequisiteCheckbox.cs | 5 +- .../Traits/Player/PlayerResources.cs | 6 ++- .../Traits/Player/ProductionQueue.cs | 3 +- .../Traits/Player/ProvidesPrerequisite.cs | 6 ++- .../Traits/Player/ProvidesTechPrerequisite.cs | 3 +- OpenRA.Mods.Common/Traits/Player/TechTree.cs | 7 +-- OpenRA.Mods.Common/Traits/Pluggable.cs | 12 +++-- .../Traits/ProducibleWithLevel.cs | 3 +- OpenRA.Mods.Common/Traits/Production.cs | 3 +- OpenRA.Mods.Common/Traits/Rearmable.cs | 5 +- .../Traits/RegionProximityCapturable.cs | 11 ++-- OpenRA.Mods.Common/Traits/RejectsOrders.cs | 10 ++-- .../Traits/Render/FloatingSpriteEmitter.cs | 13 ++--- .../Traits/Render/LeavesTrails.cs | 9 ++-- .../Traits/Render/ReloadArmamentsBar.cs | 3 +- .../Traits/Render/RenderSprites.cs | 3 +- .../Render/WithAircraftLandingEffect.cs | 7 +-- .../Traits/Render/WithAmmoPipsDecoration.cs | 4 +- .../Traits/Render/WithBridgeSpriteBody.cs | 9 ++-- .../Traits/Render/WithCargoPipsDecoration.cs | 3 +- .../Traits/Render/WithCrateBody.cs | 6 ++- .../Traits/Render/WithDamageOverlay.cs | 5 +- .../Traits/Render/WithDeadBridgeSpriteBody.cs | 11 ++-- .../Traits/Render/WithDeathAnimation.cs | 5 +- .../Traits/Render/WithDecorationBase.cs | 11 ++-- .../Traits/Render/WithGateSpriteBody.cs | 3 +- .../Traits/Render/WithIdleAnimation.cs | 5 +- .../Traits/Render/WithInfantryBody.cs | 8 +-- .../Traits/Render/WithMakeAnimation.cs | 3 +- .../Render/WithProductionIconOverlay.cs | 6 +-- .../Traits/Render/WithProductionOverlay.cs | 4 +- .../WithStoresResourcesPipsDecoration.cs | 3 +- OpenRA.Mods.Common/Traits/Repairable.cs | 3 +- OpenRA.Mods.Common/Traits/RepairableNear.cs | 3 +- OpenRA.Mods.Common/Traits/Replaceable.cs | 4 +- OpenRA.Mods.Common/Traits/Replacement.cs | 4 +- .../Traits/RequiresSpecificOwners.cs | 4 +- OpenRA.Mods.Common/Traits/RevealOnFire.cs | 4 +- OpenRA.Mods.Common/Traits/Sellable.cs | 3 +- .../Traits/Sound/AmbientSound.cs | 7 +-- .../Traits/Sound/AttackSounds.cs | 3 +- .../Traits/Sound/SoundOnDamageTransition.cs | 5 +- .../Traits/SpawnActorsOnSell.cs | 9 ++-- OpenRA.Mods.Common/Traits/StoresResources.cs | 7 ++- .../SupportPowers/DirectionalSupportPower.cs | 3 +- .../Traits/SupportPowers/NukePower.cs | 5 +- .../Traits/SupportPowers/ParatroopersPower.cs | 3 +- .../Traits/SupportPowers/ProduceActorPower.cs | 3 +- .../Traits/SupportPowers/SpawnActorPower.cs | 4 +- .../Traits/SupportPowers/SupportPower.cs | 3 +- OpenRA.Mods.Common/Traits/Transforms.cs | 5 +- OpenRA.Mods.Common/Traits/Wanders.cs | 4 +- .../Traits/World/ActorSpawnManager.cs | 9 ++-- .../Traits/World/BridgeLayer.cs | 3 +- .../Traits/World/BuildableTerrainOverlay.cs | 4 +- .../Traits/World/ClearMapGenerator.cs | 12 ++--- .../Traits/World/ColorPickerManager.cs | 12 +++-- .../Traits/World/ControlGroups.cs | 6 +-- .../Traits/World/CrateSpawner.cs | 10 ++-- .../Traits/World/CreateMapPlayers.cs | 3 +- .../Traits/World/EditorActorLayer.cs | 3 +- .../Traits/World/EditorResourceLayer.cs | 9 ++-- .../Traits/World/ExperimentalMapGenerator.cs | 13 ++--- .../Traits/World/LegacyBridgeLayer.cs | 3 +- OpenRA.Mods.Common/Traits/World/Locomotor.cs | 6 +-- .../Traits/World/MapStartingLocations.cs | 3 +- .../Traits/World/MapStartingUnits.cs | 7 +-- .../Traits/World/MarkerLayerOverlay.cs | 9 ++-- .../Traits/World/ResourceLayer.cs | 7 +-- .../Traits/World/ResourceRenderer.cs | 8 +-- .../Traits/World/ShroudRenderer.cs | 7 +-- .../Traits/World/SmudgeLayer.cs | 8 +-- .../Traits/World/SubterraneanLocomotor.cs | 4 +- .../Traits/World/TilingPathTool.cs | 6 +-- .../Traits/World/TimeLimitManager.cs | 8 +-- .../Traits/World/WarheadDebugOverlay.cs | 7 +-- .../Traits/World/WeatherOverlay.cs | 19 +++---- OpenRA.Mods.Common/TraitsInterfaces.cs | 18 ++++--- OpenRA.Mods.Common/UpdateRules/UpdateUtils.cs | 7 +-- OpenRA.Mods.Common/Util.cs | 7 +-- .../ConvertSpriteToPngCommand.cs | 3 +- .../UtilityCommands/DebugChromeRegions.cs | 3 +- .../UtilityCommands/ExtractMapRules.cs | 5 +- .../FuzzMapGeneratorCommand.cs | 7 +-- .../UtilityCommands/Utilities.cs | 3 +- .../Warheads/CreateEffectWarhead.cs | 5 +- .../Warheads/CreateResourceWarhead.cs | 3 +- OpenRA.Mods.Common/Warheads/DamageWarhead.cs | 4 +- .../Warheads/DestroyResourceWarhead.cs | 7 +-- .../Warheads/LeaveSmudgeWarhead.cs | 7 +-- .../Warheads/SpreadDamageWarhead.cs | 9 ++-- .../Widgets/Logic/ColorPickerLogic.cs | 3 +- .../Logic/Editor/ActorSelectorLogic.cs | 3 +- .../Logic/Editor/MapGeneratorToolLogic.cs | 2 +- .../Widgets/Logic/Editor/TileSelectorLogic.cs | 3 +- .../Widgets/Logic/Ingame/IngameMenuLogic.cs | 3 +- .../Installation/InstallFromSourceLogic.cs | 6 ++- .../Logic/Installation/ModContentLogic.cs | 2 +- .../Widgets/Logic/Lobby/MapPreviewLogic.cs | 5 +- .../Logic/Lobby/SpawnSelectorTooltipLogic.cs | 1 - .../Widgets/Logic/MapChooserLogic.cs | 5 +- .../Widgets/Logic/MapGeneratorLogic.cs | 2 +- .../Widgets/Logic/ReplayBrowserLogic.cs | 6 +-- .../Logic/Settings/HotkeysSettingsLogic.cs | 5 +- .../Widgets/MapPreviewWidget.cs | 6 +-- OpenRA.Mods.D2k/Projectiles/SonicBlast.cs | 7 +-- OpenRA.Mods.D2k/SpriteLoaders/R8Loader.cs | 3 +- OpenRA.Mods.D2k/Traits/AttractsWorms.cs | 9 ++-- .../D2kActorPreviewPlaceBuildingPreview.cs | 6 ++- .../Traits/Buildings/D2kBuilding.cs | 6 +-- OpenRA.Mods.D2k/Traits/SpiceBloom.cs | 12 +++-- .../Traits/World/D2kMapGenerator.cs | 13 ++--- OpenRA.Platforms.Default/Shader.cs | 4 +- .../ThreadedGraphicsContext.cs | 2 +- OpenRA.Server/Program.cs | 3 +- OpenRA.Utility/Program.cs | 3 +- 308 files changed, 1233 insertions(+), 901 deletions(-) diff --git a/OpenRA.Game/ExternalMods.cs b/OpenRA.Game/ExternalMods.cs index 40dff9d933..ced98bbaa8 100644 --- a/OpenRA.Game/ExternalMods.cs +++ b/OpenRA.Game/ExternalMods.cs @@ -12,6 +12,7 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Collections.Immutable; using System.IO; using System.Linq; using OpenRA.FileFormats; @@ -28,7 +29,7 @@ namespace OpenRA public readonly string Id; public readonly string Version; public readonly string LaunchPath; - public readonly string[] LaunchArgs; + public readonly ImmutableArray LaunchArgs; public Sprite Icon { get; internal set; } public Sprite Icon2x { get; internal set; } public Sprite Icon3x { get; internal set; } diff --git a/OpenRA.Game/Exts.cs b/OpenRA.Game/Exts.cs index 4046a91a3e..934f2f21ed 100644 --- a/OpenRA.Game/Exts.cs +++ b/OpenRA.Game/Exts.cs @@ -11,6 +11,7 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Globalization; using System.Linq; using System.Reflection; @@ -78,7 +79,7 @@ namespace OpenRA return Math.Sign((v1.X - v0.X) * (p.Y - v0.Y) - (p.X - v0.X) * (v1.Y - v0.Y)); } - public static bool PolygonContains(this int2[] polygon, int2 p) + public static bool PolygonContains(this ImmutableArray polygon, int2 p) { var windingNumber = 0; diff --git a/OpenRA.Game/FluentBundle.cs b/OpenRA.Game/FluentBundle.cs index b132c4f009..33ff64b6bb 100644 --- a/OpenRA.Game/FluentBundle.cs +++ b/OpenRA.Game/FluentBundle.cs @@ -11,6 +11,7 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Globalization; using System.IO; using Linguini.Bundle; @@ -87,19 +88,19 @@ namespace OpenRA { readonly Linguini.Bundle.FluentBundle bundle; - public FluentBundle(string culture, string[] paths, IReadOnlyFileSystem fileSystem) + public FluentBundle(string culture, ImmutableArray paths, IReadOnlyFileSystem fileSystem) : this(culture, paths, fileSystem, error => Log.Write("debug", error.Message)) { } - public FluentBundle(string culture, string[] paths, IReadOnlyFileSystem fileSystem, string text) + public FluentBundle(string culture, ImmutableArray paths, IReadOnlyFileSystem fileSystem, string text) : this(culture, paths, fileSystem, text, error => Log.Write("debug", error.Message)) { } - public FluentBundle(string culture, string[] paths, IReadOnlyFileSystem fileSystem, Action onError) + public FluentBundle(string culture, ImmutableArray paths, IReadOnlyFileSystem fileSystem, Action onError) : this(culture, paths, fileSystem, null, onError) { } public FluentBundle(string culture, string text, Action onError) - : this(culture, null, null, text, onError) { } + : this(culture, default, null, text, onError) { } - public FluentBundle(string culture, string[] paths, IReadOnlyFileSystem fileSystem, string text, Action onError) + public FluentBundle(string culture, ImmutableArray paths, IReadOnlyFileSystem fileSystem, string text, Action onError) { bundle = LinguiniBuilder.Builder() .CultureInfo(new CultureInfo(culture)) diff --git a/OpenRA.Game/FluentProvider.cs b/OpenRA.Game/FluentProvider.cs index 6dba8e9086..88b24efdc5 100644 --- a/OpenRA.Game/FluentProvider.cs +++ b/OpenRA.Game/FluentProvider.cs @@ -10,6 +10,7 @@ #endregion using System; +using System.Collections.Immutable; using System.Text; using OpenRA.FileSystem; @@ -29,9 +30,9 @@ namespace OpenRA modFluentBundle = new FluentBundle(modData.Manifest.FluentCulture, modData.Manifest.FluentMessages, fileSystem); if (fileSystem is Map map && map.FluentMessageDefinitions != null) { - var files = Array.Empty(); + var files = ImmutableArray.Empty; if (map.FluentMessageDefinitions.Value != null) - files = FieldLoader.GetValue("value", map.FluentMessageDefinitions.Value); + files = FieldLoader.GetValue>("value", map.FluentMessageDefinitions.Value); string text = null; if (map.FluentMessageDefinitions.Nodes.Length > 0) diff --git a/OpenRA.Game/Fonts.cs b/OpenRA.Game/Fonts.cs index c1edafd1b7..a068552129 100644 --- a/OpenRA.Game/Fonts.cs +++ b/OpenRA.Game/Fonts.cs @@ -9,6 +9,7 @@ */ #endregion +using System.Collections.Frozen; using System.Collections.Generic; namespace OpenRA @@ -23,15 +24,15 @@ namespace OpenRA public class Fonts : IGlobalModData { [FieldLoader.LoadUsing(nameof(LoadFonts))] - public readonly Dictionary FontList; + public readonly FrozenDictionary FontList; static object LoadFonts(MiniYaml y) { - var ret = new Dictionary(); + var ret = new Dictionary(y.Nodes.Length); foreach (var node in y.Nodes) ret.Add(node.Key, FieldLoader.Load(node.Value)); - return ret; + return ret.ToFrozenDictionary(); } } } diff --git a/OpenRA.Game/Game.cs b/OpenRA.Game/Game.cs index 5760109cc0..131e89c8a8 100644 --- a/OpenRA.Game/Game.cs +++ b/OpenRA.Game/Game.cs @@ -11,6 +11,7 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Diagnostics; using System.Globalization; using System.IO; @@ -415,7 +416,7 @@ namespace OpenRA var modSearchArg = args.GetValue("Engine.ModSearchPaths", null); var modSearchPaths = modSearchArg != null ? - FieldLoader.GetValue("Engine.ModsPath", modSearchArg) : + FieldLoader.GetValue>("Engine.ModsPath", modSearchArg) : [Path.Combine(Platform.EngineDir, "mods")]; Mods = new InstalledMods(modSearchPaths, explicitModPaths); diff --git a/OpenRA.Game/GameInformation.cs b/OpenRA.Game/GameInformation.cs index aee285be3a..b200bd962b 100644 --- a/OpenRA.Game/GameInformation.cs +++ b/OpenRA.Game/GameInformation.cs @@ -10,6 +10,7 @@ #endregion using System; +using System.Collections.Frozen; using System.Collections.Generic; using System.Linq; using OpenRA.FileSystem; @@ -41,7 +42,7 @@ namespace OpenRA public TimeSpan Duration => EndTimeUtc > StartTimeUtc ? EndTimeUtc - StartTimeUtc : TimeSpan.Zero; public IList Players { get; } - public HashSet DisabledSpawnPoints = []; + public FrozenSet DisabledSpawnPoints = FrozenSet.Empty; public MapPreview MapPreview { diff --git a/OpenRA.Game/GameRules/Ruleset.cs b/OpenRA.Game/GameRules/Ruleset.cs index de6cd72147..31902afe76 100644 --- a/OpenRA.Game/GameRules/Ruleset.cs +++ b/OpenRA.Game/GameRules/Ruleset.cs @@ -11,6 +11,7 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using System.Threading.Tasks; using OpenRA.FileSystem; @@ -268,7 +269,7 @@ namespace OpenRA if (mapRules.Value != null) { - var mapFiles = FieldLoader.GetValue("value", mapRules.Value); + var mapFiles = FieldLoader.GetValue>("value", mapRules.Value); foreach (var f in mapFiles) if (AnyFlaggedTraits(modData, MiniYaml.FromStream(fileSystem.Open(f), f))) return true; diff --git a/OpenRA.Game/GameRules/SoundInfo.cs b/OpenRA.Game/GameRules/SoundInfo.cs index 8f704d54b6..1a1ba17491 100644 --- a/OpenRA.Game/GameRules/SoundInfo.cs +++ b/OpenRA.Game/GameRules/SoundInfo.cs @@ -10,37 +10,38 @@ #endregion using System; +using System.Collections.Frozen; using System.Collections.Generic; -using System.Linq; +using System.Collections.Immutable; namespace OpenRA.GameRules { public class SoundInfo { - public readonly Dictionary Variants = []; - public readonly Dictionary Prefixes = []; - public readonly Dictionary Voices = []; - public readonly Dictionary Notifications = []; + public readonly FrozenDictionary> Variants = FrozenDictionary>.Empty; + public readonly FrozenDictionary> Prefixes = FrozenDictionary>.Empty; + public readonly FrozenDictionary> Voices = FrozenDictionary>.Empty; + public readonly FrozenDictionary> Notifications = FrozenDictionary>.Empty; public readonly string DefaultVariant = ".aud"; public readonly string DefaultPrefix = ""; - public readonly HashSet DisableVariants = []; - public readonly HashSet DisablePrefixes = []; + public readonly FrozenSet DisableVariants = FrozenSet.Empty; + public readonly FrozenSet DisablePrefixes = FrozenSet.Empty; - public readonly Lazy> VoicePools; - public readonly Lazy> NotificationsPools; + public readonly Lazy> VoicePools; + public readonly Lazy> NotificationsPools; public SoundInfo(MiniYaml y) { FieldLoader.Load(this, y); - VoicePools = Exts.Lazy(() => Voices.ToDictionary(a => a.Key, a => new SoundPool(1f, SoundPool.DefaultInterruptType, a.Value))); + VoicePools = Exts.Lazy(() => Voices.ToFrozenDictionary(a => a.Key, a => new SoundPool(1f, SoundPool.DefaultInterruptType, a.Value))); NotificationsPools = Exts.Lazy(() => ParseSoundPool(y, "Notifications")); } - static Dictionary ParseSoundPool(MiniYaml y, string key) + static FrozenDictionary ParseSoundPool(MiniYaml y, string key) { - var ret = new Dictionary(); var classifiction = y.NodeWithKey(key); + var ret = new Dictionary(classifiction.Value.Nodes.Length); foreach (var t in classifiction.Value.Nodes) { var volumeModifier = 1f; @@ -53,12 +54,12 @@ namespace OpenRA.GameRules if (interruptTypeNode != null) interruptType = FieldLoader.GetValue(interruptTypeNode.Key, interruptTypeNode.Value.Value); - var names = FieldLoader.GetValue(t.Key, t.Value.Value); + var names = FieldLoader.GetValue>(t.Key, t.Value.Value); var sp = new SoundPool(volumeModifier, interruptType, names); ret.Add(t.Key, sp); } - return ret; + return ret.ToFrozenDictionary(); } } @@ -68,10 +69,10 @@ namespace OpenRA.GameRules public const InterruptType DefaultInterruptType = InterruptType.DoNotPlay; public readonly float VolumeModifier; public readonly InterruptType Type; - readonly string[] clips; + readonly ImmutableArray clips; readonly List liveclips = []; - public SoundPool(float volumeModifier, InterruptType interruptType, params string[] clips) + public SoundPool(float volumeModifier, InterruptType interruptType, ImmutableArray clips) { VolumeModifier = volumeModifier; Type = interruptType; diff --git a/OpenRA.Game/GameRules/WeaponInfo.cs b/OpenRA.Game/GameRules/WeaponInfo.cs index adb8c26e69..ea1a8c608b 100644 --- a/OpenRA.Game/GameRules/WeaponInfo.cs +++ b/OpenRA.Game/GameRules/WeaponInfo.cs @@ -11,6 +11,7 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using OpenRA.Effects; using OpenRA.Primitives; @@ -82,13 +83,13 @@ namespace OpenRA.GameRules public readonly WVec FollowingBurstTargetOffset = WVec.Zero; [Desc("The sound played each time the weapon is fired.")] - public readonly string[] Report = null; + public readonly ImmutableArray Report = default; [Desc("Sound played only on first burst in a salvo.")] - public readonly string[] StartBurstReport = null; + public readonly ImmutableArray StartBurstReport = default; [Desc("The sound played when the weapon is reloaded.")] - public readonly string[] AfterFireSound = null; + public readonly ImmutableArray AfterFireSound = default; [Desc("Delay in ticks to play reloading sound.")] public readonly int AfterFireSoundDelay = 0; @@ -116,7 +117,7 @@ namespace OpenRA.GameRules [Desc("Delay in ticks between firing shots from the same ammo magazine. If one entry, it will be used for all bursts.", "If multiple entries, their number needs to match Burst - 1.")] - public readonly int[] BurstDelays = [5]; + public readonly ImmutableArray BurstDelays = [5]; [Desc("The minimum range the weapon can fire.")] public readonly WDist MinRange = WDist.Zero; @@ -128,7 +129,7 @@ namespace OpenRA.GameRules public readonly IProjectileInfo Projectile; [FieldLoader.LoadUsing(nameof(LoadWarheads))] - public readonly List Warheads = []; + public readonly ImmutableArray Warheads = []; /// /// This constructor is used solely for documentation generation. @@ -170,7 +171,7 @@ namespace OpenRA.GameRules retList.Add(ret); } - return retList; + return retList.ToImmutableArray(); } public bool IsValidTarget(BitSet targetTypes) diff --git a/OpenRA.Game/Graphics/Animation.cs b/OpenRA.Game/Graphics/Animation.cs index 9397f2058f..20fc20710b 100644 --- a/OpenRA.Game/Graphics/Animation.cs +++ b/OpenRA.Game/Graphics/Animation.cs @@ -10,6 +10,7 @@ #endregion using System; +using System.Collections.Immutable; using System.Linq; using OpenRA.Primitives; using OpenRA.Support; @@ -250,7 +251,7 @@ namespace OpenRA.Graphics return sequences.GetSequence(Name, sequenceName); } - public string GetRandomExistingSequence(string[] sequences, MersenneTwister random) + public string GetRandomExistingSequence(ImmutableArray sequences, MersenneTwister random) { return sequences.Where(HasSequence).RandomOrDefault(random); } diff --git a/OpenRA.Game/Graphics/ChromeProvider.cs b/OpenRA.Game/Graphics/ChromeProvider.cs index ba20c69d40..e43806fa0f 100644 --- a/OpenRA.Game/Graphics/ChromeProvider.cs +++ b/OpenRA.Game/Graphics/ChromeProvider.cs @@ -10,7 +10,9 @@ #endregion using System; +using System.Collections.Frozen; using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using OpenRA.FileSystem; using OpenRA.Primitives; @@ -47,9 +49,9 @@ namespace OpenRA.Graphics public readonly string Image2x = null; public readonly string Image3x = null; - public readonly int[] PanelRegion = null; + public readonly ImmutableArray PanelRegion = default; public readonly PanelSides PanelSides = PanelSides.All; - public readonly Dictionary Regions = []; + public readonly FrozenDictionary Regions = FrozenDictionary.Empty; } public static IReadOnlyDictionary Collections => collections; diff --git a/OpenRA.Game/Graphics/Palette.cs b/OpenRA.Game/Graphics/Palette.cs index 4d71d4a59a..4d3c282715 100644 --- a/OpenRA.Game/Graphics/Palette.cs +++ b/OpenRA.Game/Graphics/Palette.cs @@ -11,6 +11,7 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.IO; using OpenRA.Primitives; @@ -64,18 +65,18 @@ namespace OpenRA.Graphics Buffer.BlockCopy(colors, 0, destination, destinationOffset * 4, Palette.Size * 4); } - public ImmutablePalette(string filename, int[] remapTransparent, int[] remap) + public ImmutablePalette(string filename, ImmutableArray remapTransparent, ImmutableArray remap) { using (var s = File.OpenRead(filename)) LoadFromStream(s, remapTransparent, remap); } - public ImmutablePalette(Stream s, int[] remapTransparent, int[] remapShadow) + public ImmutablePalette(Stream s, ImmutableArray remapTransparent, ImmutableArray remapShadow) { LoadFromStream(s, remapTransparent, remapShadow); } - void LoadFromStream(Stream s, int[] remapTransparent, int[] remapShadow) + void LoadFromStream(Stream s, ImmutableArray remapTransparent, ImmutableArray remapShadow) { using (var reader = new BinaryReader(s)) for (var i = 0; i < Palette.Size; i++) diff --git a/OpenRA.Game/Graphics/PlatformInterfaces.cs b/OpenRA.Game/Graphics/PlatformInterfaces.cs index 37876ae506..f4594b520a 100644 --- a/OpenRA.Game/Graphics/PlatformInterfaces.cs +++ b/OpenRA.Game/Graphics/PlatformInterfaces.cs @@ -135,7 +135,7 @@ namespace OpenRA void SetVec(string name, float x); void SetVec(string name, float x, float y); void SetVec(string name, float x, float y, float z); - void SetVec(string name, float[] vec, int length); + void SetVec(string name, ReadOnlyMemory vec, int length); void SetTexture(string param, ITexture texture); void SetMatrix(string param, float[] mtx); void PrepareRender(); diff --git a/OpenRA.Game/Graphics/PlayerColorRemap.cs b/OpenRA.Game/Graphics/PlayerColorRemap.cs index fe21631227..6a2db545fb 100644 --- a/OpenRA.Game/Graphics/PlayerColorRemap.cs +++ b/OpenRA.Game/Graphics/PlayerColorRemap.cs @@ -10,19 +10,19 @@ #endregion using System; -using System.Linq; +using System.Collections.Immutable; using OpenRA.Primitives; namespace OpenRA.Graphics { public class PlayerColorRemap : IPaletteRemap { - readonly int[] remapIndices; + readonly ImmutableArray remapIndices; readonly float hue; readonly float saturation; readonly float value; - public PlayerColorRemap(int[] remapIndices, Color color) + public PlayerColorRemap(ImmutableArray remapIndices, Color color) { this.remapIndices = remapIndices; diff --git a/OpenRA.Game/Graphics/SpriteCache.cs b/OpenRA.Game/Graphics/SpriteCache.cs index afa82f47b8..c177ca2697 100644 --- a/OpenRA.Game/Graphics/SpriteCache.cs +++ b/OpenRA.Game/Graphics/SpriteCache.cs @@ -11,6 +11,7 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.IO; using System.Linq; using OpenRA.FileSystem; @@ -27,7 +28,7 @@ namespace OpenRA.Graphics readonly Dictionary< int, - (int[] Frames, MiniYamlNode.SourceLocation Location, AdjustFrame AdjustFrame, bool Premultiplied)> spriteReservations = []; + (ImmutableArray Frames, MiniYamlNode.SourceLocation Location, AdjustFrame AdjustFrame, bool Premultiplied)> spriteReservations = []; readonly Dictionary> reservationsByFilename = []; readonly Dictionary resolvedSprites = []; @@ -49,11 +50,11 @@ namespace OpenRA.Graphics this.loaders = loaders; } - public int ReserveSprites(string filename, IEnumerable frames, MiniYamlNode.SourceLocation location, + public int ReserveSprites(string filename, ImmutableArray frames, MiniYamlNode.SourceLocation location, AdjustFrame adjustFrame = null, bool premultiplied = false) { var token = nextReservationToken++; - spriteReservations[token] = (frames?.ToArray(), location, adjustFrame, premultiplied); + spriteReservations[token] = (frames, location, adjustFrame, premultiplied); reservationsByFilename.GetOrAdd(filename, _ => []).Add(token); return token; } @@ -103,8 +104,8 @@ namespace OpenRA.Graphics throw new InvalidOperationException($"{rs.Location}: {filename} does not contain frames: " + string.Join(',', rs.Frames.Where(f => f >= loadedFrames.Length))); - var frames = rs.Frames ?? Enumerable.Range(0, loadedFrames.Length); - var total = rs.Frames?.Length ?? loadedFrames.Length; + var frames = rs.Frames != null ? rs.Frames : Enumerable.Range(0, loadedFrames.Length); + var total = rs.Frames != null ? rs.Frames.Length : loadedFrames.Length; var j = 0; foreach (var i in frames) diff --git a/OpenRA.Game/Graphics/Viewport.cs b/OpenRA.Game/Graphics/Viewport.cs index 224d0aa40b..f6d7861926 100644 --- a/OpenRA.Game/Graphics/Viewport.cs +++ b/OpenRA.Game/Graphics/Viewport.cs @@ -11,6 +11,7 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using OpenRA.Primitives; @@ -268,7 +269,7 @@ namespace OpenRA.Graphics { var ramp = map.Grid.Ramps[map.Ramp.Contains(uv) ? map.Ramp[uv] : 0]; var pos = map.CenterOfCell(uv.ToCPos(map)) - new WVec(0, 0, ramp.CenterHeightOffset); - var screen = ramp.Corners.Select(c => worldRenderer.ScreenPxPosition(pos + c)).ToArray(); + var screen = ramp.Corners.Select(c => worldRenderer.ScreenPxPosition(pos + c)).ToImmutableArray(); if (screen.PolygonContains(world)) return uv.ToCPos(map); } diff --git a/OpenRA.Game/HotkeyDefinition.cs b/OpenRA.Game/HotkeyDefinition.cs index 5228d51bea..5c0b09beb0 100644 --- a/OpenRA.Game/HotkeyDefinition.cs +++ b/OpenRA.Game/HotkeyDefinition.cs @@ -9,7 +9,8 @@ */ #endregion -using System.Collections.Generic; +using System.Collections.Frozen; +using System.Collections.Immutable; using System.Linq; namespace OpenRA @@ -24,10 +25,10 @@ namespace OpenRA [FluentReference] public readonly string Description = ""; - public readonly HashSet Types = []; + public readonly FrozenSet Types = FrozenSet.Empty; [FluentReference] - public readonly HashSet Contexts = []; + public readonly FrozenSet Contexts = FrozenSet.Empty; public readonly bool Readonly = false; public bool HasDuplicates { get; internal set; } @@ -45,11 +46,11 @@ namespace OpenRA Description = descriptionYaml.Value; if (nodeDict.TryGetValue("Types", out var typesYaml)) - Types = FieldLoader.GetValue>("Types", typesYaml.Value); + Types = FieldLoader.GetValue>("Types", typesYaml.Value); if (nodeDict.TryGetValue("Contexts", out var contextYaml)) - Contexts = FieldLoader.GetValue>("Contexts", contextYaml.Value) - .Select(c => ContextFluentPrefix + "." + c).ToHashSet(); + Contexts = FieldLoader.GetValue>("Contexts", contextYaml.Value) + .Select(c => ContextFluentPrefix + "." + c).ToFrozenSet(); if (nodeDict.TryGetValue("Platform", out var platformYaml)) { diff --git a/OpenRA.Game/Manifest.cs b/OpenRA.Game/Manifest.cs index 5f753caada..adee445e74 100644 --- a/OpenRA.Game/Manifest.cs +++ b/OpenRA.Game/Manifest.cs @@ -10,7 +10,9 @@ #endregion using System; +using System.Collections.Frozen; using System.Collections.Generic; +using System.Collections.Immutable; using System.Collections.ObjectModel; using System.IO; using System.Linq; @@ -68,22 +70,22 @@ namespace OpenRA public readonly string Id; public readonly IReadOnlyPackage Package; public readonly ModMetadata Metadata; - public readonly string[] + public readonly ImmutableArray Rules, ServerTraits, Sequences, ModelSequences, Cursors, Chrome, ChromeLayout, Weapons, Voices, Notifications, Music, FluentMessages, TileSets, ChromeMetrics, MapCompatibility, Missions, Hotkeys; - public readonly IReadOnlyDictionary MapFolders; + public readonly FrozenDictionary MapFolders; public readonly MiniYaml FileSystem; public readonly MiniYaml LoadScreen; public readonly string DefaultOrderGenerator; - public readonly string[] Assemblies = []; - public readonly string[] SoundFormats = []; - public readonly string[] SpriteFormats = []; - public readonly string[] PackageFormats = []; - public readonly string[] VideoFormats = []; + public readonly ImmutableArray Assemblies = []; + public readonly ImmutableArray SoundFormats = []; + public readonly ImmutableArray SpriteFormats = []; + public readonly ImmutableArray PackageFormats = []; + public readonly ImmutableArray VideoFormats = []; public readonly int FontSheetSize = 512; public readonly int CursorSheetSize = 512; @@ -91,14 +93,14 @@ namespace OpenRA public readonly string FluentCulture = "en"; public readonly bool AllowUnusedFluentMessagesInExternalPackages = true; - readonly string[] reservedModuleNames = - [ + static readonly FrozenSet ReservedModuleNames = new HashSet + { "Include", "Metadata", "FileSystem", "MapFolders", "Rules", "Sequences", "ModelSequences", "Cursors", "Chrome", "Assemblies", "ChromeLayout", "Weapons", "Voices", "Notifications", "Music", "FluentMessages", "TileSets", "ChromeMetrics", "Missions", "Hotkeys", "ServerTraits", "LoadScreen", "DefaultOrderGenerator", "SupportsMapsFrom", "SoundFormats", "SpriteFormats", "VideoFormats", "RequiresMods", "PackageFormats", "AllowUnusedFluentMessagesInExternalPackages", "FontSheetSize", "CursorSheetSize" - ]; + }.ToFrozenSet(); readonly TypeDictionary modules = []; readonly Dictionary yaml; @@ -165,25 +167,25 @@ namespace OpenRA if (yaml.TryGetValue("SupportsMapsFrom", out var entry)) compat.AddRange(entry.Value.Split(',').Select(c => c.Trim())); - MapCompatibility = compat.ToArray(); + MapCompatibility = compat.ToImmutableArray(); if (yaml.TryGetValue("DefaultOrderGenerator", out entry)) DefaultOrderGenerator = entry.Value; if (yaml.TryGetValue("Assemblies", out entry)) - Assemblies = FieldLoader.GetValue("Assemblies", entry.Value); + Assemblies = FieldLoader.GetValue>("Assemblies", entry.Value); if (yaml.TryGetValue("PackageFormats", out entry)) - PackageFormats = FieldLoader.GetValue("PackageFormats", entry.Value); + PackageFormats = FieldLoader.GetValue>("PackageFormats", entry.Value); if (yaml.TryGetValue("SoundFormats", out entry)) - SoundFormats = FieldLoader.GetValue("SoundFormats", entry.Value); + SoundFormats = FieldLoader.GetValue>("SoundFormats", entry.Value); if (yaml.TryGetValue("SpriteFormats", out entry)) - SpriteFormats = FieldLoader.GetValue("SpriteFormats", entry.Value); + SpriteFormats = FieldLoader.GetValue>("SpriteFormats", entry.Value); if (yaml.TryGetValue("VideoFormats", out entry)) - VideoFormats = FieldLoader.GetValue("VideoFormats", entry.Value); + VideoFormats = FieldLoader.GetValue>("VideoFormats", entry.Value); if (yaml.TryGetValue("AllowUnusedFluentMessagesInExternalPackages", out entry)) AllowUnusedFluentMessagesInExternalPackages = @@ -200,7 +202,7 @@ namespace OpenRA { foreach (var kv in yaml) { - if (reservedModuleNames.Contains(kv.Key)) + if (ReservedModuleNames.Contains(kv.Key)) continue; var t = oc.FindType(kv.Key); @@ -227,20 +229,20 @@ namespace OpenRA customDataLoaded = true; } - static string[] YamlList(Dictionary yaml, string key) + static ImmutableArray YamlList(Dictionary yaml, string key) { if (!yaml.TryGetValue(key, out var value)) return []; - return value.Nodes.Select(n => n.Key).ToArray(); + return value.Nodes.Select(n => n.Key).ToImmutableArray(); } - static IReadOnlyDictionary YamlDictionary(Dictionary yaml, string key) + static FrozenDictionary YamlDictionary(Dictionary yaml, string key) { if (!yaml.TryGetValue(key, out var value)) - return new Dictionary(); + return FrozenDictionary.Empty; - return value.ToDictionary(my => my.Value); + return value.ToDictionary(my => my.Value).ToFrozenDictionary(); } public bool Contains() where T : IGlobalModData diff --git a/OpenRA.Game/Map/Map.cs b/OpenRA.Game/Map/Map.cs index f19f38fb35..fb4537eb24 100644 --- a/OpenRA.Game/Map/Map.cs +++ b/OpenRA.Game/Map/Map.cs @@ -11,6 +11,7 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.IO; using System.Linq; using System.Reflection; @@ -192,7 +193,7 @@ namespace OpenRA public bool LockPreview; public Rectangle Bounds; public MapVisibility Visibility = MapVisibility.Lobby; - public string[] Categories = ["Conquest"]; + public ImmutableArray Categories = ["Conquest"]; public Size MapSize { get; private set; } diff --git a/OpenRA.Game/Map/MapGrid.cs b/OpenRA.Game/Map/MapGrid.cs index 50827697b2..f32043c5e5 100644 --- a/OpenRA.Game/Map/MapGrid.cs +++ b/OpenRA.Game/Map/MapGrid.cs @@ -117,7 +117,7 @@ namespace OpenRA public readonly bool EnableDepthBuffer = false; - public readonly WVec[] SubCellOffsets = + public readonly ImmutableArray SubCellOffsets = [ new(0, 0, 0), // full cell - index 0 new(-299, -256, 0), // top left - index 1 @@ -127,9 +127,9 @@ namespace OpenRA new(256, 256, 0), // bottom right - index 5 ]; - public CellRamp[] Ramps { get; } + public ImmutableArray Ramps { get; } - internal readonly CVec[][] TilesByDistance; + internal readonly ImmutableArray> TilesByDistance; public int TileScale { get; } @@ -202,7 +202,7 @@ namespace OpenRA TilesByDistance = CreateTilesByDistance(); } - CVec[][] CreateTilesByDistance() + ImmutableArray> CreateTilesByDistance() { var ts = new List[MaximumTileSearchRange + 1]; for (var i = 0; i < MaximumTileSearchRange + 1; i++) @@ -238,7 +238,7 @@ namespace OpenRA }); } - return ts.Select(list => list.ToArray()).ToArray(); + return ts.Select(list => list.ToImmutableArray()).ToImmutableArray(); } public WVec OffsetOfSubCell(SubCell subCell) diff --git a/OpenRA.Game/Map/MapPlayers.cs b/OpenRA.Game/Map/MapPlayers.cs index 7e26d1549c..476bdd6218 100644 --- a/OpenRA.Game/Map/MapPlayers.cs +++ b/OpenRA.Game/Map/MapPlayers.cs @@ -10,6 +10,7 @@ #endregion using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using OpenRA.Traits; @@ -54,7 +55,7 @@ namespace OpenRA Name = "Creeps", Faction = firstFaction, NonCombatant = true, - Enemies = Exts.MakeArray(playerCount, i => $"Multi{i}") + Enemies = Exts.MakeArray(playerCount, i => $"Multi{i}").ToImmutableArray() } } }; diff --git a/OpenRA.Game/Map/MapPreview.cs b/OpenRA.Game/Map/MapPreview.cs index 9c951275db..a78cc9f31a 100644 --- a/OpenRA.Game/Map/MapPreview.cs +++ b/OpenRA.Game/Map/MapPreview.cs @@ -11,6 +11,7 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; @@ -49,10 +50,10 @@ namespace OpenRA { public readonly string title; public readonly string author; - public readonly string[] categories; + public readonly ImmutableArray categories; public readonly int players; public readonly Rectangle bounds; - public readonly short[] spawnpoints = []; + public readonly ImmutableArray spawnpoints = []; public readonly MapGridType map_grid_type; public readonly string minimap; public readonly bool downloading; @@ -70,12 +71,12 @@ namespace OpenRA { public int MapFormat; public string Title; - public string[] Categories; + public ImmutableArray Categories; public string Author; public string TileSet; public MapPlayers Players; public int PlayerCount; - public CPos[] SpawnPoints; + public ImmutableArray SpawnPoints; public MapGridType GridType; public Rectangle Bounds; public Png Preview; @@ -129,9 +130,9 @@ namespace OpenRA { if (FluentMessageDefinitions != null) { - var files = Array.Empty(); + var files = ImmutableArray.Empty; if (FluentMessageDefinitions.Value != null) - files = FieldLoader.GetValue("value", FluentMessageDefinitions.Value); + files = FieldLoader.GetValue>("value", FluentMessageDefinitions.Value); string text = null; if (FluentMessageDefinitions.Nodes.Length > 0) @@ -157,8 +158,8 @@ namespace OpenRA var files = Enumerable.Empty(); if (RuleDefinitions.Value != null) { - var mapFiles = FieldLoader.GetValue("value", RuleDefinitions.Value); - files = files.Append(mapFiles); + var mapFiles = FieldLoader.GetValue>("value", RuleDefinitions.Value); + files = files.Concat(mapFiles); } var stringPool = new HashSet(); // Reuse common strings in YAML @@ -196,7 +197,6 @@ namespace OpenRA } } - static readonly CPos[] NoSpawns = []; readonly object syncRoot = new(); readonly MapCache cache; readonly ModData modData; @@ -238,12 +238,12 @@ namespace OpenRA public int MapFormat => innerData.MapFormat; public string Title => innerData.Title; - public string[] Categories => innerData.Categories; + public ImmutableArray Categories => innerData.Categories; public string Author => innerData.Author; public string TileSet => innerData.TileSet; public MapPlayers Players => innerData.Players; public int PlayerCount => innerData.PlayerCount; - public CPos[] SpawnPoints => innerData.SpawnPoints; + public ImmutableArray SpawnPoints => innerData.SpawnPoints; public MapGridType GridType => innerData.GridType; public Rectangle Bounds => innerData.Bounds; public Png Preview => innerData.Preview; @@ -341,7 +341,7 @@ namespace OpenRA TileSet = "unknown", Players = null, PlayerCount = 0, - SpawnPoints = NoSpawns, + SpawnPoints = [], GridType = gridType, Bounds = Rectangle.Empty, Preview = null, @@ -397,7 +397,7 @@ namespace OpenRA newData.Title = temp.Value; if (yaml.TryGetValue("Categories", out temp)) - newData.Categories = FieldLoader.GetValue("Categories", temp.Value); + newData.Categories = FieldLoader.GetValue>("Categories", temp.Value); if (yaml.TryGetValue("Tileset", out temp)) newData.TileSet = temp.Value; @@ -433,7 +433,7 @@ namespace OpenRA spawns.Add(s.Get().Value); } - newData.SpawnPoints = spawns.ToArray(); + newData.SpawnPoints = spawns.ToImmutableArray(); } else newData.SpawnPoints = []; @@ -526,7 +526,7 @@ namespace OpenRA var spawns = new CPos[r.spawnpoints.Length / 2]; for (var j = 0; j < r.spawnpoints.Length; j += 2) spawns[j / 2] = new CPos(r.spawnpoints[j], r.spawnpoints[j + 1]); - newData.SpawnPoints = spawns; + newData.SpawnPoints = spawns.ToImmutableArray(); newData.GridType = r.map_grid_type; if (cache.LoadPreviewImages) { diff --git a/OpenRA.Game/Map/PlayerReference.cs b/OpenRA.Game/Map/PlayerReference.cs index b01b43937f..9c7a188872 100644 --- a/OpenRA.Game/Map/PlayerReference.cs +++ b/OpenRA.Game/Map/PlayerReference.cs @@ -9,6 +9,7 @@ */ #endregion +using System.Collections.Immutable; using OpenRA.Primitives; namespace OpenRA @@ -53,8 +54,8 @@ namespace OpenRA public bool LockHandicap = false; public int Handicap = 0; - public string[] Allies = []; - public string[] Enemies = []; + public ImmutableArray Allies = []; + public ImmutableArray Enemies = []; public PlayerReference() { } public PlayerReference(MiniYaml my) { FieldLoader.Load(this, my); } diff --git a/OpenRA.Game/Map/TerrainInfo.cs b/OpenRA.Game/Map/TerrainInfo.cs index 169bf2d37d..fd36fa42d2 100644 --- a/OpenRA.Game/Map/TerrainInfo.cs +++ b/OpenRA.Game/Map/TerrainInfo.cs @@ -10,6 +10,7 @@ #endregion using System.Collections.Generic; +using System.Collections.Immutable; using OpenRA.FileSystem; using OpenRA.Primitives; using OpenRA.Support; @@ -27,14 +28,14 @@ namespace OpenRA string Id { get; } string Name { get; } Size TileSize { get; } - TerrainTypeInfo[] TerrainTypes { get; } + ImmutableArray TerrainTypes { get; } TerrainTileInfo GetTerrainInfo(TerrainTile r); bool TryGetTerrainInfo(TerrainTile r, out TerrainTileInfo info); byte GetTerrainIndex(string type); byte GetTerrainIndex(TerrainTile r); TerrainTile DefaultTerrainTile { get; } - Color[] HeightDebugColors { get; } + ImmutableArray HeightDebugColors { get; } IEnumerable RestrictedPlayerColors { get; } float MinHeightColorBrightness { get; } float MaxHeightColorBrightness { get; } @@ -62,7 +63,7 @@ namespace OpenRA { public readonly string Type; public readonly BitSet TargetTypes; - public readonly HashSet AcceptsSmudgeType = []; + public readonly ImmutableArray AcceptsSmudgeType = []; public readonly Color Color; public readonly bool RestrictPlayerColor = false; diff --git a/OpenRA.Game/MiniYaml.cs b/OpenRA.Game/MiniYaml.cs index d84fea5c10..e2066477b7 100644 --- a/OpenRA.Game/MiniYaml.cs +++ b/OpenRA.Game/MiniYaml.cs @@ -678,8 +678,8 @@ namespace OpenRA { if (mapRules != null && mapRules.Value != null) { - var mapFiles = FieldLoader.GetValue("value", mapRules.Value); - files = files.Append(mapFiles); + var mapFiles = FieldLoader.GetValue>("value", mapRules.Value); + files = files.Concat(mapFiles); } var stringPool = new HashSet(); // Reuse common strings in YAML diff --git a/OpenRA.Game/Network/FluentMessage.cs b/OpenRA.Game/Network/FluentMessage.cs index 27cfbc92a2..87261da281 100644 --- a/OpenRA.Game/Network/FluentMessage.cs +++ b/OpenRA.Game/Network/FluentMessage.cs @@ -11,6 +11,7 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using Linguini.Shared.Types.Bundle; namespace OpenRA.Network @@ -55,31 +56,32 @@ namespace OpenRA.Network public readonly string Key = string.Empty; [FieldLoader.LoadUsing(nameof(LoadArguments))] - public readonly object[] Arguments; + public readonly ImmutableArray Arguments; static object LoadArguments(MiniYaml yaml) { - var arguments = new List(); var argumentsNode = yaml.NodeWithKeyOrDefault("Arguments"); - if (argumentsNode != null) - { - foreach (var argumentNode in argumentsNode.Value.Nodes) - { - var argument = FieldLoader.Load(argumentNode.Value); - arguments.Add(argument.Key); - if (argument.Type == FluentArgument.FluentArgumentType.Number) - { - if (!double.TryParse(argument.Value, out var number)) - Log.Write("debug", $"Failed to parse {argument.Value}"); - arguments.Add(number); - } - else - arguments.Add(argument.Value); + if (argumentsNode == null) + return ImmutableArray.Empty; + + var arguments = new List(argumentsNode.Value.Nodes.Length * 2); + foreach (var argumentNode in argumentsNode.Value.Nodes) + { + var argument = FieldLoader.Load(argumentNode.Value); + arguments.Add(argument.Key); + if (argument.Type == FluentArgument.FluentArgumentType.Number) + { + if (!double.TryParse(argument.Value, out var number)) + Log.Write("debug", $"Failed to parse {argument.Value}"); + + arguments.Add(number); } + else + arguments.Add(argument.Value); } - return arguments.ToArray(); + return arguments.ToImmutableArray(); } public FluentMessage(MiniYaml yaml) diff --git a/OpenRA.Game/Network/GameServer.cs b/OpenRA.Game/Network/GameServer.cs index 0c335e1a53..7d8b688d94 100644 --- a/OpenRA.Game/Network/GameServer.cs +++ b/OpenRA.Game/Network/GameServer.cs @@ -10,7 +10,9 @@ #endregion using System; +using System.Collections.Frozen; using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using System.Text.RegularExpressions; using OpenRA.Primitives; @@ -47,7 +49,7 @@ namespace OpenRA.Network public class GameServer { - static readonly string[] SerializeFields = + static readonly ImmutableArray SerializeFields = [ // Server information @@ -131,10 +133,10 @@ namespace OpenRA.Network public readonly bool IsJoinable = false; [FieldLoader.LoadUsing(nameof(LoadClients))] - public readonly GameClient[] Clients; + public readonly ImmutableArray Clients; /// The list of spawnpoints that are disabled for this game. - public readonly int[] DisabledSpawnPoints = []; + public readonly FrozenSet DisabledSpawnPoints = FrozenSet.Empty; public string ModLabel => $"{ModTitle} ({Version})"; @@ -150,7 +152,7 @@ namespace OpenRA.Network clients.Add(FieldLoader.Load(client.Value)); } - return clients.ToArray(); + return clients.ToImmutableArray(); } public GameServer(MiniYaml yaml) @@ -227,9 +229,9 @@ namespace OpenRA.Network ModWebsite = manifest.Metadata.Website; ModIcon32 = manifest.Metadata.WebIcon32; Protected = !string.IsNullOrEmpty(server.Settings.Password); - Authentication = server.Settings.RequireAuthentication || server.Settings.ProfileIDWhitelist.Length > 0; - Clients = server.LobbyInfo.Clients.Select(c => new GameClient(c)).ToArray(); - DisabledSpawnPoints = server.LobbyInfo.DisabledSpawnPoints?.ToArray() ?? []; + Authentication = server.Settings.RequireAuthentication || server.Settings.ProfileIDWhitelist.Count > 0; + Clients = server.LobbyInfo.Clients.Select(c => new GameClient(c)).ToImmutableArray(); + DisabledSpawnPoints = server.LobbyInfo.DisabledSpawnPoints?.ToFrozenSet() ?? FrozenSet.Empty; } public string ToPOSTData(bool lanGame) diff --git a/OpenRA.Game/Network/OrderManager.cs b/OpenRA.Game/Network/OrderManager.cs index 5f8ec3d701..9fcb6c7671 100644 --- a/OpenRA.Game/Network/OrderManager.cs +++ b/OpenRA.Game/Network/OrderManager.cs @@ -10,6 +10,7 @@ #endregion using System; +using System.Collections.Frozen; using System.Collections.Generic; using System.IO; using System.Linq; @@ -40,7 +41,7 @@ namespace OpenRA.Network public bool AuthenticationFailed = false; // The default null means "no map restriction" while an empty set means "all maps restricted" - public HashSet ServerMapPool = null; + public FrozenSet ServerMapPool = null; public int NetFrameNumber { get; private set; } public int LocalFrameNumber; diff --git a/OpenRA.Game/Network/UnitOrders.cs b/OpenRA.Game/Network/UnitOrders.cs index c8f7edc97e..3e3b0f7b16 100644 --- a/OpenRA.Game/Network/UnitOrders.cs +++ b/OpenRA.Game/Network/UnitOrders.cs @@ -9,6 +9,7 @@ */ #endregion +using System.Collections.Frozen; using System.Collections.Generic; using System.Linq; using OpenRA.Server; @@ -67,11 +68,11 @@ namespace OpenRA.Network { var message = new FluentMessage(node.Value); if (message.Key == Joined) - TextNotificationsManager.AddPlayerJoinedLine(message.Key, message.Arguments); + TextNotificationsManager.AddPlayerJoinedLine(message.Key, message.Arguments.ToArray()); else if (message.Key == Left) - TextNotificationsManager.AddPlayerLeftLine(message.Key, message.Arguments); + TextNotificationsManager.AddPlayerLeftLine(message.Key, message.Arguments.ToArray()); else - TextNotificationsManager.AddSystemLine(message.Key, message.Arguments); + TextNotificationsManager.AddSystemLine(message.Key, message.Arguments.ToArray()); } break; @@ -385,7 +386,7 @@ namespace OpenRA.Network case "SyncMapPool": { - orderManager.ServerMapPool = FieldLoader.GetValue>("SyncMapPool", order.TargetString); + orderManager.ServerMapPool = FieldLoader.GetValue>("SyncMapPool", order.TargetString); break; } diff --git a/OpenRA.Game/Primitives/Polygon.cs b/OpenRA.Game/Primitives/Polygon.cs index c18ef0749e..0d85552643 100644 --- a/OpenRA.Game/Primitives/Polygon.cs +++ b/OpenRA.Game/Primitives/Polygon.cs @@ -10,6 +10,7 @@ #endregion using System; +using System.Collections.Immutable; using System.Linq; namespace OpenRA.Primitives @@ -19,7 +20,7 @@ namespace OpenRA.Primitives public static readonly Polygon Empty = new(Rectangle.Empty); public readonly Rectangle BoundingRect; - public readonly int2[] Vertices; + public readonly ImmutableArray Vertices; readonly bool isRectangle; public Polygon(Rectangle bounds) @@ -29,7 +30,7 @@ namespace OpenRA.Primitives isRectangle = true; } - public Polygon(int2[] vertices) + public Polygon(ImmutableArray vertices) { if (vertices != null && vertices.Length > 0) { @@ -53,7 +54,7 @@ namespace OpenRA.Primitives { isRectangle = true; BoundingRect = Rectangle.Empty; - Vertices = Exts.MakeArray(4, _ => int2.Zero); + Vertices = Exts.MakeArray(4, _ => int2.Zero).ToImmutableArray(); } } diff --git a/OpenRA.Game/Server/Server.cs b/OpenRA.Game/Server/Server.cs index 02651bff27..00cc4d7dca 100644 --- a/OpenRA.Game/Server/Server.cs +++ b/OpenRA.Game/Server/Server.cs @@ -11,6 +11,7 @@ using System; using System.Collections.Concurrent; +using System.Collections.Frozen; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; @@ -132,7 +133,7 @@ namespace OpenRA.Server public MapPreview Map; public readonly MapStatusCache MapStatusCache; public GameSave GameSave; - public HashSet MapPool; + public FrozenSet MapPool; // Default to the next frame for ServerType.Local - MP servers take the value from the selected GameSpeed. public int OrderLatency = 1; @@ -661,9 +662,9 @@ namespace OpenRA.Server events.Add(new CallbackEvent(() => { - var notAuthenticated = Type == ServerType.Dedicated && profile == null && (Settings.RequireAuthentication || Settings.ProfileIDWhitelist.Length > 0); + var notAuthenticated = Type == ServerType.Dedicated && profile == null && (Settings.RequireAuthentication || Settings.ProfileIDWhitelist.Count > 0); var blacklisted = Type == ServerType.Dedicated && profile != null && Settings.ProfileIDBlacklist.Contains(profile.ProfileID); - var notWhitelisted = Type == ServerType.Dedicated && Settings.ProfileIDWhitelist.Length > 0 && + var notWhitelisted = Type == ServerType.Dedicated && Settings.ProfileIDWhitelist.Count > 0 && (profile == null || !Settings.ProfileIDWhitelist.Contains(profile.ProfileID)); if (notAuthenticated) @@ -689,7 +690,7 @@ namespace OpenRA.Server } else { - if (Type == ServerType.Dedicated && (Settings.RequireAuthentication || Settings.ProfileIDWhitelist.Length > 0)) + if (Type == ServerType.Dedicated && (Settings.RequireAuthentication || Settings.ProfileIDWhitelist.Count > 0)) { Log.Write("server", $"Rejected connection from {newConn.EndPoint}; Not authenticated."); SendOrderTo(newConn, "ServerError", RequiresAuthentication); diff --git a/OpenRA.Game/Settings.cs b/OpenRA.Game/Settings.cs index 33edca79ea..59ddadf429 100644 --- a/OpenRA.Game/Settings.cs +++ b/OpenRA.Game/Settings.cs @@ -10,7 +10,9 @@ #endregion using System; +using System.Collections.Frozen; using System.Collections.Generic; +using System.Collections.Immutable; using System.IO; using System.Linq; using OpenRA.Primitives; @@ -69,16 +71,16 @@ namespace OpenRA public string Map = null; [Desc("Takes a comma separated list of IP addresses that are not allowed to join.")] - public string[] Ban = []; + public FrozenSet Ban = FrozenSet.Empty; [Desc("For dedicated servers only, allow anonymous clients to join.")] public bool RequireAuthentication = false; [Desc("For dedicated servers only, if non-empty, only allow authenticated players with these profile IDs to join.")] - public int[] ProfileIDWhitelist = []; + public FrozenSet ProfileIDWhitelist = FrozenSet.Empty; [Desc("For dedicated servers only, if non-empty, always reject players with these user IDs from joining.")] - public int[] ProfileIDBlacklist = []; + public FrozenSet ProfileIDBlacklist = FrozenSet.Empty; [Desc("For dedicated servers only, controls whether a game can be started with just one human player in the lobby.")] public bool EnableSingleplayer = false; @@ -105,7 +107,7 @@ namespace OpenRA public bool EnableLintChecks = true; [Desc("For dedicated servers only, a comma separated list of map uids that are allowed to be used.")] - public string[] MapPool = []; + public FrozenSet MapPool = FrozenSet.Empty; [Desc("Delay in milliseconds before newly joined players can send chat messages.")] public int FloodLimitJoinCooldown = 5000; @@ -251,7 +253,7 @@ namespace OpenRA public string Name = "Commander"; public Color Color = Color.FromArgb(200, 32, 32); public string LastServer = "localhost:1234"; - public Color[] CustomColors = []; + public ImmutableArray CustomColors = []; } public class SinglePlayerGameSettings diff --git a/OpenRA.Game/Sound/Sound.cs b/OpenRA.Game/Sound/Sound.cs index 54656cba9f..f431aa8a8b 100644 --- a/OpenRA.Game/Sound/Sound.cs +++ b/OpenRA.Game/Sound/Sound.cs @@ -11,6 +11,7 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.IO; using OpenRA.FileSystem; using OpenRA.GameRules; @@ -165,12 +166,12 @@ namespace OpenRA public ISound PlayLooped(SoundType type, string name) { return Play(type, null, name, true, WPos.Zero, 1f, true); } public ISound PlayLooped(SoundType type, string name, WPos pos) { return Play(type, null, name, false, pos, 1f, true); } - public ISound Play(SoundType type, string[] names, World world, Player player = null, float volumeModifier = 1f) + public ISound Play(SoundType type, ImmutableArray names, World world, Player player = null, float volumeModifier = 1f) { return Play(type, player, names.Random(world.LocalRandom), true, WPos.Zero, volumeModifier); } - public ISound Play(SoundType type, string[] names, World world, WPos pos, Player player = null, float volumeModifier = 1f) + public ISound Play(SoundType type, ImmutableArray names, World world, WPos pos, Player player = null, float volumeModifier = 1f) { return Play(type, player, names.Random(world.LocalRandom), false, pos, volumeModifier); } @@ -399,9 +400,9 @@ namespace OpenRA if (variant != null) { if (rules.Variants.TryGetValue(variant, out var v) && !rules.DisableVariants.Contains(definition)) - suffix = v[id % v.Length]; + suffix = v[(int)(id % v.Length)]; if (rules.Prefixes.TryGetValue(variant, out var p) && !rules.DisablePrefixes.Contains(definition)) - prefix = p[id % p.Length]; + prefix = p[(int)(id % p.Length)]; } var name = prefix + clip + suffix; diff --git a/OpenRA.Game/Traits/TraitsInterfaces.cs b/OpenRA.Game/Traits/TraitsInterfaces.cs index 0e7338d4e5..bc5d29602e 100644 --- a/OpenRA.Game/Traits/TraitsInterfaces.cs +++ b/OpenRA.Game/Traits/TraitsInterfaces.cs @@ -11,6 +11,7 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Collections.ObjectModel; using System.Diagnostics.CodeAnalysis; using System.Linq; @@ -175,7 +176,7 @@ namespace OpenRA.Traits [RequireExplicitImplementation] public interface IStoresResourcesInfo : ITraitInfoInterface { - string[] ResourceTypes { get; } + ImmutableArray ResourceTypes { get; } } public interface IStoresResources @@ -505,12 +506,12 @@ namespace OpenRA.Traits public interface IControlGroupsInfo : ITraitInfoInterface { - string[] Groups { get; } + ImmutableArray Groups { get; } } public interface IControlGroups { - string[] Groups { get; } + ImmutableArray Groups { get; } void SelectControlGroup(int group); void CreateControlGroup(int group); diff --git a/OpenRA.Game/Traits/World/Faction.cs b/OpenRA.Game/Traits/World/Faction.cs index a013771e63..bbf21f1648 100644 --- a/OpenRA.Game/Traits/World/Faction.cs +++ b/OpenRA.Game/Traits/World/Faction.cs @@ -9,7 +9,7 @@ */ #endregion -using System.Collections.Generic; +using System.Collections.Frozen; namespace OpenRA.Traits { @@ -25,7 +25,7 @@ namespace OpenRA.Traits public readonly string InternalName = null; [Desc("Pick a random faction as the player's faction out of this list.")] - public readonly HashSet RandomFactionMembers = []; + public readonly FrozenSet RandomFactionMembers = FrozenSet.Empty; [Desc("The side that the faction belongs to. For example, England belongs to the 'Allies' side.")] public readonly string Side = null; diff --git a/OpenRA.Game/Widgets/Widget.cs b/OpenRA.Game/Widgets/Widget.cs index b88e4f6a04..7927fc1c27 100644 --- a/OpenRA.Game/Widgets/Widget.cs +++ b/OpenRA.Game/Widgets/Widget.cs @@ -11,6 +11,7 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Collections.ObjectModel; using System.Linq; using OpenRA.Graphics; @@ -208,8 +209,8 @@ namespace OpenRA.Widgets public IntegerExpression Y; public IntegerExpression Width; public IntegerExpression Height; - public string[] Logic = []; - public ChromeLogic[] LogicObjects { get; private set; } + public ImmutableArray Logic = []; + public ImmutableArray LogicObjects { get; private set; } public bool Visible = true; public bool IgnoreMouseOver; public bool IgnoreChildMouseOver; @@ -307,7 +308,7 @@ namespace OpenRA.Widgets args["widget"] = this; LogicObjects = Logic.Select(l => Game.ModData.ObjectCreator.CreateObject(l, args)) - .ToArray(); + .ToImmutableArray(); foreach (var logicObject in LogicObjects) Ui.Subscribe(logicObject); diff --git a/OpenRA.Game/World.cs b/OpenRA.Game/World.cs index 11739801a0..351f513a56 100644 --- a/OpenRA.Game/World.cs +++ b/OpenRA.Game/World.cs @@ -10,6 +10,7 @@ #endregion using System; +using System.Collections.Frozen; using System.Collections.Generic; using System.IO; using System.Linq; @@ -302,7 +303,7 @@ namespace OpenRA foreach (var player in Players) gameInfo.AddPlayer(player, OrderManager.LobbyInfo); - gameInfo.DisabledSpawnPoints = OrderManager.LobbyInfo.DisabledSpawnPoints; + gameInfo.DisabledSpawnPoints = OrderManager.LobbyInfo.DisabledSpawnPoints.ToFrozenSet(); gameInfo.StartTimeUtc = DateTime.UtcNow; diff --git a/OpenRA.Mods.Cnc/Graphics/ClassicTilesetSpecificSpriteSequence.cs b/OpenRA.Mods.Cnc/Graphics/ClassicTilesetSpecificSpriteSequence.cs index 429d09e0b8..166c170702 100644 --- a/OpenRA.Mods.Cnc/Graphics/ClassicTilesetSpecificSpriteSequence.cs +++ b/OpenRA.Mods.Cnc/Graphics/ClassicTilesetSpecificSpriteSequence.cs @@ -9,7 +9,9 @@ */ #endregion +using System.Collections.Frozen; using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using OpenRA.Graphics; using OpenRA.Mods.Common.Graphics; @@ -33,15 +35,15 @@ namespace OpenRA.Mods.Cnc.Graphics public class ClassicTilesetSpecificSpriteSequence : ClassicSpriteSequence { [Desc("Dictionary of : filename to override the Filename key.")] - static readonly SpriteSequenceField> TilesetFilenames = new(nameof(TilesetFilenames), null); + static readonly SpriteSequenceField> TilesetFilenames = new(nameof(TilesetFilenames), null); [Desc("Dictionary of : to override the FilenamePattern key.")] - static readonly SpriteSequenceField> TilesetFilenamesPattern = new(nameof(TilesetFilenamesPattern), null); + static readonly SpriteSequenceField> TilesetFilenamesPattern = new(nameof(TilesetFilenamesPattern), null); public ClassicTilesetSpecificSpriteSequence(SpriteCache cache, ISpriteSequenceLoader loader, string image, string sequence, MiniYaml data, MiniYaml defaults) : base(cache, loader, image, sequence, data, defaults) { } - protected override IEnumerable ParseFilenames(ModData modData, string tileset, int[] frames, MiniYaml data, MiniYaml defaults) + protected override IEnumerable ParseFilenames(ModData modData, string tileset, ImmutableArray frames, MiniYaml data, MiniYaml defaults) { var tilesetFilenamesPatternNode = data.NodeWithKeyOrDefault(TilesetFilenamesPattern.Key) ?? defaults.NodeWithKeyOrDefault(TilesetFilenamesPattern.Key); if (tilesetFilenamesPatternNode != null) @@ -71,7 +73,7 @@ namespace OpenRA.Mods.Cnc.Graphics return base.ParseFilenames(modData, tileset, frames, data, defaults); } - protected override IEnumerable ParseCombineFilenames(ModData modData, string tileset, int[] frames, MiniYaml data) + protected override IEnumerable ParseCombineFilenames(ModData modData, string tileset, ImmutableArray frames, MiniYaml data) { var node = data.NodeWithKeyOrDefault(TilesetFilenames.Key); if (node != null) @@ -83,7 +85,7 @@ namespace OpenRA.Mods.Cnc.Graphics { var subStart = LoadField("Start", 0, data); var subLength = LoadField("Length", 1, data); - frames = Exts.MakeArray(subLength, i => subStart + i); + frames = Exts.MakeArray(subLength, i => subStart + i).ToImmutableArray(); } return [new ReservationInfo(tilesetNode.Value.Value, frames, frames, tilesetNode.Location)]; diff --git a/OpenRA.Mods.Cnc/Graphics/ModelActorPreview.cs b/OpenRA.Mods.Cnc/Graphics/ModelActorPreview.cs index 2f5feb434c..ff86f5a420 100644 --- a/OpenRA.Mods.Cnc/Graphics/ModelActorPreview.cs +++ b/OpenRA.Mods.Cnc/Graphics/ModelActorPreview.cs @@ -10,6 +10,7 @@ #endregion using System.Collections.Generic; +using System.Collections.Immutable; using OpenRA.Graphics; using OpenRA.Mods.Cnc.Traits; using OpenRA.Mods.Common.Graphics; @@ -22,8 +23,8 @@ namespace OpenRA.Mods.Cnc.Graphics readonly ModelRenderer renderer; readonly ModelAnimation[] components; readonly float scale; - readonly float[] lightAmbientColor; - readonly float[] lightDiffuseColor; + readonly ImmutableArray lightAmbientColor; + readonly ImmutableArray lightDiffuseColor; readonly WRot lightSource; readonly WRot camera; readonly PaletteReference colorPalette; @@ -33,7 +34,7 @@ namespace OpenRA.Mods.Cnc.Graphics readonly int zOffset; public ModelPreview(ModelRenderer renderer, ModelAnimation[] components, in WVec offset, int zOffset, float scale, WAngle lightPitch, WAngle lightYaw, - float[] lightAmbientColor, float[] lightDiffuseColor, WAngle cameraPitch, + ImmutableArray lightAmbientColor, ImmutableArray lightDiffuseColor, WAngle cameraPitch, PaletteReference colorPalette, PaletteReference normalsPalette, PaletteReference shadowPalette) { this.renderer = renderer; diff --git a/OpenRA.Mods.Cnc/Graphics/ModelRenderable.cs b/OpenRA.Mods.Cnc/Graphics/ModelRenderable.cs index aa6be48fc5..ab00825c6b 100644 --- a/OpenRA.Mods.Cnc/Graphics/ModelRenderable.cs +++ b/OpenRA.Mods.Cnc/Graphics/ModelRenderable.cs @@ -11,6 +11,7 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using OpenRA.Graphics; using OpenRA.Mods.Cnc.Traits; @@ -24,15 +25,15 @@ namespace OpenRA.Mods.Cnc.Graphics readonly IEnumerable models; readonly WRot camera; readonly WRot lightSource; - readonly float[] lightAmbientColor; - readonly float[] lightDiffuseColor; + readonly ImmutableArray lightAmbientColor; + readonly ImmutableArray lightDiffuseColor; readonly PaletteReference normalsPalette; readonly PaletteReference shadowPalette; readonly float scale; public ModelRenderable( ModelRenderer renderer, IEnumerable models, WPos pos, int zOffset, in WRot camera, float scale, - in WRot lightSource, float[] lightAmbientColor, float[] lightDiffuseColor, + in WRot lightSource, ImmutableArray lightAmbientColor, ImmutableArray lightDiffuseColor, PaletteReference color, PaletteReference normals, PaletteReference shadow) : this(renderer, models, pos, zOffset, camera, scale, lightSource, lightAmbientColor, lightDiffuseColor, @@ -42,7 +43,7 @@ namespace OpenRA.Mods.Cnc.Graphics public ModelRenderable( ModelRenderer renderer, IEnumerable models, WPos pos, int zOffset, in WRot camera, float scale, - in WRot lightSource, float[] lightAmbientColor, float[] lightDiffuseColor, + in WRot lightSource, ImmutableArray lightAmbientColor, ImmutableArray lightDiffuseColor, PaletteReference color, PaletteReference normals, PaletteReference shadow, float alpha, in float3 tint, TintModifiers tintModifiers) { diff --git a/OpenRA.Mods.Cnc/Graphics/UIModelRenderable.cs b/OpenRA.Mods.Cnc/Graphics/UIModelRenderable.cs index bbdd311e35..57198e784d 100644 --- a/OpenRA.Mods.Cnc/Graphics/UIModelRenderable.cs +++ b/OpenRA.Mods.Cnc/Graphics/UIModelRenderable.cs @@ -11,6 +11,7 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using OpenRA.Graphics; using OpenRA.Mods.Cnc.Traits; @@ -25,15 +26,15 @@ namespace OpenRA.Mods.Cnc.Graphics readonly int2 screenPos; readonly WRot camera; readonly WRot lightSource; - readonly float[] lightAmbientColor; - readonly float[] lightDiffuseColor; + readonly ImmutableArray lightAmbientColor; + readonly ImmutableArray lightDiffuseColor; readonly PaletteReference normalsPalette; readonly PaletteReference shadowPalette; readonly float scale; public UIModelRenderable( ModelRenderer renderer, IEnumerable models, WPos effectiveWorldPos, int2 screenPos, int zOffset, - in WRot camera, float scale, in WRot lightSource, float[] lightAmbientColor, float[] lightDiffuseColor, + in WRot camera, float scale, in WRot lightSource, ImmutableArray lightAmbientColor, ImmutableArray lightDiffuseColor, PaletteReference color, PaletteReference normals, PaletteReference shadow) { this.renderer = renderer; diff --git a/OpenRA.Mods.Cnc/Traits/Disguise.cs b/OpenRA.Mods.Cnc/Traits/Disguise.cs index 8fc434f6c5..848c7f5e97 100644 --- a/OpenRA.Mods.Cnc/Traits/Disguise.cs +++ b/OpenRA.Mods.Cnc/Traits/Disguise.cs @@ -10,6 +10,7 @@ #endregion using System; +using System.Collections.Frozen; using System.Collections.Generic; using System.Linq; using OpenRA.Mods.Common.Orders; @@ -87,7 +88,7 @@ namespace OpenRA.Mods.Cnc.Traits [ActorReference(dictionaryReference: LintDictionaryReference.Keys)] [Desc("Conditions to grant when disguised as specified actor.", "A dictionary of [actor id]: [condition].")] - public readonly Dictionary DisguisedAsConditions = []; + public readonly FrozenDictionary DisguisedAsConditions = FrozenDictionary.Empty; [CursorReference] [Desc("Cursor to display when hovering over a valid actor to disguise as.")] diff --git a/OpenRA.Mods.Cnc/Traits/PaletteEffects/LightPaletteRotator.cs b/OpenRA.Mods.Cnc/Traits/PaletteEffects/LightPaletteRotator.cs index 464826d8a8..4771b2f402 100644 --- a/OpenRA.Mods.Cnc/Traits/PaletteEffects/LightPaletteRotator.cs +++ b/OpenRA.Mods.Cnc/Traits/PaletteEffects/LightPaletteRotator.cs @@ -9,7 +9,9 @@ */ #endregion +using System.Collections.Frozen; using System.Collections.Generic; +using System.Collections.Immutable; using OpenRA.Graphics; using OpenRA.Traits; @@ -20,7 +22,7 @@ namespace OpenRA.Mods.Cnc.Traits sealed class LightPaletteRotatorInfo : TraitInfo { [Desc("Palettes this effect should not apply to.")] - public readonly HashSet ExcludePalettes = []; + public readonly FrozenSet ExcludePalettes = FrozenSet.Empty; [Desc("'Speed' at which the effect cycles through palette indices.")] public readonly float TimeStep = .5f; @@ -29,7 +31,7 @@ namespace OpenRA.Mods.Cnc.Traits public readonly int ModifyIndex = 103; [Desc("Palette indices to rotate through.")] - public readonly int[] RotationIndices = [230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 238, 237, 236, 235, 234, 233, 232, 231]; + public readonly ImmutableArray RotationIndices = [230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 238, 237, 236, 235, 234, 233, 232, 231]; public override object Create(ActorInitializer init) { return new LightPaletteRotator(this); } } diff --git a/OpenRA.Mods.Cnc/Traits/Render/RenderVoxels.cs b/OpenRA.Mods.Cnc/Traits/Render/RenderVoxels.cs index e5b56f4aa1..bc263b8dc0 100644 --- a/OpenRA.Mods.Cnc/Traits/Render/RenderVoxels.cs +++ b/OpenRA.Mods.Cnc/Traits/Render/RenderVoxels.cs @@ -11,6 +11,7 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using OpenRA.Graphics; using OpenRA.Mods.Cnc.Graphics; @@ -52,8 +53,8 @@ namespace OpenRA.Mods.Cnc.Traits.Render public readonly WAngle LightPitch = WAngle.FromDegrees(50); public readonly WAngle LightYaw = WAngle.FromDegrees(240); - public readonly float[] LightAmbientColor = [0.6f, 0.6f, 0.6f]; - public readonly float[] LightDiffuseColor = [0.4f, 0.4f, 0.4f]; + public readonly ImmutableArray LightAmbientColor = [0.6f, 0.6f, 0.6f]; + public readonly ImmutableArray LightDiffuseColor = [0.4f, 0.4f, 0.4f]; public override object Create(ActorInitializer init) { return new RenderVoxels(init.Self, this); } diff --git a/OpenRA.Mods.Cnc/Traits/Render/WithCargo.cs b/OpenRA.Mods.Cnc/Traits/Render/WithCargo.cs index ca76a76dce..fd5beee795 100644 --- a/OpenRA.Mods.Cnc/Traits/Render/WithCargo.cs +++ b/OpenRA.Mods.Cnc/Traits/Render/WithCargo.cs @@ -9,7 +9,9 @@ */ #endregion +using System.Collections.Frozen; using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using OpenRA.Graphics; using OpenRA.Mods.Common; @@ -24,10 +26,10 @@ namespace OpenRA.Mods.Cnc.Traits.Render public class WithCargoInfo : TraitInfo, Requires, Requires { [Desc("Cargo position relative to turret or body in (forward, right, up) triples. The default offset should be in the middle of the list.")] - public readonly WVec[] LocalOffset = [WVec.Zero]; + public readonly ImmutableArray LocalOffset = [WVec.Zero]; [Desc("Passenger CargoType to display.")] - public readonly HashSet DisplayTypes = []; + public readonly FrozenSet DisplayTypes = FrozenSet.Empty; public override object Create(ActorInitializer init) { return new WithCargo(init.Self, this); } } diff --git a/OpenRA.Mods.Cnc/Traits/Render/WithHarvesterSpriteBody.cs b/OpenRA.Mods.Cnc/Traits/Render/WithHarvesterSpriteBody.cs index 5617e8f094..e4008f7801 100644 --- a/OpenRA.Mods.Cnc/Traits/Render/WithHarvesterSpriteBody.cs +++ b/OpenRA.Mods.Cnc/Traits/Render/WithHarvesterSpriteBody.cs @@ -9,6 +9,7 @@ */ #endregion +using System.Collections.Immutable; using OpenRA.Mods.Common.Traits; using OpenRA.Mods.Common.Traits.Render; using OpenRA.Traits; @@ -18,7 +19,7 @@ namespace OpenRA.Mods.Cnc.Traits.Render public class WithHarvesterSpriteBodyInfo : WithFacingSpriteBodyInfo, Requires { [Desc("Images switched between depending on fullness of harvester. Overrides RenderSprites.Image.")] - public readonly string[] ImageByFullness = []; + public readonly ImmutableArray ImageByFullness = []; public override object Create(ActorInitializer init) { return new WithHarvesterSpriteBody(init, this); } } diff --git a/OpenRA.Mods.Cnc/Traits/Render/WithLandingCraftAnimation.cs b/OpenRA.Mods.Cnc/Traits/Render/WithLandingCraftAnimation.cs index 3361cc6580..0fa3f5dd01 100644 --- a/OpenRA.Mods.Cnc/Traits/Render/WithLandingCraftAnimation.cs +++ b/OpenRA.Mods.Cnc/Traits/Render/WithLandingCraftAnimation.cs @@ -9,6 +9,7 @@ */ #endregion +using System.Collections.Frozen; using System.Collections.Generic; using System.Linq; using OpenRA.Mods.Common.Traits; @@ -19,7 +20,7 @@ namespace OpenRA.Mods.Cnc.Traits.Render { public class WithLandingCraftAnimationInfo : TraitInfo, Requires, Requires, Requires { - public readonly HashSet OpenTerrainTypes = ["Clear"]; + public readonly FrozenSet OpenTerrainTypes = new HashSet { "Clear" }.ToFrozenSet(); [SequenceReference] public readonly string OpenSequence = "open"; diff --git a/OpenRA.Mods.Cnc/Traits/SupportPowers/DropPodsPower.cs b/OpenRA.Mods.Cnc/Traits/SupportPowers/DropPodsPower.cs index a329cf238f..ab8f2efaae 100644 --- a/OpenRA.Mods.Cnc/Traits/SupportPowers/DropPodsPower.cs +++ b/OpenRA.Mods.Cnc/Traits/SupportPowers/DropPodsPower.cs @@ -10,7 +10,9 @@ #endregion using System; +using System.Collections.Frozen; using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using OpenRA.GameRules; using OpenRA.Mods.Cnc.Effects; @@ -26,7 +28,7 @@ namespace OpenRA.Mods.Cnc.Traits [FieldLoader.Require] [Desc("Drop pod unit")] [ActorReference([typeof(AircraftInfo), typeof(FallsToEarthInfo)])] - public readonly string[] UnitTypes = null; + public readonly ImmutableArray UnitTypes = default; [Desc("Number of drop pods spawned.")] public readonly int2 Drops = new(5, 8); @@ -82,7 +84,7 @@ namespace OpenRA.Mods.Cnc.Traits readonly DropPodsPowerInfo info; readonly string[] unitTypes; readonly Dictionary> getLaunchLocation = []; - readonly Dictionary> landableTerrainTypes = []; + readonly Dictionary> landableTerrainTypes = []; public DropPodsPower(Actor self, DropPodsPowerInfo info) : base(self, info) diff --git a/OpenRA.Mods.Cnc/Traits/TransformsNearResources.cs b/OpenRA.Mods.Cnc/Traits/TransformsNearResources.cs index e40ba6f254..9d3397815d 100644 --- a/OpenRA.Mods.Cnc/Traits/TransformsNearResources.cs +++ b/OpenRA.Mods.Cnc/Traits/TransformsNearResources.cs @@ -9,6 +9,7 @@ */ #endregion +using System.Collections.Immutable; using OpenRA.Mods.Common.Activities; using OpenRA.Mods.Common.Traits; using OpenRA.Traits; @@ -38,7 +39,7 @@ namespace OpenRA.Mods.Cnc.Traits public readonly int Adjacency = 1; [Desc("The range of time (in ticks) until the transformation starts.")] - public readonly int[] Delay = [1000, 3000]; + public readonly ImmutableArray Delay = [1000, 3000]; public override object Create(ActorInitializer init) { return new TransformsNearResources(init.Self, this); } } diff --git a/OpenRA.Mods.Cnc/Traits/World/JumpjetLocomotor.cs b/OpenRA.Mods.Cnc/Traits/World/JumpjetLocomotor.cs index 7b7395263c..e0db72334f 100644 --- a/OpenRA.Mods.Cnc/Traits/World/JumpjetLocomotor.cs +++ b/OpenRA.Mods.Cnc/Traits/World/JumpjetLocomotor.cs @@ -9,7 +9,7 @@ */ #endregion -using System.Collections.Generic; +using System.Collections.Frozen; using OpenRA.Mods.Common.Traits; using OpenRA.Traits; @@ -23,7 +23,7 @@ namespace OpenRA.Mods.Cnc.Traits public readonly short JumpjetTransitionCost = 0; [Desc("The terrain types that this actor can transition on. Leave empty to allow any.")] - public readonly HashSet JumpjetTransitionTerrainTypes = []; + public readonly FrozenSet JumpjetTransitionTerrainTypes = FrozenSet.Empty; [Desc("Can this actor transition on slopes?")] public readonly bool JumpjetTransitionOnRamps = true; diff --git a/OpenRA.Mods.Cnc/Traits/World/ModelRenderer.cs b/OpenRA.Mods.Cnc/Traits/World/ModelRenderer.cs index 0708f81318..4acbe7b789 100644 --- a/OpenRA.Mods.Cnc/Traits/World/ModelRenderer.cs +++ b/OpenRA.Mods.Cnc/Traits/World/ModelRenderer.cs @@ -11,6 +11,7 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using OpenRA.Graphics; using OpenRA.Primitives; @@ -44,8 +45,8 @@ namespace OpenRA.Mods.Cnc.Traits public sealed class ModelRenderer : IDisposable, IRenderer, INotifyActorDisposing { // Static constants - static readonly float[] ShadowDiffuse = [0, 0, 0]; - static readonly float[] ShadowAmbient = [1, 1, 1]; + static readonly ImmutableArray ShadowDiffuse = [0, 0, 0]; + static readonly ImmutableArray ShadowAmbient = [1, 1, 1]; static readonly float2 SpritePadding = new(2, 2); static readonly float[] ZeroVector = [0, 0, 0, 1]; static readonly float[] ZVector = [0, 0, 1, 1]; @@ -94,7 +95,7 @@ namespace OpenRA.Mods.Cnc.Traits public ModelRenderProxy RenderAsync( WorldRenderer wr, IEnumerable models, in WRot camera, float scale, - in WRot groundOrientation, in WRot lightSource, float[] lightAmbientColor, float[] lightDiffuseColor, + in WRot groundOrientation, in WRot lightSource, ImmutableArray lightAmbientColor, ImmutableArray lightDiffuseColor, PaletteReference color, PaletteReference normals, PaletteReference shadowPalette) { if (!isInFrame) @@ -281,15 +282,15 @@ namespace OpenRA.Mods.Cnc.Traits ModelRenderData renderData, IModelCache cache, float[] t, float[] lightDirection, - float[] ambientLight, float[] diffuseLight, + ImmutableArray ambientLight, ImmutableArray diffuseLight, float colorPaletteTextureIndex, float normalsPaletteTextureIndex) { shader.SetTexture("DiffuseTexture", renderData.Sheet.GetTexture()); shader.SetVec("Palettes", colorPaletteTextureIndex, normalsPaletteTextureIndex); shader.SetMatrix("TransformMatrix", t); shader.SetVec("LightDirection", lightDirection, 4); - shader.SetVec("AmbientLight", ambientLight, 3); - shader.SetVec("DiffuseLight", diffuseLight, 3); + shader.SetVec("AmbientLight", ambientLight.AsMemory(), 3); + shader.SetVec("DiffuseLight", diffuseLight.AsMemory(), 3); shader.PrepareRender(); renderer.DrawBatch(cache.VertexBuffer, shader, renderData.Start, renderData.Count, PrimitiveType.TriangleList); diff --git a/OpenRA.Mods.Cnc/Traits/World/TSEditorResourceLayer.cs b/OpenRA.Mods.Cnc/Traits/World/TSEditorResourceLayer.cs index 5315c39e26..1aa738ceab 100644 --- a/OpenRA.Mods.Cnc/Traits/World/TSEditorResourceLayer.cs +++ b/OpenRA.Mods.Cnc/Traits/World/TSEditorResourceLayer.cs @@ -9,7 +9,7 @@ */ #endregion -using System.Collections.Generic; +using System.Collections.Frozen; using System.Linq; using OpenRA.Mods.Common.Traits; using OpenRA.Traits; @@ -23,7 +23,7 @@ namespace OpenRA.Mods.Cnc.Traits [ActorReference] [Desc("Actor types that should be treated as veins for adjacency.")] - public readonly HashSet VeinholeActors = []; + public readonly FrozenSet VeinholeActors = FrozenSet.Empty; public override object Create(ActorInitializer init) { return new TSEditorResourceLayer(init.Self, this); } } diff --git a/OpenRA.Mods.Cnc/Traits/World/TSResourceLayer.cs b/OpenRA.Mods.Cnc/Traits/World/TSResourceLayer.cs index efa492962d..fe8771da70 100644 --- a/OpenRA.Mods.Cnc/Traits/World/TSResourceLayer.cs +++ b/OpenRA.Mods.Cnc/Traits/World/TSResourceLayer.cs @@ -10,6 +10,7 @@ #endregion using System; +using System.Collections.Frozen; using System.Collections.Generic; using System.Linq; using OpenRA.Graphics; @@ -25,7 +26,7 @@ namespace OpenRA.Mods.Cnc.Traits [ActorReference] [Desc("Actor types that should be treated as veins for adjacency.")] - public readonly HashSet VeinholeActors = []; + public readonly FrozenSet VeinholeActors = FrozenSet.Empty; public override object Create(ActorInitializer init) { return new TSResourceLayer(init.Self, this); } } diff --git a/OpenRA.Mods.Cnc/Traits/World/TSTiberiumRenderer.cs b/OpenRA.Mods.Cnc/Traits/World/TSTiberiumRenderer.cs index db40b3ae97..35d6ab4ae6 100644 --- a/OpenRA.Mods.Cnc/Traits/World/TSTiberiumRenderer.cs +++ b/OpenRA.Mods.Cnc/Traits/World/TSTiberiumRenderer.cs @@ -9,7 +9,9 @@ */ #endregion +using System.Collections.Frozen; using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using OpenRA.Graphics; using OpenRA.Mods.Common.Traits; @@ -22,16 +24,16 @@ namespace OpenRA.Mods.Cnc.Traits public class TSTiberiumRendererInfo : ResourceRendererInfo { [Desc("Sequences to use for ramp type 1.", "Dictionary of [resource type]: [list of sequences].")] - public readonly Dictionary Ramp1Sequences = []; + public readonly FrozenDictionary> Ramp1Sequences = FrozenDictionary>.Empty; [Desc("Sequences to use for ramp type 2.", "Dictionary of [resource type]: [list of sequences].")] - public readonly Dictionary Ramp2Sequences = []; + public readonly FrozenDictionary> Ramp2Sequences = FrozenDictionary>.Empty; [Desc("Sequences to use for ramp type 3.", "Dictionary of [resource type]: [list of sequences].")] - public readonly Dictionary Ramp3Sequences = []; + public readonly FrozenDictionary> Ramp3Sequences = FrozenDictionary>.Empty; [Desc("Sequences to use for ramp type 4.", "Dictionary of [resource type]: [list of sequences].")] - public readonly Dictionary Ramp4Sequences = []; + public readonly FrozenDictionary> Ramp4Sequences = FrozenDictionary>.Empty; public override object Create(ActorInitializer init) { return new TSTiberiumRenderer(init.Self, this); } } @@ -52,7 +54,7 @@ namespace OpenRA.Mods.Cnc.Traits world = self.World; } - void LoadVariants(Dictionary rampSequences, Dictionary> rampVariants) + void LoadVariants(FrozenDictionary> rampSequences, Dictionary> rampVariants) { var sequences = world.Map.Sequences; foreach (var kv in rampSequences) diff --git a/OpenRA.Mods.Cnc/Traits/World/TSVeinsRenderer.cs b/OpenRA.Mods.Cnc/Traits/World/TSVeinsRenderer.cs index 88156e6ba9..fb9b7fa935 100644 --- a/OpenRA.Mods.Cnc/Traits/World/TSVeinsRenderer.cs +++ b/OpenRA.Mods.Cnc/Traits/World/TSVeinsRenderer.cs @@ -10,6 +10,7 @@ #endregion using System; +using System.Collections.Frozen; using System.Collections.Generic; using System.Linq; using OpenRA.Graphics; @@ -45,7 +46,7 @@ namespace OpenRA.Mods.Cnc.Traits [ActorReference] [Desc("Actor types that should be treated as veins for adjacency.")] - public readonly HashSet VeinholeActors = []; + public readonly FrozenSet VeinholeActors = FrozenSet.Empty; void IMapPreviewSignatureInfo.PopulateMapPreviewSignatureCells(Map map, ActorInfo ai, ActorReference s, List<(MPos Uv, Color Color)> destinationBuffer) { diff --git a/OpenRA.Mods.Cnc/Traits/World/WithResourceAnimation.cs b/OpenRA.Mods.Cnc/Traits/World/WithResourceAnimation.cs index dcbe23055d..a24f46497c 100644 --- a/OpenRA.Mods.Cnc/Traits/World/WithResourceAnimation.cs +++ b/OpenRA.Mods.Cnc/Traits/World/WithResourceAnimation.cs @@ -10,7 +10,9 @@ #endregion using System; +using System.Collections.Frozen; using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using OpenRA.Graphics; using OpenRA.Mods.Common; @@ -26,13 +28,13 @@ namespace OpenRA.Mods.Cnc.Traits { [FieldLoader.Require] [Desc("Resource types to animate.")] - public readonly HashSet Types = null; + public readonly FrozenSet Types = null; [Desc("The percentage of resource cells to play the animation on.", "Use two values to randomize between them.")] - public readonly int[] Ratio = [1, 10]; + public readonly ImmutableArray Ratio = [1, 10]; [Desc("Tick interval between two animation spawning.", "Use two values to randomize between them.")] - public readonly int[] Interval = [200, 500]; + public readonly ImmutableArray Interval = [200, 500]; [FieldLoader.Require] [Desc("Animation image.")] @@ -40,7 +42,7 @@ namespace OpenRA.Mods.Cnc.Traits [SequenceReference(nameof(Image))] [Desc("Randomly select one of these sequences to render.")] - public readonly string[] Sequences = ["idle"]; + public readonly ImmutableArray Sequences = ["idle"]; [PaletteReference] [Desc("Animation palette.")] diff --git a/OpenRA.Mods.Cnc/UtilityCommands/ImportGen1MapCommand.cs b/OpenRA.Mods.Cnc/UtilityCommands/ImportGen1MapCommand.cs index 42113db9d3..c96d858380 100644 --- a/OpenRA.Mods.Cnc/UtilityCommands/ImportGen1MapCommand.cs +++ b/OpenRA.Mods.Cnc/UtilityCommands/ImportGen1MapCommand.cs @@ -11,6 +11,7 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.IO; using System.Linq; using System.Text; @@ -394,8 +395,8 @@ namespace OpenRA.Mods.Cnc.UtilityCommands switch (s.Key) { case "Allies": - pr.Allies = s.Value.Split(',').Intersect(players).Except(neutral).ToArray(); - pr.Enemies = s.Value.Split(',').SymmetricDifference(players).Except(neutral).ToArray(); + pr.Allies = s.Value.Split(',').Intersect(players).Except(neutral).ToImmutableArray(); + pr.Enemies = s.Value.Split(',').SymmetricDifference(players).Except(neutral).ToImmutableArray(); break; default: Console.WriteLine("Ignoring unknown {0}={1} for player {2}", s.Key, s.Value, pr.Name); diff --git a/OpenRA.Mods.Cnc/UtilityCommands/RemapShpCommand.cs b/OpenRA.Mods.Cnc/UtilityCommands/RemapShpCommand.cs index 38a7a5e26f..ea51b72bc9 100644 --- a/OpenRA.Mods.Cnc/UtilityCommands/RemapShpCommand.cs +++ b/OpenRA.Mods.Cnc/UtilityCommands/RemapShpCommand.cs @@ -11,6 +11,7 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.IO; using System.Linq; using OpenRA.Graphics; @@ -50,7 +51,7 @@ namespace OpenRA.Mods.Cnc.UtilityCommands Game.ModData = destModData; var destPaletteInfo = destModData.DefaultRules.Actors[SystemActors.Player].TraitInfo(); var destRemapIndex = destPaletteInfo.RemapIndex; - var shadowIndex = Array.Empty(); + var shadowIndex = ImmutableArray.Empty; // the remap range is always 16 entries, but their location and order changes for (var i = 0; i < 16; i++) diff --git a/OpenRA.Mods.Cnc/Widgets/ModelWidget.cs b/OpenRA.Mods.Cnc/Widgets/ModelWidget.cs index ecddb5083c..5d02dae60b 100644 --- a/OpenRA.Mods.Cnc/Widgets/ModelWidget.cs +++ b/OpenRA.Mods.Cnc/Widgets/ModelWidget.cs @@ -10,6 +10,7 @@ #endregion using System; +using System.Collections.Immutable; using OpenRA.Graphics; using OpenRA.Mods.Cnc.Graphics; using OpenRA.Mods.Cnc.Traits; @@ -26,8 +27,8 @@ namespace OpenRA.Mods.Cnc.Widgets public float Scale = 10f; public int LightPitch = 142; public int LightYaw = 682; - public float[] LightAmbientColor = [0.6f, 0.6f, 0.6f]; - public float[] LightDiffuseColor = [0.4f, 0.4f, 0.4f]; + public ImmutableArray LightAmbientColor = [0.6f, 0.6f, 0.6f]; + public ImmutableArray LightDiffuseColor = [0.4f, 0.4f, 0.4f]; public WRot Rotation = WRot.None; public WAngle CameraAngle = WAngle.FromDegrees(40); @@ -35,8 +36,8 @@ namespace OpenRA.Mods.Cnc.Widgets public Func GetPlayerPalette; public Func GetNormalsPalette; public Func GetShadowPalette; - public Func GetLightAmbientColor; - public Func GetLightDiffuseColor; + public Func> GetLightAmbientColor; + public Func> GetLightDiffuseColor; public Func GetScale; public Func GetLightPitch; public Func GetLightYaw; diff --git a/OpenRA.Mods.Common/AIUtils.cs b/OpenRA.Mods.Common/AIUtils.cs index 6c364414b0..80a54e31df 100644 --- a/OpenRA.Mods.Common/AIUtils.cs +++ b/OpenRA.Mods.Common/AIUtils.cs @@ -9,6 +9,7 @@ */ #endregion +using System.Collections.Frozen; using System.Collections.Generic; using System.Linq; using OpenRA.Mods.Common.Traits; @@ -22,7 +23,7 @@ namespace OpenRA.Mods.Common public static class AIUtils { - public static bool IsAreaAvailable(World world, Player player, Map map, int radius, HashSet terrainTypes) + public static bool IsAreaAvailable(World world, Player player, Map map, int radius, FrozenSet terrainTypes) { var cells = world.ActorsHavingTrait().Where(a => a.Owner == player); diff --git a/OpenRA.Mods.Common/Activities/Transform.cs b/OpenRA.Mods.Common/Activities/Transform.cs index 02185c4443..a1c8d9a7ce 100644 --- a/OpenRA.Mods.Common/Activities/Transform.cs +++ b/OpenRA.Mods.Common/Activities/Transform.cs @@ -10,6 +10,7 @@ #endregion using System.Collections.Generic; +using System.Collections.Immutable; using OpenRA.Activities; using OpenRA.Mods.Common.Traits; using OpenRA.Mods.Common.Traits.Render; @@ -23,7 +24,7 @@ namespace OpenRA.Mods.Common.Activities public readonly string ToActor; public CVec Offset = CVec.Zero; public WAngle Facing = new(384); - public string[] Sounds = []; + public ImmutableArray Sounds = []; public string Notification = null; public string TextNotification = null; public int ForceHealthPercentage = 0; diff --git a/OpenRA.Mods.Common/AssetBrowser.cs b/OpenRA.Mods.Common/AssetBrowser.cs index db40a7213c..6baebb6781 100644 --- a/OpenRA.Mods.Common/AssetBrowser.cs +++ b/OpenRA.Mods.Common/AssetBrowser.cs @@ -9,13 +9,15 @@ */ #endregion +using System.Collections.Immutable; + namespace OpenRA { public class AssetBrowser : IGlobalModData { - public readonly string[] SpriteExtensions = []; - public readonly string[] ModelExtensions = []; - public readonly string[] AudioExtensions = []; - public readonly string[] VideoExtensions = []; + public readonly ImmutableArray SpriteExtensions = []; + public readonly ImmutableArray ModelExtensions = []; + public readonly ImmutableArray AudioExtensions = []; + public readonly ImmutableArray VideoExtensions = []; } } diff --git a/OpenRA.Mods.Common/EditorBrushes/EditorMarkerLayerBrush.cs b/OpenRA.Mods.Common/EditorBrushes/EditorMarkerLayerBrush.cs index 26eebf8363..1489ef10ba 100644 --- a/OpenRA.Mods.Common/EditorBrushes/EditorMarkerLayerBrush.cs +++ b/OpenRA.Mods.Common/EditorBrushes/EditorMarkerLayerBrush.cs @@ -9,6 +9,7 @@ */ #endregion +using System.Collections.Frozen; using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; @@ -234,13 +235,13 @@ namespace OpenRA.Mods.Common.Widgets public string Text { get; } readonly MarkerLayerOverlay markerLayerOverlay; - readonly ImmutableDictionary> tiles; + readonly FrozenDictionary> tiles; public ClearAllMarkerTilesEditorAction( MarkerLayerOverlay markerLayerOverlay) { this.markerLayerOverlay = markerLayerOverlay; - tiles = markerLayerOverlay.Tiles.ToImmutableDictionary(t => t.Key, t => t.Value.ToImmutableArray()); + tiles = markerLayerOverlay.Tiles.ToFrozenDictionary(t => t.Key, t => t.Value.ToImmutableArray()); var allTilesCount = tiles.Values.Sum(x => x.Length); Text = FluentProvider.GetMessage(ClearedAllMarkerTiles, "count", allTilesCount); diff --git a/OpenRA.Mods.Common/Effects/FloatingSprite.cs b/OpenRA.Mods.Common/Effects/FloatingSprite.cs index 7f374eccab..688e64b80f 100644 --- a/OpenRA.Mods.Common/Effects/FloatingSprite.cs +++ b/OpenRA.Mods.Common/Effects/FloatingSprite.cs @@ -10,6 +10,7 @@ #endregion using System.Collections.Generic; +using System.Collections.Immutable; using OpenRA.Effects; using OpenRA.Graphics; @@ -17,8 +18,8 @@ namespace OpenRA.Mods.Common.Effects { public sealed class FloatingSprite : IEffect, ISpatiallyPartitionable { - readonly WDist[] speed; - readonly WDist[] gravity; + readonly ImmutableArray speed; + readonly ImmutableArray gravity; readonly Animation anim; readonly bool visibleThroughFog; @@ -32,8 +33,8 @@ namespace OpenRA.Mods.Common.Effects int ticks; WAngle facing; - public FloatingSprite(Actor emitter, string image, string[] sequences, string palette, bool isPlayerPalette, - int[] lifetime, WDist[] speed, WDist[] gravity, int turnRate, int randomRate, WPos pos, WAngle facing, + public FloatingSprite(Actor emitter, string image, ImmutableArray sequences, string palette, bool isPlayerPalette, + ImmutableArray lifetime, ImmutableArray speed, ImmutableArray gravity, int turnRate, int randomRate, WPos pos, WAngle facing, bool visibleThroughFog = false) { var world = emitter.World; diff --git a/OpenRA.Mods.Common/FileSystem/ContentInstallerFileSystemLoader.cs b/OpenRA.Mods.Common/FileSystem/ContentInstallerFileSystemLoader.cs index c4478ab6b4..cef00f44b8 100644 --- a/OpenRA.Mods.Common/FileSystem/ContentInstallerFileSystemLoader.cs +++ b/OpenRA.Mods.Common/FileSystem/ContentInstallerFileSystemLoader.cs @@ -10,6 +10,7 @@ #endregion using System.Collections.Generic; +using System.Collections.Immutable; namespace OpenRA.Mods.Common.FileSystem { @@ -20,18 +21,52 @@ namespace OpenRA.Mods.Common.FileSystem [Desc("Mod to use for content installation.")] public readonly string ContentInstallerMod = null; - [FieldLoader.Require] [Desc("A list of mod-provided packages. Anything required to display the initial load screen must be listed here.")] - public readonly Dictionary SystemPackages = null; + [FieldLoader.LoadUsing(nameof(LoadSystemPackages))] + public readonly ImmutableArray> SystemPackages = default; [Desc("A list of user-installed packages. If missing (and not marked as optional), these will trigger the content installer.")] - public readonly Dictionary ContentPackages = null; + [FieldLoader.LoadUsing(nameof(LoadContentPackages))] + public readonly ImmutableArray> ContentPackages = default; [Desc("Files that aren't mounted as packages, but still need to trigger the content installer if missing.")] - public readonly Dictionary RequiredContentFiles = null; + [FieldLoader.LoadUsing(nameof(LoadRequiredContentFiles))] + public readonly ImmutableArray> RequiredContentFiles = default; bool isContentAvailable = true; + static object LoadSystemPackages(MiniYaml yaml) + { + return LoadPackages(yaml, nameof(SystemPackages), true); + } + + static object LoadContentPackages(MiniYaml yaml) + { + return LoadPackages(yaml, nameof(ContentPackages), false); + } + + static object LoadRequiredContentFiles(MiniYaml yaml) + { + return LoadPackages(yaml, nameof(RequiredContentFiles), false); + } + + static object LoadPackages(MiniYaml yaml, string key, bool required) + { + var packageNode = yaml.NodeWithKeyOrDefault(key); + if (packageNode == null) + { + if (required) + throw new FieldLoader.MissingFieldsException([key]); + return default(ImmutableArray>); + } + + var packages = new List>(packageNode.Value.Nodes.Length); + foreach (var node in packageNode.Value.Nodes) + packages.Add(KeyValuePair.Create(node.Key, node.Value.Value)); + + return packages.ToImmutableArray(); + } + public void Mount(OpenRA.FileSystem.FileSystem fileSystem, ObjectCreator objectCreator) { foreach (var kv in SystemPackages) diff --git a/OpenRA.Mods.Common/FileSystem/DefaultFileSystemLoader.cs b/OpenRA.Mods.Common/FileSystem/DefaultFileSystemLoader.cs index 2194fa4862..3f0d8983d0 100644 --- a/OpenRA.Mods.Common/FileSystem/DefaultFileSystemLoader.cs +++ b/OpenRA.Mods.Common/FileSystem/DefaultFileSystemLoader.cs @@ -10,6 +10,7 @@ #endregion using System.Collections.Generic; +using System.Collections.Immutable; using OpenRA.Traits; namespace OpenRA.Mods.Common.FileSystem @@ -23,7 +24,21 @@ namespace OpenRA.Mods.Common.FileSystem public class DefaultFileSystemLoader : IFileSystemLoader { - public readonly Dictionary Packages = null; + [FieldLoader.LoadUsing(nameof(LoadPackages))] + public readonly ImmutableArray> Packages = default; + + static object LoadPackages(MiniYaml yaml) + { + var packageNode = yaml.NodeWithKeyOrDefault(nameof(Packages)); + if (packageNode == null) + return default(ImmutableArray>); + + var packages = new List>(packageNode.Value.Nodes.Length); + foreach (var node in packageNode.Value.Nodes) + packages.Add(KeyValuePair.Create(node.Key, node.Value.Value)); + + return packages.ToImmutableArray(); + } public void Mount(OpenRA.FileSystem.FileSystem fileSystem, ObjectCreator objectCreator) { diff --git a/OpenRA.Mods.Common/Graphics/DefaultSpriteSequence.cs b/OpenRA.Mods.Common/Graphics/DefaultSpriteSequence.cs index b024964773..3fd863217f 100644 --- a/OpenRA.Mods.Common/Graphics/DefaultSpriteSequence.cs +++ b/OpenRA.Mods.Common/Graphics/DefaultSpriteSequence.cs @@ -11,6 +11,7 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Collections.ObjectModel; using System.IO; using System.Linq; @@ -87,20 +88,17 @@ namespace OpenRA.Mods.Common.Graphics public bool FlipY; public float ZRamp; public BlendMode BlendMode; - public int[] Frames; + public ImmutableArray Frames; } protected readonly struct ReservationInfo { public readonly string Filename; - public readonly List LoadFrames; - public readonly int[] Frames; + public readonly ImmutableArray LoadFrames; + public readonly ImmutableArray Frames; public readonly MiniYamlNode.SourceLocation Location; - public ReservationInfo(string filename, int[] loadFrames, int[] frames, MiniYamlNode.SourceLocation location) - : this(filename, loadFrames?.ToList(), frames, location) { } - - public ReservationInfo(string filename, List loadFrames, int[] frames, MiniYamlNode.SourceLocation location) + public ReservationInfo(string filename, ImmutableArray loadFrames, ImmutableArray frames, MiniYamlNode.SourceLocation location) { Filename = filename; LoadFrames = loadFrames; @@ -150,7 +148,7 @@ namespace OpenRA.Mods.Common.Graphics protected static readonly SpriteSequenceField ShadowZOffset = new(nameof(ShadowZOffset), new WDist(-5)); [Desc("The individual frames to play instead of going through them sequentially from the `Start`.")] - protected static readonly SpriteSequenceField Frames = new(nameof(Frames), null); + protected static readonly SpriteSequenceField> Frames = new(nameof(Frames), default); [Desc("Don't apply terrain lighting or colored overlays.")] protected static readonly SpriteSequenceField IgnoreWorldTint = new(nameof(IgnoreWorldTint), false); @@ -181,7 +179,7 @@ namespace OpenRA.Mods.Common.Graphics protected static readonly SpriteSequenceField Combine = new(nameof(Combine), null); [Desc("Sets transparency - use one value to set for all frames or provide a value for each frame.")] - protected static readonly SpriteSequenceField Alpha = new(nameof(Alpha), null); + protected static readonly SpriteSequenceField> Alpha = new(nameof(Alpha), default); [Desc("Fade the animation from fully opaque on the first frame to fully transparent after the last frame.")] protected static readonly SpriteSequenceField AlphaFade = new(nameof(AlphaFade), false); @@ -196,7 +194,7 @@ namespace OpenRA.Mods.Common.Graphics protected static readonly SpriteSequenceField DepthSpriteOffset = new(nameof(DepthSpriteOffset), float2.Zero); protected static readonly MiniYaml NoData = new(null); - protected static readonly int[] FirstFrame = [0]; + protected static readonly ImmutableArray FirstFrame = [0]; protected readonly ISpriteSequenceLoader Loader; @@ -220,7 +218,7 @@ namespace OpenRA.Mods.Common.Graphics protected int shadowZOffset; protected bool ignoreWorldTint; protected float scale; - protected float[] alpha; + protected ImmutableArray alpha; protected bool alphaFade; protected Rectangle? bounds; @@ -296,12 +294,12 @@ namespace OpenRA.Mods.Common.Graphics return Rectangle.FromLTRB(left, top, right, bottom); } - protected static List CalculateFrameIndices( - int start, int? length, int stride, int facings, int[] frames, bool transpose, bool reverseFacings, int shadowStart) + protected static ImmutableArray CalculateFrameIndices( + int start, int? length, int stride, int facings, ImmutableArray frames, bool transpose, bool reverseFacings, int shadowStart) { // Request all frames if (length == null) - return null; + return default; // Only request the subset of frames that we actually need var usedFrames = new List(); @@ -313,7 +311,7 @@ namespace OpenRA.Mods.Common.Graphics var i = transpose ? frame * facings + facingInner : facingInner * stride + frame; - usedFrames.Add(frames?[i] ?? start + i); + usedFrames.Add(frames != null ? frames[i] : start + i); } } @@ -325,10 +323,10 @@ namespace OpenRA.Mods.Common.Graphics usedFrames.Add(usedFrames[i] + shadowOffset); } - return usedFrames; + return usedFrames.ToImmutableArray(); } - protected virtual IEnumerable ParseFilenames(ModData modData, string tileset, int[] frames, MiniYaml data, MiniYaml defaults) + protected virtual IEnumerable ParseFilenames(ModData modData, string tileset, ImmutableArray frames, MiniYaml data, MiniYaml defaults) { var filenamePatternNode = data.NodeWithKeyOrDefault(FilenamePattern.Key) ?? defaults.NodeWithKeyOrDefault(FilenamePattern.Key); if (!string.IsNullOrEmpty(filenamePatternNode?.Value.Value)) @@ -347,14 +345,14 @@ namespace OpenRA.Mods.Common.Graphics return [new ReservationInfo(filename, loadFrames, frames, location)]; } - protected virtual IEnumerable ParseCombineFilenames(ModData modData, string tileset, int[] frames, MiniYaml data) + protected virtual IEnumerable ParseCombineFilenames(ModData modData, string tileset, ImmutableArray frames, MiniYaml data) { var filename = LoadField(Filename, data, null, out var location); if (frames == null && LoadField(Length.Key, null, data) != "*") { var subStart = LoadField("Start", 0, data); var subLength = LoadField("Length", 1, data); - frames = Exts.MakeArray(subLength, i => subStart + i); + frames = Exts.MakeArray(subLength, i => subStart + i).ToImmutableArray(); } yield return new ReservationInfo(filename, frames, frames, location); @@ -522,24 +520,25 @@ namespace OpenRA.Mods.Common.Graphics if (alpha != null) { if (alpha.Length == 1) - alpha = Exts.MakeArray(length.Value, _ => alpha[0]); + alpha = Exts.MakeArray(length.Value, _ => alpha[0]).ToImmutableArray(); else if (alpha.Length != length.Value) throw new YamlException($"Sequence {image}.{Name} must define either 1 or {length.Value} Alpha values."); } else if (alphaFade) - alpha = Exts.MakeArray(length.Value, i => float2.Lerp(1f, 0f, i / (length.Value - 1f))); + alpha = Exts.MakeArray(length.Value, i => float2.Lerp(1f, 0f, i / (length.Value - 1f))).ToImmutableArray(); // Reindex sprites to order facings anti-clockwise and remove unused frames - var index = CalculateFrameIndices(start, length.Value, stride ?? length.Value, facings, null, transpose, reverseFacings, -1); + var index = CalculateFrameIndices(start, length.Value, stride ?? length.Value, facings, default, transpose, reverseFacings, -1); if (reverses) { - index.AddRange(index.Skip(1).Take(length.Value - 2).Reverse()); - alpha = alpha?.Concat(alpha.Skip(1).Take(length.Value - 2).Reverse()).ToArray(); + index = index.AddRange(index.Skip(1).Take(length.Value - 2).Reverse()); + if (alpha != null) + alpha = alpha.AddRange(alpha.Skip(1).Take(length.Value - 2).Reverse()); length = 2 * length - 2; } - if (index.Count == 0) + if (index.Length == 0) throw new YamlException($"Sequence {image}.{Name} does not define any frames."); var minIndex = index.Min(); @@ -610,7 +609,7 @@ namespace OpenRA.Mods.Common.Graphics public virtual float GetAlpha(int frame) { - return alpha?[frame] ?? 1f; + return alpha != null ? alpha[frame] : 1f; } protected virtual float GetScale() diff --git a/OpenRA.Mods.Common/Graphics/TilesetSpecificSpriteSequence.cs b/OpenRA.Mods.Common/Graphics/TilesetSpecificSpriteSequence.cs index 6e50cb0c05..aa82ef1a40 100644 --- a/OpenRA.Mods.Common/Graphics/TilesetSpecificSpriteSequence.cs +++ b/OpenRA.Mods.Common/Graphics/TilesetSpecificSpriteSequence.cs @@ -9,7 +9,9 @@ */ #endregion +using System.Collections.Frozen; using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using OpenRA.Graphics; @@ -31,15 +33,15 @@ namespace OpenRA.Mods.Common.Graphics public class TilesetSpecificSpriteSequence : DefaultSpriteSequence { [Desc("Dictionary of : filename to override the Filename key.")] - static readonly SpriteSequenceField> TilesetFilenames = new(nameof(TilesetFilenames), null); + static readonly SpriteSequenceField> TilesetFilenames = new(nameof(TilesetFilenames), null); [Desc("Dictionary of : to override the FilenamePattern key.")] - static readonly SpriteSequenceField> TilesetFilenamesPattern = new(nameof(TilesetFilenamesPattern), null); + static readonly SpriteSequenceField> TilesetFilenamesPattern = new(nameof(TilesetFilenamesPattern), null); public TilesetSpecificSpriteSequence(SpriteCache cache, ISpriteSequenceLoader loader, string image, string sequence, MiniYaml data, MiniYaml defaults) : base(cache, loader, image, sequence, data, defaults) { } - protected override IEnumerable ParseFilenames(ModData modData, string tileset, int[] frames, MiniYaml data, MiniYaml defaults) + protected override IEnumerable ParseFilenames(ModData modData, string tileset, ImmutableArray frames, MiniYaml data, MiniYaml defaults) { var tilesetFilenamesPatternNode = data.NodeWithKeyOrDefault(TilesetFilenamesPattern.Key) ?? defaults.NodeWithKeyOrDefault(TilesetFilenamesPattern.Key); if (tilesetFilenamesPatternNode != null) @@ -69,7 +71,7 @@ namespace OpenRA.Mods.Common.Graphics return base.ParseFilenames(modData, tileset, frames, data, defaults); } - protected override IEnumerable ParseCombineFilenames(ModData modData, string tileset, int[] frames, MiniYaml data) + protected override IEnumerable ParseCombineFilenames(ModData modData, string tileset, ImmutableArray frames, MiniYaml data) { var node = data.NodeWithKeyOrDefault(TilesetFilenames.Key); if (node != null) @@ -81,7 +83,7 @@ namespace OpenRA.Mods.Common.Graphics { var subStart = LoadField("Start", 0, data); var subLength = LoadField("Length", 1, data); - frames = Exts.MakeArray(subLength, i => subStart + i); + frames = Exts.MakeArray(subLength, i => subStart + i).ToImmutableArray(); } return [new ReservationInfo(tilesetNode.Value.Value, frames, frames, tilesetNode.Location)]; diff --git a/OpenRA.Mods.Common/HitShapes/Polygon.cs b/OpenRA.Mods.Common/HitShapes/Polygon.cs index 451fabc9c8..56571f8557 100644 --- a/OpenRA.Mods.Common/HitShapes/Polygon.cs +++ b/OpenRA.Mods.Common/HitShapes/Polygon.cs @@ -11,6 +11,7 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using OpenRA.Graphics; using OpenRA.Mods.Common.Graphics; @@ -24,7 +25,7 @@ namespace OpenRA.Mods.Common.HitShapes public WDist OuterRadius { get; private set; } [FieldLoader.Require] - public readonly int2[] Points; + public readonly ImmutableArray Points; [Desc("Defines the top offset relative to the actor's center.")] public readonly int VerticalTopOffset = 0; @@ -42,7 +43,7 @@ namespace OpenRA.Mods.Common.HitShapes public PolygonShape() { } - public PolygonShape(int2[] points) { Points = points; } + public PolygonShape(ImmutableArray points) { Points = points; } public void Initialize() { diff --git a/OpenRA.Mods.Common/Lint/CheckChromeHotkeys.cs b/OpenRA.Mods.Common/Lint/CheckChromeHotkeys.cs index 1b76801634..dcbd58f518 100644 --- a/OpenRA.Mods.Common/Lint/CheckChromeHotkeys.cs +++ b/OpenRA.Mods.Common/Lint/CheckChromeHotkeys.cs @@ -100,7 +100,7 @@ namespace OpenRA.Mods.Common.Lint // Logic classes can declare the data key names that specify hotkeys. if (node.Key == "Logic" && node.Value.Nodes.Length > 0) { - var typeNames = FieldLoader.GetValue(node.Key, node.Value.Value); + var typeNames = FieldLoader.GetValue>(node.Key, node.Value.Value); var checkArgKeys = new List(); foreach (var typeName in typeNames) { diff --git a/OpenRA.Mods.Common/Lint/CheckChromeLogic.cs b/OpenRA.Mods.Common/Lint/CheckChromeLogic.cs index 5ad6072dd6..4e29fd56b8 100644 --- a/OpenRA.Mods.Common/Lint/CheckChromeLogic.cs +++ b/OpenRA.Mods.Common/Lint/CheckChromeLogic.cs @@ -11,6 +11,7 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using OpenRA.Widgets; namespace OpenRA.Mods.Common.Lint @@ -32,7 +33,7 @@ namespace OpenRA.Mods.Common.Lint if (node.Key == "Logic") { - var typeNames = FieldLoader.GetValue(node.Key, node.Value.Value); + var typeNames = FieldLoader.GetValue>(node.Key, node.Value.Value); foreach (var typeName in typeNames) { var type = Game.ModData.ObjectCreator.FindType(typeName); diff --git a/OpenRA.Mods.Common/Lint/CheckFluentReferences.cs b/OpenRA.Mods.Common/Lint/CheckFluentReferences.cs index f4a216aa46..ef8a27e265 100644 --- a/OpenRA.Mods.Common/Lint/CheckFluentReferences.cs +++ b/OpenRA.Mods.Common/Lint/CheckFluentReferences.cs @@ -12,6 +12,7 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Collections.Immutable; using System.IO; using System.Linq; using System.Reflection; @@ -39,7 +40,7 @@ namespace OpenRA.Mods.Common.Lint foreach (var context in usedKeys.EmptyKeyContexts) emitWarning($"Empty key in map ftl files required by {context}"); - var mapMessages = FieldLoader.GetValue("value", map.FluentMessageDefinitions.Value); + var mapMessages = FieldLoader.GetValue>("value", map.FluentMessageDefinitions.Value); var modMessages = modData.Manifest.FluentMessages; // For maps we don't warn on unused keys. They might be unused on *this* map, @@ -363,7 +364,7 @@ namespace OpenRA.Mods.Common.Lint { if (childNode.Key == "Logic") { - foreach (var logicName in FieldLoader.GetValue(childNode.Key, childNode.Value.Value)) + foreach (var logicName in FieldLoader.GetValue>(childNode.Key, childNode.Value.Value)) { var logicType = modData.ObjectCreator.FindType(logicName); if (logicType == null) diff --git a/OpenRA.Mods.Common/Lint/CheckFluentSyntax.cs b/OpenRA.Mods.Common/Lint/CheckFluentSyntax.cs index 97fd828abf..15fce15a21 100644 --- a/OpenRA.Mods.Common/Lint/CheckFluentSyntax.cs +++ b/OpenRA.Mods.Common/Lint/CheckFluentSyntax.cs @@ -11,6 +11,7 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.IO; using Linguini.Syntax.Ast; using Linguini.Syntax.Parser; @@ -25,7 +26,7 @@ namespace OpenRA.Mods.Common.Lint if (map.FluentMessageDefinitions == null) return; - Run(emitError, emitWarning, map, FieldLoader.GetValue("value", map.FluentMessageDefinitions.Value)); + Run(emitError, emitWarning, map, FieldLoader.GetValue>("value", map.FluentMessageDefinitions.Value)); } void ILintPass.Run(Action emitError, Action emitWarning, ModData modData) diff --git a/OpenRA.Mods.Common/Lint/CheckInteractable.cs b/OpenRA.Mods.Common/Lint/CheckInteractable.cs index 2919bffc37..33b06b735f 100644 --- a/OpenRA.Mods.Common/Lint/CheckInteractable.cs +++ b/OpenRA.Mods.Common/Lint/CheckInteractable.cs @@ -10,6 +10,7 @@ #endregion using System; +using System.Collections.Immutable; using System.Linq; using OpenRA.Mods.Common.Traits; using OpenRA.Primitives; @@ -60,7 +61,7 @@ namespace OpenRA.Mods.Common.Lint } } - static bool HasInvalidBounds(WDist[] bounds, Size tileSize, int tileScale) + static bool HasInvalidBounds(ImmutableArray bounds, Size tileSize, int tileScale) { if (bounds == null) return false; diff --git a/OpenRA.Mods.Common/Lint/CheckMapMetadata.cs b/OpenRA.Mods.Common/Lint/CheckMapMetadata.cs index e9dcd12012..77222967dc 100644 --- a/OpenRA.Mods.Common/Lint/CheckMapMetadata.cs +++ b/OpenRA.Mods.Common/Lint/CheckMapMetadata.cs @@ -10,6 +10,7 @@ #endregion using System; +using System.Collections.Immutable; using OpenRA.Server; namespace OpenRA.Mods.Common.Lint @@ -26,7 +27,7 @@ namespace OpenRA.Mods.Common.Lint Run(emitError, map.MapFormat, map.Author, map.Title, map.Categories); } - static void Run(Action emitError, int mapFormat, string author, string title, string[] categories) + static void Run(Action emitError, int mapFormat, string author, string title, ImmutableArray categories) { if (mapFormat < Map.SupportedMapFormat) emitError($"Map format `{mapFormat}` does not match the supported version `{Map.CurrentMapFormat}`."); diff --git a/OpenRA.Mods.Common/Lint/CheckPlayers.cs b/OpenRA.Mods.Common/Lint/CheckPlayers.cs index 37ddb33e68..9189a97e47 100644 --- a/OpenRA.Mods.Common/Lint/CheckPlayers.cs +++ b/OpenRA.Mods.Common/Lint/CheckPlayers.cs @@ -11,6 +11,7 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using OpenRA.Mods.Common.Traits; using OpenRA.Server; @@ -30,7 +31,7 @@ namespace OpenRA.Mods.Common.Lint spawns.Add(s.Get().Value); } - Run(emitError, emitWarning, players, map.Visibility, map.Rules.Actors[SystemActors.World], spawns.ToArray()); + Run(emitError, emitWarning, players, map.Visibility, map.Rules.Actors[SystemActors.World], spawns.ToImmutableArray()); } void ILintServerMapPass.Run(Action emitError, Action emitWarning, ModData modData, MapPreview map, Ruleset mapRules) @@ -39,7 +40,7 @@ namespace OpenRA.Mods.Common.Lint } static void Run(Action emitError, Action emitWarning, - MapPlayers players, MapVisibility visibility, ActorInfo worldActorInfo, CPos[] spawnPoints) + MapPlayers players, MapVisibility visibility, ActorInfo worldActorInfo, ImmutableArray spawnPoints) { if (players.Players.Count > 64) emitError("Defining more than 64 players is not allowed."); diff --git a/OpenRA.Mods.Common/Lint/CheckUnknownTraitFields.cs b/OpenRA.Mods.Common/Lint/CheckUnknownTraitFields.cs index d30221174b..8d59e98aaa 100644 --- a/OpenRA.Mods.Common/Lint/CheckUnknownTraitFields.cs +++ b/OpenRA.Mods.Common/Lint/CheckUnknownTraitFields.cs @@ -11,6 +11,7 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using OpenRA.FileSystem; using OpenRA.Server; @@ -94,7 +95,7 @@ namespace OpenRA.Mods.Common.Lint if (ruleDefinitions == null) return; - var mapFiles = FieldLoader.GetValue("value", ruleDefinitions.Value); + var mapFiles = FieldLoader.GetValue>("value", ruleDefinitions.Value); foreach (var f in mapFiles) CheckActors(MiniYaml.FromStream(fileSystem.Open(f), f), emitError, modData); diff --git a/OpenRA.Mods.Common/Lint/CheckUnknownWeaponFields.cs b/OpenRA.Mods.Common/Lint/CheckUnknownWeaponFields.cs index 4707d52869..6171c5ef63 100644 --- a/OpenRA.Mods.Common/Lint/CheckUnknownWeaponFields.cs +++ b/OpenRA.Mods.Common/Lint/CheckUnknownWeaponFields.cs @@ -11,6 +11,7 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using OpenRA.FileSystem; using OpenRA.GameRules; using OpenRA.Server; @@ -115,7 +116,7 @@ namespace OpenRA.Mods.Common.Lint if (weaponDefinitions == null) return; - var mapFiles = FieldLoader.GetValue("value", weaponDefinitions.Value); + var mapFiles = FieldLoader.GetValue>("value", weaponDefinitions.Value); foreach (var f in mapFiles) CheckWeapons(MiniYaml.FromStream(fileSystem.Open(f), f), emitError, emitWarning, modData); diff --git a/OpenRA.Mods.Common/Lint/CheckWorldAndPlayerInherits.cs b/OpenRA.Mods.Common/Lint/CheckWorldAndPlayerInherits.cs index 1af91323bc..84b0e97fe5 100644 --- a/OpenRA.Mods.Common/Lint/CheckWorldAndPlayerInherits.cs +++ b/OpenRA.Mods.Common/Lint/CheckWorldAndPlayerInherits.cs @@ -11,6 +11,7 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using OpenRA.FileSystem; using OpenRA.Mods.Common.UpdateRules; @@ -47,8 +48,8 @@ namespace OpenRA.Mods.Common.Lint var files = modData.Manifest.Rules.AsEnumerable(); if (ruleDefinitions.Value != null) { - var mapFiles = FieldLoader.GetValue("value", ruleDefinitions.Value); - files = files.Append(mapFiles); + var mapFiles = FieldLoader.GetValue>("value", ruleDefinitions.Value); + files = files.Concat(mapFiles); } var nodes = new List(); diff --git a/OpenRA.Mods.Common/MapGenerator/MapGeneratorSettings.cs b/OpenRA.Mods.Common/MapGenerator/MapGeneratorSettings.cs index f0bb425ea9..b4d1d8ff27 100644 --- a/OpenRA.Mods.Common/MapGenerator/MapGeneratorSettings.cs +++ b/OpenRA.Mods.Common/MapGenerator/MapGeneratorSettings.cs @@ -10,6 +10,7 @@ #endregion using System; +using System.Collections.Frozen; using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; @@ -85,16 +86,16 @@ namespace OpenRA.Mods.Common.MapGenerator public class MapGeneratorDropdownChoice { public readonly string Label = null; - public readonly string[] Tileset = null; - public readonly int[] Players = null; + public readonly ImmutableArray Tileset = default; + public readonly ImmutableArray Players = default; [FieldLoader.LoadUsing(nameof(LoadSettings))] [FieldLoader.Require] - public readonly ImmutableList Settings = null; + public readonly ImmutableArray Settings = default; - static ImmutableList LoadSettings(MiniYaml yaml) + static object LoadSettings(MiniYaml yaml) { - return yaml.NodeWithKey("Settings").Value.Nodes.ToImmutableList(); + return yaml.NodeWithKey("Settings").Value.Nodes.ToImmutableArray(); } } @@ -114,7 +115,7 @@ namespace OpenRA.Mods.Common.MapGenerator return ret; } - public readonly string[] Default = null; + public readonly ImmutableArray Default = default; string value = null; public MapGeneratorMultiChoiceOption(string id, MiniYaml yaml) @@ -126,7 +127,7 @@ namespace OpenRA.Mods.Common.MapGenerator if (validChoices.Contains(value)) return Choices[value].Settings; - var fallback = Default?.FirstOrDefault(validChoices.Contains) ?? validChoices.FirstOrDefault(); + var fallback = Default != null ? Default.FirstOrDefault(validChoices.Contains) : validChoices.FirstOrDefault(); return fallback != null ? Choices[fallback].Settings : []; } @@ -146,8 +147,8 @@ namespace OpenRA.Mods.Common.MapGenerator { return Choices .Where(kv => - (kv.Value.Tileset?.Contains(terrainInfo.Id) ?? true) && - (kv.Value.Players?.Contains(playerCount) ?? true)) + (kv.Value.Tileset == null || kv.Value.Tileset.Contains(terrainInfo.Id)) && + (kv.Value.Players == null || kv.Value.Players.Contains(playerCount))) .Select(kv => kv.Key) .ToList(); } @@ -177,7 +178,7 @@ namespace OpenRA.Mods.Common.MapGenerator public readonly string Parameter = null; [FieldLoader.Require] - public readonly int[] Choices = null; + public readonly ImmutableArray Choices = default; public readonly int? Default = null; int value; @@ -185,7 +186,7 @@ namespace OpenRA.Mods.Common.MapGenerator public MapGeneratorMultiIntegerChoiceOption(string id, MiniYaml yaml) : base(id, yaml) { - Value = Default ?? Choices?.First() ?? 0; + Value = Default ?? (Choices != null ? Choices[0] : 0); } public int Value @@ -210,7 +211,7 @@ namespace OpenRA.Mods.Common.MapGenerator { sealed class MapGenerationArgsWithOptions : MapGenerationArgs { - public Dictionary Options = []; + public FrozenDictionary Options = FrozenDictionary.Empty; } readonly IMapGeneratorInfo generatorInfo; @@ -218,6 +219,8 @@ namespace OpenRA.Mods.Common.MapGenerator public MapGeneratorSettings(IMapGeneratorInfo generatorInfo, MiniYaml yaml) { this.generatorInfo = generatorInfo; + + var options = new List(); foreach (var node in yaml.Nodes) { var split = node.Key.Split('@'); @@ -225,17 +228,19 @@ namespace OpenRA.Mods.Common.MapGenerator continue; if (split[0] == "BooleanOption") - Options.Add(new MapGeneratorBooleanOption(split[1], node.Value)); + options.Add(new MapGeneratorBooleanOption(split[1], node.Value)); else if (split[0] == "IntegerOption") - Options.Add(new MapGeneratorIntegerOption(split[1], node.Value)); + options.Add(new MapGeneratorIntegerOption(split[1], node.Value)); else if (split[0] == "MultiIntegerChoiceOption") - Options.Add(new MapGeneratorMultiIntegerChoiceOption(split[1], node.Value)); + options.Add(new MapGeneratorMultiIntegerChoiceOption(split[1], node.Value)); else if (split[0] == "MultiChoiceOption") - Options.Add(new MapGeneratorMultiChoiceOption(split[1], node.Value)); + options.Add(new MapGeneratorMultiChoiceOption(split[1], node.Value)); } + + Options = options.ToImmutableArray(); } - public List Options { get; } = []; + public ImmutableArray Options { get; } = []; public void Randomize(MersenneTwister random) { @@ -306,7 +311,7 @@ namespace OpenRA.Mods.Common.MapGenerator Title = FluentProvider.GetMessage(generatorInfo.MapTitle), Author = FluentProvider.GetMessage(generatorInfo.Name), Settings = new MiniYaml(null, MiniYaml.Merge(layers)), - Options = options + Options = options.ToFrozenDictionary() }; } } diff --git a/OpenRA.Mods.Common/MapGenerator/Terraformer.cs b/OpenRA.Mods.Common/MapGenerator/Terraformer.cs index 6e86212fc0..9dab8c6eea 100644 --- a/OpenRA.Mods.Common/MapGenerator/Terraformer.cs +++ b/OpenRA.Mods.Common/MapGenerator/Terraformer.cs @@ -1951,7 +1951,7 @@ namespace OpenRA.Mods.Common.MapGenerator var allowedTerrainResourceCombos = resourceTypes .SelectMany(resourceTypeInfo => resourceTypeInfo.AllowedTerrainTypes .Select(terrainName => (resourceTypeInfo, terrainInfo.GetTerrainIndex(terrainName)))) - .ToImmutableHashSet(); + .ToHashSet(); var strengths = new Dictionary>(); foreach (var resourceType in resourceTypes) diff --git a/OpenRA.Mods.Common/MapGenerator/TilingPath.cs b/OpenRA.Mods.Common/MapGenerator/TilingPath.cs index 74ae15ae07..d2891e7312 100644 --- a/OpenRA.Mods.Common/MapGenerator/TilingPath.cs +++ b/OpenRA.Mods.Common/MapGenerator/TilingPath.cs @@ -597,10 +597,9 @@ namespace OpenRA.Mods.Common.MapGenerator var pathStart = points[0]; var pathEnd = points[^1]; var orderedPermittedBrushes = Brushes.All.ToImmutableArray(); - var permittedBrushes = orderedPermittedBrushes.ToImmutableHashSet(); - var permittedStartBrushes = Brushes.Start.ToImmutableHashSet(); - var permittedInnerBrushes = Brushes.Inner.ToImmutableHashSet(); - var permittedEndBrushes = Brushes.End.ToImmutableHashSet(); + var permittedStartBrushes = Brushes.Start.ToHashSet(); + var permittedInnerBrushes = Brushes.Inner.ToHashSet(); + var permittedEndBrushes = Brushes.End.ToHashSet(); const int MaxCost = int.MaxValue; var segmentTypeToId = new Dictionary(); diff --git a/OpenRA.Mods.Common/ModContent.cs b/OpenRA.Mods.Common/ModContent.cs index 95ec9d0659..fb8050c503 100644 --- a/OpenRA.Mods.Common/ModContent.cs +++ b/OpenRA.Mods.Common/ModContent.cs @@ -9,6 +9,7 @@ */ #endregion +using System.Collections.Frozen; using System.Collections.Generic; using System.Collections.Immutable; using System.IO; @@ -24,8 +25,8 @@ namespace OpenRA.Mods.Common [FluentReference] public readonly string Title; public readonly string Identifier; - public readonly string[] TestFiles = []; - public readonly string[] Sources = []; + public readonly ImmutableArray TestFiles = []; + public readonly ImmutableArray Sources = []; public readonly bool Required; public readonly string Download; @@ -46,7 +47,7 @@ namespace OpenRA.Mods.Common public readonly MiniYaml Type; // Used to find installation locations for SourceType.Install - public readonly string[] RegistryPrefixes = [string.Empty]; + public readonly ImmutableArray RegistryPrefixes = [string.Empty]; public readonly string RegistryKey; public readonly string RegistryValue; @@ -87,7 +88,7 @@ namespace OpenRA.Mods.Common public readonly string MirrorList; public readonly string SHA1; public readonly string Type; - public readonly Dictionary Extract; + public readonly FrozenDictionary Extract; public ModDownload(MiniYaml yaml) { @@ -103,35 +104,35 @@ namespace OpenRA.Mods.Common [IncludeFluentReferences(LintDictionaryReference.Values)] [FieldLoader.LoadUsing(nameof(LoadPackages))] - public readonly Dictionary Packages = []; + public readonly ImmutableArray> Packages = []; static object LoadPackages(MiniYaml yaml) { - var packages = new Dictionary(); + var packages = new List>(); var packageNode = yaml.NodeWithKeyOrDefault("Packages"); if (packageNode != null) foreach (var node in packageNode.Value.Nodes) - packages.Add(node.Key, new ModPackage(node.Value)); + packages.Add(KeyValuePair.Create(node.Key, new ModPackage(node.Value))); - return packages; + return packages.ToImmutableArray(); } [FieldLoader.LoadUsing(nameof(LoadDownloads))] - public readonly string[] Downloads = []; + public readonly ImmutableArray Downloads = []; static object LoadDownloads(MiniYaml yaml) { var downloadNode = yaml.NodeWithKeyOrDefault("Downloads"); - return downloadNode != null ? downloadNode.Value.Nodes.Select(n => n.Key).ToArray() : []; + return downloadNode != null ? downloadNode.Value.Nodes.Select(n => n.Key).ToImmutableArray() : []; } [FieldLoader.LoadUsing(nameof(LoadSources))] - public readonly string[] Sources = []; + public readonly ImmutableArray Sources = []; static object LoadSources(MiniYaml yaml) { var sourceNode = yaml.NodeWithKeyOrDefault("Sources"); - return sourceNode != null ? sourceNode.Value.Nodes.Select(n => n.Key).ToArray() : []; + return sourceNode != null ? sourceNode.Value.Nodes.Select(n => n.Key).ToImmutableArray() : []; } } } diff --git a/OpenRA.Mods.Common/Projectiles/AreaBeam.cs b/OpenRA.Mods.Common/Projectiles/AreaBeam.cs index 0f4fbb8964..6a38ea1970 100644 --- a/OpenRA.Mods.Common/Projectiles/AreaBeam.cs +++ b/OpenRA.Mods.Common/Projectiles/AreaBeam.cs @@ -11,6 +11,7 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using OpenRA.GameRules; using OpenRA.Graphics; @@ -25,7 +26,7 @@ namespace OpenRA.Mods.Common.Projectiles public class AreaBeamInfo : IProjectileInfo { [Desc("Projectile speed in WDist / tick, two values indicate a randomly picked velocity per beam.")] - public readonly WDist[] Speed = [new(128)]; + public readonly ImmutableArray Speed = [new(128)]; [Desc("The maximum duration (in ticks) of each beam burst.")] public readonly int Duration = 10; @@ -46,10 +47,10 @@ namespace OpenRA.Mods.Common.Projectiles public readonly WDist MinDistance = WDist.Zero; [Desc("Damage modifier applied at each range step.")] - public readonly int[] Falloff = [100, 100]; + public readonly ImmutableArray Falloff = [100, 100]; [Desc("Ranges at which each Falloff step is defined.")] - public readonly WDist[] Range = [WDist.Zero, new(int.MaxValue)]; + public readonly ImmutableArray Range = [WDist.Zero, new(int.MaxValue)]; [Desc("The maximum/constant/incremental inaccuracy used in conjunction with the InaccuracyType property.")] public readonly WDist Inaccuracy = WDist.Zero; diff --git a/OpenRA.Mods.Common/Projectiles/Bullet.cs b/OpenRA.Mods.Common/Projectiles/Bullet.cs index ed1cc8b44a..987d608c54 100644 --- a/OpenRA.Mods.Common/Projectiles/Bullet.cs +++ b/OpenRA.Mods.Common/Projectiles/Bullet.cs @@ -10,7 +10,9 @@ #endregion using System; +using System.Collections.Frozen; using System.Collections.Generic; +using System.Collections.Immutable; using OpenRA.GameRules; using OpenRA.Graphics; using OpenRA.Mods.Common.Effects; @@ -25,7 +27,7 @@ namespace OpenRA.Mods.Common.Projectiles public class BulletInfo : IProjectileInfo { [Desc("Projectile speed in WDist / tick, two values indicate variable velocity.")] - public readonly WDist[] Speed = [new(17)]; + public readonly ImmutableArray Speed = [new(17)]; [Desc("The maximum/constant/incremental inaccuracy used in conjunction with the InaccuracyType property.")] public readonly WDist Inaccuracy = WDist.Zero; @@ -41,7 +43,7 @@ namespace OpenRA.Mods.Common.Projectiles [SequenceReference(nameof(Image), allowNullImage: true)] [Desc("Loop a randomly chosen sequence of Image from this list while this projectile is moving.")] - public readonly string[] Sequences = ["idle"]; + public readonly ImmutableArray Sequences = ["idle"]; [PaletteReference(nameof(IsPlayerPalette))] [Desc("The palette used to draw this projectile.")] @@ -61,7 +63,7 @@ namespace OpenRA.Mods.Common.Projectiles [SequenceReference(nameof(TrailImage), allowNullImage: true)] [Desc("Loop a randomly chosen sequence of TrailImage from this list while this projectile is moving.")] - public readonly string[] TrailSequences = ["idle"]; + public readonly ImmutableArray TrailSequences = ["idle"]; [Desc("Interval in ticks between each spawned Trail animation.")] public readonly int TrailInterval = 2; @@ -83,7 +85,7 @@ namespace OpenRA.Mods.Common.Projectiles public readonly WDist Width = new(1); [Desc("Arc in WAngles, two values indicate variable arc.")] - public readonly WAngle[] LaunchAngle = [WAngle.Zero]; + public readonly ImmutableArray LaunchAngle = [WAngle.Zero]; [Desc("Up to how many times does this bullet bounce when touching ground without hitting a target.", "0 implies exploding on contact with the originally targeted position.")] @@ -96,7 +98,7 @@ namespace OpenRA.Mods.Common.Projectiles public readonly string BounceSound = null; [Desc("Terrain where the projectile explodes instead of bouncing.")] - public readonly HashSet InvalidBounceTerrain = []; + public readonly FrozenSet InvalidBounceTerrain = FrozenSet.Empty; [Desc("Trigger the explosion if the projectile touches an actor thats owner has these player relationships.")] public readonly PlayerRelationship ValidBounceBlockerRelationships = PlayerRelationship.Enemy | PlayerRelationship.Neutral; diff --git a/OpenRA.Mods.Common/Projectiles/GravityBomb.cs b/OpenRA.Mods.Common/Projectiles/GravityBomb.cs index 8fc1e77219..ec783ac030 100644 --- a/OpenRA.Mods.Common/Projectiles/GravityBomb.cs +++ b/OpenRA.Mods.Common/Projectiles/GravityBomb.cs @@ -10,6 +10,7 @@ #endregion using System.Collections.Generic; +using System.Collections.Immutable; using OpenRA.GameRules; using OpenRA.Graphics; using OpenRA.Primitives; @@ -24,7 +25,7 @@ namespace OpenRA.Mods.Common.Projectiles [SequenceReference(nameof(Image), allowNullImage: true)] [Desc("Loop a randomly chosen sequence of Image from this list while falling.")] - public readonly string[] Sequences = ["idle"]; + public readonly ImmutableArray Sequences = ["idle"]; [SequenceReference(nameof(Image), allowNullImage: true)] [Desc("Sequence to play when launched. Skipped if null or empty.")] diff --git a/OpenRA.Mods.Common/Projectiles/Missile.cs b/OpenRA.Mods.Common/Projectiles/Missile.cs index d28a16c28f..1167ba1436 100644 --- a/OpenRA.Mods.Common/Projectiles/Missile.cs +++ b/OpenRA.Mods.Common/Projectiles/Missile.cs @@ -10,6 +10,7 @@ #endregion using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using OpenRA.GameRules; using OpenRA.Graphics; @@ -29,7 +30,7 @@ namespace OpenRA.Mods.Common.Projectiles [SequenceReference(nameof(Image), allowNullImage: true)] [Desc("Loop a randomly chosen sequence of Image from this list while this projectile is moving.")] - public readonly string[] Sequences = ["idle"]; + public readonly ImmutableArray Sequences = ["idle"]; [PaletteReference(nameof(IsPlayerPalette))] [Desc("Palette used to render the projectile sequence.")] @@ -118,7 +119,7 @@ namespace OpenRA.Mods.Common.Projectiles [SequenceReference(nameof(TrailImage), allowNullImage: true)] [Desc("Loop a randomly chosen sequence of TrailImage from this list while this projectile is moving.")] - public readonly string[] TrailSequences = ["idle"]; + public readonly ImmutableArray TrailSequences = ["idle"]; [PaletteReference(nameof(TrailUsePlayerPalette))] [Desc("Palette used to render the trail sequence.")] diff --git a/OpenRA.Mods.Common/Projectiles/NukeLaunch.cs b/OpenRA.Mods.Common/Projectiles/NukeLaunch.cs index 90d35d89ef..761fdeb7f3 100644 --- a/OpenRA.Mods.Common/Projectiles/NukeLaunch.cs +++ b/OpenRA.Mods.Common/Projectiles/NukeLaunch.cs @@ -10,6 +10,7 @@ #endregion using System.Collections.Generic; +using System.Collections.Immutable; using OpenRA.Effects; using OpenRA.GameRules; using OpenRA.Graphics; @@ -35,7 +36,7 @@ namespace OpenRA.Mods.Common.Effects readonly int impactDelay; readonly int turn; readonly string trailImage; - readonly string[] trailSequences; + readonly ImmutableArray trailSequences; readonly string trailPalette; readonly int trailInterval; readonly int trailDelay; @@ -49,7 +50,7 @@ namespace OpenRA.Mods.Common.Effects public NukeLaunch(Player firedBy, string image, WeaponInfo weapon, string weaponPalette, string upSequence, string downSequence, WPos launchPos, WPos targetPos, WDist detonationAltitude, bool removeOnDetonation, WDist velocity, int launchDelay, int impactDelay, bool skipAscent, - string trailImage, string[] trailSequences, string trailPalette, bool trailUsePlayerPalette, int trailDelay, int trailInterval) + string trailImage, ImmutableArray trailSequences, string trailPalette, bool trailUsePlayerPalette, int trailDelay, int trailInterval) { this.firedBy = firedBy; this.weapon = weapon; diff --git a/OpenRA.Mods.Common/Scripting/LuaScript.cs b/OpenRA.Mods.Common/Scripting/LuaScript.cs index 204863d1d6..50ce07116e 100644 --- a/OpenRA.Mods.Common/Scripting/LuaScript.cs +++ b/OpenRA.Mods.Common/Scripting/LuaScript.cs @@ -9,7 +9,7 @@ */ #endregion -using System.Collections.Generic; +using System.Collections.Frozen; using System.Linq; using OpenRA.Graphics; using OpenRA.Mods.Common.Traits; @@ -23,7 +23,7 @@ namespace OpenRA.Mods.Common.Scripting public class LuaScriptInfo : TraitInfo, Requires, NotBefore { [Desc("File names with location relative to the map.")] - public readonly HashSet Scripts = []; + public readonly FrozenSet Scripts = FrozenSet.Empty; public override object Create(ActorInitializer init) { return new LuaScript(this); } } diff --git a/OpenRA.Mods.Common/ServerTraits/LobbyCommands.cs b/OpenRA.Mods.Common/ServerTraits/LobbyCommands.cs index c6495219bc..c26cb4f6bb 100644 --- a/OpenRA.Mods.Common/ServerTraits/LobbyCommands.cs +++ b/OpenRA.Mods.Common/ServerTraits/LobbyCommands.cs @@ -10,6 +10,7 @@ #endregion using System; +using System.Collections.Frozen; using System.Collections.Generic; using System.Diagnostics; using System.Linq; @@ -1298,13 +1299,13 @@ namespace OpenRA.Mods.Common.Server return; var mapCache = server.ModData.MapCache; - if (server.Settings.MapPool.Length > 0) - server.MapPool = server.Settings.MapPool.ToHashSet(); + if (server.Settings.MapPool.Count > 0) + server.MapPool = server.Settings.MapPool; else if (!server.Settings.QueryMapRepository) server.MapPool = mapCache .Where(p => p.Status == MapStatus.Available && p.Visibility.HasFlag(MapVisibility.Lobby)) .Select(p => p.Uid) - .ToHashSet(); + .ToFrozenSet(); else return; diff --git a/OpenRA.Mods.Common/Terrain/DefaultTerrain.cs b/OpenRA.Mods.Common/Terrain/DefaultTerrain.cs index d21648c6d9..5142be3eba 100644 --- a/OpenRA.Mods.Common/Terrain/DefaultTerrain.cs +++ b/OpenRA.Mods.Common/Terrain/DefaultTerrain.cs @@ -9,6 +9,7 @@ */ #endregion +using System.Collections.Frozen; using System.Collections.Generic; using System.Collections.Immutable; using System.IO; @@ -40,9 +41,9 @@ namespace OpenRA.Mods.Common.Terrain public class DefaultTerrainTemplateInfo : TerrainTemplateInfo { - public readonly string[] Images; - public readonly string[] DepthImages; - public readonly int[] Frames; + public readonly ImmutableArray Images; + public readonly ImmutableArray DepthImages; + public readonly ImmutableArray Frames; public readonly string Palette; public DefaultTerrainTemplateInfo(ITerrainInfo terrainInfo, MiniYaml my) @@ -75,8 +76,8 @@ namespace OpenRA.Mods.Common.Terrain public readonly string Id; public readonly Size TileSize = new(24, 24); public readonly int SheetSize = 512; - public readonly Color[] HeightDebugColors = [Color.Red]; - public readonly string[] EditorTemplateOrder; + public readonly ImmutableArray HeightDebugColors = [Color.Red]; + public readonly ImmutableArray EditorTemplateOrder; public readonly bool IgnoreTileSpriteOffsets; public readonly bool EnableDepth = false; public readonly float MinHeightColorBrightness = 1.0f; @@ -84,13 +85,13 @@ namespace OpenRA.Mods.Common.Terrain public readonly string Palette = TileSet.TerrainPaletteInternalName; [FieldLoader.Ignore] - public readonly IReadOnlyDictionary Templates; + public readonly FrozenDictionary Templates; [FieldLoader.Ignore] - public readonly IReadOnlyDictionary> MultiBrushCollections; + public readonly FrozenDictionary> MultiBrushCollections; [FieldLoader.Ignore] - public readonly TerrainTypeInfo[] TerrainInfo; - readonly Dictionary terrainIndexByType = []; + public readonly ImmutableArray TerrainInfo; + readonly FrozenDictionary terrainIndexByType; readonly byte defaultWalkableTerrainIndex; public DefaultTerrain(IReadOnlyFileSystem fileSystem, string filepath) @@ -105,35 +106,38 @@ namespace OpenRA.Mods.Common.Terrain TerrainInfo = yaml["Terrain"].ToDictionary().Values .Select(y => new TerrainTypeInfo(y)) .OrderBy(tt => tt.Type) - .ToArray(); + .ToImmutableArray(); if (TerrainInfo.Length >= byte.MaxValue) throw new YamlException("Too many terrain types."); + var tiby = new Dictionary(TerrainInfo.Length); for (byte i = 0; i < TerrainInfo.Length; i++) { var tt = TerrainInfo[i].Type; - if (terrainIndexByType.ContainsKey(tt)) + if (!tiby.TryAdd(tt, i)) throw new YamlException($"Duplicate terrain type '{tt}' in '{filepath}'."); - - terrainIndexByType.Add(tt, i); } + terrainIndexByType = tiby.ToFrozenDictionary(); + defaultWalkableTerrainIndex = GetTerrainIndex("Clear"); // Templates Templates = yaml["Templates"].ToDictionary().Values - .Select(y => (TerrainTemplateInfo)new DefaultTerrainTemplateInfo(this, y)).ToDictionary(t => t.Id); + .Select(y => (TerrainTemplateInfo)new DefaultTerrainTemplateInfo(this, y)) + .ToDictionary(t => t.Id) + .ToFrozenDictionary(); MultiBrushCollections = yaml.TryGetValue("MultiBrushCollections", out var collectionDefinitions) ? collectionDefinitions.ToDictionary() - .Select(kv => new KeyValuePair>( + .Select(kv => new KeyValuePair>( kv.Key, MultiBrushInfo.ParseCollection(kv.Value))) - .ToImmutableDictionary() - : ImmutableDictionary>.Empty; + .ToFrozenDictionary() + : FrozenDictionary>.Empty; } public TerrainTypeInfo this[byte index] => TerrainInfo[index]; @@ -175,19 +179,19 @@ namespace OpenRA.Mods.Common.Terrain string ITerrainInfo.Id => Id; string ITerrainInfo.Name => Name; Size ITerrainInfo.TileSize => TileSize; - TerrainTypeInfo[] ITerrainInfo.TerrainTypes => TerrainInfo; + ImmutableArray ITerrainInfo.TerrainTypes => TerrainInfo; TerrainTileInfo ITerrainInfo.GetTerrainInfo(TerrainTile r) { return GetTileInfo(r); } bool ITerrainInfo.TryGetTerrainInfo(TerrainTile r, out TerrainTileInfo info) { return TryGetTileInfo(r, out info); } - Color[] ITerrainInfo.HeightDebugColors => HeightDebugColors; + ImmutableArray ITerrainInfo.HeightDebugColors => HeightDebugColors; IEnumerable ITerrainInfo.RestrictedPlayerColors { get { return TerrainInfo.Where(ti => ti.RestrictPlayerColor).Select(ti => ti.Color); } } float ITerrainInfo.MinHeightColorBrightness => MinHeightColorBrightness; float ITerrainInfo.MaxHeightColorBrightness => MaxHeightColorBrightness; TerrainTile ITerrainInfo.DefaultTerrainTile => new(Templates.First().Key, 0); - string[] ITemplatedTerrainInfo.EditorTemplateOrder => EditorTemplateOrder; - IReadOnlyDictionary ITemplatedTerrainInfo.Templates => Templates; - IReadOnlyDictionary> ITemplatedTerrainInfo.MultiBrushCollections => MultiBrushCollections; + ImmutableArray ITemplatedTerrainInfo.EditorTemplateOrder => EditorTemplateOrder; + FrozenDictionary ITemplatedTerrainInfo.Templates => Templates; + FrozenDictionary> ITemplatedTerrainInfo.MultiBrushCollections => MultiBrushCollections; void IDumpSheetsTerrainInfo.DumpSheets(string terrainName, ImmutablePalette palette, ref int sheetCount) { diff --git a/OpenRA.Mods.Common/Terrain/DefaultTileCache.cs b/OpenRA.Mods.Common/Terrain/DefaultTileCache.cs index 3ffcb85dcd..152c44787b 100644 --- a/OpenRA.Mods.Common/Terrain/DefaultTileCache.cs +++ b/OpenRA.Mods.Common/Terrain/DefaultTileCache.cs @@ -11,6 +11,7 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.IO; using System.Linq; using OpenRA.Graphics; @@ -83,7 +84,7 @@ namespace OpenRA.Mods.Common.Terrain } var frameCount = terrainInfo.EnableDepth && depthFrames == null ? allFrames.Length / 2 : allFrames.Length; - var indices = templateInfo.Frames ?? Exts.MakeArray(t.Value.TilesCount, j => j); + var indices = templateInfo.Frames != null ? templateInfo.Frames : Exts.MakeArray(t.Value.TilesCount, j => j).ToImmutableArray(); var start = indices.Min(); var end = indices.Max(); diff --git a/OpenRA.Mods.Common/Terrain/TerrainInfo.cs b/OpenRA.Mods.Common/Terrain/TerrainInfo.cs index f1dd033fc4..fb969668d0 100644 --- a/OpenRA.Mods.Common/Terrain/TerrainInfo.cs +++ b/OpenRA.Mods.Common/Terrain/TerrainInfo.cs @@ -9,16 +9,17 @@ */ #endregion -using System.Collections.Generic; +using System.Collections.Frozen; +using System.Collections.Immutable; using OpenRA.Mods.Common.MapGenerator; namespace OpenRA.Mods.Common.Terrain { public interface ITemplatedTerrainInfo : ITerrainInfo { - string[] EditorTemplateOrder { get; } - IReadOnlyDictionary Templates { get; } - IReadOnlyDictionary> MultiBrushCollections { get; } + ImmutableArray EditorTemplateOrder { get; } + FrozenDictionary Templates { get; } + FrozenDictionary> MultiBrushCollections { get; } } public interface ITerrainInfoNotifyMapCreated : ITerrainInfo @@ -31,7 +32,7 @@ namespace OpenRA.Mods.Common.Terrain public readonly ushort Id; public readonly int2 Size; public readonly bool PickAny; - public readonly string[] Categories; + public readonly ImmutableArray Categories; readonly TerrainTileInfo[] tileInfo; diff --git a/OpenRA.Mods.Common/Traits/AcceptsDeliveredCash.cs b/OpenRA.Mods.Common/Traits/AcceptsDeliveredCash.cs index aa152e01f6..0cd24ea861 100644 --- a/OpenRA.Mods.Common/Traits/AcceptsDeliveredCash.cs +++ b/OpenRA.Mods.Common/Traits/AcceptsDeliveredCash.cs @@ -9,7 +9,8 @@ */ #endregion -using System.Collections.Generic; +using System.Collections.Frozen; +using System.Collections.Immutable; using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits @@ -18,13 +19,13 @@ namespace OpenRA.Mods.Common.Traits public class AcceptsDeliveredCashInfo : TraitInfo { [Desc("Accepted `DeliversCash` types. Leave empty to accept all types.")] - public readonly HashSet ValidTypes = []; + public readonly FrozenSet ValidTypes = FrozenSet.Empty; [Desc("Player relationships the owner of the delivering actor needs.")] public readonly PlayerRelationship ValidRelationships = PlayerRelationship.Ally; [Desc("Play a randomly selected sound from this list when accepting cash.")] - public readonly string[] Sounds = []; + public readonly ImmutableArray Sounds = []; public override object Create(ActorInitializer init) { return new AcceptsDeliveredCash(this); } } diff --git a/OpenRA.Mods.Common/Traits/AcceptsDeliveredExperience.cs b/OpenRA.Mods.Common/Traits/AcceptsDeliveredExperience.cs index 6d060affbf..ba0ecd07c5 100644 --- a/OpenRA.Mods.Common/Traits/AcceptsDeliveredExperience.cs +++ b/OpenRA.Mods.Common/Traits/AcceptsDeliveredExperience.cs @@ -9,7 +9,7 @@ */ #endregion -using System.Collections.Generic; +using System.Collections.Frozen; using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits @@ -18,7 +18,7 @@ namespace OpenRA.Mods.Common.Traits public class AcceptsDeliveredExperienceInfo : TraitInfo, Requires { [Desc("Accepted `DeliversExperience` types. Leave empty to accept all types.")] - public readonly HashSet ValidTypes = []; + public readonly FrozenSet ValidTypes = FrozenSet.Empty; [Desc("Player relationships the owner of the delivering actor needs.")] public readonly PlayerRelationship ValidRelationships = PlayerRelationship.Ally; diff --git a/OpenRA.Mods.Common/Traits/ActorSpawner.cs b/OpenRA.Mods.Common/Traits/ActorSpawner.cs index cb2477d613..4822a891d9 100644 --- a/OpenRA.Mods.Common/Traits/ActorSpawner.cs +++ b/OpenRA.Mods.Common/Traits/ActorSpawner.cs @@ -9,7 +9,7 @@ */ #endregion -using System.Collections.Generic; +using System.Collections.Frozen; namespace OpenRA.Mods.Common.Traits { @@ -17,7 +17,7 @@ namespace OpenRA.Mods.Common.Traits public class ActorSpawnerInfo : ConditionalTraitInfo { [Desc("Type of ActorSpawner with which it connects.")] - public readonly HashSet Types = []; + public readonly FrozenSet Types = FrozenSet.Empty; public override object Create(ActorInitializer init) { return new ActorSpawner(this); } } @@ -27,6 +27,6 @@ namespace OpenRA.Mods.Common.Traits public ActorSpawner(ActorSpawnerInfo info) : base(info) { } - public HashSet Types => Info.Types; + public FrozenSet Types => Info.Types; } } diff --git a/OpenRA.Mods.Common/Traits/Air/Aircraft.cs b/OpenRA.Mods.Common/Traits/Air/Aircraft.cs index 5bf03160c3..af209a058a 100644 --- a/OpenRA.Mods.Common/Traits/Air/Aircraft.cs +++ b/OpenRA.Mods.Common/Traits/Air/Aircraft.cs @@ -10,7 +10,9 @@ #endregion using System; +using System.Collections.Frozen; using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using OpenRA.Activities; using OpenRA.Mods.Common.Activities; @@ -83,7 +85,7 @@ namespace OpenRA.Mods.Common.Traits [Desc("Minimum altitude where this aircraft is considered airborne.")] public readonly int MinAirborneAltitude = 1; - public readonly HashSet LandableTerrainTypes = []; + public readonly FrozenSet LandableTerrainTypes = FrozenSet.Empty; [Desc("Can the actor be ordered to move in to shroud?")] public readonly bool MoveIntoShroud = true; @@ -142,10 +144,10 @@ namespace OpenRA.Mods.Common.Traits public readonly WDist AltitudeVelocity = new(43); [Desc("Sounds to play when the actor is taking off.")] - public readonly string[] TakeoffSounds = []; + public readonly ImmutableArray TakeoffSounds = []; [Desc("Sounds to play when the actor is landing.")] - public readonly string[] LandingSounds = []; + public readonly ImmutableArray LandingSounds = []; [Desc("The distance of the resupply base that the aircraft will wait for its turn.")] public readonly WDist WaitDistanceFromResupplyBase = new(3072); diff --git a/OpenRA.Mods.Common/Traits/AmmoPool.cs b/OpenRA.Mods.Common/Traits/AmmoPool.cs index 21a4591d99..1f17e6ac79 100644 --- a/OpenRA.Mods.Common/Traits/AmmoPool.cs +++ b/OpenRA.Mods.Common/Traits/AmmoPool.cs @@ -10,7 +10,7 @@ #endregion using System.Collections.Generic; -using System.Linq; +using System.Collections.Immutable; using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits @@ -22,7 +22,7 @@ namespace OpenRA.Mods.Common.Traits public readonly string Name = "primary"; [Desc("Name(s) of armament(s) that use this pool.")] - public readonly string[] Armaments = ["primary", "secondary"]; + public readonly ImmutableArray Armaments = ["primary", "secondary"]; [Desc("How much ammo does this pool contain when fully loaded.")] public readonly int Ammo = 1; diff --git a/OpenRA.Mods.Common/Traits/Armament.cs b/OpenRA.Mods.Common/Traits/Armament.cs index 7552a08c2c..083405082f 100644 --- a/OpenRA.Mods.Common/Traits/Armament.cs +++ b/OpenRA.Mods.Common/Traits/Armament.cs @@ -11,6 +11,7 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using OpenRA.GameRules; using OpenRA.Mods.Common.Traits.Render; @@ -42,10 +43,10 @@ namespace OpenRA.Mods.Common.Traits [Desc("Muzzle position relative to turret or body, (forward, right, up) triples.", "If weapon Burst = 1, it cycles through all listed offsets, otherwise the offset corresponding to current burst is used.")] - public readonly WVec[] LocalOffset = []; + public readonly ImmutableArray LocalOffset = []; [Desc("Muzzle yaw relative to turret or body.")] - public readonly WAngle[] LocalYaw = []; + public readonly ImmutableArray LocalYaw = []; [Desc("Move the turret backwards when firing.")] public readonly WDist Recoil = WDist.Zero; diff --git a/OpenRA.Mods.Common/Traits/Attack/AttackBase.cs b/OpenRA.Mods.Common/Traits/Attack/AttackBase.cs index bbd7ca72f0..55808a4174 100644 --- a/OpenRA.Mods.Common/Traits/Attack/AttackBase.cs +++ b/OpenRA.Mods.Common/Traits/Attack/AttackBase.cs @@ -11,6 +11,7 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using OpenRA.Activities; using OpenRA.Mods.Common.Activities; @@ -24,7 +25,7 @@ namespace OpenRA.Mods.Common.Traits public abstract class AttackBaseInfo : PausableConditionalTraitInfo { [Desc("Armament names")] - public readonly string[] Armaments = ["primary", "secondary"]; + public readonly ImmutableArray Armaments = ["primary", "secondary"]; [CursorReference] [Desc("Cursor to display when hovering over a valid target.")] diff --git a/OpenRA.Mods.Common/Traits/Attack/AttackGarrisoned.cs b/OpenRA.Mods.Common/Traits/Attack/AttackGarrisoned.cs index f62bea3ba0..dc12e2fcd6 100644 --- a/OpenRA.Mods.Common/Traits/Attack/AttackGarrisoned.cs +++ b/OpenRA.Mods.Common/Traits/Attack/AttackGarrisoned.cs @@ -11,6 +11,7 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using OpenRA.Graphics; using OpenRA.Mods.Common.Traits.Render; @@ -32,17 +33,17 @@ namespace OpenRA.Mods.Common.Traits { [FieldLoader.Require] [Desc("Fire port offsets in local coordinates.")] - public readonly WVec[] PortOffsets = null; + public readonly ImmutableArray PortOffsets = default; [FieldLoader.Require] [Desc("Fire port yaw angles.")] - public readonly WAngle[] PortYaws = null; + public readonly ImmutableArray PortYaws = default; [FieldLoader.Require] [Desc("Fire port yaw cone angle.")] - public readonly WAngle[] PortCones = null; + public readonly ImmutableArray PortCones = default; - public FirePort[] Ports { get; private set; } + public ImmutableArray Ports { get; private set; } [PaletteReference] public readonly string MuzzlePalette = "effect"; @@ -59,11 +60,11 @@ namespace OpenRA.Mods.Common.Traits if (PortCones.Length != PortOffsets.Length) throw new YamlException("PortCones must define an angle for each port."); - Ports = new FirePort[PortOffsets.Length]; + var ports = new FirePort[PortOffsets.Length]; for (var i = 0; i < PortOffsets.Length; i++) { - Ports[i] = new FirePort + ports[i] = new FirePort { Offset = PortOffsets[i], Yaw = PortYaws[i], @@ -71,6 +72,8 @@ namespace OpenRA.Mods.Common.Traits }; } + Ports = ports.ToImmutableArray(); + base.RulesetLoaded(rules, ai); } } diff --git a/OpenRA.Mods.Common/Traits/Attack/AttackTurreted.cs b/OpenRA.Mods.Common/Traits/Attack/AttackTurreted.cs index 3ad0803b97..86d500aa0d 100644 --- a/OpenRA.Mods.Common/Traits/Attack/AttackTurreted.cs +++ b/OpenRA.Mods.Common/Traits/Attack/AttackTurreted.cs @@ -9,6 +9,7 @@ */ #endregion +using System.Collections.Immutable; using System.Linq; using OpenRA.Traits; @@ -18,7 +19,7 @@ namespace OpenRA.Mods.Common.Traits public class AttackTurretedInfo : AttackFollowInfo, Requires { [Desc("Turret names")] - public readonly string[] Turrets = ["primary"]; + public readonly ImmutableArray Turrets = ["primary"]; public override object Create(ActorInitializer init) { return new AttackTurreted(init.Self, this); } } diff --git a/OpenRA.Mods.Common/Traits/AutoTarget.cs b/OpenRA.Mods.Common/Traits/AutoTarget.cs index c175989d90..6e7d5e108c 100644 --- a/OpenRA.Mods.Common/Traits/AutoTarget.cs +++ b/OpenRA.Mods.Common/Traits/AutoTarget.cs @@ -9,6 +9,7 @@ */ #endregion +using System.Collections.Frozen; using System.Collections.Generic; using System.Linq; using OpenRA.Primitives; @@ -69,7 +70,7 @@ namespace OpenRA.Mods.Common.Traits public readonly string AttackAnythingCondition = null; [FieldLoader.Ignore] - public readonly Dictionary ConditionByStance = []; + public FrozenDictionary ConditionByStance = FrozenDictionary.Empty; [Desc("Allow the player to change the unit stance.")] public readonly bool EnableStances = true; @@ -89,17 +90,20 @@ namespace OpenRA.Mods.Common.Traits { base.RulesetLoaded(rules, info); + var conditionByStance = new Dictionary(); if (HoldFireCondition != null) - ConditionByStance[UnitStance.HoldFire] = HoldFireCondition; + conditionByStance[UnitStance.HoldFire] = HoldFireCondition; if (ReturnFireCondition != null) - ConditionByStance[UnitStance.ReturnFire] = ReturnFireCondition; + conditionByStance[UnitStance.ReturnFire] = ReturnFireCondition; if (DefendCondition != null) - ConditionByStance[UnitStance.Defend] = DefendCondition; + conditionByStance[UnitStance.Defend] = DefendCondition; if (AttackAnythingCondition != null) - ConditionByStance[UnitStance.AttackAnything] = AttackAnythingCondition; + conditionByStance[UnitStance.AttackAnything] = AttackAnythingCondition; + + ConditionByStance = conditionByStance.ToFrozenDictionary(); } IEnumerable IEditorActorOptions.ActorOptions(ActorInfo ai, World world) diff --git a/OpenRA.Mods.Common/Traits/BotModules/BaseBuilderBotModule.cs b/OpenRA.Mods.Common/Traits/BotModules/BaseBuilderBotModule.cs index f0f58ed4a4..32b5a685da 100644 --- a/OpenRA.Mods.Common/Traits/BotModules/BaseBuilderBotModule.cs +++ b/OpenRA.Mods.Common/Traits/BotModules/BaseBuilderBotModule.cs @@ -10,7 +10,9 @@ #endregion using System; +using System.Collections.Frozen; using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using OpenRA.Traits; @@ -21,34 +23,34 @@ namespace OpenRA.Mods.Common.Traits public class BaseBuilderBotModuleInfo : ConditionalTraitInfo, NotBefore, NotBefore { [Desc("Tells the AI what building types are considered construction yards.")] - public readonly HashSet ConstructionYardTypes = []; + public readonly FrozenSet ConstructionYardTypes = FrozenSet.Empty; [Desc("Tells the AI what building types are considered refineries.")] - public readonly HashSet RefineryTypes = []; + public readonly FrozenSet RefineryTypes = FrozenSet.Empty; [Desc("Tells the AI what building types are considered power plants.")] - public readonly HashSet PowerTypes = []; + public readonly FrozenSet PowerTypes = FrozenSet.Empty; [Desc("Tells the AI what building types are considered production facilities.")] - public readonly HashSet ProductionTypes = []; + public readonly FrozenSet ProductionTypes = FrozenSet.Empty; [Desc("Tells the AI what building types are considered tech buildings.")] - public readonly HashSet TechTypes = []; + public readonly FrozenSet TechTypes = FrozenSet.Empty; [Desc("Tells the AI what building types are considered naval production facilities.")] - public readonly HashSet NavalProductionTypes = []; + public readonly FrozenSet NavalProductionTypes = FrozenSet.Empty; [Desc("Tells the AI what building types are considered silos (resource storage).")] - public readonly HashSet SiloTypes = []; + public readonly FrozenSet SiloTypes = FrozenSet.Empty; [Desc("Tells the AI what building types are considered defenses.")] - public readonly HashSet DefenseTypes = []; + public readonly FrozenSet DefenseTypes = FrozenSet.Empty; [Desc("Production queues AI uses for buildings.")] - public readonly HashSet BuildingQueues = ["Building"]; + public readonly FrozenSet BuildingQueues = new HashSet { "Building" }.ToFrozenSet(); [Desc("Production queues AI uses for defenses.")] - public readonly HashSet DefenseQueues = ["Defense"]; + public readonly FrozenSet DefenseQueues = new HashSet { "Defense" }.ToFrozenSet(); [Desc("Minimum distance in cells from center of the base when checking for building placement.")] public readonly int MinBaseRadius = 2; @@ -122,16 +124,16 @@ namespace OpenRA.Mods.Common.Traits public readonly int CheckForWaterRadius = 8; [Desc("Terrain types which are considered water for base building purposes.")] - public readonly HashSet WaterTerrainTypes = ["Water"]; + public readonly FrozenSet WaterTerrainTypes = new HashSet { "Water" }.ToFrozenSet(); [Desc("What buildings to the AI should build.", "What integer percentage of the total base must be this type of building.")] - public readonly Dictionary BuildingFractions = null; + public readonly FrozenDictionary BuildingFractions = null; [Desc("What buildings should the AI have a maximum limit to build.")] - public readonly Dictionary BuildingLimits = null; + public readonly FrozenDictionary BuildingLimits = null; [Desc("When should the AI start building specific buildings.")] - public readonly Dictionary BuildingDelays = null; + public readonly FrozenDictionary BuildingDelays = null; [Desc("Only queue construction of a new structure when above this requirement.")] public readonly int ProductionMinCashRequirement = 500; @@ -155,10 +157,10 @@ namespace OpenRA.Mods.Common.Traits public readonly int MaxRefineryPerIndice = 2; [Desc($"AI will move mcv when those numbers of refinery <= productions + tech - {nameof(ExpansionTolerate)}.")] - public readonly int[] ExpansionTolerate = [0, 1]; + public readonly ImmutableArray ExpansionTolerate = [0, 1]; [Desc($"AI will move the only mcv when those numbers of refinery <= productions + tech - {nameof(ForceExpansionTolerate)}.")] - public readonly int[] ForceExpansionTolerate = [2, 3]; + public readonly ImmutableArray ForceExpansionTolerate = [2, 3]; public override object Create(ActorInitializer init) { return new BaseBuilderBotModule(init.Self, this); } } diff --git a/OpenRA.Mods.Common/Traits/BotModules/BotModuleLogic/BaseBuilderQueueManager.cs b/OpenRA.Mods.Common/Traits/BotModules/BotModuleLogic/BaseBuilderQueueManager.cs index 6784d0ca03..d3848a54d1 100644 --- a/OpenRA.Mods.Common/Traits/BotModules/BotModuleLogic/BaseBuilderQueueManager.cs +++ b/OpenRA.Mods.Common/Traits/BotModules/BotModuleLogic/BaseBuilderQueueManager.cs @@ -10,6 +10,7 @@ #endregion using System; +using System.Collections.Frozen; using System.Collections.Generic; using System.Linq; using OpenRA.Traits; @@ -261,7 +262,7 @@ namespace OpenRA.Mods.Common.Traits return true; } - ActorInfo GetProducibleBuilding(HashSet actors, IEnumerable buildables, Func orderBy = null) + ActorInfo GetProducibleBuilding(FrozenSet actors, IEnumerable buildables, Func orderBy = null) { var available = buildables.Where(actor => { diff --git a/OpenRA.Mods.Common/Traits/BotModules/BotModuleLogic/MinelayerBotModule.cs b/OpenRA.Mods.Common/Traits/BotModules/BotModuleLogic/MinelayerBotModule.cs index 4a26c18131..cdfdb38ce6 100644 --- a/OpenRA.Mods.Common/Traits/BotModules/BotModuleLogic/MinelayerBotModule.cs +++ b/OpenRA.Mods.Common/Traits/BotModules/BotModuleLogic/MinelayerBotModule.cs @@ -10,6 +10,7 @@ #endregion using System; +using System.Collections.Frozen; using System.Collections.Generic; using System.Linq; using OpenRA.Primitives; @@ -31,7 +32,7 @@ namespace OpenRA.Mods.Common.Traits [ActorReference(typeof(MinelayerInfo))] [Desc("Actors with " + nameof(Minelayer) + "trait.")] - public readonly HashSet MinelayingActorTypes = default; + public readonly FrozenSet MinelayingActorTypes = default; [Desc("Find this amount of suitable actors and lay mine to a location.")] public readonly int MaxPerAssign = 1; diff --git a/OpenRA.Mods.Common/Traits/BotModules/BotModuleLogic/SupportPowerDecision.cs b/OpenRA.Mods.Common/Traits/BotModules/BotModuleLogic/SupportPowerDecision.cs index be2faee491..287b5af8d7 100644 --- a/OpenRA.Mods.Common/Traits/BotModules/BotModuleLogic/SupportPowerDecision.cs +++ b/OpenRA.Mods.Common/Traits/BotModules/BotModuleLogic/SupportPowerDecision.cs @@ -10,6 +10,7 @@ #endregion using System.Collections.Generic; +using System.Collections.Immutable; using OpenRA.Primitives; using OpenRA.Traits; @@ -38,7 +39,7 @@ namespace OpenRA.Mods.Common.Traits [FieldLoader.LoadUsing(nameof(LoadConsiderations))] [Desc("The decisions associated with this power")] - public readonly List Considerations = []; + public readonly ImmutableArray Considerations = []; [Desc("Minimum ticks to wait until next Decision scan attempt.")] public readonly int MinimumScanTimeInterval = 250; @@ -58,7 +59,7 @@ namespace OpenRA.Mods.Common.Traits if (d.Key.Split('@')[0] == "Consideration") ret.Add(new Consideration(d.Value)); - return ret; + return ret.ToImmutableArray(); } /// Evaluates the attractiveness of a position according to all considerations. diff --git a/OpenRA.Mods.Common/Traits/BotModules/CaptureManagerBotModule.cs b/OpenRA.Mods.Common/Traits/BotModules/CaptureManagerBotModule.cs index f5f04e1bac..50a5945615 100644 --- a/OpenRA.Mods.Common/Traits/BotModules/CaptureManagerBotModule.cs +++ b/OpenRA.Mods.Common/Traits/BotModules/CaptureManagerBotModule.cs @@ -10,6 +10,7 @@ #endregion using System; +using System.Collections.Frozen; using System.Collections.Generic; using System.Linq; using OpenRA.Traits; @@ -22,11 +23,11 @@ namespace OpenRA.Mods.Common.Traits { [Desc("Actor types that can capture other actors (via `Captures`).", "Leave this empty to disable capturing.")] - public readonly HashSet CapturingActorTypes = []; + public readonly FrozenSet CapturingActorTypes = FrozenSet.Empty; [Desc("Actor types that can be targeted for capturing.", "Leave this empty to include all actors.")] - public readonly HashSet CapturableActorTypes = []; + public readonly FrozenSet CapturableActorTypes = FrozenSet.Empty; [Desc("Minimum delay (in ticks) between trying to capture with CapturingActorTypes.")] public readonly int MinimumCaptureDelay = 375; diff --git a/OpenRA.Mods.Common/Traits/BotModules/HarvesterBotModule.cs b/OpenRA.Mods.Common/Traits/BotModules/HarvesterBotModule.cs index 7e15239438..c33f6d6220 100644 --- a/OpenRA.Mods.Common/Traits/BotModules/HarvesterBotModule.cs +++ b/OpenRA.Mods.Common/Traits/BotModules/HarvesterBotModule.cs @@ -10,6 +10,7 @@ #endregion using System; +using System.Collections.Frozen; using System.Collections.Generic; using System.Linq; using OpenRA.Graphics; @@ -24,10 +25,10 @@ namespace OpenRA.Mods.Common.Traits { [Desc("Actor types that are considered harvesters. If harvester count drops below RefineryTypes count, a new harvester is built.", "Leave empty to disable harvester replacement. Currently only needed by harvester replacement system.")] - public readonly HashSet HarvesterTypes = []; + public readonly FrozenSet HarvesterTypes = FrozenSet.Empty; [Desc("Actor types that are counted as refineries. Currently only needed by harvester replacement system.")] - public readonly HashSet RefineryTypes = []; + public readonly FrozenSet RefineryTypes = FrozenSet.Empty; [Desc("Interval (in ticks) between giving out orders to idle harvesters.")] public readonly int ScanForIdleHarvestersInterval = 50; diff --git a/OpenRA.Mods.Common/Traits/BotModules/McvExpansionManagerBotModule.cs b/OpenRA.Mods.Common/Traits/BotModules/McvExpansionManagerBotModule.cs index ef10452e6f..b9ba2fd2a5 100644 --- a/OpenRA.Mods.Common/Traits/BotModules/McvExpansionManagerBotModule.cs +++ b/OpenRA.Mods.Common/Traits/BotModules/McvExpansionManagerBotModule.cs @@ -10,6 +10,7 @@ #endregion using System; +using System.Collections.Frozen; using System.Collections.Generic; using System.Linq; using OpenRA.Traits; @@ -23,13 +24,13 @@ namespace OpenRA.Mods.Common.Traits public class McvExpansionManagerBotModuleInfo : ConditionalTraitInfo, Requires, NotBefore { [Desc("Actor types that are considered MCVs (deploy into base builders).")] - public readonly HashSet McvTypes = []; + public readonly FrozenSet McvTypes = FrozenSet.Empty; [Desc("Actor types that are considered construction yards (base builders).")] - public readonly HashSet ConstructionYardTypes = []; + public readonly FrozenSet ConstructionYardTypes = FrozenSet.Empty; [Desc("Actor types that are able to produce MCVs.")] - public readonly HashSet McvFactoryTypes = []; + public readonly FrozenSet McvFactoryTypes = FrozenSet.Empty; [Desc("Try to maintain at least this many ConstructionYardTypes, build an MCV if number is below this.")] public readonly int MinimumConstructionYardCount = 1; diff --git a/OpenRA.Mods.Common/Traits/BotModules/McvManagerBotModule.cs b/OpenRA.Mods.Common/Traits/BotModules/McvManagerBotModule.cs index 9ab15c5f48..9d878045ba 100644 --- a/OpenRA.Mods.Common/Traits/BotModules/McvManagerBotModule.cs +++ b/OpenRA.Mods.Common/Traits/BotModules/McvManagerBotModule.cs @@ -9,6 +9,7 @@ */ #endregion +using System.Collections.Frozen; using System.Collections.Generic; using System.Linq; using OpenRA.Traits; @@ -20,13 +21,13 @@ namespace OpenRA.Mods.Common.Traits public class McvManagerBotModuleInfo : ConditionalTraitInfo { [Desc("Actor types that are considered MCVs (deploy into base builders).")] - public readonly HashSet McvTypes = []; + public readonly FrozenSet McvTypes = FrozenSet.Empty; [Desc("Actor types that are considered construction yards (base builders).")] - public readonly HashSet ConstructionYardTypes = []; + public readonly FrozenSet ConstructionYardTypes = FrozenSet.Empty; [Desc("Actor types that are able to produce MCVs.")] - public readonly HashSet McvFactoryTypes = []; + public readonly FrozenSet McvFactoryTypes = FrozenSet.Empty; [Desc("Try to maintain at least this many ConstructionYardTypes, build an MCV if number is below this.")] public readonly int MinimumConstructionYardCount = 1; diff --git a/OpenRA.Mods.Common/Traits/BotModules/PowerDownBotManager.cs b/OpenRA.Mods.Common/Traits/BotModules/PowerDownBotManager.cs index 1437aea9cf..a0c7755916 100644 --- a/OpenRA.Mods.Common/Traits/BotModules/PowerDownBotManager.cs +++ b/OpenRA.Mods.Common/Traits/BotModules/PowerDownBotManager.cs @@ -10,6 +10,7 @@ #endregion using System; +using System.Collections.Frozen; using System.Collections.Generic; using System.Linq; using OpenRA.Traits; @@ -25,7 +26,7 @@ namespace OpenRA.Mods.Common.Traits public readonly int Interval = 150; [Desc("Actors that allow this module to toggle")] - public readonly HashSet PowerDownTypes = []; + public readonly FrozenSet PowerDownTypes = FrozenSet.Empty; [Desc("Order used by " + nameof(ToggleConditionOnOrderInfo) + " for powerdown on toggled actor.")] public readonly string PowerDownOrder = "PowerDown"; diff --git a/OpenRA.Mods.Common/Traits/BotModules/ResourceMapBotModule.cs b/OpenRA.Mods.Common/Traits/BotModules/ResourceMapBotModule.cs index fe486525d6..7551b9e251 100644 --- a/OpenRA.Mods.Common/Traits/BotModules/ResourceMapBotModule.cs +++ b/OpenRA.Mods.Common/Traits/BotModules/ResourceMapBotModule.cs @@ -10,7 +10,7 @@ #endregion using System; -using System.Collections.Generic; +using System.Collections.Frozen; using System.Linq; using OpenRA.Traits; @@ -20,20 +20,20 @@ namespace OpenRA.Mods.Common.Traits public class ResourceMapBotModuleInfo : ConditionalTraitInfo, NotBefore { [Desc("Harvestable and valuable resource types.")] - public readonly HashSet ValuableResourceTypes = []; + public readonly FrozenSet ValuableResourceTypes = FrozenSet.Empty; [Desc("Tells the AI what types are considered resource creator.")] - public readonly HashSet ResourceCreatorTypes = []; + public readonly FrozenSet ResourceCreatorTypes = FrozenSet.Empty; [Desc($"Actor types that are considered refineries for {nameof(HarvesterTypes)}.")] - public readonly HashSet RefineryTypes = []; + public readonly FrozenSet RefineryTypes = FrozenSet.Empty; [Desc($"Actor types that are considered harvesters for {nameof(ValuableResourceTypes)}.")] - public readonly HashSet HarvesterTypes = []; + public readonly FrozenSet HarvesterTypes = FrozenSet.Empty; [Desc("Actor types that are considered to be the base building for expansion. Other enemy units will also be recorded", "Defence and production building is suggested")] - public readonly HashSet EnemyBaseBuildingTypes = []; + public readonly FrozenSet EnemyBaseBuildingTypes = FrozenSet.Empty; [Desc("Delay (in ticks) for updating the indicies.")] public readonly int UpdateResourceMapInverval = 67; diff --git a/OpenRA.Mods.Common/Traits/BotModules/SquadManagerBotModule.cs b/OpenRA.Mods.Common/Traits/BotModules/SquadManagerBotModule.cs index c870c676cb..b4edab92df 100644 --- a/OpenRA.Mods.Common/Traits/BotModules/SquadManagerBotModule.cs +++ b/OpenRA.Mods.Common/Traits/BotModules/SquadManagerBotModule.cs @@ -10,6 +10,7 @@ #endregion using System; +using System.Collections.Frozen; using System.Collections.Generic; using System.Linq; using OpenRA.Mods.Common.Traits.BotModules.Squads; @@ -24,27 +25,27 @@ namespace OpenRA.Mods.Common.Traits { [ActorReference] [Desc("Actor types that are valid for naval squads.")] - public readonly HashSet NavalUnitsTypes = []; + public readonly FrozenSet NavalUnitsTypes = FrozenSet.Empty; [ActorReference] [Desc("Actor types that are excluded from ground attacks.")] - public readonly HashSet AirUnitsTypes = []; + public readonly FrozenSet AirUnitsTypes = FrozenSet.Empty; [ActorReference] [Desc("Actor types that should generally be excluded from attack squads.")] - public readonly HashSet ExcludeFromSquadsTypes = []; + public readonly FrozenSet ExcludeFromSquadsTypes = FrozenSet.Empty; [ActorReference] [Desc("Actor types that are considered construction yards (base builders).")] - public readonly HashSet ConstructionYardTypes = []; + public readonly FrozenSet ConstructionYardTypes = FrozenSet.Empty; [ActorReference] [Desc("Enemy building types around which to scan for targets for naval squads.")] - public readonly HashSet NavalProductionTypes = []; + public readonly FrozenSet NavalProductionTypes = FrozenSet.Empty; [ActorReference] [Desc("Own actor types that are prioritized when defending.")] - public readonly HashSet ProtectionTypes = []; + public readonly FrozenSet ProtectionTypes = FrozenSet.Empty; [Desc("Target types are used for identifying aircraft.")] public readonly BitSet AircraftTargetType = new("Air"); diff --git a/OpenRA.Mods.Common/Traits/BotModules/Squads/AttackOrFleeFuzzy.cs b/OpenRA.Mods.Common/Traits/BotModules/Squads/AttackOrFleeFuzzy.cs index 6cb5ecedb4..2d6ac50b1c 100644 --- a/OpenRA.Mods.Common/Traits/BotModules/Squads/AttackOrFleeFuzzy.cs +++ b/OpenRA.Mods.Common/Traits/BotModules/Squads/AttackOrFleeFuzzy.cs @@ -11,7 +11,6 @@ using System; using System.Collections.Generic; -using System.Linq; using AI.Fuzzy.Library; using OpenRA.Mods.Common.Warheads; using OpenRA.Traits; diff --git a/OpenRA.Mods.Common/Traits/BotModules/SupportPowerBotModule.cs b/OpenRA.Mods.Common/Traits/BotModules/SupportPowerBotModule.cs index f69db9d138..fff081af57 100644 --- a/OpenRA.Mods.Common/Traits/BotModules/SupportPowerBotModule.cs +++ b/OpenRA.Mods.Common/Traits/BotModules/SupportPowerBotModule.cs @@ -10,6 +10,7 @@ #endregion using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using OpenRA.Traits; @@ -21,7 +22,7 @@ namespace OpenRA.Mods.Common.Traits { [Desc("Tells the AI how to use its support powers.")] [FieldLoader.LoadUsing(nameof(LoadDecisions))] - public readonly List Decisions = []; + public readonly ImmutableArray Decisions = []; static object LoadDecisions(MiniYaml yaml) { @@ -31,7 +32,7 @@ namespace OpenRA.Mods.Common.Traits foreach (var d in decisions.Value.Nodes) ret.Add(new SupportPowerDecision(d.Value)); - return ret; + return ret.ToImmutableArray(); } public override object Create(ActorInitializer init) { return new SupportPowerBotModule(init.Self, this); } diff --git a/OpenRA.Mods.Common/Traits/BotModules/UnitBuilderBotModule.cs b/OpenRA.Mods.Common/Traits/BotModules/UnitBuilderBotModule.cs index 25b804f698..20ca72dd7e 100644 --- a/OpenRA.Mods.Common/Traits/BotModules/UnitBuilderBotModule.cs +++ b/OpenRA.Mods.Common/Traits/BotModules/UnitBuilderBotModule.cs @@ -9,7 +9,9 @@ */ #endregion +using System.Collections.Frozen; using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using OpenRA.Traits; @@ -28,16 +30,16 @@ namespace OpenRA.Mods.Common.Traits public readonly int IdleBaseUnitsMaximum = -1; [Desc("Production queues AI uses for producing units.")] - public readonly string[] UnitQueues = ["Vehicle", "Infantry", "Plane", "Ship", "Aircraft"]; + public readonly ImmutableArray UnitQueues = ["Vehicle", "Infantry", "Plane", "Ship", "Aircraft"]; [Desc("What units to the AI should build.", "What relative share of the total army must be this type of unit.")] - public readonly Dictionary UnitsToBuild = null; + public readonly FrozenDictionary UnitsToBuild = null; [Desc("What units should the AI have a maximum limit to train.")] - public readonly Dictionary UnitLimits = null; + public readonly FrozenDictionary UnitLimits = null; [Desc("When should the AI start train specific units.")] - public readonly Dictionary UnitDelays = null; + public readonly FrozenDictionary UnitDelays = null; [Desc("Only queue construction of a new unit when above this requirement.")] public readonly int ProductionMinCashRequirement = 500; @@ -253,7 +255,7 @@ namespace OpenRA.Mods.Common.Traits if (queuedBuildRequestsNode != null) { queuedBuildRequests.Clear(); - queuedBuildRequests.AddRange(FieldLoader.GetValue("QueuedBuildRequests", queuedBuildRequestsNode.Value.Value)); + queuedBuildRequests.AddRange(FieldLoader.GetValue>("QueuedBuildRequests", queuedBuildRequestsNode.Value.Value)); } var idleUnitCountNode = data.NodeWithKeyOrDefault("IdleUnitCount"); diff --git a/OpenRA.Mods.Common/Traits/Buildable.cs b/OpenRA.Mods.Common/Traits/Buildable.cs index 64e140b229..261a78ac44 100644 --- a/OpenRA.Mods.Common/Traits/Buildable.cs +++ b/OpenRA.Mods.Common/Traits/Buildable.cs @@ -9,7 +9,8 @@ */ #endregion -using System.Collections.Generic; +using System.Collections.Frozen; +using System.Collections.Immutable; using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits @@ -20,10 +21,10 @@ namespace OpenRA.Mods.Common.Traits "This can be prefixed with ! to invert the prerequisite (disabling production if the prerequisite is available)", "and/or ~ to hide the actor from the production palette if the prerequisite is not available.", "Prerequisites are granted by actors with the ProvidesPrerequisite trait.")] - public readonly string[] Prerequisites = []; + public readonly ImmutableArray Prerequisites = []; [Desc("Production queue(s) that can produce this.")] - public readonly HashSet Queue = []; + public readonly FrozenSet Queue = FrozenSet.Empty; [Desc("Override the production structure type (from the Production Produces list) that this unit should be built at.")] public readonly string BuildAtProductionType = null; diff --git a/OpenRA.Mods.Common/Traits/Buildings/Bridge.cs b/OpenRA.Mods.Common/Traits/Buildings/Bridge.cs index 913a7469cf..292680a601 100644 --- a/OpenRA.Mods.Common/Traits/Buildings/Bridge.cs +++ b/OpenRA.Mods.Common/Traits/Buildings/Bridge.cs @@ -11,8 +11,8 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.IO; -using System.Linq; using OpenRA.Effects; using OpenRA.GameRules; using OpenRA.Mods.Common.Terrain; @@ -37,9 +37,9 @@ namespace OpenRA.Mods.Common.Traits public readonly ushort DestroyedPlusSouthTemplate = 0; public readonly ushort DestroyedPlusBothTemplate = 0; - public readonly string[] ShorePieces = ["br1", "br2"]; - public readonly int[] NorthOffset = null; - public readonly int[] SouthOffset = null; + public readonly ImmutableArray ShorePieces = ["br1", "br2"]; + public readonly ImmutableArray NorthOffset = default; + public readonly ImmutableArray SouthOffset = default; [WeaponReference] [Desc("The name of the weapon to use when demolishing the bridge")] @@ -188,7 +188,7 @@ namespace OpenRA.Mods.Common.Traits } public LegacyBridgeHut GetHut(int index) { return huts[index]; } - public Bridge GetNeighbor(int[] offset, LegacyBridgeLayer bridges) + public Bridge GetNeighbor(ImmutableArray offset, LegacyBridgeLayer bridges) { if (offset == null) return null; diff --git a/OpenRA.Mods.Common/Traits/Buildings/BridgeHut.cs b/OpenRA.Mods.Common/Traits/Buildings/BridgeHut.cs index 868901e301..7c7c21ccb6 100644 --- a/OpenRA.Mods.Common/Traits/Buildings/BridgeHut.cs +++ b/OpenRA.Mods.Common/Traits/Buildings/BridgeHut.cs @@ -9,8 +9,8 @@ */ #endregion -using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using OpenRA.Effects; using OpenRA.Primitives; @@ -22,10 +22,10 @@ namespace OpenRA.Mods.Common.Traits public class BridgeHutInfo : TraitInfo, IDemolishableInfo { [Desc("Bridge types to act on")] - public readonly string[] Types = ["GroundLevelBridge"]; + public readonly ImmutableArray Types = ["GroundLevelBridge"]; [Desc("Offsets to look for adjacent bridges to act on")] - public readonly CVec[] NeighbourOffsets = []; + public readonly ImmutableArray NeighbourOffsets = []; [Desc("Delay between each segment repair step")] public readonly int RepairPropagationDelay = 20; diff --git a/OpenRA.Mods.Common/Traits/Buildings/BridgePlaceholder.cs b/OpenRA.Mods.Common/Traits/Buildings/BridgePlaceholder.cs index 60f84e06cf..4578f69c2d 100644 --- a/OpenRA.Mods.Common/Traits/Buildings/BridgePlaceholder.cs +++ b/OpenRA.Mods.Common/Traits/Buildings/BridgePlaceholder.cs @@ -9,6 +9,7 @@ */ #endregion +using System.Collections.Immutable; using OpenRA.Primitives; using OpenRA.Traits; @@ -25,7 +26,7 @@ namespace OpenRA.Mods.Common.Traits [Desc("Actor type to replace with on repair.")] public readonly string ReplaceWithActor = null; - public readonly CVec[] NeighbourOffsets = []; + public readonly ImmutableArray NeighbourOffsets = []; public override object Create(ActorInitializer init) { return new BridgePlaceholder(init.Self, this); } } @@ -78,7 +79,7 @@ namespace OpenRA.Mods.Common.Traits string IBridgeSegment.Type => Info.Type; DamageState IBridgeSegment.DamageState => Info.DamageState; bool IBridgeSegment.Valid => self.IsInWorld; - CVec[] IBridgeSegment.NeighbourOffsets => Info.NeighbourOffsets; + ImmutableArray IBridgeSegment.NeighbourOffsets => Info.NeighbourOffsets; CPos IBridgeSegment.Location => self.Location; } } diff --git a/OpenRA.Mods.Common/Traits/Buildings/Building.cs b/OpenRA.Mods.Common/Traits/Buildings/Building.cs index 917acc67d4..a86286af54 100644 --- a/OpenRA.Mods.Common/Traits/Buildings/Building.cs +++ b/OpenRA.Mods.Common/Traits/Buildings/Building.cs @@ -10,7 +10,9 @@ #endregion using System; +using System.Collections.Frozen; using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using OpenRA.Graphics; using OpenRA.Traits; @@ -29,12 +31,12 @@ namespace OpenRA.Mods.Common.Traits public class BuildingInfo : TraitInfo, IOccupySpaceInfo, IPlaceBuildingDecorationInfo { [Desc("Where you are allowed to place the building (Water, Clear, ...)")] - public readonly HashSet TerrainTypes = []; + public readonly FrozenSet TerrainTypes = FrozenSet.Empty; [Desc("x means cell is blocked, capital X means blocked but not counting as targetable, ", "= means part of the footprint but passable, _ means completely empty.")] [FieldLoader.LoadUsing(nameof(LoadFootprint))] - public readonly Dictionary Footprint; + public readonly FrozenDictionary Footprint; public readonly CVec Dimensions = new(1, 1); @@ -54,9 +56,9 @@ namespace OpenRA.Mods.Common.Traits [Desc("Clear smudges from underneath the building footprint on transform.")] public readonly bool RemoveSmudgesOnTransform = true; - public readonly string[] BuildSounds = []; + public readonly ImmutableArray BuildSounds = []; - public readonly string[] UndeploySounds = []; + public readonly ImmutableArray UndeploySounds = []; public override object Create(ActorInitializer init) { return new Building(init, this); } @@ -76,7 +78,7 @@ namespace OpenRA.Mods.Common.Traits } var index = 0; - var ret = new Dictionary(); + var ret = new Dictionary(dim.X * dim.Y); for (var y = 0; y < dim.Y; y++) { for (var x = 0; x < dim.X; x++) @@ -89,7 +91,7 @@ namespace OpenRA.Mods.Common.Traits } } - return ret; + return ret.ToFrozenDictionary(); } public IEnumerable FootprintTiles(CPos location, FootprintCellType type) diff --git a/OpenRA.Mods.Common/Traits/Buildings/Exit.cs b/OpenRA.Mods.Common/Traits/Buildings/Exit.cs index 3f85616807..c1880bca2d 100644 --- a/OpenRA.Mods.Common/Traits/Buildings/Exit.cs +++ b/OpenRA.Mods.Common/Traits/Buildings/Exit.cs @@ -10,6 +10,7 @@ #endregion using System; +using System.Collections.Frozen; using System.Collections.Generic; using System.Linq; using OpenRA.Traits; @@ -27,7 +28,7 @@ namespace OpenRA.Mods.Common.Traits public readonly WAngle? Facing = null; [Desc("Type tags on this exit.")] - public readonly HashSet ProductionTypes = []; + public readonly FrozenSet ProductionTypes = FrozenSet.Empty; [Desc("Number of ticks to wait before moving into the world.")] public readonly int ExitDelay = 0; diff --git a/OpenRA.Mods.Common/Traits/Buildings/GivesBuildableArea.cs b/OpenRA.Mods.Common/Traits/Buildings/GivesBuildableArea.cs index 7b8ffa54a0..11fd25ff06 100644 --- a/OpenRA.Mods.Common/Traits/Buildings/GivesBuildableArea.cs +++ b/OpenRA.Mods.Common/Traits/Buildings/GivesBuildableArea.cs @@ -9,7 +9,7 @@ */ #endregion -using System.Collections.Generic; +using System.Collections.Frozen; namespace OpenRA.Mods.Common.Traits { @@ -18,7 +18,7 @@ namespace OpenRA.Mods.Common.Traits { [FieldLoader.Require] [Desc("Types of buildable area this actor gives.")] - public readonly HashSet AreaTypes = []; + public readonly FrozenSet AreaTypes = FrozenSet.Empty; public override object Create(ActorInitializer init) { return new GivesBuildableArea(this); } } @@ -28,8 +28,6 @@ namespace OpenRA.Mods.Common.Traits public GivesBuildableArea(GivesBuildableAreaInfo info) : base(info) { } - readonly HashSet noAreaTypes = []; - - public HashSet AreaTypes => !IsTraitDisabled ? Info.AreaTypes : noAreaTypes; + public FrozenSet AreaTypes => !IsTraitDisabled ? Info.AreaTypes : FrozenSet.Empty; } } diff --git a/OpenRA.Mods.Common/Traits/Buildings/GroundLevelBridge.cs b/OpenRA.Mods.Common/Traits/Buildings/GroundLevelBridge.cs index af8c7c5fda..a1dd1a50e5 100644 --- a/OpenRA.Mods.Common/Traits/Buildings/GroundLevelBridge.cs +++ b/OpenRA.Mods.Common/Traits/Buildings/GroundLevelBridge.cs @@ -10,6 +10,7 @@ #endregion using System.Collections.Generic; +using System.Collections.Immutable; using OpenRA.GameRules; using OpenRA.Primitives; using OpenRA.Traits; @@ -23,7 +24,7 @@ namespace OpenRA.Mods.Common.Traits public readonly string Type = "GroundLevelBridge"; - public readonly CVec[] NeighbourOffsets = []; + public readonly ImmutableArray NeighbourOffsets = []; [WeaponReference] [Desc("The name of the weapon to use when demolishing the bridge")] @@ -118,7 +119,7 @@ namespace OpenRA.Mods.Common.Traits string IBridgeSegment.Type => Info.Type; DamageState IBridgeSegment.DamageState => self.GetDamageState(); bool IBridgeSegment.Valid => self.IsInWorld; - CVec[] IBridgeSegment.NeighbourOffsets => Info.NeighbourOffsets; + ImmutableArray IBridgeSegment.NeighbourOffsets => Info.NeighbourOffsets; CPos IBridgeSegment.Location => self.Location; } } diff --git a/OpenRA.Mods.Common/Traits/Buildings/LineBuild.cs b/OpenRA.Mods.Common/Traits/Buildings/LineBuild.cs index afe084e613..2697348960 100644 --- a/OpenRA.Mods.Common/Traits/Buildings/LineBuild.cs +++ b/OpenRA.Mods.Common/Traits/Buildings/LineBuild.cs @@ -9,6 +9,7 @@ */ #endregion +using System.Collections.Frozen; using System.Collections.Generic; using System.Linq; using OpenRA.Traits; @@ -55,7 +56,7 @@ namespace OpenRA.Mods.Common.Traits public readonly int Range = 5; [Desc("LineBuildNode 'Types' to attach to.")] - public readonly HashSet NodeTypes = ["wall"]; + public readonly FrozenSet NodeTypes = new HashSet { "wall" }.ToFrozenSet(); [ActorReference(typeof(LineBuildInfo))] [Desc("Actor type for line-built segments (defaults to same actor).")] diff --git a/OpenRA.Mods.Common/Traits/Buildings/LineBuildNode.cs b/OpenRA.Mods.Common/Traits/Buildings/LineBuildNode.cs index 038d535ae6..fc623b9abd 100644 --- a/OpenRA.Mods.Common/Traits/Buildings/LineBuildNode.cs +++ b/OpenRA.Mods.Common/Traits/Buildings/LineBuildNode.cs @@ -9,7 +9,9 @@ */ #endregion +using System.Collections.Frozen; using System.Collections.Generic; +using System.Collections.Immutable; using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits @@ -18,10 +20,10 @@ namespace OpenRA.Mods.Common.Traits public class LineBuildNodeInfo : TraitInfo { [Desc("This actor is of LineBuild 'NodeType'...")] - public readonly HashSet Types = ["wall"]; + public readonly FrozenSet Types = new HashSet { "wall" }.ToFrozenSet(); [Desc("Cells (outside the footprint) that contain cells that can connect to this actor.")] - public readonly CVec[] Connections = [new CVec(1, 0), new CVec(0, 1), new CVec(-1, 0), new CVec(0, -1)]; + public readonly ImmutableArray Connections = [new CVec(1, 0), new CVec(0, 1), new CVec(-1, 0), new CVec(0, -1)]; } public class LineBuildNode { } diff --git a/OpenRA.Mods.Common/Traits/Buildings/PlaceBuildingVariants.cs b/OpenRA.Mods.Common/Traits/Buildings/PlaceBuildingVariants.cs index 102877f112..a9ae4603dd 100644 --- a/OpenRA.Mods.Common/Traits/Buildings/PlaceBuildingVariants.cs +++ b/OpenRA.Mods.Common/Traits/Buildings/PlaceBuildingVariants.cs @@ -9,6 +9,7 @@ */ #endregion +using System.Collections.Immutable; using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits @@ -19,10 +20,10 @@ namespace OpenRA.Mods.Common.Traits [FieldLoader.Require] [ActorReference(typeof(BuildingInfo))] [Desc("Variant actors that can be cycled between when placing a structure.")] - public readonly string[] Actors = null; + public readonly ImmutableArray Actors = default; [Desc("Facing of the non-variant actor, followed by facings for each variant actor. The length equals the length of Actors + 1.")] - public readonly WAngle[] Facings = null; + public readonly ImmutableArray Facings = default; public override object Create(ActorInitializer init) { return new PlaceBuildingVariants(); } } diff --git a/OpenRA.Mods.Common/Traits/Buildings/PrimaryBuilding.cs b/OpenRA.Mods.Common/Traits/Buildings/PrimaryBuilding.cs index b1f086c2c2..dfcacce633 100644 --- a/OpenRA.Mods.Common/Traits/Buildings/PrimaryBuilding.cs +++ b/OpenRA.Mods.Common/Traits/Buildings/PrimaryBuilding.cs @@ -9,8 +9,8 @@ */ #endregion -using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using OpenRA.Mods.Common.Orders; using OpenRA.Traits; @@ -43,7 +43,7 @@ namespace OpenRA.Mods.Common.Traits [Desc("List of production queues for which the primary flag should be set.", "If empty, the list given in the `Produces` property of the `" + nameof(Production) + "` trait will be used.")] - public readonly string[] ProductionQueues = []; + public readonly ImmutableArray ProductionQueues = []; [CursorReference] [Desc("Cursor to display when setting the primary building.")] diff --git a/OpenRA.Mods.Common/Traits/Buildings/RallyPoint.cs b/OpenRA.Mods.Common/Traits/Buildings/RallyPoint.cs index d0e5aaa054..0c59ee12b8 100644 --- a/OpenRA.Mods.Common/Traits/Buildings/RallyPoint.cs +++ b/OpenRA.Mods.Common/Traits/Buildings/RallyPoint.cs @@ -10,6 +10,7 @@ #endregion using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using OpenRA.Mods.Common.Effects; using OpenRA.Traits; @@ -42,7 +43,7 @@ namespace OpenRA.Mods.Common.Traits public readonly bool IsPlayerPalette = true; [Desc("A list of 0 or more offsets defining the initial rally point path.")] - public readonly CVec[] Path = []; + public readonly ImmutableArray Path = []; [NotificationReference("Speech")] [Desc("Speech notification to play when setting a new rallypoint.")] diff --git a/OpenRA.Mods.Common/Traits/Buildings/RepairableBuilding.cs b/OpenRA.Mods.Common/Traits/Buildings/RepairableBuilding.cs index 6c95305e1c..5095ec1dd7 100644 --- a/OpenRA.Mods.Common/Traits/Buildings/RepairableBuilding.cs +++ b/OpenRA.Mods.Common/Traits/Buildings/RepairableBuilding.cs @@ -11,6 +11,7 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using OpenRA.Primitives; using OpenRA.Traits; @@ -33,7 +34,7 @@ namespace OpenRA.Mods.Common.Traits public readonly BitSet RepairDamageTypes = default; [Desc("The percentage repair bonus applied with increasing numbers of repairers.")] - public readonly int[] RepairBonuses = [100, 150, 175, 200, 220, 240, 260, 280, 300]; + public readonly ImmutableArray RepairBonuses = [100, 150, 175, 200, 220, 240, 260, 280, 300]; // TODO: This should be replaced with a pause condition [Desc("Cancel the repair state when the trait is disabled.")] diff --git a/OpenRA.Mods.Common/Traits/Buildings/RequiresBuildableArea.cs b/OpenRA.Mods.Common/Traits/Buildings/RequiresBuildableArea.cs index f8d3e28d1d..f2ec1f4e64 100644 --- a/OpenRA.Mods.Common/Traits/Buildings/RequiresBuildableArea.cs +++ b/OpenRA.Mods.Common/Traits/Buildings/RequiresBuildableArea.cs @@ -9,7 +9,7 @@ */ #endregion -using System.Collections.Generic; +using System.Collections.Frozen; using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits @@ -19,7 +19,7 @@ namespace OpenRA.Mods.Common.Traits { [FieldLoader.Require] [Desc("Types of buildable are this actor requires.")] - public readonly HashSet AreaTypes = []; + public readonly FrozenSet AreaTypes = FrozenSet.Empty; [Desc("Maximum range from the actor with 'GivesBuildableArea' this can be placed at.")] public readonly int Adjacent = 2; diff --git a/OpenRA.Mods.Common/Traits/Buildings/TransformsIntoAircraft.cs b/OpenRA.Mods.Common/Traits/Buildings/TransformsIntoAircraft.cs index 37b395281a..7fecac83da 100644 --- a/OpenRA.Mods.Common/Traits/Buildings/TransformsIntoAircraft.cs +++ b/OpenRA.Mods.Common/Traits/Buildings/TransformsIntoAircraft.cs @@ -9,6 +9,7 @@ */ #endregion +using System.Collections.Frozen; using System.Collections.Generic; using System.Linq; using OpenRA.Mods.Common.Activities; @@ -26,7 +27,7 @@ namespace OpenRA.Mods.Common.Traits [ActorReference] [FieldLoader.Require] - public readonly HashSet DockActors = []; + public readonly FrozenSet DockActors = FrozenSet.Empty; [VoiceReference] public readonly string Voice = "Action"; diff --git a/OpenRA.Mods.Common/Traits/Buildings/TransformsIntoMobile.cs b/OpenRA.Mods.Common/Traits/Buildings/TransformsIntoMobile.cs index c3a0ba77af..b9647fa617 100644 --- a/OpenRA.Mods.Common/Traits/Buildings/TransformsIntoMobile.cs +++ b/OpenRA.Mods.Common/Traits/Buildings/TransformsIntoMobile.cs @@ -9,6 +9,7 @@ */ #endregion +using System.Collections.Frozen; using System.Collections.Generic; using System.Linq; using OpenRA.Mods.Common.Activities; @@ -33,7 +34,7 @@ namespace OpenRA.Mods.Common.Traits [CursorReference(dictionaryReference: LintDictionaryReference.Values)] [Desc("Cursor overrides to display for specific terrain types.", "A dictionary of [terrain type]: [cursor name].")] - public readonly Dictionary TerrainCursors = []; + public readonly FrozenDictionary TerrainCursors = FrozenDictionary.Empty; [CursorReference] [Desc("Cursor to display when a move order cannot be issued at target location.")] diff --git a/OpenRA.Mods.Common/Traits/Buildings/TransformsIntoRepairable.cs b/OpenRA.Mods.Common/Traits/Buildings/TransformsIntoRepairable.cs index 8ccedb95eb..2c77dcf4bd 100644 --- a/OpenRA.Mods.Common/Traits/Buildings/TransformsIntoRepairable.cs +++ b/OpenRA.Mods.Common/Traits/Buildings/TransformsIntoRepairable.cs @@ -9,6 +9,7 @@ */ #endregion +using System.Collections.Frozen; using System.Collections.Generic; using System.Linq; using OpenRA.Mods.Common.Activities; @@ -23,7 +24,7 @@ namespace OpenRA.Mods.Common.Traits { [ActorReference] [FieldLoader.Require] - public readonly HashSet RepairActors = []; + public readonly FrozenSet RepairActors = FrozenSet.Empty; [VoiceReference] public readonly string Voice = "Action"; diff --git a/OpenRA.Mods.Common/Traits/Cargo.cs b/OpenRA.Mods.Common/Traits/Cargo.cs index bb48cfda71..9eaa7909c5 100644 --- a/OpenRA.Mods.Common/Traits/Cargo.cs +++ b/OpenRA.Mods.Common/Traits/Cargo.cs @@ -10,7 +10,9 @@ #endregion using System; +using System.Collections.Frozen; using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using OpenRA.Mods.Common.Activities; using OpenRA.Mods.Common.Orders; @@ -26,10 +28,10 @@ namespace OpenRA.Mods.Common.Traits public readonly int MaxWeight = 0; [Desc("`Passenger.CargoType`s that can be loaded into this actor.")] - public readonly HashSet Types = []; + public readonly FrozenSet Types = FrozenSet.Empty; [Desc("A list of actor types that are initially spawned into this actor.")] - public readonly string[] InitialUnits = []; + public readonly ImmutableArray InitialUnits = []; [Desc("When this actor is sold should all of its passengers be unloaded?")] public readonly bool EjectOnSell = true; @@ -38,7 +40,7 @@ namespace OpenRA.Mods.Common.Traits public readonly bool EjectOnDeath = false; [Desc("Terrain types that this actor is allowed to eject actors onto. Leave empty for all terrain types.")] - public readonly HashSet UnloadTerrainTypes = []; + public readonly FrozenSet UnloadTerrainTypes = FrozenSet.Empty; [VoiceReference] [Desc("Voice to play when ordered to unload the passengers.")] @@ -82,7 +84,7 @@ namespace OpenRA.Mods.Common.Traits [ActorReference(dictionaryReference: LintDictionaryReference.Keys)] [Desc("Conditions to grant when specified actors are loaded inside the transport.", "A dictionary of [actor name]: [condition].")] - public readonly Dictionary PassengerConditions = []; + public readonly FrozenDictionary PassengerConditions = FrozenDictionary.Empty; [GrantedConditionReference] public IEnumerable LinterPassengerConditions => PassengerConditions.Values; diff --git a/OpenRA.Mods.Common/Traits/Carryall.cs b/OpenRA.Mods.Common/Traits/Carryall.cs index b0effb5b15..8a9ab4b978 100644 --- a/OpenRA.Mods.Common/Traits/Carryall.cs +++ b/OpenRA.Mods.Common/Traits/Carryall.cs @@ -9,6 +9,7 @@ */ #endregion +using System.Collections.Frozen; using System.Collections.Generic; using System.Linq; using OpenRA.Graphics; @@ -69,7 +70,7 @@ namespace OpenRA.Mods.Common.Traits [ActorReference(dictionaryReference: LintDictionaryReference.Keys)] [Desc("Conditions to grant when a specified actor is being carried.", "A dictionary of [actor name]: [condition].")] - public readonly Dictionary CarryableConditions = []; + public readonly FrozenDictionary CarryableConditions = FrozenDictionary.Empty; [VoiceReference] public readonly string Voice = "Action"; @@ -107,7 +108,7 @@ namespace OpenRA.Mods.Common.Traits WAngle cachedFacing; IActorPreview[] carryablePreview; - HashSet landableTerrainTypes; + FrozenSet landableTerrainTypes; int carryConditionToken = Actor.InvalidConditionToken; int carryableConditionToken = Actor.InvalidConditionToken; @@ -197,7 +198,7 @@ namespace OpenRA.Mods.Common.Traits } } - HashSet IOverrideAircraftLanding.LandableTerrainTypes => landableTerrainTypes ?? aircraft.Info.LandableTerrainTypes; + FrozenSet IOverrideAircraftLanding.LandableTerrainTypes => landableTerrainTypes ?? aircraft.Info.LandableTerrainTypes; public virtual bool AttachCarryable(Actor self, Actor carryable) { @@ -214,7 +215,7 @@ namespace OpenRA.Mods.Common.Traits carryableConditionToken = self.GrantCondition(carryableCondition); CarryableOffset = OffsetForCarryable(self, carryable); - landableTerrainTypes = Carryable.Trait().Info.LocomotorInfo.TerrainSpeeds.Keys.ToHashSet(); + landableTerrainTypes = Carryable.Trait().Info.LocomotorInfo.TerrainSpeeds.Keys.ToFrozenSet(); return true; } diff --git a/OpenRA.Mods.Common/Traits/Conditions/GrantChargedConditionOnToggle.cs b/OpenRA.Mods.Common/Traits/Conditions/GrantChargedConditionOnToggle.cs index 10edb4f62d..131e8102df 100644 --- a/OpenRA.Mods.Common/Traits/Conditions/GrantChargedConditionOnToggle.cs +++ b/OpenRA.Mods.Common/Traits/Conditions/GrantChargedConditionOnToggle.cs @@ -10,6 +10,7 @@ #endregion using System.Collections.Generic; +using System.Collections.Immutable; using OpenRA.Activities; using OpenRA.Mods.Common.Orders; using OpenRA.Primitives; @@ -58,10 +59,10 @@ namespace OpenRA.Mods.Common.Traits public readonly string BlockedCursor = "deploy-blocked"; [Desc("Play a randomly selected sound from this list when turning on.")] - public readonly string[] ActivationSounds = null; + public readonly ImmutableArray ActivationSounds = default; [Desc("Play a randomly selected sound from this list when turning off.")] - public readonly string[] DeactivattionSounds = null; + public readonly ImmutableArray DeactivattionSounds = default; [VoiceReference] public readonly string Voice = "Action"; diff --git a/OpenRA.Mods.Common/Traits/Conditions/GrantConditionOnAttack.cs b/OpenRA.Mods.Common/Traits/Conditions/GrantConditionOnAttack.cs index c875c4365e..7d17e8a091 100644 --- a/OpenRA.Mods.Common/Traits/Conditions/GrantConditionOnAttack.cs +++ b/OpenRA.Mods.Common/Traits/Conditions/GrantConditionOnAttack.cs @@ -9,7 +9,9 @@ */ #endregion +using System.Collections.Frozen; using System.Collections.Generic; +using System.Collections.Immutable; using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits @@ -22,11 +24,11 @@ namespace OpenRA.Mods.Common.Traits public readonly string Condition = null; [Desc("Name of the armaments that grant this condition.")] - public readonly HashSet ArmamentNames = ["primary"]; + public readonly FrozenSet ArmamentNames = new HashSet { "primary" }.ToFrozenSet(); [Desc("Shots required to apply an instance of the condition. If there are more instances of the condition granted than values listed,", "the last value is used for all following instances beyond the defined range.")] - public readonly int[] RequiredShotsPerInstance = [1]; + public readonly ImmutableArray RequiredShotsPerInstance = [1]; [Desc("Maximum instances of the condition to grant.")] public readonly int MaximumInstances = 1; diff --git a/OpenRA.Mods.Common/Traits/Conditions/GrantConditionOnBotOwner.cs b/OpenRA.Mods.Common/Traits/Conditions/GrantConditionOnBotOwner.cs index 3bcda48aee..94eb52375f 100644 --- a/OpenRA.Mods.Common/Traits/Conditions/GrantConditionOnBotOwner.cs +++ b/OpenRA.Mods.Common/Traits/Conditions/GrantConditionOnBotOwner.cs @@ -9,8 +9,7 @@ */ #endregion -using System; -using System.Linq; +using System.Collections.Immutable; using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits @@ -25,7 +24,7 @@ namespace OpenRA.Mods.Common.Traits [FieldLoader.Require] [Desc("Bot types that trigger the condition.")] - public readonly string[] Bots = []; + public readonly ImmutableArray Bots = []; public override object Create(ActorInitializer init) { return new GrantConditionOnBotOwner(this); } } diff --git a/OpenRA.Mods.Common/Traits/Conditions/GrantConditionOnClientDock.cs b/OpenRA.Mods.Common/Traits/Conditions/GrantConditionOnClientDock.cs index faad3d20b3..a20dbff499 100644 --- a/OpenRA.Mods.Common/Traits/Conditions/GrantConditionOnClientDock.cs +++ b/OpenRA.Mods.Common/Traits/Conditions/GrantConditionOnClientDock.cs @@ -9,7 +9,7 @@ */ #endregion -using System.Collections.Generic; +using System.Collections.Frozen; using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits @@ -25,7 +25,7 @@ namespace OpenRA.Mods.Common.Traits public readonly int AfterDockDuration = 0; [Desc("Host actor type(s) leading to the condition being granted. Leave empty for allowing all hosts by default.")] - public readonly HashSet DockHostNames = null; + public readonly FrozenSet DockHostNames = null; public override object Create(ActorInitializer init) { return new GrantConditionOnClientDock(this); } } diff --git a/OpenRA.Mods.Common/Traits/Conditions/GrantConditionOnDamageState.cs b/OpenRA.Mods.Common/Traits/Conditions/GrantConditionOnDamageState.cs index 2c230d840b..0012915ae9 100644 --- a/OpenRA.Mods.Common/Traits/Conditions/GrantConditionOnDamageState.cs +++ b/OpenRA.Mods.Common/Traits/Conditions/GrantConditionOnDamageState.cs @@ -9,6 +9,7 @@ */ #endregion +using System.Collections.Immutable; using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits @@ -22,10 +23,10 @@ namespace OpenRA.Mods.Common.Traits public readonly string Condition = null; [Desc("Play a random sound from this list when enabled.")] - public readonly string[] EnabledSounds = []; + public readonly ImmutableArray EnabledSounds = []; [Desc("Play a random sound from this list when disabled.")] - public readonly string[] DisabledSounds = []; + public readonly ImmutableArray DisabledSounds = []; [Desc("Levels of damage at which to grant the condition.")] public readonly DamageState ValidDamageStates = DamageState.Heavy | DamageState.Critical; diff --git a/OpenRA.Mods.Common/Traits/Conditions/GrantConditionOnDeploy.cs b/OpenRA.Mods.Common/Traits/Conditions/GrantConditionOnDeploy.cs index 02be42033c..c8d43a914a 100644 --- a/OpenRA.Mods.Common/Traits/Conditions/GrantConditionOnDeploy.cs +++ b/OpenRA.Mods.Common/Traits/Conditions/GrantConditionOnDeploy.cs @@ -9,7 +9,9 @@ */ #endregion +using System.Collections.Frozen; using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using OpenRA.Activities; using OpenRA.Mods.Common.Activities; @@ -32,7 +34,7 @@ namespace OpenRA.Mods.Common.Traits public readonly string DeployedCondition = null; [Desc("The terrain types that this actor can deploy on. Leave empty to allow any.")] - public readonly HashSet AllowedTerrainTypes = []; + public readonly FrozenSet AllowedTerrainTypes = FrozenSet.Empty; [Desc("Can this actor deploy on slopes?")] public readonly bool CanDeployOnRamps = false; @@ -52,10 +54,10 @@ namespace OpenRA.Mods.Common.Traits public readonly WAngle? Facing = null; [Desc("Play a randomly selected sound from this list when deploying.")] - public readonly string[] DeploySounds = null; + public readonly ImmutableArray DeploySounds = default; [Desc("Play a randomly selected sound from this list when undeploying.")] - public readonly string[] UndeploySounds = null; + public readonly ImmutableArray UndeploySounds = default; [Desc("Skip make/deploy animation?")] public readonly bool SkipMakeAnimation = false; diff --git a/OpenRA.Mods.Common/Traits/Conditions/GrantConditionOnFaction.cs b/OpenRA.Mods.Common/Traits/Conditions/GrantConditionOnFaction.cs index 2831119bfd..c5f4109886 100644 --- a/OpenRA.Mods.Common/Traits/Conditions/GrantConditionOnFaction.cs +++ b/OpenRA.Mods.Common/Traits/Conditions/GrantConditionOnFaction.cs @@ -9,7 +9,7 @@ */ #endregion -using System.Collections.Generic; +using System.Collections.Frozen; using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits @@ -23,7 +23,7 @@ namespace OpenRA.Mods.Common.Traits public readonly string Condition = null; [Desc("Only grant this condition for certain factions.")] - public readonly HashSet Factions = []; + public readonly FrozenSet Factions = FrozenSet.Empty; [Desc("Should it recheck everything when it is captured?")] public readonly bool ResetOnOwnerChange = false; diff --git a/OpenRA.Mods.Common/Traits/Conditions/GrantConditionOnHealth.cs b/OpenRA.Mods.Common/Traits/Conditions/GrantConditionOnHealth.cs index 7620f67f7b..1a20582431 100644 --- a/OpenRA.Mods.Common/Traits/Conditions/GrantConditionOnHealth.cs +++ b/OpenRA.Mods.Common/Traits/Conditions/GrantConditionOnHealth.cs @@ -9,6 +9,7 @@ */ #endregion +using System.Collections.Immutable; using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits @@ -22,10 +23,10 @@ namespace OpenRA.Mods.Common.Traits public readonly string Condition = null; [Desc("Play a random sound from this list when enabled.")] - public readonly string[] EnabledSounds = []; + public readonly ImmutableArray EnabledSounds = []; [Desc("Play a random sound from this list when disabled.")] - public readonly string[] DisabledSounds = []; + public readonly ImmutableArray DisabledSounds = []; [Desc("Minimum level of health at which to grant the condition.")] public readonly int MinHP = 0; diff --git a/OpenRA.Mods.Common/Traits/Conditions/GrantConditionOnHostDock.cs b/OpenRA.Mods.Common/Traits/Conditions/GrantConditionOnHostDock.cs index 4adcdbaf95..83794fec3b 100644 --- a/OpenRA.Mods.Common/Traits/Conditions/GrantConditionOnHostDock.cs +++ b/OpenRA.Mods.Common/Traits/Conditions/GrantConditionOnHostDock.cs @@ -9,7 +9,7 @@ */ #endregion -using System.Collections.Generic; +using System.Collections.Frozen; using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits @@ -25,7 +25,7 @@ namespace OpenRA.Mods.Common.Traits public readonly int AfterDockDuration = 0; [Desc("Client actor type(s) leading to the condition being granted. Leave empty for allowing all clients by default.")] - public readonly HashSet DockClientNames = null; + public readonly FrozenSet DockClientNames = null; public override object Create(ActorInitializer init) { return new GrantConditionOnHostDock(this); } } diff --git a/OpenRA.Mods.Common/Traits/Conditions/GrantConditionOnPrerequisite.cs b/OpenRA.Mods.Common/Traits/Conditions/GrantConditionOnPrerequisite.cs index 47c1457a64..b0bfde2e01 100644 --- a/OpenRA.Mods.Common/Traits/Conditions/GrantConditionOnPrerequisite.cs +++ b/OpenRA.Mods.Common/Traits/Conditions/GrantConditionOnPrerequisite.cs @@ -9,6 +9,7 @@ */ #endregion +using System.Collections.Immutable; using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits @@ -23,7 +24,7 @@ namespace OpenRA.Mods.Common.Traits [FieldLoader.Require] [Desc("List of required prerequisites.")] - public readonly string[] Prerequisites = []; + public readonly ImmutableArray Prerequisites = []; public override object Create(ActorInitializer init) { return new GrantConditionOnPrerequisite(this); } } diff --git a/OpenRA.Mods.Common/Traits/Conditions/GrantConditionOnProduction.cs b/OpenRA.Mods.Common/Traits/Conditions/GrantConditionOnProduction.cs index 0bdf10960d..ec602894cc 100644 --- a/OpenRA.Mods.Common/Traits/Conditions/GrantConditionOnProduction.cs +++ b/OpenRA.Mods.Common/Traits/Conditions/GrantConditionOnProduction.cs @@ -9,7 +9,7 @@ */ #endregion -using System.Collections.Generic; +using System.Collections.Frozen; using System.Linq; using OpenRA.Primitives; using OpenRA.Traits; @@ -26,7 +26,7 @@ namespace OpenRA.Mods.Common.Traits [ActorReference] [Desc("The actors to grant condition for. If empty condition will be granted for all actors.")] - public readonly HashSet Actors = []; + public readonly FrozenSet Actors = FrozenSet.Empty; [Desc("How long condition is applies for. Use -1 for infinite.")] public readonly int Duration = -1; diff --git a/OpenRA.Mods.Common/Traits/Conditions/GrantConditionOnTerrain.cs b/OpenRA.Mods.Common/Traits/Conditions/GrantConditionOnTerrain.cs index 45937f42ba..9d33a2023e 100644 --- a/OpenRA.Mods.Common/Traits/Conditions/GrantConditionOnTerrain.cs +++ b/OpenRA.Mods.Common/Traits/Conditions/GrantConditionOnTerrain.cs @@ -9,8 +9,7 @@ */ #endregion -using System; -using System.Linq; +using System.Collections.Immutable; using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits @@ -24,7 +23,7 @@ namespace OpenRA.Mods.Common.Traits [FieldLoader.Require] [Desc("Terrain names to trigger the condition.")] - public readonly string[] TerrainTypes = []; + public readonly ImmutableArray TerrainTypes = []; public override object Create(ActorInitializer init) { return new GrantConditionOnTerrain(init, this); } } @@ -32,7 +31,7 @@ namespace OpenRA.Mods.Common.Traits public class GrantConditionOnTerrain : ITick { readonly GrantConditionOnTerrainInfo info; - readonly TerrainTypeInfo[] terrainTypes; + readonly ImmutableArray terrainTypes; int conditionToken = Actor.InvalidConditionToken; string cachedTerrain; diff --git a/OpenRA.Mods.Common/Traits/Conditions/GrantConditionOnTileSet.cs b/OpenRA.Mods.Common/Traits/Conditions/GrantConditionOnTileSet.cs index fa6d3aa987..88cd142f1c 100644 --- a/OpenRA.Mods.Common/Traits/Conditions/GrantConditionOnTileSet.cs +++ b/OpenRA.Mods.Common/Traits/Conditions/GrantConditionOnTileSet.cs @@ -9,8 +9,7 @@ */ #endregion -using System; -using System.Linq; +using System.Collections.Immutable; using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits @@ -24,7 +23,7 @@ namespace OpenRA.Mods.Common.Traits [FieldLoader.Require] [Desc("Tile set IDs to trigger the condition.")] - public readonly string[] TileSets = []; + public readonly ImmutableArray TileSets = []; public override object Create(ActorInitializer init) { return new GrantConditionOnTileSet(this); } } diff --git a/OpenRA.Mods.Common/Traits/Conditions/GrantRandomCondition.cs b/OpenRA.Mods.Common/Traits/Conditions/GrantRandomCondition.cs index 314216e243..1346472a83 100644 --- a/OpenRA.Mods.Common/Traits/Conditions/GrantRandomCondition.cs +++ b/OpenRA.Mods.Common/Traits/Conditions/GrantRandomCondition.cs @@ -9,6 +9,7 @@ */ #endregion +using System.Collections.Immutable; using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits.Conditions @@ -19,7 +20,7 @@ namespace OpenRA.Mods.Common.Traits.Conditions [FieldLoader.Require] [GrantedConditionReference] [Desc("List of conditions to grant from.")] - public readonly string[] Conditions = null; + public readonly ImmutableArray Conditions = default; public override object Create(ActorInitializer init) { return new GrantRandomCondition(this); } } diff --git a/OpenRA.Mods.Common/Traits/Crates/Crate.cs b/OpenRA.Mods.Common/Traits/Crates/Crate.cs index c2d8b01c04..7e373b4f0f 100644 --- a/OpenRA.Mods.Common/Traits/Crates/Crate.cs +++ b/OpenRA.Mods.Common/Traits/Crates/Crate.cs @@ -9,6 +9,7 @@ */ #endregion +using System.Collections.Frozen; using System.Collections.Generic; using System.Linq; using OpenRA.Mods.Common.Activities; @@ -25,7 +26,7 @@ namespace OpenRA.Mods.Common.Traits public readonly int Duration = 0; [Desc("Allowed to land on.")] - public readonly HashSet TerrainTypes = []; + public readonly FrozenSet TerrainTypes = FrozenSet.Empty; [Desc("Define actors that can collect crates by setting this into the Crushes field from the Mobile trait.")] public readonly string CrushClass = "crate"; diff --git a/OpenRA.Mods.Common/Traits/Crates/CrateAction.cs b/OpenRA.Mods.Common/Traits/Crates/CrateAction.cs index 2a4e839388..3a6b90f201 100644 --- a/OpenRA.Mods.Common/Traits/Crates/CrateAction.cs +++ b/OpenRA.Mods.Common/Traits/Crates/CrateAction.cs @@ -9,8 +9,7 @@ */ #endregion -using System; -using System.Linq; +using System.Collections.Immutable; using OpenRA.Mods.Common.Effects; using OpenRA.Traits; @@ -47,11 +46,11 @@ namespace OpenRA.Mods.Common.Traits public readonly int TimeDelay = 0; [Desc("Only allow this crate action when the collector has these prerequisites")] - public readonly string[] Prerequisites = []; + public readonly ImmutableArray Prerequisites = []; [ActorReference] [Desc("Actor types that this crate action will not occur for.")] - public readonly string[] ExcludedActorTypes = []; + public readonly ImmutableArray ExcludedActorTypes = []; public override object Create(ActorInitializer init) { return new CrateAction(init.Self, this); } } diff --git a/OpenRA.Mods.Common/Traits/Crates/DuplicateUnitCrateAction.cs b/OpenRA.Mods.Common/Traits/Crates/DuplicateUnitCrateAction.cs index 40b89fa2c1..1c11e30947 100644 --- a/OpenRA.Mods.Common/Traits/Crates/DuplicateUnitCrateAction.cs +++ b/OpenRA.Mods.Common/Traits/Crates/DuplicateUnitCrateAction.cs @@ -10,7 +10,7 @@ #endregion using System; -using System.Collections.Generic; +using System.Collections.Frozen; using System.Linq; using OpenRA.Primitives; using OpenRA.Traits; @@ -36,7 +36,7 @@ namespace OpenRA.Mods.Common.Traits public readonly BitSet ValidTargets = new("Ground", "Water"); [Desc("Which factions this crate action can occur for.")] - public readonly HashSet ValidFactions = []; + public readonly FrozenSet ValidFactions = FrozenSet.Empty; [Desc("Is the new duplicates given to a specific owner, regardless of whom collected it?")] public readonly string Owner = null; diff --git a/OpenRA.Mods.Common/Traits/Crates/GiveUnitCrateAction.cs b/OpenRA.Mods.Common/Traits/Crates/GiveUnitCrateAction.cs index eb9927a787..b75653ee86 100644 --- a/OpenRA.Mods.Common/Traits/Crates/GiveUnitCrateAction.cs +++ b/OpenRA.Mods.Common/Traits/Crates/GiveUnitCrateAction.cs @@ -9,7 +9,9 @@ */ #endregion +using System.Collections.Frozen; using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using OpenRA.Traits; @@ -21,10 +23,10 @@ namespace OpenRA.Mods.Common.Traits [ActorReference] [FieldLoader.Require] [Desc("The list of units to spawn.")] - public readonly string[] Units = []; + public readonly ImmutableArray Units = []; [Desc("Factions that are allowed to trigger this action.")] - public readonly HashSet ValidFactions = []; + public readonly FrozenSet ValidFactions = FrozenSet.Empty; [Desc("Override the owner of the newly spawned unit: e.g. Creeps or Neutral")] public readonly string Owner = null; diff --git a/OpenRA.Mods.Common/Traits/DamagedByTerrain.cs b/OpenRA.Mods.Common/Traits/DamagedByTerrain.cs index 0ccb622743..5c06e75c8e 100644 --- a/OpenRA.Mods.Common/Traits/DamagedByTerrain.cs +++ b/OpenRA.Mods.Common/Traits/DamagedByTerrain.cs @@ -9,8 +9,7 @@ */ #endregion -using System; -using System.Linq; +using System.Collections.Immutable; using OpenRA.Primitives; using OpenRA.Traits; @@ -31,7 +30,7 @@ namespace OpenRA.Mods.Common.Traits [FieldLoader.Require] [Desc("Terrain types where the actor will take damage.")] - public readonly string[] Terrain = []; + public readonly ImmutableArray Terrain = []; public override object Create(ActorInitializer init) { return new DamagedByTerrain(this); } } diff --git a/OpenRA.Mods.Common/Traits/DeliversCash.cs b/OpenRA.Mods.Common/Traits/DeliversCash.cs index 5fb65b3f50..f9d8d3169c 100644 --- a/OpenRA.Mods.Common/Traits/DeliversCash.cs +++ b/OpenRA.Mods.Common/Traits/DeliversCash.cs @@ -10,6 +10,7 @@ #endregion using System.Collections.Generic; +using System.Collections.Immutable; using OpenRA.Mods.Common.Activities; using OpenRA.Mods.Common.Orders; using OpenRA.Primitives; @@ -30,7 +31,7 @@ namespace OpenRA.Mods.Common.Traits public readonly string Type = null; [Desc("Sound to play when delivering cash")] - public readonly string[] Sounds = []; + public readonly ImmutableArray Sounds = []; [CursorReference] [Desc("Cursor to display when hovering over a valid actor to deliver cash to.")] diff --git a/OpenRA.Mods.Common/Traits/DockClientManager.cs b/OpenRA.Mods.Common/Traits/DockClientManager.cs index 71da00b2fe..b75f570346 100644 --- a/OpenRA.Mods.Common/Traits/DockClientManager.cs +++ b/OpenRA.Mods.Common/Traits/DockClientManager.cs @@ -10,6 +10,7 @@ #endregion using System; +using System.Collections.Frozen; using System.Collections.Generic; using System.Linq; using OpenRA.Mods.Common.Activities; @@ -40,7 +41,7 @@ namespace OpenRA.Mods.Common.Traits [CursorReference(dictionaryReference: LintDictionaryReference.Values)] [Desc($"Cursor to display when able to dock at target actor. Overrides the default cursor specified in {nameof(EnterCursor)}", "A dictionary of [DockType]: [cursor name].")] - public readonly Dictionary EnterCursorOverrides = []; + public readonly FrozenDictionary EnterCursorOverrides = FrozenDictionary.Empty; [CursorReference] [Desc("Cursor to display when unable to dock at target actor.")] diff --git a/OpenRA.Mods.Common/Traits/FireProjectilesOnDeath.cs b/OpenRA.Mods.Common/Traits/FireProjectilesOnDeath.cs index 5f34138298..0bb401f897 100644 --- a/OpenRA.Mods.Common/Traits/FireProjectilesOnDeath.cs +++ b/OpenRA.Mods.Common/Traits/FireProjectilesOnDeath.cs @@ -9,6 +9,7 @@ */ #endregion +using System.Collections.Immutable; using System.Linq; using OpenRA.GameRules; using OpenRA.Primitives; @@ -22,7 +23,7 @@ namespace OpenRA.Mods.Common.Traits [WeaponReference] [FieldLoader.Require] [Desc("The weapons used for shrapnel.")] - public readonly string[] Weapons = []; + public readonly ImmutableArray Weapons = []; [Desc("What damage type needs to kill the actor to trigger the firing of projectiles? " + "Leave empty to ignore damage types.")] @@ -35,12 +36,12 @@ namespace OpenRA.Mods.Common.Traits public readonly int MaximumDamage = int.MaxValue; [Desc("The amount of pieces of shrapnel to expel. Two values indicate a range.")] - public readonly int[] Pieces = [3, 10]; + public readonly ImmutableArray Pieces = [3, 10]; [Desc("The minimum and maximum distances the shrapnel may travel.")] - public readonly WDist[] Range = [WDist.FromCells(2), WDist.FromCells(5)]; + public readonly ImmutableArray Range = [WDist.FromCells(2), WDist.FromCells(5)]; - public WeaponInfo[] WeaponInfos { get; private set; } + public ImmutableArray WeaponInfos { get; private set; } public override object Create(ActorInitializer actor) { return new FireProjectilesOnDeath(this); } public override void RulesetLoaded(Ruleset rules, ActorInfo ai) @@ -53,7 +54,7 @@ namespace OpenRA.Mods.Common.Traits if (!rules.Weapons.TryGetValue(weaponToLower, out var weapon)) throw new YamlException($"Weapons Ruleset does not contain an entry '{weaponToLower}'"); return weapon; - }).ToArray(); + }).ToImmutableArray(); } } diff --git a/OpenRA.Mods.Common/Traits/FireWarheads.cs b/OpenRA.Mods.Common/Traits/FireWarheads.cs index be6fd8da7d..de4854c987 100644 --- a/OpenRA.Mods.Common/Traits/FireWarheads.cs +++ b/OpenRA.Mods.Common/Traits/FireWarheads.cs @@ -9,6 +9,7 @@ */ #endregion +using System.Collections.Immutable; using System.Linq; using OpenRA.GameRules; using OpenRA.Traits; @@ -21,7 +22,7 @@ namespace OpenRA.Mods.Common.Traits [WeaponReference] [FieldLoader.Require] [Desc("Weapons to fire.")] - public readonly string[] Weapons = []; + public readonly ImmutableArray Weapons = []; [Desc("How long (in ticks) to wait before the first detonation.")] public readonly int StartCooldown = 0; @@ -31,7 +32,7 @@ namespace OpenRA.Mods.Common.Traits public override object Create(ActorInitializer init) { return new FireWarheads(this); } - public WeaponInfo[] WeaponInfos { get; private set; } + public ImmutableArray WeaponInfos { get; private set; } public override void RulesetLoaded(Ruleset rules, ActorInfo ai) { @@ -43,7 +44,7 @@ namespace OpenRA.Mods.Common.Traits if (!rules.Weapons.TryGetValue(weaponToLower, out var weapon)) throw new YamlException($"Weapons Ruleset does not contain an entry '{weaponToLower}'"); return weapon; - }).ToArray(); + }).ToImmutableArray(); } } diff --git a/OpenRA.Mods.Common/Traits/GainsExperience.cs b/OpenRA.Mods.Common/Traits/GainsExperience.cs index 3ea5c2593a..58a95dd2d0 100644 --- a/OpenRA.Mods.Common/Traits/GainsExperience.cs +++ b/OpenRA.Mods.Common/Traits/GainsExperience.cs @@ -10,6 +10,7 @@ #endregion using System; +using System.Collections.Frozen; using System.Collections.Generic; using OpenRA.Mods.Common.Effects; using OpenRA.Primitives; @@ -24,7 +25,7 @@ namespace OpenRA.Mods.Common.Traits [Desc("Condition to grant at each level.", "Key is the XP requirements for each level as a percentage of our own value.", "Value is the condition to grant.")] - public readonly Dictionary Conditions = null; + public readonly FrozenDictionary Conditions = null; [GrantedConditionReference] public IEnumerable LinterConditions => Conditions.Values; diff --git a/OpenRA.Mods.Common/Traits/Harvester.cs b/OpenRA.Mods.Common/Traits/Harvester.cs index d240eed1da..59c044234a 100644 --- a/OpenRA.Mods.Common/Traits/Harvester.cs +++ b/OpenRA.Mods.Common/Traits/Harvester.cs @@ -11,6 +11,7 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using OpenRA.Mods.Common.Activities; using OpenRA.Primitives; @@ -37,7 +38,7 @@ namespace OpenRA.Mods.Common.Traits public readonly int HarvestFacings = 0; [Desc("Which resources it can harvest.")] - public readonly string[] Resources = []; + public readonly ImmutableArray Resources = []; [Desc("Percentage of maximum speed when fully loaded.")] public readonly int FullyLoadedSpeed = 85; diff --git a/OpenRA.Mods.Common/Traits/HitShape.cs b/OpenRA.Mods.Common/Traits/HitShape.cs index b044e9f0a5..f3584a058e 100644 --- a/OpenRA.Mods.Common/Traits/HitShape.cs +++ b/OpenRA.Mods.Common/Traits/HitShape.cs @@ -10,6 +10,7 @@ #endregion using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using OpenRA.Graphics; using OpenRA.Mods.Common.Graphics; @@ -26,7 +27,7 @@ namespace OpenRA.Mods.Common.Traits public readonly string Turret = null; [Desc("Create a targetable position for each offset listed here (relative to CenterPosition).")] - public readonly WVec[] TargetableOffsets = [WVec.Zero]; + public readonly ImmutableArray TargetableOffsets = [WVec.Zero]; [Desc("Create a targetable position at the center of each occupied cell. Stacks with TargetableOffsets.")] public readonly bool UseTargetableCellsOffsets = false; diff --git a/OpenRA.Mods.Common/Traits/Husk.cs b/OpenRA.Mods.Common/Traits/Husk.cs index e95baaa4de..d65fc9382f 100644 --- a/OpenRA.Mods.Common/Traits/Husk.cs +++ b/OpenRA.Mods.Common/Traits/Husk.cs @@ -10,6 +10,7 @@ #endregion using System; +using System.Collections.Frozen; using System.Collections.Generic; using System.Linq; using OpenRA.Activities; @@ -22,7 +23,7 @@ namespace OpenRA.Mods.Common.Traits [Desc("Spawns remains of a husk actor with the correct facing.")] public class HuskInfo : TraitInfo, IPositionableInfo, IFacingInfo, IActorPreviewInitInfo, IRulesetLoaded { - public readonly HashSet AllowedTerrain = []; + public readonly FrozenSet AllowedTerrain = FrozenSet.Empty; [Desc("Facing to use for actor previews (map editor, color picker, etc)")] public readonly WAngle PreviewFacing = new(384); diff --git a/OpenRA.Mods.Common/Traits/Infantry/ScaredyCat.cs b/OpenRA.Mods.Common/Traits/Infantry/ScaredyCat.cs index 5d9b116c7f..10ab54eaf9 100644 --- a/OpenRA.Mods.Common/Traits/Infantry/ScaredyCat.cs +++ b/OpenRA.Mods.Common/Traits/Infantry/ScaredyCat.cs @@ -10,7 +10,7 @@ #endregion using System; -using System.Collections.Generic; +using System.Collections.Frozen; using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits @@ -31,7 +31,7 @@ namespace OpenRA.Mods.Common.Traits public readonly int AttackPanicChance = 20; [Desc("The terrain types that this actor should avoid running on to while panicking.")] - public readonly HashSet AvoidTerrainTypes = []; + public readonly FrozenSet AvoidTerrainTypes = FrozenSet.Empty; [SequenceReference(prefix: true)] public readonly string PanicSequencePrefix = "panic-"; diff --git a/OpenRA.Mods.Common/Traits/Infantry/TakeCover.cs b/OpenRA.Mods.Common/Traits/Infantry/TakeCover.cs index 0c9d284eab..4081772de8 100644 --- a/OpenRA.Mods.Common/Traits/Infantry/TakeCover.cs +++ b/OpenRA.Mods.Common/Traits/Infantry/TakeCover.cs @@ -9,7 +9,7 @@ */ #endregion -using System.Collections.Generic; +using System.Collections.Frozen; using System.Linq; using OpenRA.Primitives; using OpenRA.Traits; @@ -31,7 +31,7 @@ namespace OpenRA.Mods.Common.Traits public readonly BitSet DamageTriggers = default; [Desc("Damage modifiers for each damage type (defined on the warheads) while the unit is prone.")] - public readonly Dictionary DamageModifiers = []; + public readonly FrozenDictionary DamageModifiers = FrozenDictionary.Empty; [Desc("Muzzle offset modifier to apply while prone.")] public readonly WVec ProneOffset = new(500, 0, 0); diff --git a/OpenRA.Mods.Common/Traits/Infantry/TerrainModifiesDamage.cs b/OpenRA.Mods.Common/Traits/Infantry/TerrainModifiesDamage.cs index 17d37543ce..842f560198 100644 --- a/OpenRA.Mods.Common/Traits/Infantry/TerrainModifiesDamage.cs +++ b/OpenRA.Mods.Common/Traits/Infantry/TerrainModifiesDamage.cs @@ -9,7 +9,7 @@ */ #endregion -using System.Collections.Generic; +using System.Collections.Frozen; using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits @@ -18,7 +18,7 @@ namespace OpenRA.Mods.Common.Traits { [FieldLoader.Require] [Desc("Damage percentage for specific terrain types. 120 = 120%, 80 = 80%, etc.")] - public readonly Dictionary TerrainModifier = null; + public readonly FrozenDictionary TerrainModifier = null; [Desc("Modify healing damage? For example: A friendly medic.")] public readonly bool ModifyHealing = false; diff --git a/OpenRA.Mods.Common/Traits/Interactable.cs b/OpenRA.Mods.Common/Traits/Interactable.cs index 32cff2e151..e39109a292 100644 --- a/OpenRA.Mods.Common/Traits/Interactable.cs +++ b/OpenRA.Mods.Common/Traits/Interactable.cs @@ -9,6 +9,7 @@ */ #endregion +using System.Collections.Immutable; using System.Linq; using OpenRA.Graphics; using OpenRA.Primitives; @@ -23,16 +24,16 @@ namespace OpenRA.Mods.Common.Traits "If null, the engine will guess an appropriate size based on the With*Body trait.", "The first two numbers define the width and height of the rectangle as a world distance.", "The (optional) second two numbers define an x and y offset from the actor center.")] - public readonly WDist[] Bounds = null; + public readonly ImmutableArray Bounds = default; [Desc("Defines a custom rectangle for Decorations (e.g. the selection box).", "If null, Bounds will be used instead")] - public readonly WDist[] DecorationBounds = null; + public readonly ImmutableArray DecorationBounds = default; [Desc("Defines a custom 2D polygon for mouse interaction with the actor.", "If null, Bounds will be used instead", "Each vertex has two components (so two numbers), which define an x and y offset from the actor center.")] - public readonly int2[] Polygon = null; + public readonly ImmutableArray Polygon = default; public override object Create(ActorInitializer init) { return new Interactable(this); } } @@ -66,7 +67,7 @@ namespace OpenRA.Mods.Common.Traits return autoBounds.Select(s => s.AutoMouseoverBounds(self, wr)).FirstOrDefault(r => !r.IsEmpty); } - int2[] PolygonBounds(Actor self, WorldRenderer wr) + ImmutableArray PolygonBounds(Actor self, WorldRenderer wr) { var screenVertices = new int2[info.Polygon.Length]; @@ -78,10 +79,10 @@ namespace OpenRA.Mods.Common.Traits screenVertices[i] = wr.ScreenPxPosition(self.CenterPosition) + offset; } - return screenVertices; + return screenVertices.ToImmutableArray(); } - Polygon Bounds(Actor self, WorldRenderer wr, WDist[] bounds) + Polygon Bounds(Actor self, WorldRenderer wr, ImmutableArray bounds) { if (bounds == null) return new Polygon(AutoBounds(self, wr)); @@ -107,7 +108,7 @@ namespace OpenRA.Mods.Common.Traits public Rectangle DecorationBounds(Actor self, WorldRenderer wr) { - return Bounds(self, wr, info.DecorationBounds ?? info.Bounds).BoundingRect; + return Bounds(self, wr, info.DecorationBounds != null ? info.DecorationBounds : info.Bounds).BoundingRect; } } } diff --git a/OpenRA.Mods.Common/Traits/IsometricSelectable.cs b/OpenRA.Mods.Common/Traits/IsometricSelectable.cs index d784b9c054..2bf4b70200 100644 --- a/OpenRA.Mods.Common/Traits/IsometricSelectable.cs +++ b/OpenRA.Mods.Common/Traits/IsometricSelectable.cs @@ -10,6 +10,7 @@ #endregion using System; +using System.Collections.Immutable; using OpenRA.Graphics; using OpenRA.Primitives; using OpenRA.Traits; @@ -23,14 +24,14 @@ namespace OpenRA.Mods.Common.Traits "If null, the engine will guess an appropriate size based on the building's footprint.", "The first two numbers define the width and depth of the footprint rectangle.", "The (optional) second two numbers define an x and y offset from the actor center.")] - public readonly int[] Bounds = null; + public readonly ImmutableArray Bounds = default; [Desc("Height above the footprint for the top of the interaction rectangle.")] public readonly int Height = 24; [Desc("Defines a custom rectangle for Decorations (e.g. the selection box).", "If null, Bounds will be used instead.")] - public readonly int[] DecorationBounds = null; + public readonly ImmutableArray DecorationBounds = default; [Desc("Defines a custom height for Decorations (e.g. the selection box).", "If < 0, Height will be used instead.", @@ -82,7 +83,7 @@ namespace OpenRA.Mods.Common.Traits buildingInfo = self.Info.TraitInfo(); } - Polygon Bounds(Actor self, WorldRenderer wr, int[] bounds, int height) + Polygon Bounds(Actor self, WorldRenderer wr, ImmutableArray bounds, int height) { int2 left, right, top, bottom; if (bounds != null) @@ -129,7 +130,11 @@ namespace OpenRA.Mods.Common.Traits public Polygon DecorationBounds(Actor self, WorldRenderer wr) { - return Bounds(self, wr, info.DecorationBounds ?? info.Bounds, info.DecorationHeight >= 0 ? info.DecorationHeight : info.Height); + return Bounds( + self, + wr, + info.DecorationBounds != null ? info.DecorationBounds : info.Bounds, + info.DecorationHeight >= 0 ? info.DecorationHeight : info.Height); } Polygon IMouseBounds.MouseoverBounds(Actor self, WorldRenderer wr) diff --git a/OpenRA.Mods.Common/Traits/KillsSelf.cs b/OpenRA.Mods.Common/Traits/KillsSelf.cs index 4134d78d6a..409867f7cd 100644 --- a/OpenRA.Mods.Common/Traits/KillsSelf.cs +++ b/OpenRA.Mods.Common/Traits/KillsSelf.cs @@ -9,6 +9,7 @@ */ #endregion +using System.Collections.Immutable; using OpenRA.Primitives; using OpenRA.Traits; @@ -20,7 +21,7 @@ namespace OpenRA.Mods.Common.Traits public readonly bool RemoveInstead = false; [Desc("The amount of time (in ticks) before the actor dies. Two values indicate a range between which a random value is chosen.")] - public readonly int[] Delay = [0]; + public readonly ImmutableArray Delay = [0]; [Desc("Types of damage that this trait causes. Leave empty for no damage types.")] public readonly BitSet DamageTypes = default; diff --git a/OpenRA.Mods.Common/Traits/MapEditorData.cs b/OpenRA.Mods.Common/Traits/MapEditorData.cs index 0ace5d176e..f575a45061 100644 --- a/OpenRA.Mods.Common/Traits/MapEditorData.cs +++ b/OpenRA.Mods.Common/Traits/MapEditorData.cs @@ -9,16 +9,17 @@ */ #endregion -using System.Collections.Generic; +using System.Collections.Frozen; +using System.Collections.Immutable; using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits { public class MapEditorDataInfo : TraitInfo { - public readonly HashSet RequireTilesets = null; - public readonly HashSet ExcludeTilesets = null; - public readonly string[] Categories; + public readonly FrozenSet RequireTilesets = null; + public readonly FrozenSet ExcludeTilesets = null; + public readonly ImmutableArray Categories; } public class MapEditorData { } diff --git a/OpenRA.Mods.Common/Traits/Minelayer.cs b/OpenRA.Mods.Common/Traits/Minelayer.cs index 1f3c961a1b..e07ed5b340 100644 --- a/OpenRA.Mods.Common/Traits/Minelayer.cs +++ b/OpenRA.Mods.Common/Traits/Minelayer.cs @@ -10,6 +10,7 @@ #endregion using System; +using System.Collections.Frozen; using System.Collections.Generic; using System.Linq; using OpenRA.Graphics; @@ -46,7 +47,7 @@ namespace OpenRA.Mods.Common.Traits public readonly string TileUnknownName = "build-unknown"; [Desc("Only allow laying mines on listed terrain types. Leave empty to allow all terrain types.")] - public readonly HashSet TerrainTypes = []; + public readonly FrozenSet TerrainTypes = FrozenSet.Empty; [CursorReference] [Desc("Cursor to display when able to lay a mine.")] diff --git a/OpenRA.Mods.Common/Traits/Mobile.cs b/OpenRA.Mods.Common/Traits/Mobile.cs index 6b055b7311..a68396d095 100644 --- a/OpenRA.Mods.Common/Traits/Mobile.cs +++ b/OpenRA.Mods.Common/Traits/Mobile.cs @@ -10,6 +10,7 @@ #endregion using System; +using System.Collections.Frozen; using System.Collections.Generic; using System.Linq; using OpenRA.Activities; @@ -50,7 +51,7 @@ namespace OpenRA.Mods.Common.Traits [CursorReference(dictionaryReference: LintDictionaryReference.Values)] [Desc("Cursor overrides to display for specific terrain types.", "A dictionary of [terrain type]: [cursor name].")] - public readonly Dictionary TerrainCursors = []; + public readonly FrozenDictionary TerrainCursors = FrozenDictionary.Empty; [CursorReference] [Desc("Cursor to display when a move order cannot be issued at target location.")] diff --git a/OpenRA.Mods.Common/Traits/Multipliers/ProductionCostMultiplier.cs b/OpenRA.Mods.Common/Traits/Multipliers/ProductionCostMultiplier.cs index 8cc2209d00..f534d6617b 100644 --- a/OpenRA.Mods.Common/Traits/Multipliers/ProductionCostMultiplier.cs +++ b/OpenRA.Mods.Common/Traits/Multipliers/ProductionCostMultiplier.cs @@ -9,7 +9,8 @@ */ #endregion -using System.Collections.Generic; +using System.Collections.Frozen; +using System.Collections.Immutable; using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits @@ -21,10 +22,10 @@ namespace OpenRA.Mods.Common.Traits public readonly int Multiplier = 100; [Desc("Only apply this cost change if owner has these prerequisites.")] - public readonly string[] Prerequisites = []; + public readonly ImmutableArray Prerequisites = []; [Desc("Queues that this cost will apply.")] - public readonly HashSet Queue = []; + public readonly FrozenSet Queue = FrozenSet.Empty; int IProductionCostModifierInfo.GetProductionCostModifier(TechTree techTree, string queue) { diff --git a/OpenRA.Mods.Common/Traits/Multipliers/ProductionTimeMultiplier.cs b/OpenRA.Mods.Common/Traits/Multipliers/ProductionTimeMultiplier.cs index bd1240a714..c49e69e83b 100644 --- a/OpenRA.Mods.Common/Traits/Multipliers/ProductionTimeMultiplier.cs +++ b/OpenRA.Mods.Common/Traits/Multipliers/ProductionTimeMultiplier.cs @@ -9,7 +9,8 @@ */ #endregion -using System.Collections.Generic; +using System.Collections.Frozen; +using System.Collections.Immutable; using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits @@ -21,10 +22,10 @@ namespace OpenRA.Mods.Common.Traits public readonly int Multiplier = 100; [Desc("Only apply this time change if owner has these prerequisites.")] - public readonly string[] Prerequisites = []; + public readonly ImmutableArray Prerequisites = []; [Desc("Queues that this time will apply.")] - public readonly HashSet Queue = []; + public readonly FrozenSet Queue = FrozenSet.Empty; int IProductionTimeModifierInfo.GetProductionTimeModifier(TechTree techTree, string queue) { diff --git a/OpenRA.Mods.Common/Traits/PaletteEffects/RotationPaletteEffect.cs b/OpenRA.Mods.Common/Traits/PaletteEffects/RotationPaletteEffect.cs index d0937a6010..766c478e8d 100644 --- a/OpenRA.Mods.Common/Traits/PaletteEffects/RotationPaletteEffect.cs +++ b/OpenRA.Mods.Common/Traits/PaletteEffects/RotationPaletteEffect.cs @@ -10,6 +10,7 @@ #endregion using System; +using System.Collections.Frozen; using System.Collections.Generic; using OpenRA.Graphics; using OpenRA.Traits; @@ -22,17 +23,17 @@ namespace OpenRA.Mods.Common.Traits { [Desc("Defines to which palettes this effect should be applied to.", "If none specified, it applies to all palettes not explicitly excluded.")] - public readonly HashSet Palettes = []; + public readonly FrozenSet Palettes = FrozenSet.Empty; [Desc("Defines for which tileset IDs this effect should be loaded.", "If none specified, it applies to all tileset IDs not explicitly excluded.")] - public readonly HashSet Tilesets = []; + public readonly FrozenSet Tilesets = FrozenSet.Empty; [Desc("Defines which palettes should be excluded from this effect.")] - public readonly HashSet ExcludePalettes = []; + public readonly FrozenSet ExcludePalettes = FrozenSet.Empty; [Desc("Don't apply the effect for these tileset IDs.")] - public readonly HashSet ExcludeTilesets = []; + public readonly FrozenSet ExcludeTilesets = FrozenSet.Empty; [Desc("Palette index of first RotationRange color.")] public readonly int RotationBase = 0x60; @@ -107,7 +108,7 @@ namespace OpenRA.Mods.Common.Traits } } - static bool StartsWithAny(string name, HashSet prefixes) + static bool StartsWithAny(string name, FrozenSet prefixes) { // PERF: Avoid LINQ. foreach (var pref in prefixes) diff --git a/OpenRA.Mods.Common/Traits/Palettes/ColorPickerPalette.cs b/OpenRA.Mods.Common/Traits/Palettes/ColorPickerPalette.cs index f789c650b7..1782e2b6b8 100644 --- a/OpenRA.Mods.Common/Traits/Palettes/ColorPickerPalette.cs +++ b/OpenRA.Mods.Common/Traits/Palettes/ColorPickerPalette.cs @@ -10,6 +10,7 @@ #endregion using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using OpenRA.Graphics; using OpenRA.Primitives; @@ -32,7 +33,7 @@ namespace OpenRA.Mods.Common.Traits public readonly string BasePalette = null; [Desc("Remap these indices to player colors.")] - public readonly int[] RemapIndex = []; + public readonly ImmutableArray RemapIndex = []; [Desc("Allow palette modifiers to change the palette.")] public readonly bool AllowModifiers = true; @@ -59,7 +60,7 @@ namespace OpenRA.Mods.Common.Traits void ILoadsPalettes.LoadPalettes(WorldRenderer wr) { color = preferredColor; - var remap = new PlayerColorRemap(info.RemapIndex.Length == 0 ? Enumerable.Range(0, 256).ToArray() : info.RemapIndex, color); + var remap = new PlayerColorRemap(info.RemapIndex.Length == 0 ? Enumerable.Range(0, 256).ToImmutableArray() : info.RemapIndex, color); wr.AddPalette(info.Name, new ImmutablePalette(wr.Palette(info.BasePalette).Palette, remap), info.AllowModifiers); } @@ -71,7 +72,7 @@ namespace OpenRA.Mods.Common.Traits return; color = preferredColor; - var remap = new PlayerColorRemap(info.RemapIndex.Length == 0 ? Enumerable.Range(0, 256).ToArray() : info.RemapIndex, color); + var remap = new PlayerColorRemap(info.RemapIndex.Length == 0 ? Enumerable.Range(0, 256).ToImmutableArray() : info.RemapIndex, color); wr.ReplacePalette(info.Name, new ImmutablePalette(wr.Palette(info.BasePalette).Palette, remap)); } } diff --git a/OpenRA.Mods.Common/Traits/Palettes/FixedColorPalette.cs b/OpenRA.Mods.Common/Traits/Palettes/FixedColorPalette.cs index abcda55f83..70b5fa5725 100644 --- a/OpenRA.Mods.Common/Traits/Palettes/FixedColorPalette.cs +++ b/OpenRA.Mods.Common/Traits/Palettes/FixedColorPalette.cs @@ -10,6 +10,7 @@ #endregion using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using OpenRA.Graphics; using OpenRA.Primitives; @@ -30,7 +31,7 @@ namespace OpenRA.Mods.Common.Traits public readonly string Name = "resources"; [Desc("Remap these indices to pre-defined colors.")] - public readonly int[] RemapIndex = []; + public readonly ImmutableArray RemapIndex = []; [Desc("The fixed color to remap.")] public readonly Color Color; @@ -52,7 +53,7 @@ namespace OpenRA.Mods.Common.Traits public void LoadPalettes(WorldRenderer wr) { - var remap = new PlayerColorRemap(info.RemapIndex.Length == 0 ? Enumerable.Range(0, 256).ToArray() : info.RemapIndex, info.Color); + var remap = new PlayerColorRemap(info.RemapIndex.Length == 0 ? Enumerable.Range(0, 256).ToImmutableArray() : info.RemapIndex, info.Color); wr.AddPalette(info.Name, new ImmutablePalette(wr.Palette(info.Base).Palette, remap), info.AllowModifiers); } diff --git a/OpenRA.Mods.Common/Traits/Palettes/FixedPlayerColorShift.cs b/OpenRA.Mods.Common/Traits/Palettes/FixedPlayerColorShift.cs index 24ea579662..eeb073f4f8 100644 --- a/OpenRA.Mods.Common/Traits/Palettes/FixedPlayerColorShift.cs +++ b/OpenRA.Mods.Common/Traits/Palettes/FixedPlayerColorShift.cs @@ -9,7 +9,8 @@ */ #endregion -using System.Collections.Generic; +using System.Collections.Frozen; +using System.Collections.Immutable; using OpenRA.Graphics; using OpenRA.Primitives; using OpenRA.Traits; @@ -25,7 +26,7 @@ namespace OpenRA.Mods.Common.Traits [Desc("The name of the palette to base off.")] public readonly string BasePalette = null; - public readonly Dictionary PlayerIndex; + public readonly FrozenDictionary> PlayerIndex; public override object Create(ActorInitializer init) { return new FixedPlayerColorShift(this); } } diff --git a/OpenRA.Mods.Common/Traits/Palettes/IndexedPalette.cs b/OpenRA.Mods.Common/Traits/Palettes/IndexedPalette.cs index 1ce4e15ddf..bd26fe3613 100644 --- a/OpenRA.Mods.Common/Traits/Palettes/IndexedPalette.cs +++ b/OpenRA.Mods.Common/Traits/Palettes/IndexedPalette.cs @@ -10,6 +10,7 @@ #endregion using System.Collections.Generic; +using System.Collections.Immutable; using System.IO; using OpenRA.Graphics; using OpenRA.Primitives; @@ -32,11 +33,11 @@ namespace OpenRA.Mods.Common.Traits [FieldLoader.Require] [Desc("Indices from BasePalette to be swapped with ReplaceIndex.")] - public readonly int[] Index = []; + public readonly ImmutableArray Index = []; [FieldLoader.Require] [Desc("Indices from BasePalette to replace from Index.")] - public readonly int[] ReplaceIndex = []; + public readonly ImmutableArray ReplaceIndex = []; [Desc("Allow palette modifiers to change the palette.")] public readonly bool AllowModifiers = true; @@ -72,7 +73,7 @@ namespace OpenRA.Mods.Common.Traits readonly Dictionary replacements = []; readonly IPalette basePalette; - public IndexedColorRemap(IPalette basePalette, int[] ramp, int[] remap) + public IndexedColorRemap(IPalette basePalette, ImmutableArray ramp, ImmutableArray remap) { this.basePalette = basePalette; if (ramp.Length != remap.Length) diff --git a/OpenRA.Mods.Common/Traits/Palettes/IndexedPlayerPalette.cs b/OpenRA.Mods.Common/Traits/Palettes/IndexedPlayerPalette.cs index 72c3bbcd49..ddd32f47bf 100644 --- a/OpenRA.Mods.Common/Traits/Palettes/IndexedPlayerPalette.cs +++ b/OpenRA.Mods.Common/Traits/Palettes/IndexedPlayerPalette.cs @@ -9,7 +9,8 @@ */ #endregion -using System.Collections.Generic; +using System.Collections.Frozen; +using System.Collections.Immutable; using System.Linq; using OpenRA.Graphics; using OpenRA.Primitives; @@ -30,12 +31,12 @@ namespace OpenRA.Mods.Common.Traits public readonly string BaseName = "player"; [Desc("Remap these indices to player colors.")] - public readonly int[] RemapIndex = []; + public readonly ImmutableArray RemapIndex = []; [Desc("Allow palette modifiers to change the palette.")] public readonly bool AllowModifiers = true; - public readonly Dictionary PlayerIndex; + public readonly FrozenDictionary> PlayerIndex; public override object Create(ActorInitializer init) { return new IndexedPlayerPalette(this); } @@ -64,7 +65,7 @@ namespace OpenRA.Mods.Common.Traits if (info.PlayerIndex.TryGetValue(playerName, out var remap)) pal = new ImmutablePalette( basePalette, - new IndexedColorRemap(basePalette, info.RemapIndex.Length == 0 ? Enumerable.Range(0, 256).ToArray() : info.RemapIndex, remap)); + new IndexedColorRemap(basePalette, info.RemapIndex.Length == 0 ? Enumerable.Range(0, 256).ToImmutableArray() : info.RemapIndex, remap)); else pal = new ImmutablePalette(basePalette); diff --git a/OpenRA.Mods.Common/Traits/Palettes/PaletteFromFile.cs b/OpenRA.Mods.Common/Traits/Palettes/PaletteFromFile.cs index 5fc25512a6..2d1497622b 100644 --- a/OpenRA.Mods.Common/Traits/Palettes/PaletteFromFile.cs +++ b/OpenRA.Mods.Common/Traits/Palettes/PaletteFromFile.cs @@ -11,6 +11,7 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using OpenRA.FileSystem; using OpenRA.Graphics; using OpenRA.Traits; @@ -34,10 +35,10 @@ namespace OpenRA.Mods.Common.Traits public readonly string Filename = null; [Desc("Map listed indices to transparent. Ignores previous color.")] - public readonly int[] TransparentIndex = [0]; + public readonly ImmutableArray TransparentIndex = [0]; [Desc("Map listed indices to shadow. Ignores previous color.")] - public readonly int[] ShadowIndex = []; + public readonly ImmutableArray ShadowIndex = []; public readonly bool AllowModifiers = true; diff --git a/OpenRA.Mods.Common/Traits/Palettes/PaletteFromGimpOrJascFile.cs b/OpenRA.Mods.Common/Traits/Palettes/PaletteFromGimpOrJascFile.cs index dfc1885f70..83d46f6740 100644 --- a/OpenRA.Mods.Common/Traits/Palettes/PaletteFromGimpOrJascFile.cs +++ b/OpenRA.Mods.Common/Traits/Palettes/PaletteFromGimpOrJascFile.cs @@ -10,6 +10,7 @@ #endregion using System; +using System.Collections.Frozen; using System.Collections.Generic; using System.IO; using OpenRA.FileSystem; @@ -30,10 +31,10 @@ namespace OpenRA.Mods.Common.Traits [Desc("Defines for which tileset IDs this palette should be loaded.", "If none specified, it applies to all tileset IDs not explicitly excluded.")] - public readonly HashSet Tilesets = []; + public readonly FrozenSet Tilesets = FrozenSet.Empty; [Desc("Don't load palette for these tileset IDs.")] - public readonly HashSet ExcludeTilesets = []; + public readonly FrozenSet ExcludeTilesets = FrozenSet.Empty; [FieldLoader.Require] [Desc("Name of the file to load.")] diff --git a/OpenRA.Mods.Common/Traits/Palettes/PaletteFromPng.cs b/OpenRA.Mods.Common/Traits/Palettes/PaletteFromPng.cs index c775e08e73..04d2963365 100644 --- a/OpenRA.Mods.Common/Traits/Palettes/PaletteFromPng.cs +++ b/OpenRA.Mods.Common/Traits/Palettes/PaletteFromPng.cs @@ -11,6 +11,7 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using OpenRA.FileFormats; using OpenRA.FileSystem; using OpenRA.Graphics; @@ -35,7 +36,7 @@ namespace OpenRA.Mods.Common.Traits public readonly string Filename = null; [Desc("Map listed indices to shadow. Ignores previous color.")] - public readonly int[] ShadowIndex = []; + public readonly ImmutableArray ShadowIndex = []; public readonly bool AllowModifiers = true; diff --git a/OpenRA.Mods.Common/Traits/Palettes/PlayerColorPalette.cs b/OpenRA.Mods.Common/Traits/Palettes/PlayerColorPalette.cs index 7cba8ecded..fbb127824c 100644 --- a/OpenRA.Mods.Common/Traits/Palettes/PlayerColorPalette.cs +++ b/OpenRA.Mods.Common/Traits/Palettes/PlayerColorPalette.cs @@ -9,6 +9,7 @@ */ #endregion +using System.Collections.Immutable; using System.Linq; using OpenRA.Graphics; using OpenRA.Primitives; @@ -29,7 +30,7 @@ namespace OpenRA.Mods.Common.Traits public readonly string BaseName = "player"; [Desc("Remap these indices to player colors.")] - public readonly int[] RemapIndex = []; + public readonly ImmutableArray RemapIndex = []; [Desc("Allow palette modifiers to change the palette.")] public readonly bool AllowModifiers = true; @@ -48,7 +49,7 @@ namespace OpenRA.Mods.Common.Traits public void LoadPlayerPalettes(WorldRenderer wr, string playerName, Color color, bool replaceExisting) { - var remap = new PlayerColorRemap(info.RemapIndex.Length == 0 ? Enumerable.Range(0, 256).ToArray() : info.RemapIndex, color); + var remap = new PlayerColorRemap(info.RemapIndex.Length == 0 ? Enumerable.Range(0, 256).ToImmutableArray() : info.RemapIndex, color); var pal = new ImmutablePalette(wr.Palette(info.BasePalette).Palette, remap); wr.AddPalette(info.BaseName + playerName, pal, info.AllowModifiers, replaceExisting); } diff --git a/OpenRA.Mods.Common/Traits/Parachutable.cs b/OpenRA.Mods.Common/Traits/Parachutable.cs index bf6b9fecb7..c846ed3049 100644 --- a/OpenRA.Mods.Common/Traits/Parachutable.cs +++ b/OpenRA.Mods.Common/Traits/Parachutable.cs @@ -9,6 +9,7 @@ */ #endregion +using System.Collections.Frozen; using System.Collections.Generic; using System.Linq; using OpenRA.Mods.Common.Effects; @@ -44,7 +45,7 @@ namespace OpenRA.Mods.Common.Traits public readonly string WaterCorpsePalette = "effect"; [Desc("Terrain types on which to display WaterCorpseSequence.")] - public readonly HashSet WaterTerrainTypes = ["Water"]; + public readonly FrozenSet WaterTerrainTypes = new HashSet { "Water" }.ToFrozenSet(); public readonly string WaterImpactSound = null; diff --git a/OpenRA.Mods.Common/Traits/Passenger.cs b/OpenRA.Mods.Common/Traits/Passenger.cs index 557ebe587c..1823facb39 100644 --- a/OpenRA.Mods.Common/Traits/Passenger.cs +++ b/OpenRA.Mods.Common/Traits/Passenger.cs @@ -9,6 +9,7 @@ */ #endregion +using System.Collections.Frozen; using System.Collections.Generic; using OpenRA.Mods.Common.Activities; using OpenRA.Mods.Common.Orders; @@ -35,7 +36,7 @@ namespace OpenRA.Mods.Common.Traits [ActorReference(dictionaryReference: LintDictionaryReference.Keys)] [Desc("Conditions to grant when this actor is loaded inside specified transport.", "A dictionary of [actor name]: [condition].")] - public readonly Dictionary CargoConditions = []; + public readonly FrozenDictionary CargoConditions = FrozenDictionary.Empty; [GrantedConditionReference] public IEnumerable LinterCargoConditions => CargoConditions.Values; diff --git a/OpenRA.Mods.Common/Traits/Player/BulkProductionQueue.cs b/OpenRA.Mods.Common/Traits/Player/BulkProductionQueue.cs index a8b57467b3..d6ebba0be2 100644 --- a/OpenRA.Mods.Common/Traits/Player/BulkProductionQueue.cs +++ b/OpenRA.Mods.Common/Traits/Player/BulkProductionQueue.cs @@ -11,6 +11,7 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using OpenRA.Traits; @@ -42,7 +43,7 @@ namespace OpenRA.Mods.Common.Traits [NotificationReference("Speech")] [Desc("Notifications to play when the delivery actor is on its way. Last notification is played when the actors to deliver are spawned on the map.")] - public readonly string[] DeliveryProgressNotifications = []; + public readonly ImmutableArray DeliveryProgressNotifications = []; public override object Create(ActorInitializer init) { return new BulkProductionQueue(init, this); } } diff --git a/OpenRA.Mods.Common/Traits/Player/ClassicParallelProductionQueue.cs b/OpenRA.Mods.Common/Traits/Player/ClassicParallelProductionQueue.cs index 1f3fc60bdf..2da1fccfb3 100644 --- a/OpenRA.Mods.Common/Traits/Player/ClassicParallelProductionQueue.cs +++ b/OpenRA.Mods.Common/Traits/Player/ClassicParallelProductionQueue.cs @@ -11,6 +11,7 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using OpenRA.Primitives; using OpenRA.Traits; @@ -30,10 +31,10 @@ namespace OpenRA.Mods.Common.Traits [Desc("Every time another production building of the same queue is", "constructed, the build times of all actors in the queue", "modified by a percentage of the original time.")] - public readonly int[] BuildingCountBuildTimeMultipliers = [100, 86, 75, 67, 60, 55, 50]; + public readonly ImmutableArray BuildingCountBuildTimeMultipliers = [100, 86, 75, 67, 60, 55, 50]; [Desc("Build time modifier multiplied by the number of parallel production for producing different actors at the same time.")] - public readonly int[] ParallelPenaltyBuildTimeMultipliers = [100, 116, 133, 150, 166, 183, 200, 216, 233, 250]; + public readonly ImmutableArray ParallelPenaltyBuildTimeMultipliers = [100, 116, 133, 150, 166, 183, 200, 216, 233, 250]; public override object Create(ActorInitializer init) { return new ClassicParallelProductionQueue(init, this); } } diff --git a/OpenRA.Mods.Common/Traits/Player/ClassicProductionQueue.cs b/OpenRA.Mods.Common/Traits/Player/ClassicProductionQueue.cs index 89c67126b4..c30e27b1e4 100644 --- a/OpenRA.Mods.Common/Traits/Player/ClassicProductionQueue.cs +++ b/OpenRA.Mods.Common/Traits/Player/ClassicProductionQueue.cs @@ -11,6 +11,7 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using OpenRA.Primitives; using OpenRA.Traits; @@ -29,7 +30,7 @@ namespace OpenRA.Mods.Common.Traits [Desc("Every time another production building of the same queue is", "constructed, the build times of all actors in the queue", "decreased by a percentage of the original time.")] - public readonly int[] BuildTimeSpeedReduction = [100, 86, 75, 67, 60, 55, 50]; + public readonly ImmutableArray BuildTimeSpeedReduction = [100, 86, 75, 67, 60, 55, 50]; public override object Create(ActorInitializer init) { return new ClassicProductionQueue(init, this); } } diff --git a/OpenRA.Mods.Common/Traits/Player/GrantConditionOnPrerequisiteManager.cs b/OpenRA.Mods.Common/Traits/Player/GrantConditionOnPrerequisiteManager.cs index 9a22780723..a77b2c04bd 100644 --- a/OpenRA.Mods.Common/Traits/Player/GrantConditionOnPrerequisiteManager.cs +++ b/OpenRA.Mods.Common/Traits/Player/GrantConditionOnPrerequisiteManager.cs @@ -10,6 +10,7 @@ #endregion using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using OpenRA.Traits; @@ -34,12 +35,12 @@ namespace OpenRA.Mods.Common.Traits techTree = self.Trait(); } - static string MakeKey(string[] prerequisites) + static string MakeKey(ImmutableArray prerequisites) { return "condition_" + string.Join("_", prerequisites.Order()); } - public void Register(Actor actor, GrantConditionOnPrerequisite u, string[] prerequisites) + public void Register(Actor actor, GrantConditionOnPrerequisite u, ImmutableArray prerequisites) { var key = MakeKey(prerequisites); if (!upgradables.TryGetValue(key, out var list)) @@ -54,7 +55,7 @@ namespace OpenRA.Mods.Common.Traits u.PrerequisitesUpdated(actor, techTree.HasPrerequisites(prerequisites)); } - public void Unregister(Actor actor, GrantConditionOnPrerequisite u, string[] prerequisites) + public void Unregister(Actor actor, GrantConditionOnPrerequisite u, ImmutableArray prerequisites) { var key = MakeKey(prerequisites); var list = upgradables[key]; diff --git a/OpenRA.Mods.Common/Traits/Player/LobbyPrerequisiteCheckbox.cs b/OpenRA.Mods.Common/Traits/Player/LobbyPrerequisiteCheckbox.cs index 80811fce55..c4625f0c06 100644 --- a/OpenRA.Mods.Common/Traits/Player/LobbyPrerequisiteCheckbox.cs +++ b/OpenRA.Mods.Common/Traits/Player/LobbyPrerequisiteCheckbox.cs @@ -9,6 +9,7 @@ */ #endregion +using System.Collections.Frozen; using System.Collections.Generic; using OpenRA.Traits; @@ -45,7 +46,7 @@ namespace OpenRA.Mods.Common.Traits [FieldLoader.Require] [Desc("Prerequisites to grant when this checkbox is enabled.")] - public readonly HashSet Prerequisites = []; + public readonly FrozenSet Prerequisites = FrozenSet.Empty; IEnumerable ITechTreePrerequisiteInfo.Prerequisites(ActorInfo info) { return Prerequisites; } @@ -61,7 +62,7 @@ namespace OpenRA.Mods.Common.Traits public class LobbyPrerequisiteCheckbox : INotifyCreated, ITechTreePrerequisite { readonly LobbyPrerequisiteCheckboxInfo info; - HashSet prerequisites = []; + FrozenSet prerequisites = FrozenSet.Empty; public LobbyPrerequisiteCheckbox(LobbyPrerequisiteCheckboxInfo info) { diff --git a/OpenRA.Mods.Common/Traits/Player/PlayerResources.cs b/OpenRA.Mods.Common/Traits/Player/PlayerResources.cs index d1632485ea..8087eb12d1 100644 --- a/OpenRA.Mods.Common/Traits/Player/PlayerResources.cs +++ b/OpenRA.Mods.Common/Traits/Player/PlayerResources.cs @@ -10,7 +10,9 @@ #endregion using System; +using System.Collections.Frozen; using System.Collections.Generic; +using System.Collections.Immutable; using System.Globalization; using System.Linq; using OpenRA.Traits; @@ -27,7 +29,7 @@ namespace OpenRA.Mods.Common.Traits public readonly string DefaultCashDropdownDescription = "The amount of cash that players start with"; [Desc("Starting cash options that are available in the lobby options.")] - public readonly int[] SelectableCash = [2500, 5000, 10000, 20000]; + public readonly ImmutableArray SelectableCash = [2500, 5000, 10000, 20000]; [Desc("Default starting cash option: should be one of the SelectableCash options.")] public readonly int DefaultCash = 5000; @@ -59,7 +61,7 @@ namespace OpenRA.Mods.Common.Traits public readonly string CashTickDownNotification = null; [Desc("Monetary value of each resource type.", "Dictionary of [resource type]: [value per unit].")] - public readonly Dictionary ResourceValues = []; + public readonly FrozenDictionary ResourceValues = FrozenDictionary.Empty; IEnumerable ILobbyOptions.LobbyOptions(MapPreview map) { diff --git a/OpenRA.Mods.Common/Traits/Player/ProductionQueue.cs b/OpenRA.Mods.Common/Traits/Player/ProductionQueue.cs index 1e87c4d17c..18f4ea917a 100644 --- a/OpenRA.Mods.Common/Traits/Player/ProductionQueue.cs +++ b/OpenRA.Mods.Common/Traits/Player/ProductionQueue.cs @@ -10,6 +10,7 @@ #endregion using System; +using System.Collections.Frozen; using System.Collections.Generic; using System.Linq; using OpenRA.Primitives; @@ -33,7 +34,7 @@ namespace OpenRA.Mods.Common.Traits public readonly string Group = null; [Desc("Only enable this queue for certain factions.")] - public readonly HashSet Factions = []; + public readonly FrozenSet Factions = FrozenSet.Empty; [Desc("Should the prerequisite remain enabled if the owner changes?")] public readonly bool Sticky = true; diff --git a/OpenRA.Mods.Common/Traits/Player/ProvidesPrerequisite.cs b/OpenRA.Mods.Common/Traits/Player/ProvidesPrerequisite.cs index d39fff42c7..f0a9211488 100644 --- a/OpenRA.Mods.Common/Traits/Player/ProvidesPrerequisite.cs +++ b/OpenRA.Mods.Common/Traits/Player/ProvidesPrerequisite.cs @@ -9,7 +9,9 @@ */ #endregion +using System.Collections.Frozen; using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using OpenRA.Traits; @@ -21,10 +23,10 @@ namespace OpenRA.Mods.Common.Traits public readonly string Prerequisite = null; [Desc("Only grant this prerequisite when you have these prerequisites.")] - public readonly string[] RequiresPrerequisites = []; + public readonly ImmutableArray RequiresPrerequisites = []; [Desc("Only grant this prerequisite for certain factions.")] - public readonly HashSet Factions = []; + public readonly FrozenSet Factions = FrozenSet.Empty; [Desc("Should it recheck everything when it is captured?")] public readonly bool ResetOnOwnerChange = false; diff --git a/OpenRA.Mods.Common/Traits/Player/ProvidesTechPrerequisite.cs b/OpenRA.Mods.Common/Traits/Player/ProvidesTechPrerequisite.cs index f363f344f2..427750a82b 100644 --- a/OpenRA.Mods.Common/Traits/Player/ProvidesTechPrerequisite.cs +++ b/OpenRA.Mods.Common/Traits/Player/ProvidesTechPrerequisite.cs @@ -10,6 +10,7 @@ #endregion using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using OpenRA.Traits; @@ -26,7 +27,7 @@ namespace OpenRA.Mods.Common.Traits public readonly string Name; [Desc("Prerequisites to grant when this tech level is active.")] - public readonly string[] Prerequisites = []; + public readonly ImmutableArray Prerequisites = []; IEnumerable ITechTreePrerequisiteInfo.Prerequisites(ActorInfo info) { return Prerequisites; } diff --git a/OpenRA.Mods.Common/Traits/Player/TechTree.cs b/OpenRA.Mods.Common/Traits/Player/TechTree.cs index 1489b56a95..18b05fec3c 100644 --- a/OpenRA.Mods.Common/Traits/Player/TechTree.cs +++ b/OpenRA.Mods.Common/Traits/Player/TechTree.cs @@ -10,6 +10,7 @@ #endregion using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using OpenRA.Traits; @@ -47,7 +48,7 @@ namespace OpenRA.Mods.Common.Traits w.Update(ownedPrerequisites); } - public void Add(string key, string[] prerequisites, int limit, ITechTreeElement tte) + public void Add(string key, ImmutableArray prerequisites, int limit, ITechTreeElement tte) { watchers.Add(new Watcher(key, prerequisites, limit, tte)); } @@ -119,13 +120,13 @@ namespace OpenRA.Mods.Common.Traits public ITechTreeElement RegisteredBy { get; } // Strings may be either actor type, or "alternate name" key - readonly string[] prerequisites; + readonly ImmutableArray prerequisites; bool hasPrerequisites; readonly int limit; bool hidden; bool initialized = false; - public Watcher(string key, string[] prerequisites, int limit, ITechTreeElement watcher) + public Watcher(string key, ImmutableArray prerequisites, int limit, ITechTreeElement watcher) { Key = key; this.prerequisites = prerequisites; diff --git a/OpenRA.Mods.Common/Traits/Pluggable.cs b/OpenRA.Mods.Common/Traits/Pluggable.cs index 7b9d30ebcd..9645304b5c 100644 --- a/OpenRA.Mods.Common/Traits/Pluggable.cs +++ b/OpenRA.Mods.Common/Traits/Pluggable.cs @@ -9,6 +9,7 @@ */ #endregion +using System.Collections.Frozen; using System.Collections.Generic; using System.Linq; using OpenRA.Support; @@ -25,17 +26,17 @@ namespace OpenRA.Mods.Common.Traits [Desc("Conditions to grant for each accepted plug type.", "Key is the plug type.", "Value is the condition that is granted when the plug is enabled.")] - public readonly Dictionary Conditions = null; + public readonly FrozenDictionary Conditions = null; [Desc("Requirements for accepting a plug type.", "Key is the plug type that the requirements applies to.", "Value is the condition expression defining the requirements to place the plug.")] - public readonly Dictionary Requirements = []; + public readonly FrozenDictionary Requirements = FrozenDictionary.Empty; [Desc("Options to display in the map editor.", "Key is the plug type that the requirements applies to.", "Value is the label that is displayed in the actor editor dropdown.")] - public readonly Dictionary EditorOptions = []; + public readonly FrozenDictionary EditorOptions = FrozenDictionary.Empty; [Desc("Label to use for an empty plug socket.")] public readonly string EmptyOption = "Empty"; @@ -58,8 +59,9 @@ namespace OpenRA.Mods.Common.Traits yield break; // Make sure the no-plug option is always available - EditorOptions[""] = EmptyOption; - yield return new EditorActorDropdown("Plug", EditorDisplayOrder, _ => EditorOptions, + var editorOptions = EditorOptions.ToDictionary(); + editorOptions[""] = EmptyOption; + yield return new EditorActorDropdown("Plug", EditorDisplayOrder, _ => editorOptions, (actor, _) => { var init = actor.GetInitOrDefault(this); diff --git a/OpenRA.Mods.Common/Traits/ProducibleWithLevel.cs b/OpenRA.Mods.Common/Traits/ProducibleWithLevel.cs index c7b5dc549c..c9dc79b300 100644 --- a/OpenRA.Mods.Common/Traits/ProducibleWithLevel.cs +++ b/OpenRA.Mods.Common/Traits/ProducibleWithLevel.cs @@ -9,6 +9,7 @@ */ #endregion +using System.Collections.Immutable; using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits @@ -17,7 +18,7 @@ namespace OpenRA.Mods.Common.Traits "this trait grants a level-up to newly spawned actors.")] public class ProducibleWithLevelInfo : TraitInfo, Requires { - public readonly string[] Prerequisites = []; + public readonly ImmutableArray Prerequisites = []; [Desc("Number of levels to give to the actor on creation.")] public readonly int InitialLevels = 1; diff --git a/OpenRA.Mods.Common/Traits/Production.cs b/OpenRA.Mods.Common/Traits/Production.cs index 2f9119e55f..b6e8c9cc57 100644 --- a/OpenRA.Mods.Common/Traits/Production.cs +++ b/OpenRA.Mods.Common/Traits/Production.cs @@ -11,6 +11,7 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using OpenRA.Primitives; using OpenRA.Traits; @@ -21,7 +22,7 @@ namespace OpenRA.Mods.Common.Traits { [FieldLoader.Require] [Desc("e.g. Infantry, Vehicles, Aircraft, Buildings")] - public readonly string[] Produces = []; + public readonly ImmutableArray Produces = []; [Desc("When owner is changed, should the Faction be updated to the new owner's faction?")] public readonly bool UpdateFactionOnOwnerChange = false; diff --git a/OpenRA.Mods.Common/Traits/Rearmable.cs b/OpenRA.Mods.Common/Traits/Rearmable.cs index d85aafc242..f976d6dc22 100644 --- a/OpenRA.Mods.Common/Traits/Rearmable.cs +++ b/OpenRA.Mods.Common/Traits/Rearmable.cs @@ -9,6 +9,7 @@ */ #endregion +using System.Collections.Frozen; using System.Collections.Generic; using System.Linq; using OpenRA.Traits; @@ -20,10 +21,10 @@ namespace OpenRA.Mods.Common.Traits [ActorReference] [FieldLoader.Require] [Desc("Actors that this actor can dock to and get rearmed by.")] - public readonly HashSet RearmActors = []; + public readonly FrozenSet RearmActors = FrozenSet.Empty; [Desc("Name(s) of AmmoPool(s) that use this trait to rearm.")] - public readonly HashSet AmmoPools = ["primary"]; + public readonly FrozenSet AmmoPools = new HashSet { "primary" }.ToFrozenSet(); public override object Create(ActorInitializer init) { return new Rearmable(this); } } diff --git a/OpenRA.Mods.Common/Traits/RegionProximityCapturable.cs b/OpenRA.Mods.Common/Traits/RegionProximityCapturable.cs index bf041d761d..afe708946b 100644 --- a/OpenRA.Mods.Common/Traits/RegionProximityCapturable.cs +++ b/OpenRA.Mods.Common/Traits/RegionProximityCapturable.cs @@ -9,6 +9,7 @@ */ #endregion +using System.Collections.Immutable; using System.Linq; using OpenRA.Graphics; using OpenRA.Mods.Common.Graphics; @@ -22,20 +23,20 @@ namespace OpenRA.Mods.Common.Traits [Desc("Set of cell offsets (relative to the actor's Location) the " + nameof(ProximityCaptor) + " needs to be in to initiate the capture. ", "A 'Region' ActorInit can be used to override this value per actor. If either is empty or non-existent, ", "the immediately neighboring cells of the actor will be used.")] - public readonly CVec[] Region = []; + public readonly ImmutableArray Region = []; public override object Create(ActorInitializer init) { return new RegionProximityCapturable(init, this); } } public class RegionProximityCapturable : ProximityCapturableBase { - readonly CVec[] offsets; + readonly ImmutableArray offsets; CPos[] region; public RegionProximityCapturable(ActorInitializer init, RegionProximityCapturableInfo info) : base(init, info) { - offsets = init.GetValue(info, info.Region); + offsets = init.GetValue>(info, info.Region); } protected override int CreateTrigger(Actor self) @@ -61,9 +62,9 @@ namespace OpenRA.Mods.Common.Traits } } - public class RegionInit : ValueActorInit + public class RegionInit : ValueActorInit> { - public RegionInit(CVec[] value) + public RegionInit(ImmutableArray value) : base(value) { } } } diff --git a/OpenRA.Mods.Common/Traits/RejectsOrders.cs b/OpenRA.Mods.Common/Traits/RejectsOrders.cs index 8dc34bd9a8..2772c6477c 100644 --- a/OpenRA.Mods.Common/Traits/RejectsOrders.cs +++ b/OpenRA.Mods.Common/Traits/RejectsOrders.cs @@ -9,7 +9,7 @@ */ #endregion -using System.Collections.Generic; +using System.Collections.Frozen; using System.Linq; namespace OpenRA.Mods.Common.Traits @@ -18,11 +18,11 @@ namespace OpenRA.Mods.Common.Traits public class RejectsOrdersInfo : ConditionalTraitInfo { [Desc("Explicit list of rejected orders. Leave empty to reject all minus those listed under Except.")] - public readonly HashSet Reject = []; + public readonly FrozenSet Reject = FrozenSet.Empty; [Desc("List of orders that should *not* be rejected.", "Also overrides other instances of this trait's Reject fields.")] - public readonly HashSet Except = []; + public readonly FrozenSet Except = FrozenSet.Empty; [Desc("Remove current and all queued orders from the actor when this trait is enabled.")] public readonly bool RemoveOrders = false; @@ -32,8 +32,8 @@ namespace OpenRA.Mods.Common.Traits public class RejectsOrders : ConditionalTrait { - public HashSet Reject => Info.Reject; - public HashSet Except => Info.Except; + public FrozenSet Reject => Info.Reject; + public FrozenSet Except => Info.Except; public RejectsOrders(RejectsOrdersInfo info) : base(info) { } diff --git a/OpenRA.Mods.Common/Traits/Render/FloatingSpriteEmitter.cs b/OpenRA.Mods.Common/Traits/Render/FloatingSpriteEmitter.cs index a12f2000a2..729f995f26 100644 --- a/OpenRA.Mods.Common/Traits/Render/FloatingSpriteEmitter.cs +++ b/OpenRA.Mods.Common/Traits/Render/FloatingSpriteEmitter.cs @@ -9,6 +9,7 @@ */ #endregion +using System.Collections.Immutable; using OpenRA.Mods.Common.Effects; using OpenRA.Traits; @@ -19,7 +20,7 @@ namespace OpenRA.Mods.Common.Traits { [FieldLoader.Require] [Desc("The time between individual particle creation. Two values mean actual lifetime will vary between them.")] - public readonly int[] Lifetime; + public readonly ImmutableArray Lifetime; [FieldLoader.Require] [Desc("The time in ticks until stop spawning. -1 means forever.")] @@ -29,13 +30,13 @@ namespace OpenRA.Mods.Common.Traits public readonly bool ResetOnDamaged = true; [Desc("Randomised offset for the particle emitter.")] - public readonly WVec[] Offset = [WVec.Zero]; + public readonly ImmutableArray Offset = [WVec.Zero]; [Desc("Randomized particle forward movement.")] - public readonly WDist[] Speed = [WDist.Zero]; + public readonly ImmutableArray Speed = [WDist.Zero]; [Desc("Randomized particle gravity.")] - public readonly WDist[] Gravity = [WDist.Zero]; + public readonly ImmutableArray Gravity = [WDist.Zero]; [Desc("Randomize particle facing.")] public readonly bool RandomFacing = true; @@ -47,14 +48,14 @@ namespace OpenRA.Mods.Common.Traits public readonly int RandomRate = 4; [Desc("How many particles should spawn. Two values for a random range.")] - public readonly int[] SpawnFrequency = [1]; + public readonly ImmutableArray SpawnFrequency = [1]; [Desc("Which image to use.")] public readonly string Image = "smoke"; [Desc("Which sequence to use.")] [SequenceReference(nameof(Image))] - public readonly string[] Sequences = ["particles"]; + public readonly ImmutableArray Sequences = ["particles"]; [Desc("Which palette to use.")] [PaletteReference(nameof(IsPlayerPalette))] diff --git a/OpenRA.Mods.Common/Traits/Render/LeavesTrails.cs b/OpenRA.Mods.Common/Traits/Render/LeavesTrails.cs index 526bdc3b18..62b781bb67 100644 --- a/OpenRA.Mods.Common/Traits/Render/LeavesTrails.cs +++ b/OpenRA.Mods.Common/Traits/Render/LeavesTrails.cs @@ -9,7 +9,8 @@ */ #endregion -using System.Collections.Generic; +using System.Collections.Frozen; +using System.Collections.Immutable; using OpenRA.Mods.Common.Effects; using OpenRA.Traits; @@ -24,13 +25,13 @@ namespace OpenRA.Mods.Common.Traits.Render public readonly string Image = null; [SequenceReference(nameof(Image))] - public readonly string[] Sequences = ["idle"]; + public readonly ImmutableArray Sequences = ["idle"]; [PaletteReference] public readonly string Palette = "effect"; [Desc("Only leave trail on listed terrain types. Leave empty to leave trail on all terrain types.")] - public readonly HashSet TerrainTypes = []; + public readonly FrozenSet TerrainTypes = FrozenSet.Empty; [Desc("Accepts values: Cell to draw the trail sprite in the center of the current cell,", "CenterPosition to draw the trail sprite at the current position.")] @@ -56,7 +57,7 @@ namespace OpenRA.Mods.Common.Traits.Render public readonly int StartDelay = 0; [Desc("Trail spawn positions relative to actor position. (forward, right, up) triples")] - public readonly WVec[] Offsets = [WVec.Zero]; + public readonly ImmutableArray Offsets = [WVec.Zero]; [Desc("Should the trail spawn relative to last position or current position?")] public readonly bool SpawnAtLastPosition = true; diff --git a/OpenRA.Mods.Common/Traits/Render/ReloadArmamentsBar.cs b/OpenRA.Mods.Common/Traits/Render/ReloadArmamentsBar.cs index 94f8f44e0e..44836f3506 100644 --- a/OpenRA.Mods.Common/Traits/Render/ReloadArmamentsBar.cs +++ b/OpenRA.Mods.Common/Traits/Render/ReloadArmamentsBar.cs @@ -10,6 +10,7 @@ #endregion using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using OpenRA.Primitives; using OpenRA.Traits; @@ -20,7 +21,7 @@ namespace OpenRA.Mods.Common.Traits.Render sealed class ReloadArmamentsBarInfo : TraitInfo { [Desc("Armament names")] - public readonly string[] Armaments = ["primary", "secondary"]; + public readonly ImmutableArray Armaments = ["primary", "secondary"]; public readonly Color Color = Color.Red; diff --git a/OpenRA.Mods.Common/Traits/Render/RenderSprites.cs b/OpenRA.Mods.Common/Traits/Render/RenderSprites.cs index f693f208b7..630ec96688 100644 --- a/OpenRA.Mods.Common/Traits/Render/RenderSprites.cs +++ b/OpenRA.Mods.Common/Traits/Render/RenderSprites.cs @@ -10,6 +10,7 @@ #endregion using System; +using System.Collections.Frozen; using System.Collections.Generic; using System.Linq; using OpenRA.Graphics; @@ -31,7 +32,7 @@ namespace OpenRA.Mods.Common.Traits.Render public readonly string Image = null; [Desc("A dictionary of faction-specific image overrides.")] - public readonly Dictionary FactionImages = null; + public readonly FrozenDictionary FactionImages = null; [PaletteReference] [Desc("Custom palette name")] diff --git a/OpenRA.Mods.Common/Traits/Render/WithAircraftLandingEffect.cs b/OpenRA.Mods.Common/Traits/Render/WithAircraftLandingEffect.cs index c872a1de23..151c507599 100644 --- a/OpenRA.Mods.Common/Traits/Render/WithAircraftLandingEffect.cs +++ b/OpenRA.Mods.Common/Traits/Render/WithAircraftLandingEffect.cs @@ -9,7 +9,8 @@ */ #endregion -using System.Collections.Generic; +using System.Collections.Frozen; +using System.Collections.Immutable; using OpenRA.Graphics; using OpenRA.Mods.Common.Effects; using OpenRA.Traits; @@ -23,7 +24,7 @@ namespace OpenRA.Mods.Common.Traits.Render public readonly string Image = null; [SequenceReference(nameof(Image))] - public readonly string[] Sequences = ["idle"]; + public readonly ImmutableArray Sequences = ["idle"]; [PaletteReference] public readonly string Palette = "effect"; @@ -35,7 +36,7 @@ namespace OpenRA.Mods.Common.Traits.Render public readonly WDist DistanceAboveTerrain = new(756); [Desc("Only play on these terrain types.")] - public readonly HashSet TerrainTypes = []; + public readonly FrozenSet TerrainTypes = FrozenSet.Empty; public override object Create(ActorInitializer init) { return new WithAircraftLandingEffect(this); } } diff --git a/OpenRA.Mods.Common/Traits/Render/WithAmmoPipsDecoration.cs b/OpenRA.Mods.Common/Traits/Render/WithAmmoPipsDecoration.cs index 59ea604f70..fd61892b92 100644 --- a/OpenRA.Mods.Common/Traits/Render/WithAmmoPipsDecoration.cs +++ b/OpenRA.Mods.Common/Traits/Render/WithAmmoPipsDecoration.cs @@ -9,8 +9,8 @@ */ #endregion -using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using OpenRA.Graphics; using OpenRA.Traits; @@ -40,7 +40,7 @@ namespace OpenRA.Mods.Common.Traits.Render public readonly string Palette = "chrome"; [Desc("Name(s) of AmmoPool(s) that use this decoration. Leave empty to include all pools.")] - public readonly string[] AmmoPools = []; + public readonly ImmutableArray AmmoPools = []; public override object Create(ActorInitializer init) { return new WithAmmoPipsDecoration(init.Self, this); } } diff --git a/OpenRA.Mods.Common/Traits/Render/WithBridgeSpriteBody.cs b/OpenRA.Mods.Common/Traits/Render/WithBridgeSpriteBody.cs index 6155c173e2..f17ea175ab 100644 --- a/OpenRA.Mods.Common/Traits/Render/WithBridgeSpriteBody.cs +++ b/OpenRA.Mods.Common/Traits/Render/WithBridgeSpriteBody.cs @@ -10,6 +10,7 @@ #endregion using System.Collections.Generic; +using System.Collections.Immutable; using OpenRA.Graphics; using OpenRA.Mods.Common.Graphics; using OpenRA.Traits; @@ -28,16 +29,16 @@ namespace OpenRA.Mods.Common.Traits.Render [SequenceReference] [Desc("Sequences to use when both neighbours are alive.")] - public readonly string[] Sequences = ["idle"]; + public readonly ImmutableArray Sequences = ["idle"]; [SequenceReference] - public readonly string[] ADestroyedSequences = ["adestroyed"]; + public readonly ImmutableArray ADestroyedSequences = ["adestroyed"]; [SequenceReference] - public readonly string[] BDestroyedSequences = ["bdestroyed"]; + public readonly ImmutableArray BDestroyedSequences = ["bdestroyed"]; [SequenceReference] - public readonly string[] ABDestroyedSequences = ["abdestroyed"]; + public readonly ImmutableArray ABDestroyedSequences = ["abdestroyed"]; public override object Create(ActorInitializer init) { return new WithBridgeSpriteBody(init, this); } diff --git a/OpenRA.Mods.Common/Traits/Render/WithCargoPipsDecoration.cs b/OpenRA.Mods.Common/Traits/Render/WithCargoPipsDecoration.cs index a55f388834..4c9ff61d91 100644 --- a/OpenRA.Mods.Common/Traits/Render/WithCargoPipsDecoration.cs +++ b/OpenRA.Mods.Common/Traits/Render/WithCargoPipsDecoration.cs @@ -9,6 +9,7 @@ */ #endregion +using System.Collections.Frozen; using System.Collections.Generic; using OpenRA.Graphics; using OpenRA.Traits; @@ -36,7 +37,7 @@ namespace OpenRA.Mods.Common.Traits.Render [SequenceReference(nameof(Image), dictionaryReference: LintDictionaryReference.Values)] [Desc("Pip sequence to use for specific passenger actors.")] - public readonly Dictionary CustomPipSequences = []; + public readonly FrozenDictionary CustomPipSequences = FrozenDictionary.Empty; [PaletteReference] public readonly string Palette = "chrome"; diff --git a/OpenRA.Mods.Common/Traits/Render/WithCrateBody.cs b/OpenRA.Mods.Common/Traits/Render/WithCrateBody.cs index 59787aae62..132c54a722 100644 --- a/OpenRA.Mods.Common/Traits/Render/WithCrateBody.cs +++ b/OpenRA.Mods.Common/Traits/Render/WithCrateBody.cs @@ -10,7 +10,9 @@ #endregion using System; +using System.Collections.Frozen; using System.Collections.Generic; +using System.Collections.Immutable; using OpenRA.Graphics; using OpenRA.Mods.Common.Graphics; using OpenRA.Traits; @@ -21,10 +23,10 @@ namespace OpenRA.Mods.Common.Traits.Render sealed class WithCrateBodyInfo : TraitInfo, Requires, IRenderActorPreviewSpritesInfo { [Desc("Easteregg sequences to use in December.")] - public readonly string[] XmasImages = []; + public readonly ImmutableArray XmasImages = []; [Desc("Terrain types on which to display WaterSequence.")] - public readonly HashSet WaterTerrainTypes = ["Water"]; + public readonly FrozenSet WaterTerrainTypes = new HashSet { "Water" }.ToFrozenSet(); [SequenceReference] public readonly string IdleSequence = "idle"; diff --git a/OpenRA.Mods.Common/Traits/Render/WithDamageOverlay.cs b/OpenRA.Mods.Common/Traits/Render/WithDamageOverlay.cs index 2739c88cac..62ea5a8818 100644 --- a/OpenRA.Mods.Common/Traits/Render/WithDamageOverlay.cs +++ b/OpenRA.Mods.Common/Traits/Render/WithDamageOverlay.cs @@ -9,6 +9,7 @@ */ #endregion +using System.Collections.Immutable; using OpenRA.Graphics; using OpenRA.Primitives; using OpenRA.Traits; @@ -34,11 +35,11 @@ namespace OpenRA.Mods.Common.Traits.Render [Desc("How many times should " + nameof(LoopSequence), " be played? A range can be provided to be randomly chosen from.")] - public readonly int[] LoopCount = [1, 3]; + public readonly ImmutableArray LoopCount = [1, 3]; [Desc("Initial delay before animation is enabled", "Two values indicate a random delay range.")] - public readonly int[] InitialDelay = [0]; + public readonly ImmutableArray InitialDelay = [0]; [PaletteReference(nameof(IsPlayerPalette))] [Desc("Custom palette name.")] diff --git a/OpenRA.Mods.Common/Traits/Render/WithDeadBridgeSpriteBody.cs b/OpenRA.Mods.Common/Traits/Render/WithDeadBridgeSpriteBody.cs index 8b9e70e3d4..b7d14e139f 100644 --- a/OpenRA.Mods.Common/Traits/Render/WithDeadBridgeSpriteBody.cs +++ b/OpenRA.Mods.Common/Traits/Render/WithDeadBridgeSpriteBody.cs @@ -9,9 +9,8 @@ */ #endregion -using System; using System.Collections.Generic; -using System.Linq; +using System.Collections.Immutable; using OpenRA.Graphics; using OpenRA.Mods.Common.Graphics; using OpenRA.Traits; @@ -21,7 +20,7 @@ namespace OpenRA.Mods.Common.Traits.Render sealed class WithDeadBridgeSpriteBodyInfo : WithSpriteBodyInfo { [ActorReference] - public readonly string[] RampActors = []; + public readonly ImmutableArray RampActors = []; [Desc("Offset to search for the 'A' neighbour")] public readonly CVec AOffset = CVec.Zero; @@ -30,13 +29,13 @@ namespace OpenRA.Mods.Common.Traits.Render public readonly CVec BOffset = CVec.Zero; [SequenceReference] - public readonly string[] ARampSequences = ["aramp"]; + public readonly ImmutableArray ARampSequences = ["aramp"]; [SequenceReference] - public readonly string[] BRampSequences = ["bramp"]; + public readonly ImmutableArray BRampSequences = ["bramp"]; [SequenceReference] - public readonly string[] ABRampSequences = ["abramp"]; + public readonly ImmutableArray ABRampSequences = ["abramp"]; [SequenceReference] [Desc("Placeholder sequence to use in the map editor.")] diff --git a/OpenRA.Mods.Common/Traits/Render/WithDeathAnimation.cs b/OpenRA.Mods.Common/Traits/Render/WithDeathAnimation.cs index f79ef11ff3..ce14e4d80d 100644 --- a/OpenRA.Mods.Common/Traits/Render/WithDeathAnimation.cs +++ b/OpenRA.Mods.Common/Traits/Render/WithDeathAnimation.cs @@ -9,7 +9,8 @@ */ #endregion -using System.Collections.Generic; +using System.Collections.Frozen; +using System.Collections.Immutable; using System.Linq; using OpenRA.Mods.Common.Effects; using OpenRA.Primitives; @@ -47,7 +48,7 @@ namespace OpenRA.Mods.Common.Traits.Render [Desc("Death animations to use for each damage type (defined on the warheads).", "Is only used if UseDeathTypeSuffix is `True`.")] - public readonly Dictionary DeathTypes = []; + public readonly FrozenDictionary> DeathTypes = FrozenDictionary>.Empty; [SequenceReference] [Desc("Sequence to use when the actor is killed by some non-standard means (e.g. suicide).")] diff --git a/OpenRA.Mods.Common/Traits/Render/WithDecorationBase.cs b/OpenRA.Mods.Common/Traits/Render/WithDecorationBase.cs index e7e755692f..7bf928016e 100644 --- a/OpenRA.Mods.Common/Traits/Render/WithDecorationBase.cs +++ b/OpenRA.Mods.Common/Traits/Render/WithDecorationBase.cs @@ -9,7 +9,9 @@ */ #endregion +using System.Collections.Frozen; using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using OpenRA.Graphics; using OpenRA.Support; @@ -35,17 +37,18 @@ namespace OpenRA.Mods.Common.Traits.Render [Desc("Screen-space offsets to apply when defined conditions are enabled.", "A dictionary of [condition string]: [x, y offset].")] - public readonly Dictionary Offsets = []; + public readonly FrozenDictionary Offsets = FrozenDictionary.Empty; [Desc("The number of ticks that each step in the blink pattern in active.")] public readonly int BlinkInterval = 5; [Desc("A pattern of ticks (BlinkInterval long) where the decoration is visible or hidden.")] - public readonly BlinkState[] BlinkPattern = []; + public readonly ImmutableArray BlinkPattern = []; [Desc("Override blink conditions to use when defined conditions are enabled.", "A dictionary of [condition string]: [pattern].")] - public readonly Dictionary BlinkPatterns = []; + public readonly FrozenDictionary> BlinkPatterns = + FrozenDictionary>.Empty; [ConsumedConditionReference] public IEnumerable ConsumedConditions @@ -58,7 +61,7 @@ namespace OpenRA.Mods.Common.Traits.Render { protected readonly Actor Self; int2 conditionalOffset; - BlinkState[] blinkPattern; + ImmutableArray blinkPattern; protected WithDecorationBase(Actor self, InfoType info) : base(info) diff --git a/OpenRA.Mods.Common/Traits/Render/WithGateSpriteBody.cs b/OpenRA.Mods.Common/Traits/Render/WithGateSpriteBody.cs index 98d936ae36..24c098c77e 100644 --- a/OpenRA.Mods.Common/Traits/Render/WithGateSpriteBody.cs +++ b/OpenRA.Mods.Common/Traits/Render/WithGateSpriteBody.cs @@ -11,6 +11,7 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using OpenRA.Graphics; using OpenRA.Mods.Common.Graphics; @@ -22,7 +23,7 @@ namespace OpenRA.Mods.Common.Traits.Render sealed class WithGateSpriteBodyInfo : WithSpriteBodyInfo, IWallConnectorInfo, Requires { [Desc("Cells (outside the gate footprint) that contain wall cells that can connect to the gate")] - public readonly CVec[] WallConnections = []; + public readonly ImmutableArray WallConnections = []; [Desc("Wall type for connections")] public readonly string Type = "wall"; diff --git a/OpenRA.Mods.Common/Traits/Render/WithIdleAnimation.cs b/OpenRA.Mods.Common/Traits/Render/WithIdleAnimation.cs index 84efee86df..4ee026fc94 100644 --- a/OpenRA.Mods.Common/Traits/Render/WithIdleAnimation.cs +++ b/OpenRA.Mods.Common/Traits/Render/WithIdleAnimation.cs @@ -9,6 +9,7 @@ */ #endregion +using System.Collections.Immutable; using System.Linq; using OpenRA.Traits; @@ -19,10 +20,10 @@ namespace OpenRA.Mods.Common.Traits.Render { [SequenceReference] [Desc("Sequence names to use.")] - public readonly string[] Sequences = ["active"]; + public readonly ImmutableArray Sequences = ["active"]; [Desc("The amount of time (in ticks) between animations. Two values indicate a range between which a random value is chosen.")] - public readonly int[] Interval = [750]; + public readonly ImmutableArray Interval = [750]; [Desc("Which sprite body to play the animation on.")] public readonly string Body = "body"; diff --git a/OpenRA.Mods.Common/Traits/Render/WithInfantryBody.cs b/OpenRA.Mods.Common/Traits/Render/WithInfantryBody.cs index 975a6a01d8..1851df4717 100644 --- a/OpenRA.Mods.Common/Traits/Render/WithInfantryBody.cs +++ b/OpenRA.Mods.Common/Traits/Render/WithInfantryBody.cs @@ -9,7 +9,9 @@ */ #endregion +using System.Collections.Frozen; using System.Collections.Generic; +using System.Collections.Immutable; using OpenRA.Graphics; using OpenRA.Mods.Common.Graphics; using OpenRA.Traits; @@ -31,13 +33,13 @@ namespace OpenRA.Mods.Common.Traits.Render [Desc("Attack sequence to use for each armament.", "A dictionary of [armament name]: [sequence name(s)].", "Multiple sequence names can be defined to specify per-burst animations.")] - public readonly Dictionary AttackSequences = []; + public readonly FrozenDictionary> AttackSequences = FrozenDictionary>.Empty; [SequenceReference] - public readonly string[] IdleSequences = []; + public readonly ImmutableArray IdleSequences = []; [SequenceReference] - public readonly string[] StandSequences = ["stand"]; + public readonly ImmutableArray StandSequences = ["stand"]; [PaletteReference(nameof(IsPlayerPalette))] [Desc("Custom palette name")] diff --git a/OpenRA.Mods.Common/Traits/Render/WithMakeAnimation.cs b/OpenRA.Mods.Common/Traits/Render/WithMakeAnimation.cs index aeee08c57d..7e38be824a 100644 --- a/OpenRA.Mods.Common/Traits/Render/WithMakeAnimation.cs +++ b/OpenRA.Mods.Common/Traits/Render/WithMakeAnimation.cs @@ -10,6 +10,7 @@ #endregion using System; +using System.Collections.Immutable; using System.Linq; using OpenRA.Activities; using OpenRA.Traits; @@ -28,7 +29,7 @@ namespace OpenRA.Mods.Common.Traits.Render public readonly string Condition = null; [Desc("Apply to sprite bodies with these names.")] - public readonly string[] BodyNames = ["body"]; + public readonly ImmutableArray BodyNames = ["body"]; public override object Create(ActorInitializer init) { return new WithMakeAnimation(init, this); } } diff --git a/OpenRA.Mods.Common/Traits/Render/WithProductionIconOverlay.cs b/OpenRA.Mods.Common/Traits/Render/WithProductionIconOverlay.cs index f73865216f..d36e5dcdde 100644 --- a/OpenRA.Mods.Common/Traits/Render/WithProductionIconOverlay.cs +++ b/OpenRA.Mods.Common/Traits/Render/WithProductionIconOverlay.cs @@ -9,7 +9,7 @@ */ #endregion -using System; +using System.Collections.Immutable; using System.Linq; using OpenRA.Traits; @@ -19,9 +19,9 @@ namespace OpenRA.Mods.Common.Traits.Render public class WithProductionIconOverlayInfo : TraitInfo, IRulesetLoaded { [FieldLoader.Require] - public readonly string[] Types = []; + public readonly ImmutableArray Types = []; - public readonly string[] Prerequisites = []; + public readonly ImmutableArray Prerequisites = []; public virtual void RulesetLoaded(Ruleset rules, ActorInfo ai) { diff --git a/OpenRA.Mods.Common/Traits/Render/WithProductionOverlay.cs b/OpenRA.Mods.Common/Traits/Render/WithProductionOverlay.cs index a8621fd53a..3fe3a709a2 100644 --- a/OpenRA.Mods.Common/Traits/Render/WithProductionOverlay.cs +++ b/OpenRA.Mods.Common/Traits/Render/WithProductionOverlay.cs @@ -9,7 +9,7 @@ */ #endregion -using System.Collections.Generic; +using System.Collections.Frozen; using System.Linq; using OpenRA.Graphics; using OpenRA.Traits; @@ -21,7 +21,7 @@ namespace OpenRA.Mods.Common.Traits.Render public class WithProductionOverlayInfo : PausableConditionalTraitInfo, Requires, Requires, Requires { [Desc("Queues that should be producing for this overlay to render.")] - public readonly HashSet Queues = []; + public readonly FrozenSet Queues = FrozenSet.Empty; [SequenceReference] [Desc("Sequence name to use")] diff --git a/OpenRA.Mods.Common/Traits/Render/WithStoresResourcesPipsDecoration.cs b/OpenRA.Mods.Common/Traits/Render/WithStoresResourcesPipsDecoration.cs index 63bfd0a21a..8efae30a51 100644 --- a/OpenRA.Mods.Common/Traits/Render/WithStoresResourcesPipsDecoration.cs +++ b/OpenRA.Mods.Common/Traits/Render/WithStoresResourcesPipsDecoration.cs @@ -9,6 +9,7 @@ */ #endregion +using System.Collections.Frozen; using System.Collections.Generic; using System.Linq; using OpenRA.Graphics; @@ -38,7 +39,7 @@ namespace OpenRA.Mods.Common.Traits.Render [SequenceReference(nameof(Image), dictionaryReference: LintDictionaryReference.Values)] [Desc("Pip sequence to use for specific resource types.")] - public readonly Dictionary ResourceSequences = []; + public readonly FrozenDictionary ResourceSequences = FrozenDictionary.Empty; [PaletteReference] public readonly string Palette = "chrome"; diff --git a/OpenRA.Mods.Common/Traits/Repairable.cs b/OpenRA.Mods.Common/Traits/Repairable.cs index 37e40e495c..b28e43ef7f 100644 --- a/OpenRA.Mods.Common/Traits/Repairable.cs +++ b/OpenRA.Mods.Common/Traits/Repairable.cs @@ -9,6 +9,7 @@ */ #endregion +using System.Collections.Frozen; using System.Collections.Generic; using System.Linq; using OpenRA.Mods.Common.Activities; @@ -23,7 +24,7 @@ namespace OpenRA.Mods.Common.Traits { [ActorReference] [FieldLoader.Require] - public readonly HashSet RepairActors = []; + public readonly FrozenSet RepairActors = FrozenSet.Empty; [VoiceReference] public readonly string Voice = "Action"; diff --git a/OpenRA.Mods.Common/Traits/RepairableNear.cs b/OpenRA.Mods.Common/Traits/RepairableNear.cs index c75f5a7678..cdb86e6e72 100644 --- a/OpenRA.Mods.Common/Traits/RepairableNear.cs +++ b/OpenRA.Mods.Common/Traits/RepairableNear.cs @@ -9,6 +9,7 @@ */ #endregion +using System.Collections.Frozen; using System.Collections.Generic; using System.Linq; using OpenRA.Mods.Common.Activities; @@ -22,7 +23,7 @@ namespace OpenRA.Mods.Common.Traits { [ActorReference] [FieldLoader.Require] - public readonly HashSet RepairActors = []; + public readonly FrozenSet RepairActors = FrozenSet.Empty; public readonly WDist CloseEnough = WDist.FromCells(4); diff --git a/OpenRA.Mods.Common/Traits/Replaceable.cs b/OpenRA.Mods.Common/Traits/Replaceable.cs index 22147c604f..a2c333d771 100644 --- a/OpenRA.Mods.Common/Traits/Replaceable.cs +++ b/OpenRA.Mods.Common/Traits/Replaceable.cs @@ -9,7 +9,7 @@ */ #endregion -using System.Collections.Generic; +using System.Collections.Frozen; namespace OpenRA.Mods.Common.Traits { @@ -17,7 +17,7 @@ namespace OpenRA.Mods.Common.Traits { [FieldLoader.Require] [Desc("Replacement types this Replaceable actor accepts.")] - public readonly HashSet Types = []; + public readonly FrozenSet Types = FrozenSet.Empty; public override object Create(ActorInitializer init) { return new Replaceable(this); } } diff --git a/OpenRA.Mods.Common/Traits/Replacement.cs b/OpenRA.Mods.Common/Traits/Replacement.cs index 82e939ccc5..4758d3cb7e 100644 --- a/OpenRA.Mods.Common/Traits/Replacement.cs +++ b/OpenRA.Mods.Common/Traits/Replacement.cs @@ -9,7 +9,7 @@ */ #endregion -using System.Collections.Generic; +using System.Collections.Frozen; using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits @@ -18,7 +18,7 @@ namespace OpenRA.Mods.Common.Traits { [FieldLoader.Require] [Desc("Replacement type (matched against Types in Replaceable).")] - public readonly HashSet ReplaceableTypes = []; + public readonly FrozenSet ReplaceableTypes = FrozenSet.Empty; } public class Replacement { } diff --git a/OpenRA.Mods.Common/Traits/RequiresSpecificOwners.cs b/OpenRA.Mods.Common/Traits/RequiresSpecificOwners.cs index 41ed1174f3..2d5d7dbe1c 100644 --- a/OpenRA.Mods.Common/Traits/RequiresSpecificOwners.cs +++ b/OpenRA.Mods.Common/Traits/RequiresSpecificOwners.cs @@ -9,7 +9,7 @@ */ #endregion -using System.Collections.Generic; +using System.Collections.Frozen; using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits @@ -19,7 +19,7 @@ namespace OpenRA.Mods.Common.Traits { [Desc("Only allow players listed here as owners.")] [FieldLoader.Require] - public readonly HashSet ValidOwnerNames = []; + public readonly FrozenSet ValidOwnerNames = FrozenSet.Empty; } public class RequiresSpecificOwners { } diff --git a/OpenRA.Mods.Common/Traits/RevealOnFire.cs b/OpenRA.Mods.Common/Traits/RevealOnFire.cs index 1c3ee94f2e..def66d573b 100644 --- a/OpenRA.Mods.Common/Traits/RevealOnFire.cs +++ b/OpenRA.Mods.Common/Traits/RevealOnFire.cs @@ -9,7 +9,7 @@ */ #endregion -using System.Linq; +using System.Collections.Immutable; using OpenRA.Mods.Common.Effects; using OpenRA.Traits; @@ -19,7 +19,7 @@ namespace OpenRA.Mods.Common.Traits public class RevealOnFireInfo : ConditionalTraitInfo { [Desc("The armament types which trigger revealing.")] - public readonly string[] ArmamentNames = ["primary", "secondary"]; + public readonly ImmutableArray ArmamentNames = ["primary", "secondary"]; [Desc("Player relationships relative to the target player this actor will be revealed to during firing.")] public readonly PlayerRelationship RevealForRelationships = PlayerRelationship.Ally; diff --git a/OpenRA.Mods.Common/Traits/Sellable.cs b/OpenRA.Mods.Common/Traits/Sellable.cs index d96322e61e..51b1ee1c1c 100644 --- a/OpenRA.Mods.Common/Traits/Sellable.cs +++ b/OpenRA.Mods.Common/Traits/Sellable.cs @@ -10,6 +10,7 @@ #endregion using System; +using System.Collections.Immutable; using OpenRA.Mods.Common.Activities; using OpenRA.Mods.Common.Orders; using OpenRA.Mods.Common.Traits.Render; @@ -24,7 +25,7 @@ namespace OpenRA.Mods.Common.Traits public readonly int RefundPercent = 50; [Desc("List of audio clips to play when the actor is being sold.")] - public readonly string[] SellSounds = []; + public readonly ImmutableArray SellSounds = []; [NotificationReference("Speech")] [Desc("Speech notification to play.")] diff --git a/OpenRA.Mods.Common/Traits/Sound/AmbientSound.cs b/OpenRA.Mods.Common/Traits/Sound/AmbientSound.cs index 904a93fa4d..84db563090 100644 --- a/OpenRA.Mods.Common/Traits/Sound/AmbientSound.cs +++ b/OpenRA.Mods.Common/Traits/Sound/AmbientSound.cs @@ -10,6 +10,7 @@ #endregion using System.Collections.Generic; +using System.Collections.Immutable; using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits.Sound @@ -18,15 +19,15 @@ namespace OpenRA.Mods.Common.Traits.Sound sealed class AmbientSoundInfo : ConditionalTraitInfo { [FieldLoader.Require] - public readonly string[] SoundFiles = null; + public readonly ImmutableArray SoundFiles = default; [Desc("Initial delay (in ticks) before playing the sound for the first time.", "Two values indicate a random delay range.")] - public readonly int[] Delay = [0]; + public readonly ImmutableArray Delay = [0]; [Desc("Interval between playing the sound (in ticks).", "Two values indicate a random delay range.")] - public readonly int[] Interval = [0]; + public readonly ImmutableArray Interval = [0]; public override object Create(ActorInitializer init) { return new AmbientSound(init.Self, this); } } diff --git a/OpenRA.Mods.Common/Traits/Sound/AttackSounds.cs b/OpenRA.Mods.Common/Traits/Sound/AttackSounds.cs index ca190bb58d..8506a9081b 100644 --- a/OpenRA.Mods.Common/Traits/Sound/AttackSounds.cs +++ b/OpenRA.Mods.Common/Traits/Sound/AttackSounds.cs @@ -9,6 +9,7 @@ */ #endregion +using System.Collections.Immutable; using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits.Sound @@ -17,7 +18,7 @@ namespace OpenRA.Mods.Common.Traits.Sound public class AttackSoundsInfo : ConditionalTraitInfo { [Desc("Play a randomly selected sound from this list when preparing for an attack or attacking.")] - public readonly string[] Sounds = []; + public readonly ImmutableArray Sounds = []; [Desc("Delay in ticks before sound starts, either relative to attack preparation or attack.")] public readonly int Delay = 0; diff --git a/OpenRA.Mods.Common/Traits/Sound/SoundOnDamageTransition.cs b/OpenRA.Mods.Common/Traits/Sound/SoundOnDamageTransition.cs index 131119b883..cea06460aa 100644 --- a/OpenRA.Mods.Common/Traits/Sound/SoundOnDamageTransition.cs +++ b/OpenRA.Mods.Common/Traits/Sound/SoundOnDamageTransition.cs @@ -9,6 +9,7 @@ */ #endregion +using System.Collections.Immutable; using OpenRA.Primitives; using OpenRA.Traits; @@ -17,10 +18,10 @@ namespace OpenRA.Mods.Common.Traits.Sound public class SoundOnDamageTransitionInfo : TraitInfo { [Desc("Play a random sound from this list when damaged.")] - public readonly string[] DamagedSounds = []; + public readonly ImmutableArray DamagedSounds = []; [Desc("Play a random sound from this list when destroyed.")] - public readonly string[] DestroyedSounds = []; + public readonly ImmutableArray DestroyedSounds = []; [Desc("DamageType(s) that trigger the sounds. Leave empty to always trigger a sound.")] public readonly BitSet DamageTypes = default; diff --git a/OpenRA.Mods.Common/Traits/SpawnActorsOnSell.cs b/OpenRA.Mods.Common/Traits/SpawnActorsOnSell.cs index e4d02c87f8..888be85d87 100644 --- a/OpenRA.Mods.Common/Traits/SpawnActorsOnSell.cs +++ b/OpenRA.Mods.Common/Traits/SpawnActorsOnSell.cs @@ -9,7 +9,8 @@ */ #endregion -using System.Collections.Generic; +using System.Collections.Frozen; +using System.Collections.Immutable; using System.Linq; using OpenRA.Traits; @@ -24,15 +25,15 @@ namespace OpenRA.Mods.Common.Traits [ActorReference] [FieldLoader.Require] [Desc("Actor types to spawn on sell, amount and type based on ValuePercent. Be sure to use lowercase.")] - public readonly string[] ActorTypes = null; + public readonly ImmutableArray ActorTypes = default; [ActorReference] [Desc("Actors to spawn on sell. Be sure to use lowercase.")] - public readonly string[] GuaranteedActorTypes = []; + public readonly ImmutableArray GuaranteedActorTypes = []; [Desc("Spawns actors only if the selling player's faction is in this list. " + "Leave empty to allow all factions by default.")] - public readonly HashSet Factions = []; + public readonly FrozenSet Factions = FrozenSet.Empty; public override object Create(ActorInitializer init) { return new SpawnActorsOnSell(init.Self, this); } } diff --git a/OpenRA.Mods.Common/Traits/StoresResources.cs b/OpenRA.Mods.Common/Traits/StoresResources.cs index 16f4069ae3..a724f269a1 100644 --- a/OpenRA.Mods.Common/Traits/StoresResources.cs +++ b/OpenRA.Mods.Common/Traits/StoresResources.cs @@ -9,10 +9,9 @@ */ #endregion -using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Collections.ObjectModel; -using System.Linq; using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits @@ -25,9 +24,9 @@ namespace OpenRA.Mods.Common.Traits public readonly int Capacity = 28; [Desc("Which resources can be stored.")] - public readonly string[] Resources = []; + public readonly ImmutableArray Resources = []; - string[] IStoresResourcesInfo.ResourceTypes => Resources; + ImmutableArray IStoresResourcesInfo.ResourceTypes => Resources; public override object Create(ActorInitializer init) { return new StoresResources(init.Self, this); } } diff --git a/OpenRA.Mods.Common/Traits/SupportPowers/DirectionalSupportPower.cs b/OpenRA.Mods.Common/Traits/SupportPowers/DirectionalSupportPower.cs index 58ea95c58c..c25ac27b8d 100644 --- a/OpenRA.Mods.Common/Traits/SupportPowers/DirectionalSupportPower.cs +++ b/OpenRA.Mods.Common/Traits/SupportPowers/DirectionalSupportPower.cs @@ -9,6 +9,7 @@ */ #endregion +using System.Collections.Immutable; using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits @@ -19,7 +20,7 @@ namespace OpenRA.Mods.Common.Traits public readonly bool UseDirectionalTarget = false; [SequenceReference(nameof(DirectionArrowAnimation), allowNullImage: true)] - public readonly string[] Arrows = ["arrow-t", "arrow-tl", "arrow-l", "arrow-bl", "arrow-b", "arrow-br", "arrow-r", "arrow-tr"]; + public readonly ImmutableArray Arrows = ["arrow-t", "arrow-tl", "arrow-l", "arrow-bl", "arrow-b", "arrow-br", "arrow-r", "arrow-tr"]; [Desc("Animation used to render the direction arrows.")] public readonly string DirectionArrowAnimation = null; diff --git a/OpenRA.Mods.Common/Traits/SupportPowers/NukePower.cs b/OpenRA.Mods.Common/Traits/SupportPowers/NukePower.cs index e25126d98f..3245bea728 100644 --- a/OpenRA.Mods.Common/Traits/SupportPowers/NukePower.cs +++ b/OpenRA.Mods.Common/Traits/SupportPowers/NukePower.cs @@ -10,6 +10,7 @@ #endregion using System.Collections.Generic; +using System.Collections.Immutable; using OpenRA.GameRules; using OpenRA.Graphics; using OpenRA.Mods.Common.Effects; @@ -62,7 +63,7 @@ namespace OpenRA.Mods.Common.Traits [SequenceReference(nameof(TrailImage), allowNullImage: true)] [Desc("Loop a randomly chosen sequence of TrailImage from this list while this projectile is moving.")] - public readonly string[] TrailSequences = []; + public readonly ImmutableArray TrailSequences = []; [Desc("Interval in ticks between each spawned Trail animation.")] public readonly int TrailInterval = 1; @@ -117,7 +118,7 @@ namespace OpenRA.Mods.Common.Traits public readonly float CircleBorderWidth = 3; [Desc("Render circles based on these distance ranges while targeting.")] - public readonly WDist[] CircleRanges = null; + public readonly ImmutableArray CircleRanges = default; public WeaponInfo WeaponInfo { get; private set; } diff --git a/OpenRA.Mods.Common/Traits/SupportPowers/ParatroopersPower.cs b/OpenRA.Mods.Common/Traits/SupportPowers/ParatroopersPower.cs index d9b7aacea7..616d5c66b7 100644 --- a/OpenRA.Mods.Common/Traits/SupportPowers/ParatroopersPower.cs +++ b/OpenRA.Mods.Common/Traits/SupportPowers/ParatroopersPower.cs @@ -11,6 +11,7 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using OpenRA.Mods.Common.Activities; using OpenRA.Mods.Common.Effects; @@ -47,7 +48,7 @@ namespace OpenRA.Mods.Common.Traits [ActorReference(typeof(PassengerInfo))] [Desc("Troops to be delivered. They will be distributed between the planes if SquadSize > 1.")] - public readonly string[] DropItems = []; + public readonly ImmutableArray DropItems = []; [Desc("Risks stuck units when they don't have the Paratrooper trait.")] public readonly bool AllowImpassableCells = false; diff --git a/OpenRA.Mods.Common/Traits/SupportPowers/ProduceActorPower.cs b/OpenRA.Mods.Common/Traits/SupportPowers/ProduceActorPower.cs index 8ea63b9bc0..00df725a65 100644 --- a/OpenRA.Mods.Common/Traits/SupportPowers/ProduceActorPower.cs +++ b/OpenRA.Mods.Common/Traits/SupportPowers/ProduceActorPower.cs @@ -9,6 +9,7 @@ */ #endregion +using System.Collections.Immutable; using System.Linq; using OpenRA.Primitives; using OpenRA.Traits; @@ -21,7 +22,7 @@ namespace OpenRA.Mods.Common.Traits [ActorReference] [FieldLoader.Require] [Desc("Actors to produce.")] - public readonly string[] Actors = null; + public readonly ImmutableArray Actors = default; [FieldLoader.Require] [Desc("Production queue type to use")] diff --git a/OpenRA.Mods.Common/Traits/SupportPowers/SpawnActorPower.cs b/OpenRA.Mods.Common/Traits/SupportPowers/SpawnActorPower.cs index 535e68d7fe..4cce7db477 100644 --- a/OpenRA.Mods.Common/Traits/SupportPowers/SpawnActorPower.cs +++ b/OpenRA.Mods.Common/Traits/SupportPowers/SpawnActorPower.cs @@ -10,7 +10,7 @@ #endregion using System.Collections.Generic; -using System.Linq; +using System.Collections.Immutable; using OpenRA.Graphics; using OpenRA.Mods.Common.Activities; using OpenRA.Mods.Common.Effects; @@ -31,7 +31,7 @@ namespace OpenRA.Mods.Common.Traits public readonly int LifeTime = 250; [Desc("Only allow this to be spawned on this terrain.")] - public readonly string[] Terrain = null; + public readonly ImmutableArray Terrain = default; public readonly bool AllowUnderShroud = true; diff --git a/OpenRA.Mods.Common/Traits/SupportPowers/SupportPower.cs b/OpenRA.Mods.Common/Traits/SupportPowers/SupportPower.cs index 9519127b48..d16d5d8b65 100644 --- a/OpenRA.Mods.Common/Traits/SupportPowers/SupportPower.cs +++ b/OpenRA.Mods.Common/Traits/SupportPowers/SupportPower.cs @@ -10,6 +10,7 @@ #endregion using System.Collections.Generic; +using System.Collections.Immutable; using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits @@ -53,7 +54,7 @@ namespace OpenRA.Mods.Common.Traits "Normal rules apply for subsequent charges.")] public readonly bool StartFullyCharged = false; - public readonly string[] Prerequisites = []; + public readonly ImmutableArray Prerequisites = []; public readonly string DetectedSound = null; diff --git a/OpenRA.Mods.Common/Traits/Transforms.cs b/OpenRA.Mods.Common/Traits/Transforms.cs index d23d17968f..ca679e6642 100644 --- a/OpenRA.Mods.Common/Traits/Transforms.cs +++ b/OpenRA.Mods.Common/Traits/Transforms.cs @@ -10,6 +10,7 @@ #endregion using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using OpenRA.Activities; using OpenRA.Mods.Common.Activities; @@ -33,10 +34,10 @@ namespace OpenRA.Mods.Common.Traits public readonly WAngle Facing = new(384); [Desc("Sounds to play when transforming.")] - public readonly string[] TransformSounds = []; + public readonly ImmutableArray TransformSounds = []; [Desc("Sounds to play when the transformation is blocked.")] - public readonly string[] NoTransformSounds = []; + public readonly ImmutableArray NoTransformSounds = []; [NotificationReference("Speech")] [Desc("Speech notification to play when transforming.")] diff --git a/OpenRA.Mods.Common/Traits/Wanders.cs b/OpenRA.Mods.Common/Traits/Wanders.cs index 6bb003aacb..72c9c11668 100644 --- a/OpenRA.Mods.Common/Traits/Wanders.cs +++ b/OpenRA.Mods.Common/Traits/Wanders.cs @@ -9,7 +9,7 @@ */ #endregion -using System.Collections.Generic; +using System.Collections.Frozen; using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits @@ -29,7 +29,7 @@ namespace OpenRA.Mods.Common.Traits public readonly int MaxMoveDelay = 0; [Desc("The terrain types that this actor should avoid wandering on to.")] - public readonly HashSet AvoidTerrainTypes = []; + public readonly FrozenSet AvoidTerrainTypes = FrozenSet.Empty; public override object Create(ActorInitializer init) { return new Wanders(init.Self, this); } } diff --git a/OpenRA.Mods.Common/Traits/World/ActorSpawnManager.cs b/OpenRA.Mods.Common/Traits/World/ActorSpawnManager.cs index 4d233ea177..503f786a5d 100644 --- a/OpenRA.Mods.Common/Traits/World/ActorSpawnManager.cs +++ b/OpenRA.Mods.Common/Traits/World/ActorSpawnManager.cs @@ -9,7 +9,8 @@ */ #endregion -using System.Collections.Generic; +using System.Collections.Frozen; +using System.Collections.Immutable; using System.Linq; using OpenRA.Traits; @@ -30,17 +31,17 @@ namespace OpenRA.Mods.Common.Traits [Desc("Time (in ticks) between actor spawn. Supports 1 or 2 values.", "If 2 values are provided they are used as a range from which a value is randomly selected.")] - public readonly int[] SpawnInterval = [6000]; + public readonly ImmutableArray SpawnInterval = [6000]; [FieldLoader.Require] [ActorReference] [Desc("Name of the actor that will be randomly picked to spawn.")] - public readonly string[] Actors = []; + public readonly ImmutableArray Actors = []; public readonly string Owner = "Creeps"; [Desc("Type of ActorSpawner with which it connects.")] - public readonly HashSet Types = []; + public readonly FrozenSet Types = FrozenSet.Empty; public override void RulesetLoaded(Ruleset rules, ActorInfo ai) { diff --git a/OpenRA.Mods.Common/Traits/World/BridgeLayer.cs b/OpenRA.Mods.Common/Traits/World/BridgeLayer.cs index 1abef2bf7b..c8e391a01b 100644 --- a/OpenRA.Mods.Common/Traits/World/BridgeLayer.cs +++ b/OpenRA.Mods.Common/Traits/World/BridgeLayer.cs @@ -9,6 +9,7 @@ */ #endregion +using System.Collections.Immutable; using OpenRA.Primitives; using OpenRA.Traits; @@ -21,7 +22,7 @@ namespace OpenRA.Mods.Common.Traits string Type { get; } DamageState DamageState { get; } - CVec[] NeighbourOffsets { get; } + ImmutableArray NeighbourOffsets { get; } bool Valid { get; } CPos Location { get; } } diff --git a/OpenRA.Mods.Common/Traits/World/BuildableTerrainOverlay.cs b/OpenRA.Mods.Common/Traits/World/BuildableTerrainOverlay.cs index f6d7bd15f7..685339bdfe 100644 --- a/OpenRA.Mods.Common/Traits/World/BuildableTerrainOverlay.cs +++ b/OpenRA.Mods.Common/Traits/World/BuildableTerrainOverlay.cs @@ -9,7 +9,7 @@ */ #endregion -using System.Collections.Generic; +using System.Collections.Frozen; using System.Linq; using OpenRA.Graphics; using OpenRA.Traits; @@ -20,7 +20,7 @@ namespace OpenRA.Mods.Common.Traits public class BuildableTerrainOverlayInfo : TraitInfo { [FieldLoader.Require] - public readonly HashSet AllowedTerrainTypes = null; + public readonly FrozenSet AllowedTerrainTypes = null; [PaletteReference] [Desc("Palette to use for rendering the sprite.")] diff --git a/OpenRA.Mods.Common/Traits/World/ClearMapGenerator.cs b/OpenRA.Mods.Common/Traits/World/ClearMapGenerator.cs index 38319a1208..a7a3309557 100644 --- a/OpenRA.Mods.Common/Traits/World/ClearMapGenerator.cs +++ b/OpenRA.Mods.Common/Traits/World/ClearMapGenerator.cs @@ -9,7 +9,7 @@ */ #endregion -using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using OpenRA.Mods.Common.MapGenerator; using OpenRA.Support; @@ -32,7 +32,7 @@ namespace OpenRA.Mods.Common.Traits [FieldLoader.Require] [Desc("Tilesets that are compatible with this map generator.")] - public readonly string[] Tilesets = null; + public readonly ImmutableArray Tilesets = default; [FluentReference] [Desc("The title to use for generated maps.")] @@ -44,7 +44,7 @@ namespace OpenRA.Mods.Common.Traits // This is purely of interest to the linter. [FieldLoader.LoadUsing(nameof(FluentReferencesLoader))] [FluentReference] - public readonly List FluentReferences = null; + public readonly ImmutableArray FluentReferences = default; [FieldLoader.LoadUsing(nameof(SettingsLoader))] public readonly MiniYaml Settings; @@ -58,10 +58,10 @@ namespace OpenRA.Mods.Common.Traits return my.NodeWithKey("Settings").Value; } - static List FluentReferencesLoader(MiniYaml my) + static object FluentReferencesLoader(MiniYaml my) { return new MapGeneratorSettings(null, my.NodeWithKey("Settings").Value) - .Options.SelectMany(o => o.GetFluentReferences()).ToList(); + .Options.SelectMany(o => o.GetFluentReferences()).ToImmutableArray(); } public IMapGeneratorSettings GetSettings() @@ -98,7 +98,7 @@ namespace OpenRA.Mods.Common.Traits return new ClearMapGenerator(this); } - string[] IEditorMapGeneratorInfo.Tilesets => Tilesets; + ImmutableArray IEditorMapGeneratorInfo.Tilesets => Tilesets; } public class ClearMapGenerator : IEditorTool diff --git a/OpenRA.Mods.Common/Traits/World/ColorPickerManager.cs b/OpenRA.Mods.Common/Traits/World/ColorPickerManager.cs index 09a18edce5..1604c1802f 100644 --- a/OpenRA.Mods.Common/Traits/World/ColorPickerManager.cs +++ b/OpenRA.Mods.Common/Traits/World/ColorPickerManager.cs @@ -10,7 +10,9 @@ #endregion using System; +using System.Collections.Frozen; using System.Collections.Generic; +using System.Collections.Immutable; using OpenRA.Graphics; using OpenRA.Mods.Common.Widgets; using OpenRA.Primitives; @@ -34,16 +36,16 @@ namespace OpenRA.Mods.Common.Traits const string InvalidPlayerColor = "notification-invalid-player-color"; [Desc("Minimum and maximum saturation levels that are valid for use.")] - public readonly float[] HsvSaturationRange = [0.3f, 0.95f]; + public readonly ImmutableArray HsvSaturationRange = [0.3f, 0.95f]; [Desc("Minimum and maximum value levels that are valid for use.")] - public readonly float[] HsvValueRange = [0.3f, 0.95f]; + public readonly ImmutableArray HsvValueRange = [0.3f, 0.95f]; [Desc("Perceptual color threshold for determining whether two colors are too similar.")] public readonly int SimilarityThreshold = 0x50; [Desc("List of colors to be displayed in the palette tab.")] - public readonly Color[] PresetColors = []; + public readonly ImmutableArray PresetColors = []; [ActorReference] [Desc("Actor type to show in the color picker. This can be overridden for specific factions with FactionPreviewActors.")] @@ -52,7 +54,7 @@ namespace OpenRA.Mods.Common.Traits [SequenceReference(dictionaryReference: LintDictionaryReference.Values)] [Desc("Actor type to show in the color picker for specific factions. Overrides PreviewActor.", "A dictionary of [faction name]: [actor name].")] - public readonly Dictionary FactionPreviewActors = []; + public readonly FrozenDictionary FactionPreviewActors = FrozenDictionary.Empty; public bool IsInvalidColor(Color color, IEnumerable candidateBlockers) { @@ -131,7 +133,7 @@ namespace OpenRA.Mods.Common.Traits (float SMin, float SMax) IColorPickerManagerInfo.SaturationRange => (HsvSaturationRange[0], HsvSaturationRange[1]); (float VMin, float VMax) IColorPickerManagerInfo.ValueRange => (HsvValueRange[0], HsvValueRange[1]); - Color[] IColorPickerManagerInfo.PresetColors => PresetColors; + ImmutableArray IColorPickerManagerInfo.PresetColors => PresetColors; Color IColorPickerManagerInfo.RandomPresetColor( MersenneTwister random, diff --git a/OpenRA.Mods.Common/Traits/World/ControlGroups.cs b/OpenRA.Mods.Common/Traits/World/ControlGroups.cs index 8a4e332c26..b44c4f33b7 100644 --- a/OpenRA.Mods.Common/Traits/World/ControlGroups.cs +++ b/OpenRA.Mods.Common/Traits/World/ControlGroups.cs @@ -18,18 +18,18 @@ namespace OpenRA.Mods.Common.Traits { public class ControlGroupsInfo : TraitInfo, IControlGroupsInfo { - public readonly string[] Groups = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "0"]; + public readonly ImmutableArray Groups = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "0"]; public override object Create(ActorInitializer init) { return new ControlGroups(init.World, this); } - string[] IControlGroupsInfo.Groups => Groups; + ImmutableArray IControlGroupsInfo.Groups => Groups; } [TraitLocation(SystemActors.World | SystemActors.EditorWorld)] public class ControlGroups : IControlGroups, ITick, IGameSaveTraitData { readonly World world; - public string[] Groups { get; } + public ImmutableArray Groups { get; } readonly List[] controlGroups; diff --git a/OpenRA.Mods.Common/Traits/World/CrateSpawner.cs b/OpenRA.Mods.Common/Traits/World/CrateSpawner.cs index 1a748193c6..62e27c67b8 100644 --- a/OpenRA.Mods.Common/Traits/World/CrateSpawner.cs +++ b/OpenRA.Mods.Common/Traits/World/CrateSpawner.cs @@ -10,7 +10,9 @@ #endregion using System; +using System.Collections.Frozen; using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using OpenRA.Mods.Common.Activities; using OpenRA.Traits; @@ -53,20 +55,20 @@ namespace OpenRA.Mods.Common.Traits public readonly int InitialSpawnDelay = 0; [Desc("Which terrain types can we drop on?")] - public readonly HashSet ValidGround = ["Clear", "Rough", "Road", "Ore", "Beach"]; + public readonly FrozenSet ValidGround = new HashSet { "Clear", "Rough", "Road", "Ore", "Beach" }.ToFrozenSet(); [Desc("Which terrain types count as water?")] - public readonly HashSet ValidWater = ["Water"]; + public readonly FrozenSet ValidWater = new HashSet { "Water" }.ToFrozenSet(); [Desc("Chance of generating a water crate instead of a land crate.")] public readonly int WaterChance = 20; [ActorReference] [Desc("Crate actors to drop.")] - public readonly string[] CrateActors = ["crate"]; + public readonly ImmutableArray CrateActors = ["crate"]; [Desc("Chance of each crate actor spawning.")] - public readonly int[] CrateActorShares = [10]; + public readonly ImmutableArray CrateActorShares = [10]; [ActorReference] [Desc("If a DeliveryAircraft: is specified, then this actor will deliver crates.")] diff --git a/OpenRA.Mods.Common/Traits/World/CreateMapPlayers.cs b/OpenRA.Mods.Common/Traits/World/CreateMapPlayers.cs index e268a56f44..0f7b252dcf 100644 --- a/OpenRA.Mods.Common/Traits/World/CreateMapPlayers.cs +++ b/OpenRA.Mods.Common/Traits/World/CreateMapPlayers.cs @@ -11,6 +11,7 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using OpenRA.Network; using OpenRA.Support; @@ -128,7 +129,7 @@ namespace OpenRA.Mods.Common.Traits NonCombatant = true, Spectating = true, Faction = "Random", - Allies = worldPlayers.Where(p => !p.NonCombatant && p.Playable).Select(p => p.InternalName).ToArray() + Allies = worldPlayers.Where(p => !p.NonCombatant && p.Playable).Select(p => p.InternalName).ToImmutableArray() }, playerRandom)); w.SetPlayers(worldPlayers, localPlayer); diff --git a/OpenRA.Mods.Common/Traits/World/EditorActorLayer.cs b/OpenRA.Mods.Common/Traits/World/EditorActorLayer.cs index b9ac7b8904..2fa55ad968 100644 --- a/OpenRA.Mods.Common/Traits/World/EditorActorLayer.cs +++ b/OpenRA.Mods.Common/Traits/World/EditorActorLayer.cs @@ -11,6 +11,7 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Globalization; using System.Linq; using System.Runtime.InteropServices; @@ -342,7 +343,7 @@ namespace OpenRA.Mods.Common.Traits var creeps = Players.Players.Keys.FirstOrDefault(p => p == "Creeps"); if (!string.IsNullOrEmpty(creeps)) - Players.Players[creeps].Enemies = Players.Players.Keys.Where(p => !Players.Players[p].NonCombatant).ToArray(); + Players.Players[creeps].Enemies = Players.Players.Keys.Where(p => !Players.Players[p].NonCombatant).ToImmutableArray(); } void UpdateNeighbours(ReadOnlySpan previews) diff --git a/OpenRA.Mods.Common/Traits/World/EditorResourceLayer.cs b/OpenRA.Mods.Common/Traits/World/EditorResourceLayer.cs index 43889b07ef..0270cdd514 100644 --- a/OpenRA.Mods.Common/Traits/World/EditorResourceLayer.cs +++ b/OpenRA.Mods.Common/Traits/World/EditorResourceLayer.cs @@ -10,6 +10,7 @@ #endregion using System; +using System.Collections.Frozen; using System.Collections.Generic; using System.Linq; using OpenRA.Graphics; @@ -22,7 +23,7 @@ namespace OpenRA.Mods.Common.Traits public class EditorResourceLayerInfo : TraitInfo, IResourceLayerInfo { [FieldLoader.LoadUsing(nameof(LoadResourceTypes))] - public readonly Dictionary ResourceTypes = null; + public readonly FrozenDictionary ResourceTypes = null; // Copied from ResourceLayerInfo protected static object LoadResourceTypes(MiniYaml yaml) @@ -33,7 +34,7 @@ namespace OpenRA.Mods.Common.Traits foreach (var r in resources.Value.Nodes) ret[r.Key] = new ResourceLayerInfo.ResourceTypeInfo(r.Value); - return ret; + return ret.ToFrozenDictionary(); } [Desc("Override the density saved in maps with values calculated based on the number of neighbouring resource cells.")] @@ -72,7 +73,7 @@ namespace OpenRA.Mods.Common.Traits protected readonly Map Map; protected readonly Dictionary ResourceTypesByIndex; protected readonly CellLayer Tiles; - protected Dictionary resourceValues; + protected FrozenDictionary resourceValues; public int NetWorth { get; protected set; } @@ -109,7 +110,7 @@ namespace OpenRA.Mods.Common.Traits public void WorldLoaded(World w, WorldRenderer wr) { var playerResourcesInfo = w.Map.Rules.Actors[SystemActors.Player].TraitInfoOrDefault(); - resourceValues = playerResourcesInfo?.ResourceValues ?? []; + resourceValues = playerResourcesInfo?.ResourceValues ?? FrozenDictionary.Empty; foreach (var cell in Map.AllCells) UpdateCell(cell); diff --git a/OpenRA.Mods.Common/Traits/World/ExperimentalMapGenerator.cs b/OpenRA.Mods.Common/Traits/World/ExperimentalMapGenerator.cs index 437a1d5a58..b19ed12a41 100644 --- a/OpenRA.Mods.Common/Traits/World/ExperimentalMapGenerator.cs +++ b/OpenRA.Mods.Common/Traits/World/ExperimentalMapGenerator.cs @@ -10,6 +10,7 @@ #endregion using System; +using System.Collections.Frozen; using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; @@ -33,7 +34,7 @@ namespace OpenRA.Mods.Common.Traits [FieldLoader.Require] [Desc("Tilesets that are compatible with this map generator.")] - public readonly string[] Tilesets = null; + public readonly ImmutableArray Tilesets = default; [FluentReference] [Desc("The title to use for generated maps.")] @@ -45,7 +46,7 @@ namespace OpenRA.Mods.Common.Traits // This is purely of interest to the linter. [FieldLoader.LoadUsing(nameof(FluentReferencesLoader))] [FluentReference] - public readonly List FluentReferences = null; + public readonly ImmutableArray FluentReferences = default; [FieldLoader.LoadUsing(nameof(SettingsLoader))] public readonly MiniYaml Settings; @@ -53,17 +54,17 @@ namespace OpenRA.Mods.Common.Traits string IMapGeneratorInfo.Type => Type; string IMapGeneratorInfo.Name => Name; string IMapGeneratorInfo.MapTitle => MapTitle; - string[] IEditorMapGeneratorInfo.Tilesets => Tilesets; + ImmutableArray IEditorMapGeneratorInfo.Tilesets => Tilesets; static MiniYaml SettingsLoader(MiniYaml my) { return my.NodeWithKey("Settings").Value; } - static List FluentReferencesLoader(MiniYaml my) + static object FluentReferencesLoader(MiniYaml my) { return new MapGeneratorSettings(null, my.NodeWithKey("Settings").Value) - .Options.SelectMany(o => o.GetFluentReferences()).ToList(); + .Options.SelectMany(o => o.GetFluentReferences()).ToImmutableArray(); } const int FractionMax = Terraformer.FractionMax; @@ -280,7 +281,7 @@ namespace OpenRA.Mods.Common.Traits return my.NodeWithKey(key).Value.Value .Split(',', StringSplitOptions.RemoveEmptyEntries) .Select(terrainInfo.GetTerrainIndex) - .ToImmutableHashSet(); + .ToFrozenSet(); } IReadOnlyList ParseSegmentTypes(string key) diff --git a/OpenRA.Mods.Common/Traits/World/LegacyBridgeLayer.cs b/OpenRA.Mods.Common/Traits/World/LegacyBridgeLayer.cs index 12bf8723e3..a50f620948 100644 --- a/OpenRA.Mods.Common/Traits/World/LegacyBridgeLayer.cs +++ b/OpenRA.Mods.Common/Traits/World/LegacyBridgeLayer.cs @@ -10,6 +10,7 @@ #endregion using System.Collections.Generic; +using System.Collections.Immutable; using System.IO; using System.Linq; using OpenRA.Graphics; @@ -22,7 +23,7 @@ namespace OpenRA.Mods.Common.Traits public class LegacyBridgeLayerInfo : TraitInfo { [ActorReference] - public readonly string[] Bridges = ["bridge1", "bridge2"]; + public readonly ImmutableArray Bridges = ["bridge1", "bridge2"]; public override object Create(ActorInitializer init) { return new LegacyBridgeLayer(init.Self, this); } } diff --git a/OpenRA.Mods.Common/Traits/World/Locomotor.cs b/OpenRA.Mods.Common/Traits/World/Locomotor.cs index ab692a16a6..208bcc13be 100644 --- a/OpenRA.Mods.Common/Traits/World/Locomotor.cs +++ b/OpenRA.Mods.Common/Traits/World/Locomotor.cs @@ -10,6 +10,7 @@ #endregion using System; +using System.Collections.Frozen; using System.Collections.Generic; using System.Linq; using OpenRA.Graphics; @@ -80,7 +81,7 @@ namespace OpenRA.Mods.Common.Traits [FieldLoader.LoadUsing(nameof(LoadSpeeds), true)] [Desc("Lower the value on rough terrain. Leave out entries for impassable terrain.")] - public readonly Dictionary TerrainSpeeds; + public readonly FrozenDictionary TerrainSpeeds; protected static object LoadSpeeds(MiniYaml y) { @@ -99,8 +100,7 @@ namespace OpenRA.Mods.Common.Traits } } - ret.TrimExcess(); - return ret; + return ret.ToFrozenDictionary(); } public class TerrainInfo diff --git a/OpenRA.Mods.Common/Traits/World/MapStartingLocations.cs b/OpenRA.Mods.Common/Traits/World/MapStartingLocations.cs index 9850f4d612..7a4ffe4419 100644 --- a/OpenRA.Mods.Common/Traits/World/MapStartingLocations.cs +++ b/OpenRA.Mods.Common/Traits/World/MapStartingLocations.cs @@ -10,6 +10,7 @@ #endregion using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using OpenRA.Graphics; using OpenRA.Mods.Common.Widgets.Logic; @@ -62,7 +63,7 @@ namespace OpenRA.Mods.Common.Traits sealed class AssignSpawnLocationsState { - public CPos[] SpawnLocations; + public ImmutableArray SpawnLocations; public List AvailableSpawnPoints; public readonly Dictionary OccupiedSpawnPoints = []; } diff --git a/OpenRA.Mods.Common/Traits/World/MapStartingUnits.cs b/OpenRA.Mods.Common/Traits/World/MapStartingUnits.cs index 0a1d6b5ea1..0fb465b140 100644 --- a/OpenRA.Mods.Common/Traits/World/MapStartingUnits.cs +++ b/OpenRA.Mods.Common/Traits/World/MapStartingUnits.cs @@ -9,7 +9,8 @@ */ #endregion -using System.Collections.Generic; +using System.Collections.Frozen; +using System.Collections.Immutable; using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits @@ -26,7 +27,7 @@ namespace OpenRA.Mods.Common.Traits public readonly string ClassName = "Unlabeled"; [Desc("Only available when selecting one of these factions.", "Leave empty for no restrictions.")] - public readonly HashSet Factions = []; + public readonly FrozenSet Factions = FrozenSet.Empty; [Desc("The actor at the center, usually the mobile construction vehicle.")] [ActorReference] @@ -37,7 +38,7 @@ namespace OpenRA.Mods.Common.Traits [Desc("A group of units ready to defend or scout.")] [ActorReference] - public readonly string[] SupportActors = []; + public readonly ImmutableArray SupportActors = []; [Desc("Inner radius for spawning support actors")] public readonly int InnerSupportRadius = 2; diff --git a/OpenRA.Mods.Common/Traits/World/MarkerLayerOverlay.cs b/OpenRA.Mods.Common/Traits/World/MarkerLayerOverlay.cs index 16c56f21af..25f76bbd2b 100644 --- a/OpenRA.Mods.Common/Traits/World/MarkerLayerOverlay.cs +++ b/OpenRA.Mods.Common/Traits/World/MarkerLayerOverlay.cs @@ -10,6 +10,7 @@ #endregion using System; +using System.Collections.Frozen; using System.Collections.Generic; using System.Collections.Immutable; using System.IO; @@ -33,7 +34,7 @@ namespace OpenRA.Mods.Common.Traits [FluentReference(LintDictionaryReference.Keys)] [Desc("A list of colors to be used for drawing.")] - public readonly Dictionary Colors = new() + public readonly FrozenDictionary Colors = new Dictionary { { "notification-added-marker-tiles-markers.red", Color.FromArgb(255, 0, 0) }, { "notification-added-marker-tiles-markers.orange", Color.FromArgb(255, 127, 0) }, @@ -43,7 +44,7 @@ namespace OpenRA.Mods.Common.Traits { "notification-added-marker-tiles-markers.blue", Color.FromArgb(0, 42, 255) }, { "notification-added-marker-tiles-markers.purple", Color.FromArgb(165, 0, 255) }, { "notification-added-marker-tiles-markers.magenta", Color.FromArgb(255, 0, 220) } - }; + }.ToFrozenDictionary(); [Desc("Default alpha blend.")] public readonly int Alpha = 85; @@ -174,7 +175,7 @@ namespace OpenRA.Mods.Common.Traits NumSides = file.NumSides; AxisAngle = file.AxisAngle; - SetAll(file.Tiles.ToImmutableDictionary(d => d.Key, d => d.Value.ToImmutableArray())); + SetAll(file.Tiles.ToFrozenDictionary(d => d.Key, d => d.Value.ToImmutableArray())); } catch (Exception e) { @@ -204,7 +205,7 @@ namespace OpenRA.Mods.Common.Traits Tiles.Clear(); } - public void SetAll(IImmutableDictionary> newTiles) + public void SetAll(FrozenDictionary> newTiles) { ClearAll(); diff --git a/OpenRA.Mods.Common/Traits/World/ResourceLayer.cs b/OpenRA.Mods.Common/Traits/World/ResourceLayer.cs index 7773db70d5..8b3967aa2e 100644 --- a/OpenRA.Mods.Common/Traits/World/ResourceLayer.cs +++ b/OpenRA.Mods.Common/Traits/World/ResourceLayer.cs @@ -10,6 +10,7 @@ #endregion using System; +using System.Collections.Frozen; using System.Collections.Generic; using System.Linq; using OpenRA.Graphics; @@ -40,7 +41,7 @@ namespace OpenRA.Mods.Common.Traits [FieldLoader.Require] [Desc("Terrain types that this resource can spawn on.")] - public readonly HashSet AllowedTerrainTypes = null; + public readonly FrozenSet AllowedTerrainTypes = null; [Desc("Maximum number of resource units allowed in a single cell.")] public readonly byte MaxDensity = 10; @@ -52,7 +53,7 @@ namespace OpenRA.Mods.Common.Traits } [FieldLoader.LoadUsing(nameof(LoadResourceTypes))] - public readonly Dictionary ResourceTypes = null; + public readonly FrozenDictionary ResourceTypes = null; [Desc("Override the density saved in maps with values calculated based on the number of neighbouring resource cells.")] public readonly bool RecalculateResourceDensity = false; @@ -66,7 +67,7 @@ namespace OpenRA.Mods.Common.Traits foreach (var r in resources.Value.Nodes) ret[r.Key] = new ResourceTypeInfo(r.Value); - return ret; + return ret.ToFrozenDictionary(); } bool IResourceLayerInfo.TryGetTerrainType(string resourceType, out string terrainType) diff --git a/OpenRA.Mods.Common/Traits/World/ResourceRenderer.cs b/OpenRA.Mods.Common/Traits/World/ResourceRenderer.cs index ed141ea9b2..e55765d5d3 100644 --- a/OpenRA.Mods.Common/Traits/World/ResourceRenderer.cs +++ b/OpenRA.Mods.Common/Traits/World/ResourceRenderer.cs @@ -10,7 +10,9 @@ #endregion using System; +using System.Collections.Frozen; using System.Collections.Generic; +using System.Collections.Immutable; using System.IO; using System.Linq; using OpenRA.Graphics; @@ -31,7 +33,7 @@ namespace OpenRA.Mods.Common.Traits [FieldLoader.Require] [SequenceReference(nameof(Image))] [Desc("Randomly chosen image sequences.")] - public readonly string[] Sequences = []; + public readonly ImmutableArray Sequences = []; [PaletteReference] [Desc("Palette used for rendering the resource sprites.")] @@ -50,7 +52,7 @@ namespace OpenRA.Mods.Common.Traits [IncludeFluentReferences(LintDictionaryReference.Values)] [FieldLoader.LoadUsing(nameof(LoadResourceTypes))] - public readonly Dictionary ResourceTypes = null; + public readonly FrozenDictionary ResourceTypes = null; // Copied from ResourceLayerInfo protected static object LoadResourceTypes(MiniYaml yaml) @@ -61,7 +63,7 @@ namespace OpenRA.Mods.Common.Traits foreach (var r in resources.Value.Nodes) ret[r.Key] = new ResourceTypeInfo(r.Value); - return ret; + return ret.ToFrozenDictionary(); } void IMapPreviewSignatureInfo.PopulateMapPreviewSignatureCells(Map map, ActorInfo ai, ActorReference s, List<(MPos Uv, Color Color)> destinationBuffer) diff --git a/OpenRA.Mods.Common/Traits/World/ShroudRenderer.cs b/OpenRA.Mods.Common/Traits/World/ShroudRenderer.cs index 833f31a170..07fa73f0fe 100644 --- a/OpenRA.Mods.Common/Traits/World/ShroudRenderer.cs +++ b/OpenRA.Mods.Common/Traits/World/ShroudRenderer.cs @@ -11,6 +11,7 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.IO; using System.Linq; using OpenRA.Graphics; @@ -24,10 +25,10 @@ namespace OpenRA.Mods.Common.Traits { public readonly string Sequence = "shroud"; [SequenceReference(nameof(Sequence))] - public readonly string[] ShroudVariants = ["shroud"]; + public readonly ImmutableArray ShroudVariants = ["shroud"]; [SequenceReference(nameof(Sequence))] - public readonly string[] FogVariants = ["fog"]; + public readonly ImmutableArray FogVariants = ["fog"]; [PaletteReference] public readonly string ShroudPalette = "shroud"; @@ -37,7 +38,7 @@ namespace OpenRA.Mods.Common.Traits [Desc("Bitfield of shroud directions for each frame. Lower four bits are", "corners clockwise from TL; upper four are edges clockwise from top")] - public readonly int[] Index = [12, 9, 8, 3, 1, 6, 4, 2, 13, 11, 7, 14]; + public readonly ImmutableArray Index = [12, 9, 8, 3, 1, 6, 4, 2, 13, 11, 7, 14]; [Desc("Use the upper four bits when calculating frame")] public readonly bool UseExtendedIndex = false; diff --git a/OpenRA.Mods.Common/Traits/World/SmudgeLayer.cs b/OpenRA.Mods.Common/Traits/World/SmudgeLayer.cs index f6ef3008fc..2e4a896be7 100644 --- a/OpenRA.Mods.Common/Traits/World/SmudgeLayer.cs +++ b/OpenRA.Mods.Common/Traits/World/SmudgeLayer.cs @@ -9,7 +9,9 @@ */ #endregion +using System.Collections.Frozen; using System.Collections.Generic; +using System.Collections.Immutable; using System.IO; using System.Linq; using OpenRA.Graphics; @@ -46,7 +48,7 @@ namespace OpenRA.Mods.Common.Traits [SequenceReference(nameof(SmokeImage), allowNullImage: true)] [Desc("Smoke sprite sequences randomly chosen from")] - public readonly string[] SmokeSequences = []; + public readonly ImmutableArray SmokeSequences = []; [PaletteReference] public readonly string SmokePalette = "effect"; @@ -55,7 +57,7 @@ namespace OpenRA.Mods.Common.Traits public readonly string Palette = TileSet.TerrainPaletteInternalName; [FieldLoader.LoadUsing(nameof(LoadInitialSmudges))] - public readonly Dictionary InitialSmudges; + public readonly FrozenDictionary InitialSmudges; public static object LoadInitialSmudges(MiniYaml yaml) { @@ -77,7 +79,7 @@ namespace OpenRA.Mods.Common.Traits } } - return smudges; + return smudges.ToFrozenDictionary(); } public override object Create(ActorInitializer init) { return new SmudgeLayer(init.Self, this); } diff --git a/OpenRA.Mods.Common/Traits/World/SubterraneanLocomotor.cs b/OpenRA.Mods.Common/Traits/World/SubterraneanLocomotor.cs index 9e6bde653d..8734c9b456 100644 --- a/OpenRA.Mods.Common/Traits/World/SubterraneanLocomotor.cs +++ b/OpenRA.Mods.Common/Traits/World/SubterraneanLocomotor.cs @@ -9,7 +9,7 @@ */ #endregion -using System.Collections.Generic; +using System.Collections.Frozen; using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits @@ -22,7 +22,7 @@ namespace OpenRA.Mods.Common.Traits public readonly short SubterraneanTransitionCost = 0; [Desc("The terrain types that this actor can transition on. Leave empty to allow any.")] - public readonly HashSet SubterraneanTransitionTerrainTypes = []; + public readonly FrozenSet SubterraneanTransitionTerrainTypes = FrozenSet.Empty; [Desc("Can this actor transition on slopes?")] public readonly bool SubterraneanTransitionOnRamps = false; diff --git a/OpenRA.Mods.Common/Traits/World/TilingPathTool.cs b/OpenRA.Mods.Common/Traits/World/TilingPathTool.cs index af11ca06a0..4c986a6b3e 100644 --- a/OpenRA.Mods.Common/Traits/World/TilingPathTool.cs +++ b/OpenRA.Mods.Common/Traits/World/TilingPathTool.cs @@ -27,11 +27,11 @@ namespace OpenRA.Mods.Common.Traits public sealed class TilingPathToolInfo : TraitInfo { [Desc("The preferred defaults for the start type.")] - public readonly string[] DefaultStart = []; + public readonly ImmutableArray DefaultStart = []; [Desc("The preferred defaults for the inner type.")] - public readonly string[] DefaultInner = []; + public readonly ImmutableArray DefaultInner = []; [Desc("The preferred defaults for the end type.")] - public readonly string[] DefaultEnd = []; + public readonly ImmutableArray DefaultEnd = []; public override object Create(ActorInitializer init) { diff --git a/OpenRA.Mods.Common/Traits/World/TimeLimitManager.cs b/OpenRA.Mods.Common/Traits/World/TimeLimitManager.cs index d15e993ec2..05851013b1 100644 --- a/OpenRA.Mods.Common/Traits/World/TimeLimitManager.cs +++ b/OpenRA.Mods.Common/Traits/World/TimeLimitManager.cs @@ -9,7 +9,9 @@ */ #endregion +using System.Collections.Frozen; using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using OpenRA.Mods.Common.Widgets; using OpenRA.Primitives; @@ -32,10 +34,10 @@ namespace OpenRA.Mods.Common.Traits public readonly string TimeLimitDescription = "dropdown-time-limit.description"; [Desc("Time Limit options that will be shown in the lobby dropdown. Values are in minutes.")] - public readonly int[] TimeLimitOptions = [0, 10, 20, 30, 40, 60, 90]; + public readonly ImmutableArray TimeLimitOptions = [0, 10, 20, 30, 40, 60, 90]; [Desc("List of remaining minutes of game time when a text and optional speech notification should be made to players.")] - public readonly Dictionary TimeLimitWarnings = new() + public readonly FrozenDictionary TimeLimitWarnings = new Dictionary { { 1, null }, { 2, null }, @@ -43,7 +45,7 @@ namespace OpenRA.Mods.Common.Traits { 4, null }, { 5, null }, { 10, null }, - }; + }.ToFrozenDictionary(); [Desc("Default selection for the time limit option in the lobby. Needs to use one of the TimeLimitOptions.")] public readonly int TimeLimitDefault = 0; diff --git a/OpenRA.Mods.Common/Traits/World/WarheadDebugOverlay.cs b/OpenRA.Mods.Common/Traits/World/WarheadDebugOverlay.cs index bf92016417..06af50e548 100644 --- a/OpenRA.Mods.Common/Traits/World/WarheadDebugOverlay.cs +++ b/OpenRA.Mods.Common/Traits/World/WarheadDebugOverlay.cs @@ -10,6 +10,7 @@ #endregion using System.Collections.Generic; +using System.Collections.Immutable; using OpenRA.Graphics; using OpenRA.Mods.Common.Graphics; using OpenRA.Primitives; @@ -28,10 +29,10 @@ namespace OpenRA.Mods.Common.Traits public class WarheadDebugOverlay : IRenderAnnotations { - sealed class WHImpact(WPos pos, WDist[] range, int time, Color color) + sealed class WHImpact(WPos pos, ImmutableArray range, int time, Color color) { public readonly WPos CenterPosition = pos; - public readonly WDist[] Range = range; + public readonly ImmutableArray Range = range; public readonly Color Color = color; public int Time = time; @@ -46,7 +47,7 @@ namespace OpenRA.Mods.Common.Traits this.info = info; } - public void AddImpact(WPos pos, WDist[] range, Color color) + public void AddImpact(WPos pos, ImmutableArray range, Color color) { impacts.Add(new WHImpact(pos, range, info.DisplayDuration, color)); } diff --git a/OpenRA.Mods.Common/Traits/World/WeatherOverlay.cs b/OpenRA.Mods.Common/Traits/World/WeatherOverlay.cs index b92a509f6f..44270ea62f 100644 --- a/OpenRA.Mods.Common/Traits/World/WeatherOverlay.cs +++ b/OpenRA.Mods.Common/Traits/World/WeatherOverlay.cs @@ -10,6 +10,7 @@ #endregion using System; +using System.Collections.Immutable; using OpenRA.Graphics; using OpenRA.Primitives; using OpenRA.Support; @@ -28,10 +29,10 @@ namespace OpenRA.Mods.Common.Traits public readonly bool ChangingWindLevel = true; [Desc("The levels of wind intensity (particles x-axis movement in px/tick).")] - public readonly int[] WindLevels = [-12, -7, -5, 0, 5, 7, 12]; + public readonly ImmutableArray WindLevels = [-12, -7, -5, 0, 5, 7, 12]; [Desc("Works only if ChangingWindLevel is enabled. Min. and max. ticks needed to change the WindLevel.")] - public readonly int[] WindTick = [150, 550]; + public readonly ImmutableArray WindTick = [150, 550]; [Desc("Hard or soft fading between the WindLevels.")] public readonly bool InstantWindChanges = false; @@ -40,25 +41,25 @@ namespace OpenRA.Mods.Common.Traits public readonly bool UseSquares = true; [Desc("Size / width of the particle in px.")] - public readonly int[] ParticleSize = [1, 3]; + public readonly ImmutableArray ParticleSize = [1, 3]; [Desc("Scatters falling direction on the x-axis. Scatter min. and max. value in px/tick.")] - public readonly int[] ScatterDirection = [-1, 1]; + public readonly ImmutableArray ScatterDirection = [-1, 1]; [Desc("Min. and max. speed at which particles fall in px/tick.")] - public readonly float[] Gravity = [2.5f, 5f]; + public readonly ImmutableArray Gravity = [2.5f, 5f]; [Desc("The current offset value for the swing movement. SwingOffset min. and max. value in px/tick.")] - public readonly float[] SwingOffset = [2.5f, 3.5f]; + public readonly ImmutableArray SwingOffset = [2.5f, 3.5f]; [Desc("The value that particles swing to the side each update. SwingSpeed min. and max. value in px/tick.")] - public readonly float[] SwingSpeed = [0.0025f, 0.06f]; + public readonly ImmutableArray SwingSpeed = [0.0025f, 0.06f]; [Desc("The value range that can be swung to the left or right. SwingAmplitude min. and max. value in px/tick.")] - public readonly float[] SwingAmplitude = [1.0f, 1.5f]; + public readonly ImmutableArray SwingAmplitude = [1.0f, 1.5f]; [Desc("The randomly selected rgb(a) hex colors for the particles. Use this order: rrggbb[aa], rrggbb[aa], ...")] - public readonly Color[] ParticleColors = + public readonly ImmutableArray ParticleColors = [ Color.FromArgb(236, 236, 236), Color.FromArgb(228, 228, 228), diff --git a/OpenRA.Mods.Common/TraitsInterfaces.cs b/OpenRA.Mods.Common/TraitsInterfaces.cs index 1aa58359c9..1723d53f56 100644 --- a/OpenRA.Mods.Common/TraitsInterfaces.cs +++ b/OpenRA.Mods.Common/TraitsInterfaces.cs @@ -10,7 +10,9 @@ #endregion using System; +using System.Collections.Frozen; using System.Collections.Generic; +using System.Collections.Immutable; using OpenRA.Activities; using OpenRA.Graphics; using OpenRA.Mods.Common.Activities; @@ -373,7 +375,7 @@ namespace OpenRA.Mods.Common.Traits (float SMin, float SMax) SaturationRange { get; } (float VMin, float VMax) ValueRange { get; } event Action OnColorPickerColorUpdate; - Color[] PresetColors { get; } + ImmutableArray PresetColors { get; } Color RandomPresetColor(MersenneTwister random, IReadOnlyCollection terrainColors, IReadOnlyCollection playerColors); Color RandomValidColor(MersenneTwister random, IReadOnlyCollection terrainColors, IReadOnlyCollection playerColors); Color MakeValid( @@ -553,7 +555,7 @@ namespace OpenRA.Mods.Common.Traits public interface IOverrideAircraftLanding { - HashSet LandableTerrainTypes { get; } + FrozenSet LandableTerrainTypes { get; } } public interface IRadarSignature @@ -703,16 +705,16 @@ namespace OpenRA.Mods.Common.Traits public class EditorActorDropdown : EditorActorOption { - public readonly Func> GetLabels; - public readonly Func, string> GetValue; + public readonly Func> GetLabels; + public readonly Func, string> GetValue; public readonly Action OnChange; /// /// Creates dropdown for editing actor's metadata with dynamically created items. /// public EditorActorDropdown(string name, int displayOrder, - Func> getLabels, - Func, string> getValue, + Func> getLabels, + Func, string> getValue, Action onChange) : base(name, displayOrder) { @@ -1007,7 +1009,7 @@ namespace OpenRA.Mods.Common.Traits public interface IMapGeneratorSettings { - List Options { get; } + ImmutableArray Options { get; } int PlayerCount { get; } @@ -1020,7 +1022,7 @@ namespace OpenRA.Mods.Common.Traits public interface IEditorMapGeneratorInfo : IMapGeneratorInfo { - string[] Tilesets { get; } + ImmutableArray Tilesets { get; } IMapGeneratorSettings GetSettings(); } } diff --git a/OpenRA.Mods.Common/UpdateRules/UpdateUtils.cs b/OpenRA.Mods.Common/UpdateRules/UpdateUtils.cs index 5c6c654b4b..03efd39194 100644 --- a/OpenRA.Mods.Common/UpdateRules/UpdateUtils.cs +++ b/OpenRA.Mods.Common/UpdateRules/UpdateUtils.cs @@ -11,6 +11,7 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using System.Text; using OpenRA.FileSystem; @@ -52,7 +53,7 @@ namespace OpenRA.Mods.Common.UpdateRules /// public static YamlFileSet LoadExternalMapYaml(ModData modData, MiniYamlBuilder yaml, HashSet externalFilenames) { - return FieldLoader.GetValue("value", yaml.Value) + return FieldLoader.GetValue>("value", yaml.Value) .Where(f => f.Contains('|')) .SelectMany(f => LoadModYaml(modData, FilterExternalFiles(modData, [f], externalFilenames))) .ToList(); @@ -69,7 +70,7 @@ namespace OpenRA.Mods.Common.UpdateRules (null, "map.yaml", yaml.Nodes) }; - var files = FieldLoader.GetValue("value", yaml.Value); + var files = FieldLoader.GetValue>("value", yaml.Value); foreach (var filename in files) { // Ignore any files that aren't in the map bundle @@ -178,7 +179,7 @@ namespace OpenRA.Mods.Common.UpdateRules if (mapNode != null && mapNode.Value != null) { - var mapFiles = FieldLoader.GetValue("value", mapNode.Value); + var mapFiles = FieldLoader.GetValue>("value", mapNode.Value); yaml.AddRange(mapFiles.Select(filename => { // Explicit package paths never refer to a map diff --git a/OpenRA.Mods.Common/Util.cs b/OpenRA.Mods.Common/Util.cs index 71d870562b..29d28ba0b1 100644 --- a/OpenRA.Mods.Common/Util.cs +++ b/OpenRA.Mods.Common/Util.cs @@ -12,6 +12,7 @@ using System; using System.Collections.Frozen; using System.Collections.Generic; +using System.Collections.Immutable; using System.ComponentModel; using System.Linq; using System.Reflection; @@ -223,7 +224,7 @@ namespace OpenRA.Mods.Common } } - public static int RandomInRange(MersenneTwister random, int[] range) + public static int RandomInRange(MersenneTwister random, ImmutableArray range) { if (range.Length == 0) return 0; @@ -241,7 +242,7 @@ namespace OpenRA.Mods.Common : t.Name; } - public static WDist RandomDistance(MersenneTwister random, WDist[] distance) + public static WDist RandomDistance(MersenneTwister random, ImmutableArray distance) { if (distance.Length == 0) return WDist.Zero; @@ -252,7 +253,7 @@ namespace OpenRA.Mods.Common return new WDist(random.Next(distance[0].Length, distance[1].Length)); } - public static WVec RandomVector(MersenneTwister random, WVec[] vector) + public static WVec RandomVector(MersenneTwister random, ImmutableArray vector) { if (vector.Length == 0) return WVec.Zero; diff --git a/OpenRA.Mods.Common/UtilityCommands/ConvertSpriteToPngCommand.cs b/OpenRA.Mods.Common/UtilityCommands/ConvertSpriteToPngCommand.cs index 058682315f..fe575378be 100644 --- a/OpenRA.Mods.Common/UtilityCommands/ConvertSpriteToPngCommand.cs +++ b/OpenRA.Mods.Common/UtilityCommands/ConvertSpriteToPngCommand.cs @@ -10,6 +10,7 @@ #endregion using System; +using System.Collections.Immutable; using System.IO; using System.Linq; using OpenRA.FileFormats; @@ -44,7 +45,7 @@ namespace OpenRA.Mods.Common.UtilityCommands shadowIndex[^3] = 4; } - var palette = new ImmutablePalette(args[2], [0], shadowIndex); + var palette = new ImmutablePalette(args[2], [0], shadowIndex.ToImmutableArray()); var palColors = new Color[Palette.Size]; for (var i = 0; i < Palette.Size; i++) palColors[i] = palette.GetColor(i); diff --git a/OpenRA.Mods.Common/UtilityCommands/DebugChromeRegions.cs b/OpenRA.Mods.Common/UtilityCommands/DebugChromeRegions.cs index 7a59dc5617..7fdc1a51f2 100644 --- a/OpenRA.Mods.Common/UtilityCommands/DebugChromeRegions.cs +++ b/OpenRA.Mods.Common/UtilityCommands/DebugChromeRegions.cs @@ -12,6 +12,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; using OpenRA.Graphics; using OpenRA.Primitives; @@ -67,7 +68,7 @@ namespace OpenRA.Mods.Common.UtilityCommands } } - foreach (var kv in c.Value.Regions) + foreach (var kv in c.Value.Regions.OrderBy(kvp => kvp.Key)) { var r = kv.Value; regions.Add($"[\"{c.Key}.{kv.Key}\",{r.X},{r.Y},{r.Width},{r.Height}]"); diff --git a/OpenRA.Mods.Common/UtilityCommands/ExtractMapRules.cs b/OpenRA.Mods.Common/UtilityCommands/ExtractMapRules.cs index 29d3c70839..7079eb75f0 100644 --- a/OpenRA.Mods.Common/UtilityCommands/ExtractMapRules.cs +++ b/OpenRA.Mods.Common/UtilityCommands/ExtractMapRules.cs @@ -11,6 +11,7 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using OpenRA.FileSystem; namespace OpenRA.Mods.Common.UtilityCommands @@ -29,7 +30,7 @@ namespace OpenRA.Mods.Common.UtilityCommands // The order of the included files matter, so we can defer to system files // only as long as they are included first. var include = false; - var files = FieldLoader.GetValue("value", value.Value); + var files = FieldLoader.GetValue>("value", value.Value); foreach (var f in files) { include |= map.Package.Contains(f); @@ -56,7 +57,7 @@ namespace OpenRA.Mods.Common.UtilityCommands // The order of the included files matter, so we can defer to system files // only as long as they are included first. var include = false; - var files = FieldLoader.GetValue("value", value.Value); + var files = FieldLoader.GetValue>("value", value.Value); foreach (var f in files) { include |= map.Package.Contains(f); diff --git a/OpenRA.Mods.Common/UtilityCommands/FuzzMapGeneratorCommand.cs b/OpenRA.Mods.Common/UtilityCommands/FuzzMapGeneratorCommand.cs index 1748ea9768..66d90e23c2 100644 --- a/OpenRA.Mods.Common/UtilityCommands/FuzzMapGeneratorCommand.cs +++ b/OpenRA.Mods.Common/UtilityCommands/FuzzMapGeneratorCommand.cs @@ -10,6 +10,7 @@ #endregion using System; +using System.Collections.Frozen; using System.Collections.Generic; using System.Collections.Immutable; using System.Globalization; @@ -37,7 +38,7 @@ namespace OpenRA.Mods.Common.UtilityCommands public readonly int Shard; public readonly int ShardCount; public readonly ImmutableArray Variables; - public readonly ImmutableDictionary> Choices; + public readonly FrozenDictionary> Choices; Configuration( string mapGeneratorName, @@ -47,7 +48,7 @@ namespace OpenRA.Mods.Common.UtilityCommands int shard, int shardCount, ImmutableArray variables, - ImmutableDictionary> choices) + FrozenDictionary> choices) { MapGeneratorType = mapGeneratorName; NoDefaults = noDefaults; @@ -176,7 +177,7 @@ namespace OpenRA.Mods.Common.UtilityCommands shard, shardCount, variables.ToImmutableArray(), - choices.ToImmutableDictionary()); + choices.ToFrozenDictionary()); } } diff --git a/OpenRA.Mods.Common/UtilityCommands/Utilities.cs b/OpenRA.Mods.Common/UtilityCommands/Utilities.cs index 786767c79a..dfbebfe45f 100644 --- a/OpenRA.Mods.Common/UtilityCommands/Utilities.cs +++ b/OpenRA.Mods.Common/UtilityCommands/Utilities.cs @@ -10,6 +10,7 @@ #endregion using System; +using System.Collections.Immutable; using System.IO; using System.Linq; using System.Reflection; @@ -23,7 +24,7 @@ namespace OpenRA.Mods.Common.UtilityCommands public static class Utilities { public static MiniYamlNode GetTopLevelNodeByKey(ModData modData, string key, - Func manifestPropertySelector, + Func> manifestPropertySelector, Func mapPropertySelector = null, string mapPath = null) { diff --git a/OpenRA.Mods.Common/Warheads/CreateEffectWarhead.cs b/OpenRA.Mods.Common/Warheads/CreateEffectWarhead.cs index 8ff0444325..2228ae91b3 100644 --- a/OpenRA.Mods.Common/Warheads/CreateEffectWarhead.cs +++ b/OpenRA.Mods.Common/Warheads/CreateEffectWarhead.cs @@ -9,6 +9,7 @@ */ #endregion +using System.Collections.Immutable; using System.Linq; using OpenRA.GameRules; using OpenRA.Mods.Common.Effects; @@ -23,7 +24,7 @@ namespace OpenRA.Mods.Common.Warheads { [SequenceReference(nameof(Image), allowNullImage: true)] [Desc("List of explosion sequences that can be used.")] - public readonly string[] Explosions = []; + public readonly ImmutableArray Explosions = []; [Desc("Image containing explosion effect sequence.")] public readonly string Image = "explosion"; @@ -39,7 +40,7 @@ namespace OpenRA.Mods.Common.Warheads public readonly bool ForceDisplayAtGroundLevel = false; [Desc("List of sounds that can be played on impact.")] - public readonly string[] ImpactSounds = []; + public readonly ImmutableArray ImpactSounds = []; [Desc("Chance of impact sound to play.")] public readonly int ImpactSoundChance = 100; diff --git a/OpenRA.Mods.Common/Warheads/CreateResourceWarhead.cs b/OpenRA.Mods.Common/Warheads/CreateResourceWarhead.cs index 1fb0004a52..2333e59c10 100644 --- a/OpenRA.Mods.Common/Warheads/CreateResourceWarhead.cs +++ b/OpenRA.Mods.Common/Warheads/CreateResourceWarhead.cs @@ -9,6 +9,7 @@ */ #endregion +using System.Collections.Immutable; using OpenRA.GameRules; using OpenRA.Mods.Common.Traits; using OpenRA.Traits; @@ -19,7 +20,7 @@ namespace OpenRA.Mods.Common.Warheads public class CreateResourceWarhead : Warhead { [Desc("Size of the area. The resources are seeded within this area.", "Provide 2 values for a ring effect (outer/inner).")] - public readonly int[] Size = [0, 0]; + public readonly ImmutableArray Size = [0, 0]; [Desc("Will this splatter resources and which?")] [FieldLoader.Require] diff --git a/OpenRA.Mods.Common/Warheads/DamageWarhead.cs b/OpenRA.Mods.Common/Warheads/DamageWarhead.cs index 2fbe65413a..3449751027 100644 --- a/OpenRA.Mods.Common/Warheads/DamageWarhead.cs +++ b/OpenRA.Mods.Common/Warheads/DamageWarhead.cs @@ -9,7 +9,7 @@ */ #endregion -using System.Collections.Generic; +using System.Collections.Frozen; using System.Linq; using OpenRA.GameRules; using OpenRA.Mods.Common.Traits; @@ -27,7 +27,7 @@ namespace OpenRA.Mods.Common.Warheads public readonly BitSet DamageTypes = default; [Desc("Damage percentage versus each armor type.")] - public readonly Dictionary Versus = []; + public readonly FrozenDictionary Versus = FrozenDictionary.Empty; public override bool IsValidAgainst(Actor victim, Actor firedBy) { diff --git a/OpenRA.Mods.Common/Warheads/DestroyResourceWarhead.cs b/OpenRA.Mods.Common/Warheads/DestroyResourceWarhead.cs index 326f65c8ba..887bdc4c54 100644 --- a/OpenRA.Mods.Common/Warheads/DestroyResourceWarhead.cs +++ b/OpenRA.Mods.Common/Warheads/DestroyResourceWarhead.cs @@ -9,7 +9,8 @@ */ #endregion -using System.Collections.Generic; +using System.Collections.Frozen; +using System.Collections.Immutable; using OpenRA.GameRules; using OpenRA.Mods.Common.Traits; using OpenRA.Traits; @@ -20,13 +21,13 @@ namespace OpenRA.Mods.Common.Warheads public class DestroyResourceWarhead : Warhead { [Desc("Size of the area. The resources are removed within this area.", "Provide 2 values for a ring effect (outer/inner).")] - public readonly int[] Size = [0, 0]; + public readonly ImmutableArray Size = [0, 0]; [Desc("Amount of resources to be removed. If zero, all resources within the area will be removed.")] public readonly byte ResourceAmount = 0; [Desc("Resource types to remove with this warhead.", "If empty, all resource types will be removed.")] - public readonly HashSet ResourceTypes = []; + public readonly FrozenSet ResourceTypes = FrozenSet.Empty; public override void DoImpact(in Target target, WarheadArgs args) { diff --git a/OpenRA.Mods.Common/Warheads/LeaveSmudgeWarhead.cs b/OpenRA.Mods.Common/Warheads/LeaveSmudgeWarhead.cs index 623e093681..c6b5e88381 100644 --- a/OpenRA.Mods.Common/Warheads/LeaveSmudgeWarhead.cs +++ b/OpenRA.Mods.Common/Warheads/LeaveSmudgeWarhead.cs @@ -10,7 +10,8 @@ #endregion using System; -using System.Collections.Generic; +using System.Collections.Frozen; +using System.Collections.Immutable; using System.Linq; using OpenRA.GameRules; using OpenRA.Mods.Common.Traits; @@ -22,10 +23,10 @@ namespace OpenRA.Mods.Common.Warheads public class LeaveSmudgeWarhead : Warhead { [Desc("Size of the area. A smudge will be created in each tile.", "Provide 2 values for a ring effect (outer/inner).")] - public readonly int[] Size = [0, 0]; + public readonly ImmutableArray Size = [0, 0]; [Desc("Type of smudge to apply to terrain.")] - public readonly HashSet SmudgeType = []; + public readonly FrozenSet SmudgeType = FrozenSet.Empty; [Desc("Percentage chance the smudge is created.")] public readonly int Chance = 100; diff --git a/OpenRA.Mods.Common/Warheads/SpreadDamageWarhead.cs b/OpenRA.Mods.Common/Warheads/SpreadDamageWarhead.cs index 0ebb258168..573e5231fb 100644 --- a/OpenRA.Mods.Common/Warheads/SpreadDamageWarhead.cs +++ b/OpenRA.Mods.Common/Warheads/SpreadDamageWarhead.cs @@ -9,6 +9,7 @@ */ #endregion +using System.Collections.Immutable; using System.Linq; using OpenRA.GameRules; using OpenRA.Mods.Common.Traits; @@ -25,15 +26,15 @@ namespace OpenRA.Mods.Common.Warheads public readonly WDist Spread = new(43); [Desc("Damage percentage at each range step")] - public readonly int[] Falloff = [100, 37, 14, 5, 0]; + public readonly ImmutableArray Falloff = [100, 37, 14, 5, 0]; [Desc("Ranges at which each Falloff step is defined. Overrides Spread.")] - public readonly WDist[] Range = null; + public readonly ImmutableArray Range = default; [Desc("Controls the way damage is calculated. Possible values are 'HitShape', 'ClosestTargetablePosition' and 'CenterPosition'.")] public readonly DamageCalculationType DamageCalculationType = DamageCalculationType.HitShape; - WDist[] effectiveRange; + ImmutableArray effectiveRange; void IRulesetLoaded.RulesetLoaded(Ruleset rules, WeaponInfo info) { @@ -49,7 +50,7 @@ namespace OpenRA.Mods.Common.Warheads effectiveRange = Range; } else - effectiveRange = Exts.MakeArray(Falloff.Length, i => i * Spread); + effectiveRange = Exts.MakeArray(Falloff.Length, i => i * Spread).ToImmutableArray(); } protected override void DoImpact(WPos pos, Actor firedBy, WarheadArgs args) diff --git a/OpenRA.Mods.Common/Widgets/Logic/ColorPickerLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/ColorPickerLogic.cs index 70ce972ab0..574b016a5b 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/ColorPickerLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/ColorPickerLogic.cs @@ -11,6 +11,7 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using OpenRA.Mods.Common.Traits; using OpenRA.Primitives; @@ -168,7 +169,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic .Where(c => c != mixer.Color) .Append(mixer.Color) .Reverse().Take(paletteCustomRows * paletteCols).Reverse() - .ToArray(); + .ToImmutableArray(); Game.Settings.Save(); // Flash the palette tab to show players that something has happened diff --git a/OpenRA.Mods.Common/Widgets/Logic/Editor/ActorSelectorLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/Editor/ActorSelectorLogic.cs index 4e1a9a5233..1cb6555278 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/Editor/ActorSelectorLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/Editor/ActorSelectorLogic.cs @@ -11,6 +11,7 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using OpenRA.Graphics; using OpenRA.Mods.Common.Traits; @@ -25,7 +26,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic [FluentReference("actorType")] const string ActorTypeTooltip = "label-actor-type"; - sealed record ActorSelectorActor(ActorInfo Actor, string[] Categories, string[] SearchTerms, string Tooltip); + sealed record ActorSelectorActor(ActorInfo Actor, ImmutableArray Categories, string[] SearchTerms, string Tooltip); readonly DropDownButtonWidget ownersDropDown; readonly Ruleset mapRules; diff --git a/OpenRA.Mods.Common/Widgets/Logic/Editor/MapGeneratorToolLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/Editor/MapGeneratorToolLogic.cs index 211b745208..58b063c724 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/Editor/MapGeneratorToolLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/Editor/MapGeneratorToolLogic.cs @@ -184,7 +184,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic { var validChoices = mo.ValidChoices(world.Map.Rules.TerrainInfo, playerCount); if (!validChoices.Contains(mo.Value)) - mo.Value = mo.Default?.FirstOrDefault(validChoices.Contains) ?? validChoices.FirstOrDefault(); + mo.Value = mo.Default != null ? mo.Default.FirstOrDefault(validChoices.Contains) : validChoices.FirstOrDefault(); if (mo.Value != null && mo.Label != null && validChoices.Count > 0) { diff --git a/OpenRA.Mods.Common/Widgets/Logic/Editor/TileSelectorLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/Editor/TileSelectorLogic.cs index 239810c9db..1a5ab98701 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/Editor/TileSelectorLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/Editor/TileSelectorLogic.cs @@ -10,6 +10,7 @@ #endregion using System; +using System.Collections.Immutable; using System.Globalization; using System.IO; using System.Linq; @@ -28,7 +29,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic sealed class TileSelectorTemplate { public readonly TerrainTemplateInfo Template; - public readonly string[] Categories; + public readonly ImmutableArray Categories; public readonly string[] SearchTerms; public readonly string Tooltip; diff --git a/OpenRA.Mods.Common/Widgets/Logic/Ingame/IngameMenuLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/Ingame/IngameMenuLogic.cs index fe61eaf2c5..3849a34f39 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/Ingame/IngameMenuLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/Ingame/IngameMenuLogic.cs @@ -11,6 +11,7 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using OpenRA.Graphics; using OpenRA.Mods.Common.Scripting; @@ -200,7 +201,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic if (logicArgs.TryGetValue("Buttons", out var buttonsNode)) { - var buttonIds = FieldLoader.GetValue("Buttons", buttonsNode.Value); + var buttonIds = FieldLoader.GetValue>("Buttons", buttonsNode.Value); foreach (var button in buttonIds) if (buttonHandlers.TryGetValue(button, out var createHandler)) createHandler(); diff --git a/OpenRA.Mods.Common/Widgets/Logic/Installation/InstallFromSourceLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/Installation/InstallFromSourceLogic.cs index 125c76c2e0..397308a19f 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/Installation/InstallFromSourceLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/Installation/InstallFromSourceLogic.cs @@ -178,7 +178,8 @@ namespace OpenRA.Mods.Common.Widgets.Logic { Log.Write("install", $"Using installer `{kv.Key}: {kv.Value.Title}` of type `{kv.Value.Type.Value}`:"); - availablePackages = content.Packages.Values + availablePackages = content.Packages + .Select(kvp => kvp.Value) .Where(p => p.Sources.Contains(kv.Key) && !p.IsInstalled()) .ToArray(); @@ -198,7 +199,8 @@ namespace OpenRA.Mods.Common.Widgets.Logic } } - var missingSources = content.Packages.Values + var missingSources = content.Packages + .Select(kvp => kvp.Value) .Where(p => !p.IsInstalled()) .SelectMany(p => p.Sources) .Select(d => sources[d]); diff --git a/OpenRA.Mods.Common/Widgets/Logic/Installation/ModContentLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/Installation/ModContentLogic.cs index 1ea51f3848..37ec4290f4 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/Installation/ModContentLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/Installation/ModContentLogic.cs @@ -165,7 +165,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic scrollPanel.AddChild(container); } - sourceAvailable = content.Packages.Values.Any(p => p.Sources.Length > 0 && !p.IsInstalled()); + sourceAvailable = content.Packages.Select(kvp => kvp.Value).Any(p => p.Sources.Length > 0 && !p.IsInstalled()); } } } diff --git a/OpenRA.Mods.Common/Widgets/Logic/Lobby/MapPreviewLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/Lobby/MapPreviewLogic.cs index 9583c02dff..6aafd6b195 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/Lobby/MapPreviewLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/Lobby/MapPreviewLogic.cs @@ -11,6 +11,7 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using OpenRA.Network; using OpenRA.Primitives; @@ -67,7 +68,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic [ObjectCreator.UseCtor] internal MapPreviewLogic(Widget widget, ModData modData, Func<(MapPreview Map, Session.MapStatus Status)> getMap, Action onMouseDown, Func> getSpawnOccupants, - bool mapUpdatesEnabled, Action onMapUpdate, Func> getDisabledSpawnPoints, bool showUnoccupiedSpawnpoints) + bool mapUpdatesEnabled, Action onMapUpdate, Func> getDisabledSpawnPoints, bool showUnoccupiedSpawnpoints) { this.getMap = getMap; @@ -112,7 +113,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic Widget SetupAuthorAndMapType(Widget parent) { var typeLabel = parent.Get("MAP_TYPE"); - var typeCache = new CachedTransform(c => c.FirstOrDefault() ?? ""); + var typeCache = new CachedTransform, string>(c => c.FirstOrDefault() ?? ""); typeLabel.GetText = () => typeCache.Update(getMap().Map.Categories); diff --git a/OpenRA.Mods.Common/Widgets/Logic/Lobby/SpawnSelectorTooltipLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/Lobby/SpawnSelectorTooltipLogic.cs index 43b121aac3..0ccd56482c 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/Lobby/SpawnSelectorTooltipLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/Lobby/SpawnSelectorTooltipLogic.cs @@ -10,7 +10,6 @@ #endregion using System; -using System.Linq; using OpenRA.Primitives; using OpenRA.Widgets; diff --git a/OpenRA.Mods.Common/Widgets/Logic/MapChooserLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/MapChooserLogic.cs index 22e4e2a2b2..2fcd7af650 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/MapChooserLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/MapChooserLogic.cs @@ -10,6 +10,7 @@ #endregion using System; +using System.Collections.Frozen; using System.Collections.Generic; using System.Linq; using OpenRA.FileSystem; @@ -112,7 +113,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic readonly Widget widget; readonly DropDownButtonWidget gameModeDropdown; readonly ModData modData; - readonly HashSet remoteMapPool; + readonly FrozenSet remoteMapPool; readonly ScrollItemWidget itemTemplate; readonly MapVisibility filter; @@ -138,7 +139,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic Func orderByFunc; [ObjectCreator.UseCtor] - internal MapChooserLogic(Widget widget, ModData modData, string initialMap, MapGenerationArgs initialGeneratedMap, HashSet remoteMapPool, + internal MapChooserLogic(Widget widget, ModData modData, string initialMap, MapGenerationArgs initialGeneratedMap, FrozenSet remoteMapPool, MapClassification initialTab, Action onExit, Action onSelect, Action onSelectGenerated, MapVisibility filter) { this.widget = widget; diff --git a/OpenRA.Mods.Common/Widgets/Logic/MapGeneratorLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/MapGeneratorLogic.cs index 1346fa0721..31e4c6c83e 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/MapGeneratorLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/MapGeneratorLogic.cs @@ -348,7 +348,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic { var validChoices = mo.ValidChoices(selectedTerrain, playerCount); if (!validChoices.Contains(mo.Value)) - mo.Value = mo.Default?.FirstOrDefault(validChoices.Contains) ?? validChoices.FirstOrDefault(); + mo.Value = mo.Default != null ? mo.Default.FirstOrDefault(validChoices.Contains) : validChoices.FirstOrDefault(); if (mo.Label != null && validChoices.Count > 0) { diff --git a/OpenRA.Mods.Common/Widgets/Logic/ReplayBrowserLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/ReplayBrowserLogic.cs index a6ab52650b..f52ee364c6 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/ReplayBrowserLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/ReplayBrowserLogic.cs @@ -11,6 +11,7 @@ using System; using System.Collections.Concurrent; +using System.Collections.Frozen; using System.Collections.Generic; using System.IO; using System.Linq; @@ -167,8 +168,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic return occupants; }); - var noSpawns = new HashSet(); - var disabledSpawnPoints = new CachedTransform>(r => r.GameInfo.DisabledSpawnPoints ?? noSpawns); + var disabledSpawnPoints = new CachedTransform>(r => r.GameInfo.DisabledSpawnPoints ?? FrozenSet.Empty); Ui.LoadWidget("MAP_PREVIEW", mapPreviewRoot, new WidgetArgs { @@ -176,7 +176,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic { "getMap", (Func<(MapPreview, Session.MapStatus)>)(() => (map, Session.MapStatus.Playable)) }, { "onMouseDown", null }, { "getSpawnOccupants", (Func>)(() => spawnOccupants.Update(selectedReplay)) }, - { "getDisabledSpawnPoints", (Func>)(() => disabledSpawnPoints.Update(selectedReplay)) }, + { "getDisabledSpawnPoints", (Func>)(() => disabledSpawnPoints.Update(selectedReplay)) }, { "showUnoccupiedSpawnpoints", false }, { "mapUpdatesEnabled", false }, { "onMapUpdate", (Action)(_ => { }) }, diff --git a/OpenRA.Mods.Common/Widgets/Logic/Settings/HotkeysSettingsLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/Settings/HotkeysSettingsLogic.cs index 9daf35b2c4..afdc7aeb08 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/Settings/HotkeysSettingsLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/Settings/HotkeysSettingsLogic.cs @@ -10,6 +10,7 @@ #endregion using System; +using System.Collections.Frozen; using System.Collections.Generic; using System.Linq; using OpenRA.Primitives; @@ -51,7 +52,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic string currentContext = AnyContext; readonly HashSet contexts = [AnyContext]; - readonly Dictionary> hotkeyGroups = []; + readonly Dictionary> hotkeyGroups = []; TextFieldWidget filterInput; Widget headerTemplate; @@ -155,7 +156,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic { var typesNode = hg.Value.NodeWithKeyOrDefault("Types"); if (typesNode != null) - hotkeyGroups.Add(hg.Key, FieldLoader.GetValue>("Types", typesNode.Value.Value)); + hotkeyGroups.Add(hg.Key, FieldLoader.GetValue>("Types", typesNode.Value.Value)); } InitHotkeyRemapDialog(panel); diff --git a/OpenRA.Mods.Common/Widgets/MapPreviewWidget.cs b/OpenRA.Mods.Common/Widgets/MapPreviewWidget.cs index 1958c465f8..022ef74501 100644 --- a/OpenRA.Mods.Common/Widgets/MapPreviewWidget.cs +++ b/OpenRA.Mods.Common/Widgets/MapPreviewWidget.cs @@ -10,8 +10,8 @@ #endregion using System; +using System.Collections.Frozen; using System.Collections.Generic; -using System.Linq; using OpenRA.Graphics; using OpenRA.Network; using OpenRA.Primitives; @@ -57,8 +57,6 @@ namespace OpenRA.Mods.Common.Widgets public class MapPreviewWidget : Widget { - static readonly int[] NoDisabledSpawnPoints = []; - public readonly bool IgnoreMouseInput = false; public readonly bool ShowSpawnPoints = true; @@ -73,7 +71,7 @@ namespace OpenRA.Mods.Common.Widgets public Func Preview = () => null; public Func> SpawnOccupants = () => []; - public Func> DisabledSpawnPoints = () => NoDisabledSpawnPoints; + public Func> DisabledSpawnPoints = () => FrozenSet.Empty; public Action OnMouseDown = _ => { }; public int TooltipSpawnIndex = -1; public bool ShowUnoccupiedSpawnpoints = true; diff --git a/OpenRA.Mods.D2k/Projectiles/SonicBlast.cs b/OpenRA.Mods.D2k/Projectiles/SonicBlast.cs index 11de6d1538..5f72bed58f 100644 --- a/OpenRA.Mods.D2k/Projectiles/SonicBlast.cs +++ b/OpenRA.Mods.D2k/Projectiles/SonicBlast.cs @@ -10,6 +10,7 @@ #endregion using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using OpenRA.GameRules; using OpenRA.Graphics; @@ -25,7 +26,7 @@ namespace OpenRA.Mods.D2k.Projectiles public class SonicBlastInfo : IProjectileInfo { [Desc("Projectile speed in WDist / tick, two values indicate a randomly picked velocity per blast.")] - public readonly WDist[] Speed = [new(128)]; + public readonly ImmutableArray Speed = [new(128)]; [Desc("The number of ticks between the blast causing warhead impacts in its area of effect.")] public readonly int DamageInterval = 1; @@ -37,10 +38,10 @@ namespace OpenRA.Mods.D2k.Projectiles public readonly WDist Width = new(650); [Desc("Damage modifier applied at each range step.")] - public readonly int[] Falloff = [100, 100]; + public readonly ImmutableArray Falloff = [100, 100]; [Desc("Ranges at which each Falloff step is defined.")] - public readonly WDist[] Range = [WDist.Zero, new(int.MaxValue)]; + public readonly ImmutableArray Range = [WDist.Zero, new(int.MaxValue)]; [Desc("The maximum/constant/incremental inaccuracy used in conjunction with the InaccuracyType property.")] public readonly WDist Inaccuracy = WDist.Zero; diff --git a/OpenRA.Mods.D2k/SpriteLoaders/R8Loader.cs b/OpenRA.Mods.D2k/SpriteLoaders/R8Loader.cs index c91ec28101..0c997ad4c8 100644 --- a/OpenRA.Mods.D2k/SpriteLoaders/R8Loader.cs +++ b/OpenRA.Mods.D2k/SpriteLoaders/R8Loader.cs @@ -11,6 +11,7 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.IO; using System.Linq; using System.Runtime.InteropServices; @@ -70,7 +71,7 @@ namespace OpenRA.Mods.D2k.SpriteLoaders if (remap != default) { - var r = new PlayerColorRemap(Enumerable.Range(240, 16).ToArray(), remap); + var r = new PlayerColorRemap(Enumerable.Range(240, 16).ToImmutableArray(), remap); for (var i = 240; i < 256; i++) palette[i] = r.GetRemappedColor(Color.FromArgb(palette[i]), i).ToArgb(); } diff --git a/OpenRA.Mods.D2k/Traits/AttractsWorms.cs b/OpenRA.Mods.D2k/Traits/AttractsWorms.cs index f0c690355f..42789ff18e 100644 --- a/OpenRA.Mods.D2k/Traits/AttractsWorms.cs +++ b/OpenRA.Mods.D2k/Traits/AttractsWorms.cs @@ -9,6 +9,7 @@ */ #endregion +using System.Collections.Immutable; using OpenRA.Mods.Common.Traits; namespace OpenRA.Mods.D2k.Traits @@ -20,13 +21,13 @@ namespace OpenRA.Mods.D2k.Traits public readonly int Intensity = 0; [Desc("Noise percentage at Range step away from the actor.")] - public readonly int[] Falloff = [100, 100, 25, 11, 6, 4, 3, 2, 1, 0]; + public readonly ImmutableArray Falloff = [100, 100, 25, 11, 6, 4, 3, 2, 1, 0]; [Desc("Range between falloff steps.")] public readonly WDist Spread = new(3072); [Desc("Ranges at which each Falloff step is defined. Overrides Spread.")] - public readonly WDist[] Range = null; + public readonly ImmutableArray Range = default; public override object Create(ActorInitializer init) { return new AttractsWorms(init, this); } } @@ -34,14 +35,14 @@ namespace OpenRA.Mods.D2k.Traits public class AttractsWorms : ConditionalTrait { readonly Actor self; - readonly WDist[] effectiveRange; + readonly ImmutableArray effectiveRange; public AttractsWorms(ActorInitializer init, AttractsWormsInfo info) : base(info) { self = init.Self; - effectiveRange = info.Range ?? Exts.MakeArray(info.Falloff.Length, i => i * info.Spread); + effectiveRange = info.Range != null ? info.Range : Exts.MakeArray(info.Falloff.Length, i => i * info.Spread).ToImmutableArray(); } int GetNoisePercentageAtDistance(int distance) diff --git a/OpenRA.Mods.D2k/Traits/Buildings/D2kActorPreviewPlaceBuildingPreview.cs b/OpenRA.Mods.D2k/Traits/Buildings/D2kActorPreviewPlaceBuildingPreview.cs index ddf55ebd67..4dc89bb045 100644 --- a/OpenRA.Mods.D2k/Traits/Buildings/D2kActorPreviewPlaceBuildingPreview.cs +++ b/OpenRA.Mods.D2k/Traits/Buildings/D2kActorPreviewPlaceBuildingPreview.cs @@ -9,7 +9,9 @@ */ #endregion +using System.Collections.Frozen; using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using OpenRA.Graphics; using OpenRA.Mods.Common.Orders; @@ -23,10 +25,10 @@ namespace OpenRA.Mods.D2k.Traits public class D2kActorPreviewPlaceBuildingPreviewInfo : ActorPreviewPlaceBuildingPreviewInfo { [Desc("Terrain types that should show the 'unsafe' footprint tile.")] - public readonly HashSet UnsafeTerrainTypes = ["Rock"]; + public readonly FrozenSet UnsafeTerrainTypes = new HashSet { "Rock" }.ToFrozenSet(); [Desc("Only check for 'unsafe' footprint tiles when you have these prerequisites.")] - public readonly string[] RequiresPrerequisites = []; + public readonly ImmutableArray RequiresPrerequisites = []; [Desc("Sprite image to use for the overlay.")] public readonly string Image = "overlay"; diff --git a/OpenRA.Mods.D2k/Traits/Buildings/D2kBuilding.cs b/OpenRA.Mods.D2k/Traits/Buildings/D2kBuilding.cs index ca892e6e78..dd90c8eeab 100644 --- a/OpenRA.Mods.D2k/Traits/Buildings/D2kBuilding.cs +++ b/OpenRA.Mods.D2k/Traits/Buildings/D2kBuilding.cs @@ -9,7 +9,7 @@ */ #endregion -using System; +using System.Collections.Immutable; using System.IO; using System.Linq; using OpenRA.Mods.Common.Terrain; @@ -31,7 +31,7 @@ namespace OpenRA.Mods.D2k.Traits.Buildings public readonly BitSet DamageTypes = default; [Desc("Terrain types where the actor will take damage.")] - public readonly string[] DamageTerrainTypes = ["Rock"]; + public readonly ImmutableArray DamageTerrainTypes = ["Rock"]; [Desc("Percentage health below which the actor will not receive further damage.")] public readonly int DamageThreshold = 50; @@ -44,7 +44,7 @@ namespace OpenRA.Mods.D2k.Traits.Buildings public readonly ushort ConcreteTemplate = 88; [Desc("List of required prerequisites to place a terrain template.")] - public readonly string[] ConcretePrerequisites = []; + public readonly ImmutableArray ConcretePrerequisites = []; public override object Create(ActorInitializer init) { return new D2kBuilding(init, this); } } diff --git a/OpenRA.Mods.D2k/Traits/SpiceBloom.cs b/OpenRA.Mods.D2k/Traits/SpiceBloom.cs index 3379c921d9..9c834d6cf5 100644 --- a/OpenRA.Mods.D2k/Traits/SpiceBloom.cs +++ b/OpenRA.Mods.D2k/Traits/SpiceBloom.cs @@ -9,7 +9,9 @@ */ #endregion +using System.Collections.Frozen; using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using OpenRA.Effects; using OpenRA.GameRules; @@ -25,28 +27,28 @@ namespace OpenRA.Mods.D2k.Traits public class SpiceBloomInfo : TraitInfo, IRenderActorPreviewSpritesInfo, Requires { [SequenceReference] - public readonly string[] GrowthSequences = ["grow1", "grow2", "grow3"]; + public readonly ImmutableArray GrowthSequences = ["grow1", "grow2", "grow3"]; [SequenceReference] public readonly string SpurtSequence = "spurt"; [Desc("The range of time (in ticks) that the spicebloom will take to grow until it blows up.")] - public readonly int[] Lifetime = [2000, 3000]; + public readonly ImmutableArray Lifetime = [2000, 3000]; public readonly string ResourceType = "Spice"; [Desc("Spice blooms only grow on these terrain types.")] - public readonly HashSet GrowthTerrainTypes = []; + public readonly FrozenSet GrowthTerrainTypes = FrozenSet.Empty; [Desc("The weapon to use for spice creation.")] [WeaponReference] public readonly string Weapon = null; [Desc("The number of times to fire Weapon at the minimum and maximum actor age.")] - public readonly int[] Bursts = [4, 12]; + public readonly ImmutableArray Bursts = [4, 12]; [Desc("The minimum and maximum distance in cells that spice may be expelled.")] - public readonly int[] Range = [3, 5]; + public readonly ImmutableArray Range = [3, 5]; [Desc("Delay between each burst. (in Ticks)")] public readonly int BurstInterval = 1; diff --git a/OpenRA.Mods.D2k/Traits/World/D2kMapGenerator.cs b/OpenRA.Mods.D2k/Traits/World/D2kMapGenerator.cs index 65222c7fd4..db8bcf869a 100644 --- a/OpenRA.Mods.D2k/Traits/World/D2kMapGenerator.cs +++ b/OpenRA.Mods.D2k/Traits/World/D2kMapGenerator.cs @@ -10,6 +10,7 @@ #endregion using System; +using System.Collections.Frozen; using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; @@ -34,7 +35,7 @@ namespace OpenRA.Mods.D2k.Traits [FieldLoader.Require] [Desc("Tilesets that are compatible with this map generator.")] - public readonly string[] Tilesets = null; + public readonly ImmutableArray Tilesets = default; [FluentReference] [Desc("The title to use for generated maps.")] @@ -46,7 +47,7 @@ namespace OpenRA.Mods.D2k.Traits // This is purely of interest to the linter. [FieldLoader.LoadUsing(nameof(FluentReferencesLoader))] [FluentReference] - public readonly List FluentReferences = null; + public readonly ImmutableArray FluentReferences = default; [FieldLoader.LoadUsing(nameof(SettingsLoader))] public readonly MiniYaml Settings; @@ -54,17 +55,17 @@ namespace OpenRA.Mods.D2k.Traits string IMapGeneratorInfo.Type => Type; string IMapGeneratorInfo.Name => Name; string IMapGeneratorInfo.MapTitle => MapTitle; - string[] IEditorMapGeneratorInfo.Tilesets => Tilesets; + ImmutableArray IEditorMapGeneratorInfo.Tilesets => Tilesets; static MiniYaml SettingsLoader(MiniYaml my) { return my.NodeWithKey("Settings").Value; } - static List FluentReferencesLoader(MiniYaml my) + static object FluentReferencesLoader(MiniYaml my) { return new MapGeneratorSettings(null, my.NodeWithKey("Settings").Value) - .Options.SelectMany(o => o.GetFluentReferences()).ToList(); + .Options.SelectMany(o => o.GetFluentReferences()).ToImmutableArray(); } const int FractionMax = Terraformer.FractionMax; @@ -217,7 +218,7 @@ namespace OpenRA.Mods.D2k.Traits return my.NodeWithKey(key).Value.Value .Split(',', StringSplitOptions.RemoveEmptyEntries) .Select(terrainInfo.GetTerrainIndex) - .ToImmutableHashSet(); + .ToFrozenSet(); } var resourceTypes = map.Rules.Actors[SystemActors.World].TraitInfoOrDefault().ResourceTypes; diff --git a/OpenRA.Platforms.Default/Shader.cs b/OpenRA.Platforms.Default/Shader.cs index 88d8a05ed9..ab4ade173a 100644 --- a/OpenRA.Platforms.Default/Shader.cs +++ b/OpenRA.Platforms.Default/Shader.cs @@ -217,13 +217,13 @@ namespace OpenRA.Platforms.Default OpenGL.CheckGLError(); } - public void SetVec(string name, float[] vec, int length) + public void SetVec(string name, ReadOnlyMemory vec, int length) { VerifyThreadAffinity(); var param = uniformCache[name]; unsafe { - fixed (float* pVec = vec) + fixed (float* pVec = vec.Span) { var ptr = new IntPtr(pVec); switch (length) diff --git a/OpenRA.Platforms.Default/ThreadedGraphicsContext.cs b/OpenRA.Platforms.Default/ThreadedGraphicsContext.cs index 8279293277..5c4d1ad133 100644 --- a/OpenRA.Platforms.Default/ThreadedGraphicsContext.cs +++ b/OpenRA.Platforms.Default/ThreadedGraphicsContext.cs @@ -815,7 +815,7 @@ namespace OpenRA.Platforms.Default device.Post(setVec1, (name, x)); } - public void SetVec(string name, float[] vec, int length) + public void SetVec(string name, ReadOnlyMemory vec, int length) { device.Post(setVec2, (name, vec, length)); } diff --git a/OpenRA.Server/Program.cs b/OpenRA.Server/Program.cs index a47de057ae..605c84ac35 100644 --- a/OpenRA.Server/Program.cs +++ b/OpenRA.Server/Program.cs @@ -11,6 +11,7 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Globalization; using System.IO; using System.Net; @@ -82,7 +83,7 @@ namespace OpenRA.Server var envModSearchPaths = Environment.GetEnvironmentVariable("MOD_SEARCH_PATHS"); var modSearchPaths = !string.IsNullOrWhiteSpace(envModSearchPaths) ? - FieldLoader.GetValue("MOD_SEARCH_PATHS", envModSearchPaths) : + FieldLoader.GetValue>("MOD_SEARCH_PATHS", envModSearchPaths) : [Path.Combine(Platform.EngineDir, "mods")]; var mods = new InstalledMods(modSearchPaths, explicitModPaths); diff --git a/OpenRA.Utility/Program.cs b/OpenRA.Utility/Program.cs index 42cbd0ef96..0b6e4c3c67 100644 --- a/OpenRA.Utility/Program.cs +++ b/OpenRA.Utility/Program.cs @@ -11,6 +11,7 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.IO; using System.Linq; @@ -62,7 +63,7 @@ namespace OpenRA var envModSearchPaths = Environment.GetEnvironmentVariable("MOD_SEARCH_PATHS"); var modSearchPaths = !string.IsNullOrWhiteSpace(envModSearchPaths) ? - FieldLoader.GetValue("MOD_SEARCH_PATHS", envModSearchPaths) : + FieldLoader.GetValue>("MOD_SEARCH_PATHS", envModSearchPaths) : [Path.Combine(Platform.EngineDir, "mods")]; if (args.Length == 0)