diff --git a/.changelog b/.changelog
index 28e9b90a82..2e6467bc61 100644
--- a/.changelog
+++ b/.changelog
@@ -1,26 +1,85 @@
-12.2 (2022-04-02)
+13.0-beta1 (2022-10-31)
------------------------------------------------------------------------
-Feature: Remember the last-used signal between games (#9792)
-Change: [MacOS] Allow touchbar usage on all supported OS versions (#9776)
-Change: Add a timestamp in name of crash files (#9761)
-Fix #9736: Duplicate multiplayer window opens upon canceling password entry (#9842)
-Fix: Removing long roads doesn't prioritise refusal of local authority over other errors (#9831)
-Fix #9020: Glitchy station coverage highlight when changing selection (#9825)
-Fix: Correct some Romanian town names (#9819)
-Fix: Original music playback rate was slightly too fast (#9814)
-Fix #9811: Use the NewGRF-defined vehicle center when dragging ships and aircraft (#9812)
-Fix: Do not let shares in the company taking over another company disappear (#9808)
-Fix #9802: Crash when using lots of NewGRF waypoint types (#9803)
-Fix #9766: Don't write uninitialised data in config file (#9767)
-Fix #9743: [MacOS] Don't try to render touchbar sprites with invalid zoom level (#9776)
-Fix #9774: Building roadstop in estimation mode updates station acceptance (#9775)
-Fix: If vehicles only refit to cargo-slots >= 32, the default cargo was wrong (#9744)
-Fix #9735: Possible desync when replacing a depot on same tile (#9738)
-Fix #9730: [Network] Connections can use an invalid socket due to a race condition (#9731)
-Fix: Don't show sign edit window for GS-owned signs (#9716)
-Fix #9702: Display order window for vehicle group on ctrl-click only when using shared orders (#9704)
-Fix #9680: Crash when loading really old savegames with aircraft in certain places (#9699)
-Fix: Update last servicing dates when using the date cheat (#9694)
-Fix: Error message shows about missing glyphs while suitable fallback font is found (#9692)
+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)
diff --git a/.github/workflows/ci-build.yml b/.github/workflows/ci-build.yml
index b7016b1650..b95f8baaf0 100644
--- a/.github/workflows/ci-build.yml
+++ b/.github/workflows/ci-build.yml
@@ -20,10 +20,10 @@ jobs:
steps:
- name: Checkout
- uses: actions/checkout@v2
+ uses: actions/checkout@v3
- name: Setup cache
- uses: actions/cache@v2
+ uses: actions/cache@v3
with:
path: /emsdk/upstream/emscripten/cache
key: 2.0.31-${{ runner.os }}
@@ -91,7 +91,7 @@ jobs:
steps:
- name: Checkout
- uses: actions/checkout@v2
+ uses: actions/checkout@v3
- name: Install dependencies
run: |
@@ -166,15 +166,15 @@ jobs:
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
@@ -244,7 +244,7 @@ jobs:
steps:
- name: Checkout
- uses: actions/checkout@v2
+ uses: actions/checkout@v3
- name: Prepare cache key
id: key
@@ -253,10 +253,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
@@ -338,7 +338,7 @@ jobs:
steps:
- name: Checkout
- uses: actions/checkout@v2
+ uses: actions/checkout@v3
- name: Setup MSYS2
uses: msys2/setup-msys2@v2
@@ -408,4 +408,4 @@ jobs:
steps:
- name: Check annotations
- uses: OpenTTD/actions/annotation-check@v2
+ 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..d2935cefbc 100644
--- a/.github/workflows/preview_build.yml
+++ b/.github/workflows/preview_build.yml
@@ -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,7 +41,7 @@ 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 }}
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..7a06e23baf 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
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/.ottdrev b/.ottdrev
index a4e83a48cf..203d48e351 100644
--- a/.ottdrev
+++ b/.ottdrev
@@ -1 +1 @@
-12.2 20220402 0 1893dba8a172636791dc79137a3331bd41cc50bc 1 1 2022
+13.0-beta1 20221031 0 c4e655b1d4fa83b8f192d2df0062f6dd7f346aad 1 0 2022
diff --git a/.release_date b/.release_date
index 3f8c9f2c93..cf53716dd2 100644
--- a/.release_date
+++ b/.release_date
@@ -1 +1 @@
-2022-04-02 10:38 UTC
+2022-11-01 19:19 UTC
diff --git a/.version b/.version
index 5807e59269..61c359a5f0 100644
--- a/.version
+++ b/.version
@@ -1 +1 @@
-12.2
+13.0-beta1
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 5fce7343e3..f4e08daf4a 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)
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..698ea54ebb 100644
--- a/CREDITS.md
+++ b/CREDITS.md
@@ -1,31 +1,31 @@
### 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
### 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 +33,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 +52,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..12d3ffe756 100644
--- a/changelog.txt
+++ b/changelog.txt
@@ -1,3 +1,88 @@
+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..b1cec89daf 100644
--- a/cmake/CompileFlags.cmake
+++ b/cmake/CompileFlags.cmake
@@ -56,6 +56,11 @@ macro(compile_flags)
if(MSVC)
add_compile_options(/W3)
+ if(MSVC_VERSION GREATER 1929)
+ # 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 057f0934f8..be37b9b745 100644
--- a/cmake/InstallAndPackage.cmake
+++ b/cmake/InstallAndPackage.cmake
@@ -122,6 +122,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)
@@ -129,7 +131,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)
@@ -181,6 +183,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/Options.cmake b/cmake/Options.cmake
index bfa14b6c3c..42d1127906 100644
--- a/cmake/Options.cmake
+++ b/cmake/Options.cmake
@@ -56,7 +56,7 @@ function(set_options)
option(OPTION_DEDICATED "Build dedicated server only (no GUI)" OFF)
option(OPTION_INSTALL_FHS "Install with Filesystem Hierarchy Standard folders" ${DEFAULT_OPTION_INSTALL_FHS})
- option(OPTION_USE_ASSERTS "Use assertions; leave enabled for nightlies, betas, and RCs" OFF)
+ option(OPTION_USE_ASSERTS "Use assertions; leave enabled for nightlies, betas, and RCs" ON)
if(EMSCRIPTEN)
# Although pthreads is supported, it is not in a way yet that is
# useful for us.
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..e3ebf2dd67 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/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/opntitle.dat b/media/baseset/opntitle.dat
index da09c5e3a5..264aaff60b 100644
Binary files a/media/baseset/opntitle.dat and b/media/baseset/opntitle.dat differ
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/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..05d6f275bd 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));
}
}
@@ -1023,6 +1024,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));
diff --git a/regression/regression/result.txt b/regression/regression/result.txt
index 984c1de82b..27f90220dc 100644
--- a/regression/regression/result.txt
+++ b/regression/regression/result.txt
@@ -1118,6 +1118,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 +1131,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 +1144,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 +1157,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 +1170,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 +1183,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 +1196,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 +1209,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 +1222,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 +1235,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 +1248,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 +1261,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 +1274,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 +1287,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 +1300,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 +1313,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 +7427,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
@@ -9272,12 +9312,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 +9327,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 +9400,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 +9412,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/CMakeLists.txt b/src/CMakeLists.txt
index 631eaf6dea..e526c5d595 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
@@ -84,12 +83,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
@@ -119,6 +118,7 @@ add_files(
depot.cpp
depot_base.h
depot_cmd.cpp
+ depot_cmd.h
depot_func.h
depot_gui.cpp
depot_map.h
@@ -127,11 +127,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
@@ -141,6 +143,7 @@ add_files(
elrail_func.h
engine.cpp
engine_base.h
+ engine_cmd.h
engine_func.h
engine_gui.cpp
engine_gui.h
@@ -155,7 +158,6 @@ add_files(
fios_gui.cpp
fontcache.cpp
fontcache.h
- fontcache_internal.h
fontdetection.h
framerate_gui.cpp
framerate_type.h
@@ -174,6 +176,7 @@ add_files(
gfxinit.h
goal.cpp
goal_base.h
+ goal_cmd.h
goal_gui.cpp
goal_type.h
graph_gui.cpp
@@ -182,6 +185,7 @@ add_files(
ground_vehicle.hpp
group.h
group_cmd.cpp
+ group_cmd.h
group_gui.cpp
group_gui.h
group_type.h
@@ -198,6 +202,7 @@ add_files(
house_type.h
industry.h
industry_cmd.cpp
+ industry_cmd.h
industry_gui.cpp
industry_map.h
industry_type.h
@@ -208,6 +213,7 @@ add_files(
intro_gui.cpp
landscape.cpp
landscape.h
+ landscape_cmd.h
landscape_type.h
language.h
livery.h
@@ -217,6 +223,7 @@ add_files(
map_type.h
misc.cpp
misc_cmd.cpp
+ misc_cmd.h
misc_gui.cpp
mixer.cpp
mixer.h
@@ -277,6 +284,7 @@ add_files(
newgrf_town.h
newgrf_townname.cpp
newgrf_townname.h
+ news_cmd.h
news_func.h
news_gui.cpp
news_gui.h
@@ -284,6 +292,7 @@ add_files(
object.h
object_base.h
object_cmd.cpp
+ object_cmd.h
object_gui.cpp
object_map.h
object_type.h
@@ -293,6 +302,7 @@ add_files(
order_backup.h
order_base.h
order_cmd.cpp
+ order_cmd.h
order_func.h
order_gui.cpp
order_type.h
@@ -305,6 +315,7 @@ add_files(
rail.cpp
rail.h
rail_cmd.cpp
+ rail_cmd.h
rail_gui.cpp
rail_gui.h
rail_map.h
@@ -327,6 +338,7 @@ add_files(
roadstop_base.h
roadveh.h
roadveh_cmd.cpp
+ roadveh_cmd.h
roadveh_gui.cpp
safeguards.h
screenshot_gui.cpp
@@ -334,6 +346,7 @@ add_files(
screenshot.cpp
screenshot.h
settings.cpp
+ settings_cmd.h
settings_func.h
settings_gui.cpp
settings_gui.h
@@ -343,6 +356,7 @@ add_files(
settings_type.h
ship.h
ship_cmd.cpp
+ ship_cmd.h
ship_gui.cpp
signal.cpp
signal_func.h
@@ -350,6 +364,7 @@ add_files(
signs.cpp
signs_base.h
signs_cmd.cpp
+ signs_cmd.h
signs_func.h
signs_gui.cpp
signs_type.h
@@ -368,6 +383,7 @@ add_files(
station.cpp
station_base.h
station_cmd.cpp
+ station_cmd.h
station_func.h
station_gui.cpp
station_gui.h
@@ -379,6 +395,7 @@ add_files(
stdafx.h
story.cpp
story_base.h
+ story_cmd.h
story_gui.cpp
story_type.h
strgen/strgen.h
@@ -393,11 +410,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
@@ -422,11 +441,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
@@ -438,22 +459,26 @@ 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
vehicle.cpp
vehicle_base.h
vehicle_cmd.cpp
+ vehicle_cmd.h
vehicle_func.h
vehicle_gui.cpp
vehicle_gui.h
@@ -462,6 +487,7 @@ add_files(
vehiclelist.cpp
vehiclelist.h
viewport.cpp
+ viewport_cmd.h
viewport_func.h
viewport_gui.cpp
viewport_kdtree.h
@@ -472,10 +498,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..f9112fc177 100644
--- a/src/ai/ai.hpp
+++ b/src/ai/ai.hpp
@@ -139,9 +139,9 @@ public:
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..add76e0685 100644
--- a/src/ai/ai_core.cpp
+++ b/src/ai/ai_core.cpp
@@ -315,14 +315,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 c7ae4740e2..b97e9f72ea 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"
@@ -683,10 +685,10 @@ static const NWidgetPart _nested_ai_config_widgets[] = {
NWidget(WWT_PANEL, COLOUR_MAUVE, WID_AIC_BACKGROUND),
NWidget(NWID_VERTICAL), SetPIP(4, 4, 4),
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),
+ NWidget(WWT_TEXT, COLOUR_MAUVE, WID_AIC_NUMBER), SetDataTip(STR_DIFFICULTY_LEVEL_SETTING_MAXIMUM_NO_COMPETITORS, STR_NULL), SetFill(1, 0),
EndContainer(),
NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), SetPIP(7, 0, 7),
NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_AIC_MOVE_UP), SetResize(1, 0), SetFill(1, 0), SetDataTip(STR_AI_CONFIG_MOVE_UP, STR_AI_CONFIG_MOVE_UP_TOOLTIP),
@@ -778,6 +780,11 @@ struct AIConfigWindow : public Window {
void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) override
{
switch (widget) {
+ case WID_AIC_DECREASE:
+ case WID_AIC_INCREASE:
+ *size = maxdim(*size, NWidgetScrollbar::GetHorizontalDimension());
+ break;
+
case WID_AIC_GAMELIST:
this->line_height = FONT_HEIGHT_NORMAL + WD_MATRIX_TOP + WD_MATRIX_BOTTOM;
size->height = 1 * this->line_height;
@@ -1290,8 +1297,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:
@@ -1330,7 +1337,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);
}
}
}
@@ -1379,7 +1386,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 */
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..0e8355d733 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"
@@ -92,13 +93,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 +110,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/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..d67969c006 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;
}
@@ -258,14 +260,13 @@ void GetAircraftSpriteSize(EngineID engine, uint &width, uint &height, int &xoff
/**
* 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..422f422002 100644
--- a/src/aircraft_gui.cpp
+++ b/src/aircraft_gui.cpp
@@ -100,7 +100,7 @@ void DrawAircraftImage(const Vehicle *v, int left, int right, int y, VehicleID s
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);
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 4d09fbdfdf..f8ef410d85 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"
@@ -26,6 +27,10 @@
#include "hotkeys.h"
#include "vehicle_func.h"
#include "gui.h"
+#include "command_func.h"
+#include "airport_cmd.h"
+#include "station_cmd.h"
+#include "zoom_func.h"
#include "widgets/airport_widget.h"
@@ -40,7 +45,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;
@@ -55,13 +60,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. */
@@ -413,11 +425,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 + ScaleGUITrad(WD_PAR_VSEP_NORMAL);
if (_selected_airport_index != -1) {
const AirportSpec *as = AirportClass::Get(_selected_airport_class)->GetSpec(_selected_airport_index);
@@ -427,20 +436,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 + ScaleGUITrad(WD_PAR_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 + ScaleGUITrad(WD_PAR_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) + ScaleGUITrad(WD_PAR_VSEP_NORMAL);
+ top = DrawStationCoverageAreaText(r.left, r.right, top, SCT_ALL, rad, true) + ScaleGUITrad(WD_PAR_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);
}
}
@@ -586,8 +602,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(WD_FRAMERECT_TOP, WD_FRAMERECT_RIGHT, WD_FRAMERECT_BOTTOM, WD_FRAMERECT_LEFT), 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),
@@ -598,7 +614,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(0, WD_FRAMERECT_RIGHT, 0, WD_FRAMERECT_LEFT), SetResize(0, 1), SetFill(1, 0),
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 9f2deaf10a..4dae0eedc7 100644
--- a/src/autoreplace_gui.cpp
+++ b/src/autoreplace_gui.cpp
@@ -25,6 +25,9 @@
#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"
@@ -217,7 +220,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:
@@ -541,10 +544,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;
}
@@ -562,7 +565,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;
}
@@ -578,6 +581,16 @@ public:
size_t engine_count = this->engines[click_side].size();
EngineID e = engine_count > i ? this->engines[click_side][i] : INVALID_ENGINE;
+
+ /* 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/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/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..ddfda8f50b 100644
--- a/src/bootstrap_gui.cpp
+++ b/src/bootstrap_gui.cpp
@@ -286,16 +286,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 74c7234d57..515a9318c3 100644
--- a/src/bridge_gui.cpp
+++ b/src/bridge_gui.cpp
@@ -20,9 +20,9 @@
#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 "tunnelbridge_cmd.h"
#include "widgets/bridge_widget.h"
@@ -50,27 +50,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);
}
}
@@ -87,7 +82,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;
@@ -112,13 +108,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 */
@@ -135,19 +131,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();
@@ -362,12 +359,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);
@@ -383,14 +374,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()) {
@@ -450,7 +441,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;
ShowErrorMessage(STR_ERROR_CAN_T_BUILD_BRIDGE_HERE, errmsg, WL_INFO, TileX(end) * TILE_SIZE, TileY(end) * TILE_SIZE);
diff --git a/src/build_vehicle_gui.cpp b/src/build_vehicle_gui.cpp
index 547ac3e003..04392c9b03 100644
--- a/src/build_vehicle_gui.cpp
+++ b/src/build_vehicle_gui.cpp
@@ -30,6 +30,9 @@
#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 "widgets/build_vehicle_widget.h"
@@ -1079,7 +1082,7 @@ struct BuildVehicleWindow : Window {
{
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;
@@ -1114,7 +1117,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;
@@ -1226,11 +1229,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;
}
@@ -1458,7 +1461,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;
}
@@ -1466,10 +1469,13 @@ 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);
+ }
}
break;
}
@@ -1634,7 +1640,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
@@ -1681,7 +1687,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.h b/src/cargotype.h
index 5ed9ac90e2..54d24f1d7a 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.
diff --git a/src/cheat_gui.cpp b/src/cheat_gui.cpp
index a0ac5fab8f..eb62dc0317 100644
--- a/src/cheat_gui.cpp
+++ b/src/cheat_gui.cpp
@@ -28,6 +28,7 @@
#include "tile_map.h"
#include "newgrf.h"
#include "error.h"
+#include "misc_cmd.h"
#include "widgets/cheat_widget.h"
@@ -54,7 +55,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;
}
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 7d1a72dec9..65e5acf10c 100644
--- a/src/command.cpp
+++ b/src/command.cpp
@@ -24,187 +24,69 @@
#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 "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
#include "table/strings.h"
#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
@@ -213,174 +95,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();
}
/*!
@@ -390,11 +116,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;
}
/*!
@@ -404,11 +130,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;
}
/**
@@ -416,7 +142,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[] = {
@@ -433,89 +159,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;
}
/*!
@@ -532,70 +176,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 && IsLocalCompany() &&
- !_generating_world &&
- !(cmd & CMD_NETWORK_COMMAND) &&
- !(GetCommandFlags(cmd) & CMD_NO_EST);
+ bool estimate_only = _shift_pressed && 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
@@ -603,63 +270,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;
@@ -667,65 +294,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
@@ -733,7 +367,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();
}
@@ -741,20 +375,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. */
@@ -763,14 +398,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..85f5680aea 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
@@ -360,28 +362,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 +405,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 +449,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..681912f17c 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);
}
@@ -219,17 +220,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 +268,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 +559,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, but we choose it here for the first (host) company. */
+ if (_company_manager_face != 0 && !is_ai) {
+ c->face = _company_manager_face;
+ } else {
+ RandomCompanyManagerFaceBits(c->face, (GenderEthnicity)Random(), false, false);
+ }
SetDefaultCompanySettings(c->index);
ClearEnginesHiddenFlagOfCompany(c->index);
@@ -603,7 +612,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 +803,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 +821,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 +847,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 +879,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. */
@@ -921,17 +927,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 +944,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 +960,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 +1050,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 +1093,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 +1115,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 +1175,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 +1211,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 fe8c2326c2..fdcc7309df 100644
--- a/src/company_gui.cpp
+++ b/src/company_gui.cpp
@@ -37,6 +37,11 @@
#include "station_func.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"
@@ -46,88 +51,127 @@
/** 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 const int EXP_INDENT = 10; ///< Amount of horizontal space for an indented line.
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 + EXP_BLOCKSPACE;
+
+ 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() + EXP_LINESPACE + FONT_HEIGHT_NORMAL + EXP_BLOCKSPACE;
+ }
+
+ /* Total income */
+ total_height += EXP_LINESPACE + FONT_HEIGHT_NORMAL + EXP_BLOCKSPACE;
+
+ 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());
+ }
+
+ return max_width;
+}
+
+/**
+ * Draw a category of expenses (revenue, operating expenses, capital expenses).
+ */
+static void DrawCategory(const Rect &r, int start_y, ExpensesList list)
+{
+ int offs_left = _current_text_dir == TD_LTR ? EXP_INDENT : 0;
+ int offs_right = _current_text_dir == TD_LTR ? 0 : EXP_INDENT;
+
+ int y = start_y;
+ ExpensesType et;
+
+ for (uint i = 0; i < list.length; i++) {
+ et = list.et[i];
+ DrawString(r.left + offs_left, r.right - offs_right, y, STR_FINANCES_SECTION_CONSTRUCTION + et);
+ y += FONT_HEIGHT_NORMAL;
+ }
+}
+
/**
* Draw the expenses categories.
* @param r Available space for drawing.
@@ -135,25 +179,28 @@ 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 + EXP_BLOCKSPACE;
- 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 total and associated total line */
+ y += EXP_LINESPACE + FONT_HEIGHT_NORMAL;
+
+ /* Advance y by a blockspace after this category block */
+ y += EXP_BLOCKSPACE;
}
- DrawString(r.left, r.right, y + EXP_LINESPACE, STR_FINANCES_TOTAL_CAPTION, TC_FROMSTRING, SA_RIGHT);
+ /* Draw total profit/loss */
+ y += EXP_LINESPACE;
+ DrawString(r.left, r.right, y, STR_FINANCES_NET_PROFIT, TC_FROMSTRING, SA_LEFT);
}
/**
@@ -162,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 += EXP_LINESPACE;
+ 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.
@@ -184,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 + EXP_BLOCKSPACE;
- 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() + EXP_LINESPACE + FONT_HEIGHT_NORMAL + EXP_BLOCKSPACE;
}
+ /* Total income. */
GfxFillRect(r.left, y, r.right, y, PC_BLACK);
y += EXP_LINESPACE;
- DrawPrice(sum, r.left, r.right, y);
+ DrawPrice(sum, r.left, r.right, y, TC_WHITE);
}
static const NWidgetPart _nested_company_finances_widgets[] = {
@@ -236,22 +304,24 @@ static const NWidgetPart _nested_company_finances_widgets[] = {
NWidget(WWT_PANEL, COLOUR_GREY),
NWidget(NWID_HORIZONTAL), SetPadding(WD_FRAMERECT_TOP, WD_FRAMERECT_RIGHT, WD_FRAMERECT_BOTTOM, WD_FRAMERECT_LEFT),
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_TOTAL_CURRENCY, 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(),
@@ -304,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;
@@ -323,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;
}
@@ -368,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;
}
@@ -394,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;
@@ -435,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
@@ -995,12 +1067,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);
}
}
@@ -1199,82 +1271,82 @@ static const NWidgetPart _nested_select_company_manager_face_widgets[] = {
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),
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),
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),
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),
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),
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),
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),
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),
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),
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),
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),
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(),
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),
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),
@@ -1304,29 +1376,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(),
- nwi_widget->pos_y + 1 + nwi_widget->IsLowered(), str, TC_WHITE, SA_HOR_CENTER);
}
}
@@ -1503,65 +1572,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;
@@ -1581,7 +1655,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 */
@@ -2148,7 +2222,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(),
@@ -2190,10 +2264,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 */
@@ -2270,13 +2342,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);
@@ -2576,11 +2643,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:
@@ -2613,7 +2680,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::Post(STR_ERROR_CAN_T_BUILD_COMPANY_HEADQUARTERS, tile, OBJECT_HQ, 0) && !_shift_pressed) {
ResetObjectToPlace();
this->RaiseButtons();
}
@@ -2635,16 +2702,16 @@ struct CompanyWindow : Window
Money money = (Money)(strtoull(str, nullptr, 10) / _currency->rate);
uint32 money_c = Clamp(ClampToI32(money), 0, 20000000); // Clamp between 20 million and 0
- DoCommandP(0, money_c, this->window_number, CMD_GIVE_MONEY | CMD_MSG(STR_ERROR_CAN_T_GIVE_MONEY));
+ Command::Post(STR_ERROR_CAN_T_GIVE_MONEY, money_c, (CompanyID)this->window_number);
break;
}
case WID_C_PRESIDENT_NAME:
- DoCommandP(0, 0, 0, CMD_RENAME_PRESIDENT | CMD_MSG(STR_ERROR_CAN_T_CHANGE_PRESIDENT), nullptr, str);
+ Command::Post(STR_ERROR_CAN_T_CHANGE_PRESIDENT, str);
break;
case WID_C_COMPANY_NAME:
- DoCommandP(0, 0, 0, CMD_RENAME_COMPANY | CMD_MSG(STR_ERROR_CAN_T_CHANGE_COMPANY_NAME), nullptr, str);
+ Command::Post(STR_ERROR_CAN_T_CHANGE_COMPANY_NAME, str);
break;
case WID_C_COMPANY_JOIN:
@@ -2771,7 +2838,7 @@ struct BuyCompanyWindow : Window {
break;
case WID_BC_YES:
- DoCommandP(0, this->window_number, 0, CMD_BUY_COMPANY | CMD_MSG(STR_ERROR_CAN_T_BUY_COMPANY));
+ Command::Post(STR_ERROR_CAN_T_BUY_COMPANY, (CompanyID)this->window_number);
break;
}
}
diff --git a/src/company_type.h b/src/company_type.h
index de2556b914..3bbcf731e6 100644
--- a/src/company_type.h
+++ b/src/company_type.h
@@ -40,6 +40,7 @@ static const uint MAX_LENGTH_PRESIDENT_NAME_CHARS = 32; ///< The maximum length
static const uint MAX_LENGTH_COMPANY_NAME_CHARS = 32; ///< The maximum length of a company name in characters including '\0'
static const uint MAX_HISTORY_QUARTERS = 24; ///< The maximum number of quarters kept as performance's history
+static const uint MAX_COMPANY_SHARE_OWNERS = 4; ///< The maximum number of shares of a company that can be owned by another company.
/** Define basic enum properties */
template <> struct EnumPropsT : MakeEnumPropsT {};
@@ -52,16 +53,18 @@ struct Company;
typedef uint32 CompanyManagerFace; ///< Company manager face bits, info see in company_manager_face.h
/** The reason why the company was removed. */
-enum CompanyRemoveReason {
+enum CompanyRemoveReason : uint8 {
CRR_MANUAL, ///< The company is manually removed.
CRR_AUTOCLEAN, ///< The company is removed due to autoclean.
CRR_BANKRUPT, ///< The company went belly-up.
CRR_END, ///< Sentinel for end.
+
+ CRR_NONE = CRR_MANUAL, ///< Dummy reason for actions that don't need one.
};
/** The action to do with CMD_COMPANY_CTRL. */
-enum CompanyCtrlAction {
+enum CompanyCtrlAction : uint8 {
CCA_NEW, ///< Create a new company.
CCA_NEW_AI, ///< Create a new AI company.
CCA_DELETE, ///< Delete a company.
diff --git a/src/console.cpp b/src/console.cpp
index 10e0bc279a..393dca2754 100644
--- a/src/console.cpp
+++ b/src/console.cpp
@@ -123,6 +123,7 @@ void IConsolePrint(TextColour colour_code, const std::string &string)
IConsoleWriteToLogFile(str);
IConsoleGUIPrint(colour_code, str);
+ free(str);
}
/**
diff --git a/src/console_cmds.cpp b/src/console_cmds.cpp
index 5cdb744abc..2936a8a147 100644
--- a/src/console_cmds.cpp
+++ b/src/console_cmds.cpp
@@ -42,6 +42,10 @@
#include "game/game.hpp"
#include "table/strings.h"
#include "walltime_func.h"
+#include "company_cmd.h"
+#include "misc_cmd.h"
+
+#include
#include "safeguards.h"
@@ -251,6 +255,59 @@ DEF_CONSOLE_CMD(ConResetTile)
}
#endif /* _DEBUG */
+/**
+ * Zoom map to given level.
+ * param level As defined by ZoomLevel and as limited by zoom_min/zoom_max from GUISettings.
+ * @return True when either console help was shown or a proper amount of parameters given.
+ */
+DEF_CONSOLE_CMD(ConZoomToLevel)
+{
+ switch (argc) {
+ case 0:
+ IConsolePrint(CC_HELP, "Set the current zoom level of the main viewport.");
+ IConsolePrint(CC_HELP, "Usage: 'zoomto '.");
+ IConsolePrint(
+ CC_HELP,
+ ZOOM_LVL_MIN < _settings_client.gui.zoom_min ?
+ "The lowest zoom-in level allowed by current client settings is {}." :
+ "The lowest supported zoom-in level is {}.",
+ std::max(ZOOM_LVL_MIN, _settings_client.gui.zoom_min)
+ );
+ IConsolePrint(
+ CC_HELP,
+ _settings_client.gui.zoom_max < ZOOM_LVL_MAX ?
+ "The highest zoom-out level allowed by current client settings is {}." :
+ "The highest supported zoom-out level is {}.",
+ std::min(_settings_client.gui.zoom_max, ZOOM_LVL_MAX)
+ );
+ return true;
+
+ case 2: {
+ uint32 level;
+ if (GetArgumentInteger(&level, argv[1])) {
+ if (level < ZOOM_LVL_MIN) {
+ IConsolePrint(CC_ERROR, "Zoom-in levels below {} are not supported.", ZOOM_LVL_MIN);
+ } else if (level < _settings_client.gui.zoom_min) {
+ IConsolePrint(CC_ERROR, "Current client settings do not allow zooming in below level {}.", _settings_client.gui.zoom_min);
+ } else if (level > ZOOM_LVL_MAX) {
+ IConsolePrint(CC_ERROR, "Zoom-in levels above {} are not supported.", ZOOM_LVL_MAX);
+ } else if (level > _settings_client.gui.zoom_max) {
+ IConsolePrint(CC_ERROR, "Current client settings do not allow zooming out beyond level {}.", _settings_client.gui.zoom_max);
+ } else {
+ Window *w = FindWindowById(WC_MAIN_WINDOW, 0);
+ Viewport *vp = w->viewport;
+ while (vp->zoom > level) DoZoomInOutWindow(ZOOM_IN, w);
+ while (vp->zoom < level) DoZoomInOutWindow(ZOOM_OUT, w);
+ }
+ return true;
+ }
+ break;
+ }
+ }
+
+ return false;
+}
+
/**
* Scroll to a tile on the map.
* param x tile number or tile x coordinate.
@@ -262,34 +319,44 @@ DEF_CONSOLE_CMD(ConResetTile)
*/
DEF_CONSOLE_CMD(ConScrollToTile)
{
- switch (argc) {
- case 0:
- IConsolePrint(CC_HELP, "Center the screen on a given tile.");
- IConsolePrint(CC_HELP, "Usage: 'scrollto ' or 'scrollto '.");
- IConsolePrint(CC_HELP, "Numbers can be either decimal (34161) or hexadecimal (0x4a5B).");
- return true;
+ if (argc == 0) {
+ IConsolePrint(CC_HELP, "Center the screen on a given tile.");
+ IConsolePrint(CC_HELP, "Usage: 'scrollto [instant] ' or 'scrollto [instant] '.");
+ IConsolePrint(CC_HELP, "Numbers can be either decimal (34161) or hexadecimal (0x4a5B).");
+ IConsolePrint(CC_HELP, "'instant' will immediately move and redraw viewport without smooth scrolling.");
+ return true;
+ }
+ if (argc < 2) return false;
- case 2: {
+ uint32 arg_index = 1;
+ bool instant = false;
+ if (strcmp(argv[arg_index], "instant") == 0) {
+ ++arg_index;
+ instant = true;
+ }
+
+ switch (argc - arg_index) {
+ case 1: {
uint32 result;
- if (GetArgumentInteger(&result, argv[1])) {
+ if (GetArgumentInteger(&result, argv[arg_index])) {
if (result >= MapSize()) {
IConsolePrint(CC_ERROR, "Tile does not exist.");
return true;
}
- ScrollMainWindowToTile((TileIndex)result);
+ ScrollMainWindowToTile((TileIndex)result, instant);
return true;
}
break;
}
- case 3: {
+ case 2: {
uint32 x, y;
- if (GetArgumentInteger(&x, argv[1]) && GetArgumentInteger(&y, argv[2])) {
+ if (GetArgumentInteger(&x, argv[arg_index]) && GetArgumentInteger(&y, argv[arg_index + 1])) {
if (x >= MapSizeX() || y >= MapSizeY()) {
IConsolePrint(CC_ERROR, "Tile does not exist.");
return true;
}
- ScrollMainWindowToTile(TileXY(x, y));
+ ScrollMainWindowToTile(TileXY(x, y), instant);
return true;
}
break;
@@ -630,7 +697,7 @@ DEF_CONSOLE_CMD(ConPauseGame)
}
if ((_pause_mode & PM_PAUSED_NORMAL) == PM_UNPAUSED) {
- DoCommandP(0, PM_PAUSED_NORMAL, 1, CMD_PAUSE);
+ Command::Post(PM_PAUSED_NORMAL, true);
if (!_networking) IConsolePrint(CC_DEFAULT, "Game paused.");
} else {
IConsolePrint(CC_DEFAULT, "Game is already paused.");
@@ -652,7 +719,7 @@ DEF_CONSOLE_CMD(ConUnpauseGame)
}
if ((_pause_mode & PM_PAUSED_NORMAL) != PM_UNPAUSED) {
- DoCommandP(0, PM_PAUSED_NORMAL, 0, CMD_PAUSE);
+ Command::Post(PM_PAUSED_NORMAL, false);
if (!_networking) IConsolePrint(CC_DEFAULT, "Game unpaused.");
} else if ((_pause_mode & PM_PAUSED_ERROR) != PM_UNPAUSED) {
IConsolePrint(CC_DEFAULT, "Game is in error state and cannot be unpaused via console.");
@@ -863,7 +930,7 @@ DEF_CONSOLE_CMD(ConResetCompany)
}
/* It is safe to remove this company */
- DoCommandP(0, CCA_DELETE | index << 16 | CRR_MANUAL << 24, 0, CMD_COMPANY_CTRL);
+ Command::Post(CCA_DELETE, index, CRR_MANUAL, INVALID_CLIENT_ID);
IConsolePrint(CC_DEFAULT, "Company deleted.");
return true;
@@ -1095,58 +1162,65 @@ DEF_CONSOLE_CMD(ConReload)
/**
* Print a text buffer line by line to the console. Lines are separated by '\n'.
- * @param buf The buffer to print.
- * @note All newlines are replace by '\0' characters.
+ * @param full_string The multi-line string to print.
*/
-static void PrintLineByLine(char *buf)
+static void PrintLineByLine(const std::string &full_string)
{
- char *p = buf;
- /* Print output line by line */
- for (char *p2 = buf; *p2 != '\0'; p2++) {
- if (*p2 == '\n') {
- *p2 = '\0';
- IConsolePrint(CC_DEFAULT, p);
- p = p2 + 1;
- }
+ std::istringstream in(full_string);
+ std::string line;
+ while (std::getline(in, line)) {
+ IConsolePrint(CC_DEFAULT, line.c_str());
}
}
DEF_CONSOLE_CMD(ConListAILibs)
{
- char buf[4096];
- AI::GetConsoleLibraryList(buf, lastof(buf));
+ if (argc == 0) {
+ IConsolePrint(CC_HELP, "List installed AI libraries. Usage: 'list_ai_libs'.");
+ return true;
+ }
- PrintLineByLine(buf);
+ const std::string output_str = AI::GetConsoleLibraryList();
+ PrintLineByLine(output_str);
return true;
}
DEF_CONSOLE_CMD(ConListAI)
{
- char buf[4096];
- AI::GetConsoleList(buf, lastof(buf));
+ if (argc == 0) {
+ IConsolePrint(CC_HELP, "List installed AIs. Usage: 'list_ai'.");
+ return true;
+ }
- PrintLineByLine(buf);
+ const std::string output_str = AI::GetConsoleList();
+ PrintLineByLine(output_str);
return true;
}
DEF_CONSOLE_CMD(ConListGameLibs)
{
- char buf[4096];
- Game::GetConsoleLibraryList(buf, lastof(buf));
+ if (argc == 0) {
+ IConsolePrint(CC_HELP, "List installed Game Script libraries. Usage: 'list_game_libs'.");
+ return true;
+ }
- PrintLineByLine(buf);
+ const std::string output_str = Game::GetConsoleLibraryList();
+ PrintLineByLine(output_str);
return true;
}
DEF_CONSOLE_CMD(ConListGame)
{
- char buf[4096];
- Game::GetConsoleList(buf, lastof(buf));
+ if (argc == 0) {
+ IConsolePrint(CC_HELP, "List installed Game Scripts. Usage: 'list_game'.");
+ return true;
+ }
- PrintLineByLine(buf);
+ const std::string output_str = Game::GetConsoleList();
+ PrintLineByLine(output_str);
return true;
}
@@ -1220,7 +1294,7 @@ DEF_CONSOLE_CMD(ConStartAI)
}
/* Start a new AI company */
- DoCommandP(0, CCA_NEW_AI | INVALID_COMPANY << 16, 0, CMD_COMPANY_CTRL);
+ Command::Post(CCA_NEW_AI, INVALID_COMPANY, CRR_NONE, INVALID_CLIENT_ID);
return true;
}
@@ -1256,8 +1330,8 @@ DEF_CONSOLE_CMD(ConReloadAI)
}
/* First kill the company of the AI, then start a new one. This should start the current AI again */
- DoCommandP(0, CCA_DELETE | company_id << 16 | CRR_MANUAL << 24, 0, CMD_COMPANY_CTRL);
- DoCommandP(0, CCA_NEW_AI | company_id << 16, 0, CMD_COMPANY_CTRL);
+ Command::Post(CCA_DELETE, company_id, CRR_MANUAL, INVALID_CLIENT_ID);
+ Command::Post(CCA_NEW_AI, company_id, CRR_NONE, INVALID_CLIENT_ID);
IConsolePrint(CC_DEFAULT, "AI reloaded.");
return true;
@@ -1294,7 +1368,7 @@ DEF_CONSOLE_CMD(ConStopAI)
}
/* Now kill the company of the AI. */
- DoCommandP(0, CCA_DELETE | company_id << 16 | CRR_MANUAL << 24, 0, CMD_COMPANY_CTRL);
+ Command::Post(CCA_DELETE, company_id, CRR_MANUAL, INVALID_CLIENT_ID);
IConsolePrint(CC_DEFAULT, "AI stopped, company deleted.");
return true;
@@ -1522,7 +1596,7 @@ DEF_CONSOLE_CMD(ConDebugLevel)
if (argc == 1) {
IConsolePrint(CC_DEFAULT, "Current debug-level: '{}'", GetDebugString());
} else {
- SetDebugString(argv[1]);
+ SetDebugString(argv[1], [](const char *err) { IConsolePrint(CC_ERROR, std::string(err)); });
}
return true;
@@ -2394,6 +2468,7 @@ void IConsoleStdLibRegister()
IConsole::CmdRegister("return", ConReturn);
IConsole::CmdRegister("screenshot", ConScreenShot);
IConsole::CmdRegister("script", ConScript);
+ IConsole::CmdRegister("zoomto", ConZoomToLevel);
IConsole::CmdRegister("scrollto", ConScrollToTile);
IConsole::CmdRegister("alias", ConAlias);
IConsole::CmdRegister("load", ConLoad);
diff --git a/src/console_gui.cpp b/src/console_gui.cpp
index 81978f08a2..eaef1e4efa 100644
--- a/src/console_gui.cpp
+++ b/src/console_gui.cpp
@@ -21,6 +21,8 @@
#include "console_func.h"
#include "rev.h"
#include "video/video_driver.hpp"
+#include
+#include
#include "widgets/console_widget.h"
@@ -37,94 +39,36 @@ static const uint ICON_BOTTOM_BORDERWIDTH = 12;
* Container for a single line of console output
*/
struct IConsoleLine {
- static IConsoleLine *front; ///< The front of the console backlog buffer
- static int size; ///< The amount of items in the backlog
-
- IConsoleLine *previous; ///< The previous console message.
- char *buffer; ///< The data to store.
+ std::string buffer; ///< The data to store.
TextColour colour; ///< The colour of the line.
uint16 time; ///< The amount of time the line is in the backlog.
+ IConsoleLine() : buffer(), colour(TC_BEGIN), time(0)
+ {
+
+ }
+
/**
* Initialize the console line.
* @param buffer the data to print.
* @param colour the colour of the line.
*/
- IConsoleLine(char *buffer, TextColour colour) :
- previous(IConsoleLine::front),
- buffer(buffer),
+ IConsoleLine(std::string buffer, TextColour colour) :
+ buffer(std::move(buffer)),
colour(colour),
time(0)
{
- IConsoleLine::front = this;
- IConsoleLine::size++;
}
- /**
- * Clear this console line and any further ones.
- */
~IConsoleLine()
{
- IConsoleLine::size--;
- free(buffer);
-
- delete previous;
- }
-
- /**
- * Get the index-ed item in the list.
- */
- static const IConsoleLine *Get(uint index)
- {
- const IConsoleLine *item = IConsoleLine::front;
- while (index != 0 && item != nullptr) {
- index--;
- item = item->previous;
- }
-
- return item;
- }
-
- /**
- * Truncate the list removing everything older than/more than the amount
- * as specified in the config file.
- * As a side effect also increase the time the other lines have been in
- * the list.
- * @return true if and only if items got removed.
- */
- static bool Truncate()
- {
- IConsoleLine *cur = IConsoleLine::front;
- if (cur == nullptr) return false;
-
- int count = 1;
- for (IConsoleLine *item = cur->previous; item != nullptr; count++, cur = item, item = item->previous) {
- if (item->time > _settings_client.gui.console_backlog_timeout &&
- count > _settings_client.gui.console_backlog_length) {
- delete item;
- cur->previous = nullptr;
- return true;
- }
-
- if (item->time != MAX_UVALUE(uint16)) item->time++;
- }
-
- return false;
- }
-
- /**
- * Reset the complete console line backlog.
- */
- static void Reset()
- {
- delete IConsoleLine::front;
- IConsoleLine::front = nullptr;
- IConsoleLine::size = 0;
}
};
-/* static */ IConsoleLine *IConsoleLine::front = nullptr;
-/* static */ int IConsoleLine::size = 0;
+/** The console backlog buffer. Item index 0 is the newest line. */
+static std::deque _iconsole_buffer;
+
+static bool TruncateBuffer();
/* ** main console cmd buffer ** */
@@ -169,7 +113,7 @@ static WindowDesc _console_window_desc(
struct IConsoleWindow : Window
{
- static int scroll;
+ static size_t scroll;
int line_height; ///< Height of one line of text in the console.
int line_offset;
GUITimer truncate_timer;
@@ -198,8 +142,15 @@ struct IConsoleWindow : Window
*/
void Scroll(int amount)
{
- int max_scroll = std::max(0, IConsoleLine::size + 1 - this->height / this->line_height);
- IConsoleWindow::scroll = Clamp(IConsoleWindow::scroll + amount, 0, max_scroll);
+ if (amount < 0) {
+ size_t namount = (size_t) -amount;
+ IConsoleWindow::scroll = (namount > IConsoleWindow::scroll) ? 0 : IConsoleWindow::scroll - namount;
+ } else {
+ assert(this->height >= 0 && this->line_height > 0);
+ size_t visible_lines = (size_t)(this->height / this->line_height);
+ size_t max_scroll = (visible_lines > _iconsole_buffer.size()) ? 0 : _iconsole_buffer.size() + 1 - visible_lines;
+ IConsoleWindow::scroll = std::min(IConsoleWindow::scroll + amount, max_scroll);
+ }
this->SetDirty();
}
@@ -209,9 +160,10 @@ struct IConsoleWindow : Window
GfxFillRect(0, 0, this->width - 1, this->height - 1, PC_BLACK);
int ypos = this->height - this->line_height;
- for (const IConsoleLine *print = IConsoleLine::Get(IConsoleWindow::scroll); print != nullptr; print = print->previous) {
- SetDParamStr(0, print->buffer);
- ypos = DrawStringMultiLine(5, right, -this->line_height, ypos, STR_JUST_RAW_STRING, print->colour, SA_LEFT | SA_BOTTOM | SA_FORCE) - ICON_LINE_SPACING;
+ for (size_t line_index = IConsoleWindow::scroll; line_index < _iconsole_buffer.size(); line_index++) {
+ const IConsoleLine &print = _iconsole_buffer[line_index];
+ SetDParamStr(0, print.buffer);
+ ypos = DrawStringMultiLine(5, right, -this->line_height, ypos, STR_JUST_RAW_STRING, print.colour, SA_LEFT | SA_BOTTOM | SA_FORCE) - ICON_LINE_SPACING;
if (ypos < 0) break;
}
/* If the text is longer than the window, don't show the starting ']' */
@@ -235,9 +187,12 @@ struct IConsoleWindow : Window
{
if (this->truncate_timer.CountElapsed(delta_ms) == 0) return;
- if (IConsoleLine::Truncate() &&
- (IConsoleWindow::scroll > IConsoleLine::size)) {
- IConsoleWindow::scroll = std::max(0, IConsoleLine::size - (this->height / this->line_height) + 1);
+ assert(this->height >= 0 && this->line_height > 0);
+ size_t visible_lines = (size_t)(this->height / this->line_height);
+
+ if (TruncateBuffer() && IConsoleWindow::scroll + visible_lines > _iconsole_buffer.size()) {
+ size_t max_scroll = (visible_lines > _iconsole_buffer.size()) ? 0 : _iconsole_buffer.size() + 1 - visible_lines;
+ IConsoleWindow::scroll = std::min(IConsoleWindow::scroll, max_scroll);
this->SetDirty();
}
}
@@ -389,14 +344,14 @@ struct IConsoleWindow : Window
}
};
-int IConsoleWindow::scroll = 0;
+size_t IConsoleWindow::scroll = 0;
void IConsoleGUIInit()
{
IConsoleResetHistoryPos();
_iconsole_mode = ICONSOLE_CLOSED;
- IConsoleLine::Reset();
+ IConsoleClearBuffer();
memset(_iconsole_history, 0, sizeof(_iconsole_history));
IConsolePrint(TC_LIGHT_BLUE, "OpenTTD Game Console Revision 7 - {}", _openttd_revision);
@@ -408,7 +363,7 @@ void IConsoleGUIInit()
void IConsoleClearBuffer()
{
- IConsoleLine::Reset();
+ _iconsole_buffer.clear();
}
void IConsoleGUIFree()
@@ -511,10 +466,38 @@ static void IConsoleHistoryNavigate(int direction)
*/
void IConsoleGUIPrint(TextColour colour_code, char *str)
{
- new IConsoleLine(str, colour_code);
+ _iconsole_buffer.push_front(IConsoleLine(str, colour_code));
SetWindowDirty(WC_CONSOLE, 0);
}
+/**
+ * Remove old lines from the backlog buffer.
+ * The buffer is limited by a maximum size and a minimum age. Every time truncation runs,
+ * all lines in the buffer are aged by one. When a line exceeds both the maximum position
+ * and also the maximum age, it gets removed.
+ * @return true if any lines were removed
+*/
+static bool TruncateBuffer()
+{
+ bool need_truncation = false;
+ size_t count = 0;
+ for (IConsoleLine &line : _iconsole_buffer) {
+ count++;
+ line.time++;
+ if (line.time > _settings_client.gui.console_backlog_timeout && count > _settings_client.gui.console_backlog_length) {
+ /* Any messages after this are older and need to be truncated */
+ need_truncation = true;
+ break;
+ }
+ }
+
+ if (need_truncation) {
+ _iconsole_buffer.resize(count - 1);
+ }
+
+ return need_truncation;
+}
+
/**
* Check whether the given TextColour is valid for console usage.
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 96da089326..1c14067ae4 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -26,5 +26,7 @@ add_files(
smallmatrix_type.hpp
smallstack_type.hpp
smallvec_type.hpp
+ span_type.hpp
string_compare_type.hpp
+ strong_typedef_type.hpp
)
diff --git a/src/core/enum_type.hpp b/src/core/enum_type.hpp
index d4ea82eff1..f094bae914 100644
--- a/src/core/enum_type.hpp
+++ b/src/core/enum_type.hpp
@@ -12,13 +12,13 @@
/** Some enums need to have allowed incrementing (i.e. StationClassID) */
#define DECLARE_POSTFIX_INCREMENT(enum_type) \
- inline enum_type operator ++(enum_type& e, int) \
+ inline constexpr enum_type operator ++(enum_type& e, int) \
{ \
enum_type e_org = e; \
e = (enum_type)((std::underlying_type::type)e + 1); \
return e_org; \
} \
- inline enum_type operator --(enum_type& e, int) \
+ inline constexpr enum_type operator --(enum_type& e, int) \
{ \
enum_type e_org = e; \
e = (enum_type)((std::underlying_type::type)e - 1); \
@@ -29,13 +29,13 @@
/** Operators to allow to work with enum as with type safe bit set in C++ */
# define DECLARE_ENUM_AS_BIT_SET(mask_t) \
- inline mask_t operator | (mask_t m1, mask_t m2) {return (mask_t)((std::underlying_type::type)m1 | m2);} \
- inline mask_t operator & (mask_t m1, mask_t m2) {return (mask_t)((std::underlying_type::type)m1 & m2);} \
- inline mask_t operator ^ (mask_t m1, mask_t m2) {return (mask_t)((std::underlying_type::type)m1 ^ m2);} \
- inline mask_t& operator |= (mask_t& m1, mask_t m2) {m1 = m1 | m2; return m1;} \
- inline mask_t& operator &= (mask_t& m1, mask_t m2) {m1 = m1 & m2; return m1;} \
- inline mask_t& operator ^= (mask_t& m1, mask_t m2) {m1 = m1 ^ m2; return m1;} \
- inline mask_t operator ~(mask_t m) {return (mask_t)(~(std::underlying_type::type)m);}
+ inline constexpr mask_t operator | (mask_t m1, mask_t m2) {return (mask_t)((std::underlying_type::type)m1 | (std::underlying_type::type)m2);} \
+ inline constexpr mask_t operator & (mask_t m1, mask_t m2) {return (mask_t)((std::underlying_type::type)m1 & (std::underlying_type::type)m2);} \
+ inline constexpr mask_t operator ^ (mask_t m1, mask_t m2) {return (mask_t)((std::underlying_type::type)m1 ^ (std::underlying_type::type)m2);} \
+ inline constexpr mask_t& operator |= (mask_t& m1, mask_t m2) {m1 = m1 | m2; return m1;} \
+ inline constexpr mask_t& operator &= (mask_t& m1, mask_t m2) {m1 = m1 & m2; return m1;} \
+ inline constexpr mask_t& operator ^= (mask_t& m1, mask_t m2) {m1 = m1 ^ m2; return m1;} \
+ inline constexpr mask_t operator ~(mask_t m) {return (mask_t)(~(std::underlying_type