Paradox.Modding.Stellaris.psm1

function ConvertTo-PdsBuilding {
    <#
    .SYNOPSIS
        Converts building configuration files into building mod files.
     
    .DESCRIPTION
        Converts building configuration files into building mod files.
        This command is used to read psd1-based configuration files for buildings and convert them into the format Stellaris expects.
        Generally, this command needs not be called directly and happens automatically during Build-PdxMod.
 
        For more details on how to define buildings via configuration file, see:
        https://github.com/FriedrichWeinmann/Paradox.Modding.Stellaris/blob/master/docs/content/buildings.md
     
    .PARAMETER Path
        Path to the file(s) to build.
     
    .PARAMETER ModRoot
        Root path of the mod you are building.
        Defaults to the parent folder of the parent folder of the first file specified in -Path.
     
    .EXAMPLE
        PS C:\> ConvertTo-PdsBuilding -Path "$PSScriptRoot\common\buildings\*.psd1"
 
        Builds all .psd1 files in the common\buildings subfolder under the path the current script is placed.
     
    .LINK
        https://github.com/FriedrichWeinmann/Paradox.Modding.Stellaris/blob/master/docs/content/buildings.md
    #>

    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        [Alias('FullName')]
        [PSFFile]
        $Path,

        [string]
        $ModRoot
    )
    begin {
        $commonProps = [ordered]@{
            Capital         = 'capital'
            CanBuild        = 'can_build'
            CanDemolish     = 'can_demolish'
            CanRuin         = 'can_be_ruined'
            CanDisable      = 'can_be_disabled'
            Position        = 'position_priority'
            BuildTime       = 'base_buildtime'
            EmpireLimit     = 'empire_limit'
            CanBeRuined     = 'can_be_ruined'
            Category        = 'category'
            BuildingSets    = 'building_sets'
            Prerequisites   = 'prerequisites'
            UpgradeTo       = 'upgrades'
            Upgrades        = 'upgrades'
            Potential       = 'potential'
            Allow           = 'allow'
            PlanetModifier  = 'planet_modifier'
            CountryModifier = 'country_modifier'
        }

        $localeMap = @{
            Name        = '{0}'
            Description = '{0}_desc'
        }

        $typeMap = @{
            upgrades      = 'array'
            prerequisites = 'array'
            building_sets = 'array'
        }
    }
    process {
        foreach ($filePath in $Path) {
            if (-not $ModRoot) {
                $ModRoot = Split-Path -Path (Split-Path -Path (Split-Path -Path $filePath))
            }

            $strings = New-PdxLocalizedString
            $data = Import-PSFPowerShellDataFile -LiteralPath $filePath -Psd1Mode Unsafe
            $allBuildings = [ordered]@{}

            #region Build Edict Data
            foreach ($buildingName in $data.Buildings.Keys) {
                $buildingData = $data.Buildings.$buildingName

                $newBuilding = [ordered]@{}

                #region Cost & Upkeep
                $currentCost = $data.Core.Cost
                if ($buildingData.Cost) { $currentCost = $buildingData.Cost }
                $currentUpkeep = $data.Core.Upkeep
                if ($buildingData.Upkeep) { $currentUpkeep = $buildingData.Upkeep }

                if ($currentCost -or $currentUpkeep) {
                    $resourceData = [ordered]@{
                        category = 'planet_buildings'
                    }
                    if ($currentCost) {
                        if ($currentCost -is [int]) {
                            $resourceData['cost'] = @{ minerals = $currentCost }
                        }
                        else {
                            $resourceData['cost'] = $currentCost
                        }
                    }
                    if ($currentUpkeep) {
                        if ($currentUpkeep -is [int]) {
                            $resourceData['upkeep'] = @{ energy = $currentUpkeep }
                        }
                        else {
                            $resourceData['upkeep'] = $currentUpkeep
                        }
                    }
                    $newBuilding['resources'] = $resourceData
                }
                #endregion Cost & Upkeep

                $allBuildings[$buildingName] = New-PdxConfigEntry -Entry $buildingData -Name $buildingName -Defaults $data.Core -Common $commonProps -Output $newBuilding -Strings $strings -LocalizationProperties $localeMap -Ignore Cost, Upkeep -TypeMap $typeMap
            }
            #endregion Build Edict Data

            # Write File
            $sourceFile = Get-Item -LiteralPath $filePath
            $exportFile = Join-Path -Path $sourceFile.DirectoryName -ChildPath "$($sourceFile.BaseName).txt"
            $allBuildings | ConvertTo-PdxConfigFormat -TopLevel | Set-Content -Path $exportFile

            # Write Localization
            Export-PdxLocalizedString -Strings $strings -ModRoot $ModRoot -Name "buildings_$($sourceFile.BaseName)"
        }
    }
}

function ConvertTo-PdsDistrict {
    <#
    .SYNOPSIS
        Converts district configuration files into district mod files.
     
    .DESCRIPTION
        Converts district configuration files into district mod files.
        This command is used to read psd1-based configuration files for districts and convert them into the format Stellaris expects.
        Generally, this command needs not be called directly and happens automatically during Build-PdxMod.
 
        For more details on how to define districts via configuration file, see:
        https://github.com/FriedrichWeinmann/Paradox.Modding.Stellaris/blob/master/docs/content/districts.md
     
    .PARAMETER Path
        Path to the file(s) to build.
     
    .PARAMETER ModRoot
        Root path of the mod you are building.
        Defaults to the parent folder of the parent folder of the first file specified in -Path.
     
    .EXAMPLE
        PS C:\> ConvertTo-PdsDistrict -Path "$PSScriptRoot\common\districts\*.psd1"
 
        Builds all .psd1 files in the common\districts subfolder under the path the current script is placed.
     
    .LINK
        https://github.com/FriedrichWeinmann/Paradox.Modding.Stellaris/blob/master/docs/content/districts.md
    #>

    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        [Alias('FullName')]
        [PSFFile]
        $Path,

        [string]
        $ModRoot
    )
    begin {
        $commonProps = [ordered]@{
            BuildTime         = 'base_buildtime'
            MinDeposits       = 'min_for_deposits_on_planet'
            MaxDeposits       = 'max_for_deposits_on_planet'
            Capped            = 'is_capped_by_modifier'
            Exempt            = 'exempt_from_ai_planet_specialization'
            Default           = 'default_starting_district'
            Slots             = 'zone_slots'
            ShowOnUncolonized = 'show_on_uncolonized'
            Potential         = 'potential'
            Allow             = 'allow'
            ConversionRatio   = 'conversion_ratio' # integer
            ConvertTo         = 'convert_to' # array
            Resources         = 'resources'
            PlanetModifier    = 'planet_modifier'
        }

        $localeMap = @{
            Name        = '{0}'
            Description = '{0}_desc'
        }
        $typeMap = @{
            zone_slots = 'array'
            convert_to = 'array'
        }
    }
    process {
        foreach ($filePath in $Path) {
            if (-not $ModRoot) {
                $ModRoot = Split-Path -Path (Split-Path -Path (Split-Path -Path $filePath))
            }

            $strings = New-PdxLocalizedString
            $data = Import-PSFPowerShellDataFile -LiteralPath $filePath -Psd1Mode Unsafe
            $alldistricts = [ordered]@{}

            #region Build Edict Data
            foreach ($districtName in $data.districts.Keys) {
                $districtData = $data.districts.$districtName

                $newdistrict = [ordered]@{}

                #region Cost & Upkeep
                $currentCost = $data.Core.Cost
                if ($districtData.Cost) { $currentCost = $districtData.Cost }
                $currentUpkeep = $data.Core.Upkeep
                if ($districtData.Upkeep) { $currentUpkeep = $districtData.Upkeep }

                if ($currentCost -or $currentUpkeep) {
                    $resourceData = [ordered]@{
                        category = 'planet_districts_cities'
                    }
                    if ($currentCost) {
                        if ($currentCost -is [int]) {
                            $resourceData['cost'] = @{ minerals = $currentCost }
                        }
                        else {
                            $resourceData['cost'] = $currentCost
                        }
                    }
                    if ($currentUpkeep) {
                        if ($currentUpkeep -is [int]) {
                            $resourceData['upkeep'] = @{ energy = $currentUpkeep }
                        }
                        else {
                            $resourceData['upkeep'] = $currentUpkeep
                        }
                    }
                    $newdistrict['resources'] = $resourceData
                }
                #endregion Cost & Upkeep

                $alldistricts[$districtName] = New-PdxConfigEntry -Entry $districtData -Name $districtName -Defaults $data.Core -Common $commonProps -Output $newdistrict -Strings $strings -LocalizationProperties $localeMap -Ignore Cost, Upkeep -TypeMap $typeMap
            }
            #endregion Build Edict Data

            # Write File
            $sourceFile = Get-Item -LiteralPath $filePath
            $exportFile = Join-Path -Path $sourceFile.DirectoryName -ChildPath "$($sourceFile.BaseName).txt"
            $alldistricts | ConvertTo-PdxConfigFormat -TopLevel | Set-Content -Path $exportFile

            # Write Localization
            Export-PdxLocalizedString -Strings $strings -ModRoot $ModRoot -Name "districts_$($sourceFile.BaseName)"
        }
    }
}

function ConvertTo-PdsEdict {
    <#
    .SYNOPSIS
        Converts edict configuration files into edict mod files.
     
    .DESCRIPTION
        Converts edict configuration files into edict mod files.
        This command is used to read psd1-based configuration files for edicts and convert them into the format Stellaris expects.
        Generally, this command needs not be called directly and happens automatically during Build-PdxMod.
 
        For more details on how to define edicts via configuration file, see:
        https://github.com/FriedrichWeinmann/Paradox.Modding.Stellaris/tree/master/docs/content/edicts.md
     
    .PARAMETER Path
        Path to the file(s) to build.
     
    .PARAMETER ModRoot
        Root path of the mod you are building.
        Defaults to the parent folder of the parent folder of the first file specified in -Path.
     
    .EXAMPLE
        PS C:\> ConvertTo-PdsEdict -Path "$PSScriptRoot\common\edicts\*.psd1"
 
        Builds all .psd1 files in the common\edicts subfolder under the path the current script is placed.
     
    .LINK
        https://github.com/FriedrichWeinmann/Paradox.Modding.Stellaris/tree/master/docs/content/edicts.md
    #>

    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        [Alias('FullName')]
        [PSFFile]
        $Path,

        [string]
        $ModRoot
    )
    begin {
        $commonProps = [ordered]@{
            Length = 'length'
            EdictCapUsage = 'edict_cap_usage'
            Potential = 'potential'
            Allow = 'allow'
            AIWeight = 'ai_weight'
            Modifier = 'modifier'
        }
    }
    process {
        foreach ($filePath in $Path) {
            if (-not $ModRoot) {
                $ModRoot = Split-Path -Path (Split-Path -Path (Split-Path -Path $filePath))
            }

            $strings = New-PdxLocalizedString
            $data = Import-PSFPowerShellDataFile -LiteralPath $filePath -Psd1Mode Unsafe
            $allEdicts = [ordered]@{}

            #region Build Edict Data
            foreach ($edictName in $data.Edicts.Keys) {
                $edictData = $data.Edicts.$edictName

                $newEdict = [ordered]@{}

                #region Cost
                $currentCost = $data.Core.Cost
                if ($edictData.Cost) { $currentCost = $edictData.Cost }
                if (-not $currentCost) { $currentCost = 0 }

                $costHash = @{ influence = $currentCost }
                if ($currentCost -isnot [int]) { $costhash = $currentCost }

                $newEdict['resources'] = [ordered]@{
                    category = 'edicts'
                    cost = $costHash
                }
                #endregion Cost

                $allEdicts[$edictName] = New-PdxConfigEntry -Entry $edictData -Name $edictName -Defaults $data.Core -Common $commonProps -Output $newEdict -Strings $strings -LocalizationProperties @{
                    Name = 'edict_{0}'
                    Description = 'edict_{0}_desc'
                } -Ignore Cost
            }
            #endregion Build Edict Data

            # Write File
            $sourceFile = Get-Item -LiteralPath $filePath
            $exportFile = Join-Path -Path $sourceFile.DirectoryName -ChildPath "$($sourceFile.BaseName).txt"
            $allEdicts | ConvertTo-PdxConfigFormat -TopLevel | Set-Content -Path $exportFile

            # Write Localization
            Export-PdxLocalizedString -Strings $strings -ModRoot $ModRoot -Name "edicts_$($sourceFile.BaseName)"
        }
    }
}

function ConvertTo-PdsZone {
    <#
    .SYNOPSIS
        Converts zone configuration files into zone mod files.
     
    .DESCRIPTION
        Converts zone configuration files into zone mod files.
        This command is used to read psd1-based configuration files for zones and convert them into the format Stellaris expects.
        Generally, this command needs not be called directly and happens automatically during Build-PdxMod.
 
        For more details on how to define zones via configuration file, see:
        https://github.com/FriedrichWeinmann/Paradox.Modding.Stellaris/blob/master/docs/content/zones.md
     
    .PARAMETER Path
        Path to the file(s) to build.
     
    .PARAMETER ModRoot
        Root path of the mod you are building.
        Defaults to the parent folder of the parent folder of the first file specified in -Path.
     
    .EXAMPLE
        PS C:\> ConvertTo-PdsZone -Path "$PSScriptRoot\common\zones\*.psd1"
 
        Builds all .psd1 files in the common\zones subfolder under the path the current script is placed.
     
    .LINK
        https://github.com/FriedrichWeinmann/Paradox.Modding.Stellaris/blob/master/docs/content/zones.md
    #>

    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        [Alias('FullName')]
        [PSFFile]
        $Path,

        [string]
        $ModRoot
    )
    begin {
        $commonProps = [ordered]@{
            Icon           = 'icon'
            BuildTime      = 'base_buildtime'
            Potential      = 'potential'
            Unlock         = 'unlock'
            Include        = 'include'
            Resources      = 'resources'
            MaxBuildings   = 'max_buildings'
            PlanetModifier = 'planet_modifier'
        }

        $localeMap = @{
            Name        = '{0}'
            Description = '{0}_desc'
        }
        $typeMap = @{
            include = 'array'
            exclude = 'array'
            included_building_sets = 'array'
            excluded_building_sets = 'array'
        }
    }
    process {
        foreach ($filePath in $Path) {
            if (-not $ModRoot) {
                $ModRoot = Split-Path -Path (Split-Path -Path (Split-Path -Path $filePath))
            }

            $strings = New-PdxLocalizedString
            $data = Import-PSFPowerShellDataFile -LiteralPath $filePath -Psd1Mode Unsafe
            $allzones = [ordered]@{}

            #region Build Edict Data
            foreach ($zoneName in $data.zones.Keys) {
                $zoneData = $data.zones.$zoneName

                $newzone = [ordered]@{}

                #region Cost & Upkeep
                $currentCost = $data.Core.Cost
                if ($zoneData.Cost) { $currentCost = $zoneData.Cost }

                if ($currentCost) {
                    $resourceData = [ordered]@{
                        category = 'planet_zones'
                    }
                    if ($currentCost) {
                        if ($currentCost -is [int]) {
                            $resourceData['cost'] = @{ minerals = $currentCost }
                        }
                        else {
                            $resourceData['cost'] = $currentCost
                        }
                    }
                    $newzone['resources'] = $resourceData
                }
                #endregion Cost & Upkeep

                $allzones[$zoneName] = New-PdxConfigEntry -Entry $zoneData -Name $zoneName -Defaults $data.Core -Common $commonProps -Output $newzone -Strings $strings -LocalizationProperties $localeMap -Ignore Cost -TypeMap $typeMap
            }
            #endregion Build Edict Data

            # Write File
            $sourceFile = Get-Item -LiteralPath $filePath
            $exportFile = Join-Path -Path $sourceFile.DirectoryName -ChildPath "$($sourceFile.BaseName).txt"
            $allzones | ConvertTo-PdxConfigFormat -TopLevel | Set-Content -Path $exportFile

            # Write Localization
            Export-PdxLocalizedString -Strings $strings -ModRoot $ModRoot -Name "zones_$($sourceFile.BaseName)"
        }
    }
}

function ConvertTo-PdsZoneSlot {
    <#
    .SYNOPSIS
        Converts zoneslot configuration files into zoneslot mod files.
     
    .DESCRIPTION
        Converts zoneslot configuration files into zoneslot mod files.
        This command is used to read psd1-based configuration files for zoneslots and convert them into the format Stellaris expects.
        Generally, this command needs not be called directly and happens automatically during Build-PdxMod.
 
        For more details on how to define zoneslots via configuration file, see:
        https://github.com/FriedrichWeinmann/Paradox.Modding.Stellaris/blob/master/docs/content/zone_slots.md
     
    .PARAMETER Path
        Path to the file(s) to build.
     
    .PARAMETER ModRoot
        Root path of the mod you are building.
        Defaults to the parent folder of the parent folder of the first file specified in -Path.
     
    .EXAMPLE
        PS C:\> ConvertTo-PdsZoneSlot -Path "$PSScriptRoot\common\zone_slots\*.psd1"
 
        Builds all .psd1 files in the common\zone_slots subfolder under the path the current script is placed.
     
    .LINK
        https://github.com/FriedrichWeinmann/Paradox.Modding.Stellaris/blob/master/docs/content/zone_slots.md
    #>

    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        [Alias('FullName')]
        [PSFFile]
        $Path,

        [string]
        $ModRoot
    )
    begin {
        $commonProps = [ordered]@{
            Start     = 'start' # A zone of this type is immediately placed in a new district slot.
            Include   = 'include' # A list of zone types that are available to be built in the slot. The key 'all' can be used to include all existing zone types.
            Exclude   = 'exclude' # A list of zone types that aren't available to be built in the slot.
            Potential = 'potential' # Trigger that defines whether the slot is available on the planet at all. Scope = Planet.
            Unlock    = 'unlock'  # Trigger that defines whether the slot is unlocked on the planet. Scope = Planet.
        }
        $typeMap = @{
            include = 'array'
            exclude = 'array'
        }
    }
    process {
        foreach ($filePath in $Path) {
            if (-not $ModRoot) {
                $ModRoot = Split-Path -Path (Split-Path -Path (Split-Path -Path $filePath))
            }

            $data = Import-PSFPowerShellDataFile -LiteralPath $filePath -Psd1Mode Unsafe
            $allzoneslots = [ordered]@{}

            #region Build Edict Data
            foreach ($zoneslotName in $data.ZoneSlots.Keys) {
                $zoneslotData = $data.ZoneSlots.$zoneslotName

                $newzoneslot = [ordered]@{}

                $allzoneslots[$zoneslotName] = New-PdxConfigEntry -Entry $zoneslotData -Name $zoneslotName -Defaults $data.Core -Common $commonProps -Strings @{} -LocalizationProperties @{} -Output $newzoneslot -TypeMap $typeMap
            }
            #endregion Build Edict Data

            # Write File
            $sourceFile = Get-Item -LiteralPath $filePath
            $exportFile = Join-Path -Path $sourceFile.DirectoryName -ChildPath "$($sourceFile.BaseName).txt"
            $allzoneslots | ConvertTo-PdxConfigFormat -TopLevel | Set-Content -Path $exportFile
        }
    }
}

function Get-PdsGameDirectory {
    <#
    .SYNOPSIS
        Returns the root path to the Stellaris game installation.
     
    .DESCRIPTION
        Returns the root path to the Stellaris game installation.
        Uses steam to figure it out the path.
        Define the 'Paradox.Modding.Stellaris.Installpath' configuration setting (using Set-PSFConfig) to override (and disable) autodetection.
     
    .EXAMPLE
        PS C:\> Get-PdsGameDirectory
 
        returns the path to where the game is installed.
    #>

    [OutputType([string])]
    [CmdletBinding()]
    param ()
    process {
        # Configuration beats it all
        if (Get-PSFConfigValue -Fullname 'Paradox.Modding.Stellaris.Installpath') {
            return Get-PSFConfigValue -Fullname 'Paradox.Modding.Stellaris.Installpath'
        }

        # Once cached, do not resolve again.
        if ($script:_stellarisGamePath) { return $script:_stellarisGamePath }

        # Resolve using Steam configuration path.
        $steamLibraryCfgFile = "${env:ProgramFiles(x86)}\Steam\config\libraryfolders.vdf"
        $steamLibraries = Get-Content -Path $steamLibraryCfgFile | Where-Object { $_ -match '"path"' } | ForEach-Object {
            $_ -replace '^.+"path".+?"' -replace '".{0,}' -replace '\\\\', '\'
        }
        foreach ($path in $steamLibraries) {
            if (Test-Path -Path "$path\steamapps\common\Stellaris\stellaris.exe") {
                $script:_stellarisGamePath = "$path\steamapps\common\Stellaris"
                return "$path\steamapps\common\Stellaris"
            }
        }

        Stop-PSFFunction -Message "Stellaris installation path not found! Autodiscovery failed to detect it, use the following line to tell the module where to find the game:`nSet-PSFConfig -FullName 'Paradox.Modding.Stellaris.Installpath' -Value '<InsertPathhere>' -PassThru | Register-PSFConfig" -EnableException $true -Cmdlet $PSCmdlet
    }
}

Register-PdxBuildExtension -Name 'Stellaris.Edicts' -Tags 'stellaris','edicts' -Description 'Builds all edicts in a Stellaris mod' -Code {
    param ($Data)

    if (Test-Path "$($Data.Root)\common\edicts\*.psd1") {
        ConvertTo-PdsEdict -Path "$($Data.Root)\common\edicts\*.psd1"
    }
}
Register-PdxBuildExtension -Name 'Stellaris.Buildings' -Tags 'stellaris','buildings' -Description 'Builds all buildings in a Stellaris mod' -Code {
    param ($Data)

    if (Test-Path "$($Data.Root)\common\buildings\*.psd1") {
        ConvertTo-PdsBuilding -Path "$($Data.Root)\common\buildings\*.psd1"
    }
}
Register-PdxBuildExtension -Name 'Stellaris.Zones' -Tags 'stellaris','zones' -Description 'Builds all zones in a Stellaris mod' -Code {
    param ($Data)

    if (Test-Path "$($Data.Root)\common\zones\*.psd1") {
        ConvertTo-PdsZone -Path "$($Data.Root)\common\zones\*.psd1"
    }
}
Register-PdxBuildExtension -Name 'Stellaris.ZoneSlotss' -Tags 'stellaris','zoneslots' -Description 'Builds all zone slots in a Stellaris mod' -Code {
    param ($Data)

    if (Test-Path "$($Data.Root)\common\zone_slots\*.psd1") {
        ConvertTo-PdsZoneSlot -Path "$($Data.Root)\common\zone_slots\*.psd1"
    }
}
Register-PdxBuildExtension -Name 'Stellaris.Districts' -Tags 'stellaris','districts' -Description 'Builds all districts in a Stellaris mod' -Code {
    param ($Data)

    if (Test-Path "$($Data.Root)\common\districts\*.psd1") {
        ConvertTo-PdsDistrict -Path "$($Data.Root)\common\districts\*.psd1"
    }
}

Set-PSFConfig -Fullname 'Paradox.Modding.Stellaris.Installpath' -Value '' -Validation string -Initialize -Description 'Path where the game was installed. Overrides auto-detection.'