diff --git a/.github/stale.yml b/.github/stale.yml
deleted file mode 100644
index 57d42d6921..0000000000
--- a/.github/stale.yml
+++ /dev/null
@@ -1,34 +0,0 @@
-daysUntilClose: 7
-staleLabel: stale
-closeComment: false
-exemptMilestones: true
-exemptAssignees: true
-
-issues:
- daysUntilStale: 60
- exemptLabels:
- - pinned
- - security
- - "good first issue"
- - regression
- markComment: >
- This issue has been automatically marked as stale because it has not had any activity in the last two months.
-
- If you believe the issue is still relevant, please test on the latest nightly and report back.
-
- It will be closed if no further activity occurs within 7 days.
-
- Thank you for your contributions.
-
-pulls:
- daysUntilStale: 30
- exemptLabels:
- - pinned
- markComment: >
- This pull request has been automatically marked as stale because it has not had any activity in the last month.
-
- Please feel free to give a status update now, ping for review, or re-open when it's ready.
-
- It will be closed if no further activity occurs within 7 days.
-
- Thank you for your contributions.
diff --git a/.github/workflows/ci-build.yml b/.github/workflows/ci-build.yml
index 5fb1b6842a..4d83725045 100644
--- a/.github/workflows/ci-build.yml
+++ b/.github/workflows/ci-build.yml
@@ -5,7 +5,6 @@ on:
push:
branches:
- master
- - 12
env:
CTEST_OUTPUT_ON_FAILURE: 1
@@ -17,55 +16,56 @@ jobs:
runs-on: ubuntu-20.04
container:
# If you change this version, change the number in the cache step too.
- image: emscripten/emsdk:3.0.0
+ image: emscripten/emsdk:3.1.28
steps:
- name: Checkout
- uses: actions/checkout@v2
-
- - name: Install dependencies
- run: |
- echo "::group::Update apt"
- sudo apt-get update
- echo "::endgroup::"
-
- echo "::group::Install dependencies"
- sudo apt-get install -y --no-install-recommends \
- autoconf \
- automake \
- libtool \
- gperf \
- libharfbuzz-dev \
- libicu-dev \
- libfreetype-dev \
- # EOF
- echo "::endgroup::"
+ uses: actions/checkout@v3
- name: Setup cache
- uses: actions/cache@v2
+ uses: actions/cache@v3
with:
path: /emsdk/upstream/emscripten/cache
- key: 3.0.0-${{ runner.os }}-v2
+ key: 3.1.28-${{ runner.os }}
+
+ - name: Patch Emscripten to support LZMA
+ run: |
+ cd /emsdk/upstream/emscripten
+ patch -p1 < ${GITHUB_WORKSPACE}/os/emscripten/emsdk-liblzma.patch
+
+ - name: Build (host tools)
+ run: |
+ mkdir build-host
+ cd build-host
+
+ echo "::group::CMake"
+ cmake .. -DOPTION_TOOLS_ONLY=ON
+ echo "::endgroup::"
+
+ echo "::group::Build"
+ echo "Running on $(nproc) cores"
+ cmake --build . -j $(nproc) --target tools
+ echo "::endgroup::"
- name: Install GCC problem matcher
uses: ammaraskar/gcc-problem-matcher@master
- name: Build
run: |
- mkdir -p release-wasm
- cd os/emscripten
- ./emscripten-build.sh release `pwd`/../../release-wasm
+ mkdir build
+ cd build
- - name: Package
- uses: actions/upload-artifact@v2
- with:
- name: openttd-wasm
- path: release-wasm
- retention-days: 5
+ echo "::group::CMake"
+ emcmake cmake .. -DHOST_BINARY_DIR=../build-host
+ echo "::endgroup::"
+
+ echo "::group::Build"
+ echo "Running on $(nproc) cores"
+ cmake --build . -j $(nproc)
+ echo "::endgroup::"
linux:
name: Linux
- if: ${{ false }}
strategy:
fail-fast: false
@@ -91,7 +91,7 @@ jobs:
steps:
- name: Checkout
- uses: actions/checkout@v2
+ uses: actions/checkout@v3
- name: Install dependencies
run: |
@@ -152,7 +152,6 @@ jobs:
macos:
name: Mac OS
- if: ${{ false }}
strategy:
fail-fast: false
@@ -163,19 +162,19 @@ jobs:
runs-on: macos-latest
env:
- MACOSX_DEPLOYMENT_TARGET: 10.14
+ MACOSX_DEPLOYMENT_TARGET: 10.13
steps:
- name: Checkout
- uses: actions/checkout@v2
+ uses: actions/checkout@v3
- name: Prepare cache key
id: key
run: |
- echo "::set-output name=image::$ImageOS-$ImageVersion"
+ echo "image=$ImageOS-$ImageVersion" >> $GITHUB_OUTPUT
- name: Enable vcpkg cache
- uses: actions/cache@v2
+ uses: actions/cache@v3
with:
path: /usr/local/share/vcpkg/installed
key: ${{ steps.key.outputs.image }}-vcpkg-${{ matrix.arch }}-0 # Increase the number whenever dependencies are modified
@@ -232,3 +231,181 @@ jobs:
cd build
ctest -j $(sysctl -n hw.logicalcpu) --timeout 120
+ windows:
+ name: Windows
+
+ strategy:
+ fail-fast: false
+ matrix:
+ os: [windows-latest, windows-2019]
+ arch: [x86, x64]
+
+ runs-on: ${{ matrix.os }}
+
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v3
+
+ - name: Prepare cache key
+ id: key
+ shell: powershell
+ run: |
+ # Work around caching failure with GNU tar
+ New-Item -Type Junction -Path vcpkg -Target c:\vcpkg
+
+ Write-Output "image=$env:ImageOS-$env:ImageVersion" >> $env:GITHUB_OUTPUT
+
+ - name: Enable vcpkg cache
+ uses: actions/cache@v3
+ with:
+ path: vcpkg/installed
+ key: ${{ steps.key.outputs.image }}-vcpkg-${{ matrix.arch }}-0 # Increase the number whenever dependencies are modified
+ restore-keys: |
+ ${{ steps.key.outputs.image }}-vcpkg-${{ matrix.arch }}
+
+ - name: Prepare vcpkg
+ shell: bash
+ run: |
+ vcpkg install --triplet=${{ matrix.arch }}-windows-static \
+ liblzma \
+ libpng \
+ lzo \
+ zlib \
+ # EOF
+
+ - name: Install OpenGFX
+ shell: bash
+ run: |
+ mkdir -p "C:/Users/Public/Documents/OpenTTD/baseset"
+ cd "C:/Users/Public/Documents/OpenTTD/baseset"
+
+ echo "::group::Download OpenGFX"
+ curl -L https://cdn.openttd.org/opengfx-releases/0.6.0/opengfx-0.6.0-all.zip -o opengfx-all.zip
+ echo "::endgroup::"
+
+ echo "::group::Unpack OpenGFX"
+ unzip opengfx-all.zip
+ echo "::endgroup::"
+
+ rm -f opengfx-all.zip
+
+ - name: Install MSVC problem matcher
+ uses: ammaraskar/msvc-problem-matcher@master
+
+ - name: Configure developer command prompt for ${{ matrix.arch }}
+ uses: ilammy/msvc-dev-cmd@v1
+ with:
+ arch: ${{ matrix.arch }}
+
+ - name: Build
+ shell: bash
+ run: |
+ mkdir build
+ cd build
+
+ echo "::group::CMake"
+ cmake .. \
+ -GNinja \
+ -DVCPKG_TARGET_TRIPLET=${{ matrix.arch }}-windows-static \
+ -DCMAKE_TOOLCHAIN_FILE="c:\vcpkg\scripts\buildsystems\vcpkg.cmake" \
+ # EOF
+ echo "::endgroup::"
+
+ echo "::group::Build"
+ cmake --build .
+ echo "::endgroup::"
+
+ - name: Test
+ shell: bash
+ run: |
+ cd ${GITHUB_WORKSPACE}/build
+ ctest --timeout 120
+
+
+ msys2:
+ name: msys2
+
+ strategy:
+ fail-fast: false
+ matrix:
+ include:
+ - msystem: MINGW64
+ arch: x86_64
+ - msystem: MINGW32
+ arch: i686
+
+ runs-on: windows-latest
+
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v3
+
+ - name: Setup MSYS2
+ uses: msys2/setup-msys2@v2
+ with:
+ msystem: ${{ matrix.msystem }}
+ release: false
+ install: >-
+ git
+ make
+ mingw-w64-${{ matrix.arch }}-cmake
+ mingw-w64-${{ matrix.arch }}-gcc
+ mingw-w64-${{ matrix.arch }}-lzo2
+ mingw-w64-${{ matrix.arch }}-libpng
+
+ - name: Install OpenGFX
+ shell: bash
+ run: |
+ mkdir -p "C:/Users/Public/Documents/OpenTTD/baseset"
+ cd "C:/Users/Public/Documents/OpenTTD/baseset"
+
+ echo "::group::Download OpenGFX"
+ curl -L https://cdn.openttd.org/opengfx-releases/0.6.0/opengfx-0.6.0-all.zip -o opengfx-all.zip
+ echo "::endgroup::"
+
+ echo "::group::Unpack OpenGFX"
+ unzip opengfx-all.zip
+ echo "::endgroup::"
+
+ rm -f opengfx-all.zip
+
+ - name: Install GCC problem matcher
+ uses: ammaraskar/gcc-problem-matcher@master
+
+ - name: Build
+ shell: msys2 {0}
+ run: |
+ mkdir build
+ cd build
+
+ echo "::group::CMake"
+ cmake .. -G"MSYS Makefiles"
+ echo "::endgroup::"
+
+ echo "::group::Build"
+ echo "Running on $(nproc) cores"
+ cmake --build . -j $(nproc)
+ echo "::endgroup::"
+
+ - name: Test
+ shell: msys2 {0}
+ run: |
+ cd build
+ ctest -j $(nproc) --timeout 120
+
+ check_annotations:
+ name: Check Annotations
+ needs:
+ - emscripten
+ - linux
+ - macos
+ - windows
+ - msys2
+
+ if: always() && github.event_name == 'pull_request'
+
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Check annotations
+ uses: OpenTTD/actions/annotation-check@v3
diff --git a/.github/workflows/commit-checker.yml b/.github/workflows/commit-checker.yml
index acf622a87e..91fc54b104 100644
--- a/.github/workflows/commit-checker.yml
+++ b/.github/workflows/commit-checker.yml
@@ -10,7 +10,7 @@ jobs:
steps:
- name: Checkout
- uses: actions/checkout@v2
+ uses: actions/checkout@v3
with:
fetch-depth: 4
diff --git a/.github/workflows/preview_build.yml b/.github/workflows/preview_build.yml
index 2a280a6fcd..68ca5f083f 100644
--- a/.github/workflows/preview_build.yml
+++ b/.github/workflows/preview_build.yml
@@ -12,7 +12,7 @@ jobs:
runs-on: ubuntu-20.04
container:
# If you change this version, change the number in the cache step too.
- image: emscripten/emsdk:2.0.31
+ image: emscripten/emsdk:3.1.28
steps:
- name: Update deployment status to in progress
@@ -31,7 +31,7 @@ jobs:
GITHUB_TOKEN: ${{ secrets.PREVIEW_GITHUB_TOKEN }}
- name: Checkout
- uses: actions/checkout@v2
+ uses: actions/checkout@v3
with:
ref: ${{ github.event.client_payload.sha }}
@@ -41,10 +41,10 @@ jobs:
git checkout -b ${name}
- name: Setup cache
- uses: actions/cache@v2
+ uses: actions/cache@v3
with:
path: /emsdk/upstream/emscripten/cache
- key: 2.0.31-${{ runner.os }}
+ key: 3.1.28-${{ runner.os }}
- name: Patch Emscripten to support LZMA
run: |
diff --git a/.github/workflows/preview_label.yml b/.github/workflows/preview_label.yml
index 6b3e17ea1a..503d4e53eb 100644
--- a/.github/workflows/preview_label.yml
+++ b/.github/workflows/preview_label.yml
@@ -59,7 +59,7 @@ jobs:
- if: steps.core_developer.outcome == 'success'
name: Trigger 'preview build'
- uses: peter-evans/repository-dispatch@v1
+ uses: peter-evans/repository-dispatch@v2
with:
token: ${{ secrets.PREVIEW_GITHUB_TOKEN }}
event-type: "Preview build #${{ github.event.number }}"
diff --git a/.github/workflows/preview_push.yml b/.github/workflows/preview_push.yml
index cb68c8f6ce..3868c16f90 100644
--- a/.github/workflows/preview_push.yml
+++ b/.github/workflows/preview_push.yml
@@ -59,7 +59,7 @@ jobs:
- if: toJson(fromJson(steps.earlier_preview.outputs.data)) != '[]' && contains(fromJson(steps.preview_label.outputs.data).*.name, 'preview')
name: Trigger 'preview build'
- uses: peter-evans/repository-dispatch@v1
+ uses: peter-evans/repository-dispatch@v2
with:
token: ${{ secrets.PREVIEW_GITHUB_TOKEN }}
event-type: "Preview build #${{ github.event.number }}"
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index d3b4dc329b..939649a14c 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -29,14 +29,14 @@ jobs:
steps:
- name: Checkout (Release)
if: github.event_name == 'release'
- uses: actions/checkout@v2
+ uses: actions/checkout@v3
with:
# We generate a changelog; for this we need the full git log.
fetch-depth: 0
- name: Checkout (Manual)
if: github.event_name == 'workflow_dispatch'
- uses: actions/checkout@v2
+ uses: actions/checkout@v3
with:
ref: ${{ github.event.inputs.ref }}
# We generate a changelog; for this we need the full git log.
@@ -44,7 +44,7 @@ jobs:
- name: Checkout (Trigger)
if: github.event_name == 'repository_dispatch'
- uses: actions/checkout@v2
+ uses: actions/checkout@v3
with:
ref: ${{ github.event.client_payload.ref }}
# We generate a changelog; for this we need the full git log.
@@ -138,10 +138,10 @@ jobs:
echo "Folder on CDN: ${FOLDER}"
echo "Workflow trigger: ${TRIGGER_TYPE}"
- echo "::set-output name=version::$(cat .version)"
- echo "::set-output name=is_tag::${IS_TAG}"
- echo "::set-output name=folder::${FOLDER}"
- echo "::set-output name=trigger_type::${TRIGGER_TYPE}"
+ echo "version=$(cat .version)" >> $GITHUB_OUTPUT
+ echo "is_tag=${IS_TAG}" >> $GITHUB_OUTPUT
+ echo "folder=${FOLDER}" >> $GITHUB_OUTPUT
+ echo "trigger_type=${TRIGGER_TYPE}" >> $GITHUB_OUTPUT
env:
NIGHTLIES_BRANCH: master
FOLDER_RELEASES: openttd-releases
@@ -174,14 +174,14 @@ jobs:
echo "::endgroup::"
- name: Store bundles
- uses: actions/upload-artifact@v2
+ uses: actions/upload-artifact@v3
with:
name: openttd-source
path: build/bundles/*
retention-days: 5
- name: Store source (for other jobs)
- uses: actions/upload-artifact@v2
+ uses: actions/upload-artifact@v3
with:
name: internal-source
path: source.tar.gz
@@ -195,7 +195,7 @@ jobs:
steps:
- name: Download source
- uses: actions/download-artifact@v2
+ uses: actions/download-artifact@v3
with:
name: internal-source
@@ -258,7 +258,7 @@ jobs:
echo "::endgroup::"
- name: Store bundles
- uses: actions/upload-artifact@v2
+ uses: actions/upload-artifact@v3
with:
name: openttd-docs
path: build/bundles/*.tar.xz
@@ -276,7 +276,7 @@ jobs:
steps:
- name: Download source
- uses: actions/download-artifact@v2
+ uses: actions/download-artifact@v3
with:
name: internal-source
@@ -355,7 +355,7 @@ jobs:
echo "::endgroup::"
- name: Store bundles
- uses: actions/upload-artifact@v2
+ uses: actions/upload-artifact@v3
with:
name: openttd-linux-generic
path: build/bundles
@@ -371,14 +371,11 @@ jobs:
fail-fast: false
matrix:
include:
- - container_image: "ubuntu:18.04"
- bundle_name: "bionic"
- compiler: "g++-8"
- container_image: "ubuntu:20.04"
bundle_name: "focal"
compiler: "g++"
- - container_image: "debian:buster"
- bundle_name: "buster"
+ - container_image: "ubuntu:22.04"
+ bundle_name: "jammy"
compiler: "g++"
- container_image: "debian:bullseye"
bundle_name: "bullseye"
@@ -390,7 +387,7 @@ jobs:
steps:
- name: Download source
- uses: actions/download-artifact@v2
+ uses: actions/download-artifact@v3
with:
name: internal-source
@@ -460,7 +457,7 @@ jobs:
echo "::endgroup::"
- name: Store bundles
- uses: actions/upload-artifact@v2
+ uses: actions/upload-artifact@v3
with:
name: openttd-linux-${{ matrix.bundle_name }}
path: build/bundles
@@ -470,13 +467,13 @@ jobs:
name: MacOS
needs: source
- runs-on: macos-10.15
+ runs-on: macos-11
env:
- MACOSX_DEPLOYMENT_TARGET: 10.14
+ MACOSX_DEPLOYMENT_TARGET: 10.13
steps:
- name: Download source
- uses: actions/download-artifact@v2
+ uses: actions/download-artifact@v3
with:
name: internal-source
@@ -494,10 +491,10 @@ jobs:
- name: Prepare cache key
id: key
run: |
- echo "::set-output name=image::$ImageOS-$ImageVersion"
+ echo "image=$ImageOS-$ImageVersion" >> $GITHUB_OUTPUT
- name: Enable vcpkg cache
- uses: actions/cache@v2
+ uses: actions/cache@v3
with:
path: /usr/local/share/vcpkg/installed
key: ${{ steps.key.outputs.image }}-vcpkg-release-0 # Increase the number whenever dependencies are modified
@@ -651,7 +648,7 @@ jobs:
mv _CPack_Packages/*/Bundle/openttd-*.zip bundles/
- name: Store bundles
- uses: actions/upload-artifact@v2
+ uses: actions/upload-artifact@v3
with:
name: openttd-macos-universal
path: build-x64/bundles
@@ -676,7 +673,7 @@ jobs:
steps:
- name: Download source
- uses: actions/download-artifact@v2
+ uses: actions/download-artifact@v3
with:
name: internal-source
@@ -697,10 +694,10 @@ jobs:
# Work around caching failure with GNU tar
New-Item -Type Junction -Path vcpkg -Target c:\vcpkg
- Write-Output "::set-output name=image::$env:ImageOS-$env:ImageVersion"
+ Write-Output "image=$env:ImageOS-$env:ImageVersion" >> $env:GITHUB_OUTPUT
- name: Enable vcpkg cache
- uses: actions/cache@v2
+ uses: actions/cache@v3
with:
path: vcpkg/installed
key: ${{ steps.key.outputs.image }}-vcpkg-${{ matrix.arch }}-0 # Increase the number whenever dependencies are modified
@@ -844,12 +841,195 @@ jobs:
WINDOWS_CERTIFICATE_COMMON_NAME: ${{ secrets.WINDOWS_CERTIFICATE_COMMON_NAME }}
- name: Store bundles
- uses: actions/upload-artifact@v2
+ uses: actions/upload-artifact@v3
with:
name: openttd-windows-${{ matrix.arch }}
path: build/bundles
retention-days: 5
+ windows-appx:
+ name: Windows Store
+ needs:
+ - source
+ - windows
+
+ if: needs.source.outputs.is_tag == 'true'
+ runs-on: windows-latest
+
+ steps:
+ - name: Download source
+ uses: actions/download-artifact@v3
+ with:
+ name: internal-source
+
+ - name: Unpack source
+ shell: bash
+ run: |
+ tar -xf source.tar.gz --strip-components=1
+
+ - name: Download x86 build
+ uses: actions/download-artifact@v3
+ with:
+ name: openttd-windows-x86
+
+ - name: Download x64 build
+ uses: actions/download-artifact@v3
+ with:
+ name: openttd-windows-x64
+
+ - name: Download arm64 build
+ uses: actions/download-artifact@v3
+ with:
+ name: openttd-windows-arm64
+
+ - name: Unpack builds
+ shell: bash
+ run: |
+ mkdir builds
+ cd builds
+
+ function extract {
+ mkdir $1
+
+ # Extract the zip version of the release
+ unzip ../openttd-*-windows-$2.zip -d $1
+
+ # Remove the extraneous directory
+ mv $1/openttd-*-windows-$2/* $1/
+ rmdir $1/openttd-*-windows-$2
+
+ # Move the openttd.exe to the '{arch}-binaries' folder
+ mkdir $1-binaries
+ mv $1/openttd.exe $1-binaries/
+ }
+
+ extract x86 win32
+ extract x64 win64
+ extract arm64 arm64
+
+ # Use the "x86" folder as the source of the common binaries (lang files, etc) and remove the others
+ mv x86 common-binaries
+ rm -rf x64 arm64
+
+ - name: Install OpenGFX
+ shell: bash
+ run: |
+ mkdir -p builds/common-binaries/baseset
+ cd builds/common-binaries/baseset
+
+ echo "::group::Download OpenGFX"
+ curl -L https://cdn.openttd.org/opengfx-releases/7.1/opengfx-7.1-all.zip -o opengfx-all.zip
+ echo "::endgroup::"
+
+ echo "::group::Unpack OpenGFX"
+ unzip opengfx-all.zip
+ tar xf opengfx-*.tar
+ echo "::endgroup::"
+
+ rm -f opengfx-all.zip opengfx-*.tar
+
+ - name: Install OpenMSX
+ shell: bash
+ run: |
+ mkdir -p builds/common-binaries/baseset
+ cd builds/common-binaries/baseset
+
+ echo "::group::Download OpenMSX"
+ curl -L https://cdn.openttd.org/openmsx-releases/0.4.2/openmsx-0.4.2-all.zip -o openmsx-all.zip
+ echo "::endgroup::"
+
+ echo "::group::Unpack OpenGFX"
+ unzip openmsx-all.zip
+ tar xf openmsx-*.tar
+ echo "::endgroup::"
+
+ rm -f openmsx-all.zip openmsx-*.tar
+
+ - name: Install OpenSFX
+ shell: bash
+ run: |
+ mkdir -p builds/common-binaries/baseset/opensfx
+ cd builds/common-binaries/baseset/opensfx
+
+ echo "::group::Download OpenSFX"
+ curl -L https://cdn.openttd.org/opensfx-releases/1.0.3/opensfx-1.0.3-all.zip -o opensfx-all.zip
+ echo "::endgroup::"
+
+ echo "::group::Unpack OpenSFX"
+ unzip opensfx-all.zip
+ tar xf opensfx-*.tar
+ echo "::endgroup::"
+
+ rm -f opensfx-all.zip opensfx-*.tar
+
+ - name: Generate signing certificate
+ shell: cmd
+ run: |
+ cd builds
+
+ REM We need to provide a signed .appx to the Windows Store, so generate a certificate with password 'password'
+ call ..\os\windows\winstore\generate-key.bat "CN=78024DA8-4BE4-4C77-B12E-547BBF7359D2" password cert.pfx
+
+ - name: Generate assets
+ shell: cmd
+ run: |
+ cd os\windows\winstore
+ call generate-assets.bat
+
+ - name: Prepare manifests
+ shell: cmd
+ run: |
+ cd builds
+ mkdir manifests
+
+ REM Set the version environment variable
+ call ..\os\windows\winstore\set-version.bat x86-binaries\openttd.exe
+
+ call ..\os\windows\winstore\prepare-manifests.bat manifests "CN=78024DA8-4BE4-4C77-B12E-547BBF7359D2" "57420OpenTTDDevelopers.OpenTTDofficial"
+
+ - name: Prepare binaries
+ shell: bash
+ run: |
+ cd builds
+
+ # Copy the Windows Store assets
+ mkdir common-binaries/Assets
+ cp -R ../os/windows/winstore/assets-common/* common-binaries/Assets/
+
+ mkdir Assets
+ cp -R ../os/windows/winstore/assets/* Assets/
+
+ cp manifests/*.xml .
+
+ - name: Build
+ shell: cmd
+ run: |
+ REM Add the Windows SDK tools to the PATH
+
+ echo|set /p="SET VS_INSTALLDIR=" > _vspath.bat
+ vswhere -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath >> _vspath.bat
+ call _vspath.bat
+ call "%VS_INSTALLDIR%\Common7\Tools\VsDevCmd.bat"
+
+ REM Set the version environment variable
+ call os\windows\winstore\set-version.bat builds\x86-binaries\openttd.exe
+
+ cd builds
+ mkdir output
+
+ REM Build and sign the package
+ makeappx build /v /f PackagingLayout.xml /op output\ /bv %OTTD_VERSION% /pv %OTTD_VERSION% /ca
+ SignTool sign /fd sha256 /a /f cert.pfx /p password "output\OpenTTD.appxbundle"
+
+ - name: Store appx
+ uses: actions/upload-artifact@v3
+ with:
+ name: openttd-windows-store
+ path: |
+ builds/output/OpenTTD.appxbundle
+ builds/cert.pfx
+ retention-days: 5
+
upload:
name: Upload (AWS)
needs:
@@ -871,7 +1051,7 @@ jobs:
steps:
- name: Download all bundles
- uses: actions/download-artifact@v2
+ uses: actions/download-artifact@v3
- name: Calculate checksums
run: |
@@ -903,7 +1083,7 @@ jobs:
AWS_DEFAULT_REGION: ${{ secrets.AWS_REGION }}
- name: Trigger 'New OpenTTD release'
- uses: peter-evans/repository-dispatch@v1
+ uses: peter-evans/repository-dispatch@v2
with:
token: ${{ secrets.DEPLOYMENT_TOKEN }}
repository: OpenTTD/workflows
@@ -924,7 +1104,7 @@ jobs:
steps:
- name: Download all bundles
- uses: actions/download-artifact@v2
+ uses: actions/download-artifact@v3
- name: Setup steamcmd
uses: CyberAndrii/setup-steamcmd@v1
diff --git a/.github/workflows/unused-strings.yml b/.github/workflows/unused-strings.yml
index d63da91c30..6026671307 100644
--- a/.github/workflows/unused-strings.yml
+++ b/.github/workflows/unused-strings.yml
@@ -10,7 +10,7 @@ jobs:
steps:
- name: Checkout
- uses: actions/checkout@v2
+ uses: actions/checkout@v3
- name: Check for unused strings
run: |
diff --git a/CMakeLists.txt b/CMakeLists.txt
index e960728a43..03097d5206 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -5,7 +5,7 @@ if(NOT BINARY_NAME)
endif()
project(${BINARY_NAME}
- VERSION 12.2
+ VERSION 13.0
)
if(CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR)
@@ -22,7 +22,7 @@ if (EMSCRIPTEN)
endif()
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake")
-set(CMAKE_OSX_DEPLOYMENT_TARGET 10.14)
+set(CMAKE_OSX_DEPLOYMENT_TARGET 10.13)
# Use GNUInstallDirs to allow customisation
# but set our own default data and bin dir
@@ -141,7 +141,7 @@ if(NOT OPTION_DEDICATED)
find_package(Fluidsynth)
find_package(Timidity)
find_package(Fontconfig)
- find_package(ICU OPTIONAL_COMPONENTS i18n lx)
+ find_package(ICU REQUIRED uc OPTIONAL_COMPONENTS i18n lx)
endif()
endif()
endif()
@@ -281,6 +281,7 @@ if(NOT OPTION_DEDICATED)
link_package(FREETYPE TARGET Freetype::Freetype)
link_package(Fontconfig TARGET Fontconfig::Fontconfig)
link_package(ICU_lx)
+ link_package(ICU_uc)
link_package(ICU_i18n)
if(SDL2_FOUND AND OPENGL_FOUND AND UNIX)
@@ -337,8 +338,7 @@ if(EMSCRIPTEN)
add_definitions(-s DISABLE_EXCEPTION_CATCHING=0)
# Export functions to Javascript.
- target_link_libraries(WASM::WASM INTERFACE "-s EXPORTED_FUNCTIONS='[\"_main\",\"_em_openttd_add_server\",\"_em_openttd_cloud_save_from_js\"]'")
- target_link_libraries(WASM::WASM INTERFACE "-s EXPORTED_RUNTIME_METHODS='[\"cwrap\",\"ccall\"]'")
+ target_link_libraries(WASM::WASM INTERFACE "-s EXPORTED_FUNCTIONS='[\"_main\", \"_em_openttd_add_server\"]' -s EXPORTED_RUNTIME_METHODS='[\"cwrap\"]'")
# Preload all the files we generate during build.
# As we do not compile with FreeType / FontConfig, we also have no way to
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index f9ffdf98ce..b0ca0ab7a9 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -94,8 +94,8 @@ Although we really appreciate feedback and ideas, we will close feature requests
Many of those ideas etc do have a place on the [forums](https://www.tt-forums.net); and if enough people like it, someone will stand up and make it.
-It's usually best discuss in [irc](https://wiki.openttd.org/en/Development/IRC%20channel) before opening a feature request or working on a large feature in a fork.
-Discussion in irc can take time, but it can be productive and avoid disappointment :)
+It's usually best to discuss on [Discord](https://discord.gg/openttd) before opening a feature request or working on a large feature in a fork.
+Discussion can take time, but it can be productive and avoid disappointment. :)
## Pull requests
@@ -212,8 +212,8 @@ When it comes to gameplay features there are at least these groups of interests:
- *Control freak:* micromanagement like conditional orders, refitting and loading etc.
- *Casual:* automatisation like cargodist, path based signalling etc.
-To please everyone, the official branch tries to stay close to the original gameplay; after all, that is what everyone brought here.
-The preferred method to alter and extent the gameplay is via add-ons like NewGRF and GameScripts.
+To please everyone, the official branch tries to stay close to the original gameplay; after all, that is what brought everyone here.
+The preferred method to alter and extend the gameplay is via add-ons like NewGRF and GameScripts.
For a long time, the official branch was also open to features which could be enabled/disabled, but the corner-cases that came with some configurations have rendered some parts of the code very complicated.
Today, new features have to work with all the already existing features, which is not only challenging in corner cases, but also requires spending considerable more work than just "making it work in the game mode that I play".
diff --git a/CREDITS.md b/CREDITS.md
index 33f8836265..8826c9dcfe 100644
--- a/CREDITS.md
+++ b/CREDITS.md
@@ -1,31 +1,32 @@
### The OpenTTD team (in alphabetical order):
-- Grzegorz Duczyński (adf88) - General coding (since 1.7.2)
-- Albert Hofkamp (Alberth) - GUI expert (since 0.7)
- Matthijs Kooijman (blathijs) - Pathfinder-guru, Debian port (since 0.3)
-- Ulf Hermann (fonsinchen) - Cargo Distribution (since 1.3)
- Christoph Elsenhans (frosch) - General coding (since 0.6)
-- Loïc Guilloux (glx) - Windows Expert (since 0.4.5)
+- Loïc Guilloux (glx) - General / Windows Expert (since 0.4.5)
- Charles Pigott (LordAro) - General / Correctness police (since 1.9)
-- Michael Lutz (michi_cc) - Path based signals (since 0.7)
+- Michael Lutz (michi_cc) - General / Path based signals (since 0.7)
- Niels Martin Hansen (nielsm) - Music system, general coding (since 1.9)
- Owen Rudge (orudge) - Forum host, OS/2 port (since 0.1)
- Peter Nelson (peter1138) - Spiritual descendant from newGRF gods (since 0.4.5)
-- Ingo von Borstel (planetmaker) - General coding, Support (since 1.1)
-- Remko Bijker (Rubidium) - Lead coder and way more (since 0.4.5)
-- José Soler (Terkhen) - General coding (since 1.0)
+- Remko Bijker (Rubidium) - Coder and way more (since 0.4.5)
+- Patric Stout (TrueBrain) - NoProgrammer (since 0.3), sys op
+- Tyler Trahan (2TallTyler) - General coding (since 13)
### Inactive Developers:
+- Grzegorz Duczyński (adf88) - General coding (1.7 - 1.8)
+- Albert Hofkamp (Alberth) - GUI expert (0.7 - 1.9)
- Jean-François Claeys (Belugas) - GUI, newindustries and more (0.4.5 - 1.0)
- Bjarni Corfitzen (Bjarni) - macOS port, coder and vehicles (0.3 - 0.7)
- Victor Fischer (Celestar) - Programming everywhere you need him to (0.3 - 0.6)
+- Ulf Hermann (fonsinchen) - Cargo Distribution (1.3 - 1.6)
- Jaroslav Mazanec (KUDr) - YAPG (Yet Another Pathfinder God) ;) (0.4.5 - 0.6)
- Jonathan Coome (Maedhros) - High priest of the NewGRF Temple (0.5 - 0.6)
-- Attila Bán (MiHaMiX) - WebTranslator 1 and 2 (0.3 - 0.5)
+- Attila Bán (MiHaMiX) - Developer WebTranslator 1 and 2 (0.3 - 0.5)
+- Ingo von Borstel (planetmaker) - General coding, Support (1.1 - 1.9)
- Zdeněk Sojka (SmatZ) - Bug finder and fixer (0.6 - 1.3)
+- José Soler (Terkhen) - General coding (1.0 - 1.4)
- Christoph Mallon (Tron) - Programmer, code correctness police (0.3 - 0.5)
-- Patric Stout (TrueBrain) - NoProgrammer (0.3 - 1.2), sys op (active)
- Thijs Marinussen (Yexo) - AI Framework, General (0.6 - 1.3)
- Leif Linse (Zuu) - AI/Game Script (1.2 - 1.6)
@@ -33,11 +34,11 @@
- Tamás Faragó (Darkvater) - Ex-Lead coder (0.3 - 0.5)
- Dominik Scherer (dominik81) - Lead programmer, GUI expert (0.3 - 0.3)
-- Emil Djupfeld (egladil) - macOS port (0.4 - 0.6)
+- Emil Djupfeld (egladil) - macOS port (0.4.5 - 0.6)
- Simon Sasburg (HackyKid) - Bug fixer (0.4 - 0.4.5)
- Ludvig Strigeus (ludde) - Original author of OpenTTD, main coder (0.1 - 0.3)
- Cian Duffy (MYOB) - BeOS port / manual writing (0.1 - 0.3)
-- Petr Baudiš (pasky) - Many patches, newgrf support, etc. (0.3 - 0.3)
+- Petr Baudiš (pasky) - Many patches, newgrf support (0.3 - 0.3)
- Benedikt Brüggemeier (skidd13) - Bug fixer and code reworker (0.6 - 0.7)
- Serge Paquet (vurlix) - 2nd contributor after ludde (0.1 - 0.3)
@@ -52,7 +53,7 @@
- Alberto Demichelis - Squirrel scripting language
- L. Peter Deutsch - MD5 implementation
- Michael Blunck - For revolutionizing TTD with awesome graphics
-- George - Canal graphics
+- George - Canal/Lock graphics
- Andrew Parkhouse (andythenorth) - River graphics
- David Dallaston (Pikka) - Tram tracks
- All Translators - For their support to make OpenTTD a truly international game
diff --git a/README.md b/README.md
index 311f9e0ed0..96f137307c 100644
--- a/README.md
+++ b/README.md
@@ -116,35 +116,6 @@ Most types of add-on content can be downloaded within OpenTTD via the 'Check Onl
Add-on content can also be installed manually, but that's more complicated; the [OpenTTD wiki](https://wiki.openttd.org/) may offer help with that, or the [OpenTTD directory structure guide](./docs/directory_structure.md).
-### 1.5.1) AI opponents
-
-OpenTTD comes without AI opponents, so if you want to play with AIs you have to download them.
-
-The easiest way is via the 'Check Online Content' button in the main menu.
-
-You can select some AIs that you think are compatible with your playing style.
-
-AI help and discussions may also be found in the [AI section of the forum](https://www.tt-forums.net/viewforum.php?f=65).
-
-### 1.5.2) Scenarios and height maps
-
-Scenarios and heightmaps can be added via the 'Check Online Content' button in the main menu.
-
-### 1.5.3) NewGRFs
-
-A wide range of add-content is available as NewGRFs, including vehicles, industries, stations, landscape objects, town names and more.
-
-NewGRFs can be added via the 'Check Online Content' button in the main menu.
-
-See also the wiki [guide to NewGRFs](https://wiki.openttd.org/en/Manual/NewGRF) and [the forum graphics development section](https://www.tt-forums.net/viewforum.php?f=66).
-
-### 1.5.4) Game scripts
-
-Game scripts can provide additional challenges or changes to the standard OpenTTD gameplay, for example setting transport goals, or changing town growth behaviour.
-
-Game scripts can be added via the 'Check Online Content' button in the main menu.
-
-See also the wiki [guide to game scripts](https://wiki.openttd.org/en/Manual/Game%20script) and [the forum graphics game script section](https://www.tt-forums.net/viewforum.php?f=65).
### 1.6) OpenTTD directories
@@ -162,6 +133,7 @@ If you want to compile OpenTTD from source, instructions can be found in [COMPIL
'Official' channels
- [OpenTTD website](https://www.openttd.org)
+- [OpenTTD official Discord](https://discord.gg/openttd)
- IRC chat using #openttd on irc.oftc.net [more info about our irc channel](https://wiki.openttd.org/en/Development/IRC%20channel)
- [OpenTTD on Github](https://github.com/OpenTTD/) for code repositories and for reporting issues
- [forum.openttd.org](https://forum.openttd.org/) - the primary community forum site for discussing OpenTTD and related games
diff --git a/bin/ai/compat_0.7.nut b/bin/ai/compat_0.7.nut
index c40308592c..341b543bd3 100644
--- a/bin/ai/compat_0.7.nut
+++ b/bin/ai/compat_0.7.nut
@@ -379,3 +379,16 @@ AIGroup.CreateGroup <- function(vehicle_type)
{
return AIGroup._CreateGroup(vehicle_type, AIGroup.GROUP_INVALID);
}
+
+/* 13 really checks RoadType against RoadType */
+AIRoad._HasRoadType <- AIRoad.HasRoadType;
+AIRoad.HasRoadType <- function(tile, road_type)
+{
+ local list = AIRoadTypeList(AIRoad.GetRoadTramType(road_type));
+ foreach (rt, _ in list) {
+ if (AIRoad._HasRoadType(tile, rt)) {
+ return true;
+ }
+ }
+ return false;
+}
diff --git a/bin/ai/compat_1.0.nut b/bin/ai/compat_1.0.nut
index b8da71a194..6b76f11534 100644
--- a/bin/ai/compat_1.0.nut
+++ b/bin/ai/compat_1.0.nut
@@ -131,3 +131,16 @@ AIGroup.CreateGroup <- function(vehicle_type)
{
return AIGroup._CreateGroup(vehicle_type, AIGroup.GROUP_INVALID);
}
+
+/* 13 really checks RoadType against RoadType */
+AIRoad._HasRoadType <- AIRoad.HasRoadType;
+AIRoad.HasRoadType <- function(tile, road_type)
+{
+ local list = AIRoadTypeList(AIRoad.GetRoadTramType(road_type));
+ foreach (rt, _ in list) {
+ if (AIRoad._HasRoadType(tile, rt)) {
+ return true;
+ }
+ }
+ return false;
+}
diff --git a/bin/ai/compat_1.1.nut b/bin/ai/compat_1.1.nut
index f1bda9c7fe..9c568a7006 100644
--- a/bin/ai/compat_1.1.nut
+++ b/bin/ai/compat_1.1.nut
@@ -68,3 +68,16 @@ AIGroup.CreateGroup <- function(vehicle_type)
{
return AIGroup._CreateGroup(vehicle_type, AIGroup.GROUP_INVALID);
}
+
+/* 13 really checks RoadType against RoadType */
+AIRoad._HasRoadType <- AIRoad.HasRoadType;
+AIRoad.HasRoadType <- function(tile, road_type)
+{
+ local list = AIRoadTypeList(AIRoad.GetRoadTramType(road_type));
+ foreach (rt, _ in list) {
+ if (AIRoad._HasRoadType(tile, rt)) {
+ return true;
+ }
+ }
+ return false;
+}
diff --git a/bin/ai/compat_1.10.nut b/bin/ai/compat_1.10.nut
index 2baaddb836..44bc2542ce 100644
--- a/bin/ai/compat_1.10.nut
+++ b/bin/ai/compat_1.10.nut
@@ -6,3 +6,16 @@
*/
AILog.Info("1.10 API compatibility in effect.");
+
+/* 13 really checks RoadType against RoadType */
+AIRoad._HasRoadType <- AIRoad.HasRoadType;
+AIRoad.HasRoadType <- function(tile, road_type)
+{
+ local list = AIRoadTypeList(AIRoad.GetRoadTramType(road_type));
+ foreach (rt, _ in list) {
+ if (AIRoad._HasRoadType(tile, rt)) {
+ return true;
+ }
+ }
+ return false;
+}
diff --git a/bin/ai/compat_1.11.nut b/bin/ai/compat_1.11.nut
index 887f3f7fd9..3d8370ffc6 100644
--- a/bin/ai/compat_1.11.nut
+++ b/bin/ai/compat_1.11.nut
@@ -6,3 +6,16 @@
*/
AILog.Info("1.11 API compatibility in effect.");
+
+/* 13 really checks RoadType against RoadType */
+AIRoad._HasRoadType <- AIRoad.HasRoadType;
+AIRoad.HasRoadType <- function(tile, road_type)
+{
+ local list = AIRoadTypeList(AIRoad.GetRoadTramType(road_type));
+ foreach (rt, _ in list) {
+ if (AIRoad._HasRoadType(tile, rt)) {
+ return true;
+ }
+ }
+ return false;
+}
diff --git a/bin/ai/compat_1.2.nut b/bin/ai/compat_1.2.nut
index 550f79969c..fc52b04935 100644
--- a/bin/ai/compat_1.2.nut
+++ b/bin/ai/compat_1.2.nut
@@ -20,3 +20,16 @@ AIGroup.CreateGroup <- function(vehicle_type)
{
return AIGroup._CreateGroup(vehicle_type, AIGroup.GROUP_INVALID);
}
+
+/* 13 really checks RoadType against RoadType */
+AIRoad._HasRoadType <- AIRoad.HasRoadType;
+AIRoad.HasRoadType <- function(tile, road_type)
+{
+ local list = AIRoadTypeList(AIRoad.GetRoadTramType(road_type));
+ foreach (rt, _ in list) {
+ if (AIRoad._HasRoadType(tile, rt)) {
+ return true;
+ }
+ }
+ return false;
+}
diff --git a/bin/ai/compat_1.3.nut b/bin/ai/compat_1.3.nut
index 6b2c7e8a71..a06e98d2dd 100644
--- a/bin/ai/compat_1.3.nut
+++ b/bin/ai/compat_1.3.nut
@@ -20,3 +20,16 @@ AIGroup.CreateGroup <- function(vehicle_type)
{
return AIGroup._CreateGroup(vehicle_type, AIGroup.GROUP_INVALID);
}
+
+/* 13 really checks RoadType against RoadType */
+AIRoad._HasRoadType <- AIRoad.HasRoadType;
+AIRoad.HasRoadType <- function(tile, road_type)
+{
+ local list = AIRoadTypeList(AIRoad.GetRoadTramType(road_type));
+ foreach (rt, _ in list) {
+ if (AIRoad._HasRoadType(tile, rt)) {
+ return true;
+ }
+ }
+ return false;
+}
diff --git a/bin/ai/compat_1.4.nut b/bin/ai/compat_1.4.nut
index a9ab5a4757..6de24bf80c 100644
--- a/bin/ai/compat_1.4.nut
+++ b/bin/ai/compat_1.4.nut
@@ -20,3 +20,16 @@ AIGroup.CreateGroup <- function(vehicle_type)
{
return AIGroup._CreateGroup(vehicle_type, AIGroup.GROUP_INVALID);
}
+
+/* 13 really checks RoadType against RoadType */
+AIRoad._HasRoadType <- AIRoad.HasRoadType;
+AIRoad.HasRoadType <- function(tile, road_type)
+{
+ local list = AIRoadTypeList(AIRoad.GetRoadTramType(road_type));
+ foreach (rt, _ in list) {
+ if (AIRoad._HasRoadType(tile, rt)) {
+ return true;
+ }
+ }
+ return false;
+}
diff --git a/bin/ai/compat_1.5.nut b/bin/ai/compat_1.5.nut
index 23944149f6..8ff5a39d15 100644
--- a/bin/ai/compat_1.5.nut
+++ b/bin/ai/compat_1.5.nut
@@ -20,3 +20,16 @@ AIGroup.CreateGroup <- function(vehicle_type)
{
return AIGroup._CreateGroup(vehicle_type, AIGroup.GROUP_INVALID);
}
+
+/* 13 really checks RoadType against RoadType */
+AIRoad._HasRoadType <- AIRoad.HasRoadType;
+AIRoad.HasRoadType <- function(tile, road_type)
+{
+ local list = AIRoadTypeList(AIRoad.GetRoadTramType(road_type));
+ foreach (rt, _ in list) {
+ if (AIRoad._HasRoadType(tile, rt)) {
+ return true;
+ }
+ }
+ return false;
+}
diff --git a/bin/ai/compat_1.6.nut b/bin/ai/compat_1.6.nut
index bcbe91455c..91744512a7 100644
--- a/bin/ai/compat_1.6.nut
+++ b/bin/ai/compat_1.6.nut
@@ -20,3 +20,16 @@ AIGroup.CreateGroup <- function(vehicle_type)
{
return AIGroup._CreateGroup(vehicle_type, AIGroup.GROUP_INVALID);
}
+
+/* 13 really checks RoadType against RoadType */
+AIRoad._HasRoadType <- AIRoad.HasRoadType;
+AIRoad.HasRoadType <- function(tile, road_type)
+{
+ local list = AIRoadTypeList(AIRoad.GetRoadTramType(road_type));
+ foreach (rt, _ in list) {
+ if (AIRoad._HasRoadType(tile, rt)) {
+ return true;
+ }
+ }
+ return false;
+}
diff --git a/bin/ai/compat_1.7.nut b/bin/ai/compat_1.7.nut
index 7c2fd9b825..584a970f60 100644
--- a/bin/ai/compat_1.7.nut
+++ b/bin/ai/compat_1.7.nut
@@ -20,3 +20,16 @@ AIGroup.CreateGroup <- function(vehicle_type)
{
return AIGroup._CreateGroup(vehicle_type, AIGroup.GROUP_INVALID);
}
+
+/* 13 really checks RoadType against RoadType */
+AIRoad._HasRoadType <- AIRoad.HasRoadType;
+AIRoad.HasRoadType <- function(tile, road_type)
+{
+ local list = AIRoadTypeList(AIRoad.GetRoadTramType(road_type));
+ foreach (rt, _ in list) {
+ if (AIRoad._HasRoadType(tile, rt)) {
+ return true;
+ }
+ }
+ return false;
+}
diff --git a/bin/ai/compat_1.8.nut b/bin/ai/compat_1.8.nut
index a118a63b57..f57a0eab27 100644
--- a/bin/ai/compat_1.8.nut
+++ b/bin/ai/compat_1.8.nut
@@ -20,3 +20,16 @@ AIGroup.CreateGroup <- function(vehicle_type)
{
return AIGroup._CreateGroup(vehicle_type, AIGroup.GROUP_INVALID);
}
+
+/* 13 really checks RoadType against RoadType */
+AIRoad._HasRoadType <- AIRoad.HasRoadType;
+AIRoad.HasRoadType <- function(tile, road_type)
+{
+ local list = AIRoadTypeList(AIRoad.GetRoadTramType(road_type));
+ foreach (rt, _ in list) {
+ if (AIRoad._HasRoadType(tile, rt)) {
+ return true;
+ }
+ }
+ return false;
+}
diff --git a/bin/ai/compat_1.9.nut b/bin/ai/compat_1.9.nut
index a3d0941327..0dde6dc6da 100644
--- a/bin/ai/compat_1.9.nut
+++ b/bin/ai/compat_1.9.nut
@@ -6,3 +6,16 @@
*/
AILog.Info("1.9 API compatibility in effect.");
+
+/* 13 really checks RoadType against RoadType */
+AIRoad._HasRoadType <- AIRoad.HasRoadType;
+AIRoad.HasRoadType <- function(tile, road_type)
+{
+ local list = AIRoadTypeList(AIRoad.GetRoadTramType(road_type));
+ foreach (rt, _ in list) {
+ if (AIRoad._HasRoadType(tile, rt)) {
+ return true;
+ }
+ }
+ return false;
+}
diff --git a/bin/ai/compat_12.nut b/bin/ai/compat_12.nut
index 3081fb58e8..d54895632f 100644
--- a/bin/ai/compat_12.nut
+++ b/bin/ai/compat_12.nut
@@ -4,3 +4,18 @@
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see .
*/
+
+AILog.Info("12 API compatibility in effect.");
+
+/* 13 really checks RoadType against RoadType */
+AIRoad._HasRoadType <- AIRoad.HasRoadType;
+AIRoad.HasRoadType <- function(tile, road_type)
+{
+ local list = AIRoadTypeList(AIRoad.GetRoadTramType(road_type));
+ foreach (rt, _ in list) {
+ if (AIRoad._HasRoadType(tile, rt)) {
+ return true;
+ }
+ }
+ return false;
+}
diff --git a/bin/ai/compat_13.nut b/bin/ai/compat_13.nut
new file mode 100644
index 0000000000..3081fb58e8
--- /dev/null
+++ b/bin/ai/compat_13.nut
@@ -0,0 +1,6 @@
+/*
+ * This file is part of OpenTTD.
+ * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
+ * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see .
+ */
diff --git a/bin/game/compat_1.10.nut b/bin/game/compat_1.10.nut
index 92cef84c53..2559ff0a9c 100644
--- a/bin/game/compat_1.10.nut
+++ b/bin/game/compat_1.10.nut
@@ -13,3 +13,16 @@ GSCompany.ChangeBankBalance <- function(company, delta, expenses_type)
{
return GSCompany._ChangeBankBalance(company, delta, expenses_type, GSMap.TILE_INVALID);
}
+
+/* 13 really checks RoadType against RoadType */
+GSRoad._HasRoadType <- GSRoad.HasRoadType;
+GSRoad.HasRoadType <- function(tile, road_type)
+{
+ local list = GSRoadTypeList(GSRoad.GetRoadTramType(road_type));
+ foreach (rt, _ in list) {
+ if (GSRoad._HasRoadType(tile, rt)) {
+ return true;
+ }
+ }
+ return false;
+}
diff --git a/bin/game/compat_1.11.nut b/bin/game/compat_1.11.nut
index fa240b5d2d..eac9a05d36 100644
--- a/bin/game/compat_1.11.nut
+++ b/bin/game/compat_1.11.nut
@@ -6,3 +6,16 @@
*/
GSLog.Info("1.11 API compatibility in effect.");
+
+/* 13 really checks RoadType against RoadType */
+GSRoad._HasRoadType <- GSRoad.HasRoadType;
+GSRoad.HasRoadType <- function(tile, road_type)
+{
+ local list = GSRoadTypeList(GSRoad.GetRoadTramType(road_type));
+ foreach (rt, _ in list) {
+ if (GSRoad._HasRoadType(tile, rt)) {
+ return true;
+ }
+ }
+ return false;
+}
diff --git a/bin/game/compat_1.2.nut b/bin/game/compat_1.2.nut
index 5fb29efedf..66e2ca5d62 100644
--- a/bin/game/compat_1.2.nut
+++ b/bin/game/compat_1.2.nut
@@ -35,3 +35,16 @@ GSCompany.ChangeBankBalance <- function(company, delta, expenses_type)
{
return GSCompany._ChangeBankBalance(company, delta, expenses_type, GSMap.TILE_INVALID);
}
+
+/* 13 really checks RoadType against RoadType */
+GSRoad._HasRoadType <- GSRoad.HasRoadType;
+GSRoad.HasRoadType <- function(tile, road_type)
+{
+ local list = GSRoadTypeList(GSRoad.GetRoadTramType(road_type));
+ foreach (rt, _ in list) {
+ if (GSRoad._HasRoadType(tile, rt)) {
+ return true;
+ }
+ }
+ return false;
+}
diff --git a/bin/game/compat_1.3.nut b/bin/game/compat_1.3.nut
index 7546e54c69..9986ea03e5 100644
--- a/bin/game/compat_1.3.nut
+++ b/bin/game/compat_1.3.nut
@@ -35,3 +35,16 @@ GSCompany.ChangeBankBalance <- function(company, delta, expenses_type)
{
return GSCompany._ChangeBankBalance(company, delta, expenses_type, GSMap.TILE_INVALID);
}
+
+/* 13 really checks RoadType against RoadType */
+GSRoad._HasRoadType <- GSRoad.HasRoadType;
+GSRoad.HasRoadType <- function(tile, road_type)
+{
+ local list = GSRoadTypeList(GSRoad.GetRoadTramType(road_type));
+ foreach (rt, _ in list) {
+ if (GSRoad._HasRoadType(tile, rt)) {
+ return true;
+ }
+ }
+ return false;
+}
diff --git a/bin/game/compat_1.4.nut b/bin/game/compat_1.4.nut
index c90b3e550b..b3ec5c45cb 100644
--- a/bin/game/compat_1.4.nut
+++ b/bin/game/compat_1.4.nut
@@ -28,3 +28,15 @@ GSCompany.ChangeBankBalance <- function(company, delta, expenses_type)
return GSCompany._ChangeBankBalance(company, delta, expenses_type, GSMap.TILE_INVALID);
}
+/* 13 really checks RoadType against RoadType */
+GSRoad._HasRoadType <- GSRoad.HasRoadType;
+GSRoad.HasRoadType <- function(tile, road_type)
+{
+ local list = GSRoadTypeList(GSRoad.GetRoadTramType(road_type));
+ foreach (rt, _ in list) {
+ if (GSRoad._HasRoadType(tile, rt)) {
+ return true;
+ }
+ }
+ return false;
+}
diff --git a/bin/game/compat_1.5.nut b/bin/game/compat_1.5.nut
index 0c62e56462..2d25d7acf0 100644
--- a/bin/game/compat_1.5.nut
+++ b/bin/game/compat_1.5.nut
@@ -20,3 +20,16 @@ GSCompany.ChangeBankBalance <- function(company, delta, expenses_type)
{
return GSCompany._ChangeBankBalance(company, delta, expenses_type, GSMap.TILE_INVALID);
}
+
+/* 13 really checks RoadType against RoadType */
+GSRoad._HasRoadType <- GSRoad.HasRoadType;
+GSRoad.HasRoadType <- function(tile, road_type)
+{
+ local list = GSRoadTypeList(GSRoad.GetRoadTramType(road_type));
+ foreach (rt, _ in list) {
+ if (GSRoad._HasRoadType(tile, rt)) {
+ return true;
+ }
+ }
+ return false;
+}
diff --git a/bin/game/compat_1.6.nut b/bin/game/compat_1.6.nut
index 198b863a77..d205832209 100644
--- a/bin/game/compat_1.6.nut
+++ b/bin/game/compat_1.6.nut
@@ -20,3 +20,16 @@ GSCompany.ChangeBankBalance <- function(company, delta, expenses_type)
{
return GSCompany._ChangeBankBalance(company, delta, expenses_type, GSMap.TILE_INVALID);
}
+
+/* 13 really checks RoadType against RoadType */
+GSRoad._HasRoadType <- GSRoad.HasRoadType;
+GSRoad.HasRoadType <- function(tile, road_type)
+{
+ local list = GSRoadTypeList(GSRoad.GetRoadTramType(road_type));
+ foreach (rt, _ in list) {
+ if (GSRoad._HasRoadType(tile, rt)) {
+ return true;
+ }
+ }
+ return false;
+}
diff --git a/bin/game/compat_1.7.nut b/bin/game/compat_1.7.nut
index 76dc424353..1108f41a7c 100644
--- a/bin/game/compat_1.7.nut
+++ b/bin/game/compat_1.7.nut
@@ -20,3 +20,16 @@ GSCompany.ChangeBankBalance <- function(company, delta, expenses_type)
{
return GSCompany._ChangeBankBalance(company, delta, expenses_type, GSMap.TILE_INVALID);
}
+
+/* 13 really checks RoadType against RoadType */
+GSRoad._HasRoadType <- GSRoad.HasRoadType;
+GSRoad.HasRoadType <- function(tile, road_type)
+{
+ local list = GSRoadTypeList(GSRoad.GetRoadTramType(road_type));
+ foreach (rt, _ in list) {
+ if (GSRoad._HasRoadType(tile, rt)) {
+ return true;
+ }
+ }
+ return false;
+}
diff --git a/bin/game/compat_1.8.nut b/bin/game/compat_1.8.nut
index b9d27458a9..139f91214b 100644
--- a/bin/game/compat_1.8.nut
+++ b/bin/game/compat_1.8.nut
@@ -20,3 +20,16 @@ GSCompany.ChangeBankBalance <- function(company, delta, expenses_type)
{
return GSCompany._ChangeBankBalance(company, delta, expenses_type, GSMap.TILE_INVALID);
}
+
+/* 13 really checks RoadType against RoadType */
+GSRoad._HasRoadType <- GSRoad.HasRoadType;
+GSRoad.HasRoadType <- function(tile, road_type)
+{
+ local list = GSRoadTypeList(GSRoad.GetRoadTramType(road_type));
+ foreach (rt, _ in list) {
+ if (GSRoad._HasRoadType(tile, rt)) {
+ return true;
+ }
+ }
+ return false;
+}
diff --git a/bin/game/compat_1.9.nut b/bin/game/compat_1.9.nut
index 32eec114af..053e377d4c 100644
--- a/bin/game/compat_1.9.nut
+++ b/bin/game/compat_1.9.nut
@@ -13,3 +13,16 @@ GSCompany.ChangeBankBalance <- function(company, delta, expenses_type)
{
return GSCompany._ChangeBankBalance(company, delta, expenses_type, GSMap.TILE_INVALID);
}
+
+/* 13 really checks RoadType against RoadType */
+GSRoad._HasRoadType <- GSRoad.HasRoadType;
+GSRoad.HasRoadType <- function(tile, road_type)
+{
+ local list = GSRoadTypeList(GSRoad.GetRoadTramType(road_type));
+ foreach (rt, _ in list) {
+ if (GSRoad._HasRoadType(tile, rt)) {
+ return true;
+ }
+ }
+ return false;
+}
diff --git a/bin/game/compat_12.nut b/bin/game/compat_12.nut
index 3081fb58e8..a11cd04a23 100644
--- a/bin/game/compat_12.nut
+++ b/bin/game/compat_12.nut
@@ -4,3 +4,18 @@
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see .
*/
+
+GSLog.Info("12 API compatibility in effect.");
+
+/* 13 really checks RoadType against RoadType */
+GSRoad._HasRoadType <- GSRoad.HasRoadType;
+GSRoad.HasRoadType <- function(tile, road_type)
+{
+ local list = GSRoadTypeList(GSRoad.GetRoadTramType(road_type));
+ foreach (rt, _ in list) {
+ if (GSRoad._HasRoadType(tile, rt)) {
+ return true;
+ }
+ }
+ return false;
+}
diff --git a/bin/game/compat_13.nut b/bin/game/compat_13.nut
new file mode 100644
index 0000000000..3081fb58e8
--- /dev/null
+++ b/bin/game/compat_13.nut
@@ -0,0 +1,6 @@
+/*
+ * This file is part of OpenTTD.
+ * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
+ * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see .
+ */
diff --git a/changelog.txt b/changelog.txt
index 5e0f4204f9..650c42586e 100644
--- a/changelog.txt
+++ b/changelog.txt
@@ -1,3 +1,117 @@
+13.0-beta2 (2022-11-27)
+------------------------------------------------------------------------
+Feature: Allow AI/GS to be fully modified in scenario editor (#10152)
+Feature: Display power-to-weight ratio in ground vehicle details GUI (#10123)
+Feature: Variable interface scaling (with chunky bevels!) (#10114)
+Feature: Hotkey to honk a vehicle's horn (#10110)
+Feature: Split AI/Game Script configuration windows and add them to world gen window (#10058)
+Feature: [GS] Scriptable league tables (#10001)
+Feature: Multi-track level crossings (#9931)
+Feature: Improved local authority action window (#9928)
+Feature: Automatic console command screenshot numbering with a filename ending in '#' (#9781)
+Feature: Add buttons to toggle music in the Game Options menu (#9727)
+Feature: Contextual actions for vehicles grouped by shared orders (#8425)
+Feature: Add cargo filter support to vehicle list (#8308)
+Feature: Show the cargoes the vehicles can carry in the vehicle list window (#8304)
+Change: Allow building canal by area outside editor (#10173)
+Change: Minor improvements to the new Finance GUI (#10168)
+Change: Let AI developers edit non-editable AI/Game Script Parameters (#8895)
+Change: Allow building docks on clearable watered object tiles (#8514)
+Fix #8770: Center vehicle status bar icon (#10178)
+Fix: Crash if error message window is too wide for screen. (#10172)
+Fix #10155: Network games not syncing company settings properly (#10158)
+Fix #10154: Network game desync related to setting a random company face (#10157)
+Fix #10011: Incorrect infrastructure totals when overbuilding bay road stop (#10143)
+Fix #10117: Object burst limit allowed one fewer object than the setting (#10120)
+Fix #10023: Allow negative input in text fields when needed (#10112)
+Fix #9908: Fix crash which could occur when a company was deleted when a depot window was open (#9912)
+
+
+13.0-beta1 (2022-10-31)
+------------------------------------------------------------------------
+Feature: Airport construction GUI displays infrastructure cost (#10094)
+Feature: Purchase land multiple tiles at a time (#10027)
+Feature: Add sticky pin & shade widgets to Object Selection UI panel (#10019, #10020)
+Feature: Improved handling of HiDPI and mixed-DPI screens (#9994, #9996, #9997, #10064)
+Feature: Alternative linkgraph colour schemes (#9866)
+Feature: Allow Shift+Insert as paste in edit box (#9836)
+Feature: Setting to make the local town authority rubber-stamp all actions (#9833)
+Feature: Add/extend console commands to enable screenshot automation (#9771)
+Feature: [Linkgraph] Show a tooltip with statistics when hovering a link (#9760)
+Feature: Build objects by area (#9709)
+Feature: Add setting to hide news about competitors vehicle crash (#9653)
+Feature: Ctrl-click to remove fully autoreplaced vehicles from list (#9639)
+Feature: Wide rivers on map generation (#9628)
+Add: [Script] ScriptCargo::GetWeight to get cargo weights (#9930)
+Add: Command line option to skip NewGRF scanning (#9879)
+Add: Show video driver name in Game Options window (#9872)
+Add: [NewGRF] Map seed as global variable (#9834)
+Add: [Script] IndustryType::ResolveNewGRFID to resolve industry id from grf_local_id and grfid (#9798)
+Add: [Script] ObjectType::ResolveNewGRFID to resolve object id from grfid and grf_local_id (#9795)
+Update: To all the friends we have lost and those we have gained (#10000)
+Change: Use the Simulation subcategory to openttd.desktop (#10015)
+Change: Constantly update destination of 'any depot' orders (#9959)
+Change: Use an indent, not a dash, to list train capacity (#9887)
+Change: [NewGRF] Increase vehicle sprite stack from 4 layers to 8 (#9863)
+Change: Don't pay Property Maintenance on stations when Infrastructure Maintenance is disabled (#9828)
+Change: Improved layout of the finance window (#9827)
+Change: [Admin] Bump admin port protocol due to command changes (#9754)
+Change: Suppress vehicle age warnings for stopped vehicles (#9718)
+Change: Make pf.yapf.rail_firstred_twoway_eol on by default (#9544)
+Change: Deliver cargo to the closest industry first (#9536)
+Fix: Lots of fixes to how windows handle resizing (#10040, #10042, #10046, #10051, #10056, #10068, #10070, #10098)
+Fix: Console commands list_ai output was truncated with a suitably large number of AIs (#10075)
+Fix #9876: MacBook Touch Bar crash / render issues w/ 32bpp graphics (#10060)
+Fix: Reduce framerate overhead in Train::Tick (#10055)
+Fix: Only open scenario editor date query once (#10050)
+Fix #10048: Don't relocate company HQ on the same exact location (#10049)
+Fix #10038: Missing upper bounds check when loading custom playlists (#10039)
+Fix: Wrong string used to determine size of zoomed out station sign (#10036)
+Fix: Disable "turn around" button for other companies' road vehicles (#10033)
+Fix: Online Players list mouse hover behaviour (#10031)
+Fix: [NewGRF] Weirdness of new stations (#10017)
+Fix #9854: DrawStringMultiLine() could draw beyond its bounding box (#10014)
+Fix: Incorrect player name in online players window (#10013)
+Fix #8099: News window zoom level fixes (#10005)
+Fix: [NewGRF] Upper 16 random bits should be the same for all station tiles in callback 140 (#9992)
+Fix #9989: £0 Net Profit is neither negative nor positive (#9991)
+Fix #9804: Only apply sprite_zoom_min setting when sprites available (#9988)
+Fix #9972: Add missing fill/resize flags on Framerate window widgets (#9982)
+Fix #9935: Use more selectivity when building SSE specific code (#9980)
+Fix #9940: Print debuglevel parse errors to console when changed from console (#9979)
+Fix #9977: Clearing the console with a large number of lines could cause a crash (#9978)
+Fix #9974: Console command getsysdate did not work due to off-by-one error (#9975)
+Fix: [NewGRF] Default value of RailVehicleInfo::railveh_type was inconsistent with other default properties (#9967)
+Fix #8584: Vehicles with shared orders getting invalid or unexpected start dates (#9955)
+Fix #9951: [NewGRF] Scenario editor random industries button broke NewGRF persistent storage (#9952)
+Fix: Validation of various internal command parameters that could have allowed a rogue client to crash servers (#9942, #9943, #9944, #9945, #9946, #9947, #9948, #9950)
+Fix #9937: Station industries_near incorrect after removing part moved sign (#9938)
+Fix: [Script] ScriptRoad::HasRoadType really check for RoadType (#9934)
+Fix #9363: Rebuild client list on reinit event (#9929)
+Fix #9925: Industry tile layout validation for layouts of only one tile (#9926)
+Fix #9918: Reset industy last production year on scenario start (#9920)
+Fix #9914: Prevent more useless pathfinder run for blocked vehicles (#9917)
+Fix: List a max of four share owners instead of three (#9905)
+Fix: [NewGRF] Industry layouts with zero regular tiles should be invalid (#9902)
+Fix #9869: Remove docking tile when doing a clear square (#9898)
+Fix: New player companies use favorite manager face, if saved (#9895)
+Fix: Towns don't build parallel, redundant bridges (#9891)
+Fix #9712: Cap town bridge length at original 11-tile limit (#9890)
+Fix #9883: Show cost/income float over end tile of rail or road construction (#9889)
+Fix #9870: Don't update infrastructure totals when overbuilding object on canal (#9888)
+Fix #9877: GS could trigger 'Cost: £0' cost message (#9878)
+Fix 44f2ef1: [strgen] Allow gender for {CARGO_SHORT} (#9873)
+Fix #9867: Industry::stations_near not filled at industry creation (#9868)
+Fix #9853: Incorrect merge of guiflags and flags for osk_activation (#9855)
+Fix #6544: Don't join AI company when loading network game in singleplayer (#9794)
+Fix: Company values do not properly account for shares (#9770)
+Fix #9546: Crash when no industries are present in game (#9726)
+Fix #9708: [Linkgraph] Don't assume vehicles have a non-zero max speed (#9693)
+Fix #9665: [Linkgraph] Fix travel times of non-direct journeys (#9693)
+Fix #8797: Use logical rail length when placing signals (#9652)
+Cleanup: [NewGRF] Remove unused flag sprites (#10052)
+
+
12.2 (2022-04-02)
------------------------------------------------------------------------
Feature: Remember the last-used signal between games (#9792)
diff --git a/cmake/CompileFlags.cmake b/cmake/CompileFlags.cmake
index ad4a46aca7..27d9a48efa 100644
--- a/cmake/CompileFlags.cmake
+++ b/cmake/CompileFlags.cmake
@@ -44,8 +44,8 @@ macro(compile_flags)
"$<$>:-fstack-protector>" # Prevent undefined references when _FORTIFY_SOURCE > 0
)
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
- add_link_options(
- "$<$:-Wl,--disable-dynamicbase,--disable-high-entropy-va,--default-image-base-low>" # ASLR somehow breaks linking for x64 Debug builds
+ add_compile_options(
+ "$<$:-Wa,-mbig-obj>" # Switch to pe-bigobj-x86-64 as x64 Debug builds push pe-x86-64 to the limits (linking errors with ASLR, ...)
)
endif()
endif()
@@ -56,6 +56,11 @@ macro(compile_flags)
if(MSVC)
add_compile_options(/W3)
+ if(MSVC_VERSION GREATER 1929 AND CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
+ # Starting with version 19.30, there is an optimisation bug, see #9966 for details
+ # This flag disables the broken optimisation to work around the bug
+ add_compile_options(/d2ssa-rse-)
+ endif()
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang" OR CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
add_compile_options(
-W
diff --git a/cmake/InstallAndPackage.cmake b/cmake/InstallAndPackage.cmake
index 8c3c98b79b..3868a74d17 100644
--- a/cmake/InstallAndPackage.cmake
+++ b/cmake/InstallAndPackage.cmake
@@ -123,6 +123,8 @@ set(CPACK_STRIP_FILES YES)
set(CPACK_OUTPUT_FILE_PREFIX "bundles")
if(APPLE)
+ # Stripping would produce unreadable stacktraces.
+ set(CPACK_STRIP_FILES NO)
set(CPACK_GENERATOR "Bundle")
include(PackageBundle)
@@ -130,7 +132,7 @@ if(APPLE)
set(CPACK_PACKAGE_FILE_NAME "openttd-#CPACK_PACKAGE_VERSION#-macos-universal")
else()
set(CPACK_PACKAGE_FILE_NAME "openttd-#CPACK_PACKAGE_VERSION#-macos-${CPACK_SYSTEM_NAME}")
- endif()
+ endif()
elseif(WIN32)
set(CPACK_GENERATOR "ZIP")
if(OPTION_USE_NSIS)
@@ -182,6 +184,10 @@ elseif(UNIX)
if(DISTRO_ID STREQUAL "arch")
set(PLATFORM "arch")
set(CPACK_GENERATOR "TXZ")
+ elseif(DISTRO_ID STREQUAL "fedora" OR DISTRO_ID STREQUAL "rhel")
+ set(PLATFORM "fedora")
+ set(CPACK_GENERATOR "RPM")
+ include(PackageRPM)
else()
set(UNSUPPORTED_PLATFORM_NAME "Linux distribution '${DISTRO_ID}' from /etc/os-release")
endif()
diff --git a/cmake/LinkPackage.cmake b/cmake/LinkPackage.cmake
index 0f62d3296c..bea9deb0cf 100644
--- a/cmake/LinkPackage.cmake
+++ b/cmake/LinkPackage.cmake
@@ -18,6 +18,6 @@ function(link_package NAME)
message(STATUS "${NAME} found -- -DWITH_${UCNAME} -- ${${NAME}_INCLUDE_DIRS} ${${NAME}_INCLUDE_DIR} -- ${${NAME}_LIBRARIES} ${${NAME}_LIBRARY}")
endif()
elseif(LP_ENCOURAGED)
- message(WARNING "${NAME} not found; compiling OpenTTD without ${NAME} is strongly disencouraged")
+ message(WARNING "${NAME} not found; compiling OpenTTD without ${NAME} is strongly discouraged")
endif()
endfunction()
diff --git a/cmake/MSVCFilters.cmake b/cmake/MSVCFilters.cmake
index db53fc2258..b334f6a91a 100644
--- a/cmake/MSVCFilters.cmake
+++ b/cmake/MSVCFilters.cmake
@@ -3,6 +3,7 @@
source_group("AI Core" REGULAR_EXPRESSION "src/ai/")
source_group("Blitters" REGULAR_EXPRESSION "src/blitter/")
source_group("Core Source Code" REGULAR_EXPRESSION "src/core/")
+source_group("Font Cache" REGULAR_EXPRESSION "src/fontcache/")
source_group("Game Core" REGULAR_EXPRESSION "src/game/")
source_group("MD5" REGULAR_EXPRESSION "src/3rdparty/md5/")
source_group("Misc" REGULAR_EXPRESSION "src/misc/")
diff --git a/cmake/PackageRPM.cmake b/cmake/PackageRPM.cmake
new file mode 100644
index 0000000000..d91bb28326
--- /dev/null
+++ b/cmake/PackageRPM.cmake
@@ -0,0 +1 @@
+set(CPACK_RPM_PACKAGE_ARCHITECTURE "${ARCHITECTURE}")
diff --git a/cmake/scripts/GenerateWidget.cmake b/cmake/scripts/GenerateWidget.cmake
index f46a67e690..b6748422f5 100644
--- a/cmake/scripts/GenerateWidget.cmake
+++ b/cmake/scripts/GenerateWidget.cmake
@@ -13,6 +13,7 @@ cmake_minimum_required(VERSION 3.5)
# The parameter "enumname" specifies the enumeration to extract. This can also be a regular expression.
# The parameter "filename" specifies the relative path to the file, where the enumeration is extracted from. This can also be a glob expression.
#
+# All files where enumerations are extracted from are automatically added via #include
#
if(NOT GENERATE_SOURCE_FILE)
@@ -41,6 +42,7 @@ foreach(ENUM IN LISTS ENUM_LINES)
string(REPLACE "${CMAKE_CURRENT_SOURCE_DIR}/" "" FILE ${FILE})
string(APPEND ${PLACE_HOLDER} "\n${ADD_INDENT}/* automatically generated from ${FILE} */")
+ list(APPEND INCLUDES "#include \"${FILE}\"")
foreach(LINE IN LISTS SOURCE_LINES)
string(REPLACE "${RM_INDENT}" "" LINE "${LINE}")
@@ -116,4 +118,7 @@ foreach(ENUM IN LISTS ENUM_LINES)
endforeach()
endforeach()
+ list(REMOVE_DUPLICATES INCLUDES)
+ string(REPLACE ";" "\n" INCLUDES "${INCLUDES}")
+
configure_file(${GENERATE_SOURCE_FILE} ${GENERATE_BINARY_FILE})
diff --git a/cmake/scripts/Regression.cmake b/cmake/scripts/Regression.cmake
index e21a86d292..19fece83f5 100644
--- a/cmake/scripts/Regression.cmake
+++ b/cmake/scripts/Regression.cmake
@@ -35,6 +35,7 @@ execute_process(COMMAND ${OPENTTD_EXECUTABLE}
-vnull:ticks=30000
-d script=2
-d misc=9
+ -Q
OUTPUT_VARIABLE REGRESSION_OUTPUT
ERROR_VARIABLE REGRESSION_RESULT
OUTPUT_STRIP_TRAILING_WHITESPACE
diff --git a/cmake/scripts/SquirrelIncludes.cmake b/cmake/scripts/SquirrelIncludes.cmake
index 672ad09531..ef81ddf041 100644
--- a/cmake/scripts/SquirrelIncludes.cmake
+++ b/cmake/scripts/SquirrelIncludes.cmake
@@ -12,32 +12,11 @@ endif()
if(NOT APIUC)
message(FATAL_ERROR "Script needs APIUC defined")
endif()
+if(NOT API_FILES)
+ message(FATAL_ERROR "Script needs API_FILES defined")
+endif()
-set(ARGC 1)
-set(ARG_READ NO)
-
-# For MSVC CMake runs this script from a batch file using || to detect errors,
-# depending on source path it may quote args, and cause cmd to not understand ||
-# and pass it as argument to ourself.
-# Read all the arguments given to CMake; we are looking for -- and everything
-# that follows, until ||. Those are our api files.
-while(ARGC LESS CMAKE_ARGC)
- set(ARG ${CMAKE_ARGV${ARGC}})
-
- if(ARG STREQUAL "||")
- set(ARG_READ NO)
- endif()
-
- if(ARG_READ)
- list(APPEND SCRIPT_API_BINARY_FILES "${ARG}")
- endif()
-
- if(ARG STREQUAL "--")
- set(ARG_READ YES)
- endif()
-
- math(EXPR ARGC "${ARGC} + 1")
-endwhile()
+file(READ "${API_FILES}" SCRIPT_API_BINARY_FILES)
foreach(FILE IN LISTS SCRIPT_API_BINARY_FILES)
file(STRINGS "${FILE}" LINES REGEX "^void SQ${APIUC}.*_Register\\(Squirrel \\*engine\\)$")
diff --git a/media/baseset/openttd.grf b/media/baseset/openttd.grf
index 837e4b12d2..9e08fb17c3 100644
Binary files a/media/baseset/openttd.grf and b/media/baseset/openttd.grf differ
diff --git a/media/baseset/openttd/flags.nfo b/media/baseset/openttd/flags.nfo
deleted file mode 100644
index 3c125ec79a..0000000000
--- a/media/baseset/openttd/flags.nfo
+++ /dev/null
@@ -1,43 +0,0 @@
-// This file is part of OpenTTD.
-// OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
-// OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-// See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see .
-//
- -1 * 0 0C "Flag graphics"
- -1 * 3 05 14 24
- -1 sprites/flags.png 8bpp 34 8 11 8 0 0 normal
- -1 sprites/flags.png 8bpp 50 8 11 8 0 0 normal
- -1 sprites/flags.png 8bpp 66 8 11 8 0 0 normal
- -1 sprites/flags.png 8bpp 82 8 11 8 0 0 normal
- -1 sprites/flags.png 8bpp 98 8 11 8 0 0 normal
- -1 sprites/flags.png 8bpp 114 8 11 8 0 0 normal
- -1 sprites/flags.png 8bpp 130 8 11 8 0 0 normal
- -1 sprites/flags.png 8bpp 146 8 11 8 0 0 normal
- -1 sprites/flags.png 8bpp 162 8 11 8 0 0 normal
- -1 sprites/flags.png 8bpp 178 8 11 8 0 0 normal
- -1 sprites/flags.png 8bpp 194 8 11 8 0 0 normal
- -1 sprites/flags.png 8bpp 210 8 11 8 0 0 normal
- -1 sprites/flags.png 8bpp 226 8 11 8 0 0 normal
- -1 sprites/flags.png 8bpp 242 8 11 8 0 0 normal
- -1 sprites/flags.png 8bpp 258 8 11 8 0 0 normal
- -1 sprites/flags.png 8bpp 274 8 11 8 0 0 normal
- -1 sprites/flags.png 8bpp 290 8 11 8 0 0 normal
- -1 sprites/flags.png 8bpp 306 8 11 8 0 0 normal
- -1 sprites/flags.png 8bpp 322 8 11 8 0 0 normal
- -1 sprites/flags.png 8bpp 338 8 11 8 0 0 normal
- -1 sprites/flags.png 8bpp 354 8 11 8 0 0 normal
- -1 sprites/flags.png 8bpp 370 8 11 8 0 0 normal
- -1 sprites/flags.png 8bpp 386 8 11 8 0 0 normal
- -1 sprites/flags.png 8bpp 402 8 11 8 0 0 normal
- -1 sprites/flags.png 8bpp 418 8 11 8 0 0 normal
- -1 sprites/flags.png 8bpp 434 8 11 8 0 0 normal
- -1 sprites/flags.png 8bpp 450 8 11 8 0 0 normal
- -1 sprites/flags.png 8bpp 466 8 11 8 0 0 normal
- -1 sprites/flags.png 8bpp 482 8 11 8 0 0 normal
- -1 sprites/flags.png 8bpp 498 8 11 8 0 0 normal
- -1 sprites/flags.png 8bpp 514 8 11 8 0 0 normal
- -1 sprites/flags.png 8bpp 530 8 11 8 0 0 normal
- -1 sprites/flags.png 8bpp 546 8 11 8 0 0 normal
- -1 sprites/flags.png 8bpp 562 8 11 8 0 0 normal
- -1 sprites/flags.png 8bpp 578 8 11 8 0 0 normal
- -1 sprites/flags.png 8bpp 594 8 11 8 0 0 normal
diff --git a/media/baseset/openttd/flags.png b/media/baseset/openttd/flags.png
deleted file mode 100644
index 2e0401ffce..0000000000
Binary files a/media/baseset/openttd/flags.png and /dev/null differ
diff --git a/media/baseset/openttd/oneway.nfo b/media/baseset/openttd/oneway.nfo
index 46f3b8f947..56d2e2809a 100644
--- a/media/baseset/openttd/oneway.nfo
+++ b/media/baseset/openttd/oneway.nfo
@@ -4,10 +4,24 @@
// See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see .
//
-1 * 0 0C "One way road graphics"
- -1 * 3 05 09 06
- -1 sprites/oneway.png 8bpp 34 8 24 16 -12 -8 normal
- -1 sprites/oneway.png 8bpp 66 8 24 16 -12 -8 normal
- -1 sprites/oneway.png 8bpp 98 8 24 16 -12 -8 normal
- -1 sprites/oneway.png 8bpp 130 8 24 16 -12 -8 normal
- -1 sprites/oneway.png 8bpp 162 8 24 16 -12 -8 normal
- -1 sprites/oneway.png 8bpp 194 8 24 16 -12 -8 normal
+ -1 * 3 05 09 12
+ -1 sprites/oneway.png 8bpp 34 8 24 16 -10 -9 normal
+ -1 sprites/oneway.png 8bpp 66 8 24 16 -13 -7 normal
+ -1 sprites/oneway.png 8bpp 98 8 24 16 -12 -8 normal
+ -1 sprites/oneway.png 8bpp 130 8 24 16 -15 -10 normal
+ -1 sprites/oneway.png 8bpp 162 8 24 16 -12 -9 normal
+ -1 sprites/oneway.png 8bpp 194 8 24 16 -11 -8 normal
+
+ -1 sprites/oneway.png 8bpp 34 40 24 16 -13 -10 normal
+ -1 sprites/oneway.png 8bpp 66 40 24 16 -12 -8 normal
+ -1 sprites/oneway.png 8bpp 98 40 24 16 -12 -9 normal
+ -1 sprites/oneway.png 8bpp 130 40 24 16 -11 -8 normal
+ -1 sprites/oneway.png 8bpp 162 40 24 16 -9 -10 normal
+ -1 sprites/oneway.png 8bpp 194 40 24 16 -10 -9 normal
+
+ -1 sprites/oneway.png 8bpp 34 72 24 16 -8 -11 normal
+ -1 sprites/oneway.png 8bpp 66 72 24 16 -11 -5 normal
+ -1 sprites/oneway.png 8bpp 98 72 24 16 -12 -8 normal
+ -1 sprites/oneway.png 8bpp 130 72 24 16 -12 -5 normal
+ -1 sprites/oneway.png 8bpp 162 72 24 16 -14 -10 normal
+ -1 sprites/oneway.png 8bpp 194 72 24 16 -12 -8 normal
diff --git a/media/baseset/openttd/oneway.png b/media/baseset/openttd/oneway.png
index 15542af856..843747677f 100644
Binary files a/media/baseset/openttd/oneway.png and b/media/baseset/openttd/oneway.png differ
diff --git a/media/baseset/openttd/openttd.nfo b/media/baseset/openttd/openttd.nfo
index b0d80824e8..c1ed751c55 100644
--- a/media/baseset/openttd/openttd.nfo
+++ b/media/baseset/openttd/openttd.nfo
@@ -92,7 +92,6 @@
#include "roadstops.nfo"
#include "aqueduct.nfo"
#include "autorail.nfo"
-#include "flags.nfo"
#include "openttdgui.nfo"
#include "airport_preview.nfo"
#include "chars.nfo"
diff --git a/media/baseset/orig_extra.grf b/media/baseset/orig_extra.grf
index 728a8b2555..881c4631ce 100644
Binary files a/media/baseset/orig_extra.grf and b/media/baseset/orig_extra.grf differ
diff --git a/media/baseset/orig_extra/fix_gui_icons.nfo b/media/baseset/orig_extra/fix_gui_icons.nfo
new file mode 100644
index 0000000000..2fdb8880bd
--- /dev/null
+++ b/media/baseset/orig_extra/fix_gui_icons.nfo
@@ -0,0 +1,12 @@
+// This file is part of OpenTTD.
+// OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
+// OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+// See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see .
+//
+ -1 * 0 0C "Fix alignment of button icons."
+
+// Fix alignment of button icons.
+ -1 * 11 0A 03 01 DC 02 01 E2 02 01 E6 02
+ -1 sprites/fix_gui_icons.png 8bpp 8 13 20 20 0 0 normal nocrop
+ -1 sprites/fix_gui_icons.png 8bpp 40 13 20 20 0 0 normal nocrop
+ -1 sprites/fix_gui_icons.png 8bpp 72 13 20 20 0 0 normal nocrop
diff --git a/media/baseset/orig_extra/fix_gui_icons.png b/media/baseset/orig_extra/fix_gui_icons.png
new file mode 100644
index 0000000000..0630179616
Binary files /dev/null and b/media/baseset/orig_extra/fix_gui_icons.png differ
diff --git a/media/baseset/orig_extra/orig_extra.nfo b/media/baseset/orig_extra/orig_extra.nfo
index 903d96a572..e6ecca8a00 100644
--- a/media/baseset/orig_extra/orig_extra.nfo
+++ b/media/baseset/orig_extra/orig_extra.nfo
@@ -83,3 +83,4 @@
#include "rivers/arctic.nfo"
#include "rivers/tropic.nfo"
#include "rivers/toyland.nfo"
+#include "fix_gui_icons.nfo"
diff --git a/media/openttd.desktop b/media/openttd.desktop
index e0aef004cd..6882a0d5c7 100644
--- a/media/openttd.desktop
+++ b/media/openttd.desktop
@@ -6,6 +6,6 @@ Name=OpenTTD
Icon=${BINARY_NAME}
Exec=${BINARY_NAME}
Terminal=false
-Categories=Game;
+Categories=Game;Simulation;
Keywords=game;simulation;transport;tycoon;deluxe;economics;multiplayer;money;train;ship;bus;truck;aircraft;cargo;
@Comment_STR_DESKTOP_SHORTCUT_COMMENT@
diff --git a/os/emscripten/Dockerfile b/os/emscripten/Dockerfile
index 5d54b54114..ae3579ec0a 100644
--- a/os/emscripten/Dockerfile
+++ b/os/emscripten/Dockerfile
@@ -1,4 +1,4 @@
-FROM emscripten/emsdk:2.0.34
+FROM emscripten/emsdk:3.1.28
COPY emsdk-liblzma.patch /
RUN cd /emsdk/upstream/emscripten && patch -p1 < /emsdk-liblzma.patch
diff --git a/os/emscripten/README.md b/os/emscripten/README.md
index 0fff310dda..cf8e3ed692 100644
--- a/os/emscripten/README.md
+++ b/os/emscripten/README.md
@@ -1,23 +1,38 @@
## How to build with Emscripten
-Building with Emscripten works with emsdk 3.0.0 and above.
-
-You will also need libharfbuzz-dev and libicu-dev installed on your host OS.
-
+Please use docker with the supplied `Dockerfile` to build for emscripten.
+It takes care of a few things:
+- Use a version of emscripten we know works
+- Patch in LibLZMA support (as this is not supported by upstream)
+First, build the docker image by navigating in the folder this `README.md` is in, and executing:
```
- sudo apt-get install autoconf automake libtool gperf libharfbuzz-dev libicu-dev libfreetype-dev
-
- ./emscripten-build.sh
+ docker build -t emsdk-lzma .
```
-And now you have in your build folder files like "openttd.html".
+Next, navigate back to the root folder of this project.
-To run it locally, you would have to start a local webserver, like:
+Now we build the host tools first:
+```
+ mkdir build-host
+ docker run -it --rm -v $(pwd):$(pwd) -u $(id -u):$(id -g) --workdir $(pwd)/build-host emsdk-lzma cmake .. -DOPTION_TOOLS_ONLY=ON
+ docker run -it --rm -v $(pwd):$(pwd) -u $(id -u):$(id -g) --workdir $(pwd)/build-host emsdk-lzma make -j$(nproc) tools
+```
+
+Finally, we build the actual game:
+```
+ mkdir build
+ docker run -it --rm -v $(pwd):$(pwd) -u $(id -u):$(id -g) --workdir $(pwd)/build emsdk-lzma emcmake cmake .. -DHOST_BINARY_DIR=../build-host -DCMAKE_BUILD_TYPE=Release -DOPTION_USE_ASSERTS=OFF
+ docker run -it --rm -v $(pwd):$(pwd) -u $(id -u):$(id -g) --workdir $(pwd)/build emsdk-lzma emmake make -j$(nproc)
+```
+
+In the `build` folder you will now see `openttd.html`.
+
+To run it locally, you would have to start a local webserver; something like:
```
cd build
python3 -m http.server
````
-Now you can play the game via http://127.0.0.1:8000/openttd.html .
+You can now play the game via http://127.0.0.1:8000/openttd.html .
diff --git a/os/macosx/Info.plist.in b/os/macosx/Info.plist.in
index 0148ab289a..71619d8936 100644
--- a/os/macosx/Info.plist.in
+++ b/os/macosx/Info.plist.in
@@ -32,6 +32,6 @@
NSHighResolutionCapable
True
LSMinimumSystemVersion
- 10.14.0
+ 10.13.0
diff --git a/os/windows/openttd.manifest b/os/windows/openttd.manifest
index ee1c7ea224..cb536a819d 100644
--- a/os/windows/openttd.manifest
+++ b/os/windows/openttd.manifest
@@ -10,6 +10,7 @@
True/PM
+ PerMonitorV2,PerMonitor
diff --git a/os/windows/winstore/generate-assets.bat b/os/windows/winstore/generate-assets.bat
new file mode 100644
index 0000000000..387fd57b85
--- /dev/null
+++ b/os/windows/winstore/generate-assets.bat
@@ -0,0 +1,2 @@
+@echo off
+powershell -File "%~dp0generate-assets.ps1"
diff --git a/os/windows/winstore/generate-assets.ps1 b/os/windows/winstore/generate-assets.ps1
new file mode 100644
index 0000000000..89c48e6ecf
--- /dev/null
+++ b/os/windows/winstore/generate-assets.ps1
@@ -0,0 +1,48 @@
+function ResizeImage() {
+ param([String]$sourcePath, [Int]$targetWidth, [Int]$targetHeight, [String]$targetPath)
+
+ Add-Type -AssemblyName "System.Drawing"
+
+ $img = [System.Drawing.Image]::FromFile($sourcePath)
+
+ $ratioX = $targetWidth / $img.Width;
+ $ratioY = $targetHeight / $img.Height;
+
+ $ratio = $ratioY
+
+ if ($ratioX -le $ratioY) {
+ $ratio = $ratioX
+ }
+
+ $newWidth = [int] ($img.Width * $ratio)
+ $newHeight = [int] ($img.Height * $ratio)
+
+ $resizedImage = New-Object System.Drawing.Bitmap($targetWidth, $targetHeight)
+ $graph = [System.Drawing.Graphics]::FromImage($resizedImage)
+ $graph.InterpolationMode = [System.Drawing.Drawing2D.InterpolationMode]::HighQualityBicubic
+
+ $graph.Clear([System.Drawing.Color]::Transparent)
+ $graph.DrawImage($img, $targetWidth / 2 - $newWidth / 2, $targetHeight / 2 - $newHeight / 2, $newWidth, $newHeight)
+
+ $resizedImage.Save($targetPath)
+ $resizedImage.Dispose()
+ $img.Dispose()
+}
+
+$logoPath = "..\..\..\media\openttd.2048.png"
+
+# Create the main image assets required for the Windows Store
+New-Item -Path "." -Name "assets" -ItemType "directory" -Force
+ResizeImage $logoPath 1240 1240 "assets\LargeTile.png"
+ResizeImage $logoPath 284 284 "assets\SmallTile.png"
+ResizeImage $logoPath 2480 1200 "assets\SplashScreen.png"
+ResizeImage $logoPath 176 176 "assets\Square44x44Logo.png"
+Copy-Item "assets\Square44x44Logo.png" -Destination "assets\Square44x44Logo.targetsize-44_altform-unplated.png"
+ResizeImage $logoPath 600 600 "assets\Square150x150Logo.png"
+Copy-Item "assets\Square150x150Logo.png" -Destination "assets\Square150x150Logo.targetsize-150_altform-unplated.png"
+ResizeImage $logoPath 200 200 "assets\StoreLogo.png"
+ResizeImage $logoPath 1240 600 "assets\Wide310x150Logo.png"
+
+# Copy the logo for the store for the common package
+New-Item -Path "." -Name "assets-common" -ItemType "directory" -Force
+Copy-Item "assets\StoreLogo.png" -Destination "assets-common\StoreLogo.png"
diff --git a/os/windows/winstore/generate-key.bat b/os/windows/winstore/generate-key.bat
new file mode 100644
index 0000000000..859d595d9c
--- /dev/null
+++ b/os/windows/winstore/generate-key.bat
@@ -0,0 +1,2 @@
+@echo off
+powershell -File "%~dp0generate-key.ps1" %1 %2 %3
diff --git a/os/windows/winstore/generate-key.ps1 b/os/windows/winstore/generate-key.ps1
new file mode 100644
index 0000000000..f59eb038e4
--- /dev/null
+++ b/os/windows/winstore/generate-key.ps1
@@ -0,0 +1,21 @@
+[CmdletBinding()]
+[Alias()]
+Param
+(
+ # Publisher ("CN=xyz")
+ [Parameter(Mandatory=$true, Position=0)]
+ $Publisher,
+
+ # Password
+ [Parameter(Mandatory=$true, Position=1)]
+ $PasswordParam,
+
+ # Filename
+ [Parameter(Mandatory=$true, Position=2)]
+ $OutputFilename
+)
+
+$cert = New-SelfSignedCertificate -Type Custom -Subject $Publisher -KeyUsage DigitalSignature -FriendlyName "OpenTTD signing certificate" -CertStoreLocation "Cert:\CurrentUser\My" -TextExtension @("2.5.29.37={text}1.3.6.1.5.5.7.3.3", "2.5.29.19={text}")
+
+$password = ConvertTo-SecureString -String $PasswordParam -Force -AsPlainText
+Export-PfxCertificate -cert "Cert:\CurrentUser\My\$($cert.Thumbprint)" -FilePath $OutputFilename -Password $password
diff --git a/os/windows/winstore/manifests/AssetsPackage.appxmanifest b/os/windows/winstore/manifests/AssetsPackage.appxmanifest
new file mode 100644
index 0000000000..a0e8f29eda
--- /dev/null
+++ b/os/windows/winstore/manifests/AssetsPackage.appxmanifest
@@ -0,0 +1,68 @@
+
+
+
+
+ false
+ OpenTTD (official)
+ OpenTTD Developers
+ Assets\StoreLogoCommon.png
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/os/windows/winstore/manifests/Package.appxmanifest b/os/windows/winstore/manifests/Package.appxmanifest
new file mode 100644
index 0000000000..7eb4a5c2b7
--- /dev/null
+++ b/os/windows/winstore/manifests/Package.appxmanifest
@@ -0,0 +1,83 @@
+
+
+
+
+ OpenTTD (official)
+ OpenTTD Developers
+ Assets\StoreLogo.png
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/os/windows/winstore/manifests/PackagingLayout.xml b/os/windows/winstore/manifests/PackagingLayout.xml
new file mode 100644
index 0000000000..dc16e45fa8
--- /dev/null
+++ b/os/windows/winstore/manifests/PackagingLayout.xml
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/os/windows/winstore/prepare-manifests.bat b/os/windows/winstore/prepare-manifests.bat
new file mode 100644
index 0000000000..634b51e5ff
--- /dev/null
+++ b/os/windows/winstore/prepare-manifests.bat
@@ -0,0 +1,2 @@
+@echo off
+powershell -File "%~dp0prepare-manifests.ps1" %1 %2 %3 %OTTD_VERSION%
diff --git a/os/windows/winstore/prepare-manifests.ps1 b/os/windows/winstore/prepare-manifests.ps1
new file mode 100644
index 0000000000..4bd1f4cb2f
--- /dev/null
+++ b/os/windows/winstore/prepare-manifests.ps1
@@ -0,0 +1,42 @@
+[CmdletBinding()]
+[Alias()]
+Param
+(
+ # Output folder
+ [Parameter(Mandatory=$true, Position=0)]
+ $OutputFolder,
+
+ # Publisher ("CN=xyz")
+ [Parameter(Mandatory=$true, Position=1)]
+ $Publisher,
+
+ # IdentityName
+ [Parameter(Mandatory=$true, Position=2)]
+ $IdentityName,
+
+ # Version
+ [Parameter(Mandatory=$true, Position=3)]
+ $AppVersion
+)
+
+function Prepare-Manifest {
+ param (
+ $Architecture
+ )
+
+ (Get-Content "$($PSScriptRoot)\manifests\Package.appxmanifest").replace('$PUBLISHER$', $Publisher).replace('$IDENTITY_NAME$', $IdentityName).replace('$VERSION$', $AppVersion).replace('$ARCHITECTURE$', $Architecture) | Set-Content "$($OutputFolder)\Package-$($Architecture).appxmanifest"
+}
+
+# Prepare the application binary manifests
+Prepare-Manifest x86
+Prepare-Manifest x64
+Prepare-Manifest arm64
+
+# Prepare the assets package manifest
+(Get-Content "$($PSScriptRoot)\manifests\AssetsPackage.appxmanifest").replace('$PUBLISHER$', $Publisher).replace('$IDENTITY_NAME$', $IdentityName).replace('$VERSION$', $AppVersion) | Set-Content "$($OutputFolder)\AssetsPackage.appxmanifest"
+
+# Prepare the overall package manifest
+(Get-Content "$($PSScriptRoot)\manifests\Package.appxmanifest").replace('$PUBLISHER$', $Publisher).replace('$IDENTITY_NAME$', $IdentityName).replace('$VERSION$', $AppVersion).replace(' ProcessorArchitecture="$ARCHITECTURE$"', '') | Set-Content "$($OutputFolder)\Package.appxmanifest"
+
+# Copy the PackagingLayout XML file
+(Get-Content "$($PSScriptRoot)\manifests\PackagingLayout.xml") | Set-Content "$($OutputFolder)\PackagingLayout.xml"
diff --git a/os/windows/winstore/set-version.bat b/os/windows/winstore/set-version.bat
new file mode 100644
index 0000000000..f198661787
--- /dev/null
+++ b/os/windows/winstore/set-version.bat
@@ -0,0 +1,14 @@
+@echo off
+if [%1]==[] goto err
+
+powershell -File "%~dp0set-version.ps1" %1 > "%temp%\ottd-set-version.bat"
+if not errorlevel 0 goto err
+call "%temp%\ottd-set-version.bat"
+del /q "%temp%\ottd-set-version.bat"
+
+@rem Version number will now be in %OTTD_VERSION%
+
+goto :eof
+
+:err
+echo Please supply the path of openttd.exe as the argument to this batch file.
diff --git a/os/windows/winstore/set-version.ps1 b/os/windows/winstore/set-version.ps1
new file mode 100644
index 0000000000..ba048581e2
--- /dev/null
+++ b/os/windows/winstore/set-version.ps1
@@ -0,0 +1,23 @@
+[CmdletBinding()]
+[Alias()]
+Param
+(
+ # EXE path
+ [Parameter(Mandatory=$true, Position=0)]
+ $ExePath
+)
+
+try
+{
+ $versionInfo = (Get-Item "$ExePath").VersionInfo
+
+ # Generate the app version - the build number (MS calls it revision) is always 0 because the Windows Store requires that
+ $AppVersion = "$($versionInfo.FileMajorPart).$($versionInfo.FileMinorPart).$($versionInfo.FileBuildPart).0"
+
+ Write-Output "SET OTTD_VERSION=$($AppVersion)"
+}
+catch
+{
+ Write-Output "@ECHO Error retrieving EXE version - did you provide a path?"
+ exit 1
+}
diff --git a/regression/regression/info.nut b/regression/regression/info.nut
index 1a52cfebbb..8799d98628 100644
--- a/regression/regression/info.nut
+++ b/regression/regression/info.nut
@@ -4,7 +4,7 @@ class Regression extends AIInfo {
function GetShortName() { return "REGR"; }
function GetDescription() { return "This runs regression-tests on some commands. On the same map the result should always be the same."; }
function GetVersion() { return 1; }
- function GetAPIVersion() { return "12"; }
+ function GetAPIVersion() { return "13"; }
function GetDate() { return "2007-03-18"; }
function CreateInstance() { return "Regression"; }
function UseAsRandomAI() { return false; }
diff --git a/regression/regression/main.nut b/regression/regression/main.nut
index f32c708614..7621c49c6f 100644
--- a/regression/regression/main.nut
+++ b/regression/regression/main.nut
@@ -341,6 +341,7 @@ function Regression::Cargo()
print(" GetCargoIncome(10, 10): " + AICargo.GetCargoIncome(i, 10, 10));
print(" GetCargoIncome(100, 10): " + AICargo.GetCargoIncome(i, 100, 10));
print(" GetCargoIncome(10, 100): " + AICargo.GetCargoIncome(i, 10, 100));
+ print(" GetWeight(100): " + AICargo.GetWeight(i, 100));
print(" GetRoadVehicleTypeForCargo(): " + AIRoad.GetRoadVehicleTypeForCargo(i));
}
}
@@ -561,6 +562,25 @@ function Regression::Prices()
print(" BT_CLEAR_WATER: " + AITile.GetBuildCost(AITile.BT_CLEAR_WATER));
}
+function Regression::Commands()
+{
+ print("");
+ print("--Commands--");
+
+ print(" -Command accounting-");
+ local test = AITestMode();
+ local costs = AIAccounting();
+ AITile.DemolishTile(2834);
+ print(" Command cost: " + costs.GetCosts());
+ {
+ local inner = AIAccounting();
+ print(" New inner cost scope: " + costs.GetCosts());
+ AITile.DemolishTile(2835);
+ print(" Further command cost: " + costs.GetCosts());
+ }
+ print(" Saved cost of outer scope: " + costs.GetCosts());
+}
+
function cost_callback(old_path, new_tile, new_direction, self) { if (old_path == null) return 0; return old_path.GetCost() + 1; }
function estimate_callback(tile, direction, goals, self) { return goals[0] - tile; }
function neighbours_callback(path, cur_tile, self) { return [[cur_tile + 1, 1]]; }
@@ -1023,6 +1043,30 @@ function Regression::Rail()
print(" IsRailTile(): " + AIRail.IsRailTile(10002));
print(" BuildRailTrack(): " + AIRail.BuildRailTrack(10002, AIRail.RAILTRACK_NW_SE));
print(" BuildSignal(): " + AIRail.BuildSignal(10002, 10258, AIRail.SIGNALTYPE_PBS));
+ print(" GetSignalType(): " + AIRail.GetSignalType(10002, 10258));
+ print(" GetSignalType(): " + AIRail.GetSignalType(10002, 9746));
+ print(" RemoveSignal(): " + AIRail.RemoveSignal(10002, 10258));
+ print(" BuildSignal(): " + AIRail.BuildSignal(10002, 9746, AIRail.SIGNALTYPE_ENTRY));
+ print(" GetSignalType(): " + AIRail.GetSignalType(10002, 10258));
+ print(" GetSignalType(): " + AIRail.GetSignalType(10002, 9746));
+ print(" RemoveSignal(): " + AIRail.RemoveSignal(10002, 9746));
+ print(" BuildSignal(): " + AIRail.BuildSignal(10002, 9746, AIRail.SIGNALTYPE_EXIT_TWOWAY));
+ print(" GetSignalType(): " + AIRail.GetSignalType(10002, 10258));
+ print(" GetSignalType(): " + AIRail.GetSignalType(10002, 9746));
+ print(" RemoveRailTrack(): " + AIRail.RemoveRailTrack(10002, AIRail.RAILTRACK_NW_NE));
+ print(" RemoveRailTrack(): " + AIRail.RemoveRailTrack(10002, AIRail.RAILTRACK_NW_SE));
+ print(" BuildRailTrack(): " + AIRail.BuildRailTrack(10002, AIRail.RAILTRACK_NW_NE));
+ print(" BuildSignal(): " + AIRail.BuildSignal(10002, 10003, AIRail.SIGNALTYPE_PBS));
+ print(" GetSignalType(): " + AIRail.GetSignalType(10002, 10003));
+ print(" GetSignalType(): " + AIRail.GetSignalType(10002, 10001));
+ print(" RemoveSignal(): " + AIRail.RemoveSignal(10002, 10003));
+ print(" BuildSignal(): " + AIRail.BuildSignal(10002, 10001, AIRail.SIGNALTYPE_ENTRY));
+ print(" GetSignalType(): " + AIRail.GetSignalType(10002, 10003));
+ print(" GetSignalType(): " + AIRail.GetSignalType(10002, 10001));
+ print(" RemoveSignal(): " + AIRail.RemoveSignal(10002, 10001));
+ print(" BuildSignal(): " + AIRail.BuildSignal(10002, 10001, AIRail.SIGNALTYPE_EXIT_TWOWAY));
+ print(" GetSignalType(): " + AIRail.GetSignalType(10002, 10003));
+ print(" GetSignalType(): " + AIRail.GetSignalType(10002, 10001));
print(" RemoveRailTrack(): " + AIRail.RemoveRailTrack(10002, AIRail.RAILTRACK_NW_NE));
print(" RemoveRailTrack(): " + AIRail.RemoveRailTrack(10002, AIRail.RAILTRACK_NW_SE));
print(" BuildRail(): " + AIRail.BuildRail(10002, 10003, 10006));
@@ -1659,6 +1703,7 @@ function Regression::Vehicle()
print(" BuildVehicle(): " + AIVehicle.BuildVehicle(33417, 153));
print(" IsValidVehicle(12): " + AIVehicle.IsValidVehicle(12));
print(" CloneVehicle(): " + AIVehicle.CloneVehicle(33417, 12, true));
+ print(" BuildVehicle(): " + AIVehicle.BuildVehicle(-1, 153));
local bank_after = AICompany.GetBankBalance(AICompany.COMPANY_SELF);
@@ -1915,6 +1960,7 @@ function Regression::Start()
/* Do this first as it gains maximum loan (which is faked to quite a lot). */
this.Company();
+ this.Commands();
this.Airport();
this.Bridge();
this.BridgeList();
diff --git a/regression/regression/result.txt b/regression/regression/result.txt
index 984c1de82b..dc3e294844 100644
--- a/regression/regression/result.txt
+++ b/regression/regression/result.txt
@@ -788,6 +788,13 @@ ERROR: IsEnd() is invalid as Begin() is never called
GetQuarterlyPerformanceRating(): 0
GetQuarterlyCompanyValue(): 0
+--Commands--
+ -Command accounting-
+ Command cost: 7500
+ New inner cost scope: 0
+ Further command cost: 30
+ Saved cost of outer scope: 7500
+
--AIAirport--
IsHangarTile(): false
IsAirportTile(): false
@@ -1118,6 +1125,7 @@ ERROR: IsEnd() is invalid as Begin() is never called
GetCargoIncome(10, 10): -1
GetCargoIncome(100, 10): -1
GetCargoIncome(10, 100): -1
+ GetWeight(100): -1
GetRoadVehicleTypeForCargo(): 1
Cargo 0
IsValidCargo(): true
@@ -1130,6 +1138,7 @@ ERROR: IsEnd() is invalid as Begin() is never called
GetCargoIncome(10, 10): 3
GetCargoIncome(100, 10): 38
GetCargoIncome(10, 100): 3
+ GetWeight(100): 6
GetRoadVehicleTypeForCargo(): 0
Cargo 1
IsValidCargo(): true
@@ -1142,6 +1151,7 @@ ERROR: IsEnd() is invalid as Begin() is never called
GetCargoIncome(10, 10): 7
GetCargoIncome(100, 10): 71
GetCargoIncome(10, 100): 6
+ GetWeight(100): 100
GetRoadVehicleTypeForCargo(): 1
Cargo 2
IsValidCargo(): true
@@ -1154,6 +1164,7 @@ ERROR: IsEnd() is invalid as Begin() is never called
GetCargoIncome(10, 10): 5
GetCargoIncome(100, 10): 55
GetCargoIncome(10, 100): 5
+ GetWeight(100): 25
GetRoadVehicleTypeForCargo(): 1
Cargo 3
IsValidCargo(): true
@@ -1166,6 +1177,7 @@ ERROR: IsEnd() is invalid as Begin() is never called
GetCargoIncome(10, 10): 5
GetCargoIncome(100, 10): 53
GetCargoIncome(10, 100): 5
+ GetWeight(100): 100
GetRoadVehicleTypeForCargo(): 1
Cargo 4
IsValidCargo(): true
@@ -1178,6 +1190,7 @@ ERROR: IsEnd() is invalid as Begin() is never called
GetCargoIncome(10, 10): 5
GetCargoIncome(100, 10): 52
GetCargoIncome(10, 100): 4
+ GetWeight(100): 18
GetRoadVehicleTypeForCargo(): 1
Cargo 5
IsValidCargo(): true
@@ -1190,6 +1203,7 @@ ERROR: IsEnd() is invalid as Begin() is never called
GetCargoIncome(10, 10): 7
GetCargoIncome(100, 10): 74
GetCargoIncome(10, 100): 6
+ GetWeight(100): 50
GetRoadVehicleTypeForCargo(): 1
Cargo 6
IsValidCargo(): true
@@ -1202,6 +1216,7 @@ ERROR: IsEnd() is invalid as Begin() is never called
GetCargoIncome(10, 10): 5
GetCargoIncome(100, 10): 58
GetCargoIncome(10, 100): 4
+ GetWeight(100): 100
GetRoadVehicleTypeForCargo(): 1
Cargo 7
IsValidCargo(): true
@@ -1214,6 +1229,7 @@ ERROR: IsEnd() is invalid as Begin() is never called
GetCargoIncome(10, 10): 6
GetCargoIncome(100, 10): 60
GetCargoIncome(10, 100): 5
+ GetWeight(100): 100
GetRoadVehicleTypeForCargo(): 1
Cargo 8
IsValidCargo(): true
@@ -1226,6 +1242,7 @@ ERROR: IsEnd() is invalid as Begin() is never called
GetCargoIncome(10, 10): 6
GetCargoIncome(100, 10): 62
GetCargoIncome(10, 100): 5
+ GetWeight(100): 100
GetRoadVehicleTypeForCargo(): 1
Cargo 9
IsValidCargo(): true
@@ -1238,6 +1255,7 @@ ERROR: IsEnd() is invalid as Begin() is never called
GetCargoIncome(10, 10): 6
GetCargoIncome(100, 10): 69
GetCargoIncome(10, 100): 6
+ GetWeight(100): 100
GetRoadVehicleTypeForCargo(): 1
Cargo 10
IsValidCargo(): true
@@ -1250,6 +1268,7 @@ ERROR: IsEnd() is invalid as Begin() is never called
GetCargoIncome(10, 10): 9
GetCargoIncome(100, 10): 90
GetCargoIncome(10, 100): 7
+ GetWeight(100): 12
GetRoadVehicleTypeForCargo(): 1
Cargo 11
IsValidCargo(): false
@@ -1262,6 +1281,7 @@ ERROR: IsEnd() is invalid as Begin() is never called
GetCargoIncome(10, 10): -1
GetCargoIncome(100, 10): -1
GetCargoIncome(10, 100): -1
+ GetWeight(100): -1
GetRoadVehicleTypeForCargo(): 1
Cargo 12
IsValidCargo(): false
@@ -1274,6 +1294,7 @@ ERROR: IsEnd() is invalid as Begin() is never called
GetCargoIncome(10, 10): -1
GetCargoIncome(100, 10): -1
GetCargoIncome(10, 100): -1
+ GetWeight(100): -1
GetRoadVehicleTypeForCargo(): 1
Cargo 13
IsValidCargo(): false
@@ -1286,6 +1307,7 @@ ERROR: IsEnd() is invalid as Begin() is never called
GetCargoIncome(10, 10): -1
GetCargoIncome(100, 10): -1
GetCargoIncome(10, 100): -1
+ GetWeight(100): -1
GetRoadVehicleTypeForCargo(): 1
Cargo 14
IsValidCargo(): false
@@ -1298,6 +1320,7 @@ ERROR: IsEnd() is invalid as Begin() is never called
GetCargoIncome(10, 10): -1
GetCargoIncome(100, 10): -1
GetCargoIncome(10, 100): -1
+ GetWeight(100): -1
GetRoadVehicleTypeForCargo(): 1
--CargoList--
@@ -7411,8 +7434,32 @@ ERROR: IsEnd() is invalid as Begin() is never called
IsRailTile(): false
BuildRailTrack(): true
BuildSignal(): true
+ GetSignalType(): 4
+ GetSignalType(): 255
+ RemoveSignal(): true
+ BuildSignal(): true
+ GetSignalType(): 255
+ GetSignalType(): 1
+ RemoveSignal(): true
+ BuildSignal(): true
+ GetSignalType(): 10
+ GetSignalType(): 10
RemoveRailTrack(): false
RemoveRailTrack(): true
+ BuildRailTrack(): true
+ BuildSignal(): false
+ GetSignalType(): 255
+ GetSignalType(): 255
+ RemoveSignal(): false
+ BuildSignal(): true
+ GetSignalType(): 255
+ GetSignalType(): 1
+ RemoveSignal(): true
+ BuildSignal(): true
+ GetSignalType(): 255
+ GetSignalType(): 10
+ RemoveRailTrack(): true
+ RemoveRailTrack(): false
BuildRail(): true
HasTransportType(): true
HasTransportType(): false
@@ -9248,6 +9295,7 @@ ERROR: IsEnd() is invalid as Begin() is never called
BuildVehicle(): 12
IsValidVehicle(12): true
CloneVehicle(): 13
+ BuildVehicle(): 1048575
--Accounting--
GetCosts(): 11894
Should be: 11894
@@ -9272,12 +9320,12 @@ ERROR: IsEnd() is invalid as Begin() is never called
GetLocation(): 33417
GetEngineType(): 153
GetUnitNumber(): 1
- GetAge(): 1
+ GetAge(): 0
GetMaxAge(): 5490
- GetAgeLeft(): 5489
+ GetAgeLeft(): 5490
GetCurrentSpeed(): 7
GetRunningCost(): 421
- GetProfitThisYear(): -1
+ GetProfitThisYear(): 0
GetProfitLastYear(): 0
GetCurrentValue(): 5947
GetVehicleType(): 1
@@ -9287,7 +9335,7 @@ ERROR: IsEnd() is invalid as Begin() is never called
IsInDepot(): false
GetNumWagons(): 1
GetWagonEngineType(): 153
- GetWagonAge(): 1
+ GetWagonAge(): 0
GetLength(): 8
GetOwner(): 1
BuildVehicle(): 14
@@ -9360,11 +9408,11 @@ ERROR: IsEnd() is invalid as Begin() is never called
14 => 1
12 => 1
Age ListDump:
+ 17 => 1
+ 16 => 1
+ 14 => 1
13 => 1
12 => 1
- 17 => 0
- 16 => 0
- 14 => 0
MaxAge ListDump:
16 => 10980
14 => 10980
@@ -9372,9 +9420,9 @@ ERROR: IsEnd() is invalid as Begin() is never called
13 => 5490
12 => 5490
AgeLeft ListDump:
- 16 => 10980
- 14 => 10980
- 17 => 7320
+ 16 => 10979
+ 14 => 10979
+ 17 => 7319
13 => 5489
12 => 5489
CurrentSpeed ListDump:
diff --git a/regression/stationlist/info.nut b/regression/stationlist/info.nut
index ad91c7645c..099a7d12ec 100644
--- a/regression/stationlist/info.nut
+++ b/regression/stationlist/info.nut
@@ -4,7 +4,7 @@ class StationList extends AIInfo {
function GetShortName() { return "REGS"; }
function GetDescription() { return "This runs stationlist-tests on some commands. On the same map the result should always be the same."; }
function GetVersion() { return 1; }
- function GetAPIVersion() { return "12"; }
+ function GetAPIVersion() { return "13"; }
function GetDate() { return "2007-03-18"; }
function CreateInstance() { return "StationList"; }
function UseAsRandomAI() { return false; }
diff --git a/src/3rdparty/squirrel/squirrel/sqstate.cpp b/src/3rdparty/squirrel/squirrel/sqstate.cpp
index 0f95c396c9..23bd520a1a 100644
--- a/src/3rdparty/squirrel/squirrel/sqstate.cpp
+++ b/src/3rdparty/squirrel/squirrel/sqstate.cpp
@@ -450,7 +450,7 @@ void RefTable::Resize(SQUnsignedInteger size)
SQUnsignedInteger oldnumofslots = _numofslots;
AllocNodes(size);
//rehash
- SQUnsignedInteger nfound = 0;
+ [[maybe_unused]] SQUnsignedInteger nfound = 0;
for(SQUnsignedInteger n = 0; n < oldnumofslots; n++) {
if(type(t->obj) != OT_NULL) {
//add back;
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 33f525b751..411c7d71f5 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -10,6 +10,7 @@ add_subdirectory(3rdparty)
add_subdirectory(ai)
add_subdirectory(blitter)
add_subdirectory(core)
+add_subdirectory(fontcache)
add_subdirectory(game)
add_subdirectory(lang)
add_subdirectory(linkgraph)
@@ -29,18 +30,15 @@ add_files(
viewport_sprite_sorter_sse4.cpp
CONDITION SSE_FOUND
)
-if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang" OR CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
- set_compile_flags(
- viewport_sprite_sorter_sse4.cpp
- COMPILE_FLAGS -msse4.1)
-endif()
add_files(
aircraft.h
aircraft_cmd.cpp
+ aircraft_cmd.h
aircraft_gui.cpp
airport.cpp
airport.h
+ airport_cmd.h
airport_gui.cpp
animated_tile.cpp
animated_tile_func.h
@@ -49,6 +47,7 @@ add_files(
autoreplace.cpp
autoreplace_base.h
autoreplace_cmd.cpp
+ autoreplace_cmd.h
autoreplace_func.h
autoreplace_gui.cpp
autoreplace_gui.h
@@ -86,12 +85,12 @@ add_files(
clear_cmd.cpp
clear_func.h
clear_map.h
- cmd_helper.h
command.cpp
command_func.h
command_type.h
company_base.h
company_cmd.cpp
+ company_cmd.h
company_func.h
company_gui.cpp
company_gui.h
@@ -121,6 +120,7 @@ add_files(
depot.cpp
depot_base.h
depot_cmd.cpp
+ depot_cmd.h
depot_func.h
depot_gui.cpp
depot_map.h
@@ -129,11 +129,13 @@ add_files(
direction_type.h
disaster_vehicle.cpp
disaster_vehicle.h
+ dock_cmd.h
dock_gui.cpp
driver.cpp
driver.h
economy.cpp
economy_base.h
+ economy_cmd.h
economy_func.h
economy_type.h
effectvehicle.cpp
@@ -143,6 +145,7 @@ add_files(
elrail_func.h
engine.cpp
engine_base.h
+ engine_cmd.h
engine_func.h
engine_gui.cpp
engine_gui.h
@@ -157,7 +160,6 @@ add_files(
fios_gui.cpp
fontcache.cpp
fontcache.h
- fontcache_internal.h
fontdetection.h
framerate_gui.cpp
framerate_type.h
@@ -176,6 +178,7 @@ add_files(
gfxinit.h
goal.cpp
goal_base.h
+ goal_cmd.h
goal_gui.cpp
goal_type.h
graph_gui.cpp
@@ -184,6 +187,7 @@ add_files(
ground_vehicle.hpp
group.h
group_cmd.cpp
+ group_cmd.h
group_gui.cpp
group_gui.h
group_type.h
@@ -200,6 +204,7 @@ add_files(
house_type.h
industry.h
industry_cmd.cpp
+ industry_cmd.h
industry_gui.cpp
industry_map.h
industry_type.h
@@ -210,8 +215,15 @@ add_files(
intro_gui.cpp
landscape.cpp
landscape.h
+ landscape_cmd.h
landscape_type.h
language.h
+ league_base.h
+ league_cmd.h
+ league_cmd.cpp
+ league_gui.h
+ league_gui.cpp
+ league_type.h
livery.h
main_gui.cpp
map.cpp
@@ -219,6 +231,7 @@ add_files(
map_type.h
misc.cpp
misc_cmd.cpp
+ misc_cmd.h
misc_gui.cpp
mixer.cpp
mixer.h
@@ -279,6 +292,7 @@ add_files(
newgrf_town.h
newgrf_townname.cpp
newgrf_townname.h
+ news_cmd.h
news_func.h
news_gui.cpp
news_gui.h
@@ -286,6 +300,7 @@ add_files(
object.h
object_base.h
object_cmd.cpp
+ object_cmd.h
object_gui.cpp
object_map.h
object_type.h
@@ -295,6 +310,7 @@ add_files(
order_backup.h
order_base.h
order_cmd.cpp
+ order_cmd.h
order_func.h
order_gui.cpp
order_type.h
@@ -307,6 +323,7 @@ add_files(
rail.cpp
rail.h
rail_cmd.cpp
+ rail_cmd.h
rail_gui.cpp
rail_gui.h
rail_map.h
@@ -329,6 +346,7 @@ add_files(
roadstop_base.h
roadveh.h
roadveh_cmd.cpp
+ roadveh_cmd.h
roadveh_gui.cpp
safeguards.h
screenshot_gui.cpp
@@ -336,6 +354,7 @@ add_files(
screenshot.cpp
screenshot.h
settings.cpp
+ settings_cmd.h
settings_func.h
settings_gui.cpp
settings_gui.h
@@ -345,6 +364,7 @@ add_files(
settings_type.h
ship.h
ship_cmd.cpp
+ ship_cmd.h
ship_gui.cpp
signal.cpp
signal_func.h
@@ -352,6 +372,7 @@ add_files(
signs.cpp
signs_base.h
signs_cmd.cpp
+ signs_cmd.h
signs_func.h
signs_gui.cpp
signs_type.h
@@ -370,6 +391,7 @@ add_files(
station.cpp
station_base.h
station_cmd.cpp
+ station_cmd.h
station_func.h
station_gui.cpp
station_gui.h
@@ -381,6 +403,7 @@ add_files(
stdafx.h
story.cpp
story_base.h
+ story_cmd.h
story_gui.cpp
story_type.h
strgen/strgen.h
@@ -395,11 +418,13 @@ add_files(
strings_type.h
subsidy.cpp
subsidy_base.h
+ subsidy_cmd.h
subsidy_func.h
subsidy_gui.cpp
subsidy_type.h
tar_type.h
terraform_cmd.cpp
+ terraform_cmd.h
terraform_gui.cpp
terraform_gui.h
textbuf.cpp
@@ -424,11 +449,13 @@ add_files(
tilematrix_type.hpp
timetable.h
timetable_cmd.cpp
+ timetable_cmd.h
timetable_gui.cpp
toolbar_gui.cpp
toolbar_gui.h
town.h
town_cmd.cpp
+ town_cmd.h
town_gui.cpp
town_kdtree.h
town_map.h
@@ -440,24 +467,28 @@ add_files(
track_type.h
train.h
train_cmd.cpp
+ train_cmd.h
train_gui.cpp
transparency.h
transparency_gui.cpp
transparency_gui.h
transport_type.h
tree_cmd.cpp
+ tree_cmd.h
tree_gui.cpp
tree_map.h
tunnel_map.cpp
tunnel_map.h
tunnelbridge.h
tunnelbridge_cmd.cpp
+ tunnelbridge_cmd.h
tunnelbridge_map.h
tutorial_gui.cpp
tutorial_gui.h
vehicle.cpp
vehicle_base.h
vehicle_cmd.cpp
+ vehicle_cmd.h
vehicle_func.h
vehicle_gui.cpp
vehicle_gui.h
@@ -466,6 +497,7 @@ add_files(
vehiclelist.cpp
vehiclelist.h
viewport.cpp
+ viewport_cmd.h
viewport_func.h
viewport_gui.cpp
viewport_kdtree.h
@@ -476,10 +508,12 @@ add_files(
walltime_func.h
water.h
water_cmd.cpp
+ water_cmd.h
water_map.h
waypoint.cpp
waypoint_base.h
waypoint_cmd.cpp
+ waypoint_cmd.h
waypoint_func.h
waypoint_gui.cpp
widget.cpp
diff --git a/src/ai/ai.hpp b/src/ai/ai.hpp
index 59886486fe..1e2e99d22f 100644
--- a/src/ai/ai.hpp
+++ b/src/ai/ai.hpp
@@ -128,20 +128,15 @@ public:
*/
static void Save(CompanyID company);
- /**
- * Load data for an AI from a savegame.
- */
- static void Load(CompanyID company, int version);
-
/**
* Get the number of days before the next AI should start.
*/
static int GetStartNextTime();
/** Wrapper function for AIScanner::GetAIConsoleList */
- static char *GetConsoleList(char *p, const char *last, bool newest_only = false);
+ static std::string GetConsoleList(bool newest_only = false);
/** Wrapper function for AIScanner::GetAIConsoleLibraryList */
- static char *GetConsoleLibraryList(char *p, const char *last);
+ static std::string GetConsoleLibraryList();
/** Wrapper function for AIScanner::GetAIInfoList */
static const ScriptInfoList *GetInfoList();
/** Wrapper function for AIScanner::GetUniqueAIInfoList */
diff --git a/src/ai/ai_core.cpp b/src/ai/ai_core.cpp
index e45738776d..4d878b2905 100644
--- a/src/ai/ai_core.cpp
+++ b/src/ai/ai_core.cpp
@@ -57,6 +57,8 @@
assert(c->ai_instance == nullptr);
c->ai_instance = new AIInstance();
c->ai_instance->Initialize(info);
+ c->ai_instance->LoadOnStack(config->GetToLoadData());
+ config->SetToLoadData(nullptr);
cur_company.Restore();
@@ -289,21 +291,6 @@
}
}
-/* static */ void AI::Load(CompanyID company, int version)
-{
- if (!_networking || _network_server) {
- Company *c = Company::GetIfValid(company);
- assert(c != nullptr && c->ai_instance != nullptr);
-
- Backup cur_company(_current_company, company, FILE_LINE);
- c->ai_instance->Load(version);
- cur_company.Restore();
- } else {
- /* Read, but ignore, the load data */
- AIInstance::LoadEmpty();
- }
-}
-
/* static */ int AI::GetStartNextTime()
{
/* Find the first company which doesn't exist yet */
@@ -315,14 +302,14 @@
return DAYS_IN_YEAR;
}
-/* static */ char *AI::GetConsoleList(char *p, const char *last, bool newest_only)
+/* static */ std::string AI::GetConsoleList(bool newest_only)
{
- return AI::scanner_info->GetConsoleList(p, last, newest_only);
+ return AI::scanner_info->GetConsoleList(newest_only);
}
-/* static */ char *AI::GetConsoleLibraryList(char *p, const char *last)
+/* static */ std::string AI::GetConsoleLibraryList()
{
- return AI::scanner_library->GetConsoleList(p, last, true);
+ return AI::scanner_library->GetConsoleList(true);
}
/* static */ const ScriptInfoList *AI::GetInfoList()
diff --git a/src/ai/ai_gui.cpp b/src/ai/ai_gui.cpp
index 4768ba7ca2..c6041d5122 100644
--- a/src/ai/ai_gui.cpp
+++ b/src/ai/ai_gui.cpp
@@ -28,6 +28,8 @@
#include "../hotkeys.h"
#include "../core/geometry_func.hpp"
#include "../guitimer_func.h"
+#include "../company_cmd.h"
+#include "../misc_cmd.h"
#include "ai.hpp"
#include "ai_gui.hpp"
@@ -110,7 +112,7 @@ struct AIListWindow : public Window {
void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) override
{
if (widget == WID_AIL_LIST) {
- this->line_height = FONT_HEIGHT_NORMAL + WD_MATRIX_TOP + WD_MATRIX_BOTTOM;
+ this->line_height = FONT_HEIGHT_NORMAL + padding.height;
this->line_height = GetMinButtonSize(this->line_height);
resize->width = 1;
@@ -124,46 +126,45 @@ struct AIListWindow : public Window {
switch (widget) {
case WID_AIL_LIST: {
/* Draw a list of all available AIs. */
- int y = this->GetWidget(WID_AIL_LIST)->pos_y;
- y = Center(y, this->line_height);
+ Rect tr = r.Shrink(WidgetDimensions::scaled.matrix);
/* First AI in the list is hardcoded to random */
if (this->vscroll->IsVisible(0)) {
- DrawString(r.left + WD_MATRIX_LEFT, r.right - WD_MATRIX_LEFT, y + WD_MATRIX_TOP, this->slot == OWNER_DEITY ? STR_AI_CONFIG_NONE : STR_AI_CONFIG_RANDOM_AI, this->selected == -1 ? TC_WHITE : TC_ORANGE);
- y += this->line_height;
+ DrawString(tr, this->slot == OWNER_DEITY ? STR_AI_CONFIG_NONE : STR_AI_CONFIG_RANDOM_AI, this->selected == -1 ? TC_WHITE : TC_ORANGE);
+ tr.top += this->line_height;
}
int i = 0;
for (const auto &item : *this->info_list) {
i++;
if (this->vscroll->IsVisible(i)) {
- DrawString(r.left + WD_MATRIX_LEFT, r.right - WD_MATRIX_RIGHT, y + WD_MATRIX_TOP, item.second->GetName(), (this->selected == i - 1) ? TC_WHITE : TC_ORANGE);
- y += this->line_height;
+ DrawString(tr, item.second->GetName(), (this->selected == i - 1) ? TC_WHITE : TC_ORANGE);
+ tr.top += this->line_height;
}
}
break;
}
case WID_AIL_INFO_BG: {
- AIInfo *selected_info = nullptr;
+ ScriptInfo *selected_info = nullptr;
int i = 0;
for (const auto &item : *this->info_list) {
i++;
- if (this->selected == i - 1) selected_info = static_cast(item.second);
+ if (this->selected == i - 1) selected_info = static_cast(item.second);
}
/* Some info about the currently selected AI. */
if (selected_info != nullptr) {
- int y = r.top + WD_FRAMERECT_TOP;
+ Rect tr = r.Shrink(WidgetDimensions::scaled.frametext, WidgetDimensions::scaled.framerect);
SetDParamStr(0, selected_info->GetAuthor());
- DrawString(r.left + WD_FRAMETEXT_LEFT, r.right - WD_FRAMETEXT_RIGHT, y, STR_AI_LIST_AUTHOR);
- y += FONT_HEIGHT_NORMAL + WD_PAR_VSEP_NORMAL;
+ DrawString(tr, STR_AI_LIST_AUTHOR);
+ tr.top += FONT_HEIGHT_NORMAL + WidgetDimensions::scaled.vsep_normal;
SetDParam(0, selected_info->GetVersion());
- DrawString(r.left + WD_FRAMETEXT_LEFT, r.right - WD_FRAMETEXT_RIGHT, y, STR_AI_LIST_VERSION);
- y += FONT_HEIGHT_NORMAL + WD_PAR_VSEP_NORMAL;
+ DrawString(tr, STR_AI_LIST_VERSION);
+ tr.top += FONT_HEIGHT_NORMAL + WidgetDimensions::scaled.vsep_normal;
if (selected_info->GetURL() != nullptr) {
SetDParamStr(0, selected_info->GetURL());
- DrawString(r.left + WD_FRAMETEXT_LEFT, r.right - WD_FRAMETEXT_RIGHT, y, STR_AI_LIST_URL);
- y += FONT_HEIGHT_NORMAL + WD_PAR_VSEP_NORMAL;
+ DrawString(tr, STR_AI_LIST_URL);
+ tr.top += FONT_HEIGHT_NORMAL + WidgetDimensions::scaled.vsep_normal;
}
SetDParamStr(0, selected_info->GetDescription());
- DrawStringMultiLine(r.left + WD_FRAMETEXT_LEFT, r.right - WD_FRAMETEXT_RIGHT, y, r.bottom - WD_FRAMERECT_BOTTOM, STR_JUST_RAW_STRING, TC_WHITE);
+ DrawStringMultiLine(tr, STR_JUST_RAW_STRING, TC_WHITE);
}
break;
}
@@ -182,7 +183,7 @@ struct AIListWindow : public Window {
for (int i = 0; i < this->selected; i++) it++;
GetConfig(slot)->Change((*it).second->GetName(), (*it).second->GetVersion());
}
- InvalidateWindowData(WC_GAME_OPTIONS, WN_GAME_OPTIONS_AI);
+ InvalidateWindowData(WC_GAME_OPTIONS, slot == OWNER_DEITY ? WN_GAME_OPTIONS_GS : WN_GAME_OPTIONS_AI);
InvalidateWindowClassesData(WC_AI_SETTINGS);
CloseWindowByClass(WC_QUERY_STRING);
InvalidateWindowClassesData(WC_TEXTFILE);
@@ -253,7 +254,7 @@ static const NWidgetPart _nested_ai_list_widgets[] = {
NWidget(WWT_MATRIX, COLOUR_MAUVE, WID_AIL_LIST), SetMinimalSize(188, 112), SetFill(1, 1), SetResize(1, 1), SetMatrixDataTip(1, 0, STR_AI_LIST_TOOLTIP), SetScrollbar(WID_AIL_SCROLLBAR),
NWidget(NWID_VSCROLLBAR, COLOUR_MAUVE, WID_AIL_SCROLLBAR),
EndContainer(),
- NWidget(WWT_PANEL, COLOUR_MAUVE, WID_AIL_INFO_BG), SetMinimalTextLines(8, WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM), SetResize(1, 0),
+ NWidget(WWT_PANEL, COLOUR_MAUVE, WID_AIL_INFO_BG), SetMinimalTextLines(8, WidgetDimensions::unscaled.framerect.Vertical() + WidgetDimensions::unscaled.vsep_normal * 3), SetResize(1, 0),
EndContainer(),
NWidget(NWID_HORIZONTAL),
NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
@@ -276,7 +277,7 @@ static WindowDesc _ai_list_desc(
* Open the AI list window to chose an AI for the given company slot.
* @param slot The slot to change the AI of.
*/
-static void ShowAIListWindow(CompanyID slot)
+void ShowAIListWindow(CompanyID slot)
{
CloseWindowByClass(WC_AI_LIST);
new AIListWindow(&_ai_list_desc, slot);
@@ -296,7 +297,7 @@ struct AISettingsWindow : public Window {
int clicked_row; ///< The clicked row of settings.
int line_height; ///< Height of a row in the matrix widget.
Scrollbar *vscroll; ///< Cache of the vertical scrollbar.
- typedef std::vector VisibleSettingsList;
+ typedef std::vector VisibleSettingsList; ///< typdef for a vector of script settings
VisibleSettingsList visible_settings; ///< List of visible AI settings
/**
@@ -320,15 +321,6 @@ struct AISettingsWindow : public Window {
this->RebuildVisibleSettings();
}
- void SetStringParameters(int widget) const override
- {
- switch (widget) {
- case WID_AIS_CAPTION:
- SetDParam(0, (this->slot == OWNER_DEITY) ? STR_AI_SETTINGS_CAPTION_GAMESCRIPT : STR_AI_SETTINGS_CAPTION_AI);
- break;
- }
- }
-
/**
* Rebuilds the list of visible settings. AI settings with the flag
* AICONFIG_AI_DEVELOPER set will only be visible if the game setting
@@ -351,7 +343,7 @@ struct AISettingsWindow : public Window {
void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) override
{
if (widget == WID_AIS_BACKGROUND) {
- this->line_height = std::max(SETTING_BUTTON_HEIGHT, FONT_HEIGHT_NORMAL) + WD_MATRIX_TOP + WD_MATRIX_BOTTOM;
+ this->line_height = std::max(SETTING_BUTTON_HEIGHT, FONT_HEIGHT_NORMAL) + padding.height;
this->line_height = GetMinButtonSize(this->line_height);
resize->width = 1;
@@ -369,11 +361,10 @@ struct AISettingsWindow : public Window {
int i = 0;
for (; !this->vscroll->IsVisible(i); i++) it++;
+ Rect ir = r.Shrink(WidgetDimensions::scaled.framerect);
bool rtl = _current_text_dir == TD_RTL;
- uint buttons_left = rtl ? r.right - SETTING_BUTTON_WIDTH - 3 : r.left + 4;
- uint text_left = r.left + (rtl ? WD_FRAMERECT_LEFT : SETTING_BUTTON_WIDTH + 8);
- uint text_right = r.right - (rtl ? SETTING_BUTTON_WIDTH + 8 : WD_FRAMERECT_RIGHT);
-
+ Rect br = ir.WithWidth(SETTING_BUTTON_WIDTH, rtl);
+ Rect tr = ir.Indent(SETTING_BUTTON_WIDTH + WidgetDimensions::scaled.hsep_wide, rtl);
int y = r.top;
int button_y_offset = (this->line_height - SETTING_BUTTON_HEIGHT) / 2;
int text_y_offset = (this->line_height - SETTING_BUTTON_HEIGHT) / 2;
@@ -401,13 +392,13 @@ struct AISettingsWindow : public Window {
}
if ((config_item.flags & SCRIPTCONFIG_BOOLEAN) != 0) {
- DrawBoolButton(buttons_left, y + button_y_offset, current_value != 0, editable);
+ DrawBoolButton(br.left, y + button_y_offset, current_value != 0, editable);
SetDParam(idx++, current_value == 0 ? STR_CONFIG_SETTING_OFF : STR_CONFIG_SETTING_ON);
} else {
if (config_item.complete_labels) {
- DrawDropDownButton(buttons_left, y + button_y_offset, COLOUR_YELLOW, this->clicked_row == i && clicked_dropdown, editable);
+ DrawDropDownButton(br.left, y + button_y_offset, COLOUR_YELLOW, this->clicked_row == i && clicked_dropdown, editable);
} else {
- DrawArrowButtons(buttons_left, y + button_y_offset, COLOUR_YELLOW, (this->clicked_button == i) ? 1 + (this->clicked_increase != rtl) : 0, editable && current_value > config_item.min_value, editable && current_value < config_item.max_value);
+ DrawArrowButtons(br.left, y + button_y_offset, COLOUR_YELLOW, (this->clicked_button == i) ? 1 + (this->clicked_increase != rtl) : 0, editable && current_value > config_item.min_value, editable && current_value < config_item.max_value);
}
if (config_item.labels != nullptr && config_item.labels->Contains(current_value)) {
SetDParam(idx++, STR_JUST_RAW_STRING);
@@ -418,7 +409,7 @@ struct AISettingsWindow : public Window {
}
}
- DrawString(text_left, text_right, y + text_y_offset, str, colour);
+ DrawString(tr.left, tr.right, y + text_y_offset, str, colour);
y += this->line_height;
}
}
@@ -436,8 +427,8 @@ struct AISettingsWindow : public Window {
{
switch (widget) {
case WID_AIS_BACKGROUND: {
- const NWidgetBase *wid = this->GetWidget(WID_AIS_BACKGROUND);
- int num = (pt.y - wid->pos_y) / this->line_height + this->vscroll->GetPosition();
+ Rect r = this->GetWidget(widget)->GetCurrentRect().Shrink(WidgetDimensions::scaled.matrix, RectPadding::zero);
+ int num = (pt.y - r.top) / this->line_height + this->vscroll->GetPosition();
if (num >= (int)this->visible_settings.size()) break;
VisibleSettingsList::const_iterator it = this->visible_settings.begin();
@@ -454,9 +445,8 @@ struct AISettingsWindow : public Window {
bool bool_item = (config_item.flags & SCRIPTCONFIG_BOOLEAN) != 0;
- int x = pt.x - wid->pos_x;
- if (_current_text_dir == TD_RTL) x = wid->current_x - 1 - x;
- x -= 4;
+ int x = pt.x - r.left;
+ if (_current_text_dir == TD_RTL) x = r.Width() - 1 - x;
/* One of the arrows is clicked (or green/red rect in case of bool value) */
int old_val = this->ai_config->GetSetting(config_item.name);
@@ -467,8 +457,7 @@ struct AISettingsWindow : public Window {
this->clicked_dropdown = false;
this->closing_dropdown = false;
} else {
- const NWidgetBase *wid = this->GetWidget(WID_AIS_BACKGROUND);
- int rel_y = (pt.y - (int)wid->pos_y) % this->line_height;
+ int rel_y = (pt.y - r.top) % this->line_height;
Rect wi_rect;
wi_rect.left = pt.x - (_current_text_dir == TD_RTL ? SETTING_BUTTON_WIDTH - 1 - x : x);
@@ -476,7 +465,7 @@ struct AISettingsWindow : public Window {
wi_rect.top = pt.y - rel_y + (this->line_height - SETTING_BUTTON_HEIGHT) / 2;
wi_rect.bottom = wi_rect.top + SETTING_BUTTON_HEIGHT - 1;
- /* For dropdowns we also have to check the y position thoroughly, the mouse may not above the just opening dropdown */
+ /* If the mouse is still held but dragged outside of the dropdown list, keep the dropdown open */
if (pt.y >= wi_rect.top && pt.y <= wi_rect.bottom) {
this->clicked_dropdown = true;
this->closing_dropdown = false;
@@ -533,24 +522,15 @@ struct AISettingsWindow : public Window {
void OnQueryTextFinished(char *str) override
{
if (StrEmpty(str)) return;
- VisibleSettingsList::const_iterator it = this->visible_settings.begin();
- for (int i = 0; i < this->clicked_row; i++) it++;
- const ScriptConfigItem config_item = **it;
- if (_game_mode == GM_NORMAL && ((this->slot == OWNER_DEITY) || Company::IsValidID(this->slot)) && (config_item.flags & SCRIPTCONFIG_INGAME) == 0) return;
int32 value = atoi(str);
- this->ai_config->SetSetting(config_item.name, value);
- this->SetDirty();
+
+ SetValue(value);
}
void OnDropdownSelect(int widget, int index) override
{
assert(this->clicked_dropdown);
- VisibleSettingsList::const_iterator it = this->visible_settings.begin();
- for (int i = 0; i < this->clicked_row; i++) it++;
- const ScriptConfigItem config_item = **it;
- if (_game_mode == GM_NORMAL && ((this->slot == OWNER_DEITY) || Company::IsValidID(this->slot)) && (config_item.flags & SCRIPTCONFIG_INGAME) == 0) return;
- this->ai_config->SetSetting(config_item.name, index);
- this->SetDirty();
+ SetValue(index);
}
void OnDropdownClose(Point pt, int widget, int index, bool instant_close) override
@@ -592,7 +572,21 @@ struct AISettingsWindow : public Window {
private:
bool IsEditableItem(const ScriptConfigItem &config_item) const
{
- return _game_mode == GM_MENU || ((this->slot != OWNER_DEITY) && !Company::IsValidID(this->slot)) || (config_item.flags & SCRIPTCONFIG_INGAME) != 0;
+ return _game_mode == GM_MENU
+ || _game_mode == GM_EDITOR
+ || ((this->slot != OWNER_DEITY) && !Company::IsValidID(this->slot))
+ || (config_item.flags & SCRIPTCONFIG_INGAME) != 0
+ || _settings_client.gui.ai_developer_tools;
+ }
+
+ void SetValue(int value)
+ {
+ VisibleSettingsList::const_iterator it = this->visible_settings.begin();
+ for (int i = 0; i < this->clicked_row; i++) it++;
+ const ScriptConfigItem config_item = **it;
+ if (_game_mode == GM_NORMAL && ((this->slot == OWNER_DEITY) || Company::IsValidID(this->slot)) && (config_item.flags & SCRIPTCONFIG_INGAME) == 0) return;
+ this->ai_config->SetSetting(config_item.name, value);
+ this->SetDirty();
}
};
@@ -600,7 +594,7 @@ private:
static const NWidgetPart _nested_ai_settings_widgets[] = {
NWidget(NWID_HORIZONTAL),
NWidget(WWT_CLOSEBOX, COLOUR_MAUVE),
- NWidget(WWT_CAPTION, COLOUR_MAUVE, WID_AIS_CAPTION), SetDataTip(STR_AI_SETTINGS_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
+ NWidget(WWT_CAPTION, COLOUR_MAUVE, WID_AIS_CAPTION), SetDataTip(STR_AI_SETTINGS_CAPTION_AI, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
NWidget(WWT_DEFSIZEBOX, COLOUR_MAUVE),
EndContainer(),
NWidget(NWID_HORIZONTAL),
@@ -680,7 +674,7 @@ void ShowScriptTextfileWindow(TextfileType file_type, CompanyID slot)
static const NWidgetPart _nested_ai_config_widgets[] = {
NWidget(NWID_HORIZONTAL),
NWidget(WWT_CLOSEBOX, COLOUR_MAUVE),
- NWidget(WWT_CAPTION, COLOUR_MAUVE), SetDataTip(STR_AI_CONFIG_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
+ NWidget(WWT_CAPTION, COLOUR_MAUVE), SetDataTip(STR_AI_CONFIG_CAPTION_AI, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
EndContainer(),
NWidget(WWT_PANEL, COLOUR_MAUVE, WID_AIC_BACKGROUND),
NWidget(NWID_HORIZONTAL), SetPIP(7, 7, 7),
@@ -693,21 +687,18 @@ static const NWidgetPart _nested_ai_config_widgets[] = {
NWidget(NWID_VERTICAL), SetPIP(5, 5, 5),
NWidget(NWID_SPACER), SetMinimalSize(0, 5),
NWidget(NWID_HORIZONTAL), SetPIP(7, 0, 7),
- NWidget(WWT_PUSHARROWBTN, COLOUR_YELLOW, WID_AIC_DECREASE), SetFill(0, 1), SetDataTip(AWV_DECREASE, STR_NULL),
- NWidget(WWT_PUSHARROWBTN, COLOUR_YELLOW, WID_AIC_INCREASE), SetFill(0, 1), SetDataTip(AWV_INCREASE, STR_NULL),
+ NWidget(WWT_PUSHARROWBTN, COLOUR_YELLOW, WID_AIC_DECREASE), SetDataTip(AWV_DECREASE, STR_NULL),
+ NWidget(WWT_PUSHARROWBTN, COLOUR_YELLOW, WID_AIC_INCREASE), SetDataTip(AWV_INCREASE, STR_NULL),
NWidget(NWID_SPACER), SetMinimalSize(6, 0),
- NWidget(WWT_TEXT, COLOUR_MAUVE, WID_AIC_NUMBER), SetDataTip(STR_DIFFICULTY_LEVEL_SETTING_MAXIMUM_NO_COMPETITORS, STR_NULL), SetFill(1, 0), SetPadding(1, 0, 0, 0),
- EndContainer(),
- NWidget(WWT_FRAME, COLOUR_MAUVE), SetDataTip(STR_AI_CONFIG_GAMESCRIPT, STR_NULL), SetPadding(0, 5, 4, 5),
- NWidget(WWT_MATRIX, COLOUR_MAUVE, WID_AIC_GAMELIST), SetMinimalSize(288, 14), SetFill(1, 0), SetMatrixDataTip(1, 1, STR_AI_CONFIG_GAMELIST_TOOLTIP),
+ NWidget(WWT_TEXT, COLOUR_MAUVE, WID_AIC_NUMBER), SetDataTip(STR_AI_CONFIG_MAX_COMPETITORS, STR_NULL), SetFill(1, 0), SetPadding(1, 0, 0, 0),
EndContainer(),
NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), SetPIP(7, 0, 7),
- NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_AIC_CHANGE), SetFill(1, 0), SetMinimalSize(93, 0), SetDataTip(STR_AI_CONFIG_CHANGE, STR_AI_CONFIG_CHANGE_TOOLTIP),
+ NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_AIC_CHANGE), SetFill(1, 0), SetMinimalSize(93, 0), SetDataTip(STR_AI_CONFIG_CHANGE_AI, STR_AI_CONFIG_CHANGE_TOOLTIP),
NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_AIC_CONFIGURE), SetFill(1, 0), SetMinimalSize(93, 0), SetDataTip(STR_AI_CONFIG_CONFIGURE, STR_AI_CONFIG_CONFIGURE_TOOLTIP),
- NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_AIC_CLOSE), SetFill(1, 0), SetMinimalSize(93, 0), SetDataTip(STR_AI_SETTINGS_CLOSE, STR_NULL),
+ NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_AIC_TEXTFILE + TFT_README), SetFill(1, 0), SetMinimalSize(93, 0), SetDataTip(STR_AI_SETTINGS_CLOSE, STR_NULL),
EndContainer(),
NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), SetPIP(7, 0, 7),
- NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_AIC_TEXTFILE + TFT_README), SetFill(1, 0), SetResize(1, 0), SetDataTip(STR_TEXTFILE_VIEW_README, STR_NULL),
+ NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_AIC_CLOSE), SetFill(1, 0), SetResize(1, 0), SetDataTip(STR_TEXTFILE_VIEW_README, STR_NULL),
NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_AIC_TEXTFILE + TFT_CHANGELOG), SetFill(1, 0), SetResize(1, 0), SetDataTip(STR_TEXTFILE_VIEW_CHANGELOG, STR_NULL),
NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_AIC_TEXTFILE + TFT_LICENSE), SetFill(1, 0), SetResize(1, 0), SetDataTip(STR_TEXTFILE_VIEW_LICENCE, STR_NULL),
EndContainer(),
@@ -757,54 +748,22 @@ struct AIConfigWindow : public Window {
case WID_AIC_NUMBER:
SetDParam(0, GetGameSettings().difficulty.max_no_competitors);
break;
- case WID_AIC_CHANGE:
- switch (selected_slot) {
- case OWNER_DEITY:
- SetDParam(0, STR_AI_CONFIG_CHANGE_GAMESCRIPT);
- break;
-
- case INVALID_COMPANY:
- SetDParam(0, STR_AI_CONFIG_CHANGE_NONE);
- break;
-
- default:
- SetDParam(0, STR_AI_CONFIG_CHANGE_AI);
- break;
- }
- break;
}
}
void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) override
{
switch (widget) {
- case WID_AIC_GAMELIST:
- this->line_height = GetMinButtonSize(FONT_HEIGHT_NORMAL + WD_MATRIX_TOP + WD_MATRIX_BOTTOM);
- size->height = this->line_height;
+ case WID_AIC_DECREASE:
+ case WID_AIC_INCREASE:
+ *size = maxdim(*size, NWidgetScrollbar::GetHorizontalDimension());
break;
case WID_AIC_LIST:
- this->line_height = FONT_HEIGHT_NORMAL + WD_MATRIX_TOP + WD_MATRIX_BOTTOM;
- this->line_height = GetMinButtonSize(this->line_height);
+ this->line_height = FONT_HEIGHT_NORMAL + padding.height;
resize->height = this->line_height;
size->height = 8 * this->line_height;
break;
-
- case WID_AIC_CHANGE: {
- SetDParam(0, STR_AI_CONFIG_CHANGE_GAMESCRIPT);
- Dimension dim = GetStringBoundingBox(STR_AI_CONFIG_CHANGE);
-
- SetDParam(0, STR_AI_CONFIG_CHANGE_NONE);
- dim = maxdim(dim, GetStringBoundingBox(STR_AI_CONFIG_CHANGE));
-
- SetDParam(0, STR_AI_CONFIG_CHANGE_AI);
- dim = maxdim(dim, GetStringBoundingBox(STR_AI_CONFIG_CHANGE));
-
- dim.width += padding.width;
- dim.height += padding.height;
- *size = maxdim(*size, dim);
- break;
- }
}
}
@@ -815,8 +774,6 @@ struct AIConfigWindow : public Window {
*/
static bool IsEditable(CompanyID slot)
{
- if (slot == OWNER_DEITY) return _game_mode != GM_NORMAL || Game::GetInstance() != nullptr;
-
if (_game_mode != GM_NORMAL) {
return slot > 0 && slot <= GetGameSettings().difficulty.max_no_competitors;
}
@@ -832,22 +789,8 @@ struct AIConfigWindow : public Window {
void DrawWidget(const Rect &r, int widget) const override
{
switch (widget) {
- case WID_AIC_GAMELIST: {
- StringID text = STR_AI_CONFIG_NONE;
-
- if (GameConfig::GetConfig()->GetInfo() != nullptr) {
- SetDParamStr(0, GameConfig::GetConfig()->GetInfo()->GetName());
- text = STR_JUST_RAW_STRING;
- }
-
- DrawString(r.left + 10, r.right - 10, Center(r.top, this->line_height), text,
- (this->selected_slot == OWNER_DEITY) ? TC_WHITE : (IsEditable(OWNER_DEITY) ? TC_ORANGE : TC_SILVER));
-
- break;
- }
-
case WID_AIC_LIST: {
- int y = Center(r.top, this->line_height);
+ Rect tr = r.Shrink(WidgetDimensions::scaled.matrix);
for (int i = this->vscroll->GetPosition(); this->vscroll->IsVisible(i) && i < MAX_COMPANIES; i++) {
StringID text;
@@ -859,9 +802,9 @@ struct AIConfigWindow : public Window {
} else {
text = STR_AI_CONFIG_RANDOM_AI;
}
- DrawString(r.left + 10, r.right - 10, y, text,
+ DrawString(tr, text,
(this->selected_slot == i) ? TC_WHITE : (IsEditable((CompanyID)i) ? TC_ORANGE : TC_SILVER));
- y += this->line_height;
+ tr.top += this->line_height;
}
break;
}
@@ -890,13 +833,6 @@ struct AIConfigWindow : public Window {
break;
}
- case WID_AIC_GAMELIST: {
- this->selected_slot = OWNER_DEITY;
- this->InvalidateData();
- if (click_count > 1 && this->selected_slot != INVALID_COMPANY && _game_mode != GM_NORMAL) ShowAIListWindow((CompanyID)this->selected_slot);
- break;
- }
-
case WID_AIC_LIST: { // Select a slot
this->selected_slot = (CompanyID)this->vscroll->GetScrolledRowFromWidget(pt.y, this, widget);
this->InvalidateData();
@@ -938,7 +874,7 @@ struct AIConfigWindow : public Window {
if (!_network_available) {
ShowErrorMessage(STR_NETWORK_ERROR_NOTAVAILABLE, INVALID_STRING_ID, WL_ERROR);
} else {
- ShowNetworkContentListWindow(nullptr, CONTENT_TYPE_AI, CONTENT_TYPE_GAME);
+ ShowNetworkContentListWindow(nullptr, CONTENT_TYPE_AI);
}
break;
}
@@ -959,10 +895,10 @@ struct AIConfigWindow : public Window {
this->SetWidgetDisabledState(WID_AIC_DECREASE, GetGameSettings().difficulty.max_no_competitors == 0);
this->SetWidgetDisabledState(WID_AIC_INCREASE, GetGameSettings().difficulty.max_no_competitors == MAX_COMPANIES - 1);
- this->SetWidgetDisabledState(WID_AIC_CHANGE, (this->selected_slot == OWNER_DEITY && _game_mode == GM_NORMAL) || this->selected_slot == INVALID_COMPANY);
+ this->SetWidgetDisabledState(WID_AIC_CHANGE, this->selected_slot == INVALID_COMPANY);
this->SetWidgetDisabledState(WID_AIC_CONFIGURE, this->selected_slot == INVALID_COMPANY || GetConfig(this->selected_slot)->GetConfigList()->size() == 0);
- this->SetWidgetDisabledState(WID_AIC_MOVE_UP, this->selected_slot == OWNER_DEITY || this->selected_slot == INVALID_COMPANY || !IsEditable((CompanyID)(this->selected_slot - 1)));
- this->SetWidgetDisabledState(WID_AIC_MOVE_DOWN, this->selected_slot == OWNER_DEITY || this->selected_slot == INVALID_COMPANY || !IsEditable((CompanyID)(this->selected_slot + 1)));
+ this->SetWidgetDisabledState(WID_AIC_MOVE_UP, this->selected_slot == INVALID_COMPANY || !IsEditable((CompanyID)(this->selected_slot - 1)));
+ this->SetWidgetDisabledState(WID_AIC_MOVE_DOWN, this->selected_slot == INVALID_COMPANY || !IsEditable((CompanyID)(this->selected_slot + 1)));
for (TextfileType tft = TFT_BEGIN; tft < TFT_END; tft++) {
this->SetWidgetDisabledState(WID_AIC_TEXTFILE + tft, this->selected_slot == INVALID_COMPANY || (GetConfig(this->selected_slot)->GetTextfile(tft, this->selected_slot) == nullptr));
@@ -1002,9 +938,6 @@ static bool SetScriptButtonColour(NWidgetCore &button, bool dead, bool paused)
* Window with everything an AI prints via ScriptLog.
*/
struct AIDebugWindow : public Window {
- static const int top_offset; ///< Offset of the text at the top of the WID_AID_LOG_PANEL.
- static const int bottom_offset; ///< Offset of the text at the bottom of the WID_AID_LOG_PANEL.
-
static const uint MAX_BREAK_STR_STRING_LENGTH = 256; ///< Maximum length of the break string.
static CompanyID ai_debug_company; ///< The AI that is (was last) being debugged.
@@ -1108,8 +1041,8 @@ struct AIDebugWindow : public Window {
void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) override
{
if (widget == WID_AID_LOG_PANEL) {
- resize->height = GetMinButtonSize(FONT_HEIGHT_NORMAL + WD_PAR_VSEP_NORMAL);
- size->height = 14 * resize->height + this->top_offset + this->bottom_offset;
+ resize->height = FONT_HEIGHT_NORMAL + WidgetDimensions::scaled.vsep_normal;
+ size->height = 14 * resize->height + WidgetDimensions::scaled.framerect.Vertical();
}
}
@@ -1124,8 +1057,6 @@ struct AIDebugWindow : public Window {
bool dirty = false;
- Dimension d = GetSpriteSize(SPR_COMPANY_ICON);
- uint offset_y = Center(0, GetMinButtonSize(d.height + WD_MATRIX_TOP + WD_MATRIX_BOTTOM + 1), d.height);
/* Paint the company icons */
for (CompanyID i = COMPANY_FIRST; i < MAX_COMPANIES; i++) {
NWidgetCore *button = this->GetWidget(i + WID_AID_COMPANY_BUTTON_START);
@@ -1146,7 +1077,7 @@ struct AIDebugWindow : public Window {
if (!valid) continue;
byte offset = (i == ai_debug_company) ? 1 : 0;
- DrawCompanyIcon(i, Center(button->pos_x + offset, button->current_x, d.width), button->pos_y + offset + offset_y);
+ DrawCompanyIcon(i, button->pos_x + button->current_x / 2 - 7 + offset, this->GetWidget(WID_AID_COMPANY_BUTTON_START + i)->pos_y + 2 + offset);
}
/* Set button colour for Game Script. */
@@ -1225,7 +1156,8 @@ struct AIDebugWindow : public Window {
ScriptLog::LogData *log = this->GetLogPointer();
if (log == nullptr) return;
- int y = Center(this->top_offset, this->resize.step_height);
+ Rect br = r.Shrink(WidgetDimensions::scaled.bevel);
+ Rect tr = r.Shrink(WidgetDimensions::scaled.framerect);
for (int i = this->vscroll->GetPosition(); this->vscroll->IsVisible(i) && i < log->used; i++) {
int pos = (i + log->pos + 1 - log->used + log->count) % log->count;
if (log->lines[pos] == nullptr) break;
@@ -1242,12 +1174,12 @@ struct AIDebugWindow : public Window {
/* Check if the current line should be highlighted */
if (pos == this->highlight_row) {
- GfxFillRect(r.left + 1, r.top + y, r.right - 1, r.top + y + this->resize.step_height - WD_PAR_VSEP_NORMAL, PC_BLACK);
+ GfxFillRect(br.left, tr.top, br.right, tr.top + this->resize.step_height - 1, PC_BLACK);
if (colour == TC_BLACK) colour = TC_WHITE; // Make black text readable by inverting it to white.
}
- DrawString(r.left + 7, r.right - 7, r.top + y, log->lines[pos], colour, SA_LEFT | SA_FORCE);
- y += this->resize.step_height;
+ DrawString(tr, log->lines[pos], colour, SA_LEFT | SA_FORCE);
+ tr.top += this->resize.step_height;
}
break;
}
@@ -1293,8 +1225,8 @@ struct AIDebugWindow : public Window {
case WID_AID_RELOAD_TOGGLE:
if (ai_debug_company == OWNER_DEITY) break;
/* First kill the company of the AI, then start a new one. This should start the current AI again */
- DoCommandP(0, CCA_DELETE | ai_debug_company << 16 | CRR_MANUAL << 24, 0, CMD_COMPANY_CTRL);
- DoCommandP(0, CCA_NEW_AI | ai_debug_company << 16, 0, CMD_COMPANY_CTRL);
+ Command::Post(CCA_DELETE, ai_debug_company, CRR_MANUAL, INVALID_CLIENT_ID);
+ Command::Post(CCA_NEW_AI, ai_debug_company, CRR_NONE, INVALID_CLIENT_ID);
break;
case WID_AID_SETTINGS:
@@ -1333,7 +1265,7 @@ struct AIDebugWindow : public Window {
}
if (all_unpaused) {
/* All scripts have been unpaused => unpause the game. */
- DoCommandP(0, PM_PAUSED_NORMAL, 0, CMD_PAUSE);
+ Command::Post(PM_PAUSED_NORMAL, false);
}
}
}
@@ -1382,7 +1314,7 @@ struct AIDebugWindow : public Window {
/* Pause the game. */
if ((_pause_mode & PM_PAUSED_NORMAL) == PM_UNPAUSED) {
- DoCommandP(0, PM_PAUSED_NORMAL, 1, CMD_PAUSE);
+ Command::Post(PM_PAUSED_NORMAL, true);
}
/* Highlight row that matched */
@@ -1419,14 +1351,12 @@ struct AIDebugWindow : public Window {
void OnResize() override
{
- this->vscroll->SetCapacityFromWidget(this, WID_AID_LOG_PANEL);
+ this->vscroll->SetCapacityFromWidget(this, WID_AID_LOG_PANEL, WidgetDimensions::scaled.framerect.Vertical());
}
static HotkeyList hotkeys;
};
-const int AIDebugWindow::top_offset = WD_FRAMERECT_TOP + 2;
-const int AIDebugWindow::bottom_offset = WD_FRAMERECT_BOTTOM;
CompanyID AIDebugWindow::ai_debug_company = INVALID_COMPANY;
char AIDebugWindow::break_string[MAX_BREAK_STR_STRING_LENGTH] = "";
bool AIDebugWindow::break_check_enabled = true;
diff --git a/src/ai/ai_gui.hpp b/src/ai/ai_gui.hpp
index 12c8ca859b..be6263ddd5 100644
--- a/src/ai/ai_gui.hpp
+++ b/src/ai/ai_gui.hpp
@@ -12,8 +12,10 @@
#include "../company_type.h"
+void ShowAIListWindow(CompanyID slot);
Window* ShowAIDebugWindow(CompanyID show_company = INVALID_COMPANY);
void ShowAIConfigWindow();
+void ShowScriptTextfileWindow(TextfileType file_type, CompanyID slot);
void ShowAIDebugWindowIfAIError();
void InitializeAIGui();
diff --git a/src/ai/ai_info.cpp b/src/ai/ai_info.cpp
index 17ad012579..fcd1b5a1a9 100644
--- a/src/ai/ai_info.cpp
+++ b/src/ai/ai_info.cpp
@@ -25,7 +25,7 @@
*/
static bool CheckAPIVersion(const char *api_version)
{
- static const std::set versions = { "0.7", "1.0", "1.1", "1.2", "1.3", "1.4", "1.5", "1.6", "1.7", "1.8", "1.9", "1.10", "1.11", "12" };
+ static const std::set versions = { "0.7", "1.0", "1.1", "1.2", "1.3", "1.4", "1.5", "1.6", "1.7", "1.8", "1.9", "1.10", "1.11", "12", "13" };
return versions.find(api_version) != versions.end();
}
diff --git a/src/ai/ai_instance.cpp b/src/ai/ai_instance.cpp
index 9e1f001631..fbec89270f 100644
--- a/src/ai/ai_instance.cpp
+++ b/src/ai/ai_instance.cpp
@@ -18,6 +18,7 @@
#include "ai.hpp"
#include "../script/script_storage.hpp"
+#include "../script/script_cmd.h"
#include "ai_info.hpp"
#include "ai_instance.hpp"
@@ -60,6 +61,9 @@ void AIInstance::Died()
{
ScriptInstance::Died();
+ /* Intro is not supposed to use AI, but it may have 'dummy' AI which instant dies. */
+ if (_game_mode == GM_MENU) return;
+
ShowAIDebugWindow(_current_company);
const AIInfo *info = AIConfig::GetConfig(_current_company, AIConfig::SSS_FORCE_GAME)->GetInfo();
@@ -92,13 +96,13 @@ ScriptInfo *AIInstance::FindLibrary(const char *library, int version)
/**
* DoCommand callback function for all commands executed by AIs.
+ * @param cmd cmd as given to DoCommandPInternal.
* @param result The result of the command.
* @param tile The tile on which the command was executed.
- * @param p1 p1 as given to DoCommandPInternal.
- * @param p2 p2 as given to DoCommandPInternal.
- * @param cmd cmd as given to DoCommandPInternal.
+ * @param data Command data as given to Command<>::Post.
+ * @param result_data Additional returned data from the command.
*/
-void CcAI(const CommandCost &result, TileIndex tile, uint32 p1, uint32 p2, uint32 cmd)
+void CcAI(Commands cmd, const CommandCost &result, TileIndex tile, const CommandDataBuffer &data, CommandDataBuffer result_data)
{
/*
* The company might not exist anymore. Check for this.
@@ -109,12 +113,12 @@ void CcAI(const CommandCost &result, TileIndex tile, uint32 p1, uint32 p2, uint3
const Company *c = Company::GetIfValid(_current_company);
if (c == nullptr || c->ai_instance == nullptr) return;
- if (c->ai_instance->DoCommandCallback(result, tile, p1, p2, cmd)) {
+ if (c->ai_instance->DoCommandCallback(result, tile, data, std::move(result_data), cmd)) {
c->ai_instance->Continue();
}
}
-CommandCallback *AIInstance::GetDoCommandCallback()
+CommandCallbackData *AIInstance::GetDoCommandCallback()
{
return &CcAI;
}
diff --git a/src/ai/ai_instance.hpp b/src/ai/ai_instance.hpp
index f8d2100b1c..2cdabd9913 100644
--- a/src/ai/ai_instance.hpp
+++ b/src/ai/ai_instance.hpp
@@ -29,7 +29,7 @@ public:
private:
void RegisterAPI() override;
void Died() override;
- CommandCallback *GetDoCommandCallback() override;
+ CommandCallbackData *GetDoCommandCallback() override;
void LoadDummyScript() override;
};
diff --git a/src/ai/ai_scanner.cpp b/src/ai/ai_scanner.cpp
index e99b9f8f61..ac77a0b8fd 100644
--- a/src/ai/ai_scanner.cpp
+++ b/src/ai/ai_scanner.cpp
@@ -10,6 +10,7 @@
#include "../stdafx.h"
#include "../debug.h"
#include "../network/network.h"
+#include "../openttd.h"
#include "../core/random_func.hpp"
#include "../script/squirrel_class.hpp"
@@ -59,6 +60,11 @@ void AIScannerInfo::RegisterAPI(class Squirrel *engine)
AIInfo *AIScannerInfo::SelectRandomAI() const
{
+ if (_game_mode == GM_MENU) {
+ Debug(script, 0, "The intro game should not use AI, loading 'dummy' AI.");
+ return this->info_dummy;
+ }
+
uint num_random_ais = 0;
for (const auto &item : info_single_list) {
AIInfo *i = static_cast(item.second);
diff --git a/src/aircraft.h b/src/aircraft.h
index d201743a6f..0a5c0e5530 100644
--- a/src/aircraft.h
+++ b/src/aircraft.h
@@ -91,7 +91,7 @@ struct Aircraft FINAL : public SpecializedVehicle {
void MarkDirty();
void UpdateDeltaXY();
- ExpensesType GetExpenseType(bool income) const { return income ? EXPENSES_AIRCRAFT_INC : EXPENSES_AIRCRAFT_RUN; }
+ ExpensesType GetExpenseType(bool income) const { return income ? EXPENSES_AIRCRAFT_REVENUE : EXPENSES_AIRCRAFT_RUN; }
bool IsPrimaryVehicle() const { return this->IsNormalAircraft(); }
void GetImage(Direction direction, EngineImageType image_type, VehicleSpriteSeq *result) const;
int GetDisplaySpeed() const { return this->cur_speed; }
diff --git a/src/aircraft_cmd.cpp b/src/aircraft_cmd.cpp
index f4f933982a..7a33d4c262 100644
--- a/src/aircraft_cmd.cpp
+++ b/src/aircraft_cmd.cpp
@@ -37,6 +37,8 @@
#include "disaster_vehicle.h"
#include "newgrf_airporttiles.h"
#include "framerate_type.h"
+#include "aircraft_cmd.h"
+#include "vehicle_cmd.h"
#include "table/strings.h"
@@ -188,7 +190,7 @@ void GetRotorImage(const Aircraft *v, EngineImageType image_type, VehicleSpriteS
const Aircraft *w = v->Next()->Next();
if (is_custom_sprite(v->spritenum)) {
- GetCustomRotorSprite(v, false, image_type, result);
+ GetCustomRotorSprite(v, image_type, result);
if (result->IsValid()) return;
}
@@ -229,7 +231,7 @@ void DrawAircraftEngine(int left, int right, int preferred_x, int y, EngineID en
VehicleSpriteSeq rotor_seq;
GetCustomRotorIcon(engine, image_type, &rotor_seq);
if (!rotor_seq.IsValid()) rotor_seq.Set(SPR_ROTOR_STOPPED);
- rotor_seq.Draw(preferred_x, y - ScaleGUITrad(5), PAL_NONE, false);
+ rotor_seq.Draw(preferred_x, y - ScaleSpriteTrad(5), PAL_NONE, false);
}
}
@@ -250,22 +252,21 @@ void GetAircraftSpriteSize(EngineID engine, uint &width, uint &height, int &xoff
Rect rect;
seq.GetBounds(&rect);
- width = UnScaleGUI(rect.right - rect.left + 1);
- height = UnScaleGUI(rect.bottom - rect.top + 1);
+ width = UnScaleGUI(rect.Width());
+ height = UnScaleGUI(rect.Height());
xoffs = UnScaleGUI(rect.left);
yoffs = UnScaleGUI(rect.top);
}
/**
* Build an aircraft.
- * @param tile tile of the depot where aircraft is built.
* @param flags type of operation.
+ * @param tile tile of the depot where aircraft is built.
* @param e the engine to build.
- * @param data unused.
* @param[out] ret the vehicle that has been built.
* @return the cost of this operation or an error.
*/
-CommandCost CmdBuildAircraft(TileIndex tile, DoCommandFlag flags, const Engine *e, uint16 data, Vehicle **ret)
+CommandCost CmdBuildAircraft(DoCommandFlag flags, TileIndex tile, const Engine *e, Vehicle **ret)
{
const AircraftVehicleInfo *avi = &e->u.air;
const Station *st = Station::GetByTile(tile);
@@ -327,8 +328,6 @@ CommandCost CmdBuildAircraft(TileIndex tile, DoCommandFlag flags, const Engine *
v->reliability_spd_dec = e->reliability_spd_dec;
v->max_age = e->GetLifeLengthInDays();
- _new_vehicle_id = v->index;
-
v->pos = GetVehiclePosOnBuild(tile);
v->state = HANGAR;
@@ -1274,7 +1273,7 @@ void HandleMissingAircraftOrders(Aircraft *v)
const Station *st = GetTargetAirportIfValid(v);
if (st == nullptr) {
Backup cur_company(_current_company, v->owner, FILE_LINE);
- CommandCost ret = DoCommand(v->tile, v->index, 0, DC_EXEC, CMD_SEND_VEHICLE_TO_DEPOT);
+ CommandCost ret = Command::Do(DC_EXEC, v->index, DepotCommand::None, {});
cur_company.Restore();
if (ret.Failed()) CrashAirplane(v);
@@ -1341,7 +1340,12 @@ static void CrashAirplane(Aircraft *v)
AI::NewEvent(v->owner, new ScriptEventVehicleCrashed(v->index, vt, st == nullptr ? ScriptEventVehicleCrashed::CRASH_AIRCRAFT_NO_AIRPORT : ScriptEventVehicleCrashed::CRASH_PLANE_LANDING));
Game::NewEvent(new ScriptEventVehicleCrashed(v->index, vt, st == nullptr ? ScriptEventVehicleCrashed::CRASH_AIRCRAFT_NO_AIRPORT : ScriptEventVehicleCrashed::CRASH_PLANE_LANDING));
- AddTileNewsItem(newsitem, NT_ACCIDENT, vt, nullptr, st != nullptr ? st->index : INVALID_STATION);
+ NewsType newstype = NT_ACCIDENT;
+ if (v->owner != _local_company) {
+ newstype = NT_ACCIDENT_OTHER;
+ }
+
+ AddTileNewsItem(newsitem, newstype, vt, nullptr, st != nullptr ? st->index : INVALID_STATION);
ModifyStationRatingAround(vt, v->owner, -160, 30);
if (_settings_client.sound.disaster) SndPlayVehicleFx(SND_12_EXPLOSION, v);
@@ -1632,7 +1636,7 @@ static void AircraftEventHandler_HeliTakeOff(Aircraft *v, const AirportFTAClass
/* Send the helicopter to a hangar if needed for replacement */
if (v->NeedsAutomaticServicing()) {
Backup cur_company(_current_company, v->owner, FILE_LINE);
- DoCommand(v->tile, v->index | DEPOT_SERVICE | DEPOT_LOCATE_HANGAR, 0, DC_EXEC, CMD_SEND_VEHICLE_TO_DEPOT);
+ Command::Do(DC_EXEC, v->index, DepotCommand::Service | DepotCommand::LocateHangar, {});
cur_company.Restore();
}
}
@@ -1683,7 +1687,7 @@ static void AircraftEventHandler_Landing(Aircraft *v, const AirportFTAClass *apc
/* check if the aircraft needs to be replaced or renewed and send it to a hangar if needed */
if (v->NeedsAutomaticServicing()) {
Backup cur_company(_current_company, v->owner, FILE_LINE);
- DoCommand(v->tile, v->index | DEPOT_SERVICE, 0, DC_EXEC, CMD_SEND_VEHICLE_TO_DEPOT);
+ Command::Do(DC_EXEC, v->index, DepotCommand::Service, {});
cur_company.Restore();
}
}
diff --git a/src/aircraft_cmd.h b/src/aircraft_cmd.h
new file mode 100644
index 0000000000..df58739a54
--- /dev/null
+++ b/src/aircraft_cmd.h
@@ -0,0 +1,19 @@
+/*
+ * This file is part of OpenTTD.
+ * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
+ * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see .
+ */
+
+/** @file aircraft_cmd.h Command definitions related to aircraft. */
+
+#ifndef AIRCRAFT_CMD_H
+#define AIRCRAFT_CMD_H
+
+#include "command_type.h"
+#include "engine_type.h"
+#include "vehicle_type.h"
+
+CommandCost CmdBuildAircraft(DoCommandFlag flags, TileIndex tile, const Engine *e, Vehicle **v);
+
+#endif /* AIRCRAFT_CMD_H */
diff --git a/src/aircraft_gui.cpp b/src/aircraft_gui.cpp
index 311bc497bf..421ddaf33b 100644
--- a/src/aircraft_gui.cpp
+++ b/src/aircraft_gui.cpp
@@ -25,59 +25,58 @@
* Draw the details for the given vehicle at the given position
*
* @param v current vehicle
- * @param left The left most coordinate to draw
- * @param right The right most coordinate to draw
- * @param y The y coordinate
+ * @param r the Rect to draw within
*/
-void DrawAircraftDetails(const Aircraft *v, int left, int right, int y)
+void DrawAircraftDetails(const Aircraft *v, const Rect &r)
{
- int y_offset = (v->Next()->cargo_cap != 0) ? -(FONT_HEIGHT_NORMAL + 1): 0;
Money feeder_share = 0;
+ int y = r.top;
for (const Aircraft *u = v; u != nullptr; u = u->Next()) {
if (u->IsNormalAircraft()) {
SetDParam(0, u->engine_type);
SetDParam(1, u->build_year);
SetDParam(2, u->value);
- DrawString(left, right, y, STR_VEHICLE_INFO_BUILT_VALUE);
+ DrawString(r.left, r.right, y, STR_VEHICLE_INFO_BUILT_VALUE);
+ y += FONT_HEIGHT_NORMAL;
SetDParam(0, u->cargo_type);
SetDParam(1, u->cargo_cap);
SetDParam(2, u->Next()->cargo_type);
SetDParam(3, u->Next()->cargo_cap);
SetDParam(4, GetCargoSubtypeText(u));
- DrawString(left, right, y + FONT_HEIGHT_NORMAL, (u->Next()->cargo_cap != 0) ? STR_VEHICLE_INFO_CAPACITY_CAPACITY : STR_VEHICLE_INFO_CAPACITY);
+ DrawString(r.left, r.right, y, (u->Next()->cargo_cap != 0) ? STR_VEHICLE_INFO_CAPACITY_CAPACITY : STR_VEHICLE_INFO_CAPACITY);
+ y += FONT_HEIGHT_NORMAL + WidgetDimensions::scaled.vsep_normal;
}
if (u->cargo_cap != 0) {
uint cargo_count = u->cargo.StoredCount();
- y_offset += FONT_HEIGHT_NORMAL + 1;
if (cargo_count != 0) {
/* Cargo names (fix pluralness) */
SetDParam(0, u->cargo_type);
SetDParam(1, cargo_count);
SetDParam(2, u->cargo.Source());
- DrawString(left, right, y + 2 * FONT_HEIGHT_NORMAL + 1 + y_offset, STR_VEHICLE_DETAILS_CARGO_FROM);
+ DrawString(r.left, r.right, y, STR_VEHICLE_DETAILS_CARGO_FROM);
+ y += FONT_HEIGHT_NORMAL;
feeder_share += u->cargo.FeederShare();
}
}
}
+ y += WidgetDimensions::scaled.vsep_normal;
SetDParam(0, feeder_share);
- DrawString(left, right, y + 3 * FONT_HEIGHT_NORMAL + 3 + y_offset, STR_VEHICLE_INFO_FEEDER_CARGO_VALUE);
+ DrawString(r.left, r.right, y, STR_VEHICLE_INFO_FEEDER_CARGO_VALUE);
}
/**
* Draws an image of an aircraft
* @param v Front vehicle
- * @param left The minimum horizontal position
- * @param right The maximum horizontal position
- * @param y Vertical position to draw at
+ * @param r Rect to draw at
* @param selection Selected vehicle to draw a frame around
*/
-void DrawAircraftImage(const Vehicle *v, int left, int right, int y, VehicleID selection, EngineImageType image_type)
+void DrawAircraftImage(const Vehicle *v, const Rect &r, VehicleID selection, EngineImageType image_type)
{
bool rtl = _current_text_dir == TD_RTL;
@@ -87,27 +86,29 @@ void DrawAircraftImage(const Vehicle *v, int left, int right, int y, VehicleID s
Rect rect;
seq.GetBounds(&rect);
- int width = UnScaleGUI(rect.right - rect.left + 1);
+ int width = UnScaleGUI(rect.Width());
int x_offs = UnScaleGUI(rect.left);
- int x = rtl ? right - width - x_offs : left - x_offs;
+ int x = rtl ? r.right - width - x_offs : r.left - x_offs;
+ /* This magic -1 offset is related to the sprite_y_offsets in build_vehicle_gui.cpp */
+ int y = ScaleSpriteTrad(-1) + CenterBounds(r.top, r.bottom, 0);
bool helicopter = v->subtype == AIR_HELICOPTER;
- int y_offs = ScaleGUITrad(10);
int heli_offs = 0;
PaletteID pal = (v->vehstatus & VS_CRASHED) ? PALETTE_CRASH : GetVehiclePalette(v);
- seq.Draw(x, y + y_offs, pal, (v->vehstatus & VS_CRASHED) != 0);
+ seq.Draw(x, y, pal, (v->vehstatus & VS_CRASHED) != 0);
if (helicopter) {
const Aircraft *a = Aircraft::From(v);
VehicleSpriteSeq rotor_seq;
- GetCustomRotorSprite(a, true, image_type, &rotor_seq);
+ GetCustomRotorSprite(a, image_type, &rotor_seq);
if (!rotor_seq.IsValid()) rotor_seq.Set(SPR_ROTOR_STOPPED);
- heli_offs = ScaleGUITrad(5);
- rotor_seq.Draw(x, y + y_offs - heli_offs, PAL_NONE, false);
+ heli_offs = ScaleSpriteTrad(5);
+ rotor_seq.Draw(x, y - heli_offs, PAL_NONE, false);
}
if (v->index == selection) {
x += x_offs;
- y += UnScaleGUI(rect.top) + y_offs - heli_offs;
- DrawFrameRect(x - 1, y - 1, x + width + 1, y + UnScaleGUI(rect.bottom - rect.top + 1) + heli_offs + 1, COLOUR_WHITE, FR_BORDERONLY);
+ y += UnScaleGUI(rect.top) - heli_offs;
+ Rect hr = {x, y, x + width - 1, y + UnScaleGUI(rect.Height()) + heli_offs - 1};
+ DrawFrameRect(hr.Expand(WidgetDimensions::scaled.bevel), COLOUR_WHITE, FR_BORDERONLY);
}
}
diff --git a/src/airport_cmd.h b/src/airport_cmd.h
new file mode 100644
index 0000000000..2e27057d8c
--- /dev/null
+++ b/src/airport_cmd.h
@@ -0,0 +1,17 @@
+/*
+ * This file is part of OpenTTD.
+ * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
+ * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see .
+ */
+
+/** @file airport_cmd.h Command definitions related to airports. */
+
+#ifndef AIRPORT_CMD_H
+#define AIRPORT_CMD_H
+
+#include "command_type.h"
+
+CommandCallback CcBuildAirport;
+
+#endif /* AIRPORT_CMD_H */
diff --git a/src/airport_gui.cpp b/src/airport_gui.cpp
index 5cf52f878a..7ff21336c7 100644
--- a/src/airport_gui.cpp
+++ b/src/airport_gui.cpp
@@ -8,6 +8,7 @@
/** @file airport_gui.cpp The GUI for airports. */
#include "stdafx.h"
+#include "economy_func.h"
#include "window_gui.h"
#include "station_gui.h"
#include "terraform_gui.h"
@@ -27,6 +28,9 @@
#include "vehicle_func.h"
#include "gui.h"
#include "command_func.h"
+#include "airport_cmd.h"
+#include "station_cmd.h"
+#include "zoom_func.h"
#include "build_confirmation_func.h"
#include "widgets/airport_widget.h"
@@ -42,7 +46,7 @@ static void ShowBuildAirportPicker(Window *parent);
SpriteID GetCustomAirportSprite(const AirportSpec *as, byte layout);
-void CcBuildAirport(const CommandCost &result, TileIndex tile, uint32 p1, uint32 p2, uint32 cmd)
+void CcBuildAirport(Commands cmd, const CommandCost &result, TileIndex tile)
{
if (result.Failed()) return;
@@ -57,13 +61,20 @@ void CcBuildAirport(const CommandCost &result, TileIndex tile, uint32 p1, uint32
static void PlaceAirport(TileIndex tile)
{
if (_selected_airport_index == -1) return;
- uint32 p2 = _ctrl_pressed;
- SB(p2, 16, 16, INVALID_STATION); // no station to join
- uint32 p1 = AirportClass::Get(_selected_airport_class)->GetSpec(_selected_airport_index)->GetIndex();
- p1 |= _selected_airport_layout << 8;
- CommandContainer cmdcont = { tile, p1, p2, CMD_BUILD_AIRPORT | CMD_MSG(STR_ERROR_CAN_T_BUILD_AIRPORT_HERE), CcBuildAirport, "" };
- ShowSelectStationIfNeeded(cmdcont, TileArea(tile, _thd.size.x / TILE_SIZE, _thd.size.y / TILE_SIZE));
+ byte airport_type = AirportClass::Get(_selected_airport_class)->GetSpec(_selected_airport_index)->GetIndex();
+ byte layout = _selected_airport_layout;
+ bool adjacent = _ctrl_pressed;
+
+ auto proc = [=](bool test, StationID to_join) -> bool {
+ if (test) {
+ return Command::Do(CommandFlagsToDCFlags(GetCommandFlags()), tile, airport_type, layout, INVALID_STATION, adjacent).Succeeded();
+ } else {
+ return Command::Post(STR_ERROR_CAN_T_BUILD_AIRPORT_HERE, CcBuildAirport, tile, airport_type, layout, to_join, adjacent);
+ }
+ };
+
+ ShowSelectStationIfNeeded(TileArea(tile, _thd.size.x / TILE_SIZE, _thd.size.y / TILE_SIZE), proc);
}
/** Airport build toolbar window handler. */
@@ -346,10 +357,10 @@ public:
const AirportSpec *as = AirportSpec::Get(i);
if (!as->enabled) continue;
- size->width = std::max(size->width, GetStringBoundingBox(as->name).width);
+ size->width = std::max(size->width, GetStringBoundingBox(as->name).width + padding.width);
}
- this->line_height = GetMinButtonSize(FONT_HEIGHT_NORMAL + WD_MATRIX_TOP + WD_MATRIX_BOTTOM);
+ this->line_height = FONT_HEIGHT_NORMAL + padding.height;
size->height = 5 * this->line_height;
break;
}
@@ -362,8 +373,8 @@ public:
SpriteID sprite = GetCustomAirportSprite(as, layout);
if (sprite != 0) {
Dimension d = GetSpriteSize(sprite);
- d.width += WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT;
- d.height += WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM;
+ d.width += WidgetDimensions::scaled.framerect.Horizontal();
+ d.height += WidgetDimensions::scaled.framerect.Vertical();
*size = maxdim(d, *size);
}
}
@@ -394,17 +405,17 @@ public:
{
switch (widget) {
case WID_AP_AIRPORT_LIST: {
- int y = r.top;
+ Rect row = r.WithHeight(this->line_height).Shrink(WidgetDimensions::scaled.bevel);
+ Rect text = r.WithHeight(this->line_height).Shrink(WidgetDimensions::scaled.matrix);
AirportClass *apclass = AirportClass::Get(_selected_airport_class);
for (uint i = this->vscroll->GetPosition(); this->vscroll->IsVisible(i) && i < apclass->GetSpecCount(); i++) {
const AirportSpec *as = apclass->GetSpec(i);
if (!as->IsAvailable()) {
- GfxFillRect(r.left + 1, y + 1, r.right - 1, y + this->line_height - 2, PC_BLACK, FILLRECT_CHECKER);
+ GfxFillRect(row, PC_BLACK, FILLRECT_CHECKER);
}
-
- DrawString(r.left + WD_MATRIX_LEFT, r.right - WD_MATRIX_RIGHT, Center(y, this->line_height), as->name, ((int)i == _selected_airport_index) ? TC_WHITE : TC_BLACK);
-
- y += this->line_height;
+ DrawString(text, as->name, ((int)i == _selected_airport_index) ? TC_WHITE : TC_BLACK);
+ row = row.Translate(0, this->line_height);
+ text = text.Translate(0, this->line_height);
}
break;
}
@@ -412,7 +423,7 @@ public:
case WID_AP_AIRPORT_SPRITE:
if (this->preview_sprite != 0) {
Dimension d = GetSpriteSize(this->preview_sprite);
- DrawSprite(this->preview_sprite, COMPANY_SPRITE_COLOUR(_local_company), (r.left + r.right - d.width) / 2, (r.top + r.bottom - d.height) / 2);
+ DrawSprite(this->preview_sprite, COMPANY_SPRITE_COLOUR(_local_company), CenterBounds(r.left, r.right, d.width), CenterBounds(r.top, r.bottom, d.height));
}
break;
@@ -433,11 +444,8 @@ public:
{
this->DrawWidgets();
- uint16 top = this->GetWidget(WID_AP_BTN_DOHILIGHT)->pos_y + this->GetWidget(WID_AP_BTN_DOHILIGHT)->current_y + WD_PAR_VSEP_NORMAL;
- NWidgetBase *panel_nwi = this->GetWidget(WID_AP_BOTTOMPANEL);
-
- int right = panel_nwi->pos_x + panel_nwi->current_x;
- int bottom = panel_nwi->pos_y + panel_nwi->current_y;
+ Rect r = this->GetWidget(WID_AP_ACCEPTANCE)->GetCurrentRect();
+ int top = r.top + WidgetDimensions::scaled.vsep_normal;
if (_selected_airport_index != -1) {
const AirportSpec *as = AirportClass::Get(_selected_airport_class)->GetSpec(_selected_airport_index);
@@ -447,20 +455,27 @@ public:
if (_settings_game.economy.station_noise_level) {
/* show the noise of the selected airport */
SetDParam(0, as->noise_level);
- DrawString(panel_nwi->pos_x + WD_FRAMERECT_LEFT, right - WD_FRAMERECT_RIGHT, top, STR_STATION_BUILD_NOISE);
- top += FONT_HEIGHT_NORMAL + WD_PAR_VSEP_NORMAL;
+ DrawString(r.left, r.right, top, STR_STATION_BUILD_NOISE);
+ top += FONT_HEIGHT_NORMAL + WidgetDimensions::scaled.vsep_normal;
+ }
+
+ if (_settings_game.economy.infrastructure_maintenance) {
+ Money monthly = _price[PR_INFRASTRUCTURE_AIRPORT] * as->maintenance_cost >> 3;
+ SetDParam(0, monthly * 12);
+ DrawString(r.left, r.right, top, STR_STATION_BUILD_INFRASTRUCTURE_COST);
+ top += FONT_HEIGHT_NORMAL + WidgetDimensions::scaled.vsep_normal;
}
/* strings such as 'Size' and 'Coverage Area' */
- top = DrawStationCoverageAreaText(panel_nwi->pos_x + WD_FRAMERECT_LEFT, right - WD_FRAMERECT_RIGHT, top, SCT_ALL, rad, false) + WD_PAR_VSEP_NORMAL;
- top = DrawStationCoverageAreaText(panel_nwi->pos_x + WD_FRAMERECT_LEFT, right - WD_FRAMERECT_RIGHT, top, SCT_ALL, rad, true) + WD_PAR_VSEP_NORMAL;
+ top = DrawStationCoverageAreaText(r.left, r.right, top, SCT_ALL, rad, false) + WidgetDimensions::scaled.vsep_normal;
+ top = DrawStationCoverageAreaText(r.left, r.right, top, SCT_ALL, rad, true) + WidgetDimensions::scaled.vsep_normal;
}
/* Resize background if the window is too small.
* Never make the window smaller to avoid oscillating if the size change affects the acceptance.
* (This is the case, if making the window bigger moves the mouse into the window.) */
- if (top > bottom) {
- ResizeWindow(this, 0, top - bottom, false);
+ if (top > r.bottom) {
+ ResizeWindow(this, 0, top - r.bottom, false);
}
}
@@ -611,8 +626,8 @@ static const NWidgetPart _nested_build_airport_widgets[] = {
NWidget(WWT_EMPTY, COLOUR_DARK_GREEN, WID_AP_EXTRA_TEXT), SetFill(1, 0), SetMinimalSize(150, 0),
EndContainer(),
/* Bottom panel. */
- NWidget(WWT_PANEL, COLOUR_DARK_GREEN, WID_AP_BOTTOMPANEL), SetPIP(2, 2, 2),
- NWidget(WWT_LABEL, COLOUR_DARK_GREEN), SetDataTip(STR_STATION_BUILD_COVERAGE_AREA_TITLE, STR_NULL), SetFill(1, 0),
+ NWidget(WWT_PANEL, COLOUR_DARK_GREEN, WID_AP_BOTTOMPANEL),
+ NWidget(WWT_LABEL, COLOUR_DARK_GREEN), SetPadding(WidgetDimensions::unscaled.framerect), SetDataTip(STR_STATION_BUILD_COVERAGE_AREA_TITLE, STR_NULL), SetFill(1, 0),
NWidget(NWID_HORIZONTAL),
NWidget(NWID_SPACER), SetMinimalSize(14, 0), SetFill(1, 0),
NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
@@ -623,7 +638,7 @@ static const NWidgetPart _nested_build_airport_widgets[] = {
EndContainer(),
NWidget(NWID_SPACER), SetMinimalSize(14, 0), SetFill(1, 0),
EndContainer(),
- NWidget(NWID_SPACER), SetMinimalSize(0, 10), SetResize(0, 1), SetFill(1, 0),
+ NWidget(WWT_EMPTY, COLOUR_DARK_GREEN, WID_AP_ACCEPTANCE), SetPadding(WidgetDimensions::unscaled.framerect), SetResize(0, 1), SetFill(1, 0),
EndContainer(),
EndContainer(),
};
diff --git a/src/autoreplace_cmd.cpp b/src/autoreplace_cmd.cpp
index 4f9cf92bb4..654003e39e 100644
--- a/src/autoreplace_cmd.cpp
+++ b/src/autoreplace_cmd.cpp
@@ -22,6 +22,11 @@
#include "ai/ai.hpp"
#include "news_func.h"
#include "strings_func.h"
+#include "autoreplace_cmd.h"
+#include "group_cmd.h"
+#include "order_cmd.h"
+#include "train_cmd.h"
+#include "vehicle_cmd.h"
#include "table/strings.h"
@@ -206,7 +211,7 @@ static int GetIncompatibleRefitOrderIdForAutoreplace(const Vehicle *v, EngineID
const Order *o;
const Vehicle *u = (v->type == VEH_TRAIN) ? v->First() : v;
- const OrderList *orders = u->orders.list;
+ const OrderList *orders = u->orders;
if (orders == nullptr) return -1;
for (VehicleOrderID i = 0; i < orders->GetNumOrders(); i++) {
o = orders->GetOrderAt(i);
@@ -340,23 +345,24 @@ static CommandCost BuildReplacementVehicle(Vehicle *old_veh, Vehicle **new_vehic
}
/* Build the new vehicle */
- cost = DoCommand(old_veh->tile, e | (CT_INVALID << 24), 0, DC_EXEC | DC_AUTOREPLACE, GetCmdBuildVeh(old_veh));
+ VehicleID new_veh_id;
+ std::tie(cost, new_veh_id, std::ignore, std::ignore) = Command::Do(DC_EXEC | DC_AUTOREPLACE, old_veh->tile, e, true, CT_INVALID, INVALID_CLIENT_ID);
if (cost.Failed()) return cost;
- Vehicle *new_veh = Vehicle::Get(_new_vehicle_id);
+ Vehicle *new_veh = Vehicle::Get(new_veh_id);
*new_vehicle = new_veh;
/* Refit the vehicle if needed */
if (refit_cargo != CT_NO_REFIT) {
byte subtype = GetBestFittingSubType(old_veh, new_veh, refit_cargo);
- cost.AddCost(DoCommand(0, new_veh->index, refit_cargo | (subtype << 8), DC_EXEC, GetCmdRefitVeh(new_veh)));
+ cost.AddCost(std::get<0>(Command::Do(DC_EXEC, new_veh->index, refit_cargo, subtype, false, false, 0)));
assert(cost.Succeeded()); // This should be ensured by GetNewCargoTypeForReplace()
}
/* Try to reverse the vehicle, but do not care if it fails as the new type might not be reversible */
if (new_veh->type == VEH_TRAIN && HasBit(Train::From(old_veh)->flags, VRF_REVERSE_DIRECTION)) {
- DoCommand(0, new_veh->index, true, DC_EXEC, CMD_REVERSE_TRAIN_DIRECTION);
+ Command::Do(DC_EXEC, new_veh->index, true);
}
return cost;
@@ -368,9 +374,9 @@ static CommandCost BuildReplacementVehicle(Vehicle *old_veh, Vehicle **new_vehic
* @param evaluate_callback shall the start/stop callback be evaluated?
* @return success or error
*/
-static inline CommandCost CmdStartStopVehicle(const Vehicle *v, bool evaluate_callback)
+static inline CommandCost DoCmdStartStopVehicle(const Vehicle *v, bool evaluate_callback)
{
- return DoCommand(0, v->index, evaluate_callback ? 1 : 0, DC_EXEC | DC_AUTOREPLACE, CMD_START_STOP_VEHICLE);
+ return Command::Do(DC_EXEC | DC_AUTOREPLACE, v->index, evaluate_callback);
}
/**
@@ -383,7 +389,7 @@ static inline CommandCost CmdStartStopVehicle(const Vehicle *v, bool evaluate_ca
*/
static inline CommandCost CmdMoveVehicle(const Vehicle *v, const Vehicle *after, DoCommandFlag flags, bool whole_chain)
{
- return DoCommand(0, v->index | (whole_chain ? 1 : 0) << 20, after != nullptr ? after->index : INVALID_VEHICLE, flags | DC_NO_CARGO_CAP_CHECK, CMD_MOVE_RAIL_VEHICLE);
+ return Command::Do(flags | DC_NO_CARGO_CAP_CHECK, v->index, after != nullptr ? after->index : INVALID_VEHICLE, whole_chain);
}
/**
@@ -397,19 +403,19 @@ static CommandCost CopyHeadSpecificThings(Vehicle *old_head, Vehicle *new_head,
CommandCost cost = CommandCost();
/* Share orders */
- if (cost.Succeeded() && old_head != new_head) cost.AddCost(DoCommand(0, new_head->index | CO_SHARE << 30, old_head->index, DC_EXEC, CMD_CLONE_ORDER));
+ if (cost.Succeeded() && old_head != new_head) cost.AddCost(Command::Do(DC_EXEC, CO_SHARE, new_head->index, old_head->index));
/* Copy group membership */
- if (cost.Succeeded() && old_head != new_head) cost.AddCost(DoCommand(0, old_head->group_id, new_head->index, DC_EXEC, CMD_ADD_VEHICLE_GROUP));
+ if (cost.Succeeded() && old_head != new_head) cost.AddCost(std::get<0>(Command::Do(DC_EXEC, old_head->group_id, new_head->index, false)));
/* Perform start/stop check whether the new vehicle suits newgrf restrictions etc. */
if (cost.Succeeded()) {
/* Start the vehicle, might be denied by certain things */
assert((new_head->vehstatus & VS_STOPPED) != 0);
- cost.AddCost(CmdStartStopVehicle(new_head, true));
+ cost.AddCost(DoCmdStartStopVehicle(new_head, true));
/* Stop the vehicle again, but do not care about evil newgrfs allowing starting but not stopping :p */
- if (cost.Succeeded()) cost.AddCost(CmdStartStopVehicle(new_head, false));
+ if (cost.Succeeded()) cost.AddCost(DoCmdStartStopVehicle(new_head, false));
}
/* Last do those things which do never fail (resp. we do not care about), but which are not undo-able */
@@ -466,11 +472,11 @@ static CommandCost ReplaceFreeUnit(Vehicle **single_unit, DoCommandFlag flags, b
}
/* Sell the old vehicle */
- cost.AddCost(DoCommand(0, old_v->index, 0, flags, GetCmdSellVeh(old_v)));
+ cost.AddCost(Command::Do(flags, old_v->index, false, false, INVALID_CLIENT_ID));
/* If we are not in DC_EXEC undo everything */
if ((flags & DC_EXEC) == 0) {
- DoCommand(0, new_v->index, 0, DC_EXEC, GetCmdSellVeh(new_v));
+ Command::Do(DC_EXEC, new_v->index, false, false, INVALID_CLIENT_ID);
}
}
@@ -597,7 +603,7 @@ static CommandCost ReplaceChain(Vehicle **chain, DoCommandFlag flags, bool wagon
assert(RailVehInfo(wagon->engine_type)->railveh_type == RAILVEH_WAGON);
/* Sell wagon */
- [[maybe_unused]] CommandCost ret = DoCommand(0, wagon->index, 0, DC_EXEC, GetCmdSellVeh(wagon));
+ [[maybe_unused]] CommandCost ret = Command::Do(DC_EXEC, wagon->index, false, false, INVALID_CLIENT_ID);
assert(ret.Succeeded());
new_vehs[i] = nullptr;
@@ -629,7 +635,7 @@ static CommandCost ReplaceChain(Vehicle **chain, DoCommandFlag flags, bool wagon
/* Sell the vehicle.
* Note: This might temporarily construct new trains, so use DC_AUTOREPLACE to prevent
* it from failing due to engine limits. */
- cost.AddCost(DoCommand(0, w->index, 0, flags | DC_AUTOREPLACE, GetCmdSellVeh(w)));
+ cost.AddCost(Command::Do(flags | DC_AUTOREPLACE, w->index, false, false, INVALID_CLIENT_ID));
if ((flags & DC_EXEC) != 0) {
old_vehs[i] = nullptr;
if (i == 0) old_head = nullptr;
@@ -660,7 +666,7 @@ static CommandCost ReplaceChain(Vehicle **chain, DoCommandFlag flags, bool wagon
if ((flags & DC_EXEC) == 0) {
for (int i = num_units - 1; i >= 0; i--) {
if (new_vehs[i] != nullptr) {
- DoCommand(0, new_vehs[i]->index, 0, DC_EXEC, GetCmdSellVeh(new_vehs[i]));
+ Command::Do(DC_EXEC, new_vehs[i]->index, false, false, INVALID_CLIENT_ID);
new_vehs[i] = nullptr;
}
}
@@ -691,12 +697,12 @@ static CommandCost ReplaceChain(Vehicle **chain, DoCommandFlag flags, bool wagon
}
/* Sell the old vehicle */
- cost.AddCost(DoCommand(0, old_head->index, 0, flags, GetCmdSellVeh(old_head)));
+ cost.AddCost(Command::Do(flags, old_head->index, false, false, INVALID_CLIENT_ID));
}
/* If we are not in DC_EXEC undo everything */
if ((flags & DC_EXEC) == 0) {
- DoCommand(0, new_head->index, 0, DC_EXEC, GetCmdSellVeh(new_head));
+ Command::Do(DC_EXEC, new_head->index, false, false, INVALID_CLIENT_ID);
}
}
}
@@ -707,22 +713,18 @@ static CommandCost ReplaceChain(Vehicle **chain, DoCommandFlag flags, bool wagon
/**
* Autoreplaces a vehicle
* Trains are replaced as a whole chain, free wagons in depot are replaced on their own
- * @param tile not used
* @param flags type of operation
- * @param p1 Index of vehicle
- * @param p2 not used
- * @param text unused
+ * @param veh_id Index of vehicle
* @return the cost of this operation or an error
*/
-CommandCost CmdAutoreplaceVehicle(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const std::string &text)
+CommandCost CmdAutoreplaceVehicle(DoCommandFlag flags, VehicleID veh_id)
{
- Vehicle *v = Vehicle::GetIfValid(p1);
+ Vehicle *v = Vehicle::GetIfValid(veh_id);
if (v == nullptr) return CMD_ERROR;
CommandCost ret = CheckOwnership(v->owner);
if (ret.Failed()) return ret;
- if (!v->IsChainInDepot()) return CMD_ERROR;
if (v->vehstatus & VS_CRASHED) return CMD_ERROR;
bool free_wagon = false;
@@ -734,6 +736,7 @@ CommandCost CmdAutoreplaceVehicle(TileIndex tile, DoCommandFlag flags, uint32 p1
} else {
if (!v->IsPrimaryVehicle()) return CMD_ERROR;
}
+ if (!v->IsChainInDepot()) return CMD_ERROR;
const Company *c = Company::Get(_current_company);
bool wagon_removal = c->settings.renew_keep_length;
@@ -759,7 +762,7 @@ CommandCost CmdAutoreplaceVehicle(TileIndex tile, DoCommandFlag flags, uint32 p1
bool was_stopped = free_wagon || ((v->vehstatus & VS_STOPPED) != 0);
/* Stop the vehicle */
- if (!was_stopped) cost.AddCost(CmdStartStopVehicle(v, true));
+ if (!was_stopped) cost.AddCost(DoCmdStartStopVehicle(v, true));
if (cost.Failed()) return cost;
assert(free_wagon || v->IsStoppedInDepot());
@@ -787,7 +790,7 @@ CommandCost CmdAutoreplaceVehicle(TileIndex tile, DoCommandFlag flags, uint32 p1
}
/* Restart the vehicle */
- if (!was_stopped) cost.AddCost(CmdStartStopVehicle(v, false));
+ if (!was_stopped) cost.AddCost(DoCmdStartStopVehicle(v, false));
}
if (cost.Succeeded() && nothing_to_do) cost = CommandCost(STR_ERROR_AUTOREPLACE_NOTHING_TO_DO);
@@ -796,35 +799,29 @@ CommandCost CmdAutoreplaceVehicle(TileIndex tile, DoCommandFlag flags, uint32 p1
/**
* Change engine renewal parameters
- * @param tile unused
* @param flags operation to perform
- * @param p1 packed data
- * - bit 0 = replace when engine gets old?
- * - bits 16-31 = engine group
- * @param p2 packed data
- * - bits 0-15 = old engine type
- * - bits 16-31 = new engine type
- * @param text unused
+ * @param id_g engine group
+ * @param old_engine_type old engine type
+ * @param new_engine_type new engine type
+ * @param when_old replace when engine gets old?
* @return the cost of this operation or an error
*/
-CommandCost CmdSetAutoReplace(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const std::string &text)
+CommandCost CmdSetAutoReplace(DoCommandFlag flags, GroupID id_g, EngineID old_engine_type, EngineID new_engine_type, bool when_old)
{
Company *c = Company::GetIfValid(_current_company);
if (c == nullptr) return CMD_ERROR;
- EngineID old_engine_type = GB(p2, 0, 16);
- EngineID new_engine_type = GB(p2, 16, 16);
- GroupID id_g = GB(p1, 16, 16);
CommandCost cost;
if (Group::IsValidID(id_g) ? Group::Get(id_g)->owner != _current_company : !IsAllGroupID(id_g) && !IsDefaultGroupID(id_g)) return CMD_ERROR;
if (!Engine::IsValidID(old_engine_type)) return CMD_ERROR;
+ if (Group::IsValidID(id_g) && Group::Get(id_g)->vehicle_type != Engine::Get(old_engine_type)->type) return CMD_ERROR;
if (new_engine_type != INVALID_ENGINE) {
if (!Engine::IsValidID(new_engine_type)) return CMD_ERROR;
if (!CheckAutoreplaceValidity(old_engine_type, new_engine_type, _current_company)) return CMD_ERROR;
- cost = AddEngineReplacementForCompany(c, old_engine_type, new_engine_type, id_g, HasBit(p1, 0), flags);
+ cost = AddEngineReplacementForCompany(c, old_engine_type, new_engine_type, id_g, when_old, flags);
} else {
cost = RemoveEngineReplacementForCompany(c, old_engine_type, id_g, flags);
}
diff --git a/src/autoreplace_cmd.h b/src/autoreplace_cmd.h
new file mode 100644
index 0000000000..c42e740c93
--- /dev/null
+++ b/src/autoreplace_cmd.h
@@ -0,0 +1,24 @@
+/*
+ * This file is part of OpenTTD.
+ * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
+ * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see .
+ */
+
+/** @file autoreplace_cmd.h Command definitions related to autoreplace. */
+
+#ifndef AUTOREPLACE_CMD_H
+#define AUTOREPLACE_CMD_H
+
+#include "command_type.h"
+#include "vehicle_type.h"
+#include "engine_type.h"
+#include "group_type.h"
+
+CommandCost CmdAutoreplaceVehicle(DoCommandFlag flags, VehicleID veh_id);
+CommandCost CmdSetAutoReplace(DoCommandFlag flags, GroupID id_g, EngineID old_engine_type, EngineID new_engine_type, bool when_old);
+
+DEF_CMD_TRAIT(CMD_AUTOREPLACE_VEHICLE, CmdAutoreplaceVehicle, 0, CMDT_VEHICLE_MANAGEMENT)
+DEF_CMD_TRAIT(CMD_SET_AUTOREPLACE, CmdSetAutoReplace, 0, CMDT_VEHICLE_MANAGEMENT)
+
+#endif /* AUTOREPLACE_CMD_H */
diff --git a/src/autoreplace_gui.cpp b/src/autoreplace_gui.cpp
index baebc0172f..0b1b850a93 100644
--- a/src/autoreplace_gui.cpp
+++ b/src/autoreplace_gui.cpp
@@ -26,16 +26,19 @@
#include "rail_gui.h"
#include "road_gui.h"
#include "widgets/dropdown_func.h"
+#include "autoreplace_cmd.h"
+#include "group_cmd.h"
+#include "settings_cmd.h"
#include "widgets/autoreplace_widget.h"
#include "safeguards.h"
-void DrawEngineList(VehicleType type, int x, int r, int y, const GUIEngineList *eng_list, uint16 min, uint16 max, EngineID selected_id, bool show_count, GroupID selected_group);
+void DrawEngineList(VehicleType type, const Rect &r, const GUIEngineList &eng_list, uint16 min, uint16 max, EngineID selected_id, bool show_count, GroupID selected_group);
-static bool EngineNumberSorter(const EngineID &a, const EngineID &b)
+static bool EngineNumberSorter(const GUIEngineListItem &a, const GUIEngineListItem &b)
{
- return Engine::Get(a)->list_position < Engine::Get(b)->list_position;
+ return Engine::Get(a.engine_id)->list_position < Engine::Get(b.engine_id)->list_position;
}
/**
@@ -111,6 +114,26 @@ class ReplaceVehicleWindow : public Window {
return true;
}
+ void AddChildren(const GUIEngineList &source, GUIEngineList &target, EngineID parent, int indent, int side)
+ {
+ for (const auto &item : source) {
+ if (item.variant_id != parent || item.engine_id == parent) continue;
+
+ const Engine *e = Engine::Get(item.engine_id);
+ EngineDisplayFlags flags = item.flags;
+ if (e->display_last_variant != INVALID_ENGINE) flags &= ~EngineDisplayFlags::Shaded;
+ target.emplace_back(e->display_last_variant == INVALID_ENGINE ? item.engine_id : e->display_last_variant, item.engine_id, flags, indent);
+
+ /* Add variants if not folded */
+ if ((item.flags & (EngineDisplayFlags::HasVariants | EngineDisplayFlags::IsFolded)) == EngineDisplayFlags::HasVariants) {
+ /* Add this engine again as a child */
+ if ((item.flags & EngineDisplayFlags::Shaded) == EngineDisplayFlags::None) {
+ target.emplace_back(item.engine_id, item.engine_id, EngineDisplayFlags::None, indent + 1);
+ }
+ AddChildren(source, target, item.engine_id, indent + 1, side);
+ }
+ }
+ }
/**
* Generate an engines list
@@ -118,12 +141,12 @@ class ReplaceVehicleWindow : public Window {
*/
void GenerateReplaceVehList(bool draw_left)
{
+ std::vector variants;
EngineID selected_engine = INVALID_ENGINE;
VehicleType type = (VehicleType)this->window_number;
byte side = draw_left ? 0 : 1;
- GUIEngineList *list = &this->engines[side];
- list->clear();
+ GUIEngineList list;
for (const Engine *e : Engine::IterateType(type)) {
if (!draw_left && !this->show_hidden_engines && e->IsHidden(_local_company)) continue;
@@ -153,15 +176,37 @@ class ReplaceVehicleWindow : public Window {
if (!CheckAutoreplaceValidity(this->sel_engine[0], eid, _local_company)) continue;
}
- list->push_back(eid);
+ EngineDisplayFlags flags = (side == 0) ? EngineDisplayFlags::None : e->display_flags;
+ if (side == 1 && eid == this->sel_engine[0]) flags |= EngineDisplayFlags::Shaded;
+ list.emplace_back(eid, e->info.variant_id, flags, 0);
+
+ if (side == 1 && e->info.variant_id != INVALID_ENGINE) variants.push_back(e->info.variant_id);
if (eid == this->sel_engine[side]) selected_engine = eid; // The selected engine is still in the list
}
+
+ if (side == 1) {
+ /* ensure primary engine of variant group is in list */
+ for (const auto &variant : variants) {
+ if (std::find(list.begin(), list.end(), variant) == list.end()) {
+ const Engine *e = Engine::Get(variant);
+ list.emplace_back(variant, e->info.variant_id, e->display_flags | EngineDisplayFlags::Shaded, 0);
+ }
+ }
+ }
+
this->sel_engine[side] = selected_engine; // update which engine we selected (the same or none, if it's not in the list anymore)
if (draw_left) {
- EngList_Sort(list, &EngineNumberSorter);
+ EngList_Sort(&list, &EngineNumberSorter);
} else {
_engine_sort_direction = this->descending_sort_order;
- EngList_Sort(list, _engine_sort_functions[this->window_number][this->sort_criteria]);
+ EngList_Sort(&list, _engine_sort_functions[this->window_number][this->sort_criteria]);
+ }
+
+ this->engines[side].clear();
+ if (side == 1) {
+ AddChildren(list, this->engines[side], INVALID_ENGINE, 0, side);
+ } else {
+ this->engines[side].swap(list);
}
}
@@ -175,7 +220,7 @@ class ReplaceVehicleWindow : public Window {
this->GenerateReplaceVehList(true);
this->vscroll[0]->SetCount((uint)this->engines[0].size());
if (this->reset_sel_engine && this->sel_engine[0] == INVALID_ENGINE && this->engines[0].size() != 0) {
- this->sel_engine[0] = this->engines[0][0];
+ this->sel_engine[0] = this->engines[0][0].engine_id;
}
}
@@ -196,8 +241,8 @@ class ReplaceVehicleWindow : public Window {
this->vscroll[1]->SetCount((uint)this->engines[1].size());
if (this->reset_sel_engine && this->sel_engine[1] != INVALID_ENGINE) {
int position = 0;
- for (EngineID &eid : this->engines[1]) {
- if (eid == this->sel_engine[1]) break;
+ for (const auto &item : this->engines[1]) {
+ if (item.engine_id == this->sel_engine[1]) break;
++position;
}
this->vscroll[1]->ScrollTowards(position);
@@ -218,7 +263,7 @@ class ReplaceVehicleWindow : public Window {
{
EngineID veh_from = this->sel_engine[0];
EngineID veh_to = this->sel_engine[1];
- DoCommandP(0, (replace_when_old ? 1 : 0) | (this->sel_group << 16), veh_from + (veh_to << 16), CMD_SET_AUTOREPLACE);
+ Command::Post(this->sel_group, veh_from, veh_to, replace_when_old);
}
public:
@@ -302,8 +347,8 @@ public:
case WID_RV_INFO_TAB: {
Dimension d = GetStringBoundingBox(STR_REPLACE_NOT_REPLACING);
d = maxdim(d, GetStringBoundingBox(STR_REPLACE_NOT_REPLACING_VEHICLE_SELECTED));
- d.width += WD_FRAMETEXT_LEFT + WD_FRAMETEXT_RIGHT;
- d.height += WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM;
+ d.width += padding.width;
+ d.height += padding.height;
*size = maxdim(*size, d);
break;
}
@@ -422,7 +467,7 @@ public:
str = STR_REPLACE_NOT_REPLACING_VEHICLE_SELECTED;
}
- DrawString(r.left + WD_FRAMETEXT_LEFT, r.right - WD_FRAMETEXT_RIGHT, Center(r.top + WD_FRAMERECT_TOP, r.bottom - r.top - WD_FRAMERECT_TOP), str, TC_BLACK, SA_HOR_CENTER);
+ DrawString(r.Shrink(WidgetDimensions::scaled.frametext, WidgetDimensions::scaled.framerect), str, TC_BLACK, SA_HOR_CENTER);
break;
}
@@ -433,8 +478,7 @@ public:
EngineID end = static_cast(std::min(this->vscroll[side]->GetCapacity() + start, this->engines[side].size()));
/* Do the actual drawing */
- DrawEngineList((VehicleType)this->window_number, r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, r.top + WD_FRAMERECT_TOP,
- &this->engines[side], start, end, this->sel_engine[side], side == 0, this->sel_group);
+ DrawEngineList((VehicleType)this->window_number, r, this->engines[side], start, end, this->sel_engine[side], side == 0, this->sel_group);
break;
}
}
@@ -485,10 +529,10 @@ public:
ted.cargo = e->GetDefaultCargoType();
ted.capacity = e->GetDisplayDefaultCapacity(&ted.mail_capacity);
- NWidgetBase *nwi = this->GetWidget(side == 0 ? WID_RV_LEFT_DETAILS : WID_RV_RIGHT_DETAILS);
- int text_end = DrawVehiclePurchaseInfo(nwi->pos_x + WD_FRAMETEXT_LEFT, nwi->pos_x + nwi->current_x - WD_FRAMETEXT_RIGHT,
- nwi->pos_y + WD_FRAMERECT_TOP, this->sel_engine[side], ted);
- needed_height = std::max(needed_height, (text_end - (int)nwi->pos_y - WD_FRAMERECT_TOP) / FONT_HEIGHT_NORMAL);
+ const Rect r = this->GetWidget(side == 0 ? WID_RV_LEFT_DETAILS : WID_RV_RIGHT_DETAILS)->GetCurrentRect()
+ .Shrink(WidgetDimensions::scaled.frametext, WidgetDimensions::scaled.framerect);
+ int text_end = DrawVehiclePurchaseInfo(r.left, r.right, r.top, this->sel_engine[side], ted);
+ needed_height = std::max(needed_height, (text_end - r.top) / FONT_HEIGHT_NORMAL);
}
}
if (needed_height != this->details_height) { // Details window are not high enough, enlarge them.
@@ -544,10 +588,10 @@ public:
case WID_RV_TRAIN_WAGONREMOVE_TOGGLE: {
const Group *g = Group::GetIfValid(this->sel_group);
if (g != nullptr) {
- DoCommandP(0, this->sel_group | (GroupFlags::GF_REPLACE_WAGON_REMOVAL << 16), (HasBit(g->flags, GroupFlags::GF_REPLACE_WAGON_REMOVAL) ? 0 : 1) | (_ctrl_pressed << 1), CMD_SET_GROUP_FLAG);
+ Command::Post(this->sel_group, GroupFlags::GF_REPLACE_WAGON_REMOVAL, !HasBit(g->flags, GroupFlags::GF_REPLACE_WAGON_REMOVAL), _ctrl_pressed);
} else {
// toggle renew_keep_length
- DoCommandP(0, 0, Company::Get(_local_company)->settings.renew_keep_length ? 0 : 1, CMD_CHANGE_COMPANY_SETTING, nullptr, "company.renew_keep_length");
+ Command::Post("company.renew_keep_length", Company::Get(_local_company)->settings.renew_keep_length ? 0 : 1);
}
break;
}
@@ -565,7 +609,7 @@ public:
case WID_RV_STOP_REPLACE: { // Stop replacing
EngineID veh_from = this->sel_engine[0];
- DoCommandP(0, this->sel_group << 16, veh_from + (INVALID_ENGINE << 16), CMD_SET_AUTOREPLACE);
+ Command::Post(this->sel_group, veh_from, INVALID_ENGINE, false);
break;
}
@@ -580,7 +624,32 @@ public:
uint i = this->vscroll[click_side]->GetScrolledRowFromWidget(pt.y, this, widget);
size_t engine_count = this->engines[click_side].size();
- EngineID e = engine_count > i ? this->engines[click_side][i] : INVALID_ENGINE;
+ EngineID e = INVALID_ENGINE;
+ if (i < engine_count) {
+ const auto &item = this->engines[click_side][i];
+ const Rect r = this->GetWidget(widget)->GetCurrentRect().Shrink(WidgetDimensions::scaled.matrix).WithWidth(WidgetDimensions::scaled.hsep_indent * (item.indent + 1), _current_text_dir == TD_RTL);
+ if ((item.flags & EngineDisplayFlags::HasVariants) != EngineDisplayFlags::None && IsInsideMM(r.left, r.right, pt.x)) {
+ /* toggle folded flag on engine */
+ assert(item.variant_id != INVALID_ENGINE);
+ Engine *engine = Engine::Get(item.variant_id);
+ engine->display_flags ^= EngineDisplayFlags::IsFolded;
+
+ InvalidateWindowData(WC_REPLACE_VEHICLE, (VehicleType)this->window_number, 0); // Update the autoreplace window
+ InvalidateWindowClassesData(WC_BUILD_VEHICLE); // The build windows needs updating as well
+ return;
+ }
+ if ((item.flags & EngineDisplayFlags::Shaded) == EngineDisplayFlags::None) e = item.engine_id;
+ }
+
+ /* If Ctrl is pressed on the left side and we don't have any engines of the selected type, stop autoreplacing.
+ * This is most common when we have finished autoreplacing the engine and want to remove it from the list. */
+ if (click_side == 0 && _ctrl_pressed && e != INVALID_ENGINE &&
+ (GetGroupNumEngines(_local_company, sel_group, e) == 0 || GetGroupNumEngines(_local_company, ALL_GROUP, e) == 0)) {
+ EngineID veh_from = e;
+ Command::Post(this->sel_group, veh_from, INVALID_ENGINE, false);
+ break;
+ }
+
if (e == this->sel_engine[click_side]) break; // we clicked the one we already selected
this->sel_engine[click_side] = e;
if (click_side == 0) {
diff --git a/src/base_station_base.h b/src/base_station_base.h
index 40543f1b8f..2ad09ca21c 100644
--- a/src/base_station_base.h
+++ b/src/base_station_base.h
@@ -62,8 +62,7 @@ struct BaseStation : StationPool::PoolItem<&_station_pool> {
Owner owner; ///< The owner of this station
StationFacility facilities; ///< The facilities that this station has
- uint8 num_specs; ///< Number of specs in the speclist
- StationSpecList *speclist; ///< List of station specs of this station
+ std::vector speclist; ///< List of rail station specs of this station.
Date build_date; ///< Date of construction
diff --git a/src/bitmap_type.h b/src/bitmap_type.h
index 99c29bf181..75f9ab6642 100644
--- a/src/bitmap_type.h
+++ b/src/bitmap_type.h
@@ -58,8 +58,8 @@ public:
void Initialize(const Rect &r)
{
this->tile = TileXY(r.left, r.top);
- this->w = r.right - r.left + 1;
- this->h = r.bottom - r.top + 1;
+ this->w = r.Width();
+ this->h = r.Height();
this->data.clear();
this->data.resize(Index(w, h));
}
diff --git a/src/blitter/32bpp_anim_sse2.cpp b/src/blitter/32bpp_anim_sse2.cpp
index ee243cc21e..117e6ec5c2 100644
--- a/src/blitter/32bpp_anim_sse2.cpp
+++ b/src/blitter/32bpp_anim_sse2.cpp
@@ -19,6 +19,7 @@
/** Instantiation of the partially SSSE2 32bpp with animation blitter factory. */
static FBlitter_32bppSSE2_Anim iFBlitter_32bppSSE2_Anim;
+GNU_TARGET("sse2")
void Blitter_32bppSSE2_Anim::PaletteAnimate(const Palette &palette)
{
assert(!_screen_disable_anim);
diff --git a/src/blitter/32bpp_anim_sse2.hpp b/src/blitter/32bpp_anim_sse2.hpp
index 8b84f703e7..669cef80a7 100644
--- a/src/blitter/32bpp_anim_sse2.hpp
+++ b/src/blitter/32bpp_anim_sse2.hpp
@@ -16,6 +16,10 @@
#define SSE_VERSION 2
#endif
+#ifndef SSE_TARGET
+#define SSE_TARGET "sse2"
+#endif
+
#ifndef FULL_ANIMATION
#define FULL_ANIMATION 1
#endif
diff --git a/src/blitter/32bpp_anim_sse4.cpp b/src/blitter/32bpp_anim_sse4.cpp
index adfe625287..93c9cb20c6 100644
--- a/src/blitter/32bpp_anim_sse4.cpp
+++ b/src/blitter/32bpp_anim_sse4.cpp
@@ -29,6 +29,7 @@ static FBlitter_32bppSSE4_Anim iFBlitter_32bppSSE4_Anim;
*/
IGNORE_UNINITIALIZED_WARNING_START
template
+GNU_TARGET("sse4.1")
inline void Blitter_32bppSSE4_Anim::Draw(const Blitter::BlitterParams *bp, ZoomLevel zoom)
{
const byte * const remap = bp->remap;
@@ -52,6 +53,7 @@ inline void Blitter_32bppSSE4_Anim::Draw(const Blitter::BlitterParams *bp, ZoomL
const __m128i a_cm = ALPHA_CONTROL_MASK;
const __m128i pack_low_cm = PACK_LOW_CONTROL_MASK;
const __m128i tr_nom_base = TRANSPARENT_NOM_BASE;
+ const __m128i a_am = ALPHA_AND_MASK;
for (int y = bp->height; y != 0; y--) {
Colour *dst = dst_line;
@@ -143,7 +145,7 @@ inline void Blitter_32bppSSE4_Anim::Draw(const Blitter::BlitterParams *bp, ZoomL
/* Blend colours. */
bmno_alpha_blend:
- srcABCD = AlphaBlendTwoPixels(srcABCD, dstABCD, a_cm, pack_low_cm);
+ srcABCD = AlphaBlendTwoPixels(srcABCD, dstABCD, a_cm, pack_low_cm, a_am);
bmno_full_opacity:
_mm_storel_epi64((__m128i *) dst, srcABCD);
bmno_full_transparency:
@@ -170,7 +172,7 @@ bmno_full_transparency:
} else {
srcABCD = _mm_cvtsi32_si128(src->data);
}
- dst->data = _mm_cvtsi128_si32(AlphaBlendTwoPixels(srcABCD, dstABCD, a_cm, pack_low_cm));
+ dst->data = _mm_cvtsi128_si32(AlphaBlendTwoPixels(srcABCD, dstABCD, a_cm, pack_low_cm, a_am));
}
}
break;
@@ -254,7 +256,7 @@ bmno_full_transparency:
/* Blend colours. */
bmcr_alpha_blend:
- srcABCD = AlphaBlendTwoPixels(srcABCD, dstABCD, a_cm, pack_low_cm);
+ srcABCD = AlphaBlendTwoPixels(srcABCD, dstABCD, a_cm, pack_low_cm, a_am);
bmcr_full_opacity:
_mm_storel_epi64((__m128i *) dst, srcABCD);
bmcr_full_transparency:
@@ -287,7 +289,7 @@ bmcr_full_transparency:
if (src->a < 255) {
bmcr_alpha_blend_single:
__m128i dstABCD = _mm_cvtsi32_si128(dst->data);
- srcABCD = AlphaBlendTwoPixels(srcABCD, dstABCD, a_cm, pack_low_cm);
+ srcABCD = AlphaBlendTwoPixels(srcABCD, dstABCD, a_cm, pack_low_cm, a_am);
}
dst->data = _mm_cvtsi128_si32(srcABCD);
}
@@ -366,6 +368,12 @@ IGNORE_UNINITIALIZED_WARNING_STOP
*/
void Blitter_32bppSSE4_Anim::Draw(Blitter::BlitterParams *bp, BlitterMode mode, ZoomLevel zoom)
{
+ if (_screen_disable_anim) {
+ /* This means our output is not to the screen, so we can't be doing any animation stuff, so use our parent Draw() */
+ Blitter_32bppSSE4::Draw(bp, mode, zoom);
+ return;
+ }
+
const Blitter_32bppSSE_Base::SpriteFlags sprite_flags = ((const Blitter_32bppSSE_Base::SpriteData *) bp->sprite)->flags;
switch (mode) {
default: {
diff --git a/src/blitter/32bpp_anim_sse4.hpp b/src/blitter/32bpp_anim_sse4.hpp
index 7674182a8f..1b32e085c5 100644
--- a/src/blitter/32bpp_anim_sse4.hpp
+++ b/src/blitter/32bpp_anim_sse4.hpp
@@ -16,6 +16,10 @@
#define SSE_VERSION 4
#endif
+#ifndef SSE_TARGET
+#define SSE_TARGET "sse4.1"
+#endif
+
#ifndef FULL_ANIMATION
#define FULL_ANIMATION 1
#endif
@@ -28,7 +32,7 @@
#define MARGIN_NORMAL_THRESHOLD 4
/** The SSE4 32 bpp blitter with palette animation. */
-class Blitter_32bppSSE4_Anim FINAL : public Blitter_32bppSSE2_Anim, public Blitter_32bppSSE_Base {
+class Blitter_32bppSSE4_Anim FINAL : public Blitter_32bppSSE2_Anim, public Blitter_32bppSSE4 {
private:
public:
@@ -39,13 +43,14 @@ public:
return Blitter_32bppSSE_Base::Encode(sprite, allocator);
}
const char *GetName() override { return "32bpp-sse4-anim"; }
+ using Blitter_32bppSSE2_Anim::LookupColourInPalette;
};
/** Factory for the SSE4 32 bpp blitter (with palette animation). */
class FBlitter_32bppSSE4_Anim: public BlitterFactory {
public:
FBlitter_32bppSSE4_Anim() : BlitterFactory("32bpp-sse4-anim", "32bpp SSE4 Blitter (palette animation)", HasCPUIDFlag(1, 2, 19)) {}
- Blitter *CreateInstance() override { return new Blitter_32bppSSE4_Anim(); }
+ Blitter *CreateInstance() override { return static_cast(new Blitter_32bppSSE4_Anim()); }
};
#endif /* WITH_SSE */
diff --git a/src/blitter/32bpp_sse2.hpp b/src/blitter/32bpp_sse2.hpp
index 12105516f8..445fa97eb5 100644
--- a/src/blitter/32bpp_sse2.hpp
+++ b/src/blitter/32bpp_sse2.hpp
@@ -16,6 +16,10 @@
#define SSE_VERSION 2
#endif
+#ifndef SSE_TARGET
+#define SSE_TARGET "sse2"
+#endif
+
#ifndef FULL_ANIMATION
#define FULL_ANIMATION 0
#endif
diff --git a/src/blitter/32bpp_sse4.hpp b/src/blitter/32bpp_sse4.hpp
index 7d44926b88..deb4fbed92 100644
--- a/src/blitter/32bpp_sse4.hpp
+++ b/src/blitter/32bpp_sse4.hpp
@@ -16,6 +16,10 @@
#define SSE_VERSION 4
#endif
+#ifndef SSE_TARGET
+#define SSE_TARGET "sse4.1"
+#endif
+
#ifndef FULL_ANIMATION
#define FULL_ANIMATION 0
#endif
diff --git a/src/blitter/32bpp_sse_func.hpp b/src/blitter/32bpp_sse_func.hpp
index d6e12af619..e8d9c05061 100644
--- a/src/blitter/32bpp_sse_func.hpp
+++ b/src/blitter/32bpp_sse_func.hpp
@@ -12,6 +12,7 @@
#ifdef WITH_SSE
+GNU_TARGET(SSE_TARGET)
static inline void InsertFirstUint32(const uint32 value, __m128i &into)
{
#if (SSE_VERSION >= 4)
@@ -22,6 +23,7 @@ static inline void InsertFirstUint32(const uint32 value, __m128i &into)
#endif
}
+GNU_TARGET(SSE_TARGET)
static inline void InsertSecondUint32(const uint32 value, __m128i &into)
{
#if (SSE_VERSION >= 4)
@@ -32,6 +34,7 @@ static inline void InsertSecondUint32(const uint32 value, __m128i &into)
#endif
}
+GNU_TARGET(SSE_TARGET)
static inline void LoadUint64(const uint64 value, __m128i &into)
{
#ifdef POINTER_IS_64BIT
@@ -46,6 +49,7 @@ static inline void LoadUint64(const uint64 value, __m128i &into)
#endif
}
+GNU_TARGET(SSE_TARGET)
static inline __m128i PackUnsaturated(__m128i from, const __m128i &mask)
{
#if (SSE_VERSION == 2)
@@ -56,36 +60,43 @@ static inline __m128i PackUnsaturated(__m128i from, const __m128i &mask)
#endif
}
+GNU_TARGET(SSE_TARGET)
static inline __m128i DistributeAlpha(const __m128i from, const __m128i &mask)
{
#if (SSE_VERSION == 2)
__m128i alphaAB = _mm_shufflelo_epi16(from, 0x3F); // PSHUFLW, put alpha1 in front of each rgb1
- return _mm_shufflehi_epi16(alphaAB, 0x3F); // PSHUFHW, put alpha2 in front of each rgb2
+ alphaAB = _mm_shufflehi_epi16(alphaAB, 0x3F); // PSHUFHW, put alpha2 in front of each rgb2
+ return _mm_andnot_si128(mask, alphaAB); // PANDN, set alpha fields to 0
#else
return _mm_shuffle_epi8(from, mask);
#endif
}
-static inline __m128i AlphaBlendTwoPixels(__m128i src, __m128i dst, const __m128i &distribution_mask, const __m128i &pack_mask)
+GNU_TARGET(SSE_TARGET)
+static inline __m128i AlphaBlendTwoPixels(__m128i src, __m128i dst, const __m128i &distribution_mask, const __m128i &pack_mask, const __m128i &alpha_mask)
{
__m128i srcAB = _mm_unpacklo_epi8(src, _mm_setzero_si128()); // PUNPCKLBW, expand each uint8 into uint16
__m128i dstAB = _mm_unpacklo_epi8(dst, _mm_setzero_si128());
- __m128i alphaAB = _mm_cmpgt_epi16(srcAB, _mm_setzero_si128()); // PCMPGTW, if (alpha > 0) a++;
- alphaAB = _mm_srli_epi16(alphaAB, 15);
- alphaAB = _mm_add_epi16(alphaAB, srcAB);
+ __m128i alphaMaskAB = _mm_cmpgt_epi16(srcAB, _mm_setzero_si128()); // PCMPGTW (alpha > 0) ? 0xFFFF : 0
+ __m128i alphaAB = _mm_sub_epi16(srcAB, alphaMaskAB); // if (alpha > 0) a++;
alphaAB = DistributeAlpha(alphaAB, distribution_mask);
srcAB = _mm_sub_epi16(srcAB, dstAB); // PSUBW, (r - Cr)
srcAB = _mm_mullo_epi16(srcAB, alphaAB); // PMULLW, a*(r - Cr)
srcAB = _mm_srli_epi16(srcAB, 8); // PSRLW, a*(r - Cr)/256
srcAB = _mm_add_epi16(srcAB, dstAB); // PADDW, a*(r - Cr)/256 + Cr
+
+ alphaMaskAB = _mm_and_si128(alphaMaskAB, alpha_mask); // PAND, set non alpha fields to 0
+ srcAB = _mm_or_si128(srcAB, alphaMaskAB); // POR, set alpha fields to 0xFFFF is src alpha was > 0
+
return PackUnsaturated(srcAB, pack_mask);
}
/* Darken 2 pixels.
* rgb = rgb * ((256/4) * 4 - (alpha/4)) / ((256/4) * 4)
*/
+GNU_TARGET(SSE_TARGET)
static inline __m128i DarkenTwoPixels(__m128i src, __m128i dst, const __m128i &distribution_mask, const __m128i &tr_nom_base)
{
__m128i srcAB = _mm_unpacklo_epi8(src, _mm_setzero_si128());
@@ -99,6 +110,7 @@ static inline __m128i DarkenTwoPixels(__m128i src, __m128i dst, const __m128i &d
}
IGNORE_UNINITIALIZED_WARNING_START
+GNU_TARGET(SSE_TARGET)
static Colour ReallyAdjustBrightness(Colour colour, uint8 brightness)
{
uint64 c16 = colour.b | (uint64) colour.g << 16 | (uint64) colour.r << 32;
@@ -141,6 +153,7 @@ static inline Colour AdjustBrightneSSE(Colour colour, uint8 brightness)
return ReallyAdjustBrightness(colour, brightness);
}
+GNU_TARGET(SSE_TARGET)
static inline __m128i AdjustBrightnessOfTwoPixels(__m128i from, uint32 brightness)
{
#if (SSE_VERSION < 3)
@@ -192,6 +205,7 @@ static inline __m128i AdjustBrightnessOfTwoPixels(__m128i from, uint32 brightnes
*/
IGNORE_UNINITIALIZED_WARNING_START
template
+GNU_TARGET(SSE_TARGET)
#if (SSE_VERSION == 2)
inline void Blitter_32bppSSE2::Draw(const Blitter::BlitterParams *bp, ZoomLevel zoom)
#elif (SSE_VERSION == 3)
@@ -217,9 +231,11 @@ inline void Blitter_32bppSSE4::Draw(const Blitter::BlitterParams *bp, ZoomLevel
const MapValue *src_mv = src_mv_line;
/* Load these variables into register before loop. */
+ const __m128i alpha_and = ALPHA_AND_MASK;
+ #define ALPHA_BLEND_PARAM_3 alpha_and
#if (SSE_VERSION == 2)
const __m128i clear_hi = CLEAR_HIGH_BYTE_MASK;
- #define ALPHA_BLEND_PARAM_1 clear_hi
+ #define ALPHA_BLEND_PARAM_1 alpha_and
#define ALPHA_BLEND_PARAM_2 clear_hi
#define DARKEN_PARAM_1 tr_nom_base
#define DARKEN_PARAM_2 tr_nom_base
@@ -265,7 +281,7 @@ inline void Blitter_32bppSSE4::Draw(const Blitter::BlitterParams *bp, ZoomLevel
for (uint x = (uint) effective_width / 2; x > 0; x--) {
__m128i srcABCD = _mm_loadl_epi64((const __m128i*) src);
__m128i dstABCD = _mm_loadl_epi64((__m128i*) dst);
- _mm_storel_epi64((__m128i*) dst, AlphaBlendTwoPixels(srcABCD, dstABCD, ALPHA_BLEND_PARAM_1, ALPHA_BLEND_PARAM_2));
+ _mm_storel_epi64((__m128i*) dst, AlphaBlendTwoPixels(srcABCD, dstABCD, ALPHA_BLEND_PARAM_1, ALPHA_BLEND_PARAM_2, ALPHA_BLEND_PARAM_3));
src += 2;
dst += 2;
}
@@ -273,7 +289,7 @@ inline void Blitter_32bppSSE4::Draw(const Blitter::BlitterParams *bp, ZoomLevel
if ((bt_last == BT_NONE && effective_width & 1) || bt_last == BT_ODD) {
__m128i srcABCD = _mm_cvtsi32_si128(src->data);
__m128i dstABCD = _mm_cvtsi32_si128(dst->data);
- dst->data = _mm_cvtsi128_si32(AlphaBlendTwoPixels(srcABCD, dstABCD, ALPHA_BLEND_PARAM_1, ALPHA_BLEND_PARAM_2));
+ dst->data = _mm_cvtsi128_si32(AlphaBlendTwoPixels(srcABCD, dstABCD, ALPHA_BLEND_PARAM_1, ALPHA_BLEND_PARAM_2, ALPHA_BLEND_PARAM_3));
}
break;
@@ -318,7 +334,7 @@ inline void Blitter_32bppSSE4::Draw(const Blitter::BlitterParams *bp, ZoomLevel
}
/* Blend colours. */
- _mm_storel_epi64((__m128i *) dst, AlphaBlendTwoPixels(srcABCD, dstABCD, ALPHA_BLEND_PARAM_1, ALPHA_BLEND_PARAM_2));
+ _mm_storel_epi64((__m128i *) dst, AlphaBlendTwoPixels(srcABCD, dstABCD, ALPHA_BLEND_PARAM_1, ALPHA_BLEND_PARAM_2, ALPHA_BLEND_PARAM_3));
dst += 2;
src += 2;
src_mv += 2;
@@ -347,7 +363,7 @@ inline void Blitter_32bppSSE4::Draw(const Blitter::BlitterParams *bp, ZoomLevel
if (src->a < 255) {
bmcr_alpha_blend_single:
__m128i dstABCD = _mm_cvtsi32_si128(dst->data);
- srcABCD = AlphaBlendTwoPixels(srcABCD, dstABCD, ALPHA_BLEND_PARAM_1, ALPHA_BLEND_PARAM_2);
+ srcABCD = AlphaBlendTwoPixels(srcABCD, dstABCD, ALPHA_BLEND_PARAM_1, ALPHA_BLEND_PARAM_2, ALPHA_BLEND_PARAM_3);
}
dst->data = _mm_cvtsi128_si32(srcABCD);
}
diff --git a/src/blitter/32bpp_sse_type.h b/src/blitter/32bpp_sse_type.h
index 33c76d9b4d..fe91b294be 100644
--- a/src/blitter/32bpp_sse_type.h
+++ b/src/blitter/32bpp_sse_type.h
@@ -51,6 +51,7 @@ typedef union ALIGN(16) um128i {
#define OVERBRIGHT_VALUE_MASK _mm_setr_epi8(-1, 0, -1, 0, -1, 0, 0, 0, -1, 0, -1, 0, -1, 0, 0, 0)
#define OVERBRIGHT_CONTROL_MASK _mm_setr_epi8( 0, 1, 0, 1, 0, 1, 7, 7, 2, 3, 2, 3, 2, 3, 7, 7)
#define TRANSPARENT_NOM_BASE _mm_setr_epi16(256, 256, 256, 256, 256, 256, 256, 256)
+#define ALPHA_AND_MASK _mm_setr_epi16( 0, 0, 0, -1, 0, 0, 0, -1)
#endif /* WITH_SSE */
#endif /* BLITTER_32BPP_SSE_TYPE_H */
diff --git a/src/blitter/32bpp_ssse3.hpp b/src/blitter/32bpp_ssse3.hpp
index cc710ed05b..c95095d4df 100644
--- a/src/blitter/32bpp_ssse3.hpp
+++ b/src/blitter/32bpp_ssse3.hpp
@@ -16,6 +16,10 @@
#define SSE_VERSION 3
#endif
+#ifndef SSE_TARGET
+#define SSE_TARGET "ssse3"
+#endif
+
#ifndef FULL_ANIMATION
#define FULL_ANIMATION 0
#endif
diff --git a/src/blitter/40bpp_anim.cpp b/src/blitter/40bpp_anim.cpp
index d29b34a179..fe805a23e9 100644
--- a/src/blitter/40bpp_anim.cpp
+++ b/src/blitter/40bpp_anim.cpp
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
diff --git a/src/blitter/40bpp_anim.hpp b/src/blitter/40bpp_anim.hpp
index c0bea15ae0..6687809593 100644
--- a/src/blitter/40bpp_anim.hpp
+++ b/src/blitter/40bpp_anim.hpp
@@ -1,5 +1,3 @@
-/* $Id$ */
-
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
diff --git a/src/blitter/CMakeLists.txt b/src/blitter/CMakeLists.txt
index ddcc9a0082..50faefbc1c 100644
--- a/src/blitter/CMakeLists.txt
+++ b/src/blitter/CMakeLists.txt
@@ -38,21 +38,6 @@ add_files(
CONDITION NOT OPTION_DEDICATED AND OPENGL_FOUND
)
-
-if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang" OR CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
- set_compile_flags(
- 32bpp_anim_sse2.cpp
- 32bpp_sse2.cpp
- COMPILE_FLAGS -msse2)
- set_compile_flags(
- 32bpp_ssse3.cpp
- COMPILE_FLAGS -mssse3)
- set_compile_flags(
- 32bpp_anim_sse4.cpp
- 32bpp_sse4.cpp
- COMPILE_FLAGS -msse4.1)
-endif()
-
add_files(
base.hpp
common.hpp
diff --git a/src/bootstrap_gui.cpp b/src/bootstrap_gui.cpp
index d8e4aedec9..a1ae02acf6 100644
--- a/src/bootstrap_gui.cpp
+++ b/src/bootstrap_gui.cpp
@@ -101,14 +101,14 @@ public:
{
if (widget == WID_BEM_MESSAGE) {
*size = GetStringBoundingBox(STR_MISSING_GRAPHICS_ERROR);
- size->height = GetStringHeight(STR_MISSING_GRAPHICS_ERROR, size->width - WD_FRAMETEXT_LEFT - WD_FRAMETEXT_RIGHT) + WD_FRAMETEXT_BOTTOM + WD_FRAMETEXT_TOP;
+ size->height = GetStringHeight(STR_MISSING_GRAPHICS_ERROR, size->width - WidgetDimensions::scaled.frametext.Horizontal()) + WidgetDimensions::scaled.frametext.Vertical();
}
}
void DrawWidget(const Rect &r, int widget) const override
{
if (widget == WID_BEM_MESSAGE) {
- DrawStringMultiLine(r.left + WD_FRAMETEXT_LEFT, r.right - WD_FRAMETEXT_RIGHT, r.top + WD_FRAMETEXT_TOP, r.bottom - WD_FRAMETEXT_BOTTOM, STR_MISSING_GRAPHICS_ERROR, TC_FROMSTRING, SA_CENTER);
+ DrawStringMultiLine(r.Shrink(WidgetDimensions::scaled.frametext), STR_MISSING_GRAPHICS_ERROR, TC_FROMSTRING, SA_CENTER);
}
}
@@ -123,8 +123,11 @@ public:
/** Nested widgets for the download window. */
static const NWidgetPart _nested_bootstrap_download_status_window_widgets[] = {
NWidget(WWT_CAPTION, COLOUR_GREY), SetDataTip(STR_CONTENT_DOWNLOAD_TITLE, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
- NWidget(WWT_PANEL, COLOUR_GREY, WID_NCDS_BACKGROUND),
- NWidget(NWID_SPACER), SetMinimalSize(350, 0), SetMinimalTextLines(3, WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM + 30),
+ NWidget(WWT_PANEL, COLOUR_GREY),
+ NWidget(NWID_VERTICAL), SetPIP(0, WidgetDimensions::unscaled.vsep_wide, 0), SetPadding(WidgetDimensions::unscaled.modalpopup),
+ NWidget(WWT_EMPTY, INVALID_COLOUR, WID_NCDS_PROGRESS_BAR), SetFill(1, 0),
+ NWidget(WWT_EMPTY, INVALID_COLOUR, WID_NCDS_PROGRESS_TEXT), SetFill(1, 0), SetMinimalSize(350, 0),
+ EndContainer(),
EndContainer(),
};
@@ -214,15 +217,15 @@ public:
/* We cache the button size. This is safe as no reinit can happen here. */
if (this->button_size.width == 0) {
this->button_size = maxdim(GetStringBoundingBox(STR_MISSING_GRAPHICS_YES_DOWNLOAD), GetStringBoundingBox(STR_MISSING_GRAPHICS_NO_QUIT));
- this->button_size.width += WD_FRAMETEXT_LEFT + WD_FRAMETEXT_RIGHT;
- this->button_size.height += WD_FRAMETEXT_TOP + WD_FRAMETEXT_BOTTOM;
+ this->button_size.width += WidgetDimensions::scaled.frametext.Horizontal();
+ this->button_size.height += WidgetDimensions::scaled.frametext.Vertical();
}
switch (widget) {
case WID_BAFD_QUESTION:
/* The question is twice as wide as the buttons, and determine the height based on the width. */
size->width = this->button_size.width * 2;
- size->height = GetStringHeight(STR_MISSING_GRAPHICS_SET_MESSAGE, size->width - WD_FRAMETEXT_LEFT - WD_FRAMETEXT_RIGHT) + WD_FRAMETEXT_BOTTOM + WD_FRAMETEXT_TOP;
+ size->height = GetStringHeight(STR_MISSING_GRAPHICS_SET_MESSAGE, size->width - WidgetDimensions::scaled.frametext.Horizontal()) + WidgetDimensions::scaled.frametext.Vertical();
break;
case WID_BAFD_YES:
@@ -236,7 +239,7 @@ public:
{
if (widget != 0) return;
- DrawStringMultiLine(r.left + WD_FRAMETEXT_LEFT, r.right - WD_FRAMETEXT_RIGHT, r.top + WD_FRAMETEXT_TOP, r.bottom - WD_FRAMETEXT_BOTTOM, STR_MISSING_GRAPHICS_SET_MESSAGE, TC_FROMSTRING, SA_CENTER);
+ DrawStringMultiLine(r.Shrink(WidgetDimensions::scaled.frametext), STR_MISSING_GRAPHICS_SET_MESSAGE, TC_FROMSTRING, SA_CENTER);
}
void OnClick(Point pt, int widget, int click_count) override
@@ -286,16 +289,16 @@ bool HandleBootstrap()
/* No user interface, bail out with an error. */
if (BlitterFactory::GetCurrentBlitter()->GetScreenDepth() == 0) goto failure;
- /* If there is no network or no freetype, then there is nothing we can do. Go straight to failure. */
+ /* If there is no network or no non-sprite font, then there is nothing we can do. Go straight to failure. */
#if (defined(_WIN32) && defined(WITH_UNISCRIBE)) || (defined(WITH_FREETYPE) && (defined(WITH_FONTCONFIG) || defined(__APPLE__))) || defined(WITH_COCOA)
if (!_network_available) goto failure;
/* First tell the game we're bootstrapping. */
_game_mode = GM_BOOTSTRAP;
- /* Initialise the freetype font code. */
+ /* Initialise the font cache. */
InitializeUnicodeGlyphMap();
- /* Next "force" finding a suitable freetype font as the local font is missing. */
+ /* Next "force" finding a suitable non-sprite font as the local font is missing. */
CheckForMissingGlyphs(false);
/* Initialise the palette. The biggest step is 'faking' some recolour sprites.
diff --git a/src/bridge_gui.cpp b/src/bridge_gui.cpp
index ed3fd9bc8c..ccb98bdc70 100644
--- a/src/bridge_gui.cpp
+++ b/src/bridge_gui.cpp
@@ -20,10 +20,10 @@
#include "sortlist_type.h"
#include "widgets/dropdown_func.h"
#include "core/geometry_func.hpp"
-#include "cmd_helper.h"
#include "tunnelbridge_map.h"
#include "road_gui.h"
#include "tilehighlight_func.h"
+#include "tunnelbridge_cmd.h"
#include "widgets/bridge_widget.h"
@@ -51,27 +51,22 @@ typedef GUIList GUIBridgeList; ///< List of bridges, used in #B
* Callback executed after a build Bridge CMD has been called
*
* @param result Whether the build succeeded
- * @param end_tile End tile of the bridge.
- * @param p1 packed start tile coords (~ dx)
- * @param p2 various bitstuffed elements
- * - p2 = (bit 0- 7) - bridge type (hi bh)
- * - p2 = (bit 8-13) - rail type or road types.
- * - p2 = (bit 15-16) - transport type.
* @param cmd unused
+ * @param end_tile End tile of the bridge.
+ * @param tile_start start tile
+ * @param transport_type transport type.
*/
-void CcBuildBridge(const CommandCost &result, TileIndex end_tile, uint32 p1, uint32 p2, uint32 cmd)
+void CcBuildBridge(Commands cmd, const CommandCost &result, TileIndex end_tile, TileIndex tile_start, TransportType transport_type, BridgeType, byte)
{
if (result.Failed()) return;
if (_settings_client.sound.confirm) SndPlayTileFx(SND_27_CONSTRUCTION_BRIDGE, end_tile);
- TransportType transport_type = Extract(p2);
-
if (transport_type == TRANSPORT_ROAD) {
DiagDirection end_direction = ReverseDiagDir(GetTunnelBridgeDirection(end_tile));
ConnectRoadToStructure(end_tile, end_direction);
- DiagDirection start_direction = ReverseDiagDir(GetTunnelBridgeDirection(p1));
- ConnectRoadToStructure(p1, start_direction);
+ DiagDirection start_direction = ReverseDiagDir(GetTunnelBridgeDirection(tile_start));
+ ConnectRoadToStructure(tile_start, start_direction);
}
}
@@ -88,7 +83,8 @@ private:
/* Internal variables */
TileIndex start_tile;
TileIndex end_tile;
- uint32 type;
+ TransportType transport_type;
+ byte road_rail_type;
GUIBridgeList *bridges;
int bridgetext_offset; ///< Horizontal offset of the text describing the bridge properties in #WID_BBS_BRIDGE_LIST relative to the left edge.
Scrollbar *vscroll;
@@ -113,13 +109,13 @@ private:
void BuildBridge(uint8 i)
{
- switch ((TransportType)(this->type >> 15)) {
+ switch (this->transport_type) {
case TRANSPORT_RAIL: _last_railbridge_type = this->bridges->at(i).index; break;
case TRANSPORT_ROAD: _last_roadbridge_type = this->bridges->at(i).index; break;
default: break;
}
- DoCommandP(this->end_tile, this->start_tile, this->type | this->bridges->at(i).index,
- CMD_BUILD_BRIDGE | CMD_MSG(STR_ERROR_CAN_T_BUILD_BRIDGE_HERE), CcBuildBridge);
+ Command::Post(STR_ERROR_CAN_T_BUILD_BRIDGE_HERE, CcBuildBridge,
+ this->end_tile, this->start_tile, this->transport_type, this->bridges->at(i).index, this->road_rail_type);
}
/** Sort the builable bridges */
@@ -136,19 +132,20 @@ private:
}
public:
- BuildBridgeWindow(WindowDesc *desc, TileIndex start, TileIndex end, uint32 br_type, GUIBridgeList *bl) : Window(desc),
+ BuildBridgeWindow(WindowDesc *desc, TileIndex start, TileIndex end, TransportType transport_type, byte road_rail_type, GUIBridgeList *bl) : Window(desc),
start_tile(start),
end_tile(end),
- type(br_type),
+ transport_type(transport_type),
+ road_rail_type(road_rail_type),
bridges(bl)
{
this->CreateNestedTree();
this->vscroll = this->GetScrollbar(WID_BBS_SCROLLBAR);
/* Change the data, or the caption of the gui. Set it to road or rail, accordingly. */
- this->GetWidget(WID_BBS_CAPTION)->widget_data = (GB(this->type, 15, 2) == TRANSPORT_ROAD) ? STR_SELECT_ROAD_BRIDGE_CAPTION : STR_SELECT_RAIL_BRIDGE_CAPTION;
- this->FinishInitNested(GB(br_type, 15, 2)); // Initializes 'this->bridgetext_offset'.
+ this->GetWidget(WID_BBS_CAPTION)->widget_data = (transport_type == TRANSPORT_ROAD) ? STR_SELECT_ROAD_BRIDGE_CAPTION : STR_SELECT_RAIL_BRIDGE_CAPTION;
+ this->FinishInitNested(transport_type); // Initializes 'this->bridgetext_offset'.
- this->parent = FindWindowById(WC_BUILD_TOOLBAR, GB(this->type, 15, 2));
+ this->parent = FindWindowById(WC_BUILD_TOOLBAR, transport_type);
this->bridges->SetListing(this->last_sorting);
this->bridges->SetSortFuncs(this->sorter_funcs);
this->bridges->NeedResort();
@@ -198,11 +195,11 @@ public:
}
sprite_dim.height++; // Sprite is rendered one pixel down in the matrix field.
text_dim.height++; // Allowing the bottom row pixels to be rendered on the edge of the matrix field.
- resize->height = std::max(sprite_dim.height, text_dim.height) + 2; // Max of both sizes + account for matrix edges.
+ resize->height = std::max(sprite_dim.height, text_dim.height) + padding.height; // Max of both sizes + account for matrix edges.
resize->height = GetMinButtonSize(resize->height);
- this->bridgetext_offset = WD_MATRIX_LEFT + sprite_dim.width + 1; // Left edge of text, 1 pixel distance from the sprite.
- size->width = this->bridgetext_offset + text_dim.width + WD_MATRIX_RIGHT;
+ this->bridgetext_offset = sprite_dim.width + WidgetDimensions::scaled.hsep_normal; // Left edge of text, 1 pixel distance from the sprite.
+ size->width = this->bridgetext_offset + text_dim.width + padding.width;
size->height = 4 * resize->height; // Smallest bridge gui is 4 entries high in the matrix.
break;
}
@@ -227,7 +224,7 @@ public:
break;
case WID_BBS_BRIDGE_LIST: {
- uint y = r.top;
+ Rect tr = r.WithHeight(this->resize.step_height).Shrink(WidgetDimensions::scaled.matrix);
for (int i = this->vscroll->GetPosition(); this->vscroll->IsVisible(i) && i < (int)this->bridges->size(); i++) {
const BridgeSpec *b = this->bridges->at(i).spec;
@@ -235,11 +232,10 @@ public:
SetDParam(1, b->speed);
SetDParam(0, b->material);
- uint y_sprite = Center(y, this->resize.step_height, GetSpriteSize(b->sprite).height);
- DrawSprite(b->sprite, b->pal, r.left + WD_MATRIX_LEFT, y_sprite);
- DrawStringMultiLine(r.left + this->bridgetext_offset, r.right, y + 2, y + this->resize.step_height,
+ DrawSprite(b->sprite, b->pal, tr.left, tr.bottom - GetSpriteSize(b->sprite).height);
+ DrawStringMultiLine(tr.Indent(this->bridgetext_offset, false),
_game_mode == GM_EDITOR ? STR_SELECT_BRIDGE_SCENEDIT_INFO : STR_SELECT_BRIDGE_INFO);
- y += this->resize.step_height;
+ tr = tr.Translate(0, this->resize.step_height);
}
break;
}
@@ -365,12 +361,6 @@ void ShowBuildBridgeWindow(TileIndex start, TileIndex end, TransportType transpo
{
CloseWindowByClass(WC_BUILD_BRIDGE);
- /* Data type for the bridge.
- * Bit 16,15 = transport type,
- * 14..8 = road/rail types,
- * 7..0 = type of bridge */
- uint32 type = (transport_type << 15) | (road_rail_type << 8);
-
/* The bridge length without ramps. */
const uint bridge_len = GetTunnelBridgeLength(start, end);
@@ -386,14 +376,14 @@ void ShowBuildBridgeWindow(TileIndex start, TileIndex end, TransportType transpo
default: break; // water ways and air routes don't have bridge types
}
if (_ctrl_pressed && CheckBridgeAvailability(last_bridge_type, bridge_len).Succeeded()) {
- DoCommandP(end, start, type | last_bridge_type, CMD_BUILD_BRIDGE | CMD_MSG(STR_ERROR_CAN_T_BUILD_BRIDGE_HERE), CcBuildBridge);
+ Command::Post(STR_ERROR_CAN_T_BUILD_BRIDGE_HERE, CcBuildBridge, end, start, transport_type, last_bridge_type, road_rail_type);
return;
}
/* only query bridge building possibility once, result is the same for all bridges!
* returns CMD_ERROR on failure, and price on success */
StringID errmsg = INVALID_STRING_ID;
- CommandCost ret = DoCommand(end, start, type, CommandFlagsToDCFlags(GetCommandFlags(CMD_BUILD_BRIDGE)) | DC_QUERY_COST, CMD_BUILD_BRIDGE);
+ CommandCost ret = Command::Do(CommandFlagsToDCFlags(GetCommandFlags()) | DC_QUERY_COST, end, start, transport_type, 0, road_rail_type);
GUIBridgeList *bl = nullptr;
if (ret.Failed()) {
@@ -453,7 +443,7 @@ void ShowBuildBridgeWindow(TileIndex start, TileIndex end, TransportType transpo
}
if (bl != nullptr && bl->size() != 0) {
- new BuildBridgeWindow(&_build_bridge_desc, start, end, type, bl);
+ new BuildBridgeWindow(&_build_bridge_desc, start, end, transport_type, road_rail_type, bl);
} else {
delete bl;
SetSelectionTilesDirty();
diff --git a/src/build_confirmation_gui.cpp b/src/build_confirmation_gui.cpp
index 72bc2b19be..66da95437c 100644
--- a/src/build_confirmation_gui.cpp
+++ b/src/build_confirmation_gui.cpp
@@ -82,16 +82,16 @@ struct BuildInfoWindow : public Window
size->height = GetStringHeight(STR_STATION_BUILD_COVERAGE_AREA_TITLE, size->width) * (this->station ? 3 : 1);
/* Increase slightly to have some space around the box. */
- size->width += 2 + WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT;
- size->height += 6 + WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM;
+ size->width += 2 + WidgetDimensions::scaled.framerect.Horizontal();
+ size->height += 6 + WidgetDimensions::scaled.framerect.Vertical();
}
void DrawWidget(const Rect &r, int widget) const override
{
/* There is only one widget. */
DrawFrameRect(r.left, r.top, r.right, r.bottom, COLOUR_GREY, FR_NONE);
+ Rect tr = r.Shrink(WidgetDimensions::scaled.frametext);
- int top = r.top + WD_FRAMERECT_TOP + 4;
Money cost = BuildInfoWindow::cost;
StringID msg = STR_MESSAGE_ESTIMATED_COST;
SetDParam(0, cost);
@@ -99,13 +99,13 @@ struct BuildInfoWindow : public Window
msg = STR_MESSAGE_ESTIMATED_INCOME;
SetDParam(0, -cost);
}
- top = DrawStringMultiLine(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, top, INT32_MAX, msg);
+ tr.top = DrawStringMultiLine(tr, msg);
if (!this->station) return;
- top = DrawStationCoverageAreaText(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, top, sct, _thd.outersize.x / TILE_SIZE / 2, false);
+ tr.top = DrawStationCoverageAreaText(tr.left, tr.right, tr.top, sct, _thd.outersize.x / TILE_SIZE / 2, false);
if (top - r.top <= GetStringHeight(STR_STATION_BUILD_COVERAGE_AREA_TITLE, r.right - r.left) * 1.5) {
- DrawStationCoverageAreaText(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, top, sct, _thd.outersize.x / TILE_SIZE / 2, true);
+ DrawStationCoverageAreaText(tr.left, tr.right, tr.top, sct, _thd.outersize.x / TILE_SIZE / 2, true);
}
}
};
diff --git a/src/build_vehicle_gui.cpp b/src/build_vehicle_gui.cpp
index fe75858ff7..8064d16c1b 100644
--- a/src/build_vehicle_gui.cpp
+++ b/src/build_vehicle_gui.cpp
@@ -30,6 +30,10 @@
#include "cargotype.h"
#include "core/geometry_func.hpp"
#include "autoreplace_func.h"
+#include "engine_cmd.h"
+#include "train_cmd.h"
+#include "vehicle_cmd.h"
+#include "zoom_func.h"
#include "widgets/build_vehicle_widget.h"
@@ -44,8 +48,7 @@
*/
uint GetEngineListHeight(VehicleType type)
{
- uint size = std::max(FONT_HEIGHT_NORMAL + WD_MATRIX_TOP + WD_MATRIX_BOTTOM, GetVehicleImageCellSize(type, EIT_PURCHASE).height);
- return GetMinButtonSize(size);
+ return std::max(FONT_HEIGHT_NORMAL + WidgetDimensions::scaled.matrix.Vertical(), GetVehicleImageCellSize(type, EIT_PURCHASE).height);
}
static const NWidgetPart _nested_build_vehicle_widgets[] = {
@@ -108,9 +111,9 @@ static CargoID _engine_sort_last_cargo_criteria[] = {CF_ANY, CF_ANY, CF_ANY, CF_
* @param b second engine to compare
* @return for descending order: returns true if a < b. Vice versa for ascending order
*/
-static bool EngineNumberSorter(const EngineID &a, const EngineID &b)
+static bool EngineNumberSorter(const GUIEngineListItem &a, const GUIEngineListItem &b)
{
- int r = Engine::Get(a)->list_position - Engine::Get(b)->list_position;
+ int r = Engine::Get(a.engine_id)->list_position - Engine::Get(b.engine_id)->list_position;
return _engine_sort_direction ? r > 0 : r < 0;
}
@@ -121,10 +124,10 @@ static bool EngineNumberSorter(const EngineID &a, const EngineID &b)
* @param b second engine to compare
* @return for descending order: returns true if a < b. Vice versa for ascending order
*/
-static bool EngineIntroDateSorter(const EngineID &a, const EngineID &b)
+static bool EngineIntroDateSorter(const GUIEngineListItem &a, const GUIEngineListItem &b)
{
- const int va = Engine::Get(a)->intro_date;
- const int vb = Engine::Get(b)->intro_date;
+ const int va = Engine::Get(a.engine_id)->intro_date;
+ const int vb = Engine::Get(b.engine_id)->intro_date;
const int r = va - vb;
/* Use EngineID to sort instead since we want consistent sorting */
@@ -141,19 +144,19 @@ static EngineID _last_engine[2] = { INVALID_ENGINE, INVALID_ENGINE };
* @param b second engine to compare
* @return for descending order: returns true if a < b. Vice versa for ascending order
*/
-static bool EngineNameSorter(const EngineID &a, const EngineID &b)
+static bool EngineNameSorter(const GUIEngineListItem &a, const GUIEngineListItem &b)
{
static char last_name[2][64] = { "", "" };
- if (a != _last_engine[0]) {
- _last_engine[0] = a;
- SetDParam(0, a);
+ if (a.engine_id != _last_engine[0]) {
+ _last_engine[0] = a.engine_id;
+ SetDParam(0, a.engine_id);
GetString(last_name[0], STR_ENGINE_NAME, lastof(last_name[0]));
}
- if (b != _last_engine[1]) {
- _last_engine[1] = b;
- SetDParam(0, b);
+ if (b.engine_id != _last_engine[1]) {
+ _last_engine[1] = b.engine_id;
+ SetDParam(0, b.engine_id);
GetString(last_name[1], STR_ENGINE_NAME, lastof(last_name[1]));
}
@@ -170,10 +173,10 @@ static bool EngineNameSorter(const EngineID &a, const EngineID &b)
* @param b second engine to compare
* @return for descending order: returns true if a < b. Vice versa for ascending order
*/
-static bool EngineReliabilitySorter(const EngineID &a, const EngineID &b)
+static bool EngineReliabilitySorter(const GUIEngineListItem &a, const GUIEngineListItem &b)
{
- const int va = Engine::Get(a)->reliability;
- const int vb = Engine::Get(b)->reliability;
+ const int va = Engine::Get(a.engine_id)->reliability;
+ const int vb = Engine::Get(b.engine_id)->reliability;
const int r = va - vb;
/* Use EngineID to sort instead since we want consistent sorting */
@@ -187,10 +190,10 @@ static bool EngineReliabilitySorter(const EngineID &a, const EngineID &b)
* @param b second engine to compare
* @return for descending order: returns true if a < b. Vice versa for ascending order
*/
-static bool EngineCostSorter(const EngineID &a, const EngineID &b)
+static bool EngineCostSorter(const GUIEngineListItem &a, const GUIEngineListItem &b)
{
- Money va = Engine::Get(a)->GetCost();
- Money vb = Engine::Get(b)->GetCost();
+ Money va = Engine::Get(a.engine_id)->GetCost();
+ Money vb = Engine::Get(b.engine_id)->GetCost();
int r = ClampToI32(va - vb);
/* Use EngineID to sort instead since we want consistent sorting */
@@ -204,10 +207,10 @@ static bool EngineCostSorter(const EngineID &a, const EngineID &b)
* @param b second engine to compare
* @return for descending order: returns true if a < b. Vice versa for ascending order
*/
-static bool EngineSpeedSorter(const EngineID &a, const EngineID &b)
+static bool EngineSpeedSorter(const GUIEngineListItem &a, const GUIEngineListItem &b)
{
- int va = Engine::Get(a)->GetDisplayMaxSpeed();
- int vb = Engine::Get(b)->GetDisplayMaxSpeed();
+ int va = Engine::Get(a.engine_id)->GetDisplayMaxSpeed();
+ int vb = Engine::Get(b.engine_id)->GetDisplayMaxSpeed();
int r = va - vb;
/* Use EngineID to sort instead since we want consistent sorting */
@@ -221,10 +224,10 @@ static bool EngineSpeedSorter(const EngineID &a, const EngineID &b)
* @param b second engine to compare
* @return for descending order: returns true if a < b. Vice versa for ascending order
*/
-static bool EnginePowerSorter(const EngineID &a, const EngineID &b)
+static bool EnginePowerSorter(const GUIEngineListItem &a, const GUIEngineListItem &b)
{
- int va = Engine::Get(a)->GetPower();
- int vb = Engine::Get(b)->GetPower();
+ int va = Engine::Get(a.engine_id)->GetPower();
+ int vb = Engine::Get(b.engine_id)->GetPower();
int r = va - vb;
/* Use EngineID to sort instead since we want consistent sorting */
@@ -238,10 +241,10 @@ static bool EnginePowerSorter(const EngineID &a, const EngineID &b)
* @param b second engine to compare
* @return for descending order: returns true if a < b. Vice versa for ascending order
*/
-static bool EngineTractiveEffortSorter(const EngineID &a, const EngineID &b)
+static bool EngineTractiveEffortSorter(const GUIEngineListItem &a, const GUIEngineListItem &b)
{
- int va = Engine::Get(a)->GetDisplayMaxTractiveEffort();
- int vb = Engine::Get(b)->GetDisplayMaxTractiveEffort();
+ int va = Engine::Get(a.engine_id)->GetDisplayMaxTractiveEffort();
+ int vb = Engine::Get(b.engine_id)->GetDisplayMaxTractiveEffort();
int r = va - vb;
/* Use EngineID to sort instead since we want consistent sorting */
@@ -255,10 +258,10 @@ static bool EngineTractiveEffortSorter(const EngineID &a, const EngineID &b)
* @param b second engine to compare
* @return for descending order: returns true if a < b. Vice versa for ascending order
*/
-static bool EngineRunningCostSorter(const EngineID &a, const EngineID &b)
+static bool EngineRunningCostSorter(const GUIEngineListItem &a, const GUIEngineListItem &b)
{
- Money va = Engine::Get(a)->GetRunningCost();
- Money vb = Engine::Get(b)->GetRunningCost();
+ Money va = Engine::Get(a.engine_id)->GetRunningCost();
+ Money vb = Engine::Get(b.engine_id)->GetRunningCost();
int r = ClampToI32(va - vb);
/* Use EngineID to sort instead since we want consistent sorting */
@@ -272,10 +275,10 @@ static bool EngineRunningCostSorter(const EngineID &a, const EngineID &b)
* @param b second engine to compare
* @return for descending order: returns true if a < b. Vice versa for ascending order
*/
-static bool EnginePowerVsRunningCostSorter(const EngineID &a, const EngineID &b)
+static bool EnginePowerVsRunningCostSorter(const GUIEngineListItem &a, const GUIEngineListItem &b)
{
- const Engine *e_a = Engine::Get(a);
- const Engine *e_b = Engine::Get(b);
+ const Engine *e_a = Engine::Get(a.engine_id);
+ const Engine *e_b = Engine::Get(b.engine_id);
uint p_a = e_a->GetPower();
uint p_b = e_b->GetPower();
Money r_a = e_a->GetRunningCost();
@@ -314,13 +317,13 @@ static bool EnginePowerVsRunningCostSorter(const EngineID &a, const EngineID &b)
* @param b second engine to compare
* @return for descending order: returns true if a < b. Vice versa for ascending order
*/
-static bool TrainEngineCapacitySorter(const EngineID &a, const EngineID &b)
+static bool TrainEngineCapacitySorter(const GUIEngineListItem &a, const GUIEngineListItem &b)
{
- const RailVehicleInfo *rvi_a = RailVehInfo(a);
- const RailVehicleInfo *rvi_b = RailVehInfo(b);
+ const RailVehicleInfo *rvi_a = RailVehInfo(a.engine_id);
+ const RailVehicleInfo *rvi_b = RailVehInfo(b.engine_id);
- int va = GetTotalCapacityOfArticulatedParts(a) * (rvi_a->railveh_type == RAILVEH_MULTIHEAD ? 2 : 1);
- int vb = GetTotalCapacityOfArticulatedParts(b) * (rvi_b->railveh_type == RAILVEH_MULTIHEAD ? 2 : 1);
+ int va = GetTotalCapacityOfArticulatedParts(a.engine_id) * (rvi_a->railveh_type == RAILVEH_MULTIHEAD ? 2 : 1);
+ int vb = GetTotalCapacityOfArticulatedParts(b.engine_id) * (rvi_b->railveh_type == RAILVEH_MULTIHEAD ? 2 : 1);
int r = va - vb;
/* Use EngineID to sort instead since we want consistent sorting */
@@ -334,10 +337,10 @@ static bool TrainEngineCapacitySorter(const EngineID &a, const EngineID &b)
* @param b second engine to compare
* @return for descending order: returns true if a < b. Vice versa for ascending order
*/
-static bool TrainEnginesThenWagonsSorter(const EngineID &a, const EngineID &b)
+static bool TrainEnginesThenWagonsSorter(const GUIEngineListItem &a, const GUIEngineListItem &b)
{
- int val_a = (RailVehInfo(a)->railveh_type == RAILVEH_WAGON ? 1 : 0);
- int val_b = (RailVehInfo(b)->railveh_type == RAILVEH_WAGON ? 1 : 0);
+ int val_a = (RailVehInfo(a.engine_id)->railveh_type == RAILVEH_WAGON ? 1 : 0);
+ int val_b = (RailVehInfo(b.engine_id)->railveh_type == RAILVEH_WAGON ? 1 : 0);
int r = val_a - val_b;
/* Use EngineID to sort instead since we want consistent sorting */
@@ -353,10 +356,10 @@ static bool TrainEnginesThenWagonsSorter(const EngineID &a, const EngineID &b)
* @param b second engine to compare
* @return for descending order: returns true if a < b. Vice versa for ascending order
*/
-static bool RoadVehEngineCapacitySorter(const EngineID &a, const EngineID &b)
+static bool RoadVehEngineCapacitySorter(const GUIEngineListItem &a, const GUIEngineListItem &b)
{
- int va = GetTotalCapacityOfArticulatedParts(a);
- int vb = GetTotalCapacityOfArticulatedParts(b);
+ int va = GetTotalCapacityOfArticulatedParts(a.engine_id);
+ int vb = GetTotalCapacityOfArticulatedParts(b.engine_id);
int r = va - vb;
/* Use EngineID to sort instead since we want consistent sorting */
@@ -372,10 +375,10 @@ static bool RoadVehEngineCapacitySorter(const EngineID &a, const EngineID &b)
* @param b second engine to compare
* @return for descending order: returns true if a < b. Vice versa for ascending order
*/
-static bool ShipEngineCapacitySorter(const EngineID &a, const EngineID &b)
+static bool ShipEngineCapacitySorter(const GUIEngineListItem &a, const GUIEngineListItem &b)
{
- const Engine *e_a = Engine::Get(a);
- const Engine *e_b = Engine::Get(b);
+ const Engine *e_a = Engine::Get(a.engine_id);
+ const Engine *e_b = Engine::Get(b.engine_id);
int va = e_a->GetDisplayDefaultCapacity();
int vb = e_b->GetDisplayDefaultCapacity();
@@ -394,10 +397,10 @@ static bool ShipEngineCapacitySorter(const EngineID &a, const EngineID &b)
* @param b second engine to compare
* @return for descending order: returns true if a < b. Vice versa for ascending order
*/
-static bool AircraftEngineCargoSorter(const EngineID &a, const EngineID &b)
+static bool AircraftEngineCargoSorter(const GUIEngineListItem &a, const GUIEngineListItem &b)
{
- const Engine *e_a = Engine::Get(a);
- const Engine *e_b = Engine::Get(b);
+ const Engine *e_a = Engine::Get(a.engine_id);
+ const Engine *e_b = Engine::Get(b.engine_id);
uint16 mail_a, mail_b;
int va = e_a->GetDisplayDefaultCapacity(&mail_a);
@@ -422,10 +425,10 @@ static bool AircraftEngineCargoSorter(const EngineID &a, const EngineID &b)
* @param b second engine to compare
* @return for descending order: returns true if a < b. Vice versa for ascending order
*/
-static bool AircraftRangeSorter(const EngineID &a, const EngineID &b)
+static bool AircraftRangeSorter(const GUIEngineListItem &a, const GUIEngineListItem &b)
{
- uint16 r_a = Engine::Get(a)->GetRange();
- uint16 r_b = Engine::Get(b)->GetRange();
+ uint16 r_a = Engine::Get(a.engine_id)->GetRange();
+ uint16 r_b = Engine::Get(b.engine_id)->GetRange();
int r = r_a - r_b;
@@ -539,14 +542,14 @@ const StringID _engine_sort_listing[][12] = {{
}};
/** Filters vehicles by cargo and engine (in case of rail vehicle). */
-static bool CDECL CargoAndEngineFilter(const EngineID *eid, const CargoID cid)
+static bool CDECL CargoAndEngineFilter(const GUIEngineListItem *item, const CargoID cid)
{
if (cid == CF_ANY) {
return true;
} else if (cid == CF_ENGINES) {
- return Engine::Get(*eid)->GetPower() != 0;
+ return Engine::Get(item->engine_id)->GetPower() != 0;
} else {
- CargoTypes refit_mask = GetUnionOfArticulatedRefitMasks(*eid, true) & _standard_cargo_mask;
+ CargoTypes refit_mask = GetUnionOfArticulatedRefitMasks(item->engine_id, true) & _standard_cargo_mask;
return (cid == CF_NONE ? refit_mask == 0 : HasBit(refit_mask, cid));
}
}
@@ -593,7 +596,7 @@ static int DrawRailWagonPurchaseInfo(int left, int right, int y, EngineID engine
/* Wagon weight - (including cargo) */
uint weight = e->GetDisplayWeight();
SetDParam(0, weight);
- uint cargo_weight = (e->CanCarryCargo() ? CargoSpec::Get(te.cargo)->weight * te.capacity / 16 : 0);
+ uint cargo_weight = (e->CanCarryCargo() ? CargoSpec::Get(te.cargo)->WeightOfNUnitsInTrain(te.capacity) : 0);
SetDParam(1, cargo_weight + weight);
DrawString(left, right, y, STR_PURCHASE_INFO_WEIGHT_CWEIGHT);
y += FONT_HEIGHT_NORMAL;
@@ -687,7 +690,7 @@ static int DrawRoadVehPurchaseInfo(int left, int right, int y, EngineID engine_n
/* Road vehicle weight - (including cargo) */
int16 weight = e->GetDisplayWeight();
SetDParam(0, weight);
- uint cargo_weight = (e->CanCarryCargo() ? CargoSpec::Get(te.cargo)->weight * te.capacity / 16 : 0);
+ uint cargo_weight = (e->CanCarryCargo() ? CargoSpec::Get(te.cargo)->WeightOfNUnits(te.capacity) : 0);
SetDParam(1, cargo_weight + weight);
DrawString(left, right, y, STR_PURCHASE_INFO_WEIGHT_CWEIGHT);
y += FONT_HEIGHT_NORMAL;
@@ -956,9 +959,7 @@ int DrawVehiclePurchaseInfo(int left, int right, int y, EngineID engine_number,
/**
* Engine drawing loop
* @param type Type of vehicle (VEH_*)
- * @param l The left most location of the list
- * @param r The right most location of the list
- * @param y The top most location of the list
+ * @param r The Rect of the list
* @param eng_list What engines to draw
* @param min where to start in the list
* @param max where in the list to end
@@ -966,21 +967,23 @@ int DrawVehiclePurchaseInfo(int left, int right, int y, EngineID engine_number,
* @param show_count Whether to show the amount of engines or not
* @param selected_group the group to list the engines of
*/
-void DrawEngineList(VehicleType type, int l, int r, int y, const GUIEngineList *eng_list, uint16 min, uint16 max, EngineID selected_id, bool show_count, GroupID selected_group)
+void DrawEngineList(VehicleType type, const Rect &r, const GUIEngineList &eng_list, uint16 min, uint16 max, EngineID selected_id, bool show_count, GroupID selected_group)
{
static const int sprite_y_offsets[] = { -1, -1, -2, -2 };
/* Obligatory sanity checks! */
- assert(max <= eng_list->size());
+ assert(max <= eng_list.size());
bool rtl = _current_text_dir == TD_RTL;
int step_size = GetEngineListHeight(type);
int sprite_left = GetVehicleImageCellSize(type, EIT_PURCHASE).extend_left;
int sprite_right = GetVehicleImageCellSize(type, EIT_PURCHASE).extend_right;
int sprite_width = sprite_left + sprite_right;
+ int circle_width = std::max(GetScaledSpriteSize(SPR_CIRCLE_FOLDED).width, GetScaledSpriteSize(SPR_CIRCLE_UNFOLDED).width);
+ int linecolour = _colour_gradient[COLOUR_ORANGE][4];
- int sprite_x = rtl ? r - sprite_right - 1 : l + sprite_left + 1;
- int sprite_y_offset = sprite_y_offsets[type] + step_size / 2;
+ Rect ir = r.WithHeight(step_size).Shrink(WidgetDimensions::scaled.matrix);
+ int sprite_y_offset = ScaleSpriteTrad(sprite_y_offsets[type]) + ir.Height() / 2;
Dimension replace_icon = {0, 0};
int count_width = 0;
@@ -990,33 +993,51 @@ void DrawEngineList(VehicleType type, int l, int r, int y, const GUIEngineList *
count_width = GetStringBoundingBox(STR_TINY_BLACK_COMA).width;
}
- int text_left = l + (rtl ? WD_FRAMERECT_LEFT + replace_icon.width + 8 + count_width : sprite_width + WD_FRAMETEXT_LEFT);
- int text_right = r - (rtl ? sprite_width + WD_FRAMETEXT_RIGHT : WD_FRAMERECT_RIGHT + replace_icon.width + 8 + count_width);
- int replace_icon_left = rtl ? l + WD_FRAMERECT_LEFT : r - WD_FRAMERECT_RIGHT - replace_icon.width;
- int count_left = l;
- int count_right = rtl ? text_left : r - WD_FRAMERECT_RIGHT - replace_icon.width - 8;
+ Rect tr = ir.Indent(circle_width + WidgetDimensions::scaled.hsep_normal + sprite_width + WidgetDimensions::scaled.hsep_wide, rtl); // Name position
+ Rect cr = tr.Indent(replace_icon.width + WidgetDimensions::scaled.hsep_wide, !rtl).WithWidth(count_width, !rtl); // Count position
+ Rect rr = tr.WithWidth(replace_icon.width, !rtl); // Replace icon position
+ if (show_count) tr = tr.Indent(count_width + WidgetDimensions::scaled.hsep_normal + replace_icon.width + WidgetDimensions::scaled.hsep_wide, !rtl);
- int normal_text_y_offset = (step_size - FONT_HEIGHT_NORMAL) / 2;
- int small_text_y_offset = step_size - FONT_HEIGHT_SMALL - WD_FRAMERECT_BOTTOM - 1;
- int replace_icon_y_offset = (step_size - replace_icon.height) / 2 - 1;
+ int normal_text_y_offset = (ir.Height() - FONT_HEIGHT_NORMAL) / 2;
+ int small_text_y_offset = ir.Height() - FONT_HEIGHT_SMALL;
+ int replace_icon_y_offset = (ir.Height() - replace_icon.height) / 2;
+ int y = ir.top;
for (; min < max; min++, y += step_size) {
- const EngineID engine = (*eng_list)[min];
+ const auto &item = eng_list[min];
+ uint indent = item.indent * WidgetDimensions::scaled.hsep_indent;
+ bool has_variants = (item.flags & EngineDisplayFlags::HasVariants) != EngineDisplayFlags::None;
+ bool is_folded = (item.flags & EngineDisplayFlags::IsFolded) != EngineDisplayFlags::None;
+ bool shaded = (item.flags & EngineDisplayFlags::Shaded) != EngineDisplayFlags::None;
/* Note: num_engines is only used in the autoreplace GUI, so it is correct to use _local_company here. */
- const uint num_engines = GetGroupNumEngines(_local_company, selected_group, engine);
+ const uint num_engines = GetGroupNumEngines(_local_company, selected_group, item.engine_id);
- const Engine *e = Engine::Get(engine);
+ const Engine *e = Engine::Get(item.engine_id);
bool hidden = HasBit(e->company_hidden, _local_company);
StringID str = hidden ? STR_HIDDEN_ENGINE_NAME : STR_ENGINE_NAME;
- TextColour tc = (engine == selected_id) ? TC_WHITE : (TC_NO_SHADE | (hidden ? TC_GREY : TC_BLACK));
+ TextColour tc = (item.engine_id == selected_id) ? TC_WHITE : (TC_NO_SHADE | ((hidden | shaded) ? TC_GREY : TC_BLACK));
- SetDParam(0, engine);
- DrawString(text_left, text_right, y + normal_text_y_offset, str, tc);
- DrawVehicleEngine(l, r, sprite_x, y + sprite_y_offset, engine, (show_count && num_engines == 0) ? PALETTE_CRASH : GetEnginePalette(engine, _local_company), EIT_PURCHASE);
+ SetDParam(0, item.engine_id);
+ Rect itr = tr.Indent(indent, rtl);
+ DrawString(itr.left, itr.right, y + normal_text_y_offset, str, tc);
+ int sprite_x = ir.Indent(indent + circle_width + WidgetDimensions::scaled.hsep_normal, rtl).WithWidth(sprite_width, rtl).left + sprite_left;
+ DrawVehicleEngine(r.left, r.right, sprite_x, y + sprite_y_offset, item.engine_id, (show_count && num_engines == 0) ? PALETTE_CRASH : GetEnginePalette(item.engine_id, _local_company), EIT_PURCHASE);
if (show_count) {
SetDParam(0, num_engines);
- DrawString(count_left, count_right, y + small_text_y_offset, STR_TINY_BLACK_COMA, TC_FROMSTRING, SA_RIGHT | SA_FORCE);
- if (EngineHasReplacementForCompany(Company::Get(_local_company), engine, selected_group)) DrawSprite(SPR_GROUP_REPLACE_ACTIVE, num_engines == 0 ? PALETTE_CRASH : PAL_NONE, replace_icon_left, y + replace_icon_y_offset);
+ DrawString(cr.left, cr.right, y + small_text_y_offset, STR_TINY_BLACK_COMA, TC_FROMSTRING, SA_RIGHT | SA_FORCE);
+ if (EngineHasReplacementForCompany(Company::Get(_local_company), item.engine_id, selected_group)) DrawSprite(SPR_GROUP_REPLACE_ACTIVE, num_engines == 0 ? PALETTE_CRASH : PAL_NONE, rr.left, y + replace_icon_y_offset);
+ }
+ if (has_variants) {
+ Rect fr = ir.Indent(indent, rtl).WithWidth(circle_width, rtl);
+ DrawSpriteIgnorePadding(is_folded ? SPR_CIRCLE_FOLDED : SPR_CIRCLE_UNFOLDED, PAL_NONE, {fr.left, y, fr.right, y + ir.Height() - 1}, false, SA_CENTER);
+ }
+ if (indent > 0) {
+ /* Draw tree lines */
+ Rect fr = ir.Indent(indent - WidgetDimensions::scaled.hsep_indent, rtl).WithWidth(circle_width, rtl);
+ int ycenter = y + normal_text_y_offset + FONT_HEIGHT_NORMAL / 2;
+ bool continues = (min + 1U) < eng_list.size() && eng_list[min + 1].indent == item.indent;
+ GfxDrawLine(fr.left + circle_width / 2, y - WidgetDimensions::scaled.matrix.top, fr.left + circle_width / 2, continues ? y - WidgetDimensions::scaled.matrix.top + step_size - 1 : ycenter, linecolour, WidgetDimensions::scaled.fullbevel.top);
+ GfxDrawLine(fr.left + circle_width / 2, ycenter, fr.right, ycenter, linecolour, WidgetDimensions::scaled.fullbevel.top);
}
}
}
@@ -1081,11 +1102,32 @@ struct BuildVehicleWindow : Window {
}
}
+ void AddChildren(const GUIEngineList &source, EngineID parent, int indent)
+ {
+ for (const auto &item : source) {
+ if (item.variant_id != parent || item.engine_id == parent) continue;
+
+ const Engine *e = Engine::Get(item.engine_id);
+ EngineDisplayFlags flags = item.flags;
+ if (e->display_last_variant != INVALID_ENGINE) flags &= ~EngineDisplayFlags::Shaded;
+ this->eng_list.emplace_back(e->display_last_variant == INVALID_ENGINE ? item.engine_id : e->display_last_variant, item.engine_id, flags, indent);
+
+ /* Add variants if not folded */
+ if ((item.flags & (EngineDisplayFlags::HasVariants | EngineDisplayFlags::IsFolded)) == EngineDisplayFlags::HasVariants) {
+ /* Add this engine again as a child */
+ if ((item.flags & EngineDisplayFlags::Shaded) == EngineDisplayFlags::None) {
+ this->eng_list.emplace_back(item.engine_id, item.engine_id, EngineDisplayFlags::None, indent + 1);
+ }
+ AddChildren(source, item.engine_id, indent + 1);
+ }
+ }
+ }
+
BuildVehicleWindow(WindowDesc *desc, TileIndex tile, VehicleType type) : Window(desc)
{
this->vehicle_type = type;
this->listview_mode = tile == INVALID_TILE;
- this->window_number = this->listview_mode ? (int)type : tile;
+ this->window_number = this->listview_mode ? (int)type : (int)tile;
this->sel_engine = INVALID_ENGINE;
@@ -1120,7 +1162,7 @@ struct BuildVehicleWindow : Window {
this->details_height = ((this->vehicle_type == VEH_TRAIN) ? 10 : 9);
- this->FinishInitNested(tile == INVALID_TILE ? (int)type : tile);
+ this->FinishInitNested(tile == INVALID_TILE ? (int)type : (int)tile);
this->owner = (tile != INVALID_TILE) ? GetTileOwner(tile) : _local_company;
@@ -1128,7 +1170,7 @@ struct BuildVehicleWindow : Window {
this->GenerateBuildList(); // generate the list, since we need it in the next line
/* Select the first engine in the list as default when opening the window */
if (this->eng_list.size() > 0) {
- this->SelectEngine(this->eng_list[0]);
+ this->SelectEngine(this->eng_list[0].engine_id);
} else {
this->SelectEngine(INVALID_ENGINE);
}
@@ -1232,11 +1274,11 @@ struct BuildVehicleWindow : Window {
if (!this->listview_mode) {
/* Query for cost and refitted capacity */
- CommandCost ret = DoCommand(this->window_number, this->sel_engine | (cargo << 24), 0, DC_QUERY_COST, GetCmdBuildVeh(this->vehicle_type));
+ auto [ret, veh_id, refit_capacity, refit_mail] = Command::Do(DC_QUERY_COST, this->window_number, this->sel_engine, true, cargo, INVALID_CLIENT_ID);
if (ret.Succeeded()) {
this->te.cost = ret.GetCost() - e->GetCost();
- this->te.capacity = _returned_refit_capacity;
- this->te.mail_capacity = _returned_mail_refit_capacity;
+ this->te.capacity = refit_capacity;
+ this->te.mail_capacity = refit_mail;
this->te.cargo = (cargo == CT_INVALID) ? e->GetDefaultCargoType() : cargo;
return;
}
@@ -1260,7 +1302,7 @@ struct BuildVehicleWindow : Window {
if (0 == this->eng_list.size()) { // no engine passed through the filter, invalidate the previously selected engine
this->SelectEngine(INVALID_ENGINE);
} else if (std::find(this->eng_list.begin(), this->eng_list.end(), this->sel_engine) == this->eng_list.end()) { // previously selected engine didn't pass the filter, select the first engine of the list
- this->SelectEngine(this->eng_list[0]);
+ this->SelectEngine(this->eng_list[0].engine_id);
}
}
@@ -1268,17 +1310,19 @@ struct BuildVehicleWindow : Window {
bool FilterSingleEngine(EngineID eid)
{
CargoID filter_type = this->cargo_filter[this->cargo_filter_criteria];
- return CargoAndEngineFilter(&eid, filter_type);
+ GUIEngineListItem item = {eid, eid, EngineDisplayFlags::None, 0};
+ return CargoAndEngineFilter(&item, filter_type);
}
/* Figure out what train EngineIDs to put in the list */
- void GenerateBuildTrainList()
+ void GenerateBuildTrainList(GUIEngineList &list)
{
+ std::vector variants;
EngineID sel_id = INVALID_ENGINE;
int num_engines = 0;
int num_wagons = 0;
- this->eng_list.clear();
+ list.clear();
/* Make list of all available train engines and wagons.
* Also check to see if the previously selected engine is still available,
@@ -1295,7 +1339,7 @@ struct BuildVehicleWindow : Window {
/* Filter now! So num_engines and num_wagons is valid */
if (!FilterSingleEngine(eid)) continue;
- this->eng_list.push_back(eid);
+ list.emplace_back(eid, e->info.variant_id, e->display_flags, 0);
if (rvi->railveh_type != RAILVEH_WAGON) {
num_engines++;
@@ -1303,9 +1347,18 @@ struct BuildVehicleWindow : Window {
num_wagons++;
}
+ if (e->info.variant_id != eid && e->info.variant_id != INVALID_ENGINE) variants.push_back(e->info.variant_id);
if (eid == this->sel_engine) sel_id = eid;
}
+ /* ensure primary engine of variant group is in list */
+ for (const auto &variant : variants) {
+ if (std::find(list.begin(), list.end(), variant) == list.end()) {
+ const Engine *e = Engine::Get(variant);
+ list.emplace_back(variant, e->info.variant_id, e->display_flags | EngineDisplayFlags::Shaded, 0);
+ }
+ }
+
this->SelectEngine(sel_id);
/* invalidate cached values for name sorter - engine names could change */
@@ -1313,14 +1366,14 @@ struct BuildVehicleWindow : Window {
/* make engines first, and then wagons, sorted by selected sort_criteria */
_engine_sort_direction = false;
- EngList_Sort(&this->eng_list, TrainEnginesThenWagonsSorter);
+ EngList_Sort(&list, TrainEnginesThenWagonsSorter);
/* and then sort engines */
_engine_sort_direction = this->descending_sort_order;
- EngList_SortPartial(&this->eng_list, _engine_sort_functions[0][this->sort_criteria], 0, num_engines);
+ EngList_SortPartial(&list, _engine_sort_functions[0][this->sort_criteria], 0, num_engines);
/* and finally sort wagons */
- EngList_SortPartial(&this->eng_list, _engine_sort_functions[0][this->sort_criteria], num_engines, num_wagons);
+ EngList_SortPartial(&list, _engine_sort_functions[0][this->sort_criteria], num_engines, num_wagons);
}
/* Figure out what road vehicle EngineIDs to put in the list */
@@ -1336,7 +1389,7 @@ struct BuildVehicleWindow : Window {
if (!IsEngineBuildable(eid, VEH_ROAD, _local_company)) continue;
if (this->filter.roadtype != INVALID_ROADTYPE && !HasPowerOnRoad(e->u.road.roadtype, this->filter.roadtype)) continue;
- this->eng_list.push_back(eid);
+ this->eng_list.emplace_back(eid, e->info.variant_id, e->display_flags, 0);
if (eid == this->sel_engine) sel_id = eid;
}
@@ -1353,7 +1406,7 @@ struct BuildVehicleWindow : Window {
if (!this->show_hidden_engines && e->IsHidden(_local_company)) continue;
EngineID eid = e->index;
if (!IsEngineBuildable(eid, VEH_SHIP, _local_company)) continue;
- this->eng_list.push_back(eid);
+ this->eng_list.emplace_back(eid, e->info.variant_id, e->display_flags, 0);
if (eid == this->sel_engine) sel_id = eid;
}
@@ -1380,7 +1433,7 @@ struct BuildVehicleWindow : Window {
/* First VEH_END window_numbers are fake to allow a window open for all different types at once */
if (!this->listview_mode && !CanVehicleUseStation(eid, st)) continue;
- this->eng_list.push_back(eid);
+ this->eng_list.emplace_back(eid, e->info.variant_id, e->display_flags, 0);
if (eid == this->sel_engine) sel_id = eid;
}
@@ -1395,13 +1448,18 @@ struct BuildVehicleWindow : Window {
/* Update filter type in case the road/railtype of the depot got converted */
this->UpdateFilterByTile();
+ this->eng_list.clear();
+
+ GUIEngineList list;
+
switch (this->vehicle_type) {
default: NOT_REACHED();
case VEH_TRAIN:
- this->GenerateBuildTrainList();
+ this->GenerateBuildTrainList(list);
+ AddChildren(list, INVALID_ENGINE, 0);
this->eng_list.shrink_to_fit();
this->eng_list.RebuildDone();
- return; // trains should not reach the last sorting
+ return;
case VEH_ROAD:
this->GenerateBuildRoadVehList();
break;
@@ -1415,9 +1473,23 @@ struct BuildVehicleWindow : Window {
this->FilterEngineList();
+ /* ensure primary engine of variant group is in list after filtering */
+ std::vector variants;
+ for (const auto &item : this->eng_list) {
+ if (item.engine_id != item.variant_id && item.variant_id != INVALID_ENGINE) variants.push_back(item.variant_id);
+ }
+ for (const auto &variant : variants) {
+ if (std::find(this->eng_list.begin(), this->eng_list.end(), variant) == this->eng_list.end()) {
+ const Engine *e = Engine::Get(variant);
+ this->eng_list.emplace_back(variant, e->info.variant_id, e->display_flags | EngineDisplayFlags::Shaded, 0);
+ }
+ }
+
_engine_sort_direction = this->descending_sort_order;
EngList_Sort(&this->eng_list, _engine_sort_functions[this->vehicle_type][this->sort_criteria]);
+ this->eng_list.swap(list);
+ AddChildren(list, INVALID_ENGINE, 0);
this->eng_list.shrink_to_fit();
this->eng_list.RebuildDone();
}
@@ -1443,7 +1515,23 @@ struct BuildVehicleWindow : Window {
case WID_BV_LIST: {
uint i = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_BV_LIST);
size_t num_items = this->eng_list.size();
- this->SelectEngine((i < num_items) ? this->eng_list[i] : INVALID_ENGINE);
+ EngineID e = INVALID_ENGINE;
+ if (i < num_items) {
+ const auto &item = this->eng_list[i];
+ const Rect r = this->GetWidget(widget)->GetCurrentRect().Shrink(WidgetDimensions::scaled.matrix).WithWidth(WidgetDimensions::scaled.hsep_indent * (item.indent + 1), _current_text_dir == TD_RTL);
+ if ((item.flags & EngineDisplayFlags::HasVariants) != EngineDisplayFlags::None && IsInsideMM(r.left, r.right, pt.x)) {
+ /* toggle folded flag on engine */
+ assert(item.variant_id != INVALID_ENGINE);
+ Engine *engine = Engine::Get(item.variant_id);
+ engine->display_flags ^= EngineDisplayFlags::IsFolded;
+
+ InvalidateWindowData(WC_REPLACE_VEHICLE, this->vehicle_type, 0); // Update the autoreplace window
+ InvalidateWindowClassesData(WC_BUILD_VEHICLE); // The build windows needs updating as well
+ return;
+ }
+ if ((item.flags & EngineDisplayFlags::Shaded) == EngineDisplayFlags::None) e = item.engine_id;
+ }
+ this->SelectEngine(e);
this->SetDirty();
if (_ctrl_pressed) {
this->OnClick(pt, WID_BV_SHOW_HIDE, 1);
@@ -1464,7 +1552,7 @@ struct BuildVehicleWindow : Window {
case WID_BV_SHOW_HIDE: {
const Engine *e = (this->sel_engine == INVALID_ENGINE) ? nullptr : Engine::Get(this->sel_engine);
if (e != nullptr) {
- DoCommandP(0, 0, this->sel_engine | (e->IsHidden(_current_company) ? 0 : (1u << 31)), CMD_SET_VEHICLE_VISIBILITY);
+ Command::Post(this->sel_engine, !e->IsHidden(_current_company));
}
break;
}
@@ -1472,10 +1560,27 @@ struct BuildVehicleWindow : Window {
case WID_BV_BUILD: {
EngineID sel_eng = this->sel_engine;
if (sel_eng != INVALID_ENGINE) {
- CommandCallback *callback = (this->vehicle_type == VEH_TRAIN && RailVehInfo(sel_eng)->railveh_type == RAILVEH_WAGON) ? CcBuildWagon : CcBuildPrimaryVehicle;
CargoID cargo = this->cargo_filter[this->cargo_filter_criteria];
if (cargo == CF_ANY || cargo == CF_ENGINES) cargo = CF_NONE;
- DoCommandP(this->window_number, sel_eng | (cargo << 24), 0, GetCmdBuildVeh(this->vehicle_type), callback);
+ if (this->vehicle_type == VEH_TRAIN && RailVehInfo(sel_eng)->railveh_type == RAILVEH_WAGON) {
+ Command::Post(GetCmdBuildVehMsg(this->vehicle_type), CcBuildWagon, this->window_number, sel_eng, true, cargo, INVALID_CLIENT_ID);
+ } else {
+ Command::Post(GetCmdBuildVehMsg(this->vehicle_type), CcBuildPrimaryVehicle, this->window_number, sel_eng, true, cargo, INVALID_CLIENT_ID);
+ }
+
+ /* Update last used variant and refresh if necessary. */
+ bool refresh = false;
+ int recursion = 10; /* In case of infinite loop */
+ for (Engine *e = Engine::Get(sel_eng); recursion > 0; e = Engine::Get(e->info.variant_id), --recursion) {
+ refresh |= (e->display_last_variant != sel_eng);
+ e->display_last_variant = sel_eng;
+ if (e->info.variant_id == INVALID_ENGINE) break;
+ }
+ if (refresh) {
+ InvalidateWindowData(WC_REPLACE_VEHICLE, this->vehicle_type, 0); // Update the autoreplace window
+ InvalidateWindowClassesData(WC_BUILD_VEHICLE); // The build windows needs updating as well
+ return;
+ }
}
break;
}
@@ -1551,7 +1656,7 @@ struct BuildVehicleWindow : Window {
case WID_BV_LIST:
resize->height = GetEngineListHeight(this->vehicle_type);
size->height = 3 * resize->height;
- size->width = std::max(size->width, GetVehicleImageCellSize(this->vehicle_type, EIT_PURCHASE).extend_left + GetVehicleImageCellSize(this->vehicle_type, EIT_PURCHASE).extend_right + 165);
+ size->width = std::max(size->width, GetVehicleImageCellSize(this->vehicle_type, EIT_PURCHASE).extend_left + GetVehicleImageCellSize(this->vehicle_type, EIT_PURCHASE).extend_right + 165) + padding.width;
break;
case WID_BV_PANEL:
@@ -1589,10 +1694,8 @@ struct BuildVehicleWindow : Window {
case WID_BV_LIST:
DrawEngineList(
this->vehicle_type,
- r.left + WD_FRAMERECT_LEFT,
- r.right - WD_FRAMERECT_RIGHT,
- r.top + WD_FRAMERECT_TOP,
- &this->eng_list,
+ r,
+ this->eng_list,
this->vscroll->GetPosition(),
static_cast(std::min(this->vscroll->GetPosition() + this->vscroll->GetCapacity(), this->eng_list.size())),
this->sel_engine,
@@ -1623,10 +1726,9 @@ struct BuildVehicleWindow : Window {
int needed_height = this->details_height;
/* Draw details panels. */
if (this->sel_engine != INVALID_ENGINE) {
- NWidgetBase *nwi = this->GetWidget(WID_BV_PANEL);
- int text_end = DrawVehiclePurchaseInfo(nwi->pos_x + WD_FRAMETEXT_LEFT, nwi->pos_x + nwi->current_x - WD_FRAMETEXT_RIGHT,
- nwi->pos_y + WD_FRAMERECT_TOP, this->sel_engine, this->te);
- needed_height = std::max(needed_height, (text_end - (int)nwi->pos_y - WD_FRAMERECT_TOP) / FONT_HEIGHT_NORMAL);
+ const Rect r = this->GetWidget(WID_BV_PANEL)->GetCurrentRect().Shrink(WidgetDimensions::scaled.frametext, WidgetDimensions::scaled.framerect);
+ int text_end = DrawVehiclePurchaseInfo(r.left, r.right, r.top, this->sel_engine, this->te);
+ needed_height = std::max(needed_height, (text_end - r.top) / FONT_HEIGHT_NORMAL);
}
if (needed_height != this->details_height) { // Details window are not high enough, enlarge them.
int resize = needed_height - this->details_height;
@@ -1641,7 +1743,7 @@ struct BuildVehicleWindow : Window {
{
if (str == nullptr) return;
- DoCommandP(0, this->rename_engine, 0, CMD_RENAME_ENGINE | CMD_MSG(STR_ERROR_CAN_T_RENAME_TRAIN_TYPE + this->vehicle_type), nullptr, str);
+ Command::Post(STR_ERROR_CAN_T_RENAME_TRAIN_TYPE + this->vehicle_type, this->rename_engine, str);
}
void OnDropdownSelect(int widget, int index) override
@@ -1688,7 +1790,7 @@ void ShowBuildVehicleWindow(TileIndex tile, VehicleType type)
* so if tile == INVALID_TILE (Available XXX Window), use 'type' as unique number.
* As it always is a low value, it won't collide with any real tile
* number. */
- uint num = (tile == INVALID_TILE) ? (int)type : tile;
+ uint num = (tile == INVALID_TILE) ? (int)type : (int)tile;
assert(IsCompanyBuildableVehicleType(type));
diff --git a/src/cargomonitor.cpp b/src/cargomonitor.cpp
index 2941a29556..7614cba6ec 100644
--- a/src/cargomonitor.cpp
+++ b/src/cargomonitor.cpp
@@ -149,9 +149,9 @@ void AddCargoDelivery(CargoID cargo_type, CompanyID company, uint32 amount, Sour
if (iter != _cargo_deliveries.end()) iter->second += amount;
/* Industry delivery. */
- for (Industry *ind : st->industries_near) {
- if (ind->index != dest) continue;
- CargoMonitorID num = EncodeCargoIndustryMonitor(company, cargo_type, ind->index);
+ for (const auto &i : st->industries_near) {
+ if (i.industry->index != dest) continue;
+ CargoMonitorID num = EncodeCargoIndustryMonitor(company, cargo_type, i.industry->index);
CargoMonitorMap::iterator iter = _cargo_deliveries.find(num);
if (iter != _cargo_deliveries.end()) iter->second += amount;
}
diff --git a/src/cargopacket.cpp b/src/cargopacket.cpp
index 86bba0261b..e9c6e1aa77 100644
--- a/src/cargopacket.cpp
+++ b/src/cargopacket.cpp
@@ -75,7 +75,7 @@ CargoPacket::CargoPacket(uint16 count, byte days_in_transit, StationID source, T
source_id(source_id),
source(source),
source_xy(source_xy),
- loaded_at_xy(loaded_at_xy)
+ loaded_at_xy(loaded_at_xy.value)
{
assert(count != 0);
this->source_type = source_type;
diff --git a/src/cargotype.cpp b/src/cargotype.cpp
index e35c94d2fe..1bb490a2cf 100644
--- a/src/cargotype.cpp
+++ b/src/cargotype.cpp
@@ -12,6 +12,7 @@
#include "newgrf_cargo.h"
#include "string_func.h"
#include "strings_func.h"
+#include "settings_type.h"
#include "table/sprites.h"
#include "table/strings.h"
@@ -209,3 +210,8 @@ void InitializeSortedCargoSpecs()
_sorted_standard_cargo_specs = { _sorted_cargo_specs.data(), nb_standard_cargo };
}
+uint64 CargoSpec::WeightOfNUnitsInTrain(uint32 n) const
+{
+ if (this->is_freight) n *= _settings_game.vehicle.freight_trains;
+ return this->WeightOfNUnits(n);
+}
diff --git a/src/cargotype.h b/src/cargotype.h
index 5ed9ac90e2..6e73f36958 100644
--- a/src/cargotype.h
+++ b/src/cargotype.h
@@ -23,7 +23,7 @@
typedef uint32 CargoLabel;
/** Town growth effect when delivering cargo. */
-enum TownEffect {
+enum TownEffect : byte {
TE_BEGIN = 0,
TE_NONE = TE_BEGIN, ///< Cargo has no effect.
TE_PASSENGERS, ///< Cargo behaves passenger-like.
@@ -66,7 +66,6 @@ struct CargoSpec {
bool is_freight; ///< Cargo type is considered to be freight (affects train freight multiplier).
TownEffect town_effect; ///< The effect that delivering this cargo type has on towns. Also affects destination of subsidies.
- uint16 multipliertowngrowth; ///< Size of the effect.
uint8 callback_mask; ///< Bitmask of cargo callbacks that have to be called
StringID name; ///< Name of this type of cargo.
@@ -124,6 +123,13 @@ struct CargoSpec {
SpriteID GetCargoIcon() const;
+ inline uint64 WeightOfNUnits(uint32 n) const
+ {
+ return n * this->weight / 16u;
+ }
+
+ uint64 WeightOfNUnitsInTrain(uint32 n) const;
+
/**
* Iterator to iterate all valid CargoSpec
*/
diff --git a/src/cheat_gui.cpp b/src/cheat_gui.cpp
index a0ac5fab8f..030bbeb693 100644
--- a/src/cheat_gui.cpp
+++ b/src/cheat_gui.cpp
@@ -28,6 +28,8 @@
#include "tile_map.h"
#include "newgrf.h"
#include "error.h"
+#include "misc_cmd.h"
+#include "core/geometry_func.hpp"
#include "widgets/cheat_widget.h"
@@ -54,7 +56,7 @@ static int32 _money_cheat_amount = 10000000;
*/
static int32 ClickMoneyCheat(int32 p1, int32 p2)
{
- DoCommandP(0, (uint32)(p2 * _money_cheat_amount), 0, CMD_MONEY_CHEAT);
+ Command::Post(p2 * _money_cheat_amount);
return _money_cheat_amount;
}
@@ -206,7 +208,7 @@ static const NWidgetPart _nested_cheat_widgets[] = {
EndContainer(),
NWidget(WWT_PANEL, COLOUR_GREY, WID_C_PANEL), SetDataTip(0x0, STR_CHEATS_TOOLTIP), EndContainer(),
NWidget(WWT_PANEL, COLOUR_GREY),
- NWidget(WWT_LABEL, COLOUR_GREY, WID_C_NOTE), SetFill(1, 1), SetDataTip(STR_CHEATS_NOTE, STR_NULL), SetPadding(WD_PAR_VSEP_NORMAL, 4, WD_PAR_VSEP_NORMAL, 4),
+ NWidget(WWT_LABEL, COLOUR_GREY, WID_C_NOTE), SetFill(1, 1), SetDataTip(STR_CHEATS_NOTE, STR_NULL), SetPadding(WidgetDimensions::unscaled.frametext),
EndContainer(),
};
@@ -215,39 +217,48 @@ struct CheatWindow : Window {
int clicked;
int clicked_widget;
uint line_height;
- int box_width;
+ Dimension box; ///< Dimension of box sprite
+ Dimension icon; ///< Dimension of company icon sprite
CheatWindow(WindowDesc *desc) : Window(desc)
{
- this->box_width = GetSpriteSize(SPR_BOX_EMPTY).width;
this->InitNested();
}
+ void OnInit() override
+ {
+ this->box = maxdim(GetSpriteSize(SPR_BOX_EMPTY), GetSpriteSize(SPR_BOX_CHECKED));
+ this->icon = GetSpriteSize(SPR_COMPANY_ICON);
+ }
+
void DrawWidget(const Rect &r, int widget) const override
{
if (widget != WID_C_PANEL) return;
- int y = r.top + WD_FRAMERECT_TOP + WD_PAR_VSEP_NORMAL;
+ const Rect ir = r.Shrink(WidgetDimensions::scaled.framerect);
+ int y = ir.top;
bool rtl = _current_text_dir == TD_RTL;
- uint box_left = rtl ? r.right - this->box_width - 5 : r.left + 5;
- uint button_left = rtl ? r.right - this->box_width - 10 - SETTING_BUTTON_WIDTH : r.left + this->box_width + 10;
- uint text_left = r.left + (rtl ? WD_FRAMERECT_LEFT : 20 + this->box_width + SETTING_BUTTON_WIDTH);
- uint text_right = r.right - (rtl ? 20 + this->box_width + SETTING_BUTTON_WIDTH : WD_FRAMERECT_RIGHT);
+ uint box_left = rtl ? ir.right - this->box.width - WidgetDimensions::scaled.hsep_wide : ir.left + WidgetDimensions::scaled.hsep_wide;
+ uint button_left = rtl ? ir.right - this->box.width - WidgetDimensions::scaled.hsep_wide * 2 - SETTING_BUTTON_WIDTH : ir.left + this->box.width + WidgetDimensions::scaled.hsep_wide * 2;
+ uint text_left = ir.left + (rtl ? 0 : WidgetDimensions::scaled.hsep_wide * 4 + this->box.width + SETTING_BUTTON_WIDTH);
+ uint text_right = ir.right - (rtl ? WidgetDimensions::scaled.hsep_wide * 4 + this->box.width + SETTING_BUTTON_WIDTH : 0);
int text_y_offset = (this->line_height - FONT_HEIGHT_NORMAL) / 2;
- int icon_y_offset = (this->line_height - SETTING_BUTTON_HEIGHT) / 2;
+ int box_y_offset = (this->line_height - this->box.height) / 2;
+ int button_y_offset = (this->line_height - SETTING_BUTTON_HEIGHT) / 2;
+ int icon_y_offset = (this->line_height - this->icon.height) / 2;
for (int i = 0; i != lengthof(_cheats_ui); i++) {
const CheatEntry *ce = &_cheats_ui[i];
- DrawSprite((*ce->been_used) ? SPR_BOX_CHECKED : SPR_BOX_EMPTY, PAL_NONE, box_left, y + icon_y_offset + 2);
+ DrawSprite((*ce->been_used) ? SPR_BOX_CHECKED : SPR_BOX_EMPTY, PAL_NONE, box_left, y + box_y_offset);
switch (ce->type) {
case SLE_BOOL: {
bool on = (*(bool*)ce->variable);
- DrawBoolButton(button_left, y + icon_y_offset, on, true);
+ DrawBoolButton(button_left, y + button_y_offset, on, true);
SetDParam(0, on ? STR_CONFIG_SETTING_ON : STR_CONFIG_SETTING_OFF);
break;
}
@@ -257,7 +268,7 @@ struct CheatWindow : Window {
char buf[512];
/* Draw [<][>] boxes for settings of an integer-type */
- DrawArrowButtons(button_left, y + icon_y_offset, COLOUR_YELLOW, clicked - (i * 2), true, true);
+ DrawArrowButtons(button_left, y + button_y_offset, COLOUR_YELLOW, clicked - (i * 2), true, true);
switch (ce->str) {
/* Display date for change date cheat */
@@ -267,8 +278,8 @@ struct CheatWindow : Window {
case STR_CHEAT_CHANGE_COMPANY: {
SetDParam(0, val + 1);
GetString(buf, STR_CHEAT_CHANGE_COMPANY, lastof(buf));
- uint offset = 10 + GetStringBoundingBox(buf).width;
- DrawCompanyIcon(_local_company, rtl ? text_right - offset - 10 : text_left + offset, y + icon_y_offset + 2);
+ uint offset = WidgetDimensions::scaled.hsep_indent + GetStringBoundingBox(buf).width;
+ DrawCompanyIcon(_local_company, rtl ? text_right - offset - WidgetDimensions::scaled.hsep_indent : text_left + offset, y + icon_y_offset);
break;
}
@@ -310,7 +321,7 @@ struct CheatWindow : Window {
/* Draw coloured flag for change company cheat */
case STR_CHEAT_CHANGE_COMPANY:
SetDParamMaxValue(0, MAX_COMPANIES);
- width = std::max(width, GetStringBoundingBox(ce->str).width + 10 + 10);
+ width = std::max(width, GetStringBoundingBox(ce->str).width + WidgetDimensions::scaled.hsep_wide * 4);
break;
default:
@@ -322,21 +333,21 @@ struct CheatWindow : Window {
}
}
- this->line_height = std::max(GetSpriteSize(SPR_BOX_CHECKED).height, GetSpriteSize(SPR_BOX_EMPTY).height);
+ this->line_height = std::max(this->box.height, this->icon.height);
this->line_height = std::max(this->line_height, SETTING_BUTTON_HEIGHT);
- this->line_height = std::max(this->line_height, FONT_HEIGHT_NORMAL) + WD_PAR_VSEP_NORMAL;
+ this->line_height = std::max(this->line_height, FONT_HEIGHT_NORMAL) + WidgetDimensions::scaled.framerect.Vertical();
- size->width = width + 20 + this->box_width + SETTING_BUTTON_WIDTH /* stuff on the left */ + 10 /* extra spacing on right */;
- size->height = WD_FRAMERECT_TOP + WD_PAR_VSEP_NORMAL + WD_FRAMERECT_BOTTOM + this->line_height * lengthof(_cheats_ui);
+ size->width = width + WidgetDimensions::scaled.hsep_wide * 4 + this->box.width + SETTING_BUTTON_WIDTH /* stuff on the left */ + WidgetDimensions::scaled.hsep_wide * 2 /* extra spacing on right */;
+ size->height = WidgetDimensions::scaled.framerect.Vertical() + this->line_height * lengthof(_cheats_ui);
}
void OnClick(Point pt, int widget, int click_count) override
{
- const NWidgetBase *wid = this->GetWidget(WID_C_PANEL);
- uint btn = (pt.y - wid->pos_y - WD_FRAMERECT_TOP - WD_PAR_VSEP_NORMAL) / this->line_height;
- int x = pt.x - wid->pos_x;
+ Rect r = this->GetWidget(WID_C_PANEL)->GetCurrentRect().Shrink(WidgetDimensions::scaled.framerect);
+ uint btn = (pt.y - r.top) / this->line_height;
+ uint x = pt.x - r.left;
bool rtl = _current_text_dir == TD_RTL;
- if (rtl) x = wid->current_x - x;
+ if (rtl) x = r.Width() - 1 - x;
if (btn >= lengthof(_cheats_ui)) return;
@@ -344,13 +355,13 @@ struct CheatWindow : Window {
int value = (int32)ReadValue(ce->variable, ce->type);
int oldvalue = value;
- if (btn == CHT_CHANGE_DATE && x >= 20 + this->box_width + SETTING_BUTTON_WIDTH) {
+ if (btn == CHT_CHANGE_DATE && x >= WidgetDimensions::scaled.hsep_wide * 2 + this->box.width + SETTING_BUTTON_WIDTH) {
/* Click at the date text directly. */
clicked_widget = CHT_CHANGE_DATE;
SetDParam(0, value);
ShowQueryString(STR_JUST_INT, STR_CHEAT_CHANGE_DATE_QUERY_CAPT, 8, this, CS_NUMERAL, QSF_ACCEPT_UNCHANGED);
return;
- } else if (btn == CHT_EDIT_MAX_HL && x >= 20 + this->box_width + SETTING_BUTTON_WIDTH) {
+ } else if (btn == CHT_EDIT_MAX_HL && x >= WidgetDimensions::scaled.hsep_wide * 2 + this->box.width + SETTING_BUTTON_WIDTH) {
clicked_widget = CHT_EDIT_MAX_HL;
SetDParam(0, value);
ShowQueryString(STR_JUST_INT, STR_CHEAT_EDIT_MAX_HL_QUERY_CAPT, 8, this, CS_NUMERAL, QSF_ACCEPT_UNCHANGED);
@@ -358,7 +369,7 @@ struct CheatWindow : Window {
}
/* Not clicking a button? */
- if (!IsInsideMM(x, 10 + this->box_width, 10 + this->box_width + SETTING_BUTTON_WIDTH)) return;
+ if (!IsInsideMM(x, WidgetDimensions::scaled.hsep_wide * 2 + this->box.width, WidgetDimensions::scaled.hsep_wide * 2 + this->box.width + SETTING_BUTTON_WIDTH)) return;
*ce->been_used = true;
@@ -370,10 +381,10 @@ struct CheatWindow : Window {
default:
/* Take whatever the function returns */
- value = ce->proc(value + ((x >= 10 + this->box_width + SETTING_BUTTON_WIDTH / 2) ? 1 : -1), (x >= 10 + this->box_width + SETTING_BUTTON_WIDTH / 2) ? 1 : -1);
+ value = ce->proc(value + ((x >= WidgetDimensions::scaled.hsep_wide * 2 + this->box.width + SETTING_BUTTON_WIDTH / 2) ? 1 : -1), (x >= WidgetDimensions::scaled.hsep_wide * 2 + this->box.width + SETTING_BUTTON_WIDTH / 2) ? 1 : -1);
/* The first cheat (money), doesn't return a different value. */
- if (value != oldvalue || btn == CHT_MONEY) this->clicked = btn * 2 + 1 + ((x >= 10 + this->box_width + SETTING_BUTTON_WIDTH / 2) != rtl ? 1 : 0);
+ if (value != oldvalue || btn == CHT_MONEY) this->clicked = btn * 2 + 1 + ((x >= WidgetDimensions::scaled.hsep_wide * 2 + this->box.width + SETTING_BUTTON_WIDTH / 2) != rtl ? 1 : 0);
break;
}
diff --git a/src/clear_cmd.cpp b/src/clear_cmd.cpp
index 4036313123..17711a6267 100644
--- a/src/clear_cmd.cpp
+++ b/src/clear_cmd.cpp
@@ -16,6 +16,7 @@
#include "water.h"
#include "core/random_func.hpp"
#include "newgrf_generic.h"
+#include "landscape_cmd.h"
#include "table/strings.h"
#include "table/sprites.h"
@@ -381,7 +382,7 @@ static void ChangeTileOwner_Clear(TileIndex tile, Owner old_owner, Owner new_own
static CommandCost TerraformTile_Clear(TileIndex tile, DoCommandFlag flags, int z_new, Slope tileh_new)
{
- return DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
+ return Command::Do(flags, tile);
}
extern const TileTypeProcs _tile_type_clear_procs = {
diff --git a/src/cmd_helper.h b/src/cmd_helper.h
deleted file mode 100644
index ceb4d4bd9b..0000000000
--- a/src/cmd_helper.h
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * This file is part of OpenTTD.
- * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
- * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see .
- */
-
-/** @file cmd_helper.h Helper functions to extract data from command parameters. */
-
-#ifndef CMD_HELPER_H
-#define CMD_HELPER_H
-
-#include "core/enum_type.hpp"
-#include "core/bitmath_func.hpp"
-
-/**
- * Extracts a given type from a value.
- * @tparam T The type of data we're looking for.
- * @tparam S The offset in the data.
- * @tparam N The amount of bits to read.
- * @tparam U The type of data passed to us.
- * @param v The data to extract the value from.
- */
-template static inline T Extract(U v)
-{
- /* Check if there are enough bits in v */
- static_assert(N == EnumPropsT::num_bits);
- static_assert(S + N <= sizeof(U) * 8);
- static_assert(EnumPropsT::end <= (1 << N));
- U masked = GB(v, S, N);
- return IsInsideMM(masked, EnumPropsT::begin, EnumPropsT::end) ? (T)masked : EnumPropsT::invalid;
-}
-
-#endif /* CMD_HELPER_H */
diff --git a/src/command.cpp b/src/command.cpp
index dd6980bb6a..a6085f7509 100644
--- a/src/command.cpp
+++ b/src/command.cpp
@@ -24,6 +24,39 @@
#include "signal_func.h"
#include "core/backup_type.hpp"
#include "object_base.h"
+#include "autoreplace_cmd.h"
+#include "company_cmd.h"
+#include "depot_cmd.h"
+#include "economy_cmd.h"
+#include "engine_cmd.h"
+#include "goal_cmd.h"
+#include "group_cmd.h"
+#include "industry_cmd.h"
+#include "league_cmd.h"
+#include "landscape_cmd.h"
+#include "misc_cmd.h"
+#include "news_cmd.h"
+#include "object_cmd.h"
+#include "order_cmd.h"
+#include "rail_cmd.h"
+#include "road_cmd.h"
+#include "roadveh_cmd.h"
+#include "settings_cmd.h"
+#include "signs_cmd.h"
+#include "station_cmd.h"
+#include "story_cmd.h"
+#include "subsidy_cmd.h"
+#include "terraform_cmd.h"
+#include "timetable_cmd.h"
+#include "town_cmd.h"
+#include "train_cmd.h"
+#include "tree_cmd.h"
+#include "tunnelbridge_cmd.h"
+#include "vehicle_cmd.h"
+#include "viewport_cmd.h"
+#include "water_cmd.h"
+#include "waypoint_cmd.h"
+#include "misc/endian_buffer.hpp"
#include "string_func.h"
#include "tilehighlight_func.h"
#include "build_confirmation_func.h"
@@ -32,182 +65,29 @@
#include "safeguards.h"
-CommandProc CmdBuildRailroadTrack;
-CommandProc CmdRemoveRailroadTrack;
-CommandProc CmdBuildSingleRail;
-CommandProc CmdRemoveSingleRail;
-CommandProc CmdLandscapeClear;
+int RecursiveCommandCounter::_counter = 0;
-CommandProc CmdBuildBridge;
-CommandProc CmdBuildRailStation;
-CommandProc CmdRemoveFromRailStation;
-CommandProc CmdConvertRail;
+/**
+ * Define a command with the flags which belongs to it.
+ *
+ * This struct connects a command handler function with the flags created with
+ * the #CMD_AUTO, #CMD_OFFLINE and #CMD_SERVER values.
+ */
+struct CommandInfo {
+ const char *name; ///< A human readable name for the procedure
+ CommandFlags flags; ///< The (command) flags to that apply to this command
+ CommandType type; ///< The type of command.
+};
+/* Helpers to generate the master command table from the command traits. */
+template
+inline constexpr CommandInfo CommandFromTrait() noexcept { return { T::name, T::flags, T::type }; };
-CommandProc CmdBuildSingleSignal;
-CommandProc CmdRemoveSingleSignal;
-
-CommandProc CmdTerraformLand;
-
-CommandProc CmdBuildObject;
-CommandProc CmdSellLandArea;
-
-CommandProc CmdBuildTunnel;
-
-CommandProc CmdBuildTrainDepot;
-CommandProc CmdBuildRailWaypoint;
-CommandProc CmdRenameWaypoint;
-CommandProc CmdRemoveFromRailWaypoint;
-
-CommandProc CmdBuildRoadStop;
-CommandProc CmdRemoveRoadStop;
-
-CommandProc CmdBuildLongRoad;
-CommandProc CmdRemoveLongRoad;
-CommandProc CmdBuildRoad;
-
-CommandProc CmdBuildRoadDepot;
-
-CommandProc CmdConvertRoad;
-
-CommandProc CmdBuildAirport;
-
-CommandProc CmdBuildDock;
-
-CommandProc CmdBuildShipDepot;
-
-CommandProc CmdBuildBuoy;
-
-CommandProc CmdPlantTree;
-
-CommandProc CmdMoveRailVehicle;
-
-CommandProc CmdBuildVehicle;
-CommandProc CmdSellVehicle;
-CommandProc CmdRefitVehicle;
-CommandProc CmdSendVehicleToDepot;
-CommandProc CmdSetVehicleVisibility;
-
-CommandProc CmdForceTrainProceed;
-CommandProc CmdReverseTrainDirection;
-
-CommandProc CmdClearOrderBackup;
-CommandProc CmdModifyOrder;
-CommandProc CmdSkipToOrder;
-CommandProc CmdDeleteOrder;
-CommandProc CmdInsertOrder;
-CommandProc CmdChangeServiceInt;
-
-CommandProc CmdBuildIndustry;
-CommandProc CmdIndustryCtrl;
-
-CommandProc CmdSetCompanyManagerFace;
-CommandProc CmdSetCompanyColour;
-
-CommandProc CmdIncreaseLoan;
-CommandProc CmdDecreaseLoan;
-
-CommandProc CmdWantEnginePreview;
-CommandProc CmdEngineCtrl;
-
-CommandProc CmdRenameVehicle;
-CommandProc CmdRenameEngine;
-
-CommandProc CmdRenameCompany;
-CommandProc CmdRenamePresident;
-
-CommandProc CmdRenameStation;
-CommandProc CmdRenameDepot;
-
-CommandProc CmdPlaceSign;
-CommandProc CmdRenameSign;
-
-CommandProc CmdTurnRoadVeh;
-
-CommandProc CmdPause;
-
-CommandProc CmdBuyShareInCompany;
-CommandProc CmdSellShareInCompany;
-CommandProc CmdBuyCompany;
-
-CommandProc CmdFoundTown;
-CommandProc CmdRenameTown;
-CommandProc CmdDoTownAction;
-CommandProc CmdTownGrowthRate;
-CommandProc CmdTownRating;
-CommandProc CmdTownCargoGoal;
-CommandProc CmdTownSetText;
-CommandProc CmdExpandTown;
-CommandProc CmdDeleteTown;
-
-CommandProc CmdChangeSetting;
-CommandProc CmdChangeCompanySetting;
-
-CommandProc CmdOrderRefit;
-CommandProc CmdCloneOrder;
-
-CommandProc CmdClearArea;
-
-CommandProc CmdGiveMoney;
-CommandProc CmdMoneyCheat;
-CommandProc CmdChangeBankBalance;
-CommandProc CmdBuildCanal;
-CommandProc CmdBuildLock;
-
-CommandProc CmdCreateSubsidy;
-CommandProc CmdCompanyCtrl;
-CommandProc CmdCustomNewsItem;
-CommandProc CmdCreateGoal;
-CommandProc CmdRemoveGoal;
-CommandProc CmdSetGoalText;
-CommandProc CmdSetGoalProgress;
-CommandProc CmdSetGoalCompleted;
-CommandProc CmdGoalQuestion;
-CommandProc CmdGoalQuestionAnswer;
-CommandProc CmdCreateStoryPage;
-CommandProc CmdCreateStoryPageElement;
-CommandProc CmdUpdateStoryPageElement;
-CommandProc CmdSetStoryPageTitle;
-CommandProc CmdSetStoryPageDate;
-CommandProc CmdShowStoryPage;
-CommandProc CmdRemoveStoryPage;
-CommandProc CmdRemoveStoryPageElement;
-CommandProc CmdScrollViewport;
-CommandProc CmdStoryPageButton;
-
-CommandProc CmdLevelLand;
-
-CommandProc CmdBuildSignalTrack;
-CommandProc CmdRemoveSignalTrack;
-
-CommandProc CmdSetAutoReplace;
-
-CommandProc CmdCloneVehicle;
-CommandProc CmdStartStopVehicle;
-CommandProc CmdMassStartStopVehicle;
-CommandProc CmdAutoreplaceVehicle;
-CommandProc CmdDepotSellAllVehicles;
-CommandProc CmdDepotMassAutoReplace;
-
-CommandProc CmdCreateGroup;
-CommandProc CmdAlterGroup;
-CommandProc CmdDeleteGroup;
-CommandProc CmdAddVehicleGroup;
-CommandProc CmdAddSharedVehicleGroup;
-CommandProc CmdRemoveAllVehiclesGroup;
-CommandProc CmdSetGroupFlag;
-CommandProc CmdSetGroupLivery;
-
-CommandProc CmdMoveOrder;
-CommandProc CmdChangeTimetable;
-CommandProc CmdSetVehicleOnTime;
-CommandProc CmdAutofillTimetable;
-CommandProc CmdSetTimetableStart;
-
-CommandProc CmdOpenCloseAirport;
-
-#define DEF_CMD(proc, flags, type) {proc, #proc, (CommandFlags)flags, type}
+template
+inline constexpr auto MakeCommandsFromTraits(std::integer_sequence) noexcept {
+ return std::array{{ CommandFromTrait(i)>>()... }};
+}
/**
* The master command table
@@ -216,174 +96,18 @@ CommandProc CmdOpenCloseAirport;
* the flags which belongs to it. The indices are the same
* as the value from the CMD_* enums.
*/
-static const Command _command_proc_table[] = {
- DEF_CMD(CmdBuildRailroadTrack, CMD_NO_WATER | CMD_AUTO, CMDT_LANDSCAPE_CONSTRUCTION), // CMD_BUILD_RAILROAD_TRACK
- DEF_CMD(CmdRemoveRailroadTrack, CMD_AUTO, CMDT_LANDSCAPE_CONSTRUCTION), // CMD_REMOVE_RAILROAD_TRACK
- DEF_CMD(CmdBuildSingleRail, CMD_NO_WATER | CMD_AUTO, CMDT_LANDSCAPE_CONSTRUCTION), // CMD_BUILD_SINGLE_RAIL
- DEF_CMD(CmdRemoveSingleRail, CMD_AUTO, CMDT_LANDSCAPE_CONSTRUCTION), // CMD_REMOVE_SINGLE_RAIL
- DEF_CMD(CmdLandscapeClear, CMD_DEITY, CMDT_LANDSCAPE_CONSTRUCTION), // CMD_LANDSCAPE_CLEAR
- DEF_CMD(CmdBuildBridge, CMD_DEITY | CMD_NO_WATER | CMD_AUTO, CMDT_LANDSCAPE_CONSTRUCTION), // CMD_BUILD_BRIDGE
- DEF_CMD(CmdBuildRailStation, CMD_NO_WATER | CMD_AUTO, CMDT_LANDSCAPE_CONSTRUCTION), // CMD_BUILD_RAIL_STATION
- DEF_CMD(CmdBuildTrainDepot, CMD_NO_WATER | CMD_AUTO, CMDT_LANDSCAPE_CONSTRUCTION), // CMD_BUILD_TRAIN_DEPOT
- DEF_CMD(CmdBuildSingleSignal, CMD_AUTO, CMDT_LANDSCAPE_CONSTRUCTION), // CMD_BUILD_SIGNALS
- DEF_CMD(CmdRemoveSingleSignal, CMD_AUTO, CMDT_LANDSCAPE_CONSTRUCTION), // CMD_REMOVE_SIGNALS
- DEF_CMD(CmdTerraformLand, CMD_ALL_TILES | CMD_AUTO, CMDT_LANDSCAPE_CONSTRUCTION), // CMD_TERRAFORM_LAND
- DEF_CMD(CmdBuildObject, CMD_DEITY | CMD_NO_WATER | CMD_AUTO, CMDT_LANDSCAPE_CONSTRUCTION), // CMD_BUILD_OBJECT
- DEF_CMD(CmdBuildTunnel, CMD_DEITY | CMD_AUTO, CMDT_LANDSCAPE_CONSTRUCTION), // CMD_BUILD_TUNNEL
- DEF_CMD(CmdRemoveFromRailStation, 0, CMDT_LANDSCAPE_CONSTRUCTION), // CMD_REMOVE_FROM_RAIL_STATION
- DEF_CMD(CmdConvertRail, 0, CMDT_LANDSCAPE_CONSTRUCTION), // CMD_CONVERT_RAILD
- DEF_CMD(CmdBuildRailWaypoint, 0, CMDT_LANDSCAPE_CONSTRUCTION), // CMD_BUILD_RAIL_WAYPOINT
- DEF_CMD(CmdRenameWaypoint, 0, CMDT_OTHER_MANAGEMENT ), // CMD_RENAME_WAYPOINT
- DEF_CMD(CmdRemoveFromRailWaypoint, 0, CMDT_LANDSCAPE_CONSTRUCTION), // CMD_REMOVE_FROM_RAIL_WAYPOINT
+static constexpr auto _command_proc_table = MakeCommandsFromTraits(std::make_integer_sequence, CMD_END>{});
- DEF_CMD(CmdBuildRoadStop, CMD_NO_WATER | CMD_AUTO, CMDT_LANDSCAPE_CONSTRUCTION), // CMD_BUILD_ROAD_STOP
- DEF_CMD(CmdRemoveRoadStop, 0, CMDT_LANDSCAPE_CONSTRUCTION), // CMD_REMOVE_ROAD_STOP
- DEF_CMD(CmdBuildLongRoad,CMD_DEITY | CMD_NO_WATER | CMD_AUTO, CMDT_LANDSCAPE_CONSTRUCTION), // CMD_BUILD_LONG_ROAD
- DEF_CMD(CmdRemoveLongRoad, CMD_NO_TEST | CMD_AUTO, CMDT_LANDSCAPE_CONSTRUCTION), // CMD_REMOVE_LONG_ROAD; towns may disallow removing road bits (as they are connected) in test, but in exec they're removed and thus removing is allowed.
- DEF_CMD(CmdBuildRoad, CMD_DEITY | CMD_NO_WATER | CMD_AUTO, CMDT_LANDSCAPE_CONSTRUCTION), // CMD_BUILD_ROAD
- DEF_CMD(CmdBuildRoadDepot, CMD_NO_WATER | CMD_AUTO, CMDT_LANDSCAPE_CONSTRUCTION), // CMD_BUILD_ROAD_DEPOT
- DEF_CMD(CmdConvertRoad, 0, CMDT_LANDSCAPE_CONSTRUCTION), // CMD_CONVERT_ROAD
-
- DEF_CMD(CmdBuildAirport, CMD_NO_WATER | CMD_AUTO, CMDT_LANDSCAPE_CONSTRUCTION), // CMD_BUILD_AIRPORT
- DEF_CMD(CmdBuildDock, CMD_AUTO, CMDT_LANDSCAPE_CONSTRUCTION), // CMD_BUILD_DOCK
- DEF_CMD(CmdBuildShipDepot, CMD_AUTO, CMDT_LANDSCAPE_CONSTRUCTION), // CMD_BUILD_SHIP_DEPOT
- DEF_CMD(CmdBuildBuoy, CMD_AUTO, CMDT_LANDSCAPE_CONSTRUCTION), // CMD_BUILD_BUOY
- DEF_CMD(CmdPlantTree, CMD_AUTO, CMDT_LANDSCAPE_CONSTRUCTION), // CMD_PLANT_TREE
-
- DEF_CMD(CmdBuildVehicle, CMD_CLIENT_ID, CMDT_VEHICLE_CONSTRUCTION ), // CMD_BUILD_VEHICLE
- DEF_CMD(CmdSellVehicle, CMD_CLIENT_ID, CMDT_VEHICLE_CONSTRUCTION ), // CMD_SELL_VEHICLE
- DEF_CMD(CmdRefitVehicle, 0, CMDT_VEHICLE_CONSTRUCTION ), // CMD_REFIT_VEHICLE
- DEF_CMD(CmdSendVehicleToDepot, 0, CMDT_VEHICLE_MANAGEMENT ), // CMD_SEND_VEHICLE_TO_DEPOT
- DEF_CMD(CmdSetVehicleVisibility, 0, CMDT_COMPANY_SETTING ), // CMD_SET_VEHICLE_VISIBILITY
-
- DEF_CMD(CmdMoveRailVehicle, 0, CMDT_VEHICLE_CONSTRUCTION ), // CMD_MOVE_RAIL_VEHICLE
- DEF_CMD(CmdForceTrainProceed, 0, CMDT_VEHICLE_MANAGEMENT ), // CMD_FORCE_TRAIN_PROCEED
- DEF_CMD(CmdReverseTrainDirection, 0, CMDT_VEHICLE_MANAGEMENT ), // CMD_REVERSE_TRAIN_DIRECTION
-
- DEF_CMD(CmdClearOrderBackup, CMD_CLIENT_ID, CMDT_SERVER_SETTING ), // CMD_CLEAR_ORDER_BACKUP
- DEF_CMD(CmdModifyOrder, 0, CMDT_ROUTE_MANAGEMENT ), // CMD_MODIFY_ORDER
- DEF_CMD(CmdSkipToOrder, 0, CMDT_ROUTE_MANAGEMENT ), // CMD_SKIP_TO_ORDER
- DEF_CMD(CmdDeleteOrder, 0, CMDT_ROUTE_MANAGEMENT ), // CMD_DELETE_ORDER
- DEF_CMD(CmdInsertOrder, 0, CMDT_ROUTE_MANAGEMENT ), // CMD_INSERT_ORDER
-
- DEF_CMD(CmdChangeServiceInt, 0, CMDT_VEHICLE_MANAGEMENT ), // CMD_CHANGE_SERVICE_INT
-
- DEF_CMD(CmdBuildIndustry, CMD_DEITY, CMDT_LANDSCAPE_CONSTRUCTION), // CMD_BUILD_INDUSTRY
- DEF_CMD(CmdIndustryCtrl, CMD_STR_CTRL | CMD_DEITY, CMDT_OTHER_MANAGEMENT ), // CMD_INDUSTRY_CTRL
-
- DEF_CMD(CmdSetCompanyManagerFace, 0, CMDT_OTHER_MANAGEMENT ), // CMD_SET_COMPANY_MANAGER_FACE
- DEF_CMD(CmdSetCompanyColour, 0, CMDT_OTHER_MANAGEMENT ), // CMD_SET_COMPANY_COLOUR
-
- DEF_CMD(CmdIncreaseLoan, 0, CMDT_MONEY_MANAGEMENT ), // CMD_INCREASE_LOAN
- DEF_CMD(CmdDecreaseLoan, 0, CMDT_MONEY_MANAGEMENT ), // CMD_DECREASE_LOAN
-
- DEF_CMD(CmdWantEnginePreview, 0, CMDT_VEHICLE_MANAGEMENT ), // CMD_WANT_ENGINE_PREVIEW
- DEF_CMD(CmdEngineCtrl, CMD_DEITY, CMDT_VEHICLE_MANAGEMENT ), // CMD_ENGINE_CTRL
-
- DEF_CMD(CmdRenameVehicle, 0, CMDT_OTHER_MANAGEMENT ), // CMD_RENAME_VEHICLE
- DEF_CMD(CmdRenameEngine, CMD_SERVER, CMDT_OTHER_MANAGEMENT ), // CMD_RENAME_ENGINE
-
- DEF_CMD(CmdRenameCompany, 0, CMDT_OTHER_MANAGEMENT ), // CMD_RENAME_COMPANY
- DEF_CMD(CmdRenamePresident, 0, CMDT_OTHER_MANAGEMENT ), // CMD_RENAME_PRESIDENT
-
- DEF_CMD(CmdRenameStation, 0, CMDT_OTHER_MANAGEMENT ), // CMD_RENAME_STATION
- DEF_CMD(CmdRenameDepot, 0, CMDT_OTHER_MANAGEMENT ), // CMD_RENAME_DEPOT
-
- DEF_CMD(CmdPlaceSign, CMD_DEITY, CMDT_OTHER_MANAGEMENT ), // CMD_PLACE_SIGN
- DEF_CMD(CmdRenameSign, CMD_DEITY, CMDT_OTHER_MANAGEMENT ), // CMD_RENAME_SIGN
-
- DEF_CMD(CmdTurnRoadVeh, 0, CMDT_VEHICLE_MANAGEMENT ), // CMD_TURN_ROADVEH
-
- DEF_CMD(CmdPause, CMD_SERVER | CMD_NO_EST, CMDT_SERVER_SETTING ), // CMD_PAUSE
-
- DEF_CMD(CmdBuyShareInCompany, 0, CMDT_MONEY_MANAGEMENT ), // CMD_BUY_SHARE_IN_COMPANY
- DEF_CMD(CmdSellShareInCompany, 0, CMDT_MONEY_MANAGEMENT ), // CMD_SELL_SHARE_IN_COMPANY
- DEF_CMD(CmdBuyCompany, 0, CMDT_MONEY_MANAGEMENT ), // CMD_BUY_COMANY
-
- DEF_CMD(CmdFoundTown, CMD_DEITY | CMD_NO_TEST, CMDT_LANDSCAPE_CONSTRUCTION), // CMD_FOUND_TOWN; founding random town can fail only in exec run
- DEF_CMD(CmdRenameTown, CMD_DEITY | CMD_SERVER, CMDT_OTHER_MANAGEMENT ), // CMD_RENAME_TOWN
- DEF_CMD(CmdDoTownAction, 0, CMDT_LANDSCAPE_CONSTRUCTION), // CMD_DO_TOWN_ACTION
- DEF_CMD(CmdTownCargoGoal, CMD_DEITY, CMDT_OTHER_MANAGEMENT ), // CMD_TOWN_CARGO_GOAL
- DEF_CMD(CmdTownGrowthRate, CMD_DEITY, CMDT_OTHER_MANAGEMENT ), // CMD_TOWN_GROWTH_RATE
- DEF_CMD(CmdTownRating, CMD_DEITY, CMDT_OTHER_MANAGEMENT ), // CMD_TOWN_RATING
- DEF_CMD(CmdTownSetText, CMD_STR_CTRL | CMD_DEITY, CMDT_OTHER_MANAGEMENT ), // CMD_TOWN_SET_TEXT
- DEF_CMD(CmdExpandTown, CMD_DEITY, CMDT_LANDSCAPE_CONSTRUCTION), // CMD_EXPAND_TOWN
- DEF_CMD(CmdDeleteTown, CMD_OFFLINE, CMDT_LANDSCAPE_CONSTRUCTION), // CMD_DELETE_TOWN
-
- DEF_CMD(CmdOrderRefit, 0, CMDT_ROUTE_MANAGEMENT ), // CMD_ORDER_REFIT
- DEF_CMD(CmdCloneOrder, 0, CMDT_ROUTE_MANAGEMENT ), // CMD_CLONE_ORDER
-
- DEF_CMD(CmdClearArea, CMD_NO_TEST, CMDT_LANDSCAPE_CONSTRUCTION), // CMD_CLEAR_AREA; destroying multi-tile houses makes town rating differ between test and execution
-
- DEF_CMD(CmdMoneyCheat, CMD_OFFLINE, CMDT_CHEAT ), // CMD_MONEY_CHEAT
- DEF_CMD(CmdChangeBankBalance, CMD_DEITY, CMDT_MONEY_MANAGEMENT ), // CMD_CHANGE_BANK_BALANCE
- DEF_CMD(CmdBuildCanal, CMD_AUTO, CMDT_LANDSCAPE_CONSTRUCTION), // CMD_BUILD_CANAL
- DEF_CMD(CmdCreateSubsidy, CMD_DEITY, CMDT_OTHER_MANAGEMENT ), // CMD_CREATE_SUBSIDY
- DEF_CMD(CmdCompanyCtrl, CMD_SPECTATOR | CMD_CLIENT_ID | CMD_NO_EST, CMDT_SERVER_SETTING ), // CMD_COMPANY_CTRL
- DEF_CMD(CmdCustomNewsItem, CMD_STR_CTRL | CMD_DEITY, CMDT_OTHER_MANAGEMENT ), // CMD_CUSTOM_NEWS_ITEM
- DEF_CMD(CmdCreateGoal, CMD_STR_CTRL | CMD_DEITY, CMDT_OTHER_MANAGEMENT ), // CMD_CREATE_GOAL
- DEF_CMD(CmdRemoveGoal, CMD_DEITY, CMDT_OTHER_MANAGEMENT ), // CMD_REMOVE_GOAL
- DEF_CMD(CmdSetGoalText, CMD_STR_CTRL | CMD_DEITY, CMDT_OTHER_MANAGEMENT ), // CMD_SET_GOAL_TEXT
- DEF_CMD(CmdSetGoalProgress, CMD_STR_CTRL | CMD_DEITY, CMDT_OTHER_MANAGEMENT ), // CMD_SET_GOAL_PROGRESS
- DEF_CMD(CmdSetGoalCompleted, CMD_STR_CTRL | CMD_DEITY, CMDT_OTHER_MANAGEMENT ), // CMD_SET_GOAL_COMPLETED
- DEF_CMD(CmdGoalQuestion, CMD_STR_CTRL | CMD_DEITY, CMDT_OTHER_MANAGEMENT ), // CMD_GOAL_QUESTION
- DEF_CMD(CmdGoalQuestionAnswer, CMD_DEITY, CMDT_OTHER_MANAGEMENT ), // CMD_GOAL_QUESTION_ANSWER
- DEF_CMD(CmdCreateStoryPage, CMD_STR_CTRL | CMD_DEITY, CMDT_OTHER_MANAGEMENT ), // CMD_CREATE_STORY_PAGE
- DEF_CMD(CmdCreateStoryPageElement, CMD_STR_CTRL | CMD_DEITY, CMDT_OTHER_MANAGEMENT ), // CMD_CREATE_STORY_PAGE_ELEMENT
- DEF_CMD(CmdUpdateStoryPageElement, CMD_STR_CTRL | CMD_DEITY, CMDT_OTHER_MANAGEMENT ), // CMD_UPDATE_STORY_PAGE_ELEMENT
- DEF_CMD(CmdSetStoryPageTitle, CMD_STR_CTRL | CMD_DEITY, CMDT_OTHER_MANAGEMENT ), // CMD_SET_STORY_PAGE_TITLE
- DEF_CMD(CmdSetStoryPageDate, CMD_DEITY, CMDT_OTHER_MANAGEMENT ), // CMD_SET_STORY_PAGE_DATE
- DEF_CMD(CmdShowStoryPage, CMD_DEITY, CMDT_OTHER_MANAGEMENT ), // CMD_SHOW_STORY_PAGE
- DEF_CMD(CmdRemoveStoryPage, CMD_DEITY, CMDT_OTHER_MANAGEMENT ), // CMD_REMOVE_STORY_PAGE
- DEF_CMD(CmdRemoveStoryPageElement, CMD_DEITY, CMDT_OTHER_MANAGEMENT ), // CMD_REMOVE_STORY_ELEMENT_PAGE
- DEF_CMD(CmdScrollViewport, CMD_DEITY, CMDT_OTHER_MANAGEMENT ), // CMD_SCROLL_VIEWPORT
- DEF_CMD(CmdStoryPageButton, CMD_DEITY, CMDT_OTHER_MANAGEMENT ), // CMD_STORY_PAGE_BUTTON
-
- DEF_CMD(CmdLevelLand, CMD_ALL_TILES | CMD_NO_TEST | CMD_AUTO, CMDT_LANDSCAPE_CONSTRUCTION), // CMD_LEVEL_LAND; test run might clear tiles multiple times, in execution that only happens once
-
- DEF_CMD(CmdBuildLock, CMD_AUTO, CMDT_LANDSCAPE_CONSTRUCTION), // CMD_BUILD_LOCK
-
- DEF_CMD(CmdBuildSignalTrack, CMD_AUTO, CMDT_LANDSCAPE_CONSTRUCTION), // CMD_BUILD_SIGNAL_TRACK
- DEF_CMD(CmdRemoveSignalTrack, CMD_AUTO, CMDT_LANDSCAPE_CONSTRUCTION), // CMD_REMOVE_SIGNAL_TRACK
-
- DEF_CMD(CmdGiveMoney, 0, CMDT_MONEY_MANAGEMENT ), // CMD_GIVE_MONEY
- DEF_CMD(CmdChangeSetting, CMD_SERVER, CMDT_SERVER_SETTING ), // CMD_CHANGE_SETTING
- DEF_CMD(CmdChangeCompanySetting, 0, CMDT_COMPANY_SETTING ), // CMD_CHANGE_COMPANY_SETTING
- DEF_CMD(CmdSetAutoReplace, 0, CMDT_VEHICLE_MANAGEMENT ), // CMD_SET_AUTOREPLACE
- DEF_CMD(CmdCloneVehicle, CMD_NO_TEST, CMDT_VEHICLE_CONSTRUCTION ), // CMD_CLONE_VEHICLE; NewGRF callbacks influence building and refitting making it impossible to correctly estimate the cost
- DEF_CMD(CmdStartStopVehicle, 0, CMDT_VEHICLE_MANAGEMENT ), // CMD_START_STOP_VEHICLE
- DEF_CMD(CmdMassStartStopVehicle, 0, CMDT_VEHICLE_MANAGEMENT ), // CMD_MASS_START_STOP
- DEF_CMD(CmdAutoreplaceVehicle, 0, CMDT_VEHICLE_MANAGEMENT ), // CMD_AUTOREPLACE_VEHICLE
- DEF_CMD(CmdDepotSellAllVehicles, 0, CMDT_VEHICLE_CONSTRUCTION ), // CMD_DEPOT_SELL_ALL_VEHICLES
- DEF_CMD(CmdDepotMassAutoReplace, 0, CMDT_VEHICLE_CONSTRUCTION ), // CMD_DEPOT_MASS_AUTOREPLACE
- DEF_CMD(CmdCreateGroup, 0, CMDT_ROUTE_MANAGEMENT ), // CMD_CREATE_GROUP
- DEF_CMD(CmdDeleteGroup, 0, CMDT_ROUTE_MANAGEMENT ), // CMD_DELETE_GROUP
- DEF_CMD(CmdAlterGroup, 0, CMDT_OTHER_MANAGEMENT ), // CMD_ALTER_GROUP
- DEF_CMD(CmdAddVehicleGroup, 0, CMDT_ROUTE_MANAGEMENT ), // CMD_ADD_VEHICLE_GROUP
- DEF_CMD(CmdAddSharedVehicleGroup, 0, CMDT_ROUTE_MANAGEMENT ), // CMD_ADD_SHARE_VEHICLE_GROUP
- DEF_CMD(CmdRemoveAllVehiclesGroup, 0, CMDT_ROUTE_MANAGEMENT ), // CMD_REMOVE_ALL_VEHICLES_GROUP
- DEF_CMD(CmdSetGroupFlag, 0, CMDT_ROUTE_MANAGEMENT ), // CMD_SET_GROUP_FLAG
- DEF_CMD(CmdSetGroupLivery, 0, CMDT_ROUTE_MANAGEMENT ), // CMD_SET_GROUP_LIVERY
- DEF_CMD(CmdMoveOrder, 0, CMDT_ROUTE_MANAGEMENT ), // CMD_MOVE_ORDER
- DEF_CMD(CmdChangeTimetable, 0, CMDT_ROUTE_MANAGEMENT ), // CMD_CHANGE_TIMETABLE
- DEF_CMD(CmdSetVehicleOnTime, 0, CMDT_ROUTE_MANAGEMENT ), // CMD_SET_VEHICLE_ON_TIME
- DEF_CMD(CmdAutofillTimetable, 0, CMDT_ROUTE_MANAGEMENT ), // CMD_AUTOFILL_TIMETABLE
- DEF_CMD(CmdSetTimetableStart, 0, CMDT_ROUTE_MANAGEMENT ), // CMD_SET_TIMETABLE_START
-
- DEF_CMD(CmdOpenCloseAirport, 0, CMDT_ROUTE_MANAGEMENT ), // CMD_OPEN_CLOSE_AIRPORT
-};
/*!
- * This function range-checks a cmd, and checks if the cmd is not nullptr
+ * This function range-checks a cmd.
*
* @param cmd The integer value of a command
* @return true if the command is valid (and got a CommandProc function)
*/
-bool IsValidCommand(uint32 cmd)
+bool IsValidCommand(Commands cmd)
{
- cmd &= CMD_ID_MASK;
-
- return cmd < lengthof(_command_proc_table) && _command_proc_table[cmd].proc != nullptr;
+ return cmd < _command_proc_table.size();
}
/*!
@@ -393,11 +117,11 @@ bool IsValidCommand(uint32 cmd)
* @param cmd The integer value of the command
* @return The flags for this command
*/
-CommandFlags GetCommandFlags(uint32 cmd)
+CommandFlags GetCommandFlags(Commands cmd)
{
assert(IsValidCommand(cmd));
- return _command_proc_table[cmd & CMD_ID_MASK].flags;
+ return _command_proc_table[cmd].flags;
}
/*!
@@ -407,11 +131,11 @@ CommandFlags GetCommandFlags(uint32 cmd)
* @param cmd The integer value of the command
* @return The name for this command
*/
-const char *GetCommandName(uint32 cmd)
+const char *GetCommandName(Commands cmd)
{
assert(IsValidCommand(cmd));
- return _command_proc_table[cmd & CMD_ID_MASK].name;
+ return _command_proc_table[cmd].name;
}
/**
@@ -419,7 +143,7 @@ const char *GetCommandName(uint32 cmd)
* @param cmd The command to check.
* @return True if the command is allowed while paused, false otherwise.
*/
-bool IsCommandAllowedWhilePaused(uint32 cmd)
+bool IsCommandAllowedWhilePaused(Commands cmd)
{
/* Lookup table for the command types that are allowed for a given pause level setting. */
static const int command_type_lookup[] = {
@@ -436,89 +160,7 @@ bool IsCommandAllowedWhilePaused(uint32 cmd)
static_assert(lengthof(command_type_lookup) == CMDT_END);
assert(IsValidCommand(cmd));
- return _game_mode == GM_EDITOR || command_type_lookup[_command_proc_table[cmd & CMD_ID_MASK].type] <= _settings_game.construction.command_pause_level;
-}
-
-
-static int _docommand_recursive = 0;
-
-/**
- * Shorthand for calling the long DoCommand with a container.
- *
- * @param container Container with (almost) all information
- * @param flags Flags for the command and how to execute the command
- * @see CommandProc
- * @return the cost
- */
-CommandCost DoCommand(const CommandContainer *container, DoCommandFlag flags)
-{
- return DoCommand(container->tile, container->p1, container->p2, flags, container->cmd & CMD_ID_MASK, container->text);
-}
-
-/*!
- * This function executes a given command with the parameters from the #CommandProc parameter list.
- * Depending on the flags parameter it execute or test a command.
- *
- * @param tile The tile to apply the command on (for the #CommandProc)
- * @param p1 Additional data for the command (for the #CommandProc)
- * @param p2 Additional data for the command (for the #CommandProc)
- * @param flags Flags for the command and how to execute the command
- * @param cmd The command-id to execute (a value of the CMD_* enums)
- * @param text The text to pass
- * @see CommandProc
- * @return the cost
- */
-CommandCost DoCommand(TileIndex tile, uint32 p1, uint32 p2, DoCommandFlag flags, uint32 cmd, const std::string &text)
-{
- CommandCost res;
-
- /* Do not even think about executing out-of-bounds tile-commands */
- if (tile != 0 && (tile >= MapSize() || (!IsValidTile(tile) && (flags & DC_ALL_TILES) == 0))) return CMD_ERROR;
-
- /* Chop of any CMD_MSG or other flags; we don't need those here */
- CommandProc *proc = _command_proc_table[cmd & CMD_ID_MASK].proc;
-
- _docommand_recursive++;
-
- /* only execute the test call if it's toplevel, or we're not execing. */
- if (_docommand_recursive == 1 || !(flags & DC_EXEC) ) {
- if (_docommand_recursive == 1) _cleared_object_areas.clear();
- SetTownRatingTestMode(true);
- res = proc(tile, flags & ~DC_EXEC, p1, p2, text);
- SetTownRatingTestMode(false);
- if (res.Failed()) {
- goto error;
- }
-
- if (_docommand_recursive == 1 &&
- !(flags & DC_QUERY_COST) &&
- !(flags & DC_BANKRUPT) &&
- !CheckCompanyHasMoney(res)) { // CheckCompanyHasMoney() modifies 'res' to an error if it fails.
- goto error;
- }
-
- if (!(flags & DC_EXEC)) {
- _docommand_recursive--;
- return res;
- }
- }
-
- /* Execute the command here. All cost-relevant functions set the expenses type
- * themselves to the cost object at some point */
- if (_docommand_recursive == 1) _cleared_object_areas.clear();
- res = proc(tile, flags, p1, p2, text);
- if (res.Failed()) {
-error:
- _docommand_recursive--;
- return res;
- }
-
- /* if toplevel, subtract the money. */
- if (--_docommand_recursive == 0 && !(flags & DC_BANKRUPT)) {
- SubtractMoneyFromCompany(res);
- }
-
- return res;
+ return _game_mode == GM_EDITOR || command_type_lookup[_command_proc_table[cmd].type] <= _settings_game.construction.command_pause_level;
}
/*!
@@ -535,77 +177,93 @@ Money GetAvailableMoneyForCommand()
return Company::Get(company)->money;
}
+
/**
- * Shortcut for the long DoCommandP when having a container with the data.
- * @param container the container with information.
- * @param my_cmd indicator if the command is from a company or server (to display error messages for a user)
- * @return true if the command succeeded, else false
+ * Prepare for calling a command proc.
+ * @param top_level Top level of command execution, i.e. command from a command.
+ * @param test Test run of command?
*/
-bool DoCommandP(const CommandContainer *container, bool my_cmd)
+void CommandHelperBase::InternalDoBefore(bool top_level, bool test)
{
- return DoCommandP(container->tile, container->p1, container->p2, container->cmd, container->callback, container->text, my_cmd);
+ if (top_level) _cleared_object_areas.clear();
+ if (test) SetTownRatingTestMode(true);
}
-/*!
- * Toplevel network safe docommand function for the current company. Must not be called recursively.
- * The callback is called when the command succeeded or failed. The parameters
- * \a tile, \a p1, and \a p2 are from the #CommandProc function. The parameter \a cmd is the command to execute.
- * The parameter \a my_cmd is used to indicate if the command is from a company or the server.
- *
- * @param tile The tile to perform a command on (see #CommandProc)
- * @param p1 Additional data for the command (see #CommandProc)
- * @param p2 Additional data for the command (see #CommandProc)
- * @param cmd The command to execute (a CMD_* value)
- * @param callback A callback function to call after the command is finished
- * @param text The text to pass
- * @param my_cmd indicator if the command is from a company or server (to display error messages for a user)
- * @return \c true if the command succeeded, else \c false.
+/**
+ * Process result after calling a command proc.
+ * @param[in,out] res Command result, may be modified.
+ * @param flags Command flags.
+ * @param top_level Top level of command execution, i.e. command from a command.
+ * @param test Test run of command?
*/
-bool DoCommandP(TileIndex tile, uint32 p1, uint32 p2, uint32 cmd, CommandCallback *callback, const std::string &text, bool my_cmd)
+void CommandHelperBase::InternalDoAfter(CommandCost &res, DoCommandFlag flags, bool top_level, bool test)
+{
+ if (test) {
+ SetTownRatingTestMode(false);
+
+ if (res.Succeeded() && top_level && !(flags & DC_QUERY_COST) && !(flags & DC_BANKRUPT)) {
+ CheckCompanyHasMoney(res); // CheckCompanyHasMoney() modifies 'res' to an error if it fails.
+ }
+ } else {
+ /* If top-level, subtract the money. */
+ if (res.Succeeded() && top_level && !(flags & DC_BANKRUPT)) {
+ SubtractMoneyFromCompany(res);
+ }
+ }
+}
+
+/**
+ * Decide what to do with the command depending on current game state.
+ * @param cmd Command to execute.
+ * @param flags Command flags.
+ * @param tile Tile of command execution.
+ * @param err_message Message prefix to show on error.
+ * @param network_command Does this command come from the network?
+ * @return error state + do only cost estimation? + send to network only?
+ */
+std::tuple CommandHelperBase::InternalPostBefore(Commands cmd, CommandFlags flags, TileIndex tile, StringID err_message, bool network_command)
{
/* Cost estimation is generally only done when the
* local user presses shift while doing something.
* However, in case of incoming network commands,
* map generation or the pause button we do want
* to execute. */
- bool estimate_only = (_shift_pressed || ConfirmationWindowEstimatingCost()) &&
- IsLocalCompany() &&
- !_generating_world &&
- !(cmd & CMD_NETWORK_COMMAND) &&
- !(GetCommandFlags(cmd) & CMD_NO_EST);
-
- if (ConfirmationWindowEstimatingCost() && !estimate_only) {
- // We cannot estimate cost, so abort the command - it will be repeated by confirmation dialog later
- ShowEstimatedCostOrIncome(0, 0, 0);
- return false;
- }
+ bool estimate_only = (_shift_pressed || ConfirmationWindowEstimatingCost()) && IsLocalCompany() && !_generating_world && !network_command && !(flags & CMD_NO_EST);
/* We're only sending the command, so don't do
* fancy things for 'success'. */
- bool only_sending = _networking && !(cmd & CMD_NETWORK_COMMAND);
+ bool only_sending = _networking && !network_command;
- /* Where to show the message? */
+ if (_pause_mode != PM_UNPAUSED && !IsCommandAllowedWhilePaused(cmd) && !estimate_only) {
+ ShowErrorMessage(err_message, STR_ERROR_NOT_ALLOWED_WHILE_PAUSED, WL_INFO, TileX(tile) * TILE_SIZE, TileY(tile) * TILE_SIZE);
+ return { true, estimate_only, only_sending };
+ } else {
+ return { false, estimate_only, only_sending };
+ }
+}
+
+/**
+ * Process result of executing a command, possibly displaying any error to the player.
+ * @param res Command result.
+ * @param tile Tile of command execution.
+ * @param estimate_only Is this just cost estimation?
+ * @param only_sending Was the command only sent to network?
+ * @param err_message Message prefix to show on error.
+ * @param my_cmd Is the command from this client?
+ */
+void CommandHelperBase::InternalPostResult(const CommandCost &res, TileIndex tile, bool estimate_only, bool only_sending, StringID err_message, bool my_cmd)
+{
int x = TileX(tile) * TILE_SIZE;
int y = TileY(tile) * TILE_SIZE;
- if (_pause_mode != PM_UNPAUSED && !IsCommandAllowedWhilePaused(cmd) && !estimate_only) {
- ShowErrorMessage(GB(cmd, 16, 16), STR_ERROR_NOT_ALLOWED_WHILE_PAUSED, WL_INFO, x, y);
- return false;
- }
-
- /* Only set p2 when the command does not come from the network. */
- if (!(cmd & CMD_NETWORK_COMMAND) && GetCommandFlags(cmd) & CMD_CLIENT_ID && p2 == 0) p2 = CLIENT_ID_SERVER;
-
- CommandCost res = DoCommandPInternal(tile, p1, p2, cmd, callback, text, my_cmd, estimate_only);
if (res.Failed()) {
/* Only show the error when it's for us. */
- StringID error_part1 = GB(cmd, 16, 16);
- if (estimate_only || (IsLocalCompany() && error_part1 != 0 && my_cmd)) {
- ShowErrorMessage(error_part1, res.GetErrorMessage(), WL_INFO, x, y, res.GetTextRefStackGRF(), res.GetTextRefStackSize(), res.GetTextRefStack());
+ if (estimate_only || (IsLocalCompany() && err_message != 0 && my_cmd)) {
+ ShowErrorMessage(err_message, res.GetErrorMessage(), WL_INFO, x, y, res.GetTextRefStackGRF(), res.GetTextRefStackSize(), res.GetTextRefStack());
}
} else if (estimate_only) {
ShowEstimatedCostOrIncome(res.GetCost(), x, y);
- } else if (!only_sending && res.GetCost() != 0 && tile != 0 && IsLocalCompany() && _game_mode != GM_EDITOR) {
+ } else if (!only_sending && tile != 0 && IsLocalCompany() && _game_mode != GM_EDITOR) {
/* Only show the cost animation when we did actually
* execute the command, i.e. we're not sending it to
* the server, when it has cost the local company
@@ -613,63 +271,23 @@ bool DoCommandP(TileIndex tile, uint32 p1, uint32 p2, uint32 cmd, CommandCallbac
* concept of cost, so don't show it there either. */
ShowCostOrIncomeAnimation(x, y, GetSlopePixelZ(x, y), res.GetCost());
}
-
- if (!estimate_only && !only_sending && callback != nullptr) {
- callback(res, tile, p1, p2, cmd);
- }
-
- return res.Succeeded();
}
+/** Helper to make a desync log for a command. */
+void CommandHelperBase::LogCommandExecution(Commands cmd, StringID err_message, TileIndex tile, const CommandDataBuffer &args, bool failed)
+{
+ Debug(desync, 1, "{}: {:08x}; {:02x}; {:02x}; {:08x}; {:08x}; {:06x}; {} ({})", failed ? "cmdf" : "cmd", _date, _date_fract, (int)_current_company, cmd, err_message, tile, FormatArrayAsHex(args), GetCommandName(cmd));
+}
/**
- * Helper to deduplicate the code for returning.
- * @param cmd the command cost to return.
+ * Prepare for the test run of a command proc call.
+ * @param cmd_flags Command flags.
+ * @param tile Tile of command execution.
+ * @param[in,out] cur_company Backup of current company at start of command execution.
+ * @return True if test run can go ahead, false on error.
*/
-#define return_dcpi(cmd) { _docommand_recursive = 0; return cmd; }
-
-/*!
- * Helper function for the toplevel network safe docommand function for the current company.
- *
- * @param tile The tile to perform a command on (see #CommandProc)
- * @param p1 Additional data for the command (see #CommandProc)
- * @param p2 Additional data for the command (see #CommandProc)
- * @param cmd The command to execute (a CMD_* value)
- * @param callback A callback function to call after the command is finished
- * @param text The text to pass
- * @param my_cmd indicator if the command is from a company or server (to display error messages for a user)
- * @param estimate_only whether to give only the estimate or also execute the command
- * @return the command cost of this function.
- */
-CommandCost DoCommandPInternal(TileIndex tile, uint32 p1, uint32 p2, uint32 cmd, CommandCallback *callback, const std::string &text, bool my_cmd, bool estimate_only)
+bool CommandHelperBase::InternalExecutePrepTest(CommandFlags cmd_flags, TileIndex tile, Backup &cur_company)
{
- /* Prevent recursion; it gives a mess over the network */
- assert(_docommand_recursive == 0);
- _docommand_recursive = 1;
-
- /* Reset the state. */
- _additional_cash_required = 0;
-
- /* Get pointer to command handler */
- byte cmd_id = cmd & CMD_ID_MASK;
- assert(cmd_id < lengthof(_command_proc_table));
-
- CommandProc *proc = _command_proc_table[cmd_id].proc;
- /* Shouldn't happen, but you never know when someone adds
- * NULLs to the _command_proc_table. */
- assert(proc != nullptr);
-
- /* Command flags are used internally */
- CommandFlags cmd_flags = GetCommandFlags(cmd);
- /* Flags get send to the DoCommand */
- DoCommandFlag flags = CommandFlagsToDCFlags(cmd_flags);
-
- /* Make sure p2 is properly set to a ClientID. */
- assert(!(cmd_flags & CMD_CLIENT_ID) || p2 != 0);
-
- /* Do not even think about executing out-of-bounds tile-commands */
- if (tile != 0 && (tile >= MapSize() || (!IsValidTile(tile) && (cmd_flags & CMD_ALL_TILES) == 0))) return_dcpi(CMD_ERROR);
-
/* Always execute server and spectator commands as spectator */
bool exec_as_spectator = (cmd_flags & (CMD_SPECTATOR | CMD_SERVER)) != 0;
@@ -677,65 +295,72 @@ CommandCost DoCommandPInternal(TileIndex tile, uint32 p1, uint32 p2, uint32 cmd,
* The server will ditch any server commands a client sends to it, so effectively
* this guards the server from executing functions for an invalid company. */
if (_game_mode == GM_NORMAL && !exec_as_spectator && !Company::IsValidID(_current_company) && !(_current_company == OWNER_DEITY && (cmd_flags & CMD_DEITY) != 0)) {
- return_dcpi(CMD_ERROR);
+ return false;
}
- Backup cur_company(_current_company, FILE_LINE);
if (exec_as_spectator) cur_company.Change(COMPANY_SPECTATOR);
- bool test_and_exec_can_differ = (cmd_flags & CMD_NO_TEST) != 0;
-
- /* Test the command. */
+ /* Enter test mode. */
_cleared_object_areas.clear();
SetTownRatingTestMode(true);
BasePersistentStorageArray::SwitchMode(PSM_ENTER_TESTMODE);
- CommandCost res = proc(tile, flags, p1, p2, text);
+ return true;
+}
+
+/**
+ * Validate result of test run and prepare for real execution.
+ * @param cmd_flags Command flags.
+ * @param[in,out] res Command result of test run, may be modified.
+ * @param estimate_only Is this just cost estimation?
+ * @param network_command Does this command come from the network?
+ * @param[in,out] cur_company Backup of current company at start of command execution.
+ * @return True if test run can go ahead, false on error.
+ */
+std::tuple CommandHelperBase::InternalExecuteValidateTestAndPrepExec(CommandCost &res, CommandFlags cmd_flags, bool estimate_only, bool network_command, Backup &cur_company)
+{
BasePersistentStorageArray::SwitchMode(PSM_LEAVE_TESTMODE);
SetTownRatingTestMode(false);
/* Make sure we're not messing things up here. */
- assert(exec_as_spectator ? _current_company == COMPANY_SPECTATOR : cur_company.Verify());
+ assert((cmd_flags & (CMD_SPECTATOR | CMD_SERVER)) != 0 ? _current_company == COMPANY_SPECTATOR : cur_company.Verify());
/* If the command fails, we're doing an estimate
* or the player does not have enough money
* (unless it's a command where the test and
* execution phase might return different costs)
* we bail out here. */
- if (res.Failed() || estimate_only ||
- (!test_and_exec_can_differ && !CheckCompanyHasMoney(res))) {
- if (!_networking || _generating_world || (cmd & CMD_NETWORK_COMMAND) != 0) {
- /* Log the failed command as well. Just to be able to be find
- * causes of desyncs due to bad command test implementations. */
- Debug(desync, 1, "cmdf: {:08x}; {:02x}; {:02x}; {:06x}; {:08x}; {:08x}; {:08x}; \"{}\" ({})", _date, _date_fract, (int)_current_company, tile, p1, p2, cmd & ~CMD_NETWORK_COMMAND, text, GetCommandName(cmd));
- }
- cur_company.Restore();
- return_dcpi(res);
+ bool test_and_exec_can_differ = (cmd_flags & CMD_NO_TEST) != 0;
+ if (res.Failed() || estimate_only || (!test_and_exec_can_differ && !CheckCompanyHasMoney(res))) {
+ return { true, !_networking || _generating_world || network_command, false };
}
- /*
- * If we are in network, and the command is not from the network
- * send it to the command-queue and abort execution
- */
- if (_networking && !_generating_world && !(cmd & CMD_NETWORK_COMMAND)) {
- NetworkSendCommand(tile, p1, p2, cmd & ~CMD_FLAGS_MASK, callback, text, _current_company);
- cur_company.Restore();
+ bool send_net = _networking && !_generating_world && !network_command;
- /* Don't return anything special here; no error, no costs.
- * This way it's not handled by DoCommand and only the
- * actual execution of the command causes messages. Also
- * reset the storages as we've not executed the command. */
- return_dcpi(CommandCost());
+ if (!send_net) {
+ /* Prepare for command execution. */
+ _cleared_object_areas.clear();
+ BasePersistentStorageArray::SwitchMode(PSM_ENTER_COMMAND);
}
- Debug(desync, 1, "cmd: {:08x}; {:02x}; {:02x}; {:06x}; {:08x}; {:08x}; {:08x}; \"{}\" ({})", _date, _date_fract, (int)_current_company, tile, p1, p2, cmd & ~CMD_NETWORK_COMMAND, text, GetCommandName(cmd));
- /* Actually try and execute the command. If no cost-type is given
- * use the construction one */
- _cleared_object_areas.clear();
- BasePersistentStorageArray::SwitchMode(PSM_ENTER_COMMAND);
- CommandCost res2 = proc(tile, flags | DC_EXEC, p1, p2, text);
+ return { false, _debug_desync_level >= 1, send_net };
+}
+
+/**
+ * Process the result of a command test run and execution run.
+ * @param cmd Command that was executed.
+ * @param cmd_flags Command flags.
+ * @param res_test Command result of test run.
+ * @param tes_exec Command result of real run.
+ * @param extra_cash Additional cash required for successful command execution.
+ * @param tile Tile of command execution.
+ * @param[in,out] cur_company Backup of current company at start of command execution.
+ * @return Final command result.
+ */
+CommandCost CommandHelperBase::InternalExecuteProcessResult(Commands cmd, CommandFlags cmd_flags, const CommandCost &res_test, const CommandCost &res_exec, Money extra_cash, TileIndex tile, Backup &cur_company)
+{
BasePersistentStorageArray::SwitchMode(PSM_LEAVE_COMMAND);
- if (cmd_id == CMD_COMPANY_CTRL) {
+ if (cmd == CMD_COMPANY_CTRL) {
cur_company.Trash();
/* We are a new company -> Switch to new local company.
* We were closed down -> Switch to spectator
@@ -743,7 +368,7 @@ CommandCost DoCommandPInternal(TileIndex tile, uint32 p1, uint32 p2, uint32 cmd,
_current_company = _local_company;
} else {
/* Make sure nothing bad happened, like changing the current company. */
- assert(exec_as_spectator ? _current_company == COMPANY_SPECTATOR : cur_company.Verify());
+ assert((cmd_flags & (CMD_SPECTATOR | CMD_SERVER)) != 0 ? _current_company == COMPANY_SPECTATOR : cur_company.Verify());
cur_company.Restore();
}
@@ -751,20 +376,21 @@ CommandCost DoCommandPInternal(TileIndex tile, uint32 p1, uint32 p2, uint32 cmd,
* return of the command. Otherwise we can check whether the
* test and execution have yielded the same result,
* i.e. cost and error state are the same. */
+ bool test_and_exec_can_differ = (cmd_flags & CMD_NO_TEST) != 0;
if (!test_and_exec_can_differ) {
- assert(res.GetCost() == res2.GetCost() && res.Failed() == res2.Failed()); // sanity check
- } else if (res2.Failed()) {
- return_dcpi(res2);
+ assert(res_test.GetCost() == res_exec.GetCost() && res_test.Failed() == res_exec.Failed()); // sanity check
+ } else if (res_exec.Failed()) {
+ return res_exec;
}
/* If we're needing more money and we haven't done
* anything yet, ask for the money! */
- if (_additional_cash_required != 0 && res2.GetCost() == 0) {
+ if (extra_cash != 0 && res_exec.GetCost() == 0) {
/* It could happen we removed rail, thus gained money, and deleted something else.
* So make sure the signal buffer is empty even in this case */
UpdateSignalsInBuffer();
- SetDParam(0, _additional_cash_required);
- return_dcpi(CommandCost(STR_ERROR_NOT_ENOUGH_CASH_REQUIRES_CURRENCY));
+ SetDParam(0, extra_cash);
+ return CommandCost(STR_ERROR_NOT_ENOUGH_CASH_REQUIRES_CURRENCY);
}
/* update last build coordinate of company. */
@@ -773,14 +399,13 @@ CommandCost DoCommandPInternal(TileIndex tile, uint32 p1, uint32 p2, uint32 cmd,
if (c != nullptr) c->last_build_coordinate = tile;
}
- SubtractMoneyFromCompany(res2);
+ SubtractMoneyFromCompany(res_exec);
/* update signals if needed */
UpdateSignalsInBuffer();
- return_dcpi(res2);
+ return res_exec;
}
-#undef return_dcpi
/**
diff --git a/src/command_func.h b/src/command_func.h
index 426283bd3a..d78c8229a4 100644
--- a/src/command_func.h
+++ b/src/command_func.h
@@ -11,7 +11,12 @@
#define COMMAND_FUNC_H
#include "command_type.h"
+#include "network/network_type.h"
#include "company_type.h"
+#include "company_func.h"
+#include "core/backup_type.hpp"
+#include "misc/endian_buffer.hpp"
+#include "tile_map.h"
/**
* Define a default return value for a failed command.
@@ -32,30 +37,26 @@ static const CommandCost CMD_ERROR = CommandCost(INVALID_STRING_ID);
*/
#define return_cmd_error(errcode) return CommandCost(errcode);
-CommandCost DoCommand(TileIndex tile, uint32 p1, uint32 p2, DoCommandFlag flags, uint32 cmd, const std::string &text = {});
-CommandCost DoCommand(const CommandContainer *container, DoCommandFlag flags);
+void NetworkSendCommand(Commands cmd, StringID err_message, CommandCallback *callback, CompanyID company, TileIndex location, const CommandDataBuffer &cmd_data);
-bool DoCommandP(TileIndex tile, uint32 p1, uint32 p2, uint32 cmd, CommandCallback *callback = nullptr, const std::string &text = {}, bool my_cmd = true);
-bool DoCommandP(const CommandContainer *container, bool my_cmd = true);
-
-CommandCost DoCommandPInternal(TileIndex tile, uint32 p1, uint32 p2, uint32 cmd, CommandCallback *callback, const std::string &text, bool my_cmd, bool estimate_only);
-
-void NetworkSendCommand(TileIndex tile, uint32 p1, uint32 p2, uint32 cmd, CommandCallback *callback, const std::string &text, CompanyID company);
-
-extern Money _additional_cash_required;
-
-bool IsValidCommand(uint32 cmd);
-CommandFlags GetCommandFlags(uint32 cmd);
-const char *GetCommandName(uint32 cmd);
+bool IsValidCommand(Commands cmd);
+CommandFlags GetCommandFlags(Commands cmd);
+const char *GetCommandName(Commands cmd);
Money GetAvailableMoneyForCommand();
-bool IsCommandAllowedWhilePaused(uint32 cmd);
+bool IsCommandAllowedWhilePaused(Commands cmd);
+
+template
+constexpr CommandFlags GetCommandFlags()
+{
+ return CommandTraits::flags;
+}
/**
* Extracts the DC flags needed for DoCommand from the flags returned by GetCommandFlags
* @param cmd_flags Flags from GetCommandFlags
* @return flags for DoCommand
*/
-static inline DoCommandFlag CommandFlagsToDCFlags(CommandFlags cmd_flags)
+static constexpr inline DoCommandFlag CommandFlagsToDCFlags(CommandFlags cmd_flags)
{
DoCommandFlag flags = DC_NONE;
if (cmd_flags & CMD_NO_WATER) flags |= DC_NO_WATER;
@@ -64,60 +65,433 @@ static inline DoCommandFlag CommandFlagsToDCFlags(CommandFlags cmd_flags)
return flags;
}
-/*** All command callbacks that exist ***/
+/** Helper class to keep track of command nesting level. */
+struct RecursiveCommandCounter {
+ RecursiveCommandCounter() noexcept { _counter++; }
+ ~RecursiveCommandCounter() noexcept { _counter--; }
-/* ai/ai_instance.cpp */
-CommandCallback CcAI;
+ /** Are we in the top-level command execution? */
+ bool IsTopLevel() const { return _counter == 1; }
+private:
+ static int _counter;
+};
-/* airport_gui.cpp */
-CommandCallback CcBuildAirport;
+#if defined(__GNUC__) && !defined(__clang__)
+/*
+ * We cast specialized function pointers to a generic one, but don't use the
+ * converted value to call the function, which is safe, except that GCC
+ * helpfully thinks it is not.
+ *
+ * "Any pointer to function can be converted to a pointer to a different function type.
+ * Calling the function through a pointer to a different function type is undefined,
+ * but converting such pointer back to pointer to the original function type yields
+ * the pointer to the original function." */
+# pragma GCC diagnostic push
+# pragma GCC diagnostic ignored "-Wcast-function-type"
+# define SILENCE_GCC_FUNCTION_POINTER_CAST
+#endif
-/* bridge_gui.cpp */
-CommandCallback CcBuildBridge;
+template struct CommandHelper;
-/* dock_gui.cpp */
-CommandCallback CcBuildDocks;
-CommandCallback CcPlaySound_CONSTRUCTION_WATER;
+class CommandHelperBase {
+protected:
+ static void InternalDoBefore(bool top_level, bool test);
+ static void InternalDoAfter(CommandCost &res, DoCommandFlag flags, bool top_level, bool test);
+ static std::tuple InternalPostBefore(Commands cmd, CommandFlags flags, TileIndex tile, StringID err_message, bool network_command);
+ static void InternalPostResult(const CommandCost &res, TileIndex tile, bool estimate_only, bool only_sending, StringID err_message, bool my_cmd);
+ static bool InternalExecutePrepTest(CommandFlags cmd_flags, TileIndex tile, Backup &cur_company);
+ static std::tuple InternalExecuteValidateTestAndPrepExec(CommandCost &res, CommandFlags cmd_flags, bool estimate_only, bool network_command, Backup &cur_company);
+ static CommandCost InternalExecuteProcessResult(Commands cmd, CommandFlags cmd_flags, const CommandCost &res_test, const CommandCost &res_exec, Money extra_cash, TileIndex tile, Backup &cur_company);
+ static void LogCommandExecution(Commands cmd, StringID err_message, TileIndex tile, const CommandDataBuffer &args, bool failed);
+};
-/* depot_gui.cpp */
-CommandCallback CcCloneVehicle;
+/**
+ * Templated wrapper that exposes the command parameter arguments
+ * for the various Command::Do/Post calls.
+ * @tparam Tcmd The command-id to execute.
+ * @tparam Tret Return type of the command.
+ * @tparam Targs The command parameter types.
+ */
+template
+struct CommandHelper : protected CommandHelperBase {
+private:
+ /** Extract the \c CommandCost from a command proc result. */
+ static inline CommandCost &ExtractCommandCost(Tret &ret)
+ {
+ if constexpr (std::is_same_v) {
+ return ret;
+ } else {
+ return std::get<0>(ret);
+ }
+ }
-/* game/game_instance.cpp */
-CommandCallback CcGame;
+ /** Make a command proc result from a \c CommandCost. */
+ static inline Tret MakeResult(const CommandCost &cost)
+ {
+ Tret ret{};
+ ExtractCommandCost(ret) = cost;
+ return ret;
+ }
-/* group_gui.cpp */
-CommandCallback CcCreateGroup;
-CommandCallback CcAddVehicleNewGroup;
+public:
+ /**
+ * This function executes a given command with the parameters from the #CommandProc parameter list.
+ * Depending on the flags parameter it executes or tests a command.
+ *
+ * @note This function is to be called from the StateGameLoop or from within the execution of a Command.
+ * This function must not be called from the context of a "player" (real person, AI, game script).
+ * Use ::Post for commands directly triggered by "players".
+ *
+ * @param flags Flags for the command and how to execute the command
+ * @param args Parameters for the command
+ * @see CommandProc
+ * @return the cost
+ */
+ static Tret Do(DoCommandFlag flags, Targs... args)
+ {
+ if constexpr (std::is_same_v>>) {
+ /* Do not even think about executing out-of-bounds tile-commands. */
+ TileIndex tile = std::get<0>(std::make_tuple(args...));
+ if (tile != 0 && (tile >= MapSize() || (!IsValidTile(tile) && (flags & DC_ALL_TILES) == 0))) return MakeResult(CMD_ERROR);
+ }
-/* industry_gui.cpp */
-CommandCallback CcBuildIndustry;
+ RecursiveCommandCounter counter{};
-/* main_gui.cpp */
-CommandCallback CcPlaySound_EXPLOSION;
-CommandCallback CcPlaceSign;
-CommandCallback CcTerraform;
+ /* Only execute the test call if it's toplevel, or we're not execing. */
+ if (counter.IsTopLevel() || !(flags & DC_EXEC)) {
+ InternalDoBefore(counter.IsTopLevel(), true);
+ Tret res = CommandTraits::proc(flags & ~DC_EXEC, args...);
+ InternalDoAfter(ExtractCommandCost(res), flags, counter.IsTopLevel(), true); // Can modify res.
-/* rail_gui.cpp */
-CommandCallback CcPlaySound_CONSTRUCTION_RAIL;
-CommandCallback CcRailDepot;
-CommandCallback CcStation;
-CommandCallback CcBuildRailTunnel;
+ if (ExtractCommandCost(res).Failed() || !(flags & DC_EXEC)) return res;
+ }
-/* road_gui.cpp */
-CommandCallback CcPlaySound_CONSTRUCTION_OTHER;
-CommandCallback CcBuildRoadTunnel;
-CommandCallback CcRoadDepot;
-CommandCallback CcRoadStop;
+ /* Execute the command here. All cost-relevant functions set the expenses type
+ * themselves to the cost object at some point. */
+ InternalDoBefore(counter.IsTopLevel(), false);
+ Tret res = CommandTraits::proc(flags, args...);
+ InternalDoAfter(ExtractCommandCost(res), flags, counter.IsTopLevel(), false);
-/* train_gui.cpp */
-CommandCallback CcBuildWagon;
+ return res;
+ }
-/* town_gui.cpp */
-CommandCallback CcFoundTown;
-CommandCallback CcFoundRandomTown;
+ /**
+ * Shortcut for the long Post when not using a callback.
+ * @param err_message Message prefix to show on error
+ * @param args Parameters for the command
+ */
+ static inline bool Post(StringID err_message, Targs... args) { return Post(err_message, nullptr, std::forward(args)...); }
+ /**
+ * Shortcut for the long Post when not using an error message.
+ * @param callback A callback function to call after the command is finished
+ * @param args Parameters for the command
+ */
+ template
+ static inline bool Post(Tcallback *callback, Targs... args) { return Post((StringID)0, callback, std::forward(args)...); }
+ /**
+ * Shortcut for the long Post when not using a callback or an error message.
+ * @param args Parameters for the command
+ */
+ static inline bool Post(Targs... args) { return Post((StringID)0, nullptr, std::forward(args)...); }
-/* vehicle_gui.cpp */
-CommandCallback CcBuildPrimaryVehicle;
-CommandCallback CcStartStopVehicle;
+ /**
+ * Top-level network safe command execution for the current company.
+ * Must not be called recursively. The callback is called when the
+ * command succeeded or failed.
+ *
+ * @param err_message Message prefix to show on error
+ * @param callback A callback function to call after the command is finished
+ * @param args Parameters for the command
+ * @return \c true if the command succeeded, else \c false.
+ */
+ template
+ static bool Post(StringID err_message, Tcallback *callback, Targs... args)
+ {
+ return InternalPost(err_message, callback, true, false, std::forward_as_tuple(args...));
+ }
+
+ /**
+ * Execute a command coming from the network.
+ * @param err_message Message prefix to show on error
+ * @param callback A callback function to call after the command is finished
+ * @param my_cmd indicator if the command is from a company or server (to display error messages for a user)
+ * @param location Tile location for user feedback.
+ * @param args Parameters for the command
+ * @return \c true if the command succeeded, else \c false.
+ */
+ template
+ static bool PostFromNet(StringID err_message, Tcallback *callback, bool my_cmd, TileIndex location, std::tuple args)
+ {
+ if constexpr (std::is_same_v>) {
+ /* Do not even think about executing out-of-bounds tile-commands. */
+ TileIndex tile = std::get<0>(args);
+ if (tile != 0 && (tile >= MapSize() || (!IsValidTile(tile) && (GetCommandFlags() & CMD_ALL_TILES) == 0))) return false;
+ }
+
+ return InternalPost(err_message, callback, my_cmd, true, location, std::move(args));
+ }
+
+ /**
+ * Prepare a command to be send over the network
+ * @param cmd The command to execute (a CMD_* value)
+ * @param err_message Message prefix to show on error
+ * @param company The company that wants to send the command
+ * @param args Parameters for the command
+ */
+ static void SendNet(StringID err_message, CompanyID company, Targs... args)
+ {
+ auto args_tuple = std::forward_as_tuple(args...);
+
+ TileIndex tile{};
+ if constexpr (std::is_same_v>) {
+ tile = std::get<0>(args_tuple);
+ }
+
+ ::NetworkSendCommand(Tcmd, err_message, nullptr, _current_company, tile, EndianBufferWriter::FromValue(args_tuple));
+ }
+
+ /**
+ * Top-level network safe command execution without safety checks.
+ * @param err_message Message prefix to show on error
+ * @param callback A callback function to call after the command is finished
+ * @param my_cmd indicator if the command is from a company or server (to display error messages for a user)
+ * @param estimate_only whether to give only the estimate or also execute the command
+ * @param location Tile location for user feedback.
+ * @param args Parameters for the command
+ * @return the command cost of this function.
+ */
+ template
+ static Tret Unsafe(StringID err_message, Tcallback *callback, bool my_cmd, bool estimate_only, TileIndex location, std::tuple args)
+ {
+ return Execute(err_message, reinterpret_cast(callback), my_cmd, estimate_only, false, location, std::move(args));
+ }
+
+protected:
+ /** Helper to process a single ClientID argument. */
+ template
+ static inline void SetClientIdHelper(T &data)
+ {
+ if constexpr (std::is_same_v) {
+ if (data == INVALID_CLIENT_ID) data = CLIENT_ID_SERVER;
+ }
+ }
+
+ /** Set all invalid ClientID's to the proper value. */
+ template
+ static inline void SetClientIds(Ttuple &values, std::index_sequence)
+ {
+ ((SetClientIdHelper(std::get(values))), ...);
+ }
+
+ /** Remove the first element of a tuple. */
+ template typename Tt, typename T1, typename... Ts>
+ static inline Tt RemoveFirstTupleElement(const Tt &tuple)
+ {
+ return std::apply([](auto &&, const auto&... args) { return std::tie(args...); }, tuple);
+ }
+
+ template
+ static bool InternalPost(StringID err_message, Tcallback *callback, bool my_cmd, bool network_command, std::tuple args)
+ {
+ /* Where to show the message? */
+ TileIndex tile{};
+ if constexpr (std::is_same_v>) {
+ tile = std::get<0>(args);
+ }
+
+ return InternalPost(err_message, callback, my_cmd, network_command, tile, std::move(args));
+ }
+
+ template
+ static bool InternalPost(StringID err_message, Tcallback *callback, bool my_cmd, bool network_command, TileIndex tile, std::tuple args)
+ {
+ /* Do not even think about executing out-of-bounds tile-commands. */
+ if (tile != 0 && (tile >= MapSize() || (!IsValidTile(tile) && (GetCommandFlags() & CMD_ALL_TILES) == 0))) return false;
+
+ auto [err, estimate_only, only_sending] = InternalPostBefore(Tcmd, GetCommandFlags(), tile, err_message, network_command);
+ if (err) return false;
+
+ /* Only set client IDs when the command does not come from the network. */
+ if (!network_command && GetCommandFlags() & CMD_CLIENT_ID) SetClientIds(args, std::index_sequence_for{});
+
+ Tret res = Execute(err_message, reinterpret_cast(callback), my_cmd, estimate_only, network_command, tile, args);
+ InternalPostResult(ExtractCommandCost(res), tile, estimate_only, only_sending, err_message, my_cmd);
+
+ if (!estimate_only && !only_sending && callback != nullptr) {
+ if constexpr (std::is_same_v) {
+ /* Callback that doesn't need any command arguments. */
+ callback(Tcmd, ExtractCommandCost(res), tile);
+ } else if constexpr (std::is_same_v) {
+ /* Generic callback that takes packed arguments as a buffer. */
+ if constexpr (std::is_same_v) {
+ callback(Tcmd, ExtractCommandCost(res), tile, EndianBufferWriter::FromValue(args), {});
+ } else {
+ callback(Tcmd, ExtractCommandCost(res), tile, EndianBufferWriter::FromValue(args), EndianBufferWriter::FromValue(RemoveFirstTupleElement(res)));
+ }
+ } else if constexpr (!std::is_same_v && std::is_same_v::RetCallbackProc>) {
+ std::apply(callback, std::tuple_cat(std::make_tuple(Tcmd), res));
+ } else {
+ /* Callback with arguments. We assume that the tile is only interesting if it actually is in the command arguments. */
+ if constexpr (std::is_same_v) {
+ std::apply(callback, std::tuple_cat(std::make_tuple(Tcmd, res), args));
+ } else {
+ std::apply(callback, std::tuple_cat(std::make_tuple(Tcmd), res, args));
+ }
+ }
+ }
+
+ return ExtractCommandCost(res).Succeeded();
+ }
+
+ /** Helper to process a single ClientID argument. */
+ template
+ static inline bool ClientIdIsSet(T &data)
+ {
+ if constexpr (std::is_same_v) {
+ return data != INVALID_CLIENT_ID;
+ } else {
+ return true;
+ }
+ }
+
+ /** Check if all ClientID arguments are set to valid values. */
+ template
+ static inline bool AllClientIdsSet(Ttuple &values, std::index_sequence)
+ {
+ return (ClientIdIsSet(std::get(values)) && ...);
+ }
+
+ template
+ static inline Money ExtractAdditionalMoney(Ttuple &values)
+ {
+ if constexpr (std::is_same_v, Money>) {
+ return std::get<1>(values);
+ } else {
+ return {};
+ }
+ }
+
+ static Tret Execute(StringID err_message, CommandCallback *callback, bool my_cmd, bool estimate_only, bool network_command, TileIndex tile, std::tuple args)
+ {
+ /* Prevent recursion; it gives a mess over the network */
+ RecursiveCommandCounter counter{};
+ assert(counter.IsTopLevel());
+
+ /* Command flags are used internally */
+ constexpr CommandFlags cmd_flags = GetCommandFlags();
+
+ if constexpr ((cmd_flags & CMD_CLIENT_ID) != 0) {
+ /* Make sure arguments are properly set to a ClientID also when processing external commands. */
+ assert(AllClientIdsSet(args, std::index_sequence_for{}));
+ }
+
+ Backup cur_company(_current_company, FILE_LINE);
+ if (!InternalExecutePrepTest(cmd_flags, tile, cur_company)) {
+ cur_company.Trash();
+ return MakeResult(CMD_ERROR);
+ }
+
+ /* Test the command. */
+ DoCommandFlag flags = CommandFlagsToDCFlags(cmd_flags);
+ Tret res = std::apply(CommandTraits::proc, std::tuple_cat(std::make_tuple(flags), args));
+
+ auto [exit_test, desync_log, send_net] = InternalExecuteValidateTestAndPrepExec(ExtractCommandCost(res), cmd_flags, estimate_only, network_command, cur_company);
+ if (exit_test) {
+ if (desync_log) LogCommandExecution(Tcmd, err_message, tile, EndianBufferWriter::FromValue(args), true);
+ cur_company.Restore();
+ return res;
+ }
+
+ /* If we are in network, and the command is not from the network
+ * send it to the command-queue and abort execution. */
+ if (send_net) {
+ ::NetworkSendCommand(Tcmd, err_message, callback, _current_company, tile, EndianBufferWriter::FromValue(args));
+ cur_company.Restore();
+
+ /* Don't return anything special here; no error, no costs.
+ * This way it's not handled by DoCommand and only the
+ * actual execution of the command causes messages. Also
+ * reset the storages as we've not executed the command. */
+ return {};
+ }
+
+ if (desync_log) LogCommandExecution(Tcmd, err_message, tile, EndianBufferWriter::FromValue(args), false);
+
+ /* Actually try and execute the command. */
+ Tret res2 = std::apply(CommandTraits::proc, std::tuple_cat(std::make_tuple(flags | DC_EXEC), args));
+
+ /* Convention: If the second result element is of type Money,
+ * this is the additional cash required for the command. */
+ Money additional_money{};
+ if constexpr (!std::is_same_v) { // No short-circuiting for 'if constexpr'.
+ additional_money = ExtractAdditionalMoney(res2);
+ }
+
+ if constexpr (std::is_same_v) {
+ return InternalExecuteProcessResult(Tcmd, cmd_flags, res, res2, additional_money, tile, cur_company);
+ } else {
+ std::get<0>(res2) = InternalExecuteProcessResult(Tcmd, cmd_flags, ExtractCommandCost(res), ExtractCommandCost(res2), additional_money, tile, cur_company);
+ return res2;
+ }
+ }
+};
+
+/**
+ * Overload for #CommandHelper that exposes additional \c Post variants
+ * for commands that don't take a TileIndex themselves.
+ * @tparam Tcmd The command-id to execute.
+ * @tparam Tret Return type of the command.
+ * @tparam Targs The command parameter types.
+ */
+template
+struct CommandHelper : CommandHelper
+{
+ /* Import Post overloads from our base class. */
+ using CommandHelper::Post;
+
+ /**
+ * Shortcut for Post when not using an error message.
+ * @param err_message Message prefix to show on error
+ * @param location Tile location for user feedback.
+ * @param args Parameters for the command
+ */
+ static inline bool Post(StringID err_message, TileIndex location, Targs... args) { return Post(err_message, nullptr, location, std::forward(args)...); }
+ /**
+ * Shortcut for Post when not using a callback.
+ * @param callback A callback function to call after the command is finished
+ * @param location Tile location for user feedback.
+ * @param args Parameters for the command
+ */
+ template
+ static inline bool Post(Tcallback *callback, TileIndex location, Targs... args) { return Post((StringID)0, callback, location, std::forward(args)...); }
+ /**
+ * Shortcut for Post when not using a callback or an error message.
+ * @param location Tile location for user feedback.
+ * @param args Parameters for the command*
+ */
+ static inline bool Post(TileIndex location, Targs... args) { return Post((StringID)0, nullptr, location, std::forward(args)...); }
+
+ /**
+ * Post variant that takes a TileIndex (for error window location and text effects) for
+ * commands that don't take a TileIndex by themselves.
+ * @param err_message Message prefix to show on error
+ * @param callback A callback function to call after the command is finished
+ * @param location Tile location for user feedback.
+ * @param args Parameters for the command
+ * @return \c true if the command succeeded, else \c false.
+ */
+ template
+ static inline bool Post(StringID err_message, Tcallback *callback, TileIndex location, Targs... args)
+ {
+ return CommandHelper::InternalPost(err_message, callback, true, false, location, std::forward_as_tuple(args...));
+ }
+};
+
+#ifdef SILENCE_GCC_FUNCTION_POINTER_CAST
+# pragma GCC diagnostic pop
+#endif
+
+template
+using Command = CommandHelper::ProcType, std::is_same_v::Args>>>;
#endif /* COMMAND_FUNC_H */
diff --git a/src/command_type.h b/src/command_type.h
index 61154ea04b..d2b573b333 100644
--- a/src/command_type.h
+++ b/src/command_type.h
@@ -13,6 +13,7 @@
#include "economy_type.h"
#include "strings_type.h"
#include "tile_type.h"
+#include
struct GRFFile;
@@ -172,7 +173,7 @@ public:
*
* @see _command_proc_table
*/
-enum Commands {
+enum Commands : uint16 {
CMD_BUILD_RAILROAD_TRACK, ///< build a rail track
CMD_REMOVE_RAILROAD_TRACK, ///< remove a rail track
CMD_BUILD_SINGLE_RAIL, ///< build a single rail track
@@ -185,6 +186,7 @@ enum Commands {
CMD_REMOVE_SIGNALS, ///< remove a signal
CMD_TERRAFORM_LAND, ///< terraform a tile
CMD_BUILD_OBJECT, ///< build an object
+ CMD_BUILD_OBJECT_AREA, ///< build an area of objects
CMD_BUILD_TUNNEL, ///< build a tunnel
CMD_REMOVE_FROM_RAIL_STATION, ///< remove a (rectangle of) tiles from a rail station
@@ -329,12 +331,19 @@ enum Commands {
CMD_MOVE_ORDER, ///< move an order
CMD_CHANGE_TIMETABLE, ///< change the timetable for a vehicle
+ CMD_BULK_CHANGE_TIMETABLE, ///< change the timetable for all orders of a vehicle
CMD_SET_VEHICLE_ON_TIME, ///< set the vehicle on time feature (timetable)
CMD_AUTOFILL_TIMETABLE, ///< autofill the timetable
CMD_SET_TIMETABLE_START, ///< set the date that a timetable should start
CMD_OPEN_CLOSE_AIRPORT, ///< open/close an airport to incoming aircraft
+ CMD_CREATE_LEAGUE_TABLE, ///< create a new league table
+ CMD_CREATE_LEAGUE_TABLE_ELEMENT, ///< create a new element in a league table
+ CMD_UPDATE_LEAGUE_TABLE_ELEMENT_DATA, ///< update the data fields of a league table element
+ CMD_UPDATE_LEAGUE_TABLE_ELEMENT_SCORE, ///< update the score of a league table element
+ CMD_REMOVE_LEAGUE_TABLE_ELEMENT, ///< remove a league table element
+
CMD_END, ///< Must ALWAYS be on the end of this list!! (period)
};
@@ -360,28 +369,6 @@ enum DoCommandFlag {
};
DECLARE_ENUM_AS_BIT_SET(DoCommandFlag)
-/**
- * Used to combine a StringID with the command.
- *
- * This macro can be used to add a StringID (the error message to show) on a command-id
- * (CMD_xxx). Use the binary or-operator "|" to combine the command with the result from
- * this macro.
- *
- * @param x The StringID to combine with a command-id
- */
-#define CMD_MSG(x) ((x) << 16)
-
-/**
- * Defines some flags.
- *
- * This enumeration defines some flags which are binary-or'ed on a command.
- */
-enum FlaggedCommands {
- CMD_NETWORK_COMMAND = 0x0100, ///< execute the command without sending it on the network
- CMD_FLAGS_MASK = 0xFF00, ///< mask for all command flags
- CMD_ID_MASK = 0x00FF, ///< mask for the command ID
-};
-
/**
* Command flags for the command table _command_proc_table.
*
@@ -425,38 +412,42 @@ enum CommandPauseLevel {
CMDPL_ALL_ACTIONS, ///< All actions may be executed.
};
-/**
- * Defines the callback type for all command handler functions.
- *
- * This type defines the function header for all functions which handles a CMD_* command.
- * A command handler use the parameters to act according to the meaning of the command.
- * The tile parameter defines the tile to perform an action on.
- * The flag parameter is filled with flags from the DC_* enumeration. The parameters
- * p1 and p2 are filled with parameters for the command like "which road type", "which
- * order" or "direction". Each function should mentioned in there doxygen comments
- * the usage of these parameters.
- *
- * @param tile The tile to apply a command on
- * @param flags Flags for the command, from the DC_* enumeration
- * @param p1 Additional data for the command
- * @param p2 Additional data for the command
- * @param text Additional text
- * @return The CommandCost of the command, which can be succeeded or failed.
- */
-typedef CommandCost CommandProc(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const std::string &text);
-/**
- * Define a command with the flags which belongs to it.
- *
- * This struct connect a command handler function with the flags created with
- * the #CMD_AUTO, #CMD_OFFLINE and #CMD_SERVER values.
- */
-struct Command {
- CommandProc *proc; ///< The procedure to actually executing
- const char *name; ///< A human readable name for the procedure
- CommandFlags flags; ///< The (command) flags to that apply to this command
- CommandType type; ///< The type of command.
+template struct CommandFunctionTraitHelper;
+template
+struct CommandFunctionTraitHelper {
+ using Args = std::tuple...>;
+ using RetTypes = void;
+ using CbArgs = Args;
+ using CbProcType = void(*)(Commands, const CommandCost &);
};
+template typename Tret, typename... Tretargs, typename... Targs>
+struct CommandFunctionTraitHelper(*)(DoCommandFlag, Targs...)> {
+ using Args = std::tuple...>;
+ using RetTypes = std::tuple...>;
+ using CbArgs = std::tuple..., std::decay_t...>;
+ using CbProcType = void(*)(Commands, const CommandCost &, Tretargs...);
+};
+
+/** Defines the traits of a command. */
+template struct CommandTraits;
+
+#define DEF_CMD_TRAIT(cmd_, proc_, flags_, type_) \
+ template<> struct CommandTraits { \
+ using ProcType = decltype(&proc_); \
+ using Args = typename CommandFunctionTraitHelper::Args; \
+ using RetTypes = typename CommandFunctionTraitHelper::RetTypes; \
+ using CbArgs = typename CommandFunctionTraitHelper::CbArgs; \
+ using RetCallbackProc = typename CommandFunctionTraitHelper::CbProcType; \
+ static constexpr Commands cmd = cmd_; \
+ static constexpr auto &proc = proc_; \
+ static constexpr CommandFlags flags = (CommandFlags)(flags_); \
+ static constexpr CommandType type = type_; \
+ static inline constexpr const char *name = #proc_; \
+ };
+
+/** Storage buffer for serialized command data. */
+typedef std::vector CommandDataBuffer;
/**
* Define a callback function for the client, after the command is finished.
@@ -465,24 +456,27 @@ struct Command {
* are from the #CommandProc callback type. The boolean parameter indicates if the
* command succeeded or failed.
*
+ * @param cmd The command that was executed
* @param result The result of the executed command
* @param tile The tile of the command action
- * @param p1 Additional data of the command
- * @param p1 Additional data of the command
* @see CommandProc
*/
-typedef void CommandCallback(const CommandCost &result, TileIndex tile, uint32 p1, uint32 p2, uint32 cmd);
+typedef void CommandCallback(Commands cmd, const CommandCost &result, TileIndex tile);
/**
- * Structure for buffering the build command when selecting a station to join.
+ * Define a callback function for the client, after the command is finished.
+ *
+ * Functions of this type are called after the command is finished. The parameters
+ * are from the #CommandProc callback type. The boolean parameter indicates if the
+ * command succeeded or failed.
+ *
+ * @param cmd The command that was executed
+ * @param result The result of the executed command
+ * @param tile The tile of the command action
+ * @param data Additional data of the command
+ * @param result_data Additional returned data from the command
+ * @see CommandProc
*/
-struct CommandContainer {
- TileIndex tile; ///< tile command being executed on.
- uint32 p1; ///< parameter p1.
- uint32 p2; ///< parameter p2.
- uint32 cmd; ///< command being executed.
- CommandCallback *callback; ///< any callback function executed upon successful completion of the command.
- std::string text; ///< possible text sent for name changes etc.
-};
+typedef void CommandCallbackData(Commands cmd, const CommandCost &result, TileIndex tile, const CommandDataBuffer &data, CommandDataBuffer result_data);
#endif /* COMMAND_TYPE_H */
diff --git a/src/company_base.h b/src/company_base.h
index 065931c6ce..bb88e94fd6 100644
--- a/src/company_base.h
+++ b/src/company_base.h
@@ -17,6 +17,7 @@
#include "settings_type.h"
#include "group.h"
#include
+#include
/** Statistics about the economy. */
struct CompanyEconomyEntry {
@@ -74,7 +75,7 @@ struct CompanyProperties {
TileIndex location_of_HQ; ///< Northern tile of HQ; #INVALID_TILE when there is none.
TileIndex last_build_coordinate; ///< Coordinate of the last build thing by this company.
- Owner share_owners[4]; ///< Owners of the 4 shares of the company. #INVALID_OWNER if nobody has bought them yet.
+ std::array share_owners; ///< Owners of the shares of the company. #INVALID_OWNER if nobody has bought them yet.
Year inaugurated_year; ///< Year of starting the company.
@@ -86,6 +87,7 @@ struct CompanyProperties {
uint32 terraform_limit; ///< Amount of tileheights we can (still) terraform (times 65536).
uint32 clear_limit; ///< Amount of tiles we can (still) clear (times 65536).
uint32 tree_limit; ///< Amount of trees we can (still) plant (times 65536).
+ uint32 build_object_limit; ///< Amount of tiles we can (still) build objects on (times 65536). Also applies to buying land.
/**
* If \c true, the company is (also) controlled by the computer (a NoAI program).
@@ -109,7 +111,7 @@ struct CompanyProperties {
face(0), money(0), money_fraction(0), current_loan(0), colour(0), block_preview(0),
location_of_HQ(0), last_build_coordinate(0), share_owners(), inaugurated_year(0),
months_of_bankruptcy(0), bankrupt_asked(0), bankrupt_timeout(0), bankrupt_value(0),
- terraform_limit(0), clear_limit(0), tree_limit(0), is_ai(false), engine_renew_list(nullptr) {}
+ terraform_limit(0), clear_limit(0), tree_limit(0), build_object_limit(0), is_ai(false), engine_renew_list(nullptr) {}
};
struct Company : CompanyProperties, CompanyPool::PoolItem<&_company_pool> {
@@ -166,6 +168,7 @@ struct Company : CompanyProperties, CompanyPool::PoolItem<&_company_pool> {
};
Money CalculateCompanyValue(const Company *c, bool including_loan = true);
+Money CalculateCompanyValueExcludingShares(const Company *c, bool including_loan = true);
extern uint _next_competitor_start;
extern uint _cur_company_tick_index;
diff --git a/src/company_cmd.cpp b/src/company_cmd.cpp
index 7549212849..a4e5fc9c3b 100644
--- a/src/company_cmd.cpp
+++ b/src/company_cmd.cpp
@@ -14,7 +14,6 @@
#include "core/backup_type.hpp"
#include "town.h"
#include "news_func.h"
-#include "cmd_helper.h"
#include "command_func.h"
#include "network/network.h"
#include "network/network_func.h"
@@ -36,6 +35,7 @@
#include "goal_base.h"
#include "story_base.h"
#include "widgets/statusbar_widget.h"
+#include "company_cmd.h"
#include "table/strings.h"
@@ -63,11 +63,12 @@ Company::Company(uint16 name_1, bool is_ai)
this->name_1 = name_1;
this->location_of_HQ = INVALID_TILE;
this->is_ai = is_ai;
- this->terraform_limit = (uint32)_settings_game.construction.terraform_frame_burst << 16;
- this->clear_limit = (uint32)_settings_game.construction.clear_frame_burst << 16;
- this->tree_limit = (uint32)_settings_game.construction.tree_frame_burst << 16;
+ this->terraform_limit = (uint32)_settings_game.construction.terraform_frame_burst << 16;
+ this->clear_limit = (uint32)_settings_game.construction.clear_frame_burst << 16;
+ this->tree_limit = (uint32)_settings_game.construction.tree_frame_burst << 16;
+ this->build_object_limit = (uint32)_settings_game.construction.build_object_frame_burst << 16;
- for (uint j = 0; j < 4; j++) this->share_owners[j] = INVALID_OWNER;
+ std::fill(this->share_owners.begin(), this->share_owners.end(), INVALID_OWNER);
InvalidateWindowData(WC_PERFORMANCE_DETAIL, 0, INVALID_COMPANY);
}
@@ -114,8 +115,11 @@ void SetLocalCompany(CompanyID new_company)
_current_company = _local_company = new_company;
- /* Delete any construction windows... */
- if (switching_company) CloseConstructionWindows();
+ if (switching_company) {
+ InvalidateWindowClassesData(WC_COMPANY);
+ /* Delete any construction windows... */
+ CloseConstructionWindows();
+ }
/* ... and redraw the whole screen. */
MarkWholeScreenDirty();
@@ -219,17 +223,17 @@ static void SubtractMoneyFromAnyCompany(Company *c, const CommandCost &cost)
c->money -= cost.GetCost();
c->yearly_expenses[0][cost.GetExpensesType()] += cost.GetCost();
- if (HasBit(1 << EXPENSES_TRAIN_INC |
- 1 << EXPENSES_ROADVEH_INC |
- 1 << EXPENSES_AIRCRAFT_INC |
- 1 << EXPENSES_SHIP_INC, cost.GetExpensesType())) {
+ if (HasBit(1 << EXPENSES_TRAIN_REVENUE |
+ 1 << EXPENSES_ROADVEH_REVENUE |
+ 1 << EXPENSES_AIRCRAFT_REVENUE |
+ 1 << EXPENSES_SHIP_REVENUE, cost.GetExpensesType())) {
c->cur_economy.income -= cost.GetCost();
} else if (HasBit(1 << EXPENSES_TRAIN_RUN |
1 << EXPENSES_ROADVEH_RUN |
1 << EXPENSES_AIRCRAFT_RUN |
1 << EXPENSES_SHIP_RUN |
1 << EXPENSES_PROPERTY |
- 1 << EXPENSES_LOAN_INT, cost.GetExpensesType())) {
+ 1 << EXPENSES_LOAN_INTEREST, cost.GetExpensesType())) {
c->cur_economy.expenses -= cost.GetCost();
}
@@ -267,9 +271,10 @@ void SubtractMoneyFromCompanyFract(CompanyID company, const CommandCost &cst)
void UpdateLandscapingLimits()
{
for (Company *c : Company::Iterate()) {
- c->terraform_limit = std::min((uint64)c->terraform_limit + _settings_game.construction.terraform_per_64k_frames, (uint64)_settings_game.construction.terraform_frame_burst << 16);
- c->clear_limit = std::min((uint64)c->clear_limit + _settings_game.construction.clear_per_64k_frames, (uint64)_settings_game.construction.clear_frame_burst << 16);
- c->tree_limit = std::min((uint64)c->tree_limit + _settings_game.construction.tree_per_64k_frames, (uint64)_settings_game.construction.tree_frame_burst << 16);
+ c->terraform_limit = std::min((uint64)c->terraform_limit + _settings_game.construction.terraform_per_64k_frames, (uint64)_settings_game.construction.terraform_frame_burst << 16);
+ c->clear_limit = std::min((uint64)c->clear_limit + _settings_game.construction.clear_per_64k_frames, (uint64)_settings_game.construction.clear_frame_burst << 16);
+ c->tree_limit = std::min((uint64)c->tree_limit + _settings_game.construction.tree_per_64k_frames, (uint64)_settings_game.construction.tree_frame_burst << 16);
+ c->build_object_limit = std::min((uint64)c->build_object_limit + _settings_game.construction.build_object_per_64k_frames, (uint64)_settings_game.construction.build_object_frame_burst << 16);
}
}
@@ -557,12 +562,19 @@ Company *DoStartupNewCompany(bool is_ai, CompanyID company = INVALID_COMPANY)
c->money = c->current_loan = (std::min(INITIAL_LOAN, _economy.max_loan) * _economy.inflation_prices >> 16) / 50000 * 50000;
- c->share_owners[0] = c->share_owners[1] = c->share_owners[2] = c->share_owners[3] = INVALID_OWNER;
+ std::fill(c->share_owners.begin(), c->share_owners.end(), INVALID_OWNER);
c->avail_railtypes = GetCompanyRailtypes(c->index);
c->avail_roadtypes = GetCompanyRoadTypes(c->index);
c->inaugurated_year = _cur_year;
- RandomCompanyManagerFaceBits(c->face, (GenderEthnicity)Random(), false, false); // create a random company manager face
+
+ /* If starting a player company in singleplayer and a favorite company manager face is selected, choose it. Otherwise, use a random face.
+ * In a network game, we'll choose the favorite face later in CmdCompanyCtrl to sync it to all clients. */
+ if (_company_manager_face != 0 && !is_ai && !_networking) {
+ c->face = _company_manager_face;
+ } else {
+ RandomCompanyManagerFaceBits(c->face, (GenderEthnicity)Random(), false, false);
+ }
SetDefaultCompanySettings(c->index);
ClearEnginesHiddenFlagOfCompany(c->index);
@@ -603,7 +615,7 @@ static bool MaybeStartNewCompany()
if (n < (uint)_settings_game.difficulty.max_no_competitors) {
/* Send a command to all clients to start up a new AI.
* Works fine for Multiplayer and Singleplayer */
- return DoCommandP(0, CCA_NEW_AI | INVALID_COMPANY << 16, 0, CMD_COMPANY_CTRL);
+ return Command::Post(CCA_NEW_AI, INVALID_COMPANY, CRR_NONE, INVALID_CLIENT_ID );
}
return false;
@@ -794,22 +806,17 @@ void CompanyAdminRemove(CompanyID company_id, CompanyRemoveReason reason)
/**
* Control the companies: add, delete, etc.
- * @param tile unused
* @param flags operation to perform
- * @param p1 various functionality
- * - bits 0..15: CompanyCtrlAction
- * - bits 16..23: CompanyID
- * - bits 24..31: CompanyRemoveReason (with CCA_DELETE)
- * @param p2 ClientID
- * @param text unused
+ * @param cca action to perform
+ * @param company_id company to perform the action on
+ * @param client_id ClientID
* @return the cost of this operation or an error
*/
-CommandCost CmdCompanyCtrl(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const std::string &text)
+CommandCost CmdCompanyCtrl(DoCommandFlag flags, CompanyCtrlAction cca, CompanyID company_id, CompanyRemoveReason reason, ClientID client_id)
{
InvalidateWindowData(WC_COMPANY_LEAGUE, 0, 0);
- CompanyID company_id = (CompanyID)GB(p1, 16, 8);
- switch ((CompanyCtrlAction)GB(p1, 0, 16)) {
+ switch (cca) {
case CCA_NEW: { // Create a new company
/* This command is only executed in a multiplayer game */
if (!_networking) return CMD_ERROR;
@@ -817,7 +824,6 @@ CommandCost CmdCompanyCtrl(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3
/* Has the network client a correct ClientIndex? */
if (!(flags & DC_EXEC)) return CommandCost();
- ClientID client_id = (ClientID)p2;
NetworkClientInfo *ci = NetworkClientInfo::GetByClientID(client_id);
/* Delete multiplayer progress bar */
@@ -844,6 +850,10 @@ CommandCost CmdCompanyCtrl(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3
NetworkChangeCompanyPassword(_local_company, _settings_client.network.default_company_pass);
}
+ /* In network games, we need to try setting the company manager face here to sync it to all clients.
+ * If a favorite company manager face is selected, choose it. Otherwise, use a random face. */
+ if (_company_manager_face != 0) Command::SendNet(STR_NULL, c->index, _company_manager_face);
+
/* Now that we have a new company, broadcast our company settings to
* all clients so everything is in sync */
SyncCompanySettings();
@@ -872,7 +882,6 @@ CommandCost CmdCompanyCtrl(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3
}
case CCA_DELETE: { // Delete a company
- CompanyRemoveReason reason = (CompanyRemoveReason)GB(p1, 24, 8);
if (reason >= CRR_END) return CMD_ERROR;
/* We can't delete the last existing company in singleplayer mode. */
@@ -883,8 +892,6 @@ CommandCost CmdCompanyCtrl(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3
if (!(flags & DC_EXEC)) return CommandCost();
- /* Delete any open window of the company */
- CloseCompanyWindows(c->index);
CompanyNewsInformation *cni = new CompanyNewsInformation(c);
/* Show the bankrupt news */
@@ -921,17 +928,12 @@ CommandCost CmdCompanyCtrl(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3
/**
* Change the company manager's face.
- * @param tile unused
* @param flags operation to perform
- * @param p1 unused
- * @param p2 face bitmasked
- * @param text unused
+ * @param cmf face bitmasked
* @return the cost of this operation or an error
*/
-CommandCost CmdSetCompanyManagerFace(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const std::string &text)
+CommandCost CmdSetCompanyManagerFace(DoCommandFlag flags, CompanyManagerFace cmf)
{
- CompanyManagerFace cmf = (CompanyManagerFace)p2;
-
if (!IsValidCompanyManagerFace(cmf)) return CMD_ERROR;
if (flags & DC_EXEC) {
@@ -943,21 +945,14 @@ CommandCost CmdSetCompanyManagerFace(TileIndex tile, DoCommandFlag flags, uint32
/**
* Change the company's company-colour
- * @param tile unused
* @param flags operation to perform
- * @param p1 bitstuffed:
- * p1 bits 0-7 scheme to set
- * p1 bit 8 set first/second colour
- * @param p2 new colour for vehicles, property, etc.
- * @param text unused
+ * @param scheme scheme to set
+ * @param primary set first/second colour
+ * @param colour new colour for vehicles, property, etc.
* @return the cost of this operation or an error
*/
-CommandCost CmdSetCompanyColour(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const std::string &text)
+CommandCost CmdSetCompanyColour(DoCommandFlag flags, LiveryScheme scheme, bool primary, Colours colour)
{
- Colours colour = Extract(p2);
- LiveryScheme scheme = Extract(p1);
- bool second = HasBit(p1, 8);
-
if (scheme >= LS_END || (colour >= COLOUR_END && colour != INVALID_COLOUR)) return CMD_ERROR;
/* Default scheme can't be reset to invalid. */
@@ -966,14 +961,14 @@ CommandCost CmdSetCompanyColour(TileIndex tile, DoCommandFlag flags, uint32 p1,
Company *c = Company::Get(_current_company);
/* Ensure no two companies have the same primary colour */
- if (scheme == LS_DEFAULT && !second) {
+ if (scheme == LS_DEFAULT && primary) {
for (const Company *cc : Company::Iterate()) {
if (cc != c && cc->colour == colour) return CMD_ERROR;
}
}
if (flags & DC_EXEC) {
- if (!second) {
+ if (primary) {
if (scheme != LS_DEFAULT) SB(c->livery[scheme].in_use, 0, 1, colour != INVALID_COLOUR);
if (colour == INVALID_COLOUR) colour = (Colours)c->livery[LS_DEFAULT].colour1;
c->livery[scheme].colour1 = colour;
@@ -1056,14 +1051,11 @@ static bool IsUniqueCompanyName(const std::string &name)
/**
* Change the name of the company.
- * @param tile unused
* @param flags operation to perform
- * @param p1 unused
- * @param p2 unused
* @param text the new name or an empty string when resetting to the default
* @return the cost of this operation or an error
*/
-CommandCost CmdRenameCompany(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const std::string &text)
+CommandCost CmdRenameCompany(DoCommandFlag flags, const std::string &text)
{
bool reset = text.empty();
@@ -1102,14 +1094,11 @@ static bool IsUniquePresidentName(const std::string &name)
/**
* Change the name of the president.
- * @param tile unused
* @param flags operation to perform
- * @param p1 unused
- * @param p2 unused
* @param text the new name or an empty string when resetting to the default
* @return the cost of this operation or an error
*/
-CommandCost CmdRenamePresident(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const std::string &text)
+CommandCost CmdRenamePresident(DoCommandFlag flags, const std::string &text)
{
bool reset = text.empty();
@@ -1127,7 +1116,7 @@ CommandCost CmdRenamePresident(TileIndex tile, DoCommandFlag flags, uint32 p1, u
c->president_name = text;
if (c->name_1 == STR_SV_UNNAMED && c->name.empty()) {
- DoCommand(0, 0, 0, DC_EXEC, CMD_RENAME_COMPANY, text + " Transport");
+ Command::Do(DC_EXEC, text + " Transport");
}
}
@@ -1187,20 +1176,17 @@ uint32 CompanyInfrastructure::GetTramTotal() const
* To prevent abuse in multiplayer games you can only send money to other
* companies if you have paid off your loan (either explicitly, or implicitly
* given the fact that you have more money than loan).
- * @param tile unused
* @param flags operation to perform
- * @param p1 the amount of money to transfer; max 20.000.000
- * @param p2 the company to transfer the money to
- * @param text unused
+ * @param money the amount of money to transfer; max 20.000.000
+ * @param dest_company the company to transfer the money to
* @return the cost of this operation or an error
*/
-CommandCost CmdGiveMoney(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const std::string &text)
+CommandCost CmdGiveMoney(DoCommandFlag flags, uint32 money, CompanyID dest_company)
{
if (!_settings_game.economy.give_money) return CMD_ERROR;
const Company *c = Company::Get(_current_company);
- CommandCost amount(EXPENSES_OTHER, std::min(p1, 20000000LL));
- CompanyID dest_company = (CompanyID)p2;
+ CommandCost amount(EXPENSES_OTHER, std::min(money, 20000000LL));
/* You can only transfer funds that is in excess of your loan */
if (c->money - c->current_loan < amount.GetCost() || amount.GetCost() < 0) return_cmd_error(STR_ERROR_INSUFFICIENT_FUNDS);
@@ -1226,3 +1212,31 @@ CommandCost CmdGiveMoney(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32
/* Subtract money from local-company */
return amount;
}
+
+/**
+ * Get the index of the first available company. It attempts,
+ * from first to last, and as soon as the attempt succeeds,
+ * to get the index of the company:
+ * 1st - get the first existing human company.
+ * 2nd - get the first non-existing company.
+ * 3rd - get COMPANY_FIRST.
+ * @return the index of the first available company.
+ */
+CompanyID GetFirstPlayableCompanyID()
+{
+ for (Company *c : Company::Iterate()) {
+ if (Company::IsHumanID(c->index)) {
+ return c->index;
+ }
+ }
+
+ if (Company::CanAllocateItem()) {
+ for (CompanyID c = COMPANY_FIRST; c < MAX_COMPANIES; c++) {
+ if (!Company::IsValidID(c)) {
+ return c;
+ }
+ }
+ }
+
+ return COMPANY_FIRST;
+}
diff --git a/src/company_cmd.h b/src/company_cmd.h
new file mode 100644
index 0000000000..5ffac5cbde
--- /dev/null
+++ b/src/company_cmd.h
@@ -0,0 +1,34 @@
+/*
+ * This file is part of OpenTTD.
+ * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
+ * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see .
+ */
+
+/** @file company_cmd.h Command definitions related to companies. */
+
+#ifndef COMPANY_CMD_H
+#define COMPANY_CMD_H
+
+#include "command_type.h"
+#include "company_type.h"
+#include "livery.h"
+
+enum ClientID : uint32;
+enum Colours : byte;
+
+CommandCost CmdCompanyCtrl(DoCommandFlag flags, CompanyCtrlAction cca, CompanyID company_id, CompanyRemoveReason reason, ClientID client_id);
+CommandCost CmdGiveMoney(DoCommandFlag flags, uint32 money, CompanyID dest_company);
+CommandCost CmdRenameCompany(DoCommandFlag flags, const std::string &text);
+CommandCost CmdRenamePresident(DoCommandFlag flags, const std::string &text);
+CommandCost CmdSetCompanyManagerFace(DoCommandFlag flags, CompanyManagerFace cmf);
+CommandCost CmdSetCompanyColour(DoCommandFlag flags, LiveryScheme scheme, bool primary, Colours colour);
+
+DEF_CMD_TRAIT(CMD_COMPANY_CTRL, CmdCompanyCtrl, CMD_SPECTATOR | CMD_CLIENT_ID | CMD_NO_EST, CMDT_SERVER_SETTING)
+DEF_CMD_TRAIT(CMD_GIVE_MONEY, CmdGiveMoney, 0, CMDT_MONEY_MANAGEMENT)
+DEF_CMD_TRAIT(CMD_RENAME_COMPANY, CmdRenameCompany, 0, CMDT_OTHER_MANAGEMENT)
+DEF_CMD_TRAIT(CMD_RENAME_PRESIDENT, CmdRenamePresident, 0, CMDT_OTHER_MANAGEMENT)
+DEF_CMD_TRAIT(CMD_SET_COMPANY_MANAGER_FACE, CmdSetCompanyManagerFace, 0, CMDT_OTHER_MANAGEMENT)
+DEF_CMD_TRAIT(CMD_SET_COMPANY_COLOUR, CmdSetCompanyColour, 0, CMDT_OTHER_MANAGEMENT)
+
+#endif /* COMPANY_CMD_H */
diff --git a/src/company_func.h b/src/company_func.h
index 01f5859104..5c58ee8da4 100644
--- a/src/company_func.h
+++ b/src/company_func.h
@@ -27,7 +27,7 @@ void UpdateLandscapingLimits();
bool CheckCompanyHasMoney(CommandCost &cost);
void SubtractMoneyFromCompany(const CommandCost& cost);
void SubtractMoneyFromCompanyFract(CompanyID company, const CommandCost& cost);
-CommandCost CheckOwnership(Owner owner, TileIndex tile = 0);
+CommandCost CheckOwnership(Owner owner, TileIndex tile = 0U);
CommandCost CheckTileOwnership(TileIndex tile);
extern CompanyID _local_company;
@@ -56,5 +56,6 @@ static inline bool IsInteractiveCompany(CompanyID company)
}
int CompanyServiceInterval(const Company *c, VehicleType type);
+CompanyID GetFirstPlayableCompanyID();
#endif /* COMPANY_FUNC_H */
diff --git a/src/company_gui.cpp b/src/company_gui.cpp
index abf02b2ac2..544e1777d6 100644
--- a/src/company_gui.cpp
+++ b/src/company_gui.cpp
@@ -38,6 +38,11 @@
#include "widget_type.h"
#include "zoom_func.h"
#include "sortlist_type.h"
+#include "company_cmd.h"
+#include "economy_cmd.h"
+#include "group_cmd.h"
+#include "misc_cmd.h"
+#include "object_cmd.h"
#include "widgets/company_widget.h"
@@ -45,90 +50,124 @@
/** Company GUI constants. */
-static const uint EXP_LINESPACE = 2; ///< Amount of vertical space for a horizontal (sub-)total line.
-static const uint EXP_BLOCKSPACE = 10; ///< Amount of vertical space between two blocks of numbers.
-
static void DoSelectCompanyManagerFace(Window *parent);
static void ShowCompanyInfrastructure(CompanyID company);
-/** Standard unsorted list of expenses. */
-static ExpensesType _expenses_list_1[] = {
- EXPENSES_CONSTRUCTION,
- EXPENSES_NEW_VEHICLES,
- EXPENSES_TRAIN_RUN,
- EXPENSES_ROADVEH_RUN,
- EXPENSES_AIRCRAFT_RUN,
- EXPENSES_SHIP_RUN,
- EXPENSES_PROPERTY,
- EXPENSES_TRAIN_INC,
- EXPENSES_ROADVEH_INC,
- EXPENSES_AIRCRAFT_INC,
- EXPENSES_SHIP_INC,
- EXPENSES_LOAN_INT,
- EXPENSES_OTHER,
+/** List of revenues. */
+static ExpensesType _expenses_list_revenue[] = {
+ EXPENSES_TRAIN_REVENUE,
+ EXPENSES_ROADVEH_REVENUE,
+ EXPENSES_AIRCRAFT_REVENUE,
+ EXPENSES_SHIP_REVENUE,
};
-/** Grouped list of expenses. */
-static ExpensesType _expenses_list_2[] = {
- EXPENSES_TRAIN_INC,
- EXPENSES_ROADVEH_INC,
- EXPENSES_AIRCRAFT_INC,
- EXPENSES_SHIP_INC,
- INVALID_EXPENSES,
+/** List of operating expenses. */
+static ExpensesType _expenses_list_operating_costs[] = {
EXPENSES_TRAIN_RUN,
EXPENSES_ROADVEH_RUN,
EXPENSES_AIRCRAFT_RUN,
EXPENSES_SHIP_RUN,
EXPENSES_PROPERTY,
- EXPENSES_LOAN_INT,
- INVALID_EXPENSES,
+ EXPENSES_LOAN_INTEREST,
+};
+
+/** List of capital expenses. */
+static ExpensesType _expenses_list_capital_costs[] = {
EXPENSES_CONSTRUCTION,
EXPENSES_NEW_VEHICLES,
EXPENSES_OTHER,
- INVALID_EXPENSES,
};
/** Expense list container. */
struct ExpensesList {
const ExpensesType *et; ///< Expenses items.
const uint length; ///< Number of items in list.
- const uint num_subtotals; ///< Number of sub-totals in the list.
- ExpensesList(ExpensesType *et, int length, int num_subtotals) : et(et), length(length), num_subtotals(num_subtotals)
+ ExpensesList(ExpensesType *et, int length) : et(et), length(length)
{
}
uint GetHeight() const
{
- /* heading + line + texts of expenses + sub-totals + total line + total text */
- return FONT_HEIGHT_NORMAL + EXP_LINESPACE + this->length * FONT_HEIGHT_NORMAL + num_subtotals * (EXP_BLOCKSPACE + EXP_LINESPACE) + EXP_LINESPACE + FONT_HEIGHT_NORMAL;
+ /* Add up the height of all the lines. */
+ return this->length * FONT_HEIGHT_NORMAL;
}
/** Compute width of the expenses categories in pixels. */
- uint GetCategoriesWidth() const
+ uint GetListWidth() const
{
uint width = 0;
- bool invalid_expenses_measured = false; // Measure 'Total' width only once.
for (uint i = 0; i < this->length; i++) {
ExpensesType et = this->et[i];
- if (et == INVALID_EXPENSES) {
- if (!invalid_expenses_measured) {
- width = std::max(width, GetStringBoundingBox(STR_FINANCES_TOTAL_CAPTION).width);
- invalid_expenses_measured = true;
- }
- } else {
- width = std::max(width, GetStringBoundingBox(STR_FINANCES_SECTION_CONSTRUCTION + et).width);
- }
+ width = std::max(width, GetStringBoundingBox(STR_FINANCES_SECTION_CONSTRUCTION + et).width);
}
return width;
}
};
+/** Types of expense lists */
static const ExpensesList _expenses_list_types[] = {
- ExpensesList(_expenses_list_1, lengthof(_expenses_list_1), 0),
- ExpensesList(_expenses_list_2, lengthof(_expenses_list_2), 3),
+ ExpensesList(_expenses_list_revenue, lengthof(_expenses_list_revenue)),
+ ExpensesList(_expenses_list_operating_costs, lengthof(_expenses_list_operating_costs)),
+ ExpensesList(_expenses_list_capital_costs, lengthof(_expenses_list_capital_costs)),
};
+/**
+ * Get the total height of the "categories" column.
+ * @return The total height in pixels.
+ */
+static uint GetTotalCategoriesHeight()
+{
+ /* There's an empty line and blockspace on the year row */
+ uint total_height = FONT_HEIGHT_NORMAL + WidgetDimensions::scaled.vsep_wide;
+
+ for (uint i = 0; i < lengthof(_expenses_list_types); i++) {
+ /* Title + expense list + total line + total + blockspace after category */
+ total_height += FONT_HEIGHT_NORMAL + _expenses_list_types[i].GetHeight() + WidgetDimensions::scaled.vsep_normal + FONT_HEIGHT_NORMAL + WidgetDimensions::scaled.vsep_wide;
+ }
+
+ /* Total income */
+ total_height += WidgetDimensions::scaled.vsep_normal + FONT_HEIGHT_NORMAL + WidgetDimensions::scaled.vsep_wide;
+
+ return total_height;
+}
+
+/**
+ * Get the required width of the "categories" column, equal to the widest element.
+ * @return The required width in pixels.
+ */
+static uint GetMaxCategoriesWidth()
+{
+ uint max_width = 0;
+
+ /* Loop through categories to check max widths. */
+ for (uint i = 0; i < lengthof(_expenses_list_types); i++) {
+ /* Title of category */
+ max_width = std::max(max_width, GetStringBoundingBox(STR_FINANCES_REVENUE_TITLE + i).width);
+ /* Entries in category */
+ max_width = std::max(max_width, _expenses_list_types[i].GetListWidth() + WidgetDimensions::scaled.hsep_indent);
+ }
+
+ return max_width;
+}
+
+/**
+ * Draw a category of expenses (revenue, operating expenses, capital expenses).
+ */
+static void DrawCategory(const Rect &r, int start_y, ExpensesList list)
+{
+ Rect tr = r.Indent(WidgetDimensions::scaled.hsep_indent, _current_text_dir == TD_RTL);
+
+ tr.top = start_y;
+ ExpensesType et;
+
+ for (uint i = 0; i < list.length; i++) {
+ et = list.et[i];
+ DrawString(tr, STR_FINANCES_SECTION_CONSTRUCTION + et);
+ tr.top += FONT_HEIGHT_NORMAL;
+ }
+}
+
/**
* Draw the expenses categories.
* @param r Available space for drawing.
@@ -136,25 +175,32 @@ static const ExpensesList _expenses_list_types[] = {
*/
static void DrawCategories(const Rect &r)
{
- int y = r.top;
+ /* Start with an empty space in the year row, plus the blockspace under the year. */
+ int y = r.top + FONT_HEIGHT_NORMAL + WidgetDimensions::scaled.vsep_wide;
- DrawString(r.left, r.right, y, STR_FINANCES_EXPENDITURE_INCOME_TITLE, TC_FROMSTRING, SA_HOR_CENTER, true);
- y += FONT_HEIGHT_NORMAL + EXP_LINESPACE;
+ for (uint i = 0; i < lengthof(_expenses_list_types); i++) {
+ /* Draw category title and advance y */
+ DrawString(r.left, r.right, y, (STR_FINANCES_REVENUE_TITLE + i), TC_FROMSTRING, SA_LEFT);
+ y += FONT_HEIGHT_NORMAL;
- int type = _settings_client.gui.expenses_layout;
- for (uint i = 0; i < _expenses_list_types[type].length; i++) {
- const ExpensesType et = _expenses_list_types[type].et[i];
- if (et == INVALID_EXPENSES) {
- y += EXP_LINESPACE;
- DrawString(r.left, r.right, y, STR_FINANCES_TOTAL_CAPTION, TC_FROMSTRING, SA_RIGHT);
- y += FONT_HEIGHT_NORMAL + EXP_BLOCKSPACE;
- } else {
- DrawString(r.left, r.right, y, STR_FINANCES_SECTION_CONSTRUCTION + et);
- y += FONT_HEIGHT_NORMAL;
- }
+ /* Draw category items and advance y */
+ DrawCategory(r, y, _expenses_list_types[i]);
+ y += _expenses_list_types[i].GetHeight();
+
+ /* Advance y by the height of the horizontal line between amounts and subtotal */
+ y += WidgetDimensions::scaled.vsep_normal;
+
+ /* Draw category total and advance y */
+ DrawString(r.left, r.right, y, STR_FINANCES_TOTAL_CAPTION, TC_FROMSTRING, SA_RIGHT);
+ y += FONT_HEIGHT_NORMAL;
+
+ /* Advance y by a blockspace after this category block */
+ y += WidgetDimensions::scaled.vsep_wide;
}
- DrawString(r.left, r.right, y + EXP_LINESPACE, STR_FINANCES_TOTAL_CAPTION, TC_FROMSTRING, SA_RIGHT);
+ /* Draw total profit/loss */
+ y += WidgetDimensions::scaled.vsep_normal;
+ DrawString(r.left, r.right, y, STR_FINANCES_PROFIT, TC_FROMSTRING, SA_LEFT);
}
/**
@@ -163,18 +209,49 @@ static void DrawCategories(const Rect &r)
* @param left Left coordinate of the space to draw in.
* @param right Right coordinate of the space to draw in.
* @param top Top coordinate of the space to draw in.
+ * @param colour The TextColour of the string.
*/
-static void DrawPrice(Money amount, int left, int right, int top)
+static void DrawPrice(Money amount, int left, int right, int top, TextColour colour)
{
StringID str = STR_FINANCES_NEGATIVE_INCOME;
- if (amount < 0) {
+ if (amount == 0) {
+ str = STR_FINANCES_ZERO_INCOME;
+ } else if (amount < 0) {
amount = -amount;
- str++;
+ str = STR_FINANCES_POSITIVE_INCOME;
}
SetDParam(0, amount);
- DrawString(left, right, top, str, TC_FROMSTRING, SA_RIGHT);
+ DrawString(left, right, top, str, colour, SA_RIGHT);
}
+/**
+ * Draw a category of expenses/revenues in the year column.
+ * @return The income sum of the category.
+ */
+static Money DrawYearCategory (const Rect &r, int start_y, ExpensesList list, const Money(*tbl)[EXPENSES_END])
+{
+ int y = start_y;
+ ExpensesType et;
+ Money sum = 0;
+
+ for (uint i = 0; i < list.length; i++) {
+ et = list.et[i];
+ Money cost = (*tbl)[et];
+ sum += cost;
+ if (cost != 0) DrawPrice(cost, r.left, r.right, y, TC_BLACK);
+ y += FONT_HEIGHT_NORMAL;
+ }
+
+ /* Draw the total at the bottom of the category. */
+ GfxFillRect(r.left, y, r.right, y, PC_BLACK);
+ y += WidgetDimensions::scaled.vsep_normal;
+ if (sum != 0) DrawPrice(sum, r.left, r.right, y, TC_WHITE);
+
+ /* Return the sum for the yearly total. */
+ return sum;
+}
+
+
/**
* Draw a column with prices.
* @param r Available space for drawing.
@@ -185,35 +262,25 @@ static void DrawPrice(Money amount, int left, int right, int top)
static void DrawYearColumn(const Rect &r, int year, const Money (*tbl)[EXPENSES_END])
{
int y = r.top;
+ Money sum;
+ /* Year header */
SetDParam(0, year);
DrawString(r.left, r.right, y, STR_FINANCES_YEAR, TC_FROMSTRING, SA_RIGHT, true);
- y += FONT_HEIGHT_NORMAL + EXP_LINESPACE;
+ y += FONT_HEIGHT_NORMAL + WidgetDimensions::scaled.vsep_wide;
- Money sum = 0;
- Money subtotal = 0;
- int type = _settings_client.gui.expenses_layout;
- for (uint i = 0; i < _expenses_list_types[type].length; i++) {
- const ExpensesType et = _expenses_list_types[type].et[i];
- if (et == INVALID_EXPENSES) {
- Money cost = subtotal;
- subtotal = 0;
- GfxFillRect(r.left, y, r.right, y, PC_BLACK);
- y += EXP_LINESPACE;
- DrawPrice(cost, r.left, r.right, y);
- y += FONT_HEIGHT_NORMAL + EXP_BLOCKSPACE;
- } else {
- Money cost = (*tbl)[et];
- subtotal += cost;
- sum += cost;
- if (cost != 0) DrawPrice(cost, r.left, r.right, y);
- y += FONT_HEIGHT_NORMAL;
- }
+ /* Categories */
+ for (uint i = 0; i < lengthof(_expenses_list_types); i++) {
+ y += FONT_HEIGHT_NORMAL;
+ sum += DrawYearCategory(r, y, _expenses_list_types[i], tbl);
+ /* Expense list + expense category title + expense category total + blockspace after category */
+ y += _expenses_list_types[i].GetHeight() + WidgetDimensions::scaled.vsep_normal + FONT_HEIGHT_NORMAL + WidgetDimensions::scaled.vsep_wide;
}
+ /* Total income. */
GfxFillRect(r.left, y, r.right, y, PC_BLACK);
- y += EXP_LINESPACE;
- DrawPrice(sum, r.left, r.right, y);
+ y += WidgetDimensions::scaled.vsep_normal;
+ DrawPrice(sum, r.left, r.right, y, TC_WHITE);
}
static const NWidgetPart _nested_company_finances_widgets[] = {
@@ -226,7 +293,7 @@ static const NWidgetPart _nested_company_finances_widgets[] = {
EndContainer(),
NWidget(NWID_SELECTION, INVALID_COLOUR, WID_CF_SEL_PANEL),
NWidget(WWT_PANEL, COLOUR_GREY),
- NWidget(NWID_HORIZONTAL), SetPadding(WD_FRAMERECT_TOP, WD_FRAMERECT_RIGHT, WD_FRAMERECT_BOTTOM, WD_FRAMERECT_LEFT), SetPIP(0, 9, 0),
+ NWidget(NWID_HORIZONTAL), SetPadding(WidgetDimensions::unscaled.framerect), SetPIP(0, WidgetDimensions::unscaled.hsep_wide, 0),
NWidget(WWT_EMPTY, COLOUR_GREY, WID_CF_EXPS_CATEGORY), SetMinimalSize(120, 0), SetFill(0, 0),
NWidget(WWT_EMPTY, COLOUR_GREY, WID_CF_EXPS_PRICE1), SetMinimalSize(86, 0), SetFill(0, 0),
NWidget(WWT_EMPTY, COLOUR_GREY, WID_CF_EXPS_PRICE2), SetMinimalSize(86, 0), SetFill(0, 0),
@@ -235,24 +302,26 @@ static const NWidgetPart _nested_company_finances_widgets[] = {
EndContainer(),
EndContainer(),
NWidget(WWT_PANEL, COLOUR_GREY),
- NWidget(NWID_HORIZONTAL), SetPadding(WD_FRAMERECT_TOP, WD_FRAMERECT_RIGHT, WD_FRAMERECT_BOTTOM, WD_FRAMERECT_LEFT),
+ NWidget(NWID_HORIZONTAL), SetPadding(WidgetDimensions::unscaled.framerect),
NWidget(NWID_VERTICAL), // Vertical column with 'bank balance', 'loan'
- NWidget(WWT_TEXT, COLOUR_GREY), SetDataTip(STR_FINANCES_BANK_BALANCE_TITLE, STR_NULL), SetFill(1, 0),
+ NWidget(WWT_TEXT, COLOUR_GREY), SetDataTip(STR_FINANCES_OWN_FUNDS_TITLE, STR_NULL), SetFill(1, 0),
NWidget(WWT_TEXT, COLOUR_GREY), SetDataTip(STR_FINANCES_LOAN_TITLE, STR_NULL), SetFill(1, 0),
+ NWidget(NWID_SPACER), SetMinimalSize(0, 2), SetFill(1, 0),
+ NWidget(WWT_TEXT, COLOUR_GREY), SetDataTip(STR_FINANCES_BANK_BALANCE_TITLE, STR_NULL), SetFill(1, 0),
NWidget(NWID_SPACER), SetFill(0, 1),
EndContainer(),
NWidget(NWID_SPACER), SetFill(0, 0), SetMinimalSize(30, 0),
NWidget(NWID_VERTICAL), // Vertical column with bank balance amount, loan amount, and total.
- NWidget(WWT_TEXT, COLOUR_GREY, WID_CF_BALANCE_VALUE), SetDataTip(STR_FINANCES_TOTAL_CURRENCY, STR_NULL), SetAlignment(SA_VERT_CENTER | SA_RIGHT),
+ NWidget(WWT_TEXT, COLOUR_GREY, WID_CF_OWN_VALUE), SetDataTip(STR_FINANCES_TOTAL_CURRENCY, STR_NULL), SetAlignment(SA_VERT_CENTER | SA_RIGHT),
NWidget(WWT_TEXT, COLOUR_GREY, WID_CF_LOAN_VALUE), SetDataTip(STR_FINANCES_TOTAL_CURRENCY, STR_NULL), SetAlignment(SA_VERT_CENTER | SA_RIGHT),
- NWidget(WWT_EMPTY, COLOUR_GREY, WID_CF_LOAN_LINE), SetMinimalSize(0, 2), SetFill(1, 0),
- NWidget(WWT_TEXT, COLOUR_GREY, WID_CF_TOTAL_VALUE), SetDataTip(STR_FINANCES_TOTAL_CURRENCY, STR_NULL), SetAlignment(SA_VERT_CENTER | SA_RIGHT),
+ NWidget(WWT_EMPTY, COLOUR_GREY, WID_CF_BALANCE_LINE), SetMinimalSize(0, 2), SetFill(1, 0),
+ NWidget(WWT_TEXT, COLOUR_GREY, WID_CF_BALANCE_VALUE), SetDataTip(STR_FINANCES_BANK_BALANCE, STR_NULL), SetAlignment(SA_VERT_CENTER | SA_RIGHT),
EndContainer(),
NWidget(NWID_SELECTION, INVALID_COLOUR, WID_CF_SEL_MAXLOAN),
NWidget(NWID_HORIZONTAL),
NWidget(NWID_SPACER), SetFill(0, 1), SetMinimalSize(25, 0),
NWidget(NWID_VERTICAL), // Max loan information
- NWidget(WWT_EMPTY, COLOUR_GREY, WID_CF_MAXLOAN_GAP), SetFill(0, 0),
+ NWidget(WWT_TEXT, COLOUR_GREY, WID_CF_INTEREST_RATE), SetDataTip(STR_FINANCES_INTEREST_RATE, STR_NULL),
NWidget(WWT_TEXT, COLOUR_GREY, WID_CF_MAXLOAN_VALUE), SetDataTip(STR_FINANCES_MAX_LOAN, STR_NULL),
NWidget(NWID_SPACER), SetFill(0, 1),
EndContainer(),
@@ -305,12 +374,16 @@ struct CompanyFinancesWindow : Window {
break;
}
- case WID_CF_TOTAL_VALUE: {
+ case WID_CF_OWN_VALUE: {
const Company *c = Company::Get((CompanyID)this->window_number);
SetDParam(0, c->money - c->current_loan);
break;
}
+ case WID_CF_INTEREST_RATE:
+ SetDParam(0, _settings_game.difficulty.initial_interest);
+ break;
+
case WID_CF_MAXLOAN_VALUE:
SetDParam(0, _economy.max_loan);
break;
@@ -324,27 +397,26 @@ struct CompanyFinancesWindow : Window {
void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) override
{
- int type = _settings_client.gui.expenses_layout;
switch (widget) {
case WID_CF_EXPS_CATEGORY:
- size->width = _expenses_list_types[type].GetCategoriesWidth();
- size->height = _expenses_list_types[type].GetHeight();
+ size->width = GetMaxCategoriesWidth();
+ size->height = GetTotalCategoriesHeight();
break;
case WID_CF_EXPS_PRICE1:
case WID_CF_EXPS_PRICE2:
case WID_CF_EXPS_PRICE3:
- size->height = _expenses_list_types[type].GetHeight();
+ size->height = GetTotalCategoriesHeight();
FALLTHROUGH;
case WID_CF_BALANCE_VALUE:
case WID_CF_LOAN_VALUE:
- case WID_CF_TOTAL_VALUE:
+ case WID_CF_OWN_VALUE:
SetDParamMaxValue(0, CompanyFinancesWindow::max_money);
size->width = std::max(GetStringBoundingBox(STR_FINANCES_NEGATIVE_INCOME).width, GetStringBoundingBox(STR_FINANCES_POSITIVE_INCOME).width) + padding.width;
break;
- case WID_CF_MAXLOAN_GAP:
+ case WID_CF_INTEREST_RATE:
size->height = FONT_HEIGHT_NORMAL;
break;
}
@@ -369,7 +441,7 @@ struct CompanyFinancesWindow : Window {
break;
}
- case WID_CF_LOAN_LINE:
+ case WID_CF_BALANCE_LINE:
GfxFillRect(r.left, r.top, r.right, r.top, PC_BLACK);
break;
}
@@ -395,8 +467,7 @@ struct CompanyFinancesWindow : Window {
if (!this->IsShaded()) {
if (!this->small) {
/* Check that the expenses panel height matches the height needed for the layout. */
- int type = _settings_client.gui.expenses_layout;
- if (_expenses_list_types[type].GetHeight() != this->GetWidget(WID_CF_EXPS_CATEGORY)->current_y) {
+ if (GetTotalCategoriesHeight() != this->GetWidget(WID_CF_EXPS_CATEGORY)->current_y) {
this->SetupWidgets();
this->ReInit();
return;
@@ -436,11 +507,11 @@ struct CompanyFinancesWindow : Window {
break;
case WID_CF_INCREASE_LOAN: // increase loan
- DoCommandP(0, 0, _ctrl_pressed, CMD_INCREASE_LOAN | CMD_MSG(STR_ERROR_CAN_T_BORROW_ANY_MORE_MONEY));
+ Command::Post(STR_ERROR_CAN_T_BORROW_ANY_MORE_MONEY, _ctrl_pressed ? LoanCommand::Max : LoanCommand::Interval, 0);
break;
case WID_CF_REPAY_LOAN: // repay loan
- DoCommandP(0, 0, _ctrl_pressed, CMD_DECREASE_LOAN | CMD_MSG(STR_ERROR_CAN_T_REPAY_LOAN));
+ Command::Post(STR_ERROR_CAN_T_REPAY_LOAN, _ctrl_pressed ? LoanCommand::Max : LoanCommand::Interval, 0);
break;
case WID_CF_INFRASTRUCTURE: // show infrastructure details
@@ -532,23 +603,20 @@ public:
return true;
}
- void Draw(int left, int right, int top, int bottom, bool sel, Colours bg_colour) const override
+ void Draw(const Rect &r, bool sel, Colours bg_colour) const override
{
bool rtl = _current_text_dir == TD_RTL;
- int height = bottom - top;
- int icon_y_offset = height / 2;
- int text_y_offset = (height - FONT_HEIGHT_NORMAL) / 2 + 1;
+ int icon_y = CenterBounds(r.top, r.bottom, 0);
+ int text_y = CenterBounds(r.top, r.bottom, FONT_HEIGHT_NORMAL);
+ Rect tr = r.Shrink(WidgetDimensions::scaled.dropdowntext);
DrawSprite(SPR_VEH_BUS_SIDE_VIEW, PALETTE_RECOLOUR_START + (this->result % COLOUR_END),
- rtl ? right - 2 - ScaleGUITrad(14) : left + ScaleGUITrad(14) + 2,
- top + icon_y_offset);
- DrawString(rtl ? left + 2 : left + ScaleGUITrad(28) + 4,
- rtl ? right - ScaleGUITrad(28) - 4 : right - 2,
- top + text_y_offset, this->String(), sel ? TC_WHITE : TC_BLACK);
+ rtl ? tr.right - ScaleGUITrad(14) : tr.left + ScaleGUITrad(14),
+ icon_y);
+ tr = tr.Indent(ScaleGUITrad(28) + WidgetDimensions::scaled.hsep_normal, rtl);
+ DrawString(tr.left, tr.right, text_y, this->String(), sel ? TC_WHITE : TC_BLACK);
}
};
-static const int LEVEL_WIDTH = 10; ///< Indenting width of a sub-group in pixels
-
typedef GUIList GUIGroupList;
/** Company livery colour scheme window. */
@@ -753,14 +821,14 @@ public:
}
}
- size->width = std::max(size->width, 5 + d.width + WD_FRAMERECT_RIGHT);
+ size->width = std::max(size->width, 5 + d.width + padding.width);
break;
}
case WID_SCL_MATRIX: {
/* 11 items in the default rail class */
this->square = GetSpriteSize(SPR_SQUARE);
- this->line_height = std::max(this->square.height, (uint)FONT_HEIGHT_NORMAL) + 4;
+ this->line_height = std::max(this->square.height, (uint)FONT_HEIGHT_NORMAL) + padding.height;
size->height = 11 * this->line_height;
resize->width = 1;
@@ -777,7 +845,7 @@ public:
case WID_SCL_PRI_COL_DROPDOWN: {
this->square = GetSpriteSize(SPR_SQUARE);
- int string_padding = this->square.width + NWidgetScrollbar::GetVerticalDimension().width + 10;
+ int string_padding = this->square.width + WidgetDimensions::scaled.hsep_normal + padding.width;
for (const StringID *id = _colour_dropdown; id != endof(_colour_dropdown); id++) {
size->width = std::max(size->width, GetStringBoundingBox(*id).width + string_padding);
}
@@ -847,40 +915,41 @@ public:
bool rtl = _current_text_dir == TD_RTL;
- /* Horizontal coordinates of scheme name column. */
+ /* Coordinates of scheme name column. */
const NWidgetBase *nwi = this->GetWidget(WID_SCL_SPACER_DROPDOWN);
- int sch_left = nwi->pos_x;
- int sch_right = sch_left + nwi->current_x - 1;
- /* Horizontal coordinates of first dropdown. */
+ Rect sch = nwi->GetCurrentRect().Shrink(WidgetDimensions::scaled.framerect);
+ /* Coordinates of first dropdown. */
nwi = this->GetWidget(WID_SCL_PRI_COL_DROPDOWN);
- int pri_left = nwi->pos_x;
- int pri_right = pri_left + nwi->current_x - 1;
- /* Horizontal coordinates of second dropdown. */
+ Rect pri = nwi->GetCurrentRect().Shrink(WidgetDimensions::scaled.framerect);
+ /* Coordinates of second dropdown. */
nwi = this->GetWidget(WID_SCL_SEC_COL_DROPDOWN);
- int sec_left = nwi->pos_x;
- int sec_right = sec_left + nwi->current_x - 1;
+ Rect sec = nwi->GetCurrentRect().Shrink(WidgetDimensions::scaled.framerect);
- int text_left = (rtl ? (uint)WD_FRAMERECT_LEFT : (this->square.width + 5));
- int text_right = (rtl ? (this->square.width + 5) : (uint)WD_FRAMERECT_RIGHT);
+ Rect pri_squ = pri.WithWidth(this->square.width, rtl);
+ Rect sec_squ = sec.WithWidth(this->square.width, rtl);
- int square_offs = (this->line_height - this->square.height) / 2 + 1;
- int text_offs = (this->line_height - FONT_HEIGHT_NORMAL) / 2 + 1;
+ pri = pri.Indent(this->square.width + WidgetDimensions::scaled.hsep_normal, rtl);
+ sec = sec.Indent(this->square.width + WidgetDimensions::scaled.hsep_normal, rtl);
- int y = r.top;
+ Rect ir = r.WithHeight(this->resize.step_height).Shrink(WidgetDimensions::scaled.matrix);
+ int square_offs = (ir.Height() - this->square.height) / 2;
+ int text_offs = (ir.Height() - FONT_HEIGHT_NORMAL) / 2;
+
+ int y = ir.top;
/* Helper function to draw livery info. */
auto draw_livery = [&](StringID str, const Livery &liv, bool sel, bool def, int indent) {
/* Livery Label. */
- DrawString(sch_left + WD_FRAMERECT_LEFT + (rtl ? 0 : indent), sch_right - WD_FRAMERECT_RIGHT - (rtl ? indent : 0), y + text_offs, str, sel ? TC_WHITE : TC_BLACK);
+ DrawString(sch.left + (rtl ? 0 : indent), sch.right - (rtl ? indent : 0), y + text_offs, str, sel ? TC_WHITE : TC_BLACK);
/* Text below the first dropdown. */
- DrawSprite(SPR_SQUARE, GENERAL_SPRITE_COLOUR(liv.colour1), (rtl ? pri_right - (this->square.width + 5) + WD_FRAMERECT_RIGHT : pri_left) + WD_FRAMERECT_LEFT, y + square_offs);
- DrawString(pri_left + text_left, pri_right - text_right, y + text_offs, (def || HasBit(liv.in_use, 0)) ? STR_COLOUR_DARK_BLUE + liv.colour1 : STR_COLOUR_DEFAULT, sel ? TC_WHITE : TC_GOLD);
+ DrawSprite(SPR_SQUARE, GENERAL_SPRITE_COLOUR(liv.colour1), pri_squ.left, y + square_offs);
+ DrawString(pri.left, pri.right, y + text_offs, (def || HasBit(liv.in_use, 0)) ? STR_COLOUR_DARK_BLUE + liv.colour1 : STR_COLOUR_DEFAULT, sel ? TC_WHITE : TC_GOLD);
/* Text below the second dropdown. */
- if (sec_right > sec_left) { // Second dropdown has non-zero size.
- DrawSprite(SPR_SQUARE, GENERAL_SPRITE_COLOUR(liv.colour2), (rtl ? sec_right - (this->square.width + 5) + WD_FRAMERECT_RIGHT : sec_left) + WD_FRAMERECT_LEFT, y + square_offs);
- DrawString(sec_left + text_left, sec_right - text_right, y + text_offs, (def || HasBit(liv.in_use, 1)) ? STR_COLOUR_DARK_BLUE + liv.colour2 : STR_COLOUR_DEFAULT, sel ? TC_WHITE : TC_GOLD);
+ if (sec.right > sec.left) { // Second dropdown has non-zero size.
+ DrawSprite(SPR_SQUARE, GENERAL_SPRITE_COLOUR(liv.colour2), sec_squ.left, y + square_offs);
+ DrawString(sec.left, sec.right, y + text_offs, (def || HasBit(liv.in_use, 1)) ? STR_COLOUR_DARK_BLUE + liv.colour2 : STR_COLOUR_DEFAULT, sel ? TC_WHITE : TC_GOLD);
}
y += this->line_height;
@@ -900,7 +969,7 @@ public:
for (uint i = this->vscroll->GetPosition(); i < max; ++i) {
const Group *g = this->groups[i];
SetDParam(0, g->index);
- draw_livery(STR_GROUP_NAME, g->livery, this->sel == g->index, false, this->indents[i] * LEVEL_WIDTH);
+ draw_livery(STR_GROUP_NAME, g->livery, this->sel == g->index, false, this->indents[i] * WidgetDimensions::scaled.hsep_indent);
}
}
}
@@ -996,12 +1065,12 @@ public:
for (LiveryScheme scheme = LS_DEFAULT; scheme < LS_END; scheme++) {
/* Changed colour for the selected scheme, or all visible schemes if CTRL is pressed. */
if (HasBit(this->sel, scheme) || (_ctrl_pressed && _livery_class[scheme] == this->livery_class && HasBit(_loaded_newgrf_features.used_liveries, scheme))) {
- DoCommandP(0, scheme | (widget == WID_SCL_PRI_COL_DROPDOWN ? 0 : 256), index, CMD_SET_COMPANY_COLOUR);
+ Command::Post(scheme, widget == WID_SCL_PRI_COL_DROPDOWN, (Colours)index);
}
}
} else {
/* Setting group livery */
- DoCommandP(0, this->sel, (widget == WID_SCL_PRI_COL_DROPDOWN ? 0 : 256) | (index << 16), CMD_SET_GROUP_LIVERY);
+ Command::Post(this->sel, widget == WID_SCL_PRI_COL_DROPDOWN, (Colours)index);
}
}
@@ -1198,86 +1267,86 @@ static const NWidgetPart _nested_select_company_manager_face_widgets[] = {
EndContainer(),
NWidget(NWID_SPACER), SetMinimalSize(0, 4),
NWidget(NWID_HORIZONTAL),
- NWidget(WWT_TEXT, INVALID_COLOUR, WID_SCMF_HAS_MOUSTACHE_EARRING_TEXT), SetFill(1, 0), SetPadding(0, WD_FRAMERECT_RIGHT, 0, WD_FRAMERECT_LEFT),
+ NWidget(WWT_TEXT, INVALID_COLOUR, WID_SCMF_HAS_MOUSTACHE_EARRING_TEXT), SetFill(1, 0), SetPadding(WidgetDimensions::unscaled.framerect),
SetDataTip(STR_FACE_EYECOLOUR, STR_NULL), SetTextColour(TC_GOLD), SetAlignment(SA_VERT_CENTER | SA_RIGHT),
- NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_HAS_MOUSTACHE_EARRING), SetDataTip(STR_EMPTY, STR_FACE_MOUSTACHE_EARRING_TOOLTIP),
+ NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_HAS_MOUSTACHE_EARRING), SetDataTip(STR_WHITE_STRING, STR_FACE_MOUSTACHE_EARRING_TOOLTIP),
EndContainer(),
NWidget(NWID_HORIZONTAL),
- NWidget(WWT_TEXT, INVALID_COLOUR, WID_SCMF_HAS_GLASSES_TEXT), SetFill(1, 0), SetPadding(0, WD_FRAMERECT_RIGHT, 0, WD_FRAMERECT_LEFT),
+ NWidget(WWT_TEXT, INVALID_COLOUR, WID_SCMF_HAS_GLASSES_TEXT), SetFill(1, 0), SetPadding(WidgetDimensions::unscaled.framerect),
SetDataTip(STR_FACE_GLASSES, STR_NULL), SetTextColour(TC_GOLD), SetAlignment(SA_VERT_CENTER | SA_RIGHT),
- NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_HAS_GLASSES), SetDataTip(STR_EMPTY, STR_FACE_GLASSES_TOOLTIP),
+ NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_HAS_GLASSES), SetDataTip(STR_WHITE_STRING, STR_FACE_GLASSES_TOOLTIP),
EndContainer(),
NWidget(NWID_SPACER), SetMinimalSize(0, 2), SetFill(1, 0),
NWidget(NWID_HORIZONTAL),
- NWidget(WWT_TEXT, INVALID_COLOUR, WID_SCMF_HAIR_TEXT), SetFill(1, 0), SetPadding(0, WD_FRAMERECT_RIGHT, 0, WD_FRAMERECT_LEFT),
+ NWidget(WWT_TEXT, INVALID_COLOUR, WID_SCMF_HAIR_TEXT), SetFill(1, 0), SetPadding(WidgetDimensions::unscaled.framerect),
SetDataTip(STR_FACE_HAIR, STR_NULL), SetTextColour(TC_GOLD), SetAlignment(SA_VERT_CENTER | SA_RIGHT),
NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_HAIR_L), SetDataTip(AWV_DECREASE, STR_FACE_HAIR_TOOLTIP),
- NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_HAIR), SetDataTip(STR_EMPTY, STR_FACE_HAIR_TOOLTIP),
+ NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_HAIR), SetDataTip(STR_WHITE_STRING, STR_FACE_HAIR_TOOLTIP),
NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_HAIR_R), SetDataTip(AWV_INCREASE, STR_FACE_HAIR_TOOLTIP),
EndContainer(),
NWidget(NWID_HORIZONTAL),
- NWidget(WWT_TEXT, INVALID_COLOUR, WID_SCMF_EYEBROWS_TEXT), SetFill(1, 0), SetPadding(0, WD_FRAMERECT_RIGHT, 0, WD_FRAMERECT_LEFT),
+ NWidget(WWT_TEXT, INVALID_COLOUR, WID_SCMF_EYEBROWS_TEXT), SetFill(1, 0), SetPadding(WidgetDimensions::unscaled.framerect),
SetDataTip(STR_FACE_EYEBROWS, STR_NULL), SetTextColour(TC_GOLD), SetAlignment(SA_VERT_CENTER | SA_RIGHT),
NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_EYEBROWS_L), SetDataTip(AWV_DECREASE, STR_FACE_EYEBROWS_TOOLTIP),
- NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_EYEBROWS), SetDataTip(STR_EMPTY, STR_FACE_EYEBROWS_TOOLTIP),
+ NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_EYEBROWS), SetDataTip(STR_WHITE_STRING, STR_FACE_EYEBROWS_TOOLTIP),
NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_EYEBROWS_R), SetDataTip(AWV_INCREASE, STR_FACE_EYEBROWS_TOOLTIP),
EndContainer(),
NWidget(NWID_HORIZONTAL),
- NWidget(WWT_TEXT, INVALID_COLOUR, WID_SCMF_EYECOLOUR_TEXT), SetFill(1, 0), SetPadding(0, WD_FRAMERECT_RIGHT, 0, WD_FRAMERECT_LEFT),
+ NWidget(WWT_TEXT, INVALID_COLOUR, WID_SCMF_EYECOLOUR_TEXT), SetFill(1, 0), SetPadding(WidgetDimensions::unscaled.framerect),
SetDataTip(STR_FACE_EYECOLOUR, STR_NULL), SetTextColour(TC_GOLD), SetAlignment(SA_VERT_CENTER | SA_RIGHT),
NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_EYECOLOUR_L), SetDataTip(AWV_DECREASE, STR_FACE_EYECOLOUR_TOOLTIP),
- NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_EYECOLOUR), SetDataTip(STR_EMPTY, STR_FACE_EYECOLOUR_TOOLTIP),
+ NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_EYECOLOUR), SetDataTip(STR_WHITE_STRING, STR_FACE_EYECOLOUR_TOOLTIP),
NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_EYECOLOUR_R), SetDataTip(AWV_INCREASE, STR_FACE_EYECOLOUR_TOOLTIP),
EndContainer(),
NWidget(NWID_HORIZONTAL),
- NWidget(WWT_TEXT, INVALID_COLOUR, WID_SCMF_GLASSES_TEXT), SetFill(1, 0), SetPadding(0, WD_FRAMERECT_RIGHT, 0, WD_FRAMERECT_LEFT),
+ NWidget(WWT_TEXT, INVALID_COLOUR, WID_SCMF_GLASSES_TEXT), SetFill(1, 0), SetPadding(WidgetDimensions::unscaled.framerect),
SetDataTip(STR_FACE_GLASSES, STR_NULL), SetTextColour(TC_GOLD), SetAlignment(SA_VERT_CENTER | SA_RIGHT),
NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_GLASSES_L), SetDataTip(AWV_DECREASE, STR_FACE_GLASSES_TOOLTIP_2),
- NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_GLASSES), SetDataTip(STR_EMPTY, STR_FACE_GLASSES_TOOLTIP_2),
+ NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_GLASSES), SetDataTip(STR_WHITE_STRING, STR_FACE_GLASSES_TOOLTIP_2),
NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_GLASSES_R), SetDataTip(AWV_INCREASE, STR_FACE_GLASSES_TOOLTIP_2),
EndContainer(),
NWidget(NWID_HORIZONTAL),
- NWidget(WWT_TEXT, INVALID_COLOUR, WID_SCMF_NOSE_TEXT), SetFill(1, 0), SetPadding(0, WD_FRAMERECT_RIGHT, 0, WD_FRAMERECT_LEFT),
+ NWidget(WWT_TEXT, INVALID_COLOUR, WID_SCMF_NOSE_TEXT), SetFill(1, 0), SetPadding(WidgetDimensions::unscaled.framerect),
SetDataTip(STR_FACE_NOSE, STR_NULL), SetTextColour(TC_GOLD), SetAlignment(SA_VERT_CENTER | SA_RIGHT),
NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_NOSE_L), SetDataTip(AWV_DECREASE, STR_FACE_NOSE_TOOLTIP),
- NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_NOSE), SetDataTip(STR_EMPTY, STR_FACE_NOSE_TOOLTIP),
+ NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_NOSE), SetDataTip(STR_WHITE_STRING, STR_FACE_NOSE_TOOLTIP),
NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_NOSE_R), SetDataTip(AWV_INCREASE, STR_FACE_NOSE_TOOLTIP),
EndContainer(),
NWidget(NWID_HORIZONTAL),
- NWidget(WWT_TEXT, INVALID_COLOUR, WID_SCMF_LIPS_MOUSTACHE_TEXT), SetFill(1, 0), SetPadding(0, WD_FRAMERECT_RIGHT, 0, WD_FRAMERECT_LEFT),
+ NWidget(WWT_TEXT, INVALID_COLOUR, WID_SCMF_LIPS_MOUSTACHE_TEXT), SetFill(1, 0), SetPadding(WidgetDimensions::unscaled.framerect),
SetDataTip(STR_FACE_MOUSTACHE, STR_NULL), SetTextColour(TC_GOLD), SetAlignment(SA_VERT_CENTER | SA_RIGHT),
NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_LIPS_MOUSTACHE_L), SetDataTip(AWV_DECREASE, STR_FACE_LIPS_MOUSTACHE_TOOLTIP),
- NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_LIPS_MOUSTACHE), SetDataTip(STR_EMPTY, STR_FACE_LIPS_MOUSTACHE_TOOLTIP),
+ NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_LIPS_MOUSTACHE), SetDataTip(STR_WHITE_STRING, STR_FACE_LIPS_MOUSTACHE_TOOLTIP),
NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_LIPS_MOUSTACHE_R), SetDataTip(AWV_INCREASE, STR_FACE_LIPS_MOUSTACHE_TOOLTIP),
EndContainer(),
NWidget(NWID_HORIZONTAL),
- NWidget(WWT_TEXT, INVALID_COLOUR, WID_SCMF_CHIN_TEXT), SetFill(1, 0), SetPadding(0, WD_FRAMERECT_RIGHT, 0, WD_FRAMERECT_LEFT),
+ NWidget(WWT_TEXT, INVALID_COLOUR, WID_SCMF_CHIN_TEXT), SetFill(1, 0), SetPadding(WidgetDimensions::unscaled.framerect),
SetDataTip(STR_FACE_CHIN, STR_NULL), SetTextColour(TC_GOLD), SetAlignment(SA_VERT_CENTER | SA_RIGHT),
NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_CHIN_L), SetDataTip(AWV_DECREASE, STR_FACE_CHIN_TOOLTIP),
- NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_CHIN), SetDataTip(STR_EMPTY, STR_FACE_CHIN_TOOLTIP),
+ NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_CHIN), SetDataTip(STR_WHITE_STRING, STR_FACE_CHIN_TOOLTIP),
NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_CHIN_R), SetDataTip(AWV_INCREASE, STR_FACE_CHIN_TOOLTIP),
EndContainer(),
NWidget(NWID_HORIZONTAL),
- NWidget(WWT_TEXT, INVALID_COLOUR, WID_SCMF_JACKET_TEXT), SetFill(1, 0), SetPadding(0, WD_FRAMERECT_RIGHT, 0, WD_FRAMERECT_LEFT),
+ NWidget(WWT_TEXT, INVALID_COLOUR, WID_SCMF_JACKET_TEXT), SetFill(1, 0), SetPadding(WidgetDimensions::unscaled.framerect),
SetDataTip(STR_FACE_JACKET, STR_NULL), SetTextColour(TC_GOLD), SetAlignment(SA_VERT_CENTER | SA_RIGHT),
NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_JACKET_L), SetDataTip(AWV_DECREASE, STR_FACE_JACKET_TOOLTIP),
- NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_JACKET), SetDataTip(STR_EMPTY, STR_FACE_JACKET_TOOLTIP),
+ NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_JACKET), SetDataTip(STR_WHITE_STRING, STR_FACE_JACKET_TOOLTIP),
NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_JACKET_R), SetDataTip(AWV_INCREASE, STR_FACE_JACKET_TOOLTIP),
EndContainer(),
NWidget(NWID_HORIZONTAL),
- NWidget(WWT_TEXT, INVALID_COLOUR, WID_SCMF_COLLAR_TEXT), SetFill(1, 0), SetPadding(0, WD_FRAMERECT_RIGHT, 0, WD_FRAMERECT_LEFT),
+ NWidget(WWT_TEXT, INVALID_COLOUR, WID_SCMF_COLLAR_TEXT), SetFill(1, 0), SetPadding(WidgetDimensions::unscaled.framerect),
SetDataTip(STR_FACE_COLLAR, STR_NULL), SetTextColour(TC_GOLD), SetAlignment(SA_VERT_CENTER | SA_RIGHT),
NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_COLLAR_L), SetDataTip(AWV_DECREASE, STR_FACE_COLLAR_TOOLTIP),
- NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_COLLAR), SetDataTip(STR_EMPTY, STR_FACE_COLLAR_TOOLTIP),
+ NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_COLLAR), SetDataTip(STR_WHITE_STRING, STR_FACE_COLLAR_TOOLTIP),
NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_COLLAR_R), SetDataTip(AWV_INCREASE, STR_FACE_COLLAR_TOOLTIP),
EndContainer(),
EndContainer(),
NWidget(NWID_SELECTION, INVALID_COLOUR, WID_SCMF_SEL_PARTS), // Advanced face parts setting.
NWidget(NWID_HORIZONTAL),
- NWidget(WWT_TEXT, INVALID_COLOUR, WID_SCMF_TIE_EARRING_TEXT), SetFill(1, 0), SetPadding(0, WD_FRAMERECT_RIGHT, 0, WD_FRAMERECT_LEFT),
+ NWidget(WWT_TEXT, INVALID_COLOUR, WID_SCMF_TIE_EARRING_TEXT), SetFill(1, 0), SetPadding(WidgetDimensions::unscaled.framerect),
SetDataTip(STR_FACE_EARRING, STR_NULL), SetTextColour(TC_GOLD), SetAlignment(SA_VERT_CENTER | SA_RIGHT),
NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_TIE_EARRING_L), SetDataTip(AWV_DECREASE, STR_FACE_TIE_EARRING_TOOLTIP),
- NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_TIE_EARRING), SetDataTip(STR_EMPTY, STR_FACE_TIE_EARRING_TOOLTIP),
+ NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_TIE_EARRING), SetDataTip(STR_WHITE_STRING, STR_FACE_TIE_EARRING_TOOLTIP),
NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_TIE_EARRING_R), SetDataTip(AWV_INCREASE, STR_FACE_TIE_EARRING_TOOLTIP),
EndContainer(),
NWidget(NWID_SPACER), SetFill(0, 1),
@@ -1308,29 +1377,26 @@ class SelectCompanyManagerFaceWindow : public Window
Dimension number_dim; ///< Dimension of a number widget of a part in the advanced face window.
/**
- * Draw dynamic a label to the left of the button and a value in the button
+ * Set parameters for value of face control buttons.
*
* @param widget_index index of this widget in the window
- * @param val the value which will be draw
+ * @param val the value which will be displayed
* @param is_bool_widget is it a bool button
*/
- void DrawFaceStringLabel(byte widget_index, uint8 val, bool is_bool_widget) const
+ void SetFaceStringParameters(byte widget_index, uint8 val, bool is_bool_widget) const
{
- StringID str;
const NWidgetCore *nwi_widget = this->GetWidget(widget_index);
- if (!nwi_widget->IsDisabled()) {
+ if (nwi_widget->IsDisabled()) {
+ SetDParam(0, STR_EMPTY);
+ } else {
if (is_bool_widget) {
/* if it a bool button write yes or no */
- str = (val != 0) ? STR_FACE_YES : STR_FACE_NO;
+ SetDParam(0, (val != 0) ? STR_FACE_YES : STR_FACE_NO);
} else {
/* else write the value + 1 */
- SetDParam(0, val + 1);
- str = STR_JUST_INT;
+ SetDParam(0, STR_JUST_INT);
+ SetDParam(1, val + 1);
}
-
- /* Draw the value/bool in white (0xC). If the button clicked adds 1px to x and y text coordinates (IsWindowWidgetLowered()). */
- DrawString(nwi_widget->pos_x + nwi_widget->IsLowered(), nwi_widget->pos_x + nwi_widget->current_x - 1 - nwi_widget->IsLowered(),
- Center(nwi_widget->pos_y + nwi_widget->IsLowered(), nwi_widget->current_y), str, TC_WHITE, SA_HOR_CENTER);
}
}
@@ -1382,8 +1448,8 @@ public:
{
/* Size of the boolean yes/no button. */
Dimension yesno_dim = maxdim(GetStringBoundingBox(STR_FACE_YES), GetStringBoundingBox(STR_FACE_NO));
- yesno_dim.width += WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT;
- yesno_dim.height += WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM;
+ yesno_dim.width += WidgetDimensions::scaled.framerect.Horizontal();
+ yesno_dim.height += WidgetDimensions::scaled.framerect.Vertical();
yesno_dim.width = GetMinButtonSize(yesno_dim.width);
yesno_dim.height = GetMinButtonSize(yesno_dim.height);
@@ -1394,9 +1460,9 @@ public:
SetDParam(0, val);
number_dim = maxdim(number_dim, GetStringBoundingBox(STR_JUST_INT));
}
- uint arrows_width = GetSpriteSize(SPR_ARROW_LEFT).width + GetSpriteSize(SPR_ARROW_RIGHT).width + 2 * (WD_IMGBTN_LEFT + WD_IMGBTN_RIGHT);
- number_dim.width += WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT + arrows_width;
- number_dim.height += WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM;
+ uint arrows_width = GetSpriteSize(SPR_ARROW_LEFT).width + GetSpriteSize(SPR_ARROW_RIGHT).width + 2 * (WidgetDimensions::scaled.imgbtn.Horizontal());
+ number_dim.width += WidgetDimensions::scaled.framerect.Horizontal() + arrows_width;
+ number_dim.height += WidgetDimensions::scaled.framerect.Vertical();
/* Compute width of both buttons. */
yesno_dim.width = std::max(yesno_dim.width, number_dim.width);
number_dim.width = yesno_dim.width - arrows_width;
@@ -1511,65 +1577,70 @@ public:
this->DrawWidgets();
}
- void DrawWidget(const Rect &r, int widget) const override
+ void SetStringParameters(int widget) const override
{
switch (widget) {
case WID_SCMF_HAS_MOUSTACHE_EARRING:
if (this->is_female) { // Only for female faces
- this->DrawFaceStringLabel(WID_SCMF_HAS_MOUSTACHE_EARRING, GetCompanyManagerFaceBits(this->face, CMFV_HAS_TIE_EARRING, this->ge), true);
+ this->SetFaceStringParameters(WID_SCMF_HAS_MOUSTACHE_EARRING, GetCompanyManagerFaceBits(this->face, CMFV_HAS_TIE_EARRING, this->ge), true);
} else { // Only for male faces
- this->DrawFaceStringLabel(WID_SCMF_HAS_MOUSTACHE_EARRING, GetCompanyManagerFaceBits(this->face, CMFV_HAS_MOUSTACHE, this->ge), true);
+ this->SetFaceStringParameters(WID_SCMF_HAS_MOUSTACHE_EARRING, GetCompanyManagerFaceBits(this->face, CMFV_HAS_MOUSTACHE, this->ge), true);
}
break;
case WID_SCMF_TIE_EARRING:
- this->DrawFaceStringLabel(WID_SCMF_TIE_EARRING, GetCompanyManagerFaceBits(this->face, CMFV_TIE_EARRING, this->ge), false);
+ this->SetFaceStringParameters(WID_SCMF_TIE_EARRING, GetCompanyManagerFaceBits(this->face, CMFV_TIE_EARRING, this->ge), false);
break;
case WID_SCMF_LIPS_MOUSTACHE:
if (this->is_moust_male) { // Only for male faces with moustache
- this->DrawFaceStringLabel(WID_SCMF_LIPS_MOUSTACHE, GetCompanyManagerFaceBits(this->face, CMFV_MOUSTACHE, this->ge), false);
+ this->SetFaceStringParameters(WID_SCMF_LIPS_MOUSTACHE, GetCompanyManagerFaceBits(this->face, CMFV_MOUSTACHE, this->ge), false);
} else { // Only for female faces or male faces without moustache
- this->DrawFaceStringLabel(WID_SCMF_LIPS_MOUSTACHE, GetCompanyManagerFaceBits(this->face, CMFV_LIPS, this->ge), false);
+ this->SetFaceStringParameters(WID_SCMF_LIPS_MOUSTACHE, GetCompanyManagerFaceBits(this->face, CMFV_LIPS, this->ge), false);
}
break;
case WID_SCMF_HAS_GLASSES:
- this->DrawFaceStringLabel(WID_SCMF_HAS_GLASSES, GetCompanyManagerFaceBits(this->face, CMFV_HAS_GLASSES, this->ge), true );
+ this->SetFaceStringParameters(WID_SCMF_HAS_GLASSES, GetCompanyManagerFaceBits(this->face, CMFV_HAS_GLASSES, this->ge), true );
break;
case WID_SCMF_HAIR:
- this->DrawFaceStringLabel(WID_SCMF_HAIR, GetCompanyManagerFaceBits(this->face, CMFV_HAIR, this->ge), false);
+ this->SetFaceStringParameters(WID_SCMF_HAIR, GetCompanyManagerFaceBits(this->face, CMFV_HAIR, this->ge), false);
break;
case WID_SCMF_EYEBROWS:
- this->DrawFaceStringLabel(WID_SCMF_EYEBROWS, GetCompanyManagerFaceBits(this->face, CMFV_EYEBROWS, this->ge), false);
+ this->SetFaceStringParameters(WID_SCMF_EYEBROWS, GetCompanyManagerFaceBits(this->face, CMFV_EYEBROWS, this->ge), false);
break;
case WID_SCMF_EYECOLOUR:
- this->DrawFaceStringLabel(WID_SCMF_EYECOLOUR, GetCompanyManagerFaceBits(this->face, CMFV_EYE_COLOUR, this->ge), false);
+ this->SetFaceStringParameters(WID_SCMF_EYECOLOUR, GetCompanyManagerFaceBits(this->face, CMFV_EYE_COLOUR, this->ge), false);
break;
case WID_SCMF_GLASSES:
- this->DrawFaceStringLabel(WID_SCMF_GLASSES, GetCompanyManagerFaceBits(this->face, CMFV_GLASSES, this->ge), false);
+ this->SetFaceStringParameters(WID_SCMF_GLASSES, GetCompanyManagerFaceBits(this->face, CMFV_GLASSES, this->ge), false);
break;
case WID_SCMF_NOSE:
- this->DrawFaceStringLabel(WID_SCMF_NOSE, GetCompanyManagerFaceBits(this->face, CMFV_NOSE, this->ge), false);
+ this->SetFaceStringParameters(WID_SCMF_NOSE, GetCompanyManagerFaceBits(this->face, CMFV_NOSE, this->ge), false);
break;
case WID_SCMF_CHIN:
- this->DrawFaceStringLabel(WID_SCMF_CHIN, GetCompanyManagerFaceBits(this->face, CMFV_CHIN, this->ge), false);
+ this->SetFaceStringParameters(WID_SCMF_CHIN, GetCompanyManagerFaceBits(this->face, CMFV_CHIN, this->ge), false);
break;
case WID_SCMF_JACKET:
- this->DrawFaceStringLabel(WID_SCMF_JACKET, GetCompanyManagerFaceBits(this->face, CMFV_JACKET, this->ge), false);
+ this->SetFaceStringParameters(WID_SCMF_JACKET, GetCompanyManagerFaceBits(this->face, CMFV_JACKET, this->ge), false);
break;
case WID_SCMF_COLLAR:
- this->DrawFaceStringLabel(WID_SCMF_COLLAR, GetCompanyManagerFaceBits(this->face, CMFV_COLLAR, this->ge), false);
+ this->SetFaceStringParameters(WID_SCMF_COLLAR, GetCompanyManagerFaceBits(this->face, CMFV_COLLAR, this->ge), false);
break;
+ }
+ }
+ void DrawWidget(const Rect &r, int widget) const override
+ {
+ switch (widget) {
case WID_SCMF_FACE:
DrawCompanyManagerFace(this->face, Company::Get((CompanyID)this->window_number)->colour, r.left, r.top);
break;
@@ -1589,7 +1660,7 @@ public:
/* OK button */
case WID_SCMF_ACCEPT:
- DoCommandP(0, 0, this->face, CMD_SET_COMPANY_MANAGER_FACE);
+ Command::Post(this->face);
FALLTHROUGH;
/* Cancel button */
@@ -1730,28 +1801,28 @@ static const NWidgetPart _nested_company_infrastructure_widgets[] = {
NWidget(WWT_STICKYBOX, COLOUR_GREY),
EndContainer(),
NWidget(WWT_PANEL, COLOUR_GREY),
- NWidget(NWID_VERTICAL), SetPIP(WD_FRAMERECT_TOP, 4, WD_FRAMETEXT_BOTTOM),
- NWidget(NWID_HORIZONTAL), SetPIP(2, 4, 2),
+ NWidget(NWID_VERTICAL), SetPadding(WidgetDimensions::unscaled.framerect), SetPIP(0, WidgetDimensions::unscaled.vsep_normal, 0),
+ NWidget(NWID_HORIZONTAL), SetPIP(0, WidgetDimensions::unscaled.hsep_wide, 0),
NWidget(WWT_EMPTY, COLOUR_GREY, WID_CI_RAIL_DESC), SetMinimalTextLines(2, 0), SetFill(1, 0),
NWidget(WWT_EMPTY, COLOUR_GREY, WID_CI_RAIL_COUNT), SetMinimalTextLines(2, 0), SetFill(0, 1),
EndContainer(),
- NWidget(NWID_HORIZONTAL), SetPIP(2, 4, 2),
+ NWidget(NWID_HORIZONTAL), SetPIP(0, WidgetDimensions::unscaled.hsep_wide, 0),
NWidget(WWT_EMPTY, COLOUR_GREY, WID_CI_ROAD_DESC), SetMinimalTextLines(2, 0), SetFill(1, 0),
NWidget(WWT_EMPTY, COLOUR_GREY, WID_CI_ROAD_COUNT), SetMinimalTextLines(2, 0), SetFill(0, 1),
EndContainer(),
- NWidget(NWID_HORIZONTAL), SetPIP(2, 4, 2),
+ NWidget(NWID_HORIZONTAL), SetPIP(0, WidgetDimensions::unscaled.hsep_wide, 0),
NWidget(WWT_EMPTY, COLOUR_GREY, WID_CI_TRAM_DESC), SetMinimalTextLines(2, 0), SetFill(1, 0),
NWidget(WWT_EMPTY, COLOUR_GREY, WID_CI_TRAM_COUNT), SetMinimalTextLines(2, 0), SetFill(0, 1),
EndContainer(),
- NWidget(NWID_HORIZONTAL), SetPIP(2, 4, 2),
+ NWidget(NWID_HORIZONTAL), SetPIP(0, WidgetDimensions::unscaled.hsep_wide, 0),
NWidget(WWT_EMPTY, COLOUR_GREY, WID_CI_WATER_DESC), SetMinimalTextLines(2, 0), SetFill(1, 0),
NWidget(WWT_EMPTY, COLOUR_GREY, WID_CI_WATER_COUNT), SetMinimalTextLines(2, 0), SetFill(0, 1),
EndContainer(),
- NWidget(NWID_HORIZONTAL), SetPIP(2, 4, 2),
+ NWidget(NWID_HORIZONTAL), SetPIP(0, WidgetDimensions::unscaled.hsep_wide, 0),
NWidget(WWT_EMPTY, COLOUR_GREY, WID_CI_STATION_DESC), SetMinimalTextLines(3, 0), SetFill(1, 0),
NWidget(WWT_EMPTY, COLOUR_GREY, WID_CI_STATION_COUNT), SetMinimalTextLines(3, 0), SetFill(0, 1),
EndContainer(),
- NWidget(NWID_HORIZONTAL), SetPIP(2, 4, 2),
+ NWidget(NWID_HORIZONTAL), SetPIP(0, WidgetDimensions::unscaled.hsep_wide, 0),
NWidget(WWT_EMPTY, COLOUR_GREY, WID_CI_TOTAL_DESC), SetFill(1, 0),
NWidget(WWT_EMPTY, COLOUR_GREY, WID_CI_TOTAL), SetFill(0, 1),
EndContainer(),
@@ -1852,12 +1923,12 @@ struct CompanyInfrastructureWindow : Window
if (HasBit(this->railtypes, rt)) {
lines++;
SetDParam(0, GetRailTypeInfo(rt)->strings.name);
- size->width = std::max(size->width, GetStringBoundingBox(STR_WHITE_STRING).width + WD_FRAMERECT_LEFT);
+ size->width = std::max(size->width, GetStringBoundingBox(STR_WHITE_STRING).width + WidgetDimensions::scaled.hsep_indent);
}
}
if (this->railtypes != RAILTYPES_NONE) {
lines++;
- size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_INFRASTRUCTURE_VIEW_SIGNALS).width + WD_FRAMERECT_LEFT);
+ size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_INFRASTRUCTURE_VIEW_SIGNALS).width + WidgetDimensions::scaled.hsep_indent);
}
size->height = std::max(size->height, lines * FONT_HEIGHT_NORMAL);
@@ -1874,7 +1945,7 @@ struct CompanyInfrastructureWindow : Window
if (HasBit(this->roadtypes, rt) && RoadTypeIsRoad(rt) == (widget == WID_CI_ROAD_DESC)) {
lines++;
SetDParam(0, GetRoadTypeInfo(rt)->strings.name);
- size->width = std::max(size->width, GetStringBoundingBox(STR_WHITE_STRING).width + WD_FRAMERECT_LEFT);
+ size->width = std::max(size->width, GetStringBoundingBox(STR_WHITE_STRING).width + WidgetDimensions::scaled.hsep_indent);
}
}
@@ -1884,13 +1955,13 @@ struct CompanyInfrastructureWindow : Window
case WID_CI_WATER_DESC:
size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_INFRASTRUCTURE_VIEW_WATER_SECT).width);
- size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_INFRASTRUCTURE_VIEW_CANALS).width + WD_FRAMERECT_LEFT);
+ size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_INFRASTRUCTURE_VIEW_CANALS).width + WidgetDimensions::scaled.hsep_indent);
break;
case WID_CI_STATION_DESC:
size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_INFRASTRUCTURE_VIEW_STATION_SECT).width);
- size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_INFRASTRUCTURE_VIEW_STATIONS).width + WD_FRAMERECT_LEFT);
- size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_INFRASTRUCTURE_VIEW_AIRPORTS).width + WD_FRAMERECT_LEFT);
+ size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_INFRASTRUCTURE_VIEW_STATIONS).width + WidgetDimensions::scaled.hsep_indent);
+ size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_INFRASTRUCTURE_VIEW_AIRPORTS).width + WidgetDimensions::scaled.hsep_indent);
break;
case WID_CI_RAIL_COUNT:
@@ -1924,11 +1995,11 @@ struct CompanyInfrastructureWindow : Window
max_cost = std::max(max_cost, AirportMaintenanceCost(c->index));
SetDParamMaxValue(0, max_val);
- uint count_width = GetStringBoundingBox(STR_WHITE_COMMA).width + 20; // Reserve some wiggle room
+ uint count_width = GetStringBoundingBox(STR_WHITE_COMMA).width + WidgetDimensions::scaled.hsep_indent; // Reserve some wiggle room
if (_settings_game.economy.infrastructure_maintenance) {
SetDParamMaxValue(0, this->GetTotalMaintenanceCost() * 12); // Convert to per year
- this->total_width = GetStringBoundingBox(STR_COMPANY_INFRASTRUCTURE_VIEW_TOTAL).width + 20;
+ this->total_width = GetStringBoundingBox(STR_COMPANY_INFRASTRUCTURE_VIEW_TOTAL).width + WidgetDimensions::scaled.hsep_indent * 2;
size->width = std::max(size->width, this->total_width);
SetDParamMaxValue(0, max_cost * 12); // Convert to per year
@@ -1939,7 +2010,7 @@ struct CompanyInfrastructureWindow : Window
/* Set height of the total line. */
if (widget == WID_CI_TOTAL) {
- size->height = _settings_game.economy.infrastructure_maintenance ? std::max(size->height, EXP_LINESPACE + FONT_HEIGHT_NORMAL) : 0;
+ size->height = _settings_game.economy.infrastructure_maintenance ? std::max(size->height, WidgetDimensions::scaled.vsep_normal + FONT_HEIGHT_NORMAL) : 0;
}
break;
}
@@ -1960,19 +2031,18 @@ struct CompanyInfrastructureWindow : Window
if (_settings_game.economy.infrastructure_maintenance) {
SetDParam(0, monthly_cost * 12); // Convert to per year
- int left = _current_text_dir == TD_RTL ? r.right - this->total_width : r.left;
- DrawString(left, left + this->total_width, y, STR_COMPANY_INFRASTRUCTURE_VIEW_TOTAL, TC_FROMSTRING, SA_RIGHT);
+ Rect tr = r.WithWidth(this->total_width, _current_text_dir == TD_RTL);
+ DrawString(tr.left, tr.right, y, STR_COMPANY_INFRASTRUCTURE_VIEW_TOTAL, TC_FROMSTRING, SA_RIGHT);
}
}
void DrawWidget(const Rect &r, int widget) const override
{
const Company *c = Company::Get((CompanyID)this->window_number);
+
int y = r.top;
- int offs_left = _current_text_dir == TD_LTR ? WD_FRAMERECT_LEFT : 0;
- int offs_right = _current_text_dir == TD_LTR ? 0 : WD_FRAMERECT_LEFT;
-
+ Rect ir = r.Indent(WidgetDimensions::scaled.hsep_indent, _current_text_dir == TD_RTL);
switch (widget) {
case WID_CI_RAIL_DESC:
DrawString(r.left, r.right, y, STR_COMPANY_INFRASTRUCTURE_VIEW_RAIL_SECT);
@@ -1982,13 +2052,13 @@ struct CompanyInfrastructureWindow : Window
for (const auto &rt : _sorted_railtypes) {
if (HasBit(this->railtypes, rt)) {
SetDParam(0, GetRailTypeInfo(rt)->strings.name);
- DrawString(r.left + offs_left, r.right - offs_right, y += FONT_HEIGHT_NORMAL, STR_WHITE_STRING);
+ DrawString(ir.left, ir.right, y += FONT_HEIGHT_NORMAL, STR_WHITE_STRING);
}
}
- DrawString(r.left + offs_left, r.right - offs_right, y += FONT_HEIGHT_NORMAL, STR_COMPANY_INFRASTRUCTURE_VIEW_SIGNALS);
+ DrawString(ir.left, ir.right, y += FONT_HEIGHT_NORMAL, STR_COMPANY_INFRASTRUCTURE_VIEW_SIGNALS);
} else {
/* No valid railtype. */
- DrawString(r.left + offs_left, r.right - offs_right, y += FONT_HEIGHT_NORMAL, STR_COMPANY_VIEW_INFRASTRUCTURE_NONE);
+ DrawString(ir.left, ir.right, y += FONT_HEIGHT_NORMAL, STR_COMPANY_VIEW_INFRASTRUCTURE_NONE);
}
break;
@@ -2015,7 +2085,7 @@ struct CompanyInfrastructureWindow : Window
for (const auto &rt : _sorted_roadtypes) {
if (HasBit(this->roadtypes, rt) && RoadTypeIsRoad(rt) == (widget == WID_CI_ROAD_DESC)) {
SetDParam(0, GetRoadTypeInfo(rt)->strings.name);
- DrawString(r.left + offs_left, r.right - offs_right, y += FONT_HEIGHT_NORMAL, STR_WHITE_STRING);
+ DrawString(ir.left, ir.right, y += FONT_HEIGHT_NORMAL, STR_WHITE_STRING);
}
}
@@ -2035,7 +2105,7 @@ struct CompanyInfrastructureWindow : Window
case WID_CI_WATER_DESC:
DrawString(r.left, r.right, y, STR_COMPANY_INFRASTRUCTURE_VIEW_WATER_SECT);
- DrawString(r.left + offs_left, r.right - offs_right, y += FONT_HEIGHT_NORMAL, STR_COMPANY_INFRASTRUCTURE_VIEW_CANALS);
+ DrawString(ir.left, ir.right, y += FONT_HEIGHT_NORMAL, STR_COMPANY_INFRASTRUCTURE_VIEW_CANALS);
break;
case WID_CI_WATER_COUNT:
@@ -2044,18 +2114,18 @@ struct CompanyInfrastructureWindow : Window
case WID_CI_TOTAL:
if (_settings_game.economy.infrastructure_maintenance) {
- int left = _current_text_dir == TD_RTL ? r.right - this->total_width : r.left;
- GfxFillRect(left, y, left + this->total_width, y, PC_WHITE);
- y += EXP_LINESPACE;
+ Rect tr = r.WithWidth(this->total_width, _current_text_dir == TD_RTL);
+ GfxFillRect(tr.left, y, tr.right, y, PC_WHITE);
+ y += WidgetDimensions::scaled.vsep_normal;
SetDParam(0, this->GetTotalMaintenanceCost() * 12); // Convert to per year
- DrawString(left, left + this->total_width, y, STR_COMPANY_INFRASTRUCTURE_VIEW_TOTAL, TC_FROMSTRING, SA_RIGHT);
+ DrawString(tr.left, tr.right, y, STR_COMPANY_INFRASTRUCTURE_VIEW_TOTAL, TC_FROMSTRING, SA_RIGHT);
}
break;
case WID_CI_STATION_DESC:
DrawString(r.left, r.right, y, STR_COMPANY_INFRASTRUCTURE_VIEW_STATION_SECT);
- DrawString(r.left + offs_left, r.right - offs_right, y += FONT_HEIGHT_NORMAL, STR_COMPANY_INFRASTRUCTURE_VIEW_STATIONS);
- DrawString(r.left + offs_left, r.right - offs_right, y += FONT_HEIGHT_NORMAL, STR_COMPANY_INFRASTRUCTURE_VIEW_AIRPORTS);
+ DrawString(ir.left, ir.right, y += FONT_HEIGHT_NORMAL, STR_COMPANY_INFRASTRUCTURE_VIEW_STATIONS);
+ DrawString(ir.left, ir.right, y += FONT_HEIGHT_NORMAL, STR_COMPANY_INFRASTRUCTURE_VIEW_AIRPORTS);
break;
case WID_CI_STATION_COUNT:
@@ -2156,7 +2226,7 @@ static const NWidgetPart _nested_company_widgets[] = {
NWidget(NWID_HORIZONTAL),
NWidget(NWID_SELECTION, INVALID_COLOUR, WID_C_SELECT_DESC_OWNERS),
NWidget(NWID_VERTICAL), SetPIP(5, 5, 4),
- NWidget(WWT_EMPTY, INVALID_COLOUR, WID_C_DESC_OWNERS), SetMinimalTextLines(3, 0),
+ NWidget(WWT_EMPTY, INVALID_COLOUR, WID_C_DESC_OWNERS), SetMinimalTextLines(MAX_COMPANY_SHARE_OWNERS, 0),
NWidget(NWID_SPACER), SetFill(0, 1),
EndContainer(),
EndContainer(),
@@ -2198,10 +2268,8 @@ static const NWidgetPart _nested_company_widgets[] = {
int GetAmountOwnedBy(const Company *c, Owner owner)
{
- return (c->share_owners[0] == owner) +
- (c->share_owners[1] == owner) +
- (c->share_owners[2] == owner) +
- (c->share_owners[3] == owner);
+ auto share_owned_by = [owner](auto share_owner) { return share_owner == owner; };
+ return std::count_if(c->share_owners.begin(), c->share_owners.end(), share_owned_by);
}
/** Strings for the company vehicle counts */
@@ -2278,13 +2346,8 @@ struct CompanyWindow : Window
}
/* Owners of company */
- plane = SZSP_HORIZONTAL;
- for (uint i = 0; i < lengthof(c->share_owners); i++) {
- if (c->share_owners[i] != INVALID_COMPANY) {
- plane = 0;
- break;
- }
- }
+ auto invalid_owner = [](auto owner) { return owner == INVALID_COMPANY; };
+ plane = std::all_of(c->share_owners.begin(), c->share_owners.end(), invalid_owner) ? SZSP_HORIZONTAL : 0;
wi = this->GetWidget(WID_C_SELECT_DESC_OWNERS);
if (plane != wi->shown_plane) {
wi->SetDisplayedPlane(plane);
@@ -2407,7 +2470,7 @@ struct CompanyWindow : Window
Point offset;
Dimension d = GetSpriteSize(SPR_VEH_BUS_SW_VIEW, &offset);
d.height -= offset.y;
- DrawSprite(SPR_VEH_BUS_SW_VIEW, COMPANY_SPRITE_COLOUR(c->index), r.left - offset.x, (r.top + r.bottom - d.height) / 2 - offset.y);
+ DrawSprite(SPR_VEH_BUS_SW_VIEW, COMPANY_SPRITE_COLOUR(c->index), r.left - offset.x, CenterBounds(r.top, r.bottom, d.height) - offset.y);
break;
}
@@ -2584,11 +2647,11 @@ struct CompanyWindow : Window
break;
case WID_C_BUY_SHARE:
- DoCommandP(0, this->window_number, 0, CMD_BUY_SHARE_IN_COMPANY | CMD_MSG(STR_ERROR_CAN_T_BUY_25_SHARE_IN_THIS));
+ Command::Post(STR_ERROR_CAN_T_BUY_25_SHARE_IN_THIS, (CompanyID)this->window_number);
break;
case WID_C_SELL_SHARE:
- DoCommandP(0, this->window_number, 0, CMD_SELL_SHARE_IN_COMPANY | CMD_MSG(STR_ERROR_CAN_T_SELL_25_SHARE_IN));
+ Command::Post(STR_ERROR_CAN_T_SELL_25_SHARE_IN, (CompanyID)this->window_number);
break;
case WID_C_COMPANY_PASSWORD:
@@ -2621,7 +2684,7 @@ struct CompanyWindow : Window
void OnPlaceObject(Point pt, TileIndex tile) override
{
- if (DoCommandP(tile, OBJECT_HQ, 0, CMD_BUILD_OBJECT | CMD_MSG(STR_ERROR_CAN_T_BUILD_COMPANY_HEADQUARTERS)) && !_shift_pressed) {
+ if (Command