Compare commits

..

25 Commits

Author SHA1 Message Date
Tyler Trahan 6338c487b8 Update: Changelog for 15.0-RC3 (#14940) 2025-12-20 14:10:15 -05:00
Peter Nelson 4c0a91aac2 Update: Backport language changes 2025-12-20 19:07:50 +00:00
Peter Nelson d04225a2fb Change: Automatically push/pop colours when formatting a sub-string. (#14006)
Reverts 226a44bf86.

This universally prevents the sub-string from changing colours in the outer string.
2025-12-20 19:07:50 +00:00
Peter Nelson 65902d4abe Fix #14932: Increase internal badge index size to avoid overflowing BadgeIDs. (#14933) 2025-12-20 19:07:50 +00:00
mmtunligit 27fbcb3cc5 Fix a41738e: Picker item recolour depends on gamemode (#14929) 2025-12-20 19:07:50 +00:00
Peter Nelson 8c6531f297 Fix #14921: Crash during station autorefit if station doesn't accept current cargo type. (#14924)
Add convenience helpers to correctly retrieve goods entry cargo available/totals.

Avoids having to check if cargo data is available before accessing it, which was missing for autorefit.
2025-12-20 19:07:50 +00:00
Peter Nelson 88a52b60f8 Fix #14917: Crash when opening house picker with no houses available. (#14920) 2025-12-20 19:07:50 +00:00
Peter Nelson aa4b5ca747 Fix #14916: Duration of error message window could be too short. (#14919)
The timer for automatically closing the error message was started when creating the window, instead of when first displaying the window.
2025-12-20 19:07:50 +00:00
Peter Nelson 5ce9c6cadd Fix #14915: Crash due to divide-by-zero of industry probabilities. (#14918) 2025-12-20 19:07:50 +00:00
Loïc Guilloux 2027912e8f Fix ff08a22: script configs were cleared by AIPL and GSDT chunks from intro game (#14910) 2025-12-20 19:07:50 +00:00
Rubidium dd835262ed Codefix: possible null pointer dereference 2025-12-20 19:07:50 +00:00
Rubidium 24364c3fbc Doc: not much is going to change in the 15.0 script APIs 2025-12-14 17:57:36 +01:00
Peter Nelson ca42564da8 Update: Changelog for 15.0-RC2 2025-12-13 18:13:31 +00:00
Peter Nelson 9062d25456 Update: Backport language changes 2025-12-13 11:54:27 +00:00
Rubidium 4213c62edd Fix #14677: desync due to using newgame time settings to validate savegame time settings 2025-12-13 11:54:27 +00:00
Peter Nelson a97587b3fe Revert: "Change: Support side-by-side fallback FontCaches instead of hierarchical. (#13303)"
This reverts commit 1829f7926d.
2025-12-13 11:54:27 +00:00
Peter Nelson 8ac9762a84 Revert: "Add: Automatically load fonts for missing glyphs. (#14856)"
This reverts commit c1d37d8699.
2025-12-13 11:54:27 +00:00
Peter Nelson 4ad85e0fe6 Fix: Graph label allocated size could be too small. (#14901)
Set initial size based on what could be displayed, instead of what is displayed right now.
2025-12-13 11:54:27 +00:00
Loïc Guilloux ab1ae7ec1a Codechange: [CI] setup-vcpkg action is now in OpenTTD/actions (#14897) 2025-12-13 11:54:27 +00:00
Peter Nelson 84c3ac6852 Fix #14891, a8650c6b06: Minimum sprite zoomlevel could break in some cases. (#14894)
Caused by sprite control flags not being reset when scanning available sprites.
2025-12-13 11:54:27 +00:00
Peter Nelson a6994a9550 Fix #14889: [FluidSynth] Don't try to load a soundfont that doesn't exist. 2025-12-13 11:54:27 +00:00
Peter Nelson f22f9d8b18 Codechange: Auto-reformat fluidsynth.cpp. 2025-12-13 11:54:27 +00:00
Peter Nelson 4b701e053f Update: Backport language changes 2025-12-08 18:06:36 +00:00
Loïc Guilloux b575769a7f Fix: [CI] Install NSIS for windows releases (#14885) 2025-12-08 18:06:36 +00:00
Rubidium 04f1a114dd Change #14155: Erato's the winner of the title game competition 2025-12-07 22:40:14 +01:00
1172 changed files with 47815 additions and 61430 deletions
-85
View File
@@ -1,85 +0,0 @@
"""
Script to scan the OpenTTD source-tree for doxygen @file annotations.
Checks whether they exist, are recognised by doxygen and match coding style.
"""
import os
import sys
END_OF_SENTENCE = [".", "!", "?"]
SOURCE_FILE_EXTENSION = ["cpp", "c", "hpp", "h", "mm", "m", "cc"]
TEMPLATE_FILE_EXTENSION = ["preamble", "in"]
REPO_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
EXCLUDED_FILES = [
os.path.join(REPO_DIR, "src", "script", "api", "squirrel_export.sq.hpp.in"),
os.path.join(REPO_DIR, "src", "script", "api", "script_includes.hpp.in"),
]
def read_files_list_from_file(file_path):
with open(file_path, "r") as f:
while line := f.readline():
yield line[:-1]
def list_files_walk(start_path="."):
for root, dirs, files in os.walk(start_path):
for file in files:
yield os.path.join(root, file)
def check_descriptions(files):
errors = []
for path in files:
if os.path.abspath(path) in EXCLUDED_FILES:
continue
if path.find("3rdparty") != -1 or path.find("lang") != -1:
continue
if not os.path.isfile(path):
continue
name = path[path.rfind("/") + 1 :]
while True:
extension = name[name.rfind('.') + 1 :]
if extension not in TEMPLATE_FILE_EXTENSION:
break
name = name[0 : -len(extension) - 1]
if extension not in SOURCE_FILE_EXTENSION:
continue
with open(path, "r") as f:
content = f.read()
ann = content.find(f"@file {name} ")
reason = f'Should be of the form "/** @file {name} Brief description of the file here. */"'
if ann == -1:
if content.find("@file") == -1:
errors.append(f'File "{path}" does not provide description. {reason}')
continue
else:
end = content.find("\n", ann)
start = content.rfind("\n", 0, ann) + 1
if content[start : ann] == "/** " and content[end - 3 : end] == " */" and content[end - 4] in END_OF_SENTENCE:
continue
elif content[start : ann] == " * ":
if content[start - 4 : start - 1] == "/**" and content[end - 1] in END_OF_SENTENCE and content[end + 1 : end + 4] != " */":
continue
reason = f"Should be of the form:\n/**\n * @file {name} Brief description of the file here.\n * Detailed description of the file here.\n */"
errors.append(f'Description of file "{path}" does not match coding style. {reason}')
return errors
def main():
if len(sys.argv) == 1:
files = list_files_walk(os.path.join(REPO_DIR, "src"))
else:
files = read_files_list_from_file(sys.argv[1])
errors = check_descriptions(files)
if errors:
print("\n".join(errors))
sys.exit(1)
print("OK")
if __name__ == "__main__":
main()
-26
View File
@@ -1,26 +0,0 @@
name: Install Doxygen
runs:
using: composite
steps:
- name: Install Doxygen
run: |
echo "::group::Downloading"
# Download currently (at the time of creation this action) latest version of Doxygen
# because version in ubuntu repository (1.9.8) is outdated and buggy.
wget -O doxygen.tar.gz "https://github.com/doxygen/doxygen/releases/download/Release_1_17_0/doxygen-1.17.0.linux.bin.tar.gz"
echo "::endgroup::"
echo "::group::Unpacking"
tar -xzf doxygen.tar.gz
rm doxygen.tar.gz
echo "::endgroup::"
echo "::group::Installing"
cd doxygen*
sudo make install
echo "::endgroup::"
cd ..
rm -r doxygen*
shell: bash
-28
View File
@@ -1,28 +0,0 @@
""" Script that sorts warnings generated by doxygen to maintain consistent order. """
import sys
def read_by_line(file_path):
with open(file_path, "r") as f:
while line := f.readline():
yield line[:-1]
def main():
if len(sys.argv) != 3:
print("Wrong number of arguments provided, expected two.")
print("Usage: python3 sort_doxygen_warnings.py [input_file] [output_file]")
sys.exit(1)
warnings = []
for line in read_by_line(sys.argv[1]):
if "warning:" in line or "error:" in line:
warnings.append(line)
continue
# Doxygen warnings can span multiple lines, keep these lines together.
warnings[-1] = f"{warnings[-1]}\n{line}"
warnings = sorted(set(warnings))
with open(sys.argv[2], "w") as out:
out.write("\n".join(warnings))
out.write("\n")
print("Doxygen warnings sorted successfully.")
if __name__ == "__main__":
main()
+11 -4
View File
@@ -14,23 +14,30 @@ jobs:
container:
# If you change this version, change the numbers in the cache step,
# .github/workflows/preview-build.yml (2x) and os/emscripten/Dockerfile too.
image: emscripten/emsdk:6.0.1
image: emscripten/emsdk:3.1.57
steps:
- name: Checkout
uses: actions/checkout@v7
uses: actions/checkout@v6
- name: Fix dubious ownership
run: |
git config --global --add safe.directory ${GITHUB_WORKSPACE}
- name: Update to modern GCC
run: |
apt-get update
apt-get install -y gcc-12 g++-12
update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-12 100
update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-12 100
- name: Setup cache
uses: actions/cache@v6
uses: actions/cache@v4
with:
# If you change this version, change the numbers in the image configuration step,
# .github/workflows/preview-build.yml (2x) and os/emscripten/Dockerfile too.
path: /emsdk/upstream/emscripten/cache
key: 6.0.1-${{ runner.os }}
key: 3.1.57-${{ runner.os }}
- name: Add liblzma support
run: |
+1 -1
View File
@@ -30,7 +30,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v7
uses: actions/checkout@v6
- name: Setup vcpkg
id: vcpkg
+1 -1
View File
@@ -32,7 +32,7 @@ jobs:
xcode-version: latest-stable
- name: Checkout
uses: actions/checkout@v7
uses: actions/checkout@v6
- name: Setup vcpkg
id: vcpkg
+10 -11
View File
@@ -21,7 +21,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v7
uses: actions/checkout@v6
- name: Setup MSYS2
uses: msys2/setup-msys2@v2
@@ -31,16 +31,15 @@ jobs:
install: >-
git
make
pacboy: >-
cmake:p
gcc:p
lzo2:p
libpng:p
lld:p
ninja:p
libogg:p
opus:p
opusfile:p
mingw-w64-${{ inputs.arch }}-cmake
mingw-w64-${{ inputs.arch }}-gcc
mingw-w64-${{ inputs.arch }}-lzo2
mingw-w64-${{ inputs.arch }}-libpng
mingw-w64-${{ inputs.arch }}-lld
mingw-w64-${{ inputs.arch }}-ninja
mingw-w64-${{ inputs.arch }}-libogg
mingw-w64-${{ inputs.arch }}-opus
mingw-w64-${{ inputs.arch }}-opusfile
- name: Install OpenGFX
shell: bash
+3 -1
View File
@@ -31,8 +31,10 @@ jobs:
fail-fast: false
matrix:
include:
- msystem: UCRT64
- msystem: MINGW64
arch: x86_64
- msystem: MINGW32
arch: i686
name: MinGW (${{ matrix.arch }})
+1 -1
View File
@@ -18,7 +18,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v7
uses: actions/checkout@v6
- name: Setup vcpkg
id: vcpkg
+1 -1
View File
@@ -25,7 +25,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v7
uses: actions/checkout@v6
- name: Setup vcpkg
id: vcpkg
+1 -1
View File
@@ -14,7 +14,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v7
uses: actions/checkout@v6
with:
fetch-depth: 4
-88
View File
@@ -1,88 +0,0 @@
name: Docs checker
on:
pull_request:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.ref != 'refs/heads/master' }}
jobs:
docs-checker:
name: New doxygen warnings checker
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v7
with:
fetch-depth: 4
- name: Get pull-request commits
uses: OpenTTD/actions/checkout-pull-request@v6
- name: Install Doxygen
uses: ./.github/install-doxygen
- name: Build docs for PR
run: |
mkdir build
echo "::group::CMake"
cmake -S . -B build -DOPTION_DOCS_ONLY=ON -DOPTION_LINE_IN_DOXYGEN_WARNINGS=OFF -DOPTION_DOXYGEN_GS_WARN_FILE="Warnings.GS" -DOPTION_DOXYGEN_AI_WARN_FILE="Warnings.AI" -DOPTION_DOXYGEN_WARN_FILE="Warnings.source"
echo "::endgroup::"
echo "::group::Build Source"
cmake --build build --target docs_source
python3 .github/sort_doxygen_warnings.py build/Warnings.source doxygen_warnings.PR
echo "::endgroup::"
echo "::group::Build AI"
cmake --build build --target docs_ai
python3 .github/sort_doxygen_warnings.py build/Warnings.AI doxygen_AI_warnings.PR
echo "::endgroup::"
echo "::group::Build GS"
cmake --build build --target docs_game
python3 .github/sort_doxygen_warnings.py build/Warnings.GS doxygen_GS_warnings.PR
echo "::endgroup::"
rm -r build
- name: Build docs for base branch
run: |
git checkout HEAD^
mkdir build
echo "::group::CMake"
cmake -S . -B build -DOPTION_DOCS_ONLY=ON -DOPTION_LINE_IN_DOXYGEN_WARNINGS=OFF -DOPTION_DOXYGEN_GS_WARN_FILE="Warnings.GS" -DOPTION_DOXYGEN_AI_WARN_FILE="Warnings.AI" -DOPTION_DOXYGEN_WARN_FILE="Warnings.source"
echo "::endgroup::"
echo "::group::Build Source"
cmake --build build --target docs_source
python3 .github/sort_doxygen_warnings.py build/Warnings.source doxygen_warnings.base
echo "::endgroup::"
echo "::group::Build AI"
cmake --build build --target docs_ai
python3 .github/sort_doxygen_warnings.py build/Warnings.AI doxygen_AI_warnings.base
echo "::endgroup::"
echo "::group::Build GS"
cmake --build build --target docs_game
python3 .github/sort_doxygen_warnings.py build/Warnings.GS doxygen_GS_warnings.base
echo "::endgroup::"
rm -r build
- name: Compare doxygen warnings and errors
run: |
unset FAIL
diff doxygen_warnings.base doxygen_warnings.PR | grep '^>' && FAIL=1
diff doxygen_AI_warnings.base doxygen_AI_warnings.PR | grep '^>' && FAIL=1
diff doxygen_GS_warnings.base doxygen_GS_warnings.PR | grep '^>' && FAIL=1
if [ $FAIL ]; then
exit 1;
fi
-29
View File
@@ -1,29 +0,0 @@
name: File descriptions
on:
pull_request:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.ref != 'refs/heads/master' }}
jobs:
file-descriptions:
name: File descriptions
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v7
with:
fetch-depth: 4
- name: Get pull-request commits
uses: OpenTTD/actions/checkout-pull-request@v6
- name: Check descriptions of source files
run: |
git diff-tree --no-commit-id --name-only -r HEAD HEAD^ > modified_files.txt
cat modified_files.txt
set -ex
python3 .github/file-descriptions.py modified_files.txt
+33 -13
View File
@@ -2,20 +2,31 @@ name: Preview build
on:
workflow_call:
secrets:
PREVIEW_CLOUDFLARE_API_TOKEN:
description: API token to upload a preview to Cloudflare Pages
required: true
PREVIEW_CLOUDFLARE_ACCOUNT_ID:
description: Account ID to upload a preview to Cloudflare Pages
required: true
jobs:
preview:
name: Build preview
environment:
name: preview
url: https://preview.openttd.org/pr${{ github.event.pull_request.number }}/
runs-on: ubuntu-latest
container:
# If you change this version, change the numbers in the cache step,
# .github/workflows/ci-emscripten.yml (2x) and os/emscripten/Dockerfile too.
image: emscripten/emsdk:6.0.1
image: emscripten/emsdk:3.1.57
steps:
- name: Checkout
uses: actions/checkout@v7
uses: actions/checkout@v6
with:
ref: ${{ github.event.pull_request.head.sha }}
@@ -24,13 +35,20 @@ jobs:
git config --global --add safe.directory ${GITHUB_WORKSPACE}
git checkout -b pr${{ github.event.pull_request.number }}
- name: Update to modern GCC
run: |
apt-get update
apt-get install -y gcc-12 g++-12
update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-12 100
update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-12 100
- name: Setup cache
uses: actions/cache@v6
uses: actions/cache@v4
with:
path: /emsdk/upstream/emscripten/cache
# If you change this version, change the numbers in the image configuration step,
# .github/workflows/ci-emscripten.yml (2x) and os/emscripten/Dockerfile too.
key: 6.0.1-${{ runner.os }}
key: 3.1.57-${{ runner.os }}
- name: Add liblzma support
run: |
@@ -79,14 +97,16 @@ jobs:
cp build/openttd.js public/
cp build/openttd.wasm public/
mkdir pr
echo ${{ github.event.pull_request.number }} > ./pr/number
# Ensure we use the latest version of npm; the one we get with current
# emscripten doesn't allow running "npx wrangler" as root.
# Current emscripten can't install npm>=10.0.0 because node is too old.
npm install -g npm@9
- name: Upload artifacts
uses: actions/upload-artifact@v7
- name: Publish preview
uses: cloudflare/pages-action@v1
with:
name: pr${{ github.event.pull_request.number }}
path: |
public
pr
retention-days: 1
apiToken: ${{ secrets.PREVIEW_CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.PREVIEW_CLOUDFLARE_ACCOUNT_ID }}
projectName: ${{ vars.PREVIEW_CLOUDFLARE_PROJECT_NAME }}
directory: public
branch: pr${{ github.event.pull_request.number }}
-59
View File
@@ -1,59 +0,0 @@
name: Preview publish
on:
workflow_run:
workflows:
- Preview
types:
- completed
jobs:
preview:
name: Publish preview
runs-on: ubuntu-latest
if: >
github.event.workflow_run.event == 'pull_request' &&
github.event.workflow_run.conclusion == 'success'
steps:
- name: Create deployment
id: deployment
run: |
curl -L \
-X POST \
-H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" \
-H "X-GitHub-Api-Version: 2026-03-10" \
https://api.github.com/repos/${{ github.repository }}/deployments \
-d '{"ref":"${{ github.event.workflow_run.head_sha }}","environment":"preview","required_contexts":[]}' \
| jq --raw-output0 '"url=\(.statuses_url)"' >> $GITHUB_OUTPUT
- name: Download artifacts
uses: actions/download-artifact@v8
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
run-id: ${{github.event.workflow_run.id }}
- name: Get PR number
id: pr
run: |
echo "number=$(cat pr/number)" >> $GITHUB_OUTPUT
- name: Publish preview
uses: cloudflare/wrangler-action@v4
with:
apiToken: ${{ secrets.PREVIEW_CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.PREVIEW_CLOUDFLARE_ACCOUNT_ID }}
command: pages deploy public --project-name=${{ vars.PREVIEW_CLOUDFLARE_PROJECT_NAME }} --branch pr${{ steps.pr.outputs.number }}
- name: Update deployment
if: always()
run: |
curl -L \
-X POST \
-H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" \
-H "X-GitHub-Api-Version: 2026-03-10" \
${{ steps.deployment.outputs.url }} \
-d '{"state":"${{ job.status }}","log_url":"${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}","environment_url":"https://preview.openttd.org/pr${{ steps.pr.outputs.number }}/","auto_inactive":false}'
+1 -1
View File
@@ -1,7 +1,7 @@
name: Preview
on:
pull_request:
pull_request_target:
types:
- labeled
- synchronize
+15 -4
View File
@@ -15,7 +15,7 @@ jobs:
steps:
- name: Download source
uses: actions/download-artifact@v8
uses: actions/download-artifact@v6
with:
name: internal-source
@@ -23,8 +23,19 @@ jobs:
run: |
tar -xf source.tar.gz --strip-components=1
- name: Install Doxygen
uses: ./.github/install-doxygen
- name: Install dependencies
run: |
echo "::group::Update apt"
sudo apt-get update
echo "::endgroup::"
echo "::group::Install dependencies"
sudo apt-get install -y --no-install-recommends \
doxygen \
# EOF
echo "::endgroup::"
env:
DEBIAN_FRONTEND: noninteractive
- name: Build
run: |
@@ -67,7 +78,7 @@ jobs:
echo "::endgroup::"
- name: Store bundles
uses: actions/upload-artifact@v7
uses: actions/upload-artifact@v5
with:
name: openttd-docs
path: build/bundles/*.tar.xz
+3 -6
View File
@@ -21,7 +21,7 @@ jobs:
steps:
- name: Download source
uses: actions/download-artifact@v8
uses: actions/download-artifact@v6
with:
name: internal-source
@@ -40,11 +40,9 @@ jobs:
echo "::group::Install system dependencies"
# perl-IPC-Cmd, wget, and zip are needed to run vcpkg.
# autoconf-archive is needed to build ICU.
# perl-Time-Piece is needed to build OpenSSL.
yum install -y \
autoconf-archive \
perl-IPC-Cmd \
perl-Time-Piece \
wget \
zip \
# EOF
@@ -83,7 +81,6 @@ jobs:
yum install -y \
alsa-lib-devel \
pulseaudio-libs-devel \
pipewire-devel \
# EOF
echo "::endgroup::"
@@ -160,14 +157,14 @@ jobs:
echo "::endgroup::"
- name: Store bundles
uses: actions/upload-artifact@v7
uses: actions/upload-artifact@v5
with:
name: openttd-linux-generic
path: build/bundles
retention-days: 5
- name: Store symbols
uses: actions/upload-artifact@v7
uses: actions/upload-artifact@v5
with:
name: symbols-linux-generic
path: build/symbols
+4 -4
View File
@@ -23,7 +23,7 @@ jobs:
xcode-version: latest-stable
- name: Download source
uses: actions/download-artifact@v8
uses: actions/download-artifact@v6
with:
name: internal-source
@@ -77,7 +77,7 @@ jobs:
echo "::endgroup::"
- name: Import code signing certificates
uses: Apple-Actions/import-codesign-certs@v7
uses: Apple-Actions/import-codesign-certs@v5
with:
# The certificates in a PKCS12 file encoded as a base64 string
p12-file-base64: ${{ secrets.APPLE_DEVELOPER_CERTIFICATE_P12_BASE64 }}
@@ -203,14 +203,14 @@ jobs:
mv _CPack_Packages/*/Bundle/openttd-*.zip bundles/
- name: Store bundles
uses: actions/upload-artifact@v7
uses: actions/upload-artifact@v5
with:
name: openttd-macos-universal
path: build-x64/bundles
retention-days: 5
- name: Store symbols
uses: actions/upload-artifact@v7
uses: actions/upload-artifact@v5
with:
name: symbols-macos-universal
path: build-x64/symbols
+5 -5
View File
@@ -30,14 +30,14 @@ jobs:
steps:
- name: Checkout (Release)
if: github.event_name == 'release'
uses: actions/checkout@v7
uses: actions/checkout@v6
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@v7
uses: actions/checkout@v6
with:
ref: ${{ github.event.inputs.ref }}
# We generate a changelog; for this we need the full git log.
@@ -45,7 +45,7 @@ jobs:
- name: Checkout (Trigger)
if: github.event_name == 'repository_dispatch'
uses: actions/checkout@v7
uses: actions/checkout@v6
with:
ref: ${{ github.event.client_payload.ref }}
# We generate a changelog; for this we need the full git log.
@@ -202,14 +202,14 @@ jobs:
echo "::endgroup::"
- name: Store bundles
uses: actions/upload-artifact@v7
uses: actions/upload-artifact@v5
with:
name: openttd-source
path: build/bundles/*
retention-days: 5
- name: Store source (for other jobs)
uses: actions/upload-artifact@v7
uses: actions/upload-artifact@v5
with:
name: internal-source
path: source.tar.gz
+8 -8
View File
@@ -15,7 +15,7 @@ jobs:
steps:
- name: Download source
uses: actions/download-artifact@v8
uses: actions/download-artifact@v6
with:
name: internal-source
@@ -25,17 +25,17 @@ jobs:
tar -xf source.tar.gz --strip-components=1
- name: Download x86 build
uses: actions/download-artifact@v8
uses: actions/download-artifact@v6
with:
name: openttd-windows-x86
- name: Download x64 build
uses: actions/download-artifact@v8
uses: actions/download-artifact@v6
with:
name: openttd-windows-x64
- name: Download arm64 build
uses: actions/download-artifact@v8
uses: actions/download-artifact@v6
with:
name: openttd-windows-arm64
@@ -124,7 +124,7 @@ jobs:
run: |
cd builds
REM We need to provide a signed .msix to the Windows Store, so generate a certificate with password 'password'
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
@@ -176,16 +176,16 @@ jobs:
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.msixbundle"
SignTool sign /fd sha256 /a /f cert.pfx /p password "output\OpenTTD.appxbundle"
REM Move resulting files to bundles folder
mkdir bundles
mkdir bundles\internal
move cert.pfx bundles\internal\openttd-${{ inputs.version }}-windows-store.pfx
move output\OpenTTD.msixbundle bundles\internal\openttd-${{ inputs.version }}-windows-store.msixbundle
move output\OpenTTD.appxbundle bundles\internal\openttd-${{ inputs.version }}-windows-store.appxbundle
- name: Store bundles
uses: actions/upload-artifact@v7
uses: actions/upload-artifact@v5
with:
name: openttd-windows-store
path: builds/bundles
+3 -3
View File
@@ -30,7 +30,7 @@ jobs:
steps:
- name: Download source
uses: actions/download-artifact@v8
uses: actions/download-artifact@v6
with:
name: internal-source
@@ -193,14 +193,14 @@ jobs:
AZURE_CODESIGN_PROFILE_NAME: ${{ secrets.AZURE_CODESIGN_PROFILE_NAME }}
- name: Store bundles
uses: actions/upload-artifact@v7
uses: actions/upload-artifact@v5
with:
name: openttd-windows-${{ matrix.arch }}
path: build/bundles
retention-days: 5
- name: Store symbols
uses: actions/upload-artifact@v7
uses: actions/upload-artifact@v5
with:
name: symbols-windows-${{ matrix.arch }}
path: build/symbols
-15
View File
@@ -153,18 +153,3 @@ jobs:
with:
version: ${{ needs.source.outputs.version }}
upload-windows-store:
name: Upload (Windows Store)
needs:
- upload
# Only upload final release builds to Windows Store; "-beta" or "-RC" tags should be ignored
if: needs.source.outputs.is_tag == 'true' && !contains(needs.source.outputs.version, '-')
uses: ./.github/workflows/upload-windows-store.yml
secrets: inherit
with:
version: ${{ needs.source.outputs.version }}
trigger_type: ${{ needs.source.outputs.trigger_type }}
@@ -14,7 +14,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v7
uses: actions/checkout@v6
- name: Check for finding script functions that require company/deity mode enforcement/checks
run: |
+1 -1
View File
@@ -14,7 +14,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v7
uses: actions/checkout@v6
- name: Check for unused strings
run: |
+3 -3
View File
@@ -21,7 +21,7 @@ jobs:
steps:
- name: Download all bundles
uses: actions/download-artifact@v8
uses: actions/download-artifact@v6
- name: Calculate checksums
run: |
@@ -70,14 +70,14 @@ jobs:
done
- name: Store bundles
uses: actions/upload-artifact@v7
uses: actions/upload-artifact@v5
with:
name: cdn-bundles
path: bundles/*
retention-days: 5
- name: Store breakpad symbols
uses: actions/upload-artifact@v7
uses: actions/upload-artifact@v5
with:
name: cdn-symbols
path: symbols/*
+11 -10
View File
@@ -15,39 +15,40 @@ jobs:
steps:
- name: Download source
uses: actions/download-artifact@v8
uses: actions/download-artifact@v6
with:
name: internal-source
path: internal-source
- name: Download bundle (Windows x86)
uses: actions/download-artifact@v8
uses: actions/download-artifact@v6
with:
name: openttd-windows-x86
path: openttd-windows-x86
- name: Download bundle (Windows x64)
uses: actions/download-artifact@v8
uses: actions/download-artifact@v6
with:
name: openttd-windows-x64
path: openttd-windows-x64
- name: Download bundle (MacOS)
uses: actions/download-artifact@v8
uses: actions/download-artifact@v6
with:
name: openttd-macos-universal
path: openttd-macos-universal
- name: Download bundle (Linux)
uses: actions/download-artifact@v8
uses: actions/download-artifact@v6
with:
name: openttd-linux-generic
path: openttd-linux-generic
- name: Install GOG Galaxy Build Creator
run: |
wget https://cdn.gog.com/open/galaxy/pipeline/10.6.7.17/gnu-linux/GOGGalaxyPipelineBuilder
chmod +x ./GOGGalaxyPipelineBuilder
wget https://cdn.gog.com/open/galaxy/pipeline/build_creator/gnu-linux/GOGGalaxyBuildCreator-1.4.0.AppImage
7z x GOGGalaxyBuildCreator-1.4.0.AppImage
chmod +x ./app/GOGGalaxyPipelineBuilder
- name: Install OpenGFX
shell: bash
@@ -144,8 +145,8 @@ jobs:
echo "::endgroup::"
echo "::group::Upload to GOG"
../GOGGalaxyPipelineBuilder build-game --username "${{ secrets.GOG_USERNAME }}" --password "${{ secrets.GOG_PASSWORD }}" --branch Testing windows.json
../GOGGalaxyPipelineBuilder build-game --username "${{ secrets.GOG_USERNAME }}" --password "${{ secrets.GOG_PASSWORD }}" --branch Testing macos.json
../GOGGalaxyPipelineBuilder build-game --username "${{ secrets.GOG_USERNAME }}" --password "${{ secrets.GOG_PASSWORD }}" --branch Testing linux.json
../app/GOGGalaxyPipelineBuilder build-game --username "${{ secrets.GOG_USERNAME }}" --password "${{ secrets.GOG_PASSWORD }}" --branch Testing windows.json
../app/GOGGalaxyPipelineBuilder build-game --username "${{ secrets.GOG_USERNAME }}" --password "${{ secrets.GOG_PASSWORD }}" --branch Testing macos.json
../app/GOGGalaxyPipelineBuilder build-game --username "${{ secrets.GOG_USERNAME }}" --password "${{ secrets.GOG_PASSWORD }}" --branch Testing linux.json
echo "::endgroup::"
)
+5 -5
View File
@@ -18,31 +18,31 @@ jobs:
steps:
- name: Download source
uses: actions/download-artifact@v8
uses: actions/download-artifact@v6
with:
name: internal-source
path: internal-source
- name: Download bundle (Windows x86)
uses: actions/download-artifact@v8
uses: actions/download-artifact@v6
with:
name: openttd-windows-x86
path: openttd-windows-x86
- name: Download bundle (Windows x64)
uses: actions/download-artifact@v8
uses: actions/download-artifact@v6
with:
name: openttd-windows-x64
path: openttd-windows-x64
- name: Download bundle (MacOS)
uses: actions/download-artifact@v8
uses: actions/download-artifact@v6
with:
name: openttd-macos-universal
path: openttd-macos-universal
- name: Download bundle (Linux)
uses: actions/download-artifact@v8
uses: actions/download-artifact@v6
with:
name: openttd-linux-generic
path: openttd-linux-generic
@@ -1,44 +0,0 @@
name: Upload (Windows Store)
on:
workflow_call:
inputs:
version:
required: true
type: string
trigger_type:
required: true
type: string
jobs:
upload:
name: Upload (Windows Store)
runs-on: windows-latest
steps:
- name: Download bundle (Windows Store)
uses: actions/download-artifact@v8
with:
name: openttd-windows-store
path: openttd-windows-store
- name: Configure Microsoft Store CLI
uses: microsoft/microsoft-store-apppublisher@v1.3
- name: Reconfigure store credentials
run: msstore reconfigure `
--tenantId ${{ secrets.AZURE_TENANT_ID }} `
--sellerId ${{ secrets.WINSTORE_SELLER_ID }} `
--clientId ${{ secrets.AZURE_WINSTORE_APPLICATION_CLIENT_ID }} `
--clientSecret ${{ secrets.AZURE_WINSTORE_APPLICATION_SECRET }}
- name: Publish msix package
shell: pwsh
run: |
$bundle = Get-ChildItem openttd-windows-store/internal/openttd-*.msixbundle | Select-Object -First 1
if (-not $bundle) {
throw "No msixbundle found"
}
msstore publish $bundle.FullName -id 9NCJG5RVRR1C -v
+5 -9
View File
@@ -5,7 +5,7 @@ if(NOT BINARY_NAME)
endif()
project(${BINARY_NAME}
VERSION 16.0
VERSION 15.0
LANGUAGES CXX
)
@@ -75,10 +75,6 @@ add_custom_target(find_version
-DREV_MAJOR=${PROJECT_VERSION_MAJOR}
-DREV_MINOR=${PROJECT_VERSION_MINOR}
-DWINDOWS=${WIN32}
-DDOXYGEN_WARN_FORMAT_LINE=${DOXYGEN_WARN_FORMAT_LINE}
-DDOXYGEN_WARN_FILE=${DOXYGEN_WARN_FILE}
-DDOXYGEN_GS_WARN_FILE=${DOXYGEN_GS_WARN_FILE}
-DDOXYGEN_AI_WARN_FILE=${DOXYGEN_AI_WARN_FILE}
-P "${CMAKE_SOURCE_DIR}/cmake/scripts/FindVersion.cmake"
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
BYPRODUCTS ${GENERATED_SOURCE_FILES}
@@ -189,7 +185,7 @@ if(UNIX AND NOT APPLE AND NOT OPTION_DEDICATED)
if(NOT SDL2_FOUND AND NOT ALLEGRO_FOUND)
message(FATAL_ERROR "SDL2 or Allegro is required for this platform")
endif()
if(HARFBUZZ_FOUND AND NOT ICU_I18N_FOUND)
if(HARFBUZZ_FOUND AND NOT ICU_i18n_FOUND)
message(WARNING "HarfBuzz depends on ICU i18n to function; HarfBuzz will be disabled")
endif()
if(NOT HARFBUZZ_FOUND)
@@ -327,8 +323,8 @@ if(NOT OPTION_DEDICATED)
link_package(FREETYPE TARGET Freetype::Freetype)
link_package(Fontconfig TARGET Fontconfig::Fontconfig)
link_package(Harfbuzz TARGET harfbuzz::harfbuzz)
link_package(ICU_I18N TARGET ICU::i18n)
link_package(ICU_UC TARGET ICU::uc)
link_package(ICU_i18n)
link_package(ICU_uc)
link_package(OpusFile TARGET OpusFile::opusfile)
if(SDL2_FOUND AND OPENGL_FOUND AND UNIX)
@@ -370,7 +366,7 @@ if(EMSCRIPTEN)
add_definitions(-s DISABLE_EXCEPTION_CATCHING=0)
# Export functions to Javascript.
target_link_libraries(WASM::WASM INTERFACE "-s EXPORTED_FUNCTIONS='[\"_main\", \"_em_openttd_add_server\"]' -s EXPORTED_RUNTIME_METHODS='[\"cwrap\", \"HEAPU8\"]'")
target_link_libraries(WASM::WASM INTERFACE "-s EXPORTED_FUNCTIONS='[\"_main\", \"_em_openttd_add_server\"]' -s EXPORTED_RUNTIME_METHODS='[\"cwrap\"]'")
# Preload all the files we generate during build.
# As we do not compile with FreeType / FontConfig, we also have no way to
+15 -19
View File
@@ -45,9 +45,7 @@ PYTHON_DOCSTRING = YES
INHERIT_DOCS = YES
SEPARATE_MEMBER_PAGES = NO
TAB_SIZE = 2
ALIASES = important="@attention" # @important is available in newer versions of doxygen.
ALIASES += split_group{1}="@}^^@{^^@ingroup \1^^" \
split_group{3;}="^^@defgroup \1_\2 \3^^@ingroup \1^^@{^^@}^^@}^^@{^^@ingroup \1_\2^^"
ALIASES =
OPTIMIZE_OUTPUT_FOR_C = YES
OPTIMIZE_OUTPUT_JAVA = NO
OPTIMIZE_FOR_FORTRAN = NO
@@ -61,7 +59,7 @@ BUILTIN_STL_SUPPORT = NO
CPP_CLI_SUPPORT = NO
SIP_SUPPORT = NO
IDL_PROPERTY_SUPPORT = YES
DISTRIBUTE_GROUP_DOC = YES
DISTRIBUTE_GROUP_DOC = NO
GROUP_NESTED_COMPOUNDS = NO
SUBGROUPING = YES
INLINE_GROUPED_CLASSES = NO
@@ -120,12 +118,11 @@ WARNINGS = YES
WARN_IF_UNDOCUMENTED = YES
WARN_IF_DOC_ERROR = YES
WARN_IF_INCOMPLETE_DOC = YES
WARN_NO_PARAMDOC = YES
WARN_IF_UNDOC_ENUM_VAL = YES
WARN_NO_PARAMDOC = NO
WARN_AS_ERROR = NO
WARN_FORMAT = "$file:$${DOXYGEN_WARN_FORMAT_LINE}: $text"
WARN_LINE_FORMAT = "at line $${DOXYGEN_WARN_FORMAT_LINE} of file $file"
WARN_LOGFILE = ${DOXYGEN_WARN_FILE}
WARN_FORMAT = "$file:$line: $text"
WARN_LINE_FORMAT = "at line $line of file $file"
WARN_LOGFILE =
#---------------------------------------------------------------------------
# Configuration options related to the input files
#---------------------------------------------------------------------------
@@ -137,15 +134,12 @@ FILE_PATTERNS = *.c \
*.cpp \
*.c++ \
*.h \
*.hpp \
*.mm \
*.m
*.hpp
RECURSIVE = YES
EXCLUDE =
EXCLUDE_SYMLINKS = NO
EXCLUDE_PATTERNS = */3rdparty \
*/script/api \
*/tests
*/script/api
EXCLUDE_SYMBOLS =
EXAMPLE_PATH =
EXAMPLE_PATTERNS = *
@@ -191,6 +185,7 @@ HTML_EXTRA_FILES =
HTML_COLORSTYLE_HUE = 220
HTML_COLORSTYLE_SAT = 100
HTML_COLORSTYLE_GAMMA = 80
HTML_TIMESTAMP = NO
HTML_DYNAMIC_MENUS = YES
HTML_DYNAMIC_SECTIONS = NO
HTML_INDEX_NUM_ENTRIES = 100
@@ -226,6 +221,7 @@ EXT_LINKS_IN_WINDOW = NO
OBFUSCATE_EMAILS = YES
HTML_FORMULA_FORMAT = png
FORMULA_FONTSIZE = 10
FORMULA_TRANSPARENT = YES
FORMULA_MACROFILE =
USE_MATHJAX = NO
MATHJAX_VERSION = MathJax_2
@@ -260,6 +256,7 @@ USE_PDFLATEX = NO
LATEX_BATCHMODE = NO
LATEX_HIDE_INDICES = NO
LATEX_BIB_STYLE = plain
LATEX_TIMESTAMP = NO
LATEX_EMOJI_DIRECTORY =
#---------------------------------------------------------------------------
# Configuration options related to the RTF output
@@ -320,14 +317,10 @@ PREDEFINED = WITH_ZLIB \
WITH_HARFBUZZ \
WITH_ICU_I18N \
UNICODE \
DOXYGEN_API \
_UNICODE \
_GNU_SOURCE \
CDECL= \
CALLBACK= \
APIENTRY= \
FINAL=
EXPAND_AS_DEFINED = DEFINE_POOL_METHOD
EXPAND_AS_DEFINED =
SKIP_FUNCTION_MACROS = YES
#---------------------------------------------------------------------------
# Configuration options related to external references
@@ -344,6 +337,8 @@ DIA_PATH =
HIDE_UNDOC_RELATIONS = YES
HAVE_DOT = NO
DOT_NUM_THREADS = 0
DOT_FONTNAME = Helvetica
DOT_FONTSIZE = 10
DOT_FONTPATH =
CLASS_GRAPH = YES
COLLABORATION_GRAPH = YES
@@ -371,6 +366,7 @@ PLANTUML_CFG_FILE =
PLANTUML_INCLUDE_PATH =
DOT_GRAPH_MAX_NODES = 50
MAX_DOT_GRAPH_DEPTH = 1000
DOT_TRANSPARENT = NO
DOT_MULTI_TARGETS = NO
GENERATE_LEGEND = YES
DOT_CLEANUP = YES
-1
View File
@@ -15,7 +15,6 @@ set(AI_COMPAT_SOURCE_FILES
${CMAKE_CURRENT_SOURCE_DIR}/compat_12.nut
${CMAKE_CURRENT_SOURCE_DIR}/compat_13.nut
${CMAKE_CURRENT_SOURCE_DIR}/compat_14.nut
${CMAKE_CURRENT_SOURCE_DIR}/compat_15.nut
)
foreach(AI_COMPAT_SOURCE_FILE IN LISTS AI_COMPAT_SOURCE_FILES)
-8
View File
@@ -1,8 +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 <https://www.gnu.org/licenses/old-licenses/gpl-2.0>.
*/
/* This file contains code to downgrade the API from 16 to 15. */
-1
View File
@@ -12,7 +12,6 @@ set(GS_COMPAT_SOURCE_FILES
${CMAKE_CURRENT_SOURCE_DIR}/compat_12.nut
${CMAKE_CURRENT_SOURCE_DIR}/compat_13.nut
${CMAKE_CURRENT_SOURCE_DIR}/compat_14.nut
${CMAKE_CURRENT_SOURCE_DIR}/compat_15.nut
)
foreach(GS_COMPAT_SOURCE_FILE IN LISTS GS_COMPAT_SOURCE_FILES)
-140
View File
@@ -1,140 +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 <https://www.gnu.org/licenses/old-licenses/gpl-2.0>.
*/
/* This file contains code to downgrade the API from 16 to 15. */
/*
* These window/widget related enumerations have never been stable, but breaking anything using them
* is a really big ask when restructuring code. If in the future things are removed from the underlying
* enumerations, feel free to remove them from this compatibility script.
*/
GSWindow.WN_GAME_OPTIONS_AI <- GSWindow.GameOptionsWindowNumber.AI;
GSWindow.WN_GAME_OPTIONS_GS <- GSWindow.GameOptionsWindowNumber.GS;
GSWindow.WN_GAME_OPTIONS_ABOUT <- GSWindow.GameOptionsWindowNumber.About;
GSWindow.WN_GAME_OPTIONS_NEWGRF_STATE <- GSWindow.GameOptionsWindowNumber.NewGRFState;
GSWindow.WN_GAME_OPTIONS_GAME_OPTIONS <- GSWindow.GameOptionsWindowNumber.GameOptions;
GSWindow.WN_GAME_OPTIONS_GAME_SETTINGS <- GSWindow.GameOptionsWindowNumber.GameOptions;
GSWindow.WN_QUERY_STRING <- GSWindow.QueryStringWindowNumber.Default;
GSWindow.WN_QUERY_STRING_SIGN <- GSWindow.QueryStringWindowNumber.Sign;
GSWindow.WN_CONFIRM_POPUP_QUERY <- GSWindow.ConfirmPopupQueryWindowNumber.Default;
GSWindow.WN_CONFIRM_POPUP_QUERY_BOOTSTRAP <- GSWindow.ConfirmPopupQueryWindowNumber.Bootstrap;
GSWindow.WN_NETWORK_WINDOW_GAME <- GSWindow.NetworkWindowNumber.Game;
GSWindow.WN_NETWORK_WINDOW_CONTENT_LIST <- GSWindow.NetworkWindowNumber.ContentList;
GSWindow.WN_NETWORK_WINDOW_START <- GSWindow.NetworkWindowNumber.StartServer;
GSWindow.WN_NETWORK_STATUS_WINDOW_JOIN <- GSWindow.NetworkStatusWindowNumber.Join;
GSWindow.WN_NETWORK_STATUS_WINDOW_CONTENT_DOWNLOAD <- GSWindow.NetworkStatusWindowNumber.ContentDownload;
GSWindow.WC_NONE <- GSWindow.WindowClass.None;
GSWindow.WC_MAIN_WINDOW <- GSWindow.WindowClass.MainWindow;
GSWindow.WC_MAIN_TOOLBAR <- GSWindow.WindowClass.MainToolbar;
GSWindow.WC_STATUS_BAR <- GSWindow.WindowClass.Statusbar;
GSWindow.WC_BUILD_TOOLBAR <- GSWindow.WindowClass.BuildToolbar;
GSWindow.WC_SCEN_BUILD_TOOLBAR <- GSWindow.WindowClass.ScenarioBuildToolbar;
GSWindow.WC_BUILD_TREES <- GSWindow.WindowClass.BuildTrees;
GSWindow.WC_TRANSPARENCY_TOOLBAR <- GSWindow.WindowClass.TransparencyToolbar;
GSWindow.WC_BUILD_SIGNAL <- GSWindow.WindowClass.BuildSignal;
GSWindow.WC_SMALLMAP <- GSWindow.WindowClass.SmallMap;
GSWindow.WC_ERRMSG <- GSWindow.WindowClass.ErrorMessage;
GSWindow.WC_TOOLTIPS <- GSWindow.WindowClass.ToolTips;
GSWindow.WC_QUERY_STRING <- GSWindow.WindowClass.QueryString;
GSWindow.WC_CONFIRM_POPUP_QUERY <- GSWindow.WindowClass.ConfirmPopupQuery;
GSWindow.WC_GOAL_QUESTION <- GSWindow.WindowClass.GoalQuestion;
GSWindow.WC_SAVELOAD <- GSWindow.WindowClass.SaveLoad;
GSWindow.WC_LAND_INFO <- GSWindow.WindowClass.LandInfo;
GSWindow.WC_DROPDOWN_MENU <- GSWindow.WindowClass.DropdownMenu;
GSWindow.WC_OSK <- GSWindow.WindowClass.OnScreenKeyboard;
GSWindow.WC_SET_DATE <- GSWindow.WindowClass.SetDate;
GSWindow.WC_SCRIPT_SETTINGS <- GSWindow.WindowClass.ScriptSettings;
GSWindow.WC_GRF_PARAMETERS <- GSWindow.WindowClass.NewGRFParameters;
GSWindow.WC_TEXTFILE <- GSWindow.WindowClass.Textfile;
GSWindow.WC_TOWN_AUTHORITY <- GSWindow.WindowClass.TownAuthority;
GSWindow.WC_VEHICLE_DETAILS <- GSWindow.WindowClass.VehicleDetails;
GSWindow.WC_VEHICLE_REFIT <- GSWindow.WindowClass.VehicleRefit;
GSWindow.WC_VEHICLE_ORDERS <- GSWindow.WindowClass.VehicleOrders;
GSWindow.WC_REPLACE_VEHICLE <- GSWindow.WindowClass.ReplaceVehicle;
GSWindow.WC_VEHICLE_TIMETABLE <- GSWindow.WindowClass.VehicleTimetable;
GSWindow.WC_COMPANY_COLOUR <- GSWindow.WindowClass.CompanyLivery;
GSWindow.WC_COMPANY_MANAGER_FACE <- GSWindow.WindowClass.CompanyManagerFace;
GSWindow.WC_SELECT_STATION <- GSWindow.WindowClass.JoinStation;
GSWindow.WC_NEWS_WINDOW <- GSWindow.WindowClass.News;
GSWindow.WC_TOWN_DIRECTORY <- GSWindow.WindowClass.TownDirectory;
GSWindow.WC_SUBSIDIES_LIST <- GSWindow.WindowClass.SubsidyList;
GSWindow.WC_INDUSTRY_DIRECTORY <- GSWindow.WindowClass.IndustryDirectory;
GSWindow.WC_MESSAGE_HISTORY <- GSWindow.WindowClass.MessageHistory;
GSWindow.WC_SIGN_LIST <- GSWindow.WindowClass.SignList;
GSWindow.WC_SCRIPT_LIST <- GSWindow.WindowClass.ScriptList;
GSWindow.WC_GOALS_LIST <- GSWindow.WindowClass.GoalList;
GSWindow.WC_STORY_BOOK <- GSWindow.WindowClass.StoryBook;
GSWindow.WC_STATION_LIST <- GSWindow.WindowClass.StationList;
GSWindow.WC_TRAINS_LIST <- GSWindow.WindowClass.TrainList;
GSWindow.WC_ROADVEH_LIST <- GSWindow.WindowClass.RoadVehicleList;
GSWindow.WC_SHIPS_LIST <- GSWindow.WindowClass.ShipList;
GSWindow.WC_AIRCRAFT_LIST <- GSWindow.WindowClass.AircraftList;
GSWindow.WC_TOWN_VIEW <- GSWindow.WindowClass.TownView;
GSWindow.WC_VEHICLE_VIEW <- GSWindow.WindowClass.VehicleView;
GSWindow.WC_STATION_VIEW <- GSWindow.WindowClass.StationView;
GSWindow.WC_VEHICLE_DEPOT <- GSWindow.WindowClass.VehicleDepot;
GSWindow.WC_WAYPOINT_VIEW <- GSWindow.WindowClass.WaypointView;
GSWindow.WC_INDUSTRY_VIEW <- GSWindow.WindowClass.IndustryView;
GSWindow.WC_COMPANY <- GSWindow.WindowClass.Company;
GSWindow.WC_BUILD_OBJECT <- GSWindow.WindowClass.BuildObject;
GSWindow.WC_BUILD_HOUSE <- GSWindow.WindowClass.BuildHouse;
GSWindow.WC_BUILD_VEHICLE <- GSWindow.WindowClass.BuildVehicle;
GSWindow.WC_BUILD_BRIDGE <- GSWindow.WindowClass.BuildBridge;
GSWindow.WC_BUILD_STATION <- GSWindow.WindowClass.BuildStation;
GSWindow.WC_BUS_STATION <- GSWindow.WindowClass.BuildBusStation;
GSWindow.WC_TRUCK_STATION <- GSWindow.WindowClass.BuildTruckStation;
GSWindow.WC_BUILD_DEPOT <- GSWindow.WindowClass.BuildDepot;
GSWindow.WC_BUILD_WAYPOINT <- GSWindow.WindowClass.BuildWaypoint;
GSWindow.WC_FOUND_TOWN <- GSWindow.WindowClass.FoundTown;
GSWindow.WC_BUILD_INDUSTRY <- GSWindow.WindowClass.BuildIndustry;
GSWindow.WC_SELECT_GAME <- GSWindow.WindowClass.SelectGame;
GSWindow.WC_SCEN_LAND_GEN <- GSWindow.WindowClass.ScenarioGenerateLandscape;
GSWindow.WC_GENERATE_LANDSCAPE <- GSWindow.WindowClass.GenerateLandscape;
GSWindow.WC_MODAL_PROGRESS <- GSWindow.WindowClass.ModalProgress;
GSWindow.WC_NETWORK_WINDOW <- GSWindow.WindowClass.Network;
GSWindow.WC_CLIENT_LIST <- GSWindow.WindowClass.NetworkClientList;
GSWindow.WC_NETWORK_STATUS_WINDOW <- GSWindow.WindowClass.NetworkStatus;
GSWindow.WC_NETWORK_ASK_RELAY <- GSWindow.WindowClass.NetworkAskRelay;
GSWindow.WC_NETWORK_ASK_SURVEY <- GSWindow.WindowClass.NetworkAskSurvey;
GSWindow.WC_SEND_NETWORK_MSG <- GSWindow.WindowClass.NetworkChat;
GSWindow.WC_INDUSTRY_CARGOES <- GSWindow.WindowClass.IndustryCargoes;
GSWindow.WC_GRAPH_LEGEND <- GSWindow.WindowClass.GraphLegend;
GSWindow.WC_FINANCES <- GSWindow.WindowClass.Finances;
GSWindow.WC_INCOME_GRAPH <- GSWindow.WindowClass.IncomeGraph;
GSWindow.WC_OPERATING_PROFIT <- GSWindow.WindowClass.OperatingProfitGraph;
GSWindow.WC_DELIVERED_CARGO <- GSWindow.WindowClass.DeliveredCargoGraph;
GSWindow.WC_PERFORMANCE_HISTORY <- GSWindow.WindowClass.PerformanceGraph;
GSWindow.WC_COMPANY_VALUE <- GSWindow.WindowClass.CompanyValueGraph;
GSWindow.WC_COMPANY_LEAGUE <- GSWindow.WindowClass.CompanyLeague;
GSWindow.WC_PAYMENT_RATES <- GSWindow.WindowClass.CargoPaymentRatesGraph;
GSWindow.WC_PERFORMANCE_DETAIL <- GSWindow.WindowClass.PerformanceDetail;
GSWindow.WC_INDUSTRY_PRODUCTION <- GSWindow.WindowClass.IndustryProductionGraph;
GSWindow.WC_TOWN_CARGO_GRAPH <- GSWindow.WindowClass.TownCargoGraph;
GSWindow.WC_COMPANY_INFRASTRUCTURE <- GSWindow.WindowClass.CompanyInfrastructure;
GSWindow.WC_BUY_COMPANY <- GSWindow.WindowClass.BuyCompany;
GSWindow.WC_ENGINE_PREVIEW <- GSWindow.WindowClass.EnginePreview;
GSWindow.WC_MUSIC_WINDOW <- GSWindow.WindowClass.Music;
GSWindow.WC_MUSIC_TRACK_SELECTION <- GSWindow.WindowClass.MusicTrackSelection;
GSWindow.WC_GAME_OPTIONS <- GSWindow.WindowClass.GameOptions;
GSWindow.WC_CUSTOM_CURRENCY <- GSWindow.WindowClass.CustomCurrenty;
GSWindow.WC_CHEATS <- GSWindow.WindowClass.Cheat;
GSWindow.WC_EXTRA_VIEWPORT <- GSWindow.WindowClass.ExtraViewport;
GSWindow.WC_CONSOLE <- GSWindow.WindowClass.Console;
GSWindow.WC_BOOTSTRAP <- GSWindow.WindowClass.Bootstrap;
GSWindow.WC_HIGHSCORE <- GSWindow.WindowClass.Highscore;
GSWindow.WC_ENDSCREEN <- GSWindow.WindowClass.Endscreen;
GSWindow.WC_SCRIPT_DEBUG <- GSWindow.WindowClass.ScriptDebug;
GSWindow.WC_NEWGRF_INSPECT <- GSWindow.WindowClass.NewGRFInspect;
GSWindow.WC_SPRITE_ALIGNER <- GSWindow.WindowClass.SpriteAligner;
GSWindow.WC_LINKGRAPH_LEGEND <- GSWindow.WindowClass.LinkGraphLegend;
GSWindow.WC_SAVE_PRESET <- GSWindow.WindowClass.SavePreset;
GSWindow.WC_FRAMERATE_DISPLAY <- GSWindow.WindowClass.FramerateDisplay;
GSWindow.WC_FRAMETIME_GRAPH <- GSWindow.WindowClass.FrametimeGraph;
GSWindow.WC_SCREENSHOT <- GSWindow.WindowClass.Screenshot;
GSWindow.WC_HELPWIN <- GSWindow.WindowClass.Help;
GSWindow.WC_INVALID <- GSWindow.WindowClass.Invalid;
-193
View File
@@ -1,198 +1,5 @@
## 16.x
### 16.0-beta2 (2026-07-12)
- Feature: Allow bridges over depots (#15836)
- Feature: Increase maximum number of vehicles per type to 10,000 (#15810)
- Feature: Add and remove whole classes to picker collections (#15808)
- Feature: Station list can be filtered by name (#15772)
- Change: Use "bridge ... too low" message for bridgeable objects (#15838)
- Fix: Use meaningful tooltips for graph range toggle buttons (#15842)
- Fix #15826: Exclude allow any/all options for AI companies (#15834)
- Fix #7992: Fix for catenary road and tram catenary sprites not being drawn on bridges (#15811)
- Fix #15784: Crash due to incorrect road build toolbar when switching road tram types (#15789)
- Fix: [NewGRF] Make 'signals on traffic side' consistent with explicit settings (#15661)
- Fix: [Win32] Explicitly initialize COM in Win32 video driver (#15553)
### 16.0-beta1 (2026-06-25)
- Feature: Add default road and tram selection (#15585)
- Feature: Trains with an engine on the rear can drive backwards when reversing (#15379)
- Feature: Add worldgen setting for average height of terrain (#14989)
- Feature: Add text filter to dropdown lists (#14842)
- Feature: User-defined collections of saved items in the picker window (#14813)
- Feature: Configurable sign text colors in scenario editor (#14743)
- Feature: Allow placing an area of 1x1 houses in scenario editor (#14708)
- Feature: Scale cargo payment aging rate (#14635)
- Feature: Support automatic detection of data from Atari's Transport Tycoon Deluxe re-release (#15483)
- Add: [NewGRF] Implement custom waypoint layouts by callback 24 (#15616)
- Add: List 'Place object' in landscaping dropdown (#15609)
- Add: Setting to disable aircraft range limit (#15433)
- Add: Show maximum reliability in vehicle info (#15401)
- Add: Lower the Send To Depot button when a vehicle is on its way to one (#15397)
- Add: Setting to disallow train magic flip, and reverse at reduced speed (#15391)
- Add: [NewGRF] Variable for when a train is driving backwards (#15379)
- Add: [NewGRF] Flag to allow unpowered wagon to lead train when backing up (#15379)
- Add: [NewGRF] Station flag to divide cargo by area instead of perimeter (#15253)
- Add: Company allow-list can be set to allow anyone (#15204)
- Add: Record history of cargo delivered to towns (#15184)
- Add: Show additional vehicle details based on sort criteria (#15158)
- Add: Setting to control minimum distance between towns (#14893)
- Change: Selecting Rail/Roadtype updates build toolbar instead of closing and reopening (#15589)
- Change: Default setting to review orders excludes stopped vehicles (#15565)
- Change: Most used rail type takes ownership into account (#15489)
- Change: Test font for eligibility before adding as fallback (#15412)
- Change: Clarify max age shown in vehicle info window (#15403)
- Change: Show "Delete All" in order window delete button when applicable (#15370)
- Change: Make continuation error messages consistent (#15328)
- Change: Distinguish between 'Flat land required' and 'Land sloped in wrong direction' for building depots (#15327)
- Change: Allow subsidies with CargoDist (#15189)
- Change: Removing river tiles reduces local authority rating (#15180)
- Change: Protect rivers from removal when terraforming (#15176)
- Change: [Script] Reformat callstack output (#15171)
- Change: Apply slightly more padding to image-text buttons (#15077)
- Change: [NewGRF] Display rail and road type speed limit text in yellow (#15076)
- Change: Make smallmap show infinite water, if enabled (#15041)
- Change: Add support for next/previous/first/last rail/road/tram type hotkeys (#15028)
- Change: Allow rocks on desert tiles (#14979)
- Change: [Script] Include Array/PriorityQueye/ScriptList contents in script memory allocation total (#14954)
- Change: [Script] Various ScriptList methods are now suspendable (#14943, #14762)
- Change: Improve generation of coasts at map edges (#14868)
- Change: Show only selected badge in badge filter dropdown buttons (#14855)
- Change: River springs must be surrounded by hills (#14845)
- Change: Vehicles loading at stations don't lose reliability (#14841)
- Change: Generate lighthouses along coasts and near towns (#14828)
- Change: Transmitter object only spawns after original base year (#14786)
- Change: Share one window for engine preview pop ups (#14703)
- Fix: [NewGRF] Town name language incorrectly decoded (#15691)
- Fix #15606: Undefined behaviour may cause buttons to be invisible (#15686)
- Fix #14975: Show correct tile for tunnel error message (#15660)
- Fix: [NewGRF] Incorrect language IDs passed to TranslateTTDPatchCodes (#15655)
- Fix: Scenario editor random industry generation (#15626)
- Fix: [NewGRF] Ignore blocked waypoint tiles (#15620)
- Fix: [NewGRF] Pylons and no-wires flags were ignored for waypoint tiles (#15619)
- Fix: Dedicated server signal handling for SIGTERM/SIGINT/SIGQUIT (#15607)
- Fix: [NewGRF] Potential out-of-bounds read when performing GRM (#15600)
- Fix: Badge offsets not ignored when determining width (#15588)
- Fix: Account for NewGRF's global train width when drawing dual-head engines (#15587)
- Fix: Station list facility buttons do not produce click sound (#15569)
- Fix #15495: Fix incorrectly built default waypoints (#15562)
- Fix #15495: Unable to build bridge over default waypoints (#15562)
- Fix #15235: Make GameScripts obey town road construction setting (#15559)
- Fix: Crash when loading crash save with no viewport coordinates (#15550)
- Fix #15520: Fix viewport snap back on stop following vehicle (#15520) (#15531)
- Fix: Buttons for choosing engine in preview window show for single engine (#15514)
- Fix #15511: Vehicle passed to commands must be a company buildable type (#15512)
- Fix #15497: [Script] ScriptController::Break not pausing in TestMode (#15498)
- Fix: Coordinate transformation for JSON town placement when anticlockwise (#15490)
- Fix: NewGRF debug handler for towns in wrong place since badges (#15467)
- Fix: Object id overrides were not reset between games (#15465)
- Fix: Debug saveload errors were clobbered (#15452)
- Fix: Refresh client list when client renames (#15439)
- Fix #15428: [Fluidsynth] Treat configured synth.gain as maximum instead of hardcoded value (#15429)
- Fix: Refresh string width cache for all loaded fonts (#15427)
- Fix: Typo in Hungarian town name suffix ("kak" -> "lak") (#15421)
- Fix: [NewGRF] Variable 0x69 has not been added for waypoints (#15396)
- Fix: Dropdown text filter could interfere with hotkeys after closing (#15395)
- Fix: Rejecting or accepting engine preview incorrectly closed preview window (#15378)
- Fix #15318: Nonsensical error message when trying to send aircraft to hangar with no usable airports (#15334)
- Fix #15234: Null pointer dereference opening sign edit window when sign 0 does not exist (#15236)
- Fix: [GS] Docs are missing or do not match the code (#15230)
- Fix: Off by one and redundant logic in snowy road checks (#15214)
- Fix #15137: Roads built on steep slopes at the minimum snow level height have inconsistent snowiness (#15190)
- Fix: Station view window fold/unfold not refreshing scrollbar (#15187)
- Fix: Use consistent description for invalid orders (#15177)
- Fix: [Script] Give GetPrimary/SecondaryLiveryColour preconditions (#15169)
- Fix: Cocoa video driver did not register (#15145)
- Fix: MacOS version detection was broken (#15145)
- Fix #13600: Make terraform click+draggable in scenario editor (#15135)
- Fix: [Script] Map some error strings to existing errors (#15113)
- Fix: Allow to build buoys at (0x0) (#14983)
- Fix #14610: Do not treat aircraft with only depot orders as having valid orders (#14961)
- Fix #12937: Allow the computer to go to sleep while the game is paused (#14948)
- Fix: Company query popups do not update in multiplayer (#14947)
- Fix #14892: Potential crash when logging fonts for survey (#14896)
- Fix #14827: Store random trigger information per-tile (#14830)
## 15.x
### 15.3 (2026-04-04)
- Fix: Conditional orders could require a maximum reliability over 100% (#15409)
- Fix: Improve appearance of toolbars and main menu images/text with some non-default base sets (#15402)
- Fix: [Script] IsBuildableRectangle for a 0x0 tile should return false (#15357)
- Fix: Desync caused by train crashes (#15338)
- Fix: Incorrect scroll bar capacity for train details window total cargo tab (#15329)
- Fix #15310: Crash caused by a helicopter running out of fuel near map edge (#15311)
### 15.2 (2026-02-18)
- Fix: Crashed zeppelin not blocking runway (#15281)
- Fix #15269: Crash when building drive-through stop on unowned one-way road (#15270)
- Fix #15247: Crash when handling invalid sprite in OpenGL cursor sprite system (#15248)
- Fix #15105: Czech townname generation causing crashes (#15231)
- Fix: Incorrect ground type test when placing towns (#15217)
- Fix: Town supplied cargo history incorrectly clamped to 16 bit values (#15183)
- Fix #15085: Crash when GUI scale changes before video driver is fully initialised (#15175)
- Fix: Articulated road vehicle following with immediately sequential u-turns (#15170)
- Fix #15166: Foundations missing adjacent to NewGRF objects without foundation (#15168)
- Fix #14035: [Fluidsynth] Read settings from config files if available (#15044)
- Fix #15263: Badge filter toggles no longer worked (#14972)
### 15.1 (2026-01-24)
- Fix #15088: When building a new train, the refit button state may be incorrect (#15162)
- Fix #15160: Incorrect company names displayed in load game window (#15161)
- Fix #15153: Wrong tile used to get bridge reservation overlay (#15154)
- Fix #15116: Old cargo/industry sets without cargo translation table broken (#15150)
- Fix: Possible crash converting company liveries in older savegames/scenarios (#15148)
- Fix: Allow infinite water to be (de)selected when loading heightmap (#15146)
- Fix: Tile suitability test for farm field no longer handled snow tiles (#15134)
- Fix #15131: Trees no longer spread on partially snowy tiles (#15133)
- Fix: Change tooltips to match change from checkboxes to switches (#15123)
- Fix: [Script] Potential out of bounds array/string slice indexes (#15106)
- Fix: [Script] Potential out of bounds indexed string access (#15106)
- Fix: [Script] Check if array sort function modified array (#15106)
- Fix #15069: World generation map edges GUI starts in an invalid state (#15082)
- Fix #15079: Incorrect dates shown on town cargo history graph (#15080)
- Fix #15067: Mark NewGRF settings as modified after moving by drag & drop (#15068)
- Fix: Incorrect error message for aqueducts reaching northern map borders (#14974)
- Fix: Standardize wording of GRF/NewGRF (#15059)
- Fix #15046: Crash on loading game due to invalid group parents (#15049)
- Fix: Disable_elrails handling with engines that use both RAIL and ELRL (#15045)
- Fix: [Fluidsynth] Read settings from system and user config files if available (#15044)
- Fix #15039: Name and version can disappear from content list (#15040)
- Fix #15026: Remove incorrect info from base sounds tooltip (#15029)
- Fix: [Script] Improve reporting of invalid GetAPIVersion return (#15015)
- Fix: [Script] Undefined behaviour after calling SwapList during iteration (#14805)
### 15.0 (2026-01-01)
- Fix: Small ufos would loop over vehicles in depots forever (#15008)
- Fix #14982: Can't place buoys under bridges (#15007)
- Fix #15004: Crashes when dropdown is held open during endgame screen (#15006)
- Fix: Further improve town spawning near water (#15002)
- Fix: Use correct 'minutes per year' setting for old savegames (#14995)
- Fix: Rare crash dividing by 0 when drawing a line (#14994)
- Fix #14992: Respect non-stop order setting when adding waypoint orders (#14993)
### 15.0-RC4 (2025-12-26)
- Fix: Incorrect text colour in fund industry window (#14987)
- Fix: Towns failed to find spawn locations near water (#14988)
- Fix: Don't draw bridge deck rail sprites for default bridges (#14985)
- Fix: "(Invalid parameter)" in error message when trying to remove another player's object (#14981)
- Fix #14978: Don't clear water tiles after removing buoys (#14980)
- Fix #14973: Composed strings are incorrect colour (#14976)
- Fix: Badge filter toggles no longer worked (#14972)
- Fix #12465: Click/tooltip alignment of industry chain cargo lines (#14963)
- Fix #14951: Possible incorrect data in industry production graphs (#14962)
- Fix #14958: Crash when opening station window (#14959)
- Fix #14938: Don't allow cacti to die off (#14956)
- Fix: Don't allow joining a company after it was taken over (#14955)
- Fix #14949: Crash when moving station sign in a network game (#14950)
- Fix #14945: Hang when deleting implicit orders during vehicle loading (#14946)
### 15.0-RC3 (2025-12-20)
- Fix #14932: [NewGRF] Increase internal badge index size to avoid overflowing BadgeIDs (#14933)
+27
View File
@@ -56,6 +56,11 @@ macro(compile_flags)
# This flag disables the broken optimisation to work around the bug
add_compile_options(/d2ssa-rse-)
endif()
if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
add_compile_options(
-Wno-multichar
)
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
@@ -73,6 +78,9 @@ macro(compile_flags)
-Wnon-virtual-dtor
-Wsuggest-override
# We use 'ABCD' multichar for SaveLoad chunks identifiers
-Wno-multichar
# Prevent optimisation supposing enums are in a range specified by the standard
# For details, see http://gcc.gnu.org/PR43680 and PR#5246.
-fno-strict-enums
@@ -92,6 +100,9 @@ macro(compile_flags)
endif()
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
include(CheckCXXCompilerFlag)
check_cxx_compiler_flag("-flifetime-dse=1" LIFETIME_DSE_FOUND)
add_compile_options(
# GCC 4.2+ automatically assumes that signed overflows do
# not occur in signed arithmetics, whereas we are not
@@ -99,6 +110,10 @@ macro(compile_flags)
# about its own optimized code in some places.
"-fno-strict-overflow"
# -flifetime-dse=2 (default since GCC 6) doesn't play
# well with our custom pool item allocator
"$<$<BOOL:${LIFETIME_DSE_FOUND}>:-flifetime-dse=1>"
# We have a fight between clang wanting std::move() and gcc not wanting it
# and of course they both warn when the other compiler is happy
"-Wno-redundant-move"
@@ -118,6 +133,18 @@ macro(compile_flags)
endif()
endif()
endif()
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Intel")
add_compile_options(
-Wall
# warning #873: function ... ::operator new ... has no corresponding operator delete ...
-wd873
# warning #1292: unknown attribute "fallthrough"
-wd1292
# warning #1899: multicharacter character literal (potential portability problem)
-wd1899
# warning #2160: anonymous union qualifier is ignored
-wd2160
)
else()
message(FATAL_ERROR "No warning flags are set for this compiler yet; please consider creating a Pull Request to add support for this compiler.")
endif()
+2 -6
View File
@@ -5,12 +5,12 @@
#
# create_regression(file1 ...)
#
macro(create_regression SCRIPT_FOLDER)
macro(create_regression)
set(REGRESSION_SOURCE_FILES ${ARGN})
foreach(REGRESSION_SOURCE_FILE IN LISTS REGRESSION_SOURCE_FILES)
string(REPLACE "${CMAKE_SOURCE_DIR}/regression/" "" REGRESSION_SOURCE_FILE_NAME "${REGRESSION_SOURCE_FILE}")
string(CONCAT REGRESSION_BINARY_FILE "${CMAKE_BINARY_DIR}/${SCRIPT_FOLDER}/" "${REGRESSION_SOURCE_FILE_NAME}")
string(CONCAT REGRESSION_BINARY_FILE "${CMAKE_BINARY_DIR}/ai/" "${REGRESSION_SOURCE_FILE_NAME}")
add_custom_command(OUTPUT ${REGRESSION_BINARY_FILE}
COMMAND ${CMAKE_COMMAND} -E copy
@@ -38,7 +38,6 @@ macro(create_regression SCRIPT_FOLDER)
-DOPENTTD_EXECUTABLE=$<TARGET_FILE:openttd>
-DEDITBIN_EXECUTABLE=${EDITBIN_EXECUTABLE}
-DREGRESSION_TEST=${REGRESSION_TEST_NAME}
-DSCRIPT_FOLDER=${SCRIPT_FOLDER}
-P "${CMAKE_SOURCE_DIR}/cmake/scripts/Regression.cmake"
DEPENDS openttd regression_${REGRESSION_TEST_NAME}_files
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
@@ -51,12 +50,9 @@ macro(create_regression SCRIPT_FOLDER)
-DOPENTTD_EXECUTABLE=$<TARGET_FILE:openttd>
-DEDITBIN_EXECUTABLE=${EDITBIN_EXECUTABLE}
-DREGRESSION_TEST=${REGRESSION_TEST_NAME}
-DSCRIPT_FOLDER=${SCRIPT_FOLDER}
-P "${CMAKE_SOURCE_DIR}/cmake/scripts/Regression.cmake"
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
)
set_property(TEST regression_${REGRESSION_TEST_NAME} PROPERTY RUN_SERIAL TRUE)
add_dependencies(regression regression_${REGRESSION_TEST_NAME})
endmacro()
+69
View File
@@ -0,0 +1,69 @@
# CMake provides a FindICU module since version 3.7.
# But it doesn't use pkgconfig, doesn't set expected variables,
# And it returns incomplete dependencies if only some modules are searched.
#[=======================================================================[.rst:
FindICU
-------
Finds components of the ICU library.
Accepted components are: uc, i18n, le, lx, io, data
Result Variables
^^^^^^^^^^^^^^^^
This will define the following variables:
``ICU_FOUND``
True if components of ICU library are found.
``ICU_VERSION``
The version of the ICU library which was found.
``ICU_<c>_FOUND``
True if the system has the <c> component of ICU library.
``ICU_<c>_INCLUDE_DIRS``
Include directories needed to use the <c> component of ICU library.
``ICU_<c>_LIBRARIES``
Libraries needed to link to the <c> component of ICU library.
#]=======================================================================]
find_package(PkgConfig QUIET)
set(ICU_KNOWN_COMPONENTS "uc" "i18n" "le" "lx" "io" "data")
foreach(MOD_NAME IN LISTS ICU_FIND_COMPONENTS)
if(NOT MOD_NAME IN_LIST ICU_KNOWN_COMPONENTS)
message(FATAL_ERROR "Unknown ICU component: ${MOD_NAME}")
endif()
pkg_check_modules(PC_ICU_${MOD_NAME} QUIET icu-${MOD_NAME})
# Check the libraries returned by pkg-config really exist.
unset(PC_LIBRARIES)
foreach(LIBRARY IN LISTS PC_ICU_${MOD_NAME}_LIBRARIES)
unset(PC_LIBRARY CACHE)
find_library(PC_LIBRARY NAMES ${LIBRARY})
if(NOT PC_LIBRARY)
unset(PC_ICU_${MOD_NAME}_FOUND)
endif()
list(APPEND PC_LIBRARIES ${PC_LIBRARY})
endforeach()
unset(PC_LIBRARY CACHE)
if(${PC_ICU_${MOD_NAME}_FOUND})
set(ICU_COMPONENT_FOUND TRUE)
set(ICU_${MOD_NAME}_FOUND TRUE)
set(ICU_${MOD_NAME}_LIBRARIES ${PC_LIBRARIES})
set(ICU_${MOD_NAME}_INCLUDE_DIRS ${PC_ICU_${MOD_NAME}_INCLUDE_DIRS})
set(ICU_VERSION ${PC_ICU_${MOD_NAME}_VERSION})
endif()
endforeach()
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(ICU
FOUND_VAR ICU_FOUND
REQUIRED_VARS ICU_COMPONENT_FOUND
VERSION_VAR ICU_VERSION
HANDLE_COMPONENTS
)
+31 -50
View File
@@ -190,63 +190,44 @@ elseif(UNIX)
set(CPACK_GENERATOR "TXZ")
set(PLATFORM "unknown")
else()
if(EXISTS "/etc/os-release")
file(STRINGS "/etc/os-release" OS_RELEASE_LIKE_CONTENTS REGEX "^ID_LIKE=")
if (OS_RELEASE_LIKE_CONTENTS)
string(REGEX MATCH "ID_LIKE=\\\"?([^\\\"]+)" _ ${OS_RELEASE_LIKE_CONTENTS})
string(REGEX MATCHALL "([^ ]+)" DISTRO_LIKE_IDS ${CMAKE_MATCH_1})
endif()
if (DISTRO_LIKE_IDS)
set(DISTRO_IDS ${DISTRO_LIKE_IDS})
else()
file(STRINGS "/etc/os-release" OS_RELEASE_CONTENTS REGEX "^ID=")
string(REGEX MATCH "ID=(.*)" _ ${OS_RELEASE_CONTENTS})
set(DISTRO_IDS ${CMAKE_MATCH_1})
endif()
if("arch" IN_LIST DISTRO_IDS)
set(PLATFORM "arch")
set(CPACK_GENERATOR "TXZ")
elseif("fedora" IN_LIST DISTRO_IDS OR "rhel" IN_LIST DISTRO_IDS)
set(PLATFORM "fedora")
set(CPACK_GENERATOR "RPM")
include(PackageRPM)
elseif("debian" IN_LIST DISTRO_IDS OR "ubuntu" IN_LIST DISTRO_IDS OR "linuxmint" IN_LIST DISTRO_IDS)
file(STRINGS "/etc/os-release" OS_RELEASE_CODENAME REGEX "^VERSION_CODENAME=")
string(REGEX MATCH "VERSION_CODENAME=(.*)" _ ${OS_RELEASE_CODENAME})
set(RELEASE_CODENAME ${CMAKE_MATCH_1})
string(TOLOWER "${DISTRO_ID}-${RELEASE_CODENAME}" PLATFORM)
find_program(LSB_RELEASE_EXEC lsb_release)
execute_process(COMMAND ${LSB_RELEASE_EXEC} -is
OUTPUT_VARIABLE LSB_RELEASE_ID
OUTPUT_STRIP_TRAILING_WHITESPACE
)
if(LSB_RELEASE_ID)
if(LSB_RELEASE_ID STREQUAL "Ubuntu" OR LSB_RELEASE_ID STREQUAL "Debian" OR LSB_RELEASE_ID STREQUAL "Linuxmint")
execute_process(COMMAND ${LSB_RELEASE_EXEC} -cs
OUTPUT_VARIABLE LSB_RELEASE_CODENAME
OUTPUT_STRIP_TRAILING_WHITESPACE
)
string(TOLOWER "${LSB_RELEASE_ID}-${LSB_RELEASE_CODENAME}" PLATFORM)
set(CPACK_GENERATOR "DEB")
include(PackageDeb)
elseif(LSB_RELEASE_ID STREQUAL "Fedora")
set(PLATFORM "fedora")
set(CPACK_GENERATOR "RPM")
include(PackageRPM)
else()
set(UNSUPPORTED_PLATFORM_NAME "LSB-based Linux distribution '${LSB_RELEASE_ID}'")
endif()
elseif(EXISTS "/etc/os-release")
file(STRINGS "/etc/os-release" OS_RELEASE_CONTENTS REGEX "^ID=")
string(REGEX MATCH "ID=(.*)" _ ${OS_RELEASE_CONTENTS})
set(DISTRO_ID ${CMAKE_MATCH_1})
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()
else()
find_program(LSB_RELEASE_EXEC lsb_release)
execute_process(COMMAND ${LSB_RELEASE_EXEC} -is
OUTPUT_VARIABLE LSB_RELEASE_ID
OUTPUT_STRIP_TRAILING_WHITESPACE
)
if(LSB_RELEASE_ID)
if(LSB_RELEASE_ID STREQUAL "Ubuntu" OR LSB_RELEASE_ID STREQUAL "Debian" OR LSB_RELEASE_ID STREQUAL "Linuxmint")
execute_process(COMMAND ${LSB_RELEASE_EXEC} -cs
OUTPUT_VARIABLE LSB_RELEASE_CODENAME
OUTPUT_STRIP_TRAILING_WHITESPACE
)
string(TOLOWER "${LSB_RELEASE_ID}-${LSB_RELEASE_CODENAME}" PLATFORM)
set(CPACK_GENERATOR "DEB")
include(PackageDeb)
elseif(LSB_RELEASE_ID STREQUAL "Fedora")
set(PLATFORM "fedora")
set(CPACK_GENERATOR "RPM")
include(PackageRPM)
else()
set(UNSUPPORTED_PLATFORM_NAME "LSB-based Linux distribution '${LSB_RELEASE_ID}'")
endif()
else()
set(UNSUPPORTED_PLATFORM_NAME "Linux distribution")
endif()
set(UNSUPPORTED_PLATFORM_NAME "Linux distribution")
endif()
if(NOT PLATFORM)
-20
View File
@@ -61,32 +61,12 @@ function(set_options)
option(OPTION_TOOLS_ONLY "Build only tools target" OFF)
option(OPTION_DOCS_ONLY "Build only docs target" OFF)
option(OPTION_ALLOW_INVALID_SIGNATURE "Allow loading of content with invalid signatures" OFF)
option(OPTION_LINE_IN_DOXYGEN_WARNINGS "Print line number in doxygen warnings" ON)
if (OPTION_DOCS_ONLY)
set(OPTION_TOOLS_ONLY ON PARENT_SCOPE)
endif()
if (OPTION_LINE_IN_DOXYGEN_WARNINGS)
set(DOXYGEN_WARN_FORMAT_LINE "line" PARENT_SCOPE)
endif()
option(OPTION_SURVEY_KEY "Survey-key to use for the opt-in survey (empty if you have none)" "")
option(OPTION_DOXYGEN_WARN_FILE "File to write doxygen warnings into (if empty warnings are written to standard error)" "")
option(OPTION_DOXYGEN_GS_WARN_FILE "File to write doxygen warnings from game script docs into (if empty warnings are written to standard error)" "")
option(OPTION_DOXYGEN_AI_WARN_FILE "File to write doxygen warnings from AI docs into (if empty warnings are written to standard error)" "")
if(OPTION_DOXYGEN_WARN_FILE)
set(DOXYGEN_WARN_FILE ${CMAKE_BINARY_DIR}/${OPTION_DOXYGEN_WARN_FILE} PARENT_SCOPE)
endif()
if(OPTION_DOXYGEN_GS_WARN_FILE)
set(DOXYGEN_GS_WARN_FILE ${CMAKE_BINARY_DIR}/${OPTION_DOXYGEN_GS_WARN_FILE} PARENT_SCOPE)
endif()
if(OPTION_DOXYGEN_AI_WARN_FILE)
set(DOXYGEN_AI_WARN_FILE ${CMAKE_BINARY_DIR}/${OPTION_DOXYGEN_AI_WARN_FILE} PARENT_SCOPE)
endif()
endfunction()
# Show the values of the generic options.
-3
View File
@@ -135,9 +135,6 @@ if(GENERATE_OTTDREV)
message(STATUS "Generating .ottdrev")
file(WRITE ${CMAKE_SOURCE_DIR}/.ottdrev "${REV_VERSION}\t${REV_ISODATE}\t${REV_MODIFIED}\t${REV_HASH}\t${REV_ISTAG}\t${REV_ISSTABLETAG}\n")
else()
if(REV_ISSTABLETAG AND NOT (REV_VERSION STREQUAL "${REV_MAJOR}.${REV_MINOR}"))
message(FATAL_ERROR "Tag (${REV_VERSION}) doesn't match internal version (${REV_MAJOR}.${REV_MINOR})")
endif()
message(STATUS "Generating rev.cpp")
configure_file("${CMAKE_SOURCE_DIR}/src/rev.cpp.in"
"${FIND_VERSION_BINARY_DIR}/rev.cpp")
+13 -23
View File
@@ -25,13 +25,6 @@ endif()
file(STRINGS ${GENERATE_SOURCE_FILE} ENUM_LINES REGEX "@enum")
function(remove_invalid_links VARIABLE)
string(REGEX REPLACE "#([A-Za-z0-9_]*Widgets)" "@hash \\1" VARIABLE "${VARIABLE}")
string(REPLACE "#" "" VARIABLE "${VARIABLE}")
string(REPLACE "@hash " "#" VARIABLE "${VARIABLE}")
set(NO_INVALID_LINKS ${VARIABLE} PARENT_SCOPE)
endfunction()
foreach(ENUM IN LISTS ENUM_LINES)
string(REGEX REPLACE "^( )// @enum ([^ ]+) ([^ ]+)@([^ ]+)@" "\\4" PLACE_HOLDER "${ENUM}")
set(ADD_INDENT "${CMAKE_MATCH_1}")
@@ -56,17 +49,14 @@ foreach(ENUM IN LISTS ENUM_LINES)
# Remember possible doxygen comment before enum declaration
if((NOT ACTIVE) AND "${LINE}" MATCHES "/\\*\\*")
remove_invalid_links("${LINE}")
set(COMMENT "${ADD_INDENT}${NO_INVALID_LINKS}")
set(COMMENT "${ADD_INDENT}${LINE}")
set(ACTIVE_COMMENT 1)
elseif(ACTIVE_COMMENT EQUAL 1)
remove_invalid_links("${LINE}")
string(APPEND COMMENT "\n${ADD_INDENT}${NO_INVALID_LINKS}")
string(APPEND COMMENT "\n${ADD_INDENT}${LINE}")
endif()
# Check for enum match
if("${LINE}" MATCHES "^ *enum *(class)? *(${ENUM_PATTERN})( *: *[^ ]*)? *\{")
set(ENUM_NAME "${CMAKE_MATCH_2}")
if("${LINE}" MATCHES "^ *enum *${ENUM_PATTERN}( *: *[^ ]*)? *\{")
# REGEX REPLACE does a REGEX MATCHALL and fails if an empty string is matched
string(REGEX MATCH "[^ ]*" RESULT "${LINE}")
string(REPLACE "${RESULT}" "" RM_INDENT "${LINE}")
@@ -110,23 +100,23 @@ foreach(ENUM IN LISTS ENUM_LINES)
endforeach()
if(CMAKE_MATCH_3)
# CMAKE_MATCH_3 contains inline comment.
remove_invalid_links("${CMAKE_MATCH_3}")
string(APPEND ${PLACE_HOLDER} "\n${ADD_INDENT}${CMAKE_MATCH_1}${CMAKE_MATCH_2}${SPACES} = to_underlying(::${ENUM_NAME}::${CMAKE_MATCH_2}),${SPACES}${NO_INVALID_LINKS}")
string(APPEND ${PLACE_HOLDER} "\n${ADD_INDENT}${CMAKE_MATCH_1}${CMAKE_MATCH_2}${SPACES} = ::${CMAKE_MATCH_2},${SPACES}${CMAKE_MATCH_3}")
else()
string(APPEND ${PLACE_HOLDER} "\n${ADD_INDENT}${CMAKE_MATCH_1}${CMAKE_MATCH_2}${SPACES} = to_underlying(::${ENUM_NAME}::${CMAKE_MATCH_2}),")
string(APPEND ${PLACE_HOLDER} "\n${ADD_INDENT}${CMAKE_MATCH_1}${CMAKE_MATCH_2}${SPACES} = ::${CMAKE_MATCH_2},")
endif()
elseif("${LINE}" STREQUAL "")
string(APPEND ${PLACE_HOLDER} "\n")
elseif("${LINE}" MATCHES "^ *\};")
string(APPEND ${PLACE_HOLDER} "\n${ADD_INDENT}${LINE}\n")
unset(ACTIVE)
else()
# Line is not an enum member, so it might be a comment.
remove_invalid_links("${LINE}")
string(APPEND ${PLACE_HOLDER} "\n${ADD_INDENT}${NO_INVALID_LINKS}")
string(APPEND ${PLACE_HOLDER} "\n${ADD_INDENT}${LINE}")
endif()
endif()
if("${LINE}" MATCHES "^ *\};")
if(ACTIVE)
string(APPEND ${PLACE_HOLDER} "\n")
endif()
unset(ACTIVE)
endif()
endforeach()
endforeach()
endforeach()
+4 -7
View File
@@ -7,14 +7,11 @@ cmake_minimum_required(VERSION 3.17)
if(NOT REGRESSION_TEST)
message(FATAL_ERROR "Script needs REGRESSION_TEST defined (tip: use -DREGRESSION_TEST=..)")
endif()
if(NOT SCRIPT_FOLDER)
message(FATAL_ERROR "Script needs SCRIPT_FOLDER defined (tip: use -DSCRIPT_FOLDER=..)")
endif()
if(NOT OPENTTD_EXECUTABLE)
message(FATAL_ERROR "Script needs OPENTTD_EXECUTABLE defined (tip: use -DOPENTTD_EXECUTABLE=..)")
endif()
if(NOT EXISTS ${SCRIPT_FOLDER}/${REGRESSION_TEST}/test.sav)
if(NOT EXISTS ai/${REGRESSION_TEST}/test.sav)
message(FATAL_ERROR "Regression test ${REGRESSION_TEST} does not exist (tip: check regression folder for the correct spelling)")
endif()
@@ -38,7 +35,7 @@ endif()
execute_process(COMMAND ${OPENTTD_EXECUTABLE}
-x
-c regression/regression.cfg
-g ${SCRIPT_FOLDER}/${REGRESSION_TEST}/test.sav
-g ai/${REGRESSION_TEST}/test.sav
-snull
-mnull
-vnull:ticks=30000
@@ -80,7 +77,7 @@ string(REGEX REPLACE "\\\[script:[0-9]\\\]" "" REGRESSION_RESULT "${REGRESSION_R
# Convert the output to a format that is expected (and more readable) by result.txt
string(REPLACE "dbg: " "ERROR: " REGRESSION_RESULT "${REGRESSION_RESULT}")
string(REGEX REPLACE "ERROR: \\\[18?\\\] " "" REGRESSION_RESULT "${REGRESSION_RESULT}")
string(REPLACE "ERROR: [1] " "" REGRESSION_RESULT "${REGRESSION_RESULT}")
string(REPLACE "[P] " "" REGRESSION_RESULT "${REGRESSION_RESULT}")
string(REPLACE "[S] " "" REGRESSION_RESULT "${REGRESSION_RESULT}")
string(REGEX REPLACE "dbg: ([^\n]*)\n?" "" REGRESSION_RESULT "${REGRESSION_RESULT}")
@@ -91,7 +88,7 @@ string(REGEX REPLACE "ERROR: [12]([^\n]*)\n?" "" REGRESSION_RESULT "${REGRESSI
string(REGEX REPLACE "ERROR: The first([^\n]*)\n?" "" REGRESSION_RESULT "${REGRESSION_RESULT}")
# Read the expected result
file(READ ${SCRIPT_FOLDER}/${REGRESSION_TEST}/result.txt REGRESSION_EXPECTED)
file(READ ai/${REGRESSION_TEST}/result.txt REGRESSION_EXPECTED)
# Convert the string to a list
string(REPLACE "\n" ";" REGRESSION_RESULT "${REGRESSION_RESULT}")
+129 -61
View File
@@ -215,10 +215,6 @@ foreach(LINE IN LISTS SOURCE_LINES)
continue()
endif()
if("${LINE}" MATCHES "^([\t ]*)\\* @suspendable.*$")
set(SUSPENDABLE TRUE)
endif()
# Ignore the comments
if("${LINE}" MATCHES "^#")
continue()
@@ -262,7 +258,7 @@ foreach(LINE IN LISTS SOURCE_LINES)
endif()
# We need to make specialized conversions for enums
if("${LINE}" MATCHES "^(\t*)enum *(class)? *([^ ]*)")
if("${LINE}" MATCHES "^(\t*)enum ([^ ]*)")
math(EXPR CLS_LEVEL "${CLS_LEVEL} + 1")
# Check if we want to publish this enum
@@ -280,10 +276,7 @@ foreach(LINE IN LISTS SOURCE_LINES)
endif()
set(IN_ENUM TRUE)
if(CMAKE_MATCH_2 STREQUAL "class")
set(IN_SCOPED_ENUM TRUE)
endif()
list(APPEND ENUMS "${CLS}::${CMAKE_MATCH_3}")
list(APPEND ENUMS "${CLS}::${CMAKE_MATCH_2}")
continue()
endif()
@@ -292,7 +285,6 @@ foreach(LINE IN LISTS SOURCE_LINES)
math(EXPR CLS_LEVEL "${CLS_LEVEL} - 1")
if(CLS_LEVEL)
unset(IN_ENUM)
unset(IN_SCOPED_ENUM)
continue()
endif()
@@ -369,86 +361,168 @@ foreach(LINE IN LISTS SOURCE_LINES)
string(APPEND SQUIRREL_EXPORT "\n")
# Enum values
macro(add_scoped_enum_end_if_needed)
if(CURRENT_ENUM_NAME)
unset(CURRENT_ENUM_NAME)
string(APPEND SQUIRREL_EXPORT "\n\tSQ${API_CLS}.AddScopedEnumEnd(engine);\n")
endif()
endmacro()
set(CURRENT_ENUM_NAME 0)
set(MLEN 0)
foreach(ENUM_VALUE IN LISTS ENUM_VALUES)
if(ENUM_VALUE MATCHES "::")
string(REGEX REPLACE ".*:" "" ENUM_VALUE_NAME "${ENUM_VALUE}")
string(REGEX MATCH "::(.*)::" ENUM_NAME "${ENUM_VALUE}")
set(ENUM_NAME ${CMAKE_MATCH_1})
if(CURRENT_ENUM_NAME STREQUAL ENUM_NAME)
else()
add_scoped_enum_end_if_needed()
string(APPEND SQUIRREL_EXPORT "\n\tSQ${API_CLS}.AddScopedEnumBegin(engine, \"${ENUM_NAME}\");")
set(CURRENT_ENUM_NAME ${ENUM_NAME})
endif()
string(APPEND SQUIRREL_EXPORT "\n\tSQ${API_CLS}.DefSQConst(engine, to_underlying(${ENUM_VALUE}), \"${ENUM_VALUE_NAME}\");")
continue()
string(LENGTH "${ENUM_VALUE}" LEN)
if(MLEN LESS LEN)
set(MLEN ${LEN})
endif()
add_scoped_enum_end_if_needed()
string(APPEND SQUIRREL_EXPORT "\n\tSQ${API_CLS}.DefSQConst(engine, ${CLS}::${ENUM_VALUE}, \"${ENUM_VALUE}\");")
endforeach()
add_scoped_enum_end_if_needed()
foreach(ENUM_VALUE IN LISTS ENUM_VALUES)
string(LENGTH "${ENUM_VALUE}" LEN)
math(EXPR LEN "${MLEN} - ${LEN}")
unset(SPACES)
foreach(i RANGE ${LEN})
string(APPEND SPACES " ")
endforeach()
string(APPEND SQUIRREL_EXPORT "\n\tSQ${API_CLS}.DefSQConst(engine, ${CLS}::${ENUM_VALUE},${SPACES}\"${ENUM_VALUE}\");")
endforeach()
if(MLEN)
string(APPEND SQUIRREL_EXPORT "\n")
endif()
# Const values
set(MLEN 0)
foreach(CONST_VALUE IN LISTS CONST_VALUES)
string(APPEND SQUIRREL_EXPORT "\n\tSQ${API_CLS}.DefSQConst(engine, ${CLS}::${CONST_VALUE}, \"${CONST_VALUE}\");")
string(LENGTH "${CONST_VALUE}" LEN)
if(MLEN LESS LEN)
set(MLEN ${LEN})
endif()
endforeach()
foreach(CONST_VALUE IN LISTS CONST_VALUES)
string(LENGTH "${CONST_VALUE}" LEN)
math(EXPR LEN "${MLEN} - ${LEN}")
unset(SPACES)
foreach(i RANGE ${LEN})
string(APPEND SPACES " ")
endforeach()
string(APPEND SQUIRREL_EXPORT "\n\tSQ${API_CLS}.DefSQConst(engine, ${CLS}::${CONST_VALUE},${SPACES}\"${CONST_VALUE}\");")
endforeach()
if(MLEN)
string(APPEND SQUIRREL_EXPORT "\n")
endif()
# Mapping of OTTD strings to errors
set(MLEN 0)
foreach(ENUM_STRING_TO_ERROR IN LISTS ENUM_STRING_TO_ERRORS)
string(REPLACE ":" ";" ENUM_STRING_TO_ERROR "${ENUM_STRING_TO_ERROR}")
list(GET ENUM_STRING_TO_ERROR 0 ENUM_STRING)
string(LENGTH "${ENUM_STRING}" LEN)
if(MLEN LESS LEN)
set(MLEN ${LEN})
endif()
endforeach()
foreach(ENUM_STRING_TO_ERROR IN LISTS ENUM_STRING_TO_ERRORS)
string(REPLACE ":" ";" ENUM_STRING_TO_ERROR "${ENUM_STRING_TO_ERROR}")
list(GET ENUM_STRING_TO_ERROR 0 ENUM_STRING)
list(GET ENUM_STRING_TO_ERROR 1 ENUM_ERROR)
string(APPEND SQUIRREL_EXPORT "\n\tScriptError::RegisterErrorMap(${ENUM_STRING}, ${CLS}::${ENUM_ERROR});")
string(LENGTH "${ENUM_STRING}" LEN)
math(EXPR LEN "${MLEN} - ${LEN}")
unset(SPACES)
foreach(i RANGE ${LEN})
string(APPEND SPACES " ")
endforeach()
string(APPEND SQUIRREL_EXPORT "\n\tScriptError::RegisterErrorMap(${ENUM_STRING},${SPACES}${CLS}::${ENUM_ERROR});")
endforeach()
if(MLEN)
string(APPEND SQUIRREL_EXPORT "\n")
endif()
# Mapping of errors to human 'readable' strings.
set(MLEN 0)
foreach(ENUM_ERROR_TO_STRING IN LISTS ENUM_ERROR_TO_STRINGS)
string(LENGTH "${ENUM_ERROR_TO_STRING}" LEN)
string(APPEND SQUIRREL_EXPORT "\n\tScriptError::RegisterErrorMapString(${CLS}::${ENUM_ERROR_TO_STRING}, \"${ENUM_ERROR_TO_STRING}\");")
if(MLEN LESS LEN)
set(MLEN ${LEN})
endif()
endforeach()
foreach(ENUM_ERROR_TO_STRING IN LISTS ENUM_ERROR_TO_STRINGS)
string(LENGTH "${ENUM_ERROR_TO_STRING}" LEN)
math(EXPR LEN "${MLEN} - ${LEN}")
unset(SPACES)
foreach(i RANGE ${LEN})
string(APPEND SPACES " ")
endforeach()
string(APPEND SQUIRREL_EXPORT "\n\tScriptError::RegisterErrorMapString(${CLS}::${ENUM_ERROR_TO_STRING},${SPACES}\"${ENUM_ERROR_TO_STRING}\");")
endforeach()
if(MLEN)
string(APPEND SQUIRREL_EXPORT "\n")
endif()
# Static methods
set(MLEN 0)
foreach(STATIC_METHOD IN LISTS STATIC_METHODS)
string(REPLACE ":" ";" STATIC_METHOD "${STATIC_METHOD}")
list(GET STATIC_METHOD 0 FUNCNAME)
string(LENGTH "${FUNCNAME}" LEN)
if(MLEN LESS LEN)
set(MLEN ${LEN})
endif()
endforeach()
foreach(STATIC_METHOD IN LISTS STATIC_METHODS)
string(REPLACE ":" ";" STATIC_METHOD "${STATIC_METHOD}")
list(GET STATIC_METHOD 0 FUNCNAME)
list(GET STATIC_METHOD 1 TYPES)
list(GET STATIC_METHOD 2 SUSPENDABLE)
if(SUSPENDABLE)
set(SUSPENDABLE ", true")
endif()
string(LENGTH "${FUNCNAME}" LEN)
math(EXPR LEN "${MLEN} - ${LEN}")
if("${TYPES}" STREQUAL "v")
string(APPEND SQUIRREL_EXPORT "\n\tSQ${API_CLS}.DefSQAdvancedStaticMethod(engine, &${CLS}::${FUNCNAME}, \"${FUNCNAME}\"${SUSPENDABLE});")
if(LEN GREATER 8)
math(EXPR LEN "${LEN} - 8")
else()
set(LEN 0)
endif()
endif()
unset(SPACES)
foreach(i RANGE ${LEN})
string(APPEND SPACES " ")
endforeach()
if("${TYPES}" STREQUAL "v")
string(APPEND SQUIRREL_EXPORT "\n\tSQ${API_CLS}.DefSQAdvancedStaticMethod(engine, &${CLS}::${FUNCNAME},${SPACES}\"${FUNCNAME}\");")
else()
string(APPEND SQUIRREL_EXPORT "\n\tSQ${API_CLS}.DefSQStaticMethod(engine, &${CLS}::${FUNCNAME}, \"${FUNCNAME}\", \"${TYPES}\"${SUSPENDABLE});")
string(APPEND SQUIRREL_EXPORT "\n\tSQ${API_CLS}.DefSQStaticMethod(engine, &${CLS}::${FUNCNAME},${SPACES}\"${FUNCNAME}\",${SPACES}\"${TYPES}\");")
endif()
endforeach()
if(MLEN)
string(APPEND SQUIRREL_EXPORT "\n")
endif()
# Non-static methods
set(MLEN 0)
foreach(METHOD IN LISTS METHODS)
string(REPLACE ":" ";" METHOD "${METHOD}")
list(GET METHOD 0 FUNCNAME)
string(LENGTH "${FUNCNAME}" LEN)
if(MLEN LESS LEN)
set(MLEN ${LEN})
endif()
endforeach()
foreach(METHOD IN LISTS METHODS)
string(REPLACE ":" ";" METHOD "${METHOD}")
list(GET METHOD 0 FUNCNAME)
list(GET METHOD 1 TYPES)
list(GET METHOD 2 SUSPENDABLE)
if(SUSPENDABLE)
set(SUSPENDABLE ", true")
endif()
string(LENGTH "${FUNCNAME}" LEN)
math(EXPR LEN "${MLEN} - ${LEN}")
if("${TYPES}" STREQUAL "v")
string(APPEND SQUIRREL_EXPORT "\n\tSQ${API_CLS}.DefSQAdvancedMethod(engine, &${CLS}::${FUNCNAME}, \"${FUNCNAME}\"${SUSPENDABLE});")
if(LEN GREATER 8)
math(EXPR LEN "${LEN} - 8")
else()
set(LEN 0)
endif()
endif()
unset(SPACES)
foreach(i RANGE ${LEN})
string(APPEND SPACES " ")
endforeach()
if("${TYPES}" STREQUAL "v")
string(APPEND SQUIRREL_EXPORT "\n\tSQ${API_CLS}.DefSQAdvancedMethod(engine, &${CLS}::${FUNCNAME},${SPACES}\"${FUNCNAME}\");")
else()
string(APPEND SQUIRREL_EXPORT "\n\tSQ${API_CLS}.DefSQMethod(engine, &${CLS}::${FUNCNAME}, \"${FUNCNAME}\", \"${TYPES}\"${SUSPENDABLE});")
string(APPEND SQUIRREL_EXPORT "\n\tSQ${API_CLS}.DefSQMethod(engine, &${CLS}::${FUNCNAME},${SPACES}\"${FUNCNAME}\",${SPACES}\"${TYPES}\");")
endif()
endforeach()
if(MLEN)
string(APPEND SQUIRREL_EXPORT "\n")
endif()
string(APPEND SQUIRREL_EXPORT "\n")
string(APPEND SQUIRREL_EXPORT "\n\tSQ${API_CLS}.PostRegister(engine);")
string(APPEND SQUIRREL_EXPORT "\n}")
@@ -468,16 +542,11 @@ foreach(LINE IN LISTS SOURCE_LINES)
# Add enums
if(IN_ENUM)
string(REGEX MATCH "([^,\t ]+)" ENUM_VALUE "${LINE}")
list(APPEND ENUM_VALUES "${ENUM_VALUE}")
# Check if this a special error enum
list(GET ENUMS -1 ENUM)
string(REGEX MATCH "([^,\t ]+)" ENUM_VALUE "${LINE}")
if(IN_SCOPED_ENUM)
list(APPEND ENUM_VALUES "${ENUM}::${ENUM_VALUE}")
else()
list(APPEND ENUM_VALUES "${ENUM_VALUE}")
endif()
if("${ENUM}" MATCHES ".*::ErrorMessages")
# syntax:
# enum ErrorMessages {
@@ -603,11 +672,10 @@ foreach(LINE IN LISTS SOURCE_LINES)
set(CLS_TYPES "${TYPES}")
elseif("${FUNCNAME}" MATCHES "^_" AND NOT "${TYPES}" STREQUAL "v")
elseif(IS_STATIC)
list(APPEND STATIC_METHODS "${FUNCNAME}:${TYPES}:${SUSPENDABLE}")
list(APPEND STATIC_METHODS "${FUNCNAME}:${TYPES}")
else()
list(APPEND METHODS "${FUNCNAME}:${TYPES}:${SUSPENDABLE}")
list(APPEND METHODS "${FUNCNAME}:${TYPES}")
endif()
unset(SUSPENDABLE)
continue()
endif()
endforeach()
+66 -66
View File
@@ -38,7 +38,7 @@ Last updated: 2024-03-26
packets are dropped in favour of a new packet.
This though will be reflected in the protocol version as announced in the
`PacketAdminType::ServerProtocol` in section 2.0).
`ADMIN_PACKET_SERVER_PROTOCOL` in section 2.0).
A reference implementation in Java for a client connecting to the admin interface
can be found at: [http://dev.openttdcoop.org/projects/joan](http://dev.openttdcoop.org/projects/joan)
@@ -49,18 +49,18 @@ Last updated: 2024-03-26
Create a TCP connection to the server on port 3977. The application is
expected to authenticate within 10 seconds.
To authenticate send either an `PacketAdminType::AdminJoin` or an
`PacketAdminType::AdminJoinSecure` packet.
To authenticate send either an `ADMIN_PACKET_ADMIN_JOIN` or an
`ADMIN_PACKET_ADMIN_JOIN_SECURE` packet.
The `PacketAdminType::AdminJoin` packet sends the password without any
The `ADMIN_PACKET_ADMIN_JOIN` packet sends the password without any
encryption or safeguards over the connection, and as such has been disabled
by default.
The `PacketAdminType::AdminJoinSecure` packet initiates a key exchange
The `ADMIN_PACKET_ADMIN_JOIN_SECURE` packet initiates a key exchange
authentication schema which tells te server which methods the client
supports and the server makes a choice. The server will then send an
`PacketAdminType::ServerAuthenticationRequest` packet to which the client has to respond
with an `PacketAdminType::AdminAuthenticationResponse` packet.
`ADMIN_PACKET_SERVER_AUTH_REQUEST` packet to which the client has to respond
with an `ADMIN_PACKET_ADMIN_AUTH_RESPONSE` packet.
The current choices for secure authentication are authorized keys, where
the client has a private key and the server a list of authorized public
@@ -69,41 +69,41 @@ Last updated: 2024-03-26
The server falls back to password authentication when the client's key is
not in the list of authorized keys.
When authentication has succeeded for either of the `AdminJoin` schemas, the
server will reply with `PacketAdminType::ServerProtocol` followed directly
by `PacketAdminType::ServerWelcome`.
When authentication has succeeded for either of the `JOIN` schemas, the
server will reply with `ADMIN_PACKET_SERVER_PROTOCOL` followed directly
by `ADMIN_PACKET_SERVER_WELCOME`.
`PacketAdminType::ServerProtocol` contains details about the protocol version.
`ADMIN_PACKET_SERVER_PROTOCOL` contains details about the protocol version.
It is the job of your application to check this number and decide whether
it will remain connected or not.
Furthermore, this packet holds details on every `AdminUpdateType` and the
supported `AdminFrequencyTypes` (bitwise representation).
`PacketAdminType::ServerWelcome` contains details on the server and the map,
`ADMIN_PACKET_SERVER_WELCOME` contains details on the server and the map,
e.g. if the server is dedicated, its NetworkLanguage, size of the Map, etc.
Once you have received `PacketAdminType::ServerWelcome` you are connected and
Once you have received `ADMIN_PACKET_SERVER_WELCOME` you are connected and
authorized to do your thing.
The server will not provide any game related updates unless you ask for them.
There are four packets the server will none the less send, if applicable:
- PacketAdminType::ServerError
- PacketAdminType::ServerWelcome
- PacketAdminType::ServerNewGame
- PacketAdminType::ServerShutdown
- ADMIN_PACKET_SERVER_ERROR
- ADMIN_PACKET_SERVER_WELCOME
- ADMIN_PACKET_SERVER_NEWGAME
- ADMIN_PACKET_SERVER_SHUTDOWN
However, `PacketAdminType::ServerWelcome` only after a `PacketAdminType::ServerNewGame`
However, `ADMIN_PACKET_SERVER_WELCOME` only after a `ADMIN_PACKET_SERVER_NEWGAME`
## 3.0) Asking for updates
Asking for updates is done with `PacketAdminType::AdminUpdateFrequency`.
Asking for updates is done with `ADMIN_PACKET_ADMIN_UPDATE_FREQUENCY`.
With this packet you define which update you wish to receive at which
frequency.
Note: not every update type supports every frequency. If in doubt, you can
verify against the data received in `PacketAdminType::ServerProtocol`.
verify against the data received in `ADMIN_PACKET_SERVER_PROTOCOL`.
Please note the potential gotcha in the "Certain packet information" section below
when using the `ADMIN_UPDATE_FREQUENCY` packet.
@@ -114,61 +114,61 @@ Last updated: 2024-03-26
Additional debug information can be found with a debug level of `net=3`.
`AdminUpdateType::Date` results in the server sending:
`ADMIN_UPDATE_DATE` results in the server sending:
- PacketAdminType::ServerDate
- ADMIN_PACKET_SERVER_DATE
`AdminUpdateType::ClientInfo` results in the server sending:
`ADMIN_UPDATE_CLIENT_INFO` results in the server sending:
- PacketAdminType::ServerClientJoin
- PacketAdminType::ServerClientInfo
- PacketAdminType::ServerClientUpdate
- PacketAdminType::ServerClientQuit
- PacketAdminType::ServerClientError
- ADMIN_PACKET_SERVER_CLIENT_JOIN
- ADMIN_PACKET_SERVER_CLIENT_INFO
- ADMIN_PACKET_SERVER_CLIENT_UPDATE
- ADMIN_PACKET_SERVER_CLIENT_QUIT
- ADMIN_PACKET_SERVER_CLIENT_ERROR
`AdminUpdateType::CompanyInfo` results in the server sending:
`ADMIN_UPDATE_COMPANY_INFO` results in the server sending:
- PacketAdminType::ServerCompanyNew
- PacketAdminType::ServerCompanyInfo
- PacketAdminType::ServerCompanyUpdate
- PacketAdminType::ServerCompanyRemove
- ADMIN_PACKET_SERVER_COMPANY_NEW
- ADMIN_PACKET_SERVER_COMPANY_INFO
- ADMIN_PACKET_SERVER_COMPANY_UPDATE
- ADMIN_PACKET_SERVER_COMPANY_REMOVE
`AdminUpdateType::CompanyEconomy` results in the server sending:
`ADMIN_UPDATE_COMPANY_ECONOMY` results in the server sending:
- PacketAdminType::ServerCompanyEconomy
- ADMIN_PACKET_SERVER_COMPANY_ECONOMY
`AdminUpdateType::CompanyStats` results in the server sending:
`ADMIN_UPDATE_COMPANY_STATS` results in the server sending:
- PacketAdminType::ServerCompanyStatistics
- ADMIN_PACKET_SERVER_COMPANY_STATS
`AdminUpdateType::Chat` results in the server sending:
`ADMIN_UPDATE_CHAT` results in the server sending:
- PacketAdminType::ServerChat
- ADMIN_PACKET_SERVER_CHAT
`AdminUpdateType::Console` results in the server sending:
`ADMIN_UPDATE_CONSOLE` results in the server sending:
- PacketAdminType::ServerConsole
- ADMIN_PACKET_SERVER_CONSOLE
`AdminUpdateType::CmdLogging` results in the server sending:
`ADMIN_UPDATE_CMD_LOGGING` results in the server sending:
- PacketAdminType::ServerCommandLogging
- ADMIN_PACKET_SERVER_CMD_LOGGING
## 3.1) Polling manually
Certain `AdminUpdateTypes` can also be polled:
- AdminUpdateType::Date
- AdminUpdateType::ClientInfo
- AdminUpdateType::CompanyInfo
- AdminUpdateType::CompanyEconomy
- AdminUpdateType::CompanyStats
- AdminUpdateType::CmdNames
- ADMIN_UPDATE_DATE
- ADMIN_UPDATE_CLIENT_INFO
- ADMIN_UPDATE_COMPANY_INFO
- ADMIN_UPDATE_COMPANY_ECONOMY
- ADMIN_UPDATE_COMPANY_STATS
- ADMIN_UPDATE_CMD_NAMES
Please note the potential gotcha in the "Certain packet information" section below
when using the `ADMIN_POLL` packet.
`AdminUpdateType::ClientInfo` and `AdminUpdateType::CompanyInfo` accept an additional
`ADMIN_UPDATE_CLIENT_INFO` and `ADMIN_UPDATE_COMPANY_INFO` accept an additional
parameter. This parameter is used to specify a certain client or company.
Setting this parameter to `UINT32_MAX (0xFFFFFFFF)` will tell the server you
want to receive updates for all clients or companies.
@@ -181,16 +181,16 @@ Last updated: 2024-03-26
## 4.0) Sending rcon commands
Rcon runs separate from the `AdminUpdateType::Console` `AdminUpdateType`. Requesting
Rcon runs separate from the `ADMIN_UPDATE_CONSOLE` `AdminUpdateType`. Requesting
the execution of a remote console command is done with the packet
`PacketAdminType::AdminRemoteConsoleCommand`.
`ADMIN_PACKET_ADMIN_RCON`.
Note: No additional authentication is required for rcon commands.
The server will reply with one or more `PacketAdminType::ServerRemoteConsoleCommand` packets.
Finally an `PacketAdminType::AdminRemoteConsoleCommandEnd` packet will be sent. Applications
The server will reply with one or more `ADMIN_PACKET_SERVER_RCON` packets.
Finally an `ADMIN_PACKET_ADMIN_RCON_END` packet will be sent. Applications
will not receive the answer twice if they have asked for the `AdminUpdateType`
`AdminUpdateType::Console`, as the result is not printed on the servers console
`ADMIN_UPDATE_CONSOLE`, as the result is not printed on the servers console
(just like clients rcon commands).
Furthermore, sending a `say` command (or any similar command) will not
@@ -199,7 +199,7 @@ Last updated: 2024-03-26
was not sent from the admin network.
Note that when content is queried or updated via rcon, the processing
happens asynchronously. But the `PacketAdminType::AdminRemoteConsoleCommandEnd` packet is sent
happens asynchronously. But the `ADMIN_PACKET_ADMIN_RCON_END` packet is sent
already right after the content is requested as there's no immediate output.
Thus other packages and the output of content rcon command may be sent at
an arbitrary later time, mixing into the output of other console activity,
@@ -208,7 +208,7 @@ Last updated: 2024-03-26
## 5.0) Sending chat
Sending a `PacketAdminType::AdminChat` results in chat originating from the server.
Sending a `ADMIN_PACKET_ADMIN_CHAT` results in chat originating from the server.
Currently four types of chat are supported:
@@ -223,7 +223,7 @@ Last updated: 2024-03-26
## 5.1) Receiving chat
Register `AdminUpdateType::Chat` at `AdminUpdateFrequency::Automatic` to receive chat.
Register `ADMIN_UPDATE_CHAT` at `AdminUpdateFrequency::Automatic` to receive chat.
The application will be able to receive all chat the server can see.
The configuration option `network.server_admin_chat` specifies whether
@@ -233,12 +233,12 @@ Last updated: 2024-03-26
## 6.0) Disconnecting
It is a kind thing to say good bye before leaving. Do this by sending the
`PacketAdminType::AdminQuit` packet.
`ADMIN_PACKET_ADMIN_QUIT` packet.
## 7.0) Certain packet information
`PacketAdminType::AdminUpdateFrequency` and `PacketAdminType::AdminPoll`
`ADMIN_PACKET_ADMIN_UPDATE_FREQUENCY` and `ADMIN_PACKET_ADMIN_POLL`
Potential gotcha: the AdminUpdateType integer type used is a
uint16 for `UPDATE_FREQUENCY`, and a uint8 for `POLL`.
@@ -246,21 +246,21 @@ Last updated: 2024-03-26
It is safe to cast between the two when sending
(i.e cast from a uint8 to a uint16).
All `PacketAdminType::Server*` packets have an enum value greater 100.
All `ADMIN_PACKET_SERVER_*` packets have an enum value greater 100.
`PacketAdminType::ServerWelcome`
`ADMIN_PACKET_SERVER_WELCOME`
Either directly follows `PacketAdminType::ServerProtocol` or is sent
Either directly follows `ADMIN_PACKET_SERVER_PROTOCOL` or is sent
after a new game has been started or a map loaded, i.e. also
after PacketAdminType::ServerNewGame.
after ADMIN_PACKET_SERVER_NEWGAME.
`PacketAdminType::ServerClientJoin` and `PacketAdminType::ServerCompanyNew`
`ADMIN_PACKET_SERVER_CLIENT_JOIN` and `ADMIN_PACKET_SERVER_COMPANY_NEW`
These packets directly follow their respective INFO packets. If you receive
a CLIENT_JOIN / COMPANY_NEW packet without having received the INFO packet
it may be a good idea to POLL for the specific ID.
`PacketAdminType::ServerCommandNames` and `PacketAdminType::ServerCommandLogging`
`ADMIN_PACKET_SERVER_CMD_NAMES` and `ADMIN_PACKET_SERVER_CMD_LOGGING`
Data provided with these packets is not stable and will not be
treated as such. Do not rely on IDs or names to be constant
-11
View File
@@ -38,17 +38,6 @@ your operating system:
It includes the OpenTTD files (grf+lng) and it will work as long as they
are not touched
7. The Atari Transport Tycoon Deluxe directory, if installed (path may vary)
This refers to the `CD` folder within the Transport Tycoon Deluxe
installation folder (2026 Atari re-release). OpenTTD detects the presence
of this folder based upon the contents of the `installpath.ini` file located
in:
- Windows: `%APPDATA%\Atari\Transport Tycoon Deluxe`
- macOS: `~/Library/Application Support/Atari/Transport Tycoon Deluxe`
- Linux: `$XDG_DATA_HOME/Atari/Transport Tycoon Deluxe`
This is used only for the loading of base sets (graphics, sound, music).
Different types of data or extensions go into different subdirectories of the
chosen main OpenTTD directory:
+94 -98
View File
@@ -16,7 +16,7 @@
<a href="landscape_grid.html">Landscape grid</a> page.
</p>
<p>Nine attributes (counting &quot;<span style="font-weight: bold;">type</span>&quot; and
&quot;<span style="font-weight: bold;">height</span>&quot;) hold the information about a tile.<br>
&quot;<span style="font-weight: bold;">height</span>&quot;) hold the information about a tile.<BR>
These attributes are referred to as
&quot;<span style="font-weight: bold;">type</span>",
&quot;<span style="font-weight: bold;">height</span>",
@@ -50,7 +50,6 @@
<tr><td><tt>0A</tt></td><td>Objects</td></tr>
</table>
</li>
<li>
Bits 3..2:
<table border="1" style="width: 30em;">
<tr bgcolor="#CCCCCC"><td colspan="2">Presence and direction of bridge above.</td></tr>
@@ -58,7 +57,6 @@
<tr><td><tt>01</tt></td><td>Axis X (North-East)</td></tr>
<tr><td><tt>02</tt></td><td>Axis Y (South-West)</td></tr>
</table>
</li>
<li>
<a name="tropic_zone"></a>
Bits 1..0:
@@ -71,14 +69,13 @@
In any other climate these 2 bits are theoretically free of use, however using them does not seem useful.
</li>
</ul>
</li>
<li><span style="font-weight: bold;">m1</span>
<ul>
<li>
<a name="WaterClass"></a>
Bits 6..5:
<table border="1" style="width: 30em;">
<tr bgcolor="#CCCCCC"><td colspan="2">The type of water that is on a tile.</td></tr>
<tr bgcolor="#CCCCCC"><td colspan="2">The type of water that is on a tile.
<tr><td style="width: 5em;"><tt>00</tt></td><td align=left>Sea</td></tr>
<tr><td><tt>01</tt></td><td align=left>Canal</td></tr>
<tr><td><tt>02</tt></td><td align=left>River</td></tr>
@@ -90,7 +87,7 @@
<a name="OwnershipInfo"></a>
Bits 4..0:
<table border="1" style="width: 30em;">
<tr bgcolor="#CCCCCC"><td colspan="2">The owner of a tile can be either companies (human or AI) or "Game entities".</td></tr>
<tr bgcolor="#CCCCCC"><td colspan="2">The owner of a tile can be either companies (human or AI) or "Game entities".
<tr><td style="width: 5em;"><tt>00..0E</tt></td><td align=left>Normal companies</td></tr>
<tr><td><tt>0F</tt></td><td align=left>a town owns the tile</td></tr>
<tr><td><tt>10</tt></td><td align=left>nobody owns the tile</td></tr>
@@ -155,8 +152,8 @@
<li>m4 bits 7..5: type of hedge on the SW border of the tile (1 through 6, or 0=none)</li>
<li>m4 bits 4..2: same as 7..5, but for the SE border</li>
<li>m5 bits 7..5: update counter, incremented on every periodic processing for tile types,
other than <tt>03</tt>, <tt>07</tt>, <tt>0B</tt>, <tt>10</tt> and above.<br>
on wraparound, the tile is updated (for fields, the type of fields in m3 is increased, for other types the tile type in m5 is increased).<br>
other than <tt>03</tt>, <tt>07</tt>, <tt>0B</tt>, <tt>10</tt> and above.<BR>
on wraparound, the tile is updated (for fields, the type of fields in m3 is increased, for other types the tile type in m5 is increased).<BR>
For snow and desert, these bits are not used, tile is updated on every periodic processing.</li>
<li>m5 bits 4..2: tile type:
<table>
@@ -393,7 +390,7 @@
</tr>
</table>
</li>
<li>m5 bit 6 set = with signals:<br>
<li>m5 bit 6 set = with signals:<BR>
There are at most 4 signals on a tile. The signals 0..3 belong to the directions:
<table>
<tr>
@@ -565,7 +562,7 @@
<td>
<ul>
<li>m2: Index into the array of towns (owning town for town roads; closest town otherwise, INVALID_TOWN if there is no town or we are creating a town)</li>
<li>m3 bits 7..4: <a href="#OwnershipInfo">owner</a> of road type 1 (tram); OWNER_NONE (<tt>10</tt>) is stored as OWNER_TOWN (<tt>0F</tt>)</li>
<li>m3 bits 7..4: <a href="#OwnershipInfo">owner</a> of road type 1 (tram); OWNER_NONE (<tt>10</tt>) is stored as OWNER_TOWN (<tt>0F</tt>)
<li>m4 bits 5..0: <a href="#RoadType">Roadtype</a></li>
<li>m7 bit 5 set = on snow or desert</li>
<li>m8 bits 11..6: <a href="#TramType">Tramtype</a></li>
@@ -724,7 +721,6 @@
</ul>
</li>
</ul>
</li>
<li>m3 bit 6 : free</li>
<li>m3 bit 5 : The house is protected from the town upgrading it</li>
<li>m3 bits 4..0 : triggers activated <a href="#newhouses">(newhouses)</a></li>
@@ -1054,96 +1050,97 @@
<td>
<ul>
<li>m1 bit 7: Ship docking tile status</li>
<li>m1 bits 6..5 : Water class (sea, canal or river)</li>
<li>m1 bits 6..5 : Water class (sea, canal or river)
<li>m1 bits 4..0: <a href="#OwnershipInfo">owner</a> (for sea, rivers, and coasts normally <tt>11</tt>)</li>
<li>m2: Depot index (for depots only)</li>
<li>m3 bit 0: Non-flooding state</li>
<li>m4: Random data for canal or river tiles</li>
<li>m5 bits 7..4: Water tile type:
<table>
<tr>
<td><tt>0</tt>&nbsp; </td>
<td>water, canal or river</td>
</tr>
<tr>
<td><tt>1</tt>&nbsp; </td>
<td>coast or riverbank</td>
</tr>
<tr>
<td nowrap valign=top><tt>2</tt>&nbsp; </td>
<td>canal lock<br>
<ul>
<li>m5 bits 3..2: Lock part
<table>
<tr>
<td><tt>0</tt>&nbsp; </td>
<td>Middle part</td>
</tr>
<tr>
<td><tt>1</tt>&nbsp; </td>
<td>Lower part</td>
</tr>
<tr>
<td><tt>2</tt>&nbsp; </td>
<td>Upper part</td>
</tr>
</table>
</li>
<li>m5 bits 1..0: Lock direction
<table>
<tr>
<td><tt>0</tt>&nbsp; </td>
<td>NE raised</td>
</tr>
<tr>
<td><tt>1</tt>&nbsp; </td>
<td>SE raised</td>
</tr>
<tr>
<td><tt>2</tt>&nbsp; </td>
<td>SW raised</td>
</tr>
<tr>
<td><tt>3</tt>&nbsp; </td>
<td>NW raised</td>
</tr>
</table>
</li>
</ul>
</td>
</tr>
<tr>
<td nowrap valign=top><tt>3</tt>&nbsp; </td>
<td>depot<br>
<ul>
<li>m5 bit 1: Depot axis
<table>
<tr>
<td><tt>0</tt>&nbsp; </td>
<td>X direction (NE-SW)</td>
</tr>
<tr>
<td><tt>1</tt>&nbsp; </td>
<td>Y direction (NW-SE)</td>
</tr>
</table>
</li>
<li>m5 bit 0: Depot part
<table>
<tr>
<td><tt>0</tt>&nbsp; </td>
<td>North part</td>
</tr>
<tr>
<td><tt>1</tt>&nbsp; </td>
<td>South part</td>
</tr>
</table>
</li>
</ul>
</td>
</tr>
</table>
<tr>
<td><tt>0</tt>&nbsp; </td>
<td>water, canal or river</td>
</tr>
<tr>
<td><tt>1</tt>&nbsp; </td>
<td>coast or riverbank</td>
</tr>
<tr>
<td nowrap valign=top><tt>2</tt>&nbsp; </td>
<td>canal lock<br>
<ul>
<li>m5 bits 3..2: Lock part
<table>
<tr>
<td><tt>0</tt>&nbsp; </td>
<td>Middle part</td>
</tr>
<tr>
<td><tt>1</tt>&nbsp; </td>
<td>Lower part</td>
</tr>
<tr>
<td><tt>2</tt>&nbsp; </td>
<td>Upper part</td>
</tr>
</table>
</li>
<li>m5 bits 1..0: Lock direction
<table>
<tr>
<td><tt>0</tt>&nbsp; </td>
<td>NE raised</td>
</tr>
<tr>
<td><tt>1</tt>&nbsp; </td>
<td>SE raised</td>
</tr>
<tr>
<td><tt>2</tt>&nbsp; </td>
<td>SW raised</td>
</tr>
<tr>
<td><tt>3</tt>&nbsp; </td>
<td>NW raised</td>
</tr>
</table>
</li>
</ul>
</td>
</tr>
<tr>
<td nowrap valign=top><tt>3</tt>&nbsp; </td>
<td>depot<br>
<ul>
<li>m5 bit 1: Depot axis
<table>
<tr>
<td><tt>0</tt>&nbsp; </td>
<td>X direction (NE-SW)</td>
</tr>
<tr>
<td><tt>1</tt>&nbsp; </td>
<td>Y direction (NW-SE)</td>
</tr>
</table>
</li>
<li>m5 bit 0: Depot part
<table>
<tr>
<td><tt>0</tt>&nbsp; </td>
<td>North part</td>
</tr>
<tr>
<td><tt>1</tt>&nbsp; </td>
<td>South part</td>
</tr>
</table>
</li>
</ul>
</td>
</tr>
</table>
</li>
</li>
</ul>
</td>
@@ -1170,7 +1167,7 @@
<ul>
<li>m1 bit 7: clear = under construction
<ul>
<li>m1 bits 6..5 : Water class (sea, canal, river or land)</li>
<li>m1 bits 6..5 : Water class (sea, canal, river or land)
<li>m1 bits 3..2: construction counter, for buildings under construction incremented on every periodic tile processing</li>
<li>m1 bits 1..0: stage of construction (<tt>3</tt> = completed), incremented when the construction counter wraps around<br>
the meaning is different for some animated tiles which are never under construction (types <tt>01</tt>, <tt>1E</tt>..<tt>20</tt>, <tt>30</tt>, <tt>58</tt>; see above)</li>
@@ -1326,8 +1323,7 @@
<td nowrap valign=top><tt>58</tt>&nbsp; </td>
<td align=left>wheel tower when animated; animation state in m7 bits 5..0; m7 bit 6 set = sound already generated</td>
</tr>
</table>
</td>
</table></td>
</tr>
<tr>
+3 -3
View File
@@ -92,7 +92,7 @@ the array so you can quickly see what is used and what is not.
<tr>
<td class="caption">farmland</td>
<td class="bits"><span class="pool" title="Industry index on pool">XXXX XXXX XXXX XXXX</span></td>
<td class="bits"><span class="used" title="Type of hedge on NE border">XXX</span> <span class="used" title="Snow presence">X</span><span class="used" title="Field production stage">XXXX</span></td>
<td class="bits"><span class="used" title="Type of hedge on NE border">XXX</span> <span class="used" title="Field production stage">XXXXX</span></td>
</tr>
<tr>
<td rowspan=3>1</td>
@@ -165,14 +165,14 @@ the array so you can quickly see what is used and what is not.
</tr>
<tr>
<td class="caption">house under construction</td>
<td class="bits"><span class="used" title="House is complete/in construction (see m5)">O</span><span class="free">O</span><span class="used" title="The house is protected from the town upgrading it.">X</span><span class="usable" title="Activated triggers (bits 2..4 don't have a meaning)">X XX</span><span class="used" title="Activated triggers (bits 2..4 don't have a meaning)">XX</span></td>
<td class="bits"><span class="used" title="House is complete/in construction (see m5)">O</span><span class="used" title="House type (m4 + m3[6])">X</span><span class="used" title="The house is protected from the town upgrading it.">X</span><span class="usable" title="Activated triggers (bits 2..4 don't have a meaning)">X XX</span><span class="used" title="Activated triggers (bits 2..4 don't have a meaning)">XX</span></td>
<td class="bits"><span class="free">OOO</span><span class="used" title="Construction stage">X X</span><span class="used" title="Construction counter">XXX</span></td>
</tr>
<tr>
<td>4</td>
<td class="caption">trees</td>
<td class="bits"><span class="free">O</span><span class="used" title="Water class">XX</span><span class="usable" title="Owner (always OWNER_NONE)">1 OOOO</span></td>
<td class="bits"><span class="free">OOOO OOO</span><span class="used" title="Tree ground">XXX</span> <span class="used" title="Tree density">XX</span> <span class="free">OOOO</span></td>
<td class="bits"><span class="free">OOOO OOO</span><span class="used" title="Tree ground">XXX</span> <span class="used" title="Tree density">XX</span> <span class="used" title="Tree counter">XXXX</span></td>
<td class="bits"><span class="usable" title="Tree type unused bits">XX</span><span class="used" title="Tree type">XX XXXX</span></td>
<td class="bits"><span class="free">OOOO OOOO</span></td>
<td class="bits"><span class="used" title="Number of trees on tile (+1)">XX</span><span class="free">OO O</span><span class="used" title="Tree growth">XXX</span></td>
+16 -16
View File
@@ -69,19 +69,19 @@ An example of a valid tag is `PLYR` when looking at it via ASCII, which contains
`[4..4]` - Next follows a byte where the lower 4 bits contain the type.
The possible valid types are:
- `0` - `ChunkType::Riff` - This chunk is a binary blob.
- `1` - `ChunkType::Array` - This chunk is a list of items.
- `2` - `ChunkType::SparseArray` - This chunk is a list of items.
- `3` - `ChunkType::Table` - This chunk is self-describing list of items.
- `4` - `ChunkType::SparseTable` - This chunk is self-describing list of items.
- `0` - `CH_RIFF` - This chunk is a binary blob.
- `1` - `CH_ARRAY` - This chunk is a list of items.
- `2` - `CH_SPARSE_ARRAY` - This chunk is a list of items.
- `3` - `CH_TABLE` - This chunk is self-describing list of items.
- `4` - `CH_SPARSE_TABLE` - This chunk is self-describing list of items.
Now per type the format is (slightly) different.
### ChunkType::Riff
### CH_RIFF
(since savegame version 295, this chunk type is only used for MAP-chunks, containing bit-information about each tile on the map)
A `ChunkType::Riff` starts with an `uint24` which together with the upper-bits of the type defines the length of the chunk.
A `CH_RIFF` starts with an `uint24` which together with the upper-bits of the type defines the length of the chunk.
In pseudo-code:
```
@@ -94,11 +94,11 @@ if type == 0
The next `length` bytes are part of the chunk.
What those bytes mean depends on the tag of the chunk; further details per chunk can be found in the source-code.
### ChunkType::Array / ChunkType::SparseArray
### CH_ARRAY / CH_SPARSE_ARRAY
(this chunk type is deprecated since savegame version 295 and is no longer in use)
`[0..G1]` - A `ChunkType::Array` / `ChunkType::SparseArray` starts with a `gamma`, indicating the size of the next item plus one.
`[0..G1]` - A `CH_ARRAY` / `CH_SPARSE_ARRAY` starts with a `gamma`, indicating the size of the next item plus one.
If this size value is zero, it indicates the end of the list.
This indicates the full length of the next item minus one.
In psuedo-code:
@@ -111,21 +111,21 @@ loop
read <size> bytes
```
`[]` - For `ChunkType::Array` there is an implicit index.
`[]` - For `CH_ARRAY` there is an implicit index.
The loop starts at zero, and every iteration adds one to the index.
For entries in the game that were not allocated, the `size` will be zero.
`[G1+1..G2]` - For `ChunkType::SparseArray` there is an explicit index.
`[G1+1..G2]` - For `CH_SPARSE_ARRAY` there is an explicit index.
The `gamma` following the size indicates the index.
The content of the item is a binary blob, and similar to `ChunkType::Riff`, it depends on the tag of the chunk what it means.
The content of the item is a binary blob, and similar to `CH_RIFF`, it depends on the tag of the chunk what it means.
Please check the source-code for further details.
### ChunkType::Table / ChunkType::SparseTable
### CH_TABLE / CH_SPARSE_TABLE
(this chunk type only exists since savegame version 295)
Both `ChunkType::Table` and `ChunkType::SparseTable` are very similar to `ChunkType::Array` / `ChunkType::SparseArray` respectively.
Both `CH_TABLE` and `CH_SPARSE_TABLE` are very similar to `CH_ARRAY` / `CH_SPARSE_ARRAY` respectively.
The only change is that the chunk starts with a header.
This header describes the chunk in details; with the header you know the meaning of each byte in the binary blob that follows.
@@ -168,7 +168,7 @@ struct | substruct3
The headers will be, in order: `table`, `substruct1`, `substruct3`, `substruct2`, each ending with a `type` is zero field.
After reading all the fields of all the headers, there is a list of records.
To read this, see `ChunkType::Array` / `ChunkType::SparseArray` for details.
To read this, see `CH_ARRAY` / `CH_SPARSE_ARRAY` for details.
As each `type` has a well defined length, you can read the records even without knowing anything about the chunk-tag yourself.
@@ -187,7 +187,7 @@ The prefix is strongly advised to avoid conflicts with future-settings in an unp
## Scripts custom data format
Script chunks (`AIPL` and `GSDT`) use `ChunkType::Table` chunk type.
Script chunks (`AIPL` and `GSDT`) use `CH_TABLE` chunk type.
At the end of each record there's an `uint8` to indicate if there's custom data (1) or not (0).
Binary file not shown.
+4 -1
View File
@@ -1,8 +1,11 @@
# If you change this version, change the numbers in .github/workflows/ci-emscripten.yml (2x)
# and .github/workflows/preview-build.yml (2x) too.
FROM emscripten/emsdk:6.0.1
FROM emscripten/emsdk:3.1.57
RUN apt-get update \
&& apt-get install -y gcc-12 g++-12 \
&& update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-12 100 \
&& update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-12 100 \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
@@ -63,6 +63,6 @@
<Resource Language="cy-gb" />
</Resources>
<Dependencies>
<TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.17701.0" MaxVersionTested="10.0.18363.0" />
<TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.17134.0" MaxVersionTested="10.0.18363.0" />
</Dependencies>
</Package>
@@ -7,7 +7,7 @@
<Logo>Assets\StoreLogo.png</Logo>
</Properties>
<Dependencies>
<TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.17701.0" MaxVersionTested="10.0.18363.0" />
<TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.17134.0" MaxVersionTested="10.0.18363.0" />
</Dependencies>
<Resources>
<Resource Language="en-gb" />
-2
View File
@@ -22,5 +22,3 @@
add_subdirectory(regression)
add_subdirectory(stationlist)
add_subdirectory(gs)
add_subdirectory(gs_compat)
-8
View File
@@ -1,8 +0,0 @@
include(CreateRegression)
create_regression("game"
${CMAKE_CURRENT_SOURCE_DIR}/info.nut
${CMAKE_CURRENT_SOURCE_DIR}/main.nut
${CMAKE_CURRENT_SOURCE_DIR}/require.nut
${CMAKE_CURRENT_SOURCE_DIR}/result.txt
${CMAKE_CURRENT_SOURCE_DIR}/test.sav
)
-14
View File
@@ -1,14 +0,0 @@
class Regression extends GSInfo {
function GetAuthor() { return "OpenTTD GS Developers Team"; }
function GetName() { return "Regression"; }
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 "16"; }
function GetDate() { return "2026-05-19"; }
function CreateInstance() { return "Regression"; }
function UseAsRandomAI() { return false; }
}
RegisterGS(Regression());
-83
View File
@@ -1,83 +0,0 @@
class Regression extends GSController {
function Start();
};
function Regression::TestInit()
{
print("");
print("--TestInit--");
print(" Ops: " + this.GetOpsTillSuspend());
print(" TickTest: " + this.GetTick());
this.Sleep(1);
print(" TickTest: " + this.GetTick());
print(" Ops: " + this.GetOpsTillSuspend());
print(" SetCommandDelay: " + GSController.SetCommandDelay(1));
print(" IsValid(vehicle.plane_speed): " + GSGameSettings.IsValid("vehicle.plane_speed"));
print(" vehicle.plane_speed: " + GSGameSettings.GetValue("vehicle.plane_speed"));
require("require.nut");
print(" TestEnum.value1: " + ::TestEnum.value1);
print(" test_constant: " + ::test_constant);
print(" TestEnum.value2: " + TestEnum.value2);
print(" test_constant: " + test_constant);
print(" min(6, 3): " + min(6, 3));
print(" min(3, 6): " + min(3, 6));
print(" max(6, 3): " + max(6, 3));
print(" max(3, 6): " + max(3, 6));
}
function Regression::Company()
{
print("");
print("--Company--");
/* Test GSXXXMode() in scopes */
{
local test = GSTestMode();
print(" SetName(): " + GSCompany.SetName("Regression"));
print(" SetName(): " + GSCompany.SetName("Regression"));
{
local exec = GSExecMode();
print(" SetName(): " + GSCompany.SetName("Regression"));
print(" SetName(): " + GSCompany.SetName("Regression"));
print(" GetLastErrorString(): " + GSError.GetLastErrorString());
}
}
print(" GetName(): " + GSCompany.GetName(GSCompany.COMPANY_SELF));
print(" GetPresidentName(): " + GSCompany.GetPresidentName(GSCompany.COMPANY_SELF));
print(" SetPresidentName(): " + GSCompany.SetPresidentName("Regression GS"));
print(" GetPresidentName(): " + GSCompany.GetPresidentName(GSCompany.COMPANY_SELF));
print("");
print("--CompanyMode--");
local company = GSCompanyMode(GSCompany.COMPANY_FIRST);
{
local test = GSTestMode();
print(" SetName(): " + GSCompany.SetName("Regression"));
print(" SetName(): " + GSCompany.SetName("Regression"));
{
local exec = GSExecMode();
print(" SetName(): " + GSCompany.SetName("Regression"));
print(" SetName(): " + GSCompany.SetName("Regression"));
print(" GetLastErrorString(): " + GSError.GetLastErrorString());
}
}
print(" GetName(): " + GSCompany.GetName(GSCompany.COMPANY_SELF));
print(" GetPresidentName(): " + GSCompany.GetPresidentName(GSCompany.COMPANY_SELF));
print(" SetPresidentName(): " + GSCompany.SetPresidentName("Regression GS"));
print(" GetPresidentName(): " + GSCompany.GetPresidentName(GSCompany.COMPANY_SELF));
}
function Regression::Start()
{
this.TestInit();
this.Company();
print("");
print("Done...");
print("");
}
-9
View File
@@ -1,9 +0,0 @@
print(" Required this file");
const test_constant = 1;
enum TestEnum {
value0,
value1,
value2
};
-44
View File
@@ -1,44 +0,0 @@
--TestInit--
Ops: 9988
TickTest: 1
TickTest: 2
Ops: 9990
SetCommandDelay: (null : 0x00000000)
IsValid(vehicle.plane_speed): true
vehicle.plane_speed: 4
Required this file
TestEnum.value1: 1
test_constant: 1
TestEnum.value2: 2
test_constant: 1
min(6, 3): 3
min(3, 6): 3
max(6, 3): 6
max(3, 6): 6
--Company--
SetName(): false
SetName(): false
SetName(): false
SetName(): false
GetLastErrorString(): ERR_PRECONDITION_INVALID_COMPANY
GetName(): (null : 0x00000000)
GetPresidentName(): (null : 0x00000000)
SetPresidentName(): false
GetPresidentName(): (null : 0x00000000)
--CompanyMode--
SetName(): true
SetName(): true
SetName(): true
SetName(): false
GetLastErrorString(): ERR_NAME_IS_NOT_UNIQUE
GetName(): Regression
GetPresidentName(): S. G. Bloggs
SetPresidentName(): true
GetPresidentName(): Regression GS
Done...
ERROR: The script died unexpectedly.
Binary file not shown.
-7
View File
@@ -1,7 +0,0 @@
include(CreateRegression)
create_regression("game"
${CMAKE_CURRENT_SOURCE_DIR}/info.nut
${CMAKE_CURRENT_SOURCE_DIR}/main.nut
${CMAKE_CURRENT_SOURCE_DIR}/result.txt
${CMAKE_CURRENT_SOURCE_DIR}/test.sav
)
-13
View File
@@ -1,13 +0,0 @@
class CompatRegression extends GSInfo {
function GetAuthor() { return "OpenTTD GS Developers Team"; }
function GetName() { return "Compat Regression"; }
function GetShortName() { return "REGC"; }
function GetDescription() { return "This runs regression on the compat scripts."; }
function GetVersion() { return 1; }
function GetAPIVersion() { return "1.2"; }
function GetDate() { return "2026-05-19"; }
function CreateInstance() { return "CompatRegression"; }
function UseAsRandomAI() { return false; }
}
RegisterGS(CompatRegression());
-6
View File
@@ -1,6 +0,0 @@
class CompatRegression extends GSController {
function Start()
{
print("Done...");
}
};
-2
View File
@@ -1,2 +0,0 @@
Done...
ERROR: The script died unexpectedly.
Binary file not shown.
+1 -1
View File
@@ -1,5 +1,5 @@
include(CreateRegression)
create_regression("ai"
create_regression(
${CMAKE_CURRENT_SOURCE_DIR}/info.nut
${CMAKE_CURRENT_SOURCE_DIR}/main.nut
${CMAKE_CURRENT_SOURCE_DIR}/require.nut
+1 -1
View File
@@ -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 "16"; }
function GetAPIVersion() { return "15"; }
function GetDate() { return "2007-03-18"; }
function CreateInstance() { return "Regression"; }
function UseAsRandomAI() { return false; }
+6 -296
View File
@@ -842,7 +842,6 @@ function Regression::List()
}
list[4000] = 50;
list[4006] = 12;
list[4012] = true;
print(" foreach():");
foreach (idx, val in list) {
@@ -850,7 +849,6 @@ function Regression::List()
}
print(" []:");
print(" 4000 => " + list[4000]);
print(" 4012 => " + list[4012]);
print(" clone:");
local list3 = clone list;
@@ -867,25 +865,25 @@ function Regression::List()
}
local it = list.Begin();
print(" " + it + " => " + list.GetValue(it) + " (" + list.IsEnd() + ")");
print(" " + it + " => " + list.GetValue(it) + " (" + !list.IsEnd() + ")");
list.Sort(list.SORT_BY_VALUE, list.SORT_ASCENDING);
it = list.Next();
print(" " + it + " => " + list.GetValue(it) + " (" + list.IsEnd() + ")");
print(" " + it + " => " + list.GetValue(it) + " (" + !list.IsEnd() + ")");
it = list.Begin();
print(" " + it + " => " + list.GetValue(it) + " (" + list.IsEnd() + ")");
print(" " + it + " => " + list.GetValue(it) + " (" + !list.IsEnd() + ")");
list.SetValue(it + 1, -5);
it = list.Next();
print(" " + it + " => " + list.GetValue(it) + " (" + list.IsEnd() + ")");
print(" " + it + " => " + list.GetValue(it) + " (" + !list.IsEnd() + ")");
list.RemoveValue(list.GetValue(it) + 1);
it = list.Next();
print(" " + it + " => " + list.GetValue(it) + " (" + list.IsEnd() + ")");
print(" " + it + " => " + list.GetValue(it) + " (" + !list.IsEnd() + ")");
list.RemoveAboveValue(list.GetValue(it));
it = list.Next();
print(" " + it + " => " + list.GetValue(it) + " (" + list.IsEnd() + ")");
print(" " + it + " => " + list.GetValue(it) + " (" + !list.IsEnd() + ")");
while (!list.IsEnd()) {
it = list.Next();
@@ -897,232 +895,6 @@ function Regression::List()
print(" " + idx + " => " + val);
}
list.RemoveItem(0);
local list4 = clone list;
foreach (sorter_type in [ AIList.SORT_BY_VALUE, AIList.SORT_BY_ITEM ]) {
foreach (sorter_direction in [ AIList.SORT_DESCENDING, AIList.SORT_ASCENDING ]) {
local type = sorter_type == AIList.SORT_BY_VALUE ? "Value" : "Item";
local direction = sorter_direction == AIList.SORT_DESCENDING ? "Descending" : "Ascending";
local sorter = " (" + type + " " + direction + ")";
list = clone list4;
list.Sort(sorter_type, sorter_direction);
print("");
print(" ListDump:" + sorter);
foreach (idx, val in list) {
print(" " + idx + " => " + val);
}
it = list.Begin();
print(" Begin(): " + it + " => " + list.GetValue(it) + " (" + list.IsEnd() + ")");
it = list.Next();
print(" Next(): " + it + " => " + list.GetValue(it) + " (" + list.IsEnd() + ")");
it = list.Next();
print(" Next(): " + it + " => " + list.GetValue(it) + " (" + list.IsEnd() + ")");
it = list.Next();
print(" Next(): " + it + " => " + list.GetValue(it) + " (" + list.IsEnd() + ")");
it = list.Next();
print(" Next(): " + it + " => " + list.GetValue(it) + " (" + list.IsEnd() + ")");
print(" foreach (idx, val in list) {print}:" + sorter);
foreach (idx, val in list) {
print(" " + idx + " => " + val + " (" + list.IsEnd() + ")");
}
print(" Post loop: (" + list.IsEnd() + ")");
it = list.Next();
print(" Post loop Next: " + it + " => " + list.GetValue(it) + " (" + list.IsEnd() + ")");
it = list.Next();
print(" Post loop Next: " + it + " => " + list.GetValue(it) + " (" + list.IsEnd() + ")");
print(" for (Begin / !IsEnd / Next) {print}:" + sorter);
for (it = list.Begin(); !list.IsEnd(); it = list.Next()) {
print(" " + it + " => " + list.GetValue(it) + " (" + list.IsEnd() + ")");
}
print(" Post loop: " + it + " => " + list.GetValue(it) + " (" + list.IsEnd() + ")");
it = list.Next();
print(" Post loop Next: " + it + " => " + list.GetValue(it) + " (" + list.IsEnd() + ")");
it = list.Next();
print(" Post loop Next: " + it + " => " + list.GetValue(it) + " (" + list.IsEnd() + ")");
print(" Begin / while (!IsEnd) {print / Next}:" + sorter);
it = list.Begin();
while (!list.IsEnd()) {
print(" " + it + " => " + list.GetValue(it) + " (" + list.IsEnd() + ")");
it = list.Next();
}
print(" Post loop: " + it + " => " + list.GetValue(it) + " (" + list.IsEnd() + ")");
it = list.Next();
print(" Post loop Next: " + it + " => " + list.GetValue(it) + " (" + list.IsEnd() + ")");
it = list.Next();
print(" Post loop Next: " + it + " => " + list.GetValue(it) + " (" + list.IsEnd() + ")");
print(" Begin / do {{print / Next} while (!IsEnd)}:" + sorter);
it = list.Begin();
do {
print(" " + it + " => " + list.GetValue(it) + " (" + list.IsEnd() + ")");
it = list.Next();
} while (!list.IsEnd());
print(" Post loop: " + it + " => " + list.GetValue(it) + " (" + list.IsEnd() + ")");
it = list.Next();
print(" Post loop Next: " + it + " => " + list.GetValue(it) + " (" + list.IsEnd() + ")");
it = list.Next();
print(" Post loop Next: " + it + " => " + list.GetValue(it) + " (" + list.IsEnd() + ")");
print(" GetValue / SetValue:" + sorter);
for (it = list.Begin(); !list.IsEnd(); it = list.Next()) {
local old_val = list.GetValue(it);
local old_isend = list.IsEnd();
local res = list.SetValue(it, old_val);
local val = list.GetValue(it);
local isend = list.IsEnd();
local new_val_to_set = old_val * 1111;
local new_res = list.SetValue(it, new_val_to_set);
local new_val = list.GetValue(it);
local new_isend = list.IsEnd();
print(" " + it + " => " + old_val + " (" + old_isend + ")");
print(" => SetValue(" + it + ", " + old_val + ") ? " + res + " => " + val + " (" + isend + ")");
print(" => SetValue(" + it + ", " + new_val_to_set + ") ? " + new_res + " => " + new_val + " (" + new_isend + ")");
}
print(" Post loop: " + it + " => " + list.GetValue(it) + " (" + list.IsEnd() + ")");
it = list.Next();
print(" Post loop Next: " + it + " => " + list.GetValue(it) + " (" + list.IsEnd() + ")");
it = list.Next();
print(" Post loop Next: " + it + " => " + list.GetValue(it) + " (" + list.IsEnd() + ")");
list.Clear();
list.AddList(list4);
print(" GetValue / AddItem:" + sorter);
for (it = list.Begin(); !list.IsEnd(); it = list.Next()) {
local old_val = list.GetValue(it);
local old_isend = list.IsEnd();
list.AddItem(it, old_val);
local val = list.GetValue(it);
local isend = list.IsEnd();
local new_val_to_set = old_val * 1111;
list.AddItem(it, new_val_to_set);
local new_val = list.GetValue(it);
local new_isend = list.IsEnd();
print(" " + it + " => " + old_val + " (" + old_isend + ")");
print(" => AddItem(" + it + ", " + old_val + ") => " + val + " (" + isend + ")");
print(" => AddItem(" + it + ", " + new_val_to_set + ") => " + new_val + " (" + new_isend + ")");
}
print(" Post loop: " + it + " => " + list.GetValue(it) + " (" + list.IsEnd() + ")");
it = list.Next();
print(" Post loop Next: " + it + " => " + list.GetValue(it) + " (" + list.IsEnd() + ")");
it = list.Next();
print(" Post loop Next: " + it + " => " + list.GetValue(it) + " (" + list.IsEnd() + ")");
list.Clear();
list.AddList(list4);
print(" RemoveItem / HasItem / AddItem:" + sorter);
for (it = list.Begin(); !list.IsEnd(); it = list.Next()) {
local val = list.GetValue(it);
print(" " + it + " => " + val + " / " + list.HasItem(it) + " (" + list.IsEnd() + ")");
list.RemoveItem(it);
print(" => RemoveItem(" + it + ") => " + list.GetValue(it) + " / " + list.HasItem(it) + " (" + list.IsEnd() + ")");
if (list.IsEnd()) {
list.AddItem(it, val);
print(" => AddItem(" + it + ", " + val + ") => " + list.GetValue(it) + " / " + list.HasItem(it) + " (" + list.IsEnd() + ")");
}
}
print(" Post loop: " + it + " => " + list.GetValue(it) + " (" + list.IsEnd() + ")");
it = list.Next();
print(" Post loop Next: " + it + " => " + list.GetValue(it) + " (" + list.IsEnd() + ")");
it = list.Next();
print(" Post loop Next: " + it + " => " + list.GetValue(it) + " (" + list.IsEnd() + ")");
}
}
list.Clear(),
list.AddList(list4);
local list1 = list;
local list2 = list3;
foreach (sorter_type_1 in [ AIList.SORT_BY_VALUE, AIList.SORT_BY_ITEM ]) {
foreach (sorter_direction_1 in [ AIList.SORT_DESCENDING, AIList.SORT_ASCENDING ]) {
local type_1 = sorter_type_1 == AIList.SORT_BY_VALUE ? "Value" : "Item";
local direction_1 = sorter_direction_1 == AIList.SORT_DESCENDING ? "Descending" : "Ascending";
local sorter_1 = " (" + type_1 + " " + direction_1 + ")";
foreach (sorter_type_2 in [ AIList.SORT_BY_VALUE, AIList.SORT_BY_ITEM ]) {
foreach (sorter_direction_2 in [ AIList.SORT_DESCENDING, AIList.SORT_ASCENDING ]) {
local type_2 = sorter_type_2 == AIList.SORT_BY_VALUE ? "Value" : "Item";
local direction_2 = sorter_direction_2 == AIList.SORT_DESCENDING ? "Descending" : "Ascending";
local sorter_2 = " (" + type_2 + " " + direction_2 + ")";
local sorter = sorter_1 + sorter_2;
list1.Clear(),
list1.AddList(list4);
list1.Sort(sorter_type_1, sorter_direction_1);
list2.Sort(sorter_type_2, sorter_direction_2);
print("");
print(" SwapList:" + sorter);
print(" DumpList 1:" + sorter_1);
foreach (idx, val in list1) {
print(" " + idx + " => " + val);
}
print(" DumpList 2:" + sorter_2);
foreach (idx, val in list2) {
print(" " + idx + " => " + val);
}
local it1 = list1.Begin();
print(" List 1 Begin: " + it1 + " => " + list1.GetValue(it1) + " (" + list1.IsEnd() + ")");
local it2 = list2.Begin();
print(" List 2 Begin: " + it2 + " => " + list2.GetValue(it2) + " (" + list2.IsEnd() + ")");
it2 = list2.Next();
print(" List 2 Next: " + it2 + " => " + list2.GetValue(it2) + " (" + list2.IsEnd() + ")");
print(" => Swap list 1 with list 2 " + list1.SwapList(list2));
it1 = list1.Next();
print(" List 1 Next: " + it1 + " => " + list1.GetValue(it1) + " (" + list1.IsEnd() + ")");
it2 = list2.Next();
print(" List 2 Next: " + it2 + " => " + list2.GetValue(it2) + " (" + list2.IsEnd() + ")");
it2 = list2.Next();
print(" List 2 Next: " + it2 + " => " + list2.GetValue(it2) + " (" + list2.IsEnd() + ")");
print(" => Swap list 1 with list 2 " + list1.SwapList(list2));
it1 = list1.Next();
print(" List 1 Next: " + it1 + " => " + list1.GetValue(it1) + " (" + list1.IsEnd() + ")");
it2 = list2.Next();
print(" List 2 Next: " + it2 + " >= " + list2.GetValue(it2) + " (" + list2.IsEnd() + ")");
it2 = list2.Next();
print(" List 2 Next: " + it2 + " >= " + list2.GetValue(it2) + " (" + list2.IsEnd() + ")");
}
}
}
}
list.Clear();
print(" IsEmpty(): " + list.IsEmpty());
list2 = AIList();
for (local i = -10; i < 10; i++) {
list2.AddItem(i, -i * i / 2);
}
list.SwapList(list2);
print(" Negative ListDump:");
for (local i = list.Begin(); !list.IsEnd(); i = list.Next()) {
print(" " + i + " => " + list.GetValue(i));
}
print(" KeepBelowValue(-12):");
list.KeepBelowValue(-12);
for (local i = list.Begin(); !list.IsEnd(); i = list.Next()) {
print(" " + i + " => " + list.GetValue(i));
}
print(" KeepAboveValue(-40):");
list.KeepAboveValue(-40);
for (local i = list.Begin(); !list.IsEnd(); i = list.Next()) {
print(" " + i + " => " + list.GetValue(i));
}
print(" KeepValue(-24):");
list.KeepValue(-24);
for (local i = list.Begin(); !list.IsEnd(); i = list.Next()) {
print(" " + i + " => " + list.GetValue(i));
}
}
function Regression::Map()
@@ -2300,67 +2072,6 @@ function Regression::PriorityQueue()
print(" Count(): " + queue.Count());
}
function Regression::Terraforming()
{
print("");
print("--Terraforming--");
print(" DemolishTile(): " + AITile.DemolishTile(4033));
print(" DemolishTile(): " + AITile.DemolishTile(60766));
print(" GetBankBalance(): " + AICompany.GetBankBalance(AICompany.COMPANY_SELF));
print(" LowerTile(): " + AITile.LowerTile(8205, AITile.SLOPE_ENW));
print(" RaiseTile(): " + AITile.RaiseTile(8205, AITile.SLOPE_ENW));
print(" GetBankBalance(): " + AICompany.GetBankBalance(AICompany.COMPANY_SELF));
print(" LowerTile(): " + AITile.LowerTile(8205, AITile.SLOPE_ENW));
print(" GetBankBalance(): " + AICompany.GetBankBalance(AICompany.COMPANY_SELF));
print(" LowerTile(): " + AITile.LowerTile(4113, AITile.SLOPE_SW));
print(" RaiseTile(): " + AITile.RaiseTile(5902, AITile.SLOPE_NE));
print(" GetBankBalance(): " + AICompany.GetBankBalance(AICompany.COMPANY_SELF));
print(" RaiseTile(): " + AITile.RaiseTile(7958, AITile.SLOPE_SE));
print(" LowerTile(): " + AITile.LowerTile(7958, AITile.SLOPE_NE));
print(" LowerTile(): " + AITile.LowerTile(7958, AITile.SLOPE_SE));
print(" RaiseTile(): " + AITile.RaiseTile(7958, AITile.SLOPE_SE));
print(" LowerTile(): " + AITile.LowerTile(7958, AITile.SLOPE_EW));
print(" GetBankBalance(): " + AICompany.GetBankBalance(AICompany.COMPANY_SELF));
print(" LowerTile(): " + AITile.LowerTile(33924, AITile.SLOPE_ELEVATED));
print(" LowerTile(): " + AITile.LowerTile(33923, AITile.SLOPE_ELEVATED));
print(" RaiseTile(): " + AITile.RaiseTile(34193, AITile.SLOPE_ELEVATED));
print(" LowerTile(): " + AITile.LowerTile(34193, AITile.SLOPE_ELEVATED));
print(" LowerTile(): " + AITile.LowerTile(34192, AITile.SLOPE_NE));
print(" RaiseTile(): " + AITile.RaiseTile(34193, AITile.SLOPE_ELEVATED));
print(" LowerTile(): " + AITile.LowerTile(34192, AITile.SLOPE_NE));
print(" GetBankBalance(): " + AICompany.GetBankBalance(AICompany.COMPANY_SELF));
print(" RaiseTile(): " + AITile.RaiseTile(5687, AITile.SLOPE_N));
print(" LowerTile(): " + AITile.LowerTile(5687, AITile.SLOPE_SE));
print(" GetBankBalance(): " + AICompany.GetBankBalance(AICompany.COMPANY_SELF));
print(" RaiseTile(): " + AITile.RaiseTile(23596, AITile.SLOPE_NE));
print(" LowerTile(): " + AITile.LowerTile(23596, AITile.SLOPE_NE));
print(" GetBankBalance(): " + AICompany.GetBankBalance(AICompany.COMPANY_SELF));
print(" RaiseTile(): " + AITile.RaiseTile(28536, AITile.SLOPE_E));
print(" RaiseTile(): " + AITile.RaiseTile(28536, AITile.SLOPE_E));
print(" GetBankBalance(): " + AICompany.GetBankBalance(AICompany.COMPANY_SELF));
print(" LevelTiles(): " + AITile.LevelTiles(28536, 29053));
print(" LevelTiles(): " + AITile.LevelTiles(28536, 29053));
print(" GetBankBalance(): " + AICompany.GetBankBalance(AICompany.COMPANY_SELF));
print(" LevelTiles(): " + AITile.LevelTiles(22348, 24405));
print(" LevelTiles(): " + AITile.LevelTiles(27717, 29772));
print(" GetBankBalance(): " + AICompany.GetBankBalance(AICompany.COMPANY_SELF));
print(" LowerTile(): " + AITile.LowerTile(61534, AITile.SLOPE_W));
print(" RaiseTile(): " + AITile.RaiseTile(61534, AITile.SLOPE_NE));
print(" RaiseTile(): " + AITile.RaiseTile(61534, AITile.SLOPE_N));
print(" LowerTile(): " + AITile.LowerTile(61534, AITile.SLOPE_W));
print(" GetBankBalance(): " + AICompany.GetBankBalance(AICompany.COMPANY_SELF));
print(" RaiseTile(): " + AITile.RaiseTile(61534, AITile.SLOPE_N));
print(" LowerTile(): " + AITile.LowerTile(61533, AITile.SLOPE_E));
print(" LowerTile(): " + AITile.LowerTile(61534, AITile.SLOPE_E));
print(" GetBankBalance(): " + AICompany.GetBankBalance(AICompany.COMPANY_SELF));
print(" RaiseTile(): " + AITile.RaiseTile(4033, AITile.SLOPE_S));
print(" LowerTile(): " + AITile.LowerTile(4033, AITile.SLOPE_S));
print(" LowerTile(): " + AITile.LowerTile(4033, AITile.SLOPE_E));
print(" RaiseTile(): " + AITile.RaiseTile(4033, AITile.SLOPE_E));
print(" RaiseTile(): " + AITile.RaiseTile(4033, AITile.SLOPE_NWS));
print(" GetBankBalance(): " + AICompany.GetBankBalance(AICompany.COMPANY_SELF));
}
function Regression::Start()
{
this.TestInit();
@@ -2459,7 +2170,6 @@ function Regression::Start()
this.Math();
this.PriorityQueue();
this.Terraforming();
/* Check Valuate() is actually limited, MUST BE THE LAST TEST. */
print("--Valuate() with excessive CPU usage--")
+16 -834
View File
@@ -573,10 +573,8 @@
4001 => 8002
4002 => 8004
4006 => 12
4012 => 1
[]:
4000 => 50
4012 => 1
clone:
Clone ListDump:
1005 => 1005
@@ -584,779 +582,21 @@
4001 => 8002
4002 => 8004
4006 => 12
4012 => 1
IsEmpty(): true
0 => 5 (false)
0 => 5 (true)
ERROR: Next() is invalid as Begin() is never called
ERROR: IsEnd() is invalid as Begin() is never called
0 => 5 (true)
0 => 5 (false)
2 => 6 (false)
3 => 6 (false)
0 => 5 (true)
2 => 6 (true)
3 => 6 (true)
0 => 5 (false)
Clone ListDump:
1005 => 1005
4000 => 50
4001 => 8002
4002 => 8004
4006 => 12
4012 => 1
ListDump: (Value Descending)
3 => 6
2 => 6
1 => -5
Begin(): 3 => 6 (false)
Next(): 2 => 6 (false)
Next(): 1 => -5 (false)
Next(): 0 => 0 (true)
Next(): 0 => 0 (true)
foreach (idx, val in list) {print}: (Value Descending)
3 => 6 (false)
2 => 6 (false)
1 => -5 (false)
Post loop: (true)
Post loop Next: 0 => 0 (true)
Post loop Next: 0 => 0 (true)
for (Begin / !IsEnd / Next) {print}: (Value Descending)
3 => 6 (false)
2 => 6 (false)
1 => -5 (false)
Post loop: 0 => 0 (true)
Post loop Next: 0 => 0 (true)
Post loop Next: 0 => 0 (true)
Begin / while (!IsEnd) {print / Next}: (Value Descending)
3 => 6 (false)
2 => 6 (false)
1 => -5 (false)
Post loop: 0 => 0 (true)
Post loop Next: 0 => 0 (true)
Post loop Next: 0 => 0 (true)
Begin / do {{print / Next} while (!IsEnd)}: (Value Descending)
3 => 6 (false)
2 => 6 (false)
1 => -5 (false)
Post loop: 0 => 0 (true)
Post loop Next: 0 => 0 (true)
Post loop Next: 0 => 0 (true)
GetValue / SetValue: (Value Descending)
3 => 6 (false)
=> SetValue(3, 6) ? true => 6 (false)
=> SetValue(3, 6666) ? true => 6666 (false)
2 => 6 (false)
=> SetValue(2, 6) ? true => 6 (false)
=> SetValue(2, 6666) ? true => 6666 (false)
1 => -5 (false)
=> SetValue(1, -5) ? true => -5 (false)
=> SetValue(1, -5555) ? true => -5555 (false)
Post loop: 0 => 0 (true)
Post loop Next: 0 => 0 (true)
Post loop Next: 0 => 0 (true)
GetValue / AddItem: (Value Descending)
3 => 6 (false)
=> AddItem(3, 6) => 6 (false)
=> AddItem(3, 6666) => 6 (false)
2 => 6 (false)
=> AddItem(2, 6) => 6 (false)
=> AddItem(2, 6666) => 6 (false)
1 => -5 (false)
=> AddItem(1, -5) => -5 (false)
=> AddItem(1, -5555) => -5 (false)
Post loop: 0 => 0 (true)
Post loop Next: 0 => 0 (true)
Post loop Next: 0 => 0 (true)
RemoveItem / HasItem / AddItem: (Value Descending)
3 => 6 / true (false)
=> RemoveItem(3) => 0 / false (false)
2 => 6 / true (false)
=> RemoveItem(2) => 0 / false (false)
1 => -5 / true (false)
=> RemoveItem(1) => 0 / false (true)
=> AddItem(1, -5) => -5 / true (false)
Post loop: 0 => 0 (true)
Post loop Next: 0 => 0 (true)
Post loop Next: 0 => 0 (true)
ListDump: (Value Ascending)
1 => -5
2 => 6
3 => 6
Begin(): 1 => -5 (false)
Next(): 2 => 6 (false)
Next(): 3 => 6 (false)
Next(): 0 => 0 (true)
Next(): 0 => 0 (true)
foreach (idx, val in list) {print}: (Value Ascending)
1 => -5 (false)
2 => 6 (false)
3 => 6 (false)
Post loop: (true)
Post loop Next: 0 => 0 (true)
Post loop Next: 0 => 0 (true)
for (Begin / !IsEnd / Next) {print}: (Value Ascending)
1 => -5 (false)
2 => 6 (false)
3 => 6 (false)
Post loop: 0 => 0 (true)
Post loop Next: 0 => 0 (true)
Post loop Next: 0 => 0 (true)
Begin / while (!IsEnd) {print / Next}: (Value Ascending)
1 => -5 (false)
2 => 6 (false)
3 => 6 (false)
Post loop: 0 => 0 (true)
Post loop Next: 0 => 0 (true)
Post loop Next: 0 => 0 (true)
Begin / do {{print / Next} while (!IsEnd)}: (Value Ascending)
1 => -5 (false)
2 => 6 (false)
3 => 6 (false)
Post loop: 0 => 0 (true)
Post loop Next: 0 => 0 (true)
Post loop Next: 0 => 0 (true)
GetValue / SetValue: (Value Ascending)
1 => -5 (false)
=> SetValue(1, -5) ? true => -5 (false)
=> SetValue(1, -5555) ? true => -5555 (false)
2 => 6 (false)
=> SetValue(2, 6) ? true => 6 (false)
=> SetValue(2, 6666) ? true => 6666 (false)
3 => 6 (false)
=> SetValue(3, 6) ? true => 6 (false)
=> SetValue(3, 6666) ? true => 6666 (false)
2 => 6666 (false)
=> SetValue(2, 6666) ? true => 6666 (false)
=> SetValue(2, 7405926) ? true => 7405926 (false)
3 => 6666 (false)
=> SetValue(3, 6666) ? true => 6666 (false)
=> SetValue(3, 7405926) ? true => 7405926 (false)
2 => 7405926 (false)
=> SetValue(2, 7405926) ? true => 7405926 (false)
=> SetValue(2, 8227983786) ? true => 8227983786 (false)
3 => 7405926 (false)
=> SetValue(3, 7405926) ? true => 7405926 (false)
=> SetValue(3, 8227983786) ? true => 8227983786 (false)
2 => 8227983786 (false)
=> SetValue(2, 8227983786) ? true => 8227983786 (false)
=> SetValue(2, 9141289986246) ? true => 9141289986246 (false)
3 => 8227983786 (false)
=> SetValue(3, 8227983786) ? true => 8227983786 (false)
=> SetValue(3, 9141289986246) ? true => 9141289986246 (false)
2 => 9141289986246 (false)
=> SetValue(2, 9141289986246) ? true => 9141289986246 (false)
=> SetValue(2, 10155973174719306) ? true => 10155973174719306 (false)
3 => 9141289986246 (false)
=> SetValue(3, 9141289986246) ? true => 9141289986246 (false)
=> SetValue(3, 10155973174719306) ? true => 10155973174719306 (false)
2 => 10155973174719306 (false)
=> SetValue(2, 10155973174719306) ? true => 10155973174719306 (false)
=> SetValue(2, -7163457876596402650) ? true => -7163457876596402650 (false)
3 => 10155973174719306 (false)
=> SetValue(3, 10155973174719306) ? true => 10155973174719306 (false)
=> SetValue(3, -7163457876596402650) ? true => -7163457876596402650 (false)
Post loop: 0 => 0 (true)
Post loop Next: 0 => 0 (true)
Post loop Next: 0 => 0 (true)
GetValue / AddItem: (Value Ascending)
1 => -5 (false)
=> AddItem(1, -5) => -5 (false)
=> AddItem(1, -5555) => -5 (false)
2 => 6 (false)
=> AddItem(2, 6) => 6 (false)
=> AddItem(2, 6666) => 6 (false)
3 => 6 (false)
=> AddItem(3, 6) => 6 (false)
=> AddItem(3, 6666) => 6 (false)
Post loop: 0 => 0 (true)
Post loop Next: 0 => 0 (true)
Post loop Next: 0 => 0 (true)
RemoveItem / HasItem / AddItem: (Value Ascending)
1 => -5 / true (false)
=> RemoveItem(1) => 0 / false (false)
2 => 6 / true (false)
=> RemoveItem(2) => 0 / false (false)
3 => 6 / true (false)
=> RemoveItem(3) => 0 / false (true)
=> AddItem(3, 6) => 6 / true (false)
Post loop: 0 => 0 (true)
Post loop Next: 0 => 0 (true)
Post loop Next: 0 => 0 (true)
ListDump: (Item Descending)
3 => 6
2 => 6
1 => -5
Begin(): 3 => 6 (false)
Next(): 2 => 6 (false)
Next(): 1 => -5 (false)
Next(): 0 => 0 (true)
Next(): 0 => 0 (true)
foreach (idx, val in list) {print}: (Item Descending)
3 => 6 (false)
2 => 6 (false)
1 => -5 (false)
Post loop: (true)
Post loop Next: 0 => 0 (true)
Post loop Next: 0 => 0 (true)
for (Begin / !IsEnd / Next) {print}: (Item Descending)
3 => 6 (false)
2 => 6 (false)
1 => -5 (false)
Post loop: 0 => 0 (true)
Post loop Next: 0 => 0 (true)
Post loop Next: 0 => 0 (true)
Begin / while (!IsEnd) {print / Next}: (Item Descending)
3 => 6 (false)
2 => 6 (false)
1 => -5 (false)
Post loop: 0 => 0 (true)
Post loop Next: 0 => 0 (true)
Post loop Next: 0 => 0 (true)
Begin / do {{print / Next} while (!IsEnd)}: (Item Descending)
3 => 6 (false)
2 => 6 (false)
1 => -5 (false)
Post loop: 0 => 0 (true)
Post loop Next: 0 => 0 (true)
Post loop Next: 0 => 0 (true)
GetValue / SetValue: (Item Descending)
3 => 6 (false)
=> SetValue(3, 6) ? true => 6 (false)
=> SetValue(3, 6666) ? true => 6666 (false)
2 => 6 (false)
=> SetValue(2, 6) ? true => 6 (false)
=> SetValue(2, 6666) ? true => 6666 (false)
1 => -5 (false)
=> SetValue(1, -5) ? true => -5 (false)
=> SetValue(1, -5555) ? true => -5555 (false)
Post loop: 0 => 0 (true)
Post loop Next: 0 => 0 (true)
Post loop Next: 0 => 0 (true)
GetValue / AddItem: (Item Descending)
3 => 6 (false)
=> AddItem(3, 6) => 6 (false)
=> AddItem(3, 6666) => 6 (false)
2 => 6 (false)
=> AddItem(2, 6) => 6 (false)
=> AddItem(2, 6666) => 6 (false)
1 => -5 (false)
=> AddItem(1, -5) => -5 (false)
=> AddItem(1, -5555) => -5 (false)
Post loop: 0 => 0 (true)
Post loop Next: 0 => 0 (true)
Post loop Next: 0 => 0 (true)
RemoveItem / HasItem / AddItem: (Item Descending)
3 => 6 / true (false)
=> RemoveItem(3) => 0 / false (false)
2 => 6 / true (false)
=> RemoveItem(2) => 0 / false (false)
1 => -5 / true (false)
=> RemoveItem(1) => 0 / false (true)
=> AddItem(1, -5) => -5 / true (false)
Post loop: 0 => 0 (true)
Post loop Next: 0 => 0 (true)
Post loop Next: 0 => 0 (true)
ListDump: (Item Ascending)
1 => -5
2 => 6
3 => 6
Begin(): 1 => -5 (false)
Next(): 2 => 6 (false)
Next(): 3 => 6 (false)
Next(): 0 => 0 (true)
Next(): 0 => 0 (true)
foreach (idx, val in list) {print}: (Item Ascending)
1 => -5 (false)
2 => 6 (false)
3 => 6 (false)
Post loop: (true)
Post loop Next: 0 => 0 (true)
Post loop Next: 0 => 0 (true)
for (Begin / !IsEnd / Next) {print}: (Item Ascending)
1 => -5 (false)
2 => 6 (false)
3 => 6 (false)
Post loop: 0 => 0 (true)
Post loop Next: 0 => 0 (true)
Post loop Next: 0 => 0 (true)
Begin / while (!IsEnd) {print / Next}: (Item Ascending)
1 => -5 (false)
2 => 6 (false)
3 => 6 (false)
Post loop: 0 => 0 (true)
Post loop Next: 0 => 0 (true)
Post loop Next: 0 => 0 (true)
Begin / do {{print / Next} while (!IsEnd)}: (Item Ascending)
1 => -5 (false)
2 => 6 (false)
3 => 6 (false)
Post loop: 0 => 0 (true)
Post loop Next: 0 => 0 (true)
Post loop Next: 0 => 0 (true)
GetValue / SetValue: (Item Ascending)
1 => -5 (false)
=> SetValue(1, -5) ? true => -5 (false)
=> SetValue(1, -5555) ? true => -5555 (false)
2 => 6 (false)
=> SetValue(2, 6) ? true => 6 (false)
=> SetValue(2, 6666) ? true => 6666 (false)
3 => 6 (false)
=> SetValue(3, 6) ? true => 6 (false)
=> SetValue(3, 6666) ? true => 6666 (false)
Post loop: 0 => 0 (true)
Post loop Next: 0 => 0 (true)
Post loop Next: 0 => 0 (true)
GetValue / AddItem: (Item Ascending)
1 => -5 (false)
=> AddItem(1, -5) => -5 (false)
=> AddItem(1, -5555) => -5 (false)
2 => 6 (false)
=> AddItem(2, 6) => 6 (false)
=> AddItem(2, 6666) => 6 (false)
3 => 6 (false)
=> AddItem(3, 6) => 6 (false)
=> AddItem(3, 6666) => 6 (false)
Post loop: 0 => 0 (true)
Post loop Next: 0 => 0 (true)
Post loop Next: 0 => 0 (true)
RemoveItem / HasItem / AddItem: (Item Ascending)
1 => -5 / true (false)
=> RemoveItem(1) => 0 / false (false)
2 => 6 / true (false)
=> RemoveItem(2) => 0 / false (false)
3 => 6 / true (false)
=> RemoveItem(3) => 0 / false (true)
=> AddItem(3, 6) => 6 / true (false)
Post loop: 0 => 0 (true)
Post loop Next: 0 => 0 (true)
Post loop Next: 0 => 0 (true)
SwapList: (Value Descending) (Value Descending)
DumpList 1: (Value Descending)
3 => 6
2 => 6
1 => -5
DumpList 2: (Value Descending)
4002 => 8004
4001 => 8002
1005 => 1005
4000 => 50
4006 => 12
4012 => 1
List 1 Begin: 3 => 6 (false)
List 2 Begin: 4002 => 8004 (false)
List 2 Next: 4001 => 8002 (false)
=> Swap list 1 with list 2 (null : 0x00000000)
List 1 Next: 1005 => 1005 (false)
List 2 Next: 2 => 6 (false)
List 2 Next: 1 => -5 (false)
=> Swap list 1 with list 2 (null : 0x00000000)
List 1 Next: 0 => 0 (true)
List 2 Next: 4000 >= 50 (false)
List 2 Next: 4006 >= 12 (false)
SwapList: (Value Descending) (Value Ascending)
DumpList 1: (Value Descending)
3 => 6
2 => 6
1 => -5
DumpList 2: (Value Ascending)
4012 => 1
4006 => 12
4000 => 50
1005 => 1005
4001 => 8002
4002 => 8004
List 1 Begin: 3 => 6 (false)
List 2 Begin: 4012 => 1 (false)
List 2 Next: 4006 => 12 (false)
=> Swap list 1 with list 2 (null : 0x00000000)
List 1 Next: 4000 => 50 (false)
List 2 Next: 2 => 6 (false)
List 2 Next: 1 => -5 (false)
=> Swap list 1 with list 2 (null : 0x00000000)
List 1 Next: 0 => 0 (true)
List 2 Next: 1005 >= 1005 (false)
List 2 Next: 4001 >= 8002 (false)
SwapList: (Value Descending) (Item Descending)
DumpList 1: (Value Descending)
3 => 6
2 => 6
1 => -5
DumpList 2: (Item Descending)
4012 => 1
4006 => 12
4002 => 8004
4001 => 8002
4000 => 50
1005 => 1005
List 1 Begin: 3 => 6 (false)
List 2 Begin: 4012 => 1 (false)
List 2 Next: 4006 => 12 (false)
=> Swap list 1 with list 2 (null : 0x00000000)
List 1 Next: 4002 => 8004 (false)
List 2 Next: 2 => 6 (false)
List 2 Next: 1 => -5 (false)
=> Swap list 1 with list 2 (null : 0x00000000)
List 1 Next: 0 => 0 (true)
List 2 Next: 4001 >= 8002 (false)
List 2 Next: 4000 >= 50 (false)
SwapList: (Value Descending) (Item Ascending)
DumpList 1: (Value Descending)
3 => 6
2 => 6
1 => -5
DumpList 2: (Item Ascending)
1005 => 1005
4000 => 50
4001 => 8002
4002 => 8004
4006 => 12
4012 => 1
List 1 Begin: 3 => 6 (false)
List 2 Begin: 1005 => 1005 (false)
List 2 Next: 4000 => 50 (false)
=> Swap list 1 with list 2 (null : 0x00000000)
List 1 Next: 4001 => 8002 (false)
List 2 Next: 2 => 6 (false)
List 2 Next: 1 => -5 (false)
=> Swap list 1 with list 2 (null : 0x00000000)
List 1 Next: 0 => 0 (true)
List 2 Next: 4002 >= 8004 (false)
List 2 Next: 4006 >= 12 (false)
SwapList: (Value Ascending) (Value Descending)
DumpList 1: (Value Ascending)
1 => -5
2 => 6
3 => 6
DumpList 2: (Value Descending)
4002 => 8004
4001 => 8002
1005 => 1005
4000 => 50
4006 => 12
4012 => 1
List 1 Begin: 1 => -5 (false)
List 2 Begin: 4002 => 8004 (false)
List 2 Next: 4001 => 8002 (false)
=> Swap list 1 with list 2 (null : 0x00000000)
List 1 Next: 1005 => 1005 (false)
List 2 Next: 2 => 6 (false)
List 2 Next: 3 => 6 (false)
=> Swap list 1 with list 2 (null : 0x00000000)
List 1 Next: 0 => 0 (true)
List 2 Next: 4000 >= 50 (false)
List 2 Next: 4006 >= 12 (false)
SwapList: (Value Ascending) (Value Ascending)
DumpList 1: (Value Ascending)
1 => -5
2 => 6
3 => 6
DumpList 2: (Value Ascending)
4012 => 1
4006 => 12
4000 => 50
1005 => 1005
4001 => 8002
4002 => 8004
List 1 Begin: 1 => -5 (false)
List 2 Begin: 4012 => 1 (false)
List 2 Next: 4006 => 12 (false)
=> Swap list 1 with list 2 (null : 0x00000000)
List 1 Next: 4000 => 50 (false)
List 2 Next: 2 => 6 (false)
List 2 Next: 3 => 6 (false)
=> Swap list 1 with list 2 (null : 0x00000000)
List 1 Next: 0 => 0 (true)
List 2 Next: 1005 >= 1005 (false)
List 2 Next: 4001 >= 8002 (false)
SwapList: (Value Ascending) (Item Descending)
DumpList 1: (Value Ascending)
1 => -5
2 => 6
3 => 6
DumpList 2: (Item Descending)
4012 => 1
4006 => 12
4002 => 8004
4001 => 8002
4000 => 50
1005 => 1005
List 1 Begin: 1 => -5 (false)
List 2 Begin: 4012 => 1 (false)
List 2 Next: 4006 => 12 (false)
=> Swap list 1 with list 2 (null : 0x00000000)
List 1 Next: 4002 => 8004 (false)
List 2 Next: 2 => 6 (false)
List 2 Next: 3 => 6 (false)
=> Swap list 1 with list 2 (null : 0x00000000)
List 1 Next: 0 => 0 (true)
List 2 Next: 4001 >= 8002 (false)
List 2 Next: 4000 >= 50 (false)
SwapList: (Value Ascending) (Item Ascending)
DumpList 1: (Value Ascending)
1 => -5
2 => 6
3 => 6
DumpList 2: (Item Ascending)
1005 => 1005
4000 => 50
4001 => 8002
4002 => 8004
4006 => 12
4012 => 1
List 1 Begin: 1 => -5 (false)
List 2 Begin: 1005 => 1005 (false)
List 2 Next: 4000 => 50 (false)
=> Swap list 1 with list 2 (null : 0x00000000)
List 1 Next: 4001 => 8002 (false)
List 2 Next: 2 => 6 (false)
List 2 Next: 3 => 6 (false)
=> Swap list 1 with list 2 (null : 0x00000000)
List 1 Next: 0 => 0 (true)
List 2 Next: 4002 >= 8004 (false)
List 2 Next: 4006 >= 12 (false)
SwapList: (Item Descending) (Value Descending)
DumpList 1: (Item Descending)
3 => 6
2 => 6
1 => -5
DumpList 2: (Value Descending)
4002 => 8004
4001 => 8002
1005 => 1005
4000 => 50
4006 => 12
4012 => 1
List 1 Begin: 3 => 6 (false)
List 2 Begin: 4002 => 8004 (false)
List 2 Next: 4001 => 8002 (false)
=> Swap list 1 with list 2 (null : 0x00000000)
List 1 Next: 1005 => 1005 (false)
List 2 Next: 2 => 6 (false)
List 2 Next: 1 => -5 (false)
=> Swap list 1 with list 2 (null : 0x00000000)
List 1 Next: 0 => 0 (true)
List 2 Next: 4000 >= 50 (false)
List 2 Next: 4006 >= 12 (false)
SwapList: (Item Descending) (Value Ascending)
DumpList 1: (Item Descending)
3 => 6
2 => 6
1 => -5
DumpList 2: (Value Ascending)
4012 => 1
4006 => 12
4000 => 50
1005 => 1005
4001 => 8002
4002 => 8004
List 1 Begin: 3 => 6 (false)
List 2 Begin: 4012 => 1 (false)
List 2 Next: 4006 => 12 (false)
=> Swap list 1 with list 2 (null : 0x00000000)
List 1 Next: 4000 => 50 (false)
List 2 Next: 2 => 6 (false)
List 2 Next: 1 => -5 (false)
=> Swap list 1 with list 2 (null : 0x00000000)
List 1 Next: 0 => 0 (true)
List 2 Next: 1005 >= 1005 (false)
List 2 Next: 4001 >= 8002 (false)
SwapList: (Item Descending) (Item Descending)
DumpList 1: (Item Descending)
3 => 6
2 => 6
1 => -5
DumpList 2: (Item Descending)
4012 => 1
4006 => 12
4002 => 8004
4001 => 8002
4000 => 50
1005 => 1005
List 1 Begin: 3 => 6 (false)
List 2 Begin: 4012 => 1 (false)
List 2 Next: 4006 => 12 (false)
=> Swap list 1 with list 2 (null : 0x00000000)
List 1 Next: 4002 => 8004 (false)
List 2 Next: 2 => 6 (false)
List 2 Next: 1 => -5 (false)
=> Swap list 1 with list 2 (null : 0x00000000)
List 1 Next: 0 => 0 (true)
List 2 Next: 4001 >= 8002 (false)
List 2 Next: 4000 >= 50 (false)
SwapList: (Item Descending) (Item Ascending)
DumpList 1: (Item Descending)
3 => 6
2 => 6
1 => -5
DumpList 2: (Item Ascending)
1005 => 1005
4000 => 50
4001 => 8002
4002 => 8004
4006 => 12
4012 => 1
List 1 Begin: 3 => 6 (false)
List 2 Begin: 1005 => 1005 (false)
List 2 Next: 4000 => 50 (false)
=> Swap list 1 with list 2 (null : 0x00000000)
List 1 Next: 4001 => 8002 (false)
List 2 Next: 2 => 6 (false)
List 2 Next: 1 => -5 (false)
=> Swap list 1 with list 2 (null : 0x00000000)
List 1 Next: 0 => 0 (true)
List 2 Next: 4002 >= 8004 (false)
List 2 Next: 4006 >= 12 (false)
SwapList: (Item Ascending) (Value Descending)
DumpList 1: (Item Ascending)
1 => -5
2 => 6
3 => 6
DumpList 2: (Value Descending)
4002 => 8004
4001 => 8002
1005 => 1005
4000 => 50
4006 => 12
4012 => 1
List 1 Begin: 1 => -5 (false)
List 2 Begin: 4002 => 8004 (false)
List 2 Next: 4001 => 8002 (false)
=> Swap list 1 with list 2 (null : 0x00000000)
List 1 Next: 1005 => 1005 (false)
List 2 Next: 2 => 6 (false)
List 2 Next: 3 => 6 (false)
=> Swap list 1 with list 2 (null : 0x00000000)
List 1 Next: 0 => 0 (true)
List 2 Next: 4000 >= 50 (false)
List 2 Next: 4006 >= 12 (false)
SwapList: (Item Ascending) (Value Ascending)
DumpList 1: (Item Ascending)
1 => -5
2 => 6
3 => 6
DumpList 2: (Value Ascending)
4012 => 1
4006 => 12
4000 => 50
1005 => 1005
4001 => 8002
4002 => 8004
List 1 Begin: 1 => -5 (false)
List 2 Begin: 4012 => 1 (false)
List 2 Next: 4006 => 12 (false)
=> Swap list 1 with list 2 (null : 0x00000000)
List 1 Next: 4000 => 50 (false)
List 2 Next: 2 => 6 (false)
List 2 Next: 3 => 6 (false)
=> Swap list 1 with list 2 (null : 0x00000000)
List 1 Next: 0 => 0 (true)
List 2 Next: 1005 >= 1005 (false)
List 2 Next: 4001 >= 8002 (false)
SwapList: (Item Ascending) (Item Descending)
DumpList 1: (Item Ascending)
1 => -5
2 => 6
3 => 6
DumpList 2: (Item Descending)
4012 => 1
4006 => 12
4002 => 8004
4001 => 8002
4000 => 50
1005 => 1005
List 1 Begin: 1 => -5 (false)
List 2 Begin: 4012 => 1 (false)
List 2 Next: 4006 => 12 (false)
=> Swap list 1 with list 2 (null : 0x00000000)
List 1 Next: 4002 => 8004 (false)
List 2 Next: 2 => 6 (false)
List 2 Next: 3 => 6 (false)
=> Swap list 1 with list 2 (null : 0x00000000)
List 1 Next: 0 => 0 (true)
List 2 Next: 4001 >= 8002 (false)
List 2 Next: 4000 >= 50 (false)
SwapList: (Item Ascending) (Item Ascending)
DumpList 1: (Item Ascending)
1 => -5
2 => 6
3 => 6
DumpList 2: (Item Ascending)
1005 => 1005
4000 => 50
4001 => 8002
4002 => 8004
4006 => 12
4012 => 1
List 1 Begin: 1 => -5 (false)
List 2 Begin: 1005 => 1005 (false)
List 2 Next: 4000 => 50 (false)
=> Swap list 1 with list 2 (null : 0x00000000)
List 1 Next: 4001 => 8002 (false)
List 2 Next: 2 => 6 (false)
List 2 Next: 3 => 6 (false)
=> Swap list 1 with list 2 (null : 0x00000000)
List 1 Next: 0 => 0 (true)
List 2 Next: 4002 >= 8004 (false)
List 2 Next: 4006 >= 12 (false)
IsEmpty(): true
Negative ListDump:
1 => 0
0 => 0
-1 => 0
2 => -2
-2 => -2
3 => -4
-3 => -4
4 => -8
-4 => -8
5 => -12
-5 => -12
6 => -18
-6 => -18
7 => -24
-7 => -24
8 => -32
-8 => -32
9 => -40
-9 => -40
-10 => -50
KeepBelowValue(-12):
6 => -18
-6 => -18
7 => -24
-7 => -24
8 => -32
-8 => -32
9 => -40
-9 => -40
-10 => -50
KeepAboveValue(-40):
6 => -18
-6 => -18
7 => -24
-7 => -24
8 => -32
-8 => -32
KeepValue(-24):
7 => -24
-7 => -24
--Company--
SetName(): true
@@ -10538,9 +9778,9 @@ ERROR: IsEnd() is invalid as Begin() is never called
GetLocation(): 33417
GetEngineType(): 153
GetUnitNumber(): 1
GetAge(): 0
GetAge(): 1
GetMaxAge(): 5490
GetAgeLeft(): 5490
GetAgeLeft(): 5489
GetCurrentSpeed(): 7
GetRunningCost(): 421
GetProfitThisYear(): 0
@@ -10553,7 +9793,7 @@ ERROR: IsEnd() is invalid as Begin() is never called
IsInDepot(): false
GetNumWagons(): 1
GetWagonEngineType(): 153
GetWagonAge(): 0
GetWagonAge(): 1
GetLength(): 8
GetOwner(): 1
BuildVehicle(): 14
@@ -10628,11 +9868,11 @@ ERROR: IsEnd() is invalid as Begin() is never called
14 => 1
12 => 1
Age ListDump:
12 => 1
17 => 0
16 => 0
14 => 0
13 => 0
12 => 0
MaxAge ListDump:
16 => 10980
14 => 10980
@@ -10644,7 +9884,7 @@ ERROR: IsEnd() is invalid as Begin() is never called
14 => 10980
17 => 7320
13 => 5490
12 => 5490
12 => 5489
CurrentSpeed ListDump:
12 => 27
17 => 0
@@ -10872,73 +10112,15 @@ ERROR: IsEnd() is invalid as Begin() is never called
Clear(): (null : 0x00000000)
IsEmpty(): true
Count(): 0
--Terraforming--
DemolishTile(): true
DemolishTile(): true
GetBankBalance(): 1999216685
LowerTile(): false
RaiseTile(): true
GetBankBalance(): 1999156124
LowerTile(): true
GetBankBalance(): 1999155563
LowerTile(): true
RaiseTile(): true
GetBankBalance(): 1999124603
RaiseTile(): false
LowerTile(): false
LowerTile(): true
RaiseTile(): true
LowerTile(): true
GetBankBalance(): 1999122125
LowerTile(): false
LowerTile(): true
RaiseTile(): false
LowerTile(): true
LowerTile(): false
RaiseTile(): true
LowerTile(): true
GetBankBalance(): 1999116574
RaiseTile(): true
LowerTile(): true
GetBankBalance(): 1999100519
RaiseTile(): false
LowerTile(): true
GetBankBalance(): 1999098443
RaiseTile(): true
RaiseTile(): true
GetBankBalance(): 1999088221
LevelTiles(): true
LevelTiles(): false
GetBankBalance(): 1999078593
LevelTiles(): true
LevelTiles(): true
GetBankBalance(): 1998792514
LowerTile(): false
RaiseTile(): true
RaiseTile(): false
LowerTile(): true
GetBankBalance(): 1998786415
RaiseTile(): true
LowerTile(): true
LowerTile(): false
GetBankBalance(): 1998785248
RaiseTile(): false
LowerTile(): true
LowerTile(): false
RaiseTile(): false
RaiseTile(): true
GetBankBalance(): 1998738752
--Valuate() with excessive CPU usage--
constructor failed with: excessive CPU usage in list filter function
Your script made an error: excessive CPU usage in valuator function
CALLSTACK WITH LOCALS
*FUNCTION [Valuate()] Valuate line [5]
[args] ARRAY
[this] INSTANCE
*FUNCTION [Start()] regression/main.nut line [2474]
[Infinite] CLOSURE
[list] INSTANCE
[this] INSTANCE
CALLSTACK
*FUNCTION [Start()] regression/main.nut line [2184]
LOCALS
[Infinite] CLOSURE
[list] INSTANCE
[this] INSTANCE
ERROR: The script died unexpectedly.
+1 -1
View File
@@ -1,5 +1,5 @@
include(CreateRegression)
create_regression("ai"
create_regression(
${CMAKE_CURRENT_SOURCE_DIR}/info.nut
${CMAKE_CURRENT_SOURCE_DIR}/main.nut
${CMAKE_CURRENT_SOURCE_DIR}/result.txt
+1 -1
View File
@@ -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 "16"; }
function GetAPIVersion() { return "15"; }
function GetDate() { return "2007-03-18"; }
function CreateInstance() { return "StationList"; }
function UseAsRandomAI() { return false; }
+1 -1
View File
@@ -3,5 +3,5 @@ add_subdirectory(tests)
add_files(
scriptrun.cpp
scriptrun.h
CONDITION ICU_I18N_FOUND
CONDITION ICU_i18n_FOUND
)
+1 -1
View File
@@ -152,7 +152,7 @@ UBool ScriptRun::next()
// if it's an open character, push it onto the stack.
// if it's a close character, find the matching open on the
// stack, and use that script code. Any non-matching open
// characters above it on the stack will be popped.
// characters above it on the stack will be poped.
if (pairIndex >= 0) {
if ((pairIndex & 1) == 0) {
parenStack[++parenSP].pairIndex = pairIndex;
+1 -1
View File
@@ -1,4 +1,4 @@
add_test_files(
test_srtest.cpp
CONDITION ICU_I18N_FOUND
CONDITION ICU_i18n_FOUND
)
+24 -18
View File
@@ -19,7 +19,7 @@ void sqstd_printcallstack(HSQUIRRELVM v)
SQInteger level=1; //1 is to skip this function that is level 0
SQInteger seq=0;
pf(v,"\n");
pf(v,"CALLSTACK WITH LOCALS\n");
pf(v,"CALLSTACK\n");
while(SQ_SUCCEEDED(sq_stackinfos(v,level,&si)))
{
std::string_view fn="unknown";
@@ -35,6 +35,13 @@ void sqstd_printcallstack(HSQUIRRELVM v)
src = (p == std::string_view::npos) ? si.source : si.source.substr(p + 4);
}
pf(v,fmt::format("*FUNCTION [{}()] {} line [{}]\n",fn,src,si.line));
level++;
}
level=0;
pf(v,"\n");
pf(v,"LOCALS\n");
for(level=0;level<10;level++){
seq=0;
std::optional<std::string_view> opt;
while ((opt = sq_getlocal(v,level,seq)).has_value()) {
@@ -43,65 +50,64 @@ void sqstd_printcallstack(HSQUIRRELVM v)
switch(sq_gettype(v,-1))
{
case OT_NULL:
pf(v,fmt::format(" [{}] NULL\n",name));
pf(v,fmt::format("[{}] NULL\n",name));
break;
case OT_INTEGER:
sq_getinteger(v,-1,&i);
pf(v,fmt::format(" [{}] {}\n",name,i));
pf(v,fmt::format("[{}] {}\n",name,i));
break;
case OT_FLOAT:
sq_getfloat(v,-1,&f);
pf(v,fmt::format(" [{}] {:14g}\n",name,f));
pf(v,fmt::format("[{}] {:14g}\n",name,f));
break;
case OT_USERPOINTER:
pf(v,fmt::format(" [{}] USERPOINTER\n",name));
pf(v,fmt::format("[{}] USERPOINTER\n",name));
break;
case OT_STRING: {
std::string_view view;
sq_getstring(v,-1,view);
pf(v,fmt::format(" [{}] \"{}\"\n",name,view));
pf(v,fmt::format("[{}] \"{}\"\n",name,view));
break;
}
case OT_TABLE:
pf(v,fmt::format(" [{}] TABLE\n",name));
pf(v,fmt::format("[{}] TABLE\n",name));
break;
case OT_ARRAY:
pf(v,fmt::format(" [{}] ARRAY\n",name));
pf(v,fmt::format("[{}] ARRAY\n",name));
break;
case OT_CLOSURE:
pf(v,fmt::format(" [{}] CLOSURE\n",name));
pf(v,fmt::format("[{}] CLOSURE\n",name));
break;
case OT_NATIVECLOSURE:
pf(v,fmt::format(" [{}] NATIVECLOSURE\n",name));
pf(v,fmt::format("[{}] NATIVECLOSURE\n",name));
break;
case OT_GENERATOR:
pf(v,fmt::format(" [{}] GENERATOR\n",name));
pf(v,fmt::format("[{}] GENERATOR\n",name));
break;
case OT_USERDATA:
pf(v,fmt::format(" [{}] USERDATA\n",name));
pf(v,fmt::format("[{}] USERDATA\n",name));
break;
case OT_THREAD:
pf(v,fmt::format(" [{}] THREAD\n",name));
pf(v,fmt::format("[{}] THREAD\n",name));
break;
case OT_CLASS:
pf(v,fmt::format(" [{}] CLASS\n",name));
pf(v,fmt::format("[{}] CLASS\n",name));
break;
case OT_INSTANCE:
pf(v,fmt::format(" [{}] INSTANCE\n",name));
pf(v,fmt::format("[{}] INSTANCE\n",name));
break;
case OT_WEAKREF:
pf(v,fmt::format(" [{}] WEAKREF\n",name));
pf(v,fmt::format("[{}] WEAKREF\n",name));
break;
case OT_BOOL:{
sq_getbool(v,-1,&b);
pf(v,fmt::format(" [{}] {}\n",name,b?"true":"false"));
pf(v,fmt::format("[{}] {}\n",name,b?"true":"false"));
}
break;
default: assert(0); break;
}
sq_pop(v,1);
}
level++;
}
}
}
+3 -3
View File
@@ -472,7 +472,7 @@ SQRESULT sq_setroottable(HSQUIRRELVM v)
v->Pop();
return SQ_OK;
}
return sq_throwerror(v, "invalid type");
return sq_throwerror(v, "ivalid type");
}
SQRESULT sq_setconsttable(HSQUIRRELVM v)
@@ -483,7 +483,7 @@ SQRESULT sq_setconsttable(HSQUIRRELVM v)
v->Pop();
return SQ_OK;
}
return sq_throwerror(v, "invalid type, expected table");
return sq_throwerror(v, "ivalid type, expected table");
}
void sq_setforeignptr(HSQUIRRELVM v,SQUserPointer p)
@@ -796,7 +796,7 @@ SQRESULT sq_setdelegate(HSQUIRRELVM v,SQInteger idx)
switch(type) {
case OT_TABLE:
if(type(mt) == OT_TABLE) {
if(!_table(self)->SetDelegate(_table(mt))) return sq_throwerror(v, "delegate cycle");
if(!_table(self)->SetDelegate(_table(mt))) return sq_throwerror(v, "delagate cycle");
v->Pop();}
else if(type(mt)==OT_NULL) {
_table(self)->SetDelegate(nullptr); v->Pop(); }
+3 -12
View File
@@ -519,7 +519,6 @@ bool _sort_compare(HSQUIRRELVM v,SQObjectPtr &a,SQObjectPtr &b,SQInteger func,SQ
bool _hsort_sift_down(HSQUIRRELVM v,SQArray *arr, SQInteger root, SQInteger bottom, SQInteger func)
{
SQInteger initial_size = arr->Size();
SQInteger maxChild;
SQInteger done = 0;
SQInteger ret;
@@ -532,10 +531,6 @@ bool _hsort_sift_down(HSQUIRRELVM v,SQArray *arr, SQInteger root, SQInteger bott
else {
if(!_sort_compare(v,arr->_values[root2],arr->_values[root2 + 1],func,ret))
return false;
if (initial_size != arr->Size()) {
v->Raise_Error("compare function modified array");
return false;
}
if (ret > 0) {
maxChild = root2;
}
@@ -547,14 +542,10 @@ bool _hsort_sift_down(HSQUIRRELVM v,SQArray *arr, SQInteger root, SQInteger bott
if(!_sort_compare(v,arr->_values[root],arr->_values[maxChild],func,ret))
return false;
if (initial_size != arr->Size()) {
v->Raise_Error("compare function modified array");
return false;
}
if (ret < 0) {
if (root == maxChild) {
v->Raise_Error("inconsistent compare function");
return false; // We'd be swapping ourself. The compare function is incorrect
return false; // We'd be swapping ourselve. The compare function is incorrect
}
_Swap(arr->_values[root],arr->_values[maxChild]);
root = maxChild;
@@ -605,7 +596,7 @@ static SQInteger array_slice(HSQUIRRELVM v)
if(sidx < 0)sidx = alen + sidx;
if(eidx < 0)eidx = alen + eidx;
if(eidx < sidx)return sq_throwerror(v,"wrong indexes");
if(sidx < 0 || eidx > alen) return sq_throwerror(v,"slice out of range");
if(eidx > alen)return sq_throwerror(v,"slice out of range");
SQArray *arr=SQArray::Create(_ss(v),eidx-sidx);
SQObjectPtr t;
SQInteger count=0;
@@ -646,7 +637,7 @@ static SQInteger string_slice(HSQUIRRELVM v)
if(sidx < 0)sidx = slen + sidx;
if(eidx < 0)eidx = slen + eidx;
if(eidx < sidx) return sq_throwerror(v,"wrong indexes");
if(sidx < 0 || eidx > slen) return sq_throwerror(v,"slice out of range");
if(eidx > slen) return sq_throwerror(v,"slice out of range");
v->Push(SQString::Create(_ss(v),_stringval(o).substr(sidx,eidx-sidx)));
return 1;
}
+1 -1
View File
@@ -245,7 +245,7 @@ bool SafeWrite(HSQUIRRELVM v,SQWRITEFUNC write,SQUserPointer up,SQUserPointer de
bool SafeRead(HSQUIRRELVM v,SQWRITEFUNC read,SQUserPointer up,SQUserPointer dest,SQInteger size)
{
if(size && read(up,dest,size) != size) {
v->Raise_Error("io error, read function failure, the origin stream could be corrupted/truncated");
v->Raise_Error("io error, read function failure, the origin stream could be corrupted/trucated");
return false;
}
return true;
+2 -2
View File
@@ -193,7 +193,7 @@ SQSharedState::~SQSharedState()
t = nx;
}
}
// assert(_gc_chain==nullptr); //just to prove a theory
// assert(_gc_chain==nullptr); //just to proove a theory
while(_gc_chain){
_gc_chain->_uiRef--;
_gc_chain->Release();
@@ -227,7 +227,7 @@ SQInteger SQSharedState::GetMetaMethodIdxByName(const SQObjectPtr &name)
* This is done internally by a vector onto which the to be freed instances are pushed. When
* this is called when not already processing, this method will actually call the FinalFree
* function which might cause more elements to end up in the queue which this method then
* picks up continuing until it has processed all instances in that queue.
* picks up continueing until it has processed all instances in that queue.
* @param collectable The collectable to (eventually) free.
*/
void SQSharedState::DelayFinalFree(SQCollectable *collectable)
+3 -3
View File
@@ -424,7 +424,7 @@ bool SQVM::Return(SQInteger _arg0, SQInteger _arg1, SQObjectPtr &retval)
else retval = _null_;
}
else {
if(target != -1) { //-1 is when a class constructor ret value has to be ignored
if(target != -1) { //-1 is when a class contructor ret value has to be ignored
if (_arg0 != MAX_FUNC_STACKSIZE)
STK(target) = _stack._vals[oldstackbase+_arg1];
else
@@ -1285,8 +1285,8 @@ bool SQVM::FallBackGet(const SQObjectPtr &self,const SQObjectPtr &key,SQObjectPt
if(sq_isnumeric(key)){
SQInteger n=tointeger(key);
std::string_view str = _stringval(self);
if (n < 0) n = str.size() + n;
if (n >= 0 && n < static_cast<SQInteger>(str.size())) {
if(std::abs(n) < static_cast<SQInteger>(str.size())){
if(n<0)n=str.size()+n;
dest=SQInteger(str[n]);
return true;
}
+2 -5
View File
@@ -37,7 +37,7 @@ add_files(
add_files(
gfx_layout_icu.cpp
gfx_layout_icu.h
CONDITION ICU_I18N_FOUND AND HARFBUZZ_FOUND
CONDITION ICU_i18n_FOUND AND HARFBUZZ_FOUND
)
add_files(
@@ -133,8 +133,7 @@ add_files(
crashlog.cpp
crashlog.h
currency.cpp
currency_func.h
currency_type.h
currency.h
date_gui.cpp
date_gui.h
debug.cpp
@@ -210,7 +209,6 @@ add_files(
goal_base.h
goal_cmd.h
goal_gui.cpp
goal_gui.h
goal_type.h
graph_gui.cpp
graph_gui.h
@@ -336,7 +334,6 @@ add_files(
newgrf_town.h
newgrf_townname.cpp
newgrf_townname.h
newgrf_type.h
news_cmd.h
news_func.h
news_gui.cpp
+7 -13
View File
@@ -37,7 +37,6 @@ public:
/**
* Get the current AI tick.
* @return The tick number.
*/
static uint GetTick();
@@ -98,35 +97,30 @@ public:
/**
* Queue a new event for an AI.
* @param company The company to receive the event.
* @param event The event.
*/
static void NewEvent(CompanyID company, ScriptEvent *event);
/**
* Broadcast a new event to all active AIs.
* @param event The event to broadcast.
* @param skip_company The optional company not to send the event to.
*/
static void BroadcastNewEvent(ScriptEvent *event, CompanyID skip_company = CompanyID::Invalid());
/**
* Save data from an AI to a savegame.
* @param company To company to save.
*/
static void Save(CompanyID company);
/** @copydoc ScriptScanner::GetConsoleList */
/** Wrapper function for AIScanner::GetAIConsoleList */
static void GetConsoleList(std::back_insert_iterator<std::string> &output_iterator, bool newest_only);
/** @copydoc ScriptScanner::GetConsoleList */
static void GetConsoleLibraryList(std::back_insert_iterator<std::string> &output_iterator, bool newest_only);
/** @copydoc ScriptScanner::GetInfoList */
/** Wrapper function for AIScanner::GetAIConsoleLibraryList */
static void GetConsoleLibraryList(std::back_insert_iterator<std::string> &output_iterator);
/** Wrapper function for AIScanner::GetAIInfoList */
static const ScriptInfoList *GetInfoList();
/** @copydoc ScriptScanner::GetUniqueInfoList */
/** Wrapper function for AIScanner::GetUniqueAIInfoList */
static const ScriptInfoList *GetUniqueInfoList();
/** @copydoc ScriptConfig::FindInfo */
/** Wrapper function for AIScanner::FindInfo */
static class AIInfo *FindInfo(const std::string &name, int version, bool force_exact_match);
/** @copydoc ScriptInstance::FindLibrary */
/** Wrapper function for AIScanner::FindLibrary */
static class AILibrary *FindLibrary(const std::string &library, int version);
/**
+3 -3
View File
@@ -21,14 +21,14 @@
{
assert(company < MAX_COMPANIES);
if (source == ScriptSettingSource::Default && _game_mode == GameMode::Menu) source = ScriptSettingSource::ForceNewGame;
if (source == SSS_DEFAULT && _game_mode == GM_MENU) source = SSS_FORCE_NEWGAME;
if (source == ScriptSettingSource::Default) {
if (source == SSS_DEFAULT) {
Company *c = Company::GetIfValid(company);
if (c != nullptr && c->ai_config != nullptr) return c->ai_config.get();
}
auto &config = (source == ScriptSettingSource::ForceNewGame) ? _settings_newgame.script_config.ai[company] : _settings_game.script_config.ai[company];
auto &config = (source == SSS_FORCE_NEWGAME) ? _settings_newgame.script_config.ai[company] : _settings_game.script_config.ai[company];
if (config == nullptr) config = std::make_unique<AIConfig>();
return config.get();
+2 -11
View File
@@ -13,30 +13,21 @@
#include "../script/script_config.hpp"
#include "../company_type.h"
/** AI instantion of script configuration. */
class AIConfig : public ScriptConfig {
public:
/**
* Get the AI configuration of specific company.
* @param company The company to get the configuration for.
* @param source The context, i.e. current / new game mode.
* @return The configuration.
* Get the config of a company.
*/
static AIConfig *GetConfig(CompanyID company, ScriptSettingSource source = ScriptSettingSource::Default);
static AIConfig *GetConfig(CompanyID company, ScriptSettingSource source = SSS_DEFAULT);
AIConfig() :
ScriptConfig()
{}
/**
* Copy constructor.
* @param config The configuration to copy.
*/
AIConfig(const AIConfig &config) :
ScriptConfig(config)
{}
/** @copydoc ScriptConfig::GetInfo. */
class AIInfo *GetInfo() const;
/**
+38 -46
View File
@@ -40,12 +40,12 @@
/* Clients shouldn't start AIs */
if (_networking && !_network_server) return;
AutoRestoreBackup cur_company(_current_company, company);
Backup<CompanyID> cur_company(_current_company, company);
Company *c = Company::Get(company);
AIConfig *config = c->ai_config.get();
if (config == nullptr) {
c->ai_config = std::make_unique<AIConfig>(*AIConfig::GetConfig(company, AIConfig::ScriptSettingSource::ForceCurrentGame));
c->ai_config = std::make_unique<AIConfig>(*AIConfig::GetConfig(company, AIConfig::SSS_FORCE_GAME));
config = c->ai_config.get();
}
@@ -65,18 +65,10 @@
c->ai_instance->LoadOnStack(config->GetToLoadData());
config->SetToLoadData(nullptr);
InvalidateWindowClassesData(WindowClass::ScriptDebug, -1);
return;
}
cur_company.Restore();
/**
* Get the \c PerformanceElement for the AI of the given company.
* @param company The company to get the PerformanceElement for.
* @return The \c PerformanceElement.
*/
static constexpr PerformanceElement GetAIPerformanceElement(CompanyID company)
{
return static_cast<PerformanceElement>(PerformanceElement::AI0 + company);
InvalidateWindowClassesData(WC_SCRIPT_DEBUG, -1);
return;
}
/* static */ void AI::GameLoop()
@@ -89,10 +81,11 @@ static constexpr PerformanceElement GetAIPerformanceElement(CompanyID company)
assert(_settings_game.difficulty.competitor_speed <= 4);
if ((AI::frame_counter & ((1 << (4 - _settings_game.difficulty.competitor_speed)) - 1)) != 0) return;
Backup<CompanyID> cur_company(_current_company);
for (const Company *c : Company::Iterate()) {
if (c->is_ai) {
PerformanceMeasurer framerate(GetAIPerformanceElement(c->index));
AutoRestoreBackup cur_company(_current_company, c->index);
PerformanceMeasurer framerate((PerformanceElement)(PFE_AI0 + c->index));
cur_company.Change(c->index);
c->ai_instance->GameLoop();
/* Occasionally collect garbage; every 255 ticks do one company.
* Effectively collecting garbage once every two months per AI. */
@@ -100,9 +93,10 @@ static constexpr PerformanceElement GetAIPerformanceElement(CompanyID company)
c->ai_instance->CollectGarbage();
}
} else {
PerformanceMeasurer::SetInactive(GetAIPerformanceElement(c->index));
PerformanceMeasurer::SetInactive((PerformanceElement)(PFE_AI0 + c->index));
}
}
cur_company.Restore();
}
/* static */ uint AI::GetTick()
@@ -113,16 +107,18 @@ static constexpr PerformanceElement GetAIPerformanceElement(CompanyID company)
/* static */ void AI::Stop(CompanyID company)
{
if (_networking && !_network_server) return;
PerformanceMeasurer::SetInactive(GetAIPerformanceElement(company));
PerformanceMeasurer::SetInactive((PerformanceElement)(PFE_AI0 + company));
AutoRestoreBackup cur_company(_current_company, company);
Backup<CompanyID> cur_company(_current_company, company);
Company *c = Company::Get(company);
c->ai_instance.reset();
c->ai_info = nullptr;
c->ai_config.reset();
InvalidateWindowClassesData(WindowClass::ScriptDebug, -1);
cur_company.Restore();
InvalidateWindowClassesData(WC_SCRIPT_DEBUG, -1);
}
/* static */ void AI::Pause(CompanyID company)
@@ -132,20 +128,28 @@ static constexpr PerformanceElement GetAIPerformanceElement(CompanyID company)
* for the server owner to unpause the script again. */
if (_network_dedicated) return;
AutoRestoreBackup cur_company(_current_company, company);
Backup<CompanyID> cur_company(_current_company, company);
Company::Get(company)->ai_instance->Pause();
cur_company.Restore();
}
/* static */ void AI::Unpause(CompanyID company)
{
AutoRestoreBackup cur_company(_current_company, company);
Backup<CompanyID> cur_company(_current_company, company);
Company::Get(company)->ai_instance->Unpause();
cur_company.Restore();
}
/* static */ bool AI::IsPaused(CompanyID company)
{
AutoRestoreBackup cur_company(_current_company, company);
return Company::Get(company)->ai_instance->IsPaused();
Backup<CompanyID> cur_company(_current_company, company);
bool paused = Company::Get(company)->ai_instance->IsPaused();
cur_company.Restore();
return paused;
}
/* static */ void AI::KillAll()
@@ -243,8 +247,9 @@ static constexpr PerformanceElement GetAIPerformanceElement(CompanyID company)
}
/* Queue the event */
AutoRestoreBackup cur_company(_current_company, company);
Backup<CompanyID> cur_company(_current_company, company);
Company::Get(_current_company)->ai_instance->InsertEvent(event);
cur_company.Restore();
}
/* static */ void AI::BroadcastNewEvent(ScriptEvent *event, CompanyID skip_company)
@@ -270,8 +275,9 @@ static constexpr PerformanceElement GetAIPerformanceElement(CompanyID company)
/* When doing emergency saving, an AI can be not fully initialised. */
if (c->ai_instance != nullptr) {
AutoRestoreBackup cur_company(_current_company, company);
Backup<CompanyID> cur_company(_current_company, company);
c->ai_instance->Save();
cur_company.Restore();
return;
}
}
@@ -284,9 +290,9 @@ static constexpr PerformanceElement GetAIPerformanceElement(CompanyID company)
AI::scanner_info->GetConsoleList(output_iterator, newest_only);
}
/* static */ void AI::GetConsoleLibraryList(std::back_insert_iterator<std::string> &output_iterator, bool newest_only)
/* static */ void AI::GetConsoleLibraryList(std::back_insert_iterator<std::string> &output_iterator)
{
AI::scanner_library->GetConsoleList(output_iterator, newest_only);
AI::scanner_library->GetConsoleList(output_iterator, true);
}
/* static */ const ScriptInfoList *AI::GetInfoList()
@@ -317,46 +323,32 @@ static constexpr PerformanceElement GetAIPerformanceElement(CompanyID company)
AI::scanner_library->RescanDir();
ResetConfig();
InvalidateWindowData(WindowClass::ScriptList, 0, 1);
SetWindowClassesDirty(WindowClass::ScriptDebug);
InvalidateWindowClassesData(WindowClass::ScriptSettings);
InvalidateWindowData(WC_SCRIPT_LIST, 0, 1);
SetWindowClassesDirty(WC_SCRIPT_DEBUG);
InvalidateWindowClassesData(WC_SCRIPT_SETTINGS);
}
/**
* Check whether we have an AI with the exact characteristics as ci.
* Check whether we have an AI (library) with the exact characteristics as ci.
* @param ci the characteristics to search on (shortname and md5sum)
* @param md5sum whether to check the MD5 checksum
* @return true iff we have an AI matching.
* @return true iff we have an AI (library) matching.
*/
/* static */ bool AI::HasAI(const ContentInfo &ci, bool md5sum)
{
return AI::scanner_info->HasScript(ci, md5sum);
}
/**
* Check whether we have an AI library with the exact characteristics as ci.
* @param ci the characteristics to search on (shortname and md5sum)
* @param md5sum whether to check the MD5 checksum
* @return true iff we have an AI library matching.
*/
/* static */ bool AI::HasAILibrary(const ContentInfo &ci, bool md5sum)
{
return AI::scanner_library->HasScript(ci, md5sum);
}
/**
* Get the scanner info for AIs.
* @return The AI scanner info.
*/
/* static */ AIScannerInfo *AI::GetScannerInfo()
{
return AI::scanner_info.get();
}
/**
* Get the scanner info for AI libraries.
* @return The AI library scanner info.
*/
/* static */ AIScannerLibrary *AI::GetScannerLibrary()
{
return AI::scanner_library.get();
+42 -44
View File
@@ -5,7 +5,7 @@
* 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 <https://www.gnu.org/licenses/old-licenses/gpl-2.0>.
*/
/** @file ai_gui.cpp %Window for configuring the AIs. */
/** @file ai_gui.cpp %Window for configuring the AIs */
#include "../stdafx.h"
#include "../error.h"
@@ -30,51 +30,51 @@
/** Widgets for the configure AI window. */
static constexpr std::initializer_list<NWidgetPart> _nested_ai_config_widgets = {
NWidget(NWID_HORIZONTAL),
NWidget(WWT_CLOSEBOX, Colours::Mauve),
NWidget(WWT_CAPTION, Colours::Mauve), SetStringTip(STR_AI_CONFIG_CAPTION_AI, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
NWidget(WWT_CLOSEBOX, COLOUR_MAUVE),
NWidget(WWT_CAPTION, COLOUR_MAUVE), SetStringTip(STR_AI_CONFIG_CAPTION_AI, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
EndContainer(),
NWidget(WWT_PANEL, Colours::Mauve, WID_AIC_BACKGROUND),
NWidget(WWT_PANEL, COLOUR_MAUVE, WID_AIC_BACKGROUND),
NWidget(NWID_VERTICAL), SetPIP(0, WidgetDimensions::unscaled.vsep_wide, 0), SetPadding(WidgetDimensions::unscaled.sparse),
NWidget(NWID_VERTICAL), SetPIP(0, WidgetDimensions::unscaled.vsep_sparse, 0),
NWidget(NWID_HORIZONTAL), SetPIP(0, WidgetDimensions::unscaled.hsep_wide, 0),
NWidget(NWID_HORIZONTAL, NWidContainerFlag::EqualSize),
NWidget(WWT_PUSHARROWBTN, Colours::Yellow, WID_AIC_DECREASE_NUMBER), SetArrowWidgetTypeTip(ArrowWidgetType::Decrease),
NWidget(WWT_PUSHARROWBTN, Colours::Yellow, WID_AIC_INCREASE_NUMBER), SetArrowWidgetTypeTip(ArrowWidgetType::Increase),
NWidget(WWT_PUSHARROWBTN, COLOUR_YELLOW, WID_AIC_DECREASE_NUMBER), SetArrowWidgetTypeTip(AWV_DECREASE),
NWidget(WWT_PUSHARROWBTN, COLOUR_YELLOW, WID_AIC_INCREASE_NUMBER), SetArrowWidgetTypeTip(AWV_INCREASE),
EndContainer(),
NWidget(WWT_TEXT, Colours::Invalid, WID_AIC_NUMBER), SetFill(1, 0),
NWidget(WWT_TEXT, INVALID_COLOUR, WID_AIC_NUMBER), SetFill(1, 0),
EndContainer(),
NWidget(NWID_HORIZONTAL), SetPIP(0, WidgetDimensions::unscaled.hsep_wide, 0),
NWidget(NWID_HORIZONTAL, NWidContainerFlag::EqualSize),
NWidget(WWT_PUSHARROWBTN, Colours::Yellow, WID_AIC_DECREASE_INTERVAL), SetArrowWidgetTypeTip(ArrowWidgetType::Decrease),
NWidget(WWT_PUSHARROWBTN, Colours::Yellow, WID_AIC_INCREASE_INTERVAL), SetArrowWidgetTypeTip(ArrowWidgetType::Increase),
NWidget(WWT_PUSHARROWBTN, COLOUR_YELLOW, WID_AIC_DECREASE_INTERVAL), SetArrowWidgetTypeTip(AWV_DECREASE),
NWidget(WWT_PUSHARROWBTN, COLOUR_YELLOW, WID_AIC_INCREASE_INTERVAL), SetArrowWidgetTypeTip(AWV_INCREASE),
EndContainer(),
NWidget(WWT_TEXT, Colours::Invalid, WID_AIC_INTERVAL), SetFill(1, 0),
NWidget(WWT_TEXT, INVALID_COLOUR, WID_AIC_INTERVAL), SetFill(1, 0),
EndContainer(),
NWidget(NWID_HORIZONTAL, NWidContainerFlag::EqualSize),
NWidget(WWT_PUSHTXTBTN, Colours::Yellow, WID_AIC_MOVE_UP), SetResize(1, 0), SetFill(1, 0), SetStringTip(STR_AI_CONFIG_MOVE_UP, STR_AI_CONFIG_MOVE_UP_TOOLTIP),
NWidget(WWT_PUSHTXTBTN, Colours::Yellow, WID_AIC_MOVE_DOWN), SetResize(1, 0), SetFill(1, 0), SetStringTip(STR_AI_CONFIG_MOVE_DOWN, STR_AI_CONFIG_MOVE_DOWN_TOOLTIP),
NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_AIC_MOVE_UP), SetResize(1, 0), SetFill(1, 0), SetStringTip(STR_AI_CONFIG_MOVE_UP, STR_AI_CONFIG_MOVE_UP_TOOLTIP),
NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_AIC_MOVE_DOWN), SetResize(1, 0), SetFill(1, 0), SetStringTip(STR_AI_CONFIG_MOVE_DOWN, STR_AI_CONFIG_MOVE_DOWN_TOOLTIP),
EndContainer(),
EndContainer(),
NWidget(WWT_FRAME, Colours::Mauve), SetStringTip(STR_AI_CONFIG_AI), SetPIP(0, WidgetDimensions::unscaled.vsep_sparse, 0),
NWidget(WWT_FRAME, COLOUR_MAUVE), SetStringTip(STR_AI_CONFIG_AI), SetPIP(0, WidgetDimensions::unscaled.vsep_sparse, 0),
NWidget(NWID_HORIZONTAL),
NWidget(WWT_MATRIX, Colours::Mauve, WID_AIC_LIST), SetMinimalSize(288, 112), SetFill(1, 0), SetMatrixDataTip(1, 8, STR_AI_CONFIG_AILIST_TOOLTIP), SetScrollbar(WID_AIC_SCROLLBAR),
NWidget(NWID_VSCROLLBAR, Colours::Mauve, WID_AIC_SCROLLBAR),
NWidget(WWT_MATRIX, COLOUR_MAUVE, WID_AIC_LIST), SetMinimalSize(288, 112), SetFill(1, 0), SetMatrixDataTip(1, 8, STR_AI_CONFIG_AILIST_TOOLTIP), SetScrollbar(WID_AIC_SCROLLBAR),
NWidget(NWID_VSCROLLBAR, COLOUR_MAUVE, WID_AIC_SCROLLBAR),
EndContainer(),
NWidget(WWT_PUSHTXTBTN, Colours::Yellow, WID_AIC_CONFIGURE), SetFill(1, 0), SetStringTip(STR_AI_CONFIG_CONFIGURE, STR_AI_CONFIG_CONFIGURE_TOOLTIP),
NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_AIC_CONFIGURE), SetFill(1, 0), SetStringTip(STR_AI_CONFIG_CONFIGURE, STR_AI_CONFIG_CONFIGURE_TOOLTIP),
EndContainer(),
NWidget(NWID_HORIZONTAL), SetPIP(0, WidgetDimensions::unscaled.hsep_wide, 0),
NWidget(NWID_VERTICAL, NWidContainerFlag::EqualSize),
NWidget(WWT_PUSHTXTBTN, Colours::Yellow, WID_AIC_CHANGE), SetFill(1, 1), SetStringTip(STR_AI_CONFIG_CHANGE_AI, STR_AI_CONFIG_CHANGE_TOOLTIP),
NWidget(WWT_PUSHTXTBTN, Colours::Yellow, WID_AIC_CONTENT_DOWNLOAD), SetFill(1, 1), SetStringTip(STR_INTRO_ONLINE_CONTENT, STR_INTRO_TOOLTIP_ONLINE_CONTENT),
NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_AIC_CHANGE), SetFill(1, 1), SetStringTip(STR_AI_CONFIG_CHANGE_AI, STR_AI_CONFIG_CHANGE_TOOLTIP),
NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_AIC_CONTENT_DOWNLOAD), SetFill(1, 1), SetStringTip(STR_INTRO_ONLINE_CONTENT, STR_INTRO_TOOLTIP_ONLINE_CONTENT),
EndContainer(),
NWidget(NWID_VERTICAL, NWidContainerFlag::EqualSize),
NWidget(NWID_HORIZONTAL, NWidContainerFlag::EqualSize),
NWidget(WWT_PUSHTXTBTN, Colours::Yellow, WID_AIC_OPEN_URL), SetResize(1, 0), SetFill(1, 0), SetStringTip(STR_CONTENT_OPEN_URL, STR_CONTENT_OPEN_URL_TOOLTIP),
NWidget(WWT_PUSHTXTBTN, Colours::Yellow, WID_AIC_TEXTFILE + TextfileType::Readme), SetFill(1, 1), SetResize(1, 0), SetStringTip(STR_TEXTFILE_VIEW_README, STR_TEXTFILE_VIEW_README_TOOLTIP),
NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_AIC_OPEN_URL), SetResize(1, 0), SetFill(1, 0), SetStringTip(STR_CONTENT_OPEN_URL, STR_CONTENT_OPEN_URL_TOOLTIP),
NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_AIC_TEXTFILE + TFT_README), SetFill(1, 1), SetResize(1, 0), SetStringTip(STR_TEXTFILE_VIEW_README, STR_TEXTFILE_VIEW_README_TOOLTIP),
EndContainer(),
NWidget(NWID_HORIZONTAL, NWidContainerFlag::EqualSize),
NWidget(WWT_PUSHTXTBTN, Colours::Yellow, WID_AIC_TEXTFILE + TextfileType::Changelog), SetFill(1, 1), SetResize(1, 0), SetStringTip(STR_TEXTFILE_VIEW_CHANGELOG, STR_TEXTFILE_VIEW_CHANGELOG_TOOLTIP),
NWidget(WWT_PUSHTXTBTN, Colours::Yellow, WID_AIC_TEXTFILE + TextfileType::License), SetFill(1, 1), SetResize(1, 0), SetStringTip(STR_TEXTFILE_VIEW_LICENCE, STR_TEXTFILE_VIEW_LICENCE_TOOLTIP),
NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_AIC_TEXTFILE + TFT_CHANGELOG), SetFill(1, 1), SetResize(1, 0), SetStringTip(STR_TEXTFILE_VIEW_CHANGELOG, STR_TEXTFILE_VIEW_CHANGELOG_TOOLTIP),
NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_AIC_TEXTFILE + TFT_LICENSE), SetFill(1, 1), SetResize(1, 0), SetStringTip(STR_TEXTFILE_VIEW_LICENCE, STR_TEXTFILE_VIEW_LICENCE_TOOLTIP),
EndContainer(),
EndContainer(),
EndContainer(),
@@ -84,8 +84,8 @@ static constexpr std::initializer_list<NWidgetPart> _nested_ai_config_widgets =
/** Window definition for the configure AI window. */
static WindowDesc _ai_config_desc(
WindowPosition::Center, {}, 0, 0,
WindowClass::GameOptions, WindowClass::None,
WDP_CENTER, {}, 0, 0,
WC_GAME_OPTIONS, WC_NONE,
{},
_nested_ai_config_widgets
);
@@ -100,7 +100,7 @@ struct AIConfigWindow : public Window {
AIConfigWindow() : Window(_ai_config_desc)
{
this->InitNested(GameOptionsWindowNumber::AI); // Initializes 'this->line_height' as a side effect.
this->InitNested(WN_GAME_OPTIONS_AI); // Initializes 'this->line_height' as a side effect.
this->vscroll = this->GetScrollbar(WID_AIC_SCROLLBAR);
this->selected_slot = CompanyID::Invalid();
NWidgetCore *nwi = this->GetWidget<NWidgetCore>(WID_AIC_LIST);
@@ -111,8 +111,8 @@ struct AIConfigWindow : public Window {
void Close([[maybe_unused]] int data = 0) override
{
CloseWindowByClass(WindowClass::ScriptList);
CloseWindowByClass(WindowClass::ScriptSettings);
CloseWindowByClass(WC_SCRIPT_LIST);
CloseWindowByClass(WC_SCRIPT_SETTINGS);
this->Window::Close();
}
@@ -141,7 +141,7 @@ struct AIConfigWindow : public Window {
break;
case WID_AIC_LIST:
this->line_height = GetCharacterHeight(FontSize::Normal) + padding.height;
this->line_height = GetCharacterHeight(FS_NORMAL) + padding.height;
fill.height = resize.height = this->line_height;
size.height = 8 * this->line_height;
break;
@@ -155,7 +155,7 @@ struct AIConfigWindow : public Window {
*/
static bool IsEditable(CompanyID slot)
{
if (_game_mode != GameMode::Normal) {
if (_game_mode != GM_NORMAL) {
return slot > 0 && slot < MAX_COMPANIES;
}
return slot < MAX_COMPANIES && !Company::IsValidID(slot);
@@ -168,7 +168,7 @@ struct AIConfigWindow : public Window {
*/
std::string GetSlotText(CompanyID cid) const
{
if ((_game_mode != GameMode::Normal && cid == 0) || (_game_mode == GameMode::Normal && Company::IsValidHumanID(cid))) return GetString(STR_AI_CONFIG_HUMAN_PLAYER);
if ((_game_mode != GM_NORMAL && cid == 0) || (_game_mode == GM_NORMAL && Company::IsValidHumanID(cid))) return GetString(STR_AI_CONFIG_HUMAN_PLAYER);
if (const AIInfo *info = AIConfig::GetConfig(cid)->GetInfo(); info != nullptr) return info->GetName();
return GetString(STR_AI_CONFIG_RANDOM_AI);
}
@@ -181,10 +181,10 @@ struct AIConfigWindow : public Window {
*/
TextColour GetSlotColour(CompanyID cid, CompanyID max_slot) const
{
if (this->selected_slot == cid) return TextColour::White;
if (IsEditable(cid)) return cid < max_slot ? TextColour::Orange : TextColour::Silver;
if (Company::IsValidAiID(cid)) return TextColour::Green;
return TextColour::Silver;
if (this->selected_slot == cid) return TC_WHITE;
if (IsEditable(cid)) return cid < max_slot ? TC_ORANGE : TC_SILVER;
if (Company::IsValidAiID(cid)) return TC_GREEN;
return TC_SILVER;
}
void DrawWidget(const Rect &r, WidgetID widget) const override
@@ -193,7 +193,7 @@ struct AIConfigWindow : public Window {
case WID_AIC_LIST: {
Rect tr = r.Shrink(WidgetDimensions::scaled.matrix);
int max_slot = GetGameSettings().difficulty.max_no_competitors;
if (_game_mode == GameMode::Normal) {
if (_game_mode == GM_NORMAL) {
for (const Company *c : Company::Iterate()) {
if (c->is_ai) max_slot--;
}
@@ -215,7 +215,7 @@ struct AIConfigWindow : public Window {
void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override
{
if (widget >= WID_AIC_TEXTFILE && widget < WID_AIC_TEXTFILE + TextfileType::ContentEnd) {
if (widget >= WID_AIC_TEXTFILE && widget < WID_AIC_TEXTFILE + TFT_CONTENT_END) {
if (this->selected_slot == CompanyID::Invalid() || AIConfig::GetConfig(this->selected_slot) == nullptr) return;
ShowScriptTextfileWindow(this, static_cast<TextfileType>(widget - WID_AIC_TEXTFILE), this->selected_slot);
@@ -258,8 +258,7 @@ struct AIConfigWindow : public Window {
case WID_AIC_MOVE_UP:
if (IsEditable(this->selected_slot) && IsEditable(static_cast<CompanyID>(this->selected_slot - 1))) {
auto it = std::next(std::begin(GetGameSettings().script_config.ai), this->selected_slot.base());
std::swap(*it, *std::prev(it));
std::swap(GetGameSettings().script_config.ai[this->selected_slot], GetGameSettings().script_config.ai[this->selected_slot - 1]);
this->selected_slot = static_cast<CompanyID>(this->selected_slot - 1);
this->vscroll->ScrollTowards(this->selected_slot.base());
this->InvalidateData();
@@ -268,8 +267,7 @@ struct AIConfigWindow : public Window {
case WID_AIC_MOVE_DOWN:
if (IsEditable(this->selected_slot) && IsEditable(static_cast<CompanyID>(this->selected_slot + 1))) {
auto it = std::next(std::begin(GetGameSettings().script_config.ai), this->selected_slot.base());
std::swap(*it, *std::next(it));
std::swap(GetGameSettings().script_config.ai[this->selected_slot], GetGameSettings().script_config.ai[this->selected_slot + 1]);
++this->selected_slot;
this->vscroll->ScrollTowards(this->selected_slot.base());
this->InvalidateData();
@@ -293,9 +291,9 @@ struct AIConfigWindow : public Window {
case WID_AIC_CONTENT_DOWNLOAD:
if (!_network_available) {
ShowErrorMessage(GetEncodedString(STR_NETWORK_ERROR_NOTAVAILABLE), {}, WarningLevel::Error);
ShowErrorMessage(GetEncodedString(STR_NETWORK_ERROR_NOTAVAILABLE), {}, WL_ERROR);
} else {
ShowNetworkContentListWindow(nullptr, ContentType::Ai);
ShowNetworkContentListWindow(nullptr, CONTENT_TYPE_AI);
}
break;
}
@@ -326,7 +324,7 @@ struct AIConfigWindow : public Window {
this->SetWidgetDisabledState(WID_AIC_MOVE_DOWN, !IsEditable(this->selected_slot) || !IsEditable(static_cast<CompanyID>(this->selected_slot + 1)));
this->SetWidgetDisabledState(WID_AIC_OPEN_URL, this->selected_slot == CompanyID::Invalid() || config->GetInfo() == nullptr || config->GetInfo()->GetURL().empty());
for (TextfileType tft : EnumRange(TextfileType::ContentBegin, TextfileType::ContentEnd)) {
for (TextfileType tft = TFT_CONTENT_BEGIN; tft < TFT_CONTENT_END; tft++) {
this->SetWidgetDisabledState(WID_AIC_TEXTFILE + tft, this->selected_slot == CompanyID::Invalid() || !config->GetTextfile(tft, this->selected_slot).has_value());
}
}
@@ -335,7 +333,7 @@ struct AIConfigWindow : public Window {
/** Open the AI config window. */
void ShowAIConfigWindow()
{
CloseWindowByClass(WindowClass::GameOptions);
CloseWindowByClass(WC_GAME_OPTIONS);
new AIConfigWindow();
}
+1 -1
View File
@@ -5,7 +5,7 @@
* 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 <https://www.gnu.org/licenses/old-licenses/gpl-2.0>.
*/
/** @file ai_gui.hpp %Window for configuring the AIs. */
/** @file ai_gui.hpp %Window for configuring the AIs */
#ifndef AI_GUI_HPP
#define AI_GUI_HPP
+2 -3
View File
@@ -5,7 +5,7 @@
* 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 <https://www.gnu.org/licenses/old-licenses/gpl-2.0>.
*/
/** @file ai_info.cpp Implementation of AIInfo and AILibrary. */
/** @file ai_info.cpp Implementation of AIInfo and AILibrary */
#include "../stdafx.h"
@@ -21,7 +21,6 @@
/**
* Check if the API version provided by the AI is supported.
* @param api_version The API version as provided by the AI.
* @return \c true if the given version is supported by this version of OpenTTD.
*/
static bool CheckAPIVersion(const std::string &api_version)
{
@@ -81,7 +80,7 @@ template <> SQInteger PushClassName<AIInfo, ScriptType::AI>(HSQUIRRELVM vm) { sq
if (info->engine->MethodExists(info->SQ_instance, "GetAPIVersion")) {
if (!info->engine->CallStringMethod(info->SQ_instance, "GetAPIVersion", &info->api_version, MAX_GET_OPS)) return SQ_ERROR;
if (!CheckAPIVersion(info->api_version)) {
sq_throwerror(vm, fmt::format("Loading info.nut from ({}.{}): GetAPIVersion returned invalid version", info->GetName(), info->GetVersion()));
Debug(script, 1, "Loading info.nut from ({}.{}): GetAPIVersion returned invalid version", info->GetName(), info->GetVersion());
return SQ_ERROR;
}
} else {
+2 -15
View File
@@ -15,47 +15,38 @@
/** All static information from an AI like name, version, etc. */
class AIInfo : public ScriptInfo {
public:
/** All valid AI API versions, in order. */
static constexpr std::string_view ApiVersions[]{ "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", "14", "15", "16" };
/* All valid AI API versions, in order. */
static constexpr std::string_view ApiVersions[]{ "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", "14", "15" };
AIInfo();
/**
* Register the functions of this class.
* @param engine The engine to register to.
*/
static void RegisterAPI(Squirrel &engine);
/**
* Create an AI, using this AIInfo as start-template.
* @param vm The virtual machine to push the instance to.
* @return The number of stack places occupied.
*/
static SQInteger Constructor(HSQUIRRELVM vm);
/**
* Create a dummy-AI.
* @param vm The virtual machine to push the instance to.
* @return The number of stack places occupied.
*/
static SQInteger DummyConstructor(HSQUIRRELVM vm);
/**
* Check if we can start this AI.
* @param version The version to check.
* @return \c true if this script can load the data from that version.
*/
bool CanLoadFromVersion(int version) const;
/**
* Use this AI as a random AI.
* @return \c true when it can be used as random AI, \c false when the user must specifically request it.
*/
bool UseAsRandomAI() const { return this->use_as_random; }
/**
* Get the API version this AI is written for.
* @return The API version.
*/
const std::string &GetAPIVersion() const { return this->api_version; }
@@ -72,20 +63,16 @@ public:
/**
* Register the functions of this class.
* @param engine The engine to register to.
*/
static void RegisterAPI(Squirrel &engine);
/**
* Create an AI, using this AIInfo as start-template.
* @param vm The virtual machine to push the instance to.
* @return The number of stack places occupied.
*/
static SQInteger Constructor(HSQUIRRELVM vm);
/**
* Get the category this library is in.
* @return The category.
*/
const std::string &GetCategory() const { return this->category; }
+4 -4
View File
@@ -55,7 +55,7 @@ void AIInstance::RegisterAPI()
/* Register all classes */
SQAI_RegisterAll(*this->engine);
if (!this->LoadCompatibilityScripts(Subdirectory::Ai, AIInfo::ApiVersions)) this->Died();
if (!this->LoadCompatibilityScripts(AI_DIR, AIInfo::ApiVersions)) this->Died();
}
void AIInstance::Died()
@@ -63,16 +63,16 @@ void AIInstance::Died()
ScriptInstance::Died();
/* Intro is not supposed to use AI, but it may have 'dummy' AI which instant dies. */
if (_game_mode == GameMode::Menu) return;
if (_game_mode == GM_MENU) return;
/* Don't show errors while loading savegame. They will be shown at end of loading anyway. */
if (_switch_mode != SwitchMode::None) return;
if (_switch_mode != SM_NONE) return;
ShowScriptDebugWindow(_current_company);
const AIInfo *info = AIConfig::GetConfig(_current_company)->GetInfo();
if (info != nullptr) {
ShowErrorMessage(GetEncodedString(STR_ERROR_AI_PLEASE_REPORT_CRASH), {}, WarningLevel::Warning);
ShowErrorMessage(GetEncodedString(STR_ERROR_AI_PLEASE_REPORT_CRASH), {}, WL_WARNING);
if (!info->GetURL().empty()) {
ScriptLog::Info("Please report the error to the following URL:");
+2 -2
View File
@@ -5,7 +5,7 @@
* 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 <https://www.gnu.org/licenses/old-licenses/gpl-2.0>.
*/
/** @file ai_scanner.cpp Allows scanning AI scripts. */
/** @file ai_scanner.cpp allows scanning AI scripts */
#include "../stdafx.h"
#include <ranges>
@@ -53,7 +53,7 @@ void AIScannerInfo::RegisterAPI(class Squirrel &engine)
AIInfo *AIScannerInfo::SelectRandomAI() const
{
if (_game_mode == GameMode::Menu) {
if (_game_mode == GM_MENU) {
Debug(script, 0, "The intro game should not use AI, loading 'dummy' AI.");
return this->info_dummy.get();
}
+4 -7
View File
@@ -5,18 +5,17 @@
* 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 <https://www.gnu.org/licenses/old-licenses/gpl-2.0>.
*/
/** @file ai_scanner.hpp Declarations of the class for AI scanner. */
/** @file ai_scanner.hpp declarations of the class for AI scanner */
#ifndef AI_SCANNER_HPP
#define AI_SCANNER_HPP
#include "../script/script_scanner.hpp"
/** AI instantiation of a ScriptScanner. */
class AIScannerInfo : public ScriptScanner {
public:
AIScannerInfo();
~AIScannerInfo() override;
~AIScannerInfo();
void Initialize() override;
@@ -37,14 +36,13 @@ public:
/**
* Set the Dummy AI.
* @param info The new dummy AI.
*/
void SetDummyAI(std::unique_ptr<class AIInfo> &&info);
protected:
std::string GetScriptName(ScriptInfo &info) override;
std::string_view GetFileName() const override { return PATHSEP "info.nut"; }
Subdirectory GetDirectory() const override { return Subdirectory::Ai; }
Subdirectory GetDirectory() const override { return AI_DIR; }
std::string_view GetScannerName() const override { return "AIs"; }
void RegisterAPI(class Squirrel &engine) override;
@@ -52,7 +50,6 @@ private:
std::unique_ptr<AIInfo> info_dummy; ///< The dummy AI.
};
/** AI instantiation of a ScriptScanner for libraries. */
class AIScannerLibrary : public ScriptScanner {
public:
void Initialize() override;
@@ -68,7 +65,7 @@ public:
protected:
std::string GetScriptName(ScriptInfo &info) override;
std::string_view GetFileName() const override { return PATHSEP "library.nut"; }
Subdirectory GetDirectory() const override { return Subdirectory::AiLibrary; }
Subdirectory GetDirectory() const override { return AI_LIBRARY_DIR; }
std::string_view GetScannerName() const override { return "AI Libraries"; }
void RegisterAPI(class Squirrel &engine) override;
};
+12 -14
View File
@@ -33,21 +33,18 @@ enum AircraftSubType : uint8_t {
};
/** Flags for air vehicles; shared with disaster vehicles. */
enum class VehicleAirFlag : uint8_t {
DestinationTooFar = 0, ///< Next destination is too far away.
enum AirVehicleFlags : uint8_t {
VAF_DEST_TOO_FAR = 0, ///< Next destination is too far away.
/* The next two flags are to prevent stair climbing of the aircraft. The idea is that the aircraft
* will ascend or descend multiple flight levels at a time instead of following the contours of the
* landscape at a fixed altitude. This only has effect when there are more than 15 height levels. */
InMaximumHeightCorrection = 1, ///< The vehicle is currently lowering its altitude because it hit the upper bound.
InMinimumHeightCorrection = 2, ///< The vehicle is currently raising its altitude because it hit the lower bound.
VAF_IN_MAX_HEIGHT_CORRECTION = 1, ///< The vehicle is currently lowering its altitude because it hit the upper bound.
VAF_IN_MIN_HEIGHT_CORRECTION = 2, ///< The vehicle is currently raising its altitude because it hit the lower bound.
HelicopterDirectDescent = 3, ///< The helicopter is descending directly at its destination (helipad or in front of hangar)
VAF_HELI_DIRECT_DESCENT = 3, ///< The helicopter is descending directly at its destination (helipad or in front of hangar)
};
/** Bitset of \c VehicleAirFlag elements. */
using VehicleAirFlags = EnumBitSet<VehicleAirFlag, uint8_t>;
static const int ROTOR_Z_OFFSET = 5; ///< Z Offset between helicopter- and rotorsprite.
void HandleAircraftEnterHangar(Aircraft *v);
@@ -72,26 +69,27 @@ struct AircraftCache {
/**
* Aircraft, helicopters, rotors and their shadows belong to this class.
*/
struct Aircraft final : public SpecializedVehicle<Aircraft, VehicleType::Aircraft> {
struct Aircraft final : public SpecializedVehicle<Aircraft, VEH_AIRCRAFT> {
uint16_t crashed_counter = 0; ///< Timer for handling crash animations.
uint8_t pos = 0; ///< Next desired position of the aircraft.
uint8_t previous_pos = 0; ///< Previous desired position of the aircraft.
StationID targetairport = StationID::Invalid(); ///< Airport to go to next.
uint8_t state = 0; ///< State of the airport. @see AirportMovementStates
Direction last_direction = Direction::Invalid;
Direction last_direction = INVALID_DIR;
uint8_t number_consecutive_turns = 0; ///< Protection to prevent the aircraft of making a lot of turns in order to reach a specific point.
uint8_t turn_counter = 0; ///< Ticks between each turn to prevent > 45 degree turns.
VehicleAirFlags flags{}; ///< Aircraft flags. @see VehicleAirFlags
uint8_t flags = 0; ///< Aircraft flags. @see AirVehicleFlags
AircraftCache acache{};
Aircraft(VehicleID index) : SpecializedVehicleBase(index) {}
/** We don't want GCC to zero our struct! It already is zeroed and has an index! */
Aircraft() : SpecializedVehicleBase() {}
/** We want to 'destruct' the right class. */
~Aircraft() override { this->PreDestructor(); }
virtual ~Aircraft() { this->PreDestructor(); }
void MarkDirty() override;
void UpdateDeltaXY() override;
ExpensesType GetExpenseType(bool income) const override { return income ? ExpensesType::AircraftRevenue : ExpensesType::AircraftRun; }
ExpensesType GetExpenseType(bool income) const override { return income ? EXPENSES_AIRCRAFT_REVENUE : EXPENSES_AIRCRAFT_RUN; }
bool IsPrimaryVehicle() const override { return this->IsNormalAircraft(); }
void GetImage(Direction direction, EngineImageType image_type, VehicleSpriteSeq *result) const override;
int GetDisplaySpeed() const override { return this->cur_speed; }
+121 -127
View File
@@ -5,7 +5,10 @@
* 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 <https://www.gnu.org/licenses/old-licenses/gpl-2.0>.
*/
/** @file aircraft_cmd.cpp This file deals with aircraft and airport movements functionalities. */
/**
* @file aircraft_cmd.cpp
* This file deals with aircraft and airport movements functionalities
*/
#include "stdafx.h"
#include "aircraft.h"
@@ -93,9 +96,8 @@ static const SpriteID _aircraft_sprite[] = {
0x0EBD, 0x0EC5
};
/** @copydoc IsValidImageIndex */
template <>
bool IsValidImageIndex<VehicleType::Aircraft>(uint8_t image_index)
bool IsValidImageIndex<VEH_AIRCRAFT>(uint8_t image_index)
{
return image_index < lengthof(_aircraft_sprite);
}
@@ -119,7 +121,7 @@ static StationID FindNearestHangar(const Aircraft *v)
{
uint best = 0;
StationID index = StationID::Invalid();
TileIndex vtile = TileVirtXYClampedToMap(v->x_pos, v->y_pos);
TileIndex vtile = TileVirtXY(v->x_pos, v->y_pos);
const AircraftVehicleInfo *avi = AircraftVehInfo(v->engine_type);
uint max_range = v->acache.cached_max_range_sqr;
@@ -178,8 +180,8 @@ void Aircraft::GetImage(Direction direction, EngineImageType image_type, Vehicle
spritenum = this->GetEngine()->original_image_index;
}
assert(IsValidImageIndex<VehicleType::Aircraft>(spritenum));
result->Set(to_underlying(direction) + _aircraft_sprite[spritenum]);
assert(IsValidImageIndex<VEH_AIRCRAFT>(spritenum));
result->Set(direction + _aircraft_sprite[spritenum]);
}
void GetRotorImage(const Aircraft *v, EngineImageType image_type, VehicleSpriteSeq *result)
@@ -202,14 +204,14 @@ static void GetAircraftIcon(EngineID engine, EngineImageType image_type, Vehicle
uint8_t spritenum = e->VehInfo<AircraftVehicleInfo>().image_index;
if (IsCustomVehicleSpriteNum(spritenum)) {
GetCustomVehicleIcon(engine, Direction::W, image_type, result);
GetCustomVehicleIcon(engine, DIR_W, image_type, result);
if (result->IsValid()) return;
spritenum = e->original_image_index;
}
assert(IsValidImageIndex<VehicleType::Aircraft>(spritenum));
result->Set(to_underlying(Direction::W) + _aircraft_sprite[spritenum]);
assert(IsValidImageIndex<VEH_AIRCRAFT>(spritenum));
result->Set(DIR_W + _aircraft_sprite[spritenum]);
}
void DrawAircraftEngine(int left, int right, int preferred_x, int y, EngineID engine, PaletteID pal, EngineImageType image_type)
@@ -276,11 +278,11 @@ CommandCost CmdBuildAircraft(DoCommandFlags flags, TileIndex tile, const Engine
tile = st->airport.GetHangarTile(st->airport.GetHangarNum(tile));
if (flags.Test(DoCommandFlag::Execute)) {
Aircraft *v = Aircraft::Create(); // aircraft
Aircraft *u = Aircraft::Create(); // shadow
Aircraft *v = new Aircraft(); // aircraft
Aircraft *u = new Aircraft(); // shadow
*ret = v;
v->direction = Direction::SE;
v->direction = DIR_SE;
v->owner = u->owner = _current_company;
@@ -367,9 +369,9 @@ CommandCost CmdBuildAircraft(DoCommandFlags flags, TileIndex tile, const Engine
/* Aircraft with 3 vehicles (chopper)? */
if (v->subtype == AIR_HELICOPTER) {
Aircraft *w = Aircraft::Create();
Aircraft *w = new Aircraft();
w->engine_type = e->index;
w->direction = Direction::N;
w->direction = DIR_N;
w->owner = _current_company;
w->x_pos = v->x_pos;
w->y_pos = v->y_pos;
@@ -427,10 +429,10 @@ static void CheckIfAircraftNeedsService(Aircraft *v)
/* only goto depot if the target airport has a depot */
if (st->airport.HasHangar() && CanVehicleUseStation(v, st)) {
v->current_order.MakeGoToDepot(st->index, OrderDepotTypeFlag::Service);
SetWindowWidgetDirty(WindowClass::VehicleView, v->index, WID_VV_START_STOP);
SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, WID_VV_START_STOP);
} else if (v->current_order.IsType(OT_GOTO_DEPOT)) {
v->current_order.MakeDummy();
SetWindowWidgetDirty(WindowClass::VehicleView, v->index, WID_VV_START_STOP);
SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, WID_VV_START_STOP);
}
}
@@ -438,7 +440,7 @@ Money Aircraft::GetRunningCost() const
{
const Engine *e = this->GetEngine();
uint cost_factor = GetVehicleProperty(this, PROP_AIRCRAFT_RUNNING_COST_FACTOR, e->VehInfo<AircraftVehicleInfo>().running_cost);
return GetPrice(Price::RunningAircraft, cost_factor, e->GetGRF());
return GetPrice(PR_RUNNING_AIRCRAFT, cost_factor, e->GetGRF());
}
/** Calendar day handler */
@@ -463,15 +465,15 @@ void Aircraft::OnNewEconomyDay()
if (this->running_ticks == 0) return;
CommandCost cost(ExpensesType::AircraftRun, this->GetRunningCost() * this->running_ticks / (CalendarTime::DAYS_IN_YEAR * Ticks::DAY_TICKS));
CommandCost cost(EXPENSES_AIRCRAFT_RUN, this->GetRunningCost() * this->running_ticks / (CalendarTime::DAYS_IN_YEAR * Ticks::DAY_TICKS));
this->profit_this_year -= cost.GetCost();
this->running_ticks = 0;
SubtractMoneyFromCompanyFract(this->owner, cost);
SetWindowDirty(WindowClass::VehicleDetails, this->index);
SetWindowClassesDirty(WindowClass::AircraftList);
SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
SetWindowClassesDirty(WC_AIRCRAFT_LIST);
}
static void HelicopterTickHandler(Aircraft *v)
@@ -504,13 +506,13 @@ static void HelicopterTickHandler(Aircraft *v)
VehicleSpriteSeq seq;
if (spd == 0) {
u->state = HRS_ROTOR_STOPPED;
GetRotorImage(v, EngineImageType::OnMap, &seq);
GetRotorImage(v, EIT_ON_MAP, &seq);
if (u->sprite_cache.sprite_seq == seq) return;
} else if (tick >= spd) {
u->tick_counter = 0;
u->state++;
if (u->state > HRS_ROTOR_MOVING_3) u->state = HRS_ROTOR_MOVING_1;
GetRotorImage(v, EngineImageType::OnMap, &seq);
GetRotorImage(v, EIT_ON_MAP, &seq);
} else {
return;
}
@@ -536,7 +538,7 @@ void SetAircraftPosition(Aircraft *v, int x, int y, int z)
v->UpdatePosition();
v->UpdateViewport(true, false);
if (v->subtype == AIR_HELICOPTER) {
GetRotorImage(v, EngineImageType::OnMap, &v->Next()->Next()->sprite_cache.sprite_seq);
GetRotorImage(v, EIT_ON_MAP, &v->Next()->Next()->sprite_cache.sprite_seq);
}
Aircraft *u = v->Next();
@@ -616,7 +618,7 @@ void UpdateAircraftCache(Aircraft *v, bool update_range)
/* Update aircraft range. */
if (update_range) {
v->acache.cached_max_range = Engine::Get(v->engine_type)->GetRange();
v->acache.cached_max_range = GetVehicleProperty(v, PROP_AIRCRAFT_RANGE, AircraftVehInfo(v->engine_type)->max_range);
/* Squared it now so we don't have to do it later all the time. */
v->acache.cached_max_range_sqr = v->acache.cached_max_range * v->acache.cached_max_range;
}
@@ -683,7 +685,7 @@ static int UpdateAircraftSpeed(Aircraft *v, uint speed_limit = SPEED_LIMIT_NONE,
/* updates statusbar only if speed have changed to save CPU time */
if (spd != v->cur_speed) {
v->cur_speed = spd;
SetWindowWidgetDirty(WindowClass::VehicleView, v->index, WID_VV_START_STOP);
SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, WID_VV_START_STOP);
}
/* Adjust distance moved by plane speed setting */
@@ -724,7 +726,7 @@ int GetTileHeightBelowAircraft(const Vehicle *v)
void GetAircraftFlightLevelBounds(const Vehicle *v, int *min_level, int *max_level)
{
int base_altitude = GetTileHeightBelowAircraft(v);
if (v->type == VehicleType::Aircraft && Aircraft::From(v)->subtype == AIR_HELICOPTER) {
if (v->type == VEH_AIRCRAFT && Aircraft::From(v)->subtype == AIR_HELICOPTER) {
base_altitude += HELICOPTER_HOLD_MAX_FLYING_ALTITUDE - PLANE_HOLD_MAX_FLYING_ALTITUDE;
}
@@ -732,10 +734,10 @@ void GetAircraftFlightLevelBounds(const Vehicle *v, int *min_level, int *max_lev
* other by providing them with vertical separation
*/
switch (v->direction) {
case Direction::N:
case Direction::NE:
case Direction::E:
case Direction::SE:
case DIR_N:
case DIR_NE:
case DIR_E:
case DIR_SE:
base_altitude += 10;
break;
@@ -779,24 +781,24 @@ int GetAircraftFlightLevel(T *v, bool takeoff)
int z = v->z_pos;
if (z < aircraft_min_altitude ||
(v->flags.Test(VehicleAirFlag::InMinimumHeightCorrection) && z < aircraft_middle_altitude)) {
(HasBit(v->flags, VAF_IN_MIN_HEIGHT_CORRECTION) && z < aircraft_middle_altitude)) {
/* Ascend. And don't fly into that mountain right ahead.
* And avoid our aircraft become a stairclimber, so if we start
* correcting altitude, then we stop correction not too early. */
v->flags.Set(VehicleAirFlag::InMinimumHeightCorrection);
SetBit(v->flags, VAF_IN_MIN_HEIGHT_CORRECTION);
z += takeoff ? 2 : 1;
} else if (!takeoff && (z > aircraft_max_altitude ||
(v->flags.Test(VehicleAirFlag::InMaximumHeightCorrection) && z > aircraft_middle_altitude))) {
(HasBit(v->flags, VAF_IN_MAX_HEIGHT_CORRECTION) && z > aircraft_middle_altitude))) {
/* Descend lower. You are an aircraft, not an space ship.
* And again, don't stop correcting altitude too early. */
v->flags.Set(VehicleAirFlag::InMaximumHeightCorrection);
SetBit(v->flags, VAF_IN_MAX_HEIGHT_CORRECTION);
z--;
} else if (v->flags.Test(VehicleAirFlag::InMinimumHeightCorrection) && z >= aircraft_middle_altitude) {
} else if (HasBit(v->flags, VAF_IN_MIN_HEIGHT_CORRECTION) && z >= aircraft_middle_altitude) {
/* Now, we have corrected altitude enough. */
v->flags.Reset(VehicleAirFlag::InMinimumHeightCorrection);
} else if (v->flags.Test(VehicleAirFlag::InMaximumHeightCorrection) && z <= aircraft_middle_altitude) {
ClrBit(v->flags, VAF_IN_MIN_HEIGHT_CORRECTION);
} else if (HasBit(v->flags, VAF_IN_MAX_HEIGHT_CORRECTION) && z <= aircraft_middle_altitude) {
/* Now, we have corrected altitude enough. */
v->flags.Reset(VehicleAirFlag::InMaximumHeightCorrection);
ClrBit(v->flags, VAF_IN_MAX_HEIGHT_CORRECTION);
}
return z;
@@ -841,13 +843,13 @@ static uint8_t AircraftGetEntryPoint(const Aircraft *v, const AirportFTAClass *a
DiagDirection dir;
if (abs(delta_y) < abs(delta_x)) {
/* We are northeast or southwest of the airport */
dir = delta_x < 0 ? DiagDirection::NE : DiagDirection::SW;
dir = delta_x < 0 ? DIAGDIR_NE : DIAGDIR_SW;
} else {
/* We are northwest or southeast of the airport */
dir = delta_y < 0 ? DiagDirection::NW : DiagDirection::SE;
dir = delta_y < 0 ? DIAGDIR_NW : DIAGDIR_SE;
}
dir = ChangeDiagDir(dir, DiagDirDifference(DiagDirection::NE, DirToDiagDir(rotation)));
return apc->entry_points[to_underlying(dir)];
dir = ChangeDiagDir(dir, DiagDirDifference(DIAGDIR_NE, DirToDiagDir(rotation)));
return apc->entry_points[dir];
}
@@ -866,7 +868,7 @@ static bool AircraftController(Aircraft *v)
const Station *st = Station::GetIfValid(v->targetairport);
/* INVALID_TILE if there is no station */
TileIndex tile = INVALID_TILE;
Direction rotation = Direction::N;
Direction rotation = DIR_N;
uint size_x = 1, size_y = 1;
if (st != nullptr) {
if (st->airport.tile != INVALID_TILE) {
@@ -885,7 +887,7 @@ static bool AircraftController(Aircraft *v)
if (st == nullptr || st->airport.tile == INVALID_TILE) {
/* Jump into our "holding pattern" state machine if possible */
if (v->pos >= afc->nofelements) {
v->pos = v->previous_pos = AircraftGetEntryPoint(v, afc, Direction::N);
v->pos = v->previous_pos = AircraftGetEntryPoint(v, afc, DIR_N);
} else if (v->targetairport != v->current_order.GetDestination()) {
/* If not possible, just get out of here fast */
v->state = FLYING;
@@ -941,7 +943,7 @@ static bool AircraftController(Aircraft *v)
/* Helicopter landing. */
if (amd.flags.Test(AirportMovingDataFlag::HeliLower)) {
v->flags.Set(VehicleAirFlag::HelicopterDirectDescent);
SetBit(v->flags, VAF_HELI_DIRECT_DESCENT);
if (st == nullptr) {
v->state = FLYING;
@@ -968,7 +970,7 @@ static bool AircraftController(Aircraft *v)
/* Increase speed of rotors. When speed is 80, we've landed. */
if (u->cur_speed >= 80) {
v->flags.Reset(VehicleAirFlag::HelicopterDirectDescent);
ClrBit(v->flags, VAF_HELI_DIRECT_DESCENT);
return true;
}
u->cur_speed += 4;
@@ -997,14 +999,14 @@ static bool AircraftController(Aircraft *v)
DirDiff dirdiff = DirDifference(amd.direction, v->direction);
/* if distance is 0, and plane points in right direction, no point in calling
* UpdateAircraftSpeed(). So do it only afterwards */
if (dirdiff == DirDiff::Same) {
if (dirdiff == DIRDIFF_SAME) {
v->cur_speed = 0;
return true;
}
if (!UpdateAircraftSpeed(v, SPEED_LIMIT_TAXI)) return false;
v->direction = ChangeDir(v->direction, LimitDirDiff(dirdiff));
v->direction = ChangeDir(v->direction, dirdiff > DIRDIFF_REVERSE ? DIRDIFF_45LEFT : DIRDIFF_45RIGHT);
v->cur_speed >>= 1;
SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos);
@@ -1182,7 +1184,6 @@ static bool AircraftController(Aircraft *v)
/**
* Handle crashed aircraft \a v.
* @param v Crashed aircraft.
* @return \c false iff the aircraft has been deleted.
*/
static bool HandleCrashedAircraft(Aircraft *v)
{
@@ -1207,7 +1208,7 @@ static bool HandleCrashedAircraft(Aircraft *v)
uint32_t r;
if (Chance16R(1, 32, r)) {
static const DirDiff delta[] = {
DirDiff::Left45, DirDiff::Same, DirDiff::Same, DirDiff::Right45
DIRDIFF_45LEFT, DIRDIFF_SAME, DIRDIFF_SAME, DIRDIFF_45RIGHT
};
v->direction = ChangeDir(v->direction, delta[GB(r, 16, 2)]);
@@ -1223,7 +1224,7 @@ static bool HandleCrashedAircraft(Aircraft *v)
/* remove rubble of crashed airplane */
/* clear runway-in on all airports, set by crashing plane
* small airports use AirportBlock::AirportBusy, city airports use AirportBlock::RunwayInOut, etc.
* small airports use AIRPORT_BUSY, city airports use AirportBlock::RunwayInOut, etc.
* but they all share the same number */
if (st != nullptr) {
st->airport.blocks.Reset(AirportBlock::RunwayIn);
@@ -1247,16 +1248,19 @@ static bool HandleCrashedAircraft(Aircraft *v)
*/
static void HandleAircraftSmoke(Aircraft *v, bool mode)
{
static constexpr DirectionIndexArray<Coord2D<int8_t>> smoke_pos{{{
{ 5, 5},
{ 6, 0},
{ 5, -5},
{ 0, -6},
{-5, -5},
{-6, 0},
{-5, 5},
{ 0, 6},
}}};
static const struct {
int8_t x;
int8_t y;
} smoke_pos[] = {
{ 5, 5 },
{ 6, 0 },
{ 5, -5 },
{ 0, -6 },
{ -5, -5 },
{ -6, 0 },
{ -5, 5 },
{ 0, 6 }
};
if (!v->vehstatus.Test(VehState::AircraftBroken)) return;
@@ -1297,8 +1301,9 @@ void HandleMissingAircraftOrders(Aircraft *v)
*/
const Station *st = GetTargetAirportIfValid(v);
if (st == nullptr) {
AutoRestoreBackup cur_company(_current_company, v->owner);
CommandCost ret = Command<Commands::SendVehicleToDepot>::Do(DoCommandFlag::Execute, v->index, DepotCommandFlag{}, {});
Backup<CompanyID> cur_company(_current_company, v->owner);
CommandCost ret = Command<CMD_SEND_VEHICLE_TO_DEPOT>::Do(DoCommandFlag::Execute, v->index, DepotCommandFlag{}, {});
cur_company.Restore();
if (ret.Failed()) CrashAirplane(v);
} else if (!v->current_order.IsType(OT_GOTO_DEPOT)) {
@@ -1323,7 +1328,7 @@ void Aircraft::MarkDirty()
this->colourmap = PAL_NONE;
this->UpdateViewport(true, false);
if (this->subtype == AIR_HELICOPTER) {
GetRotorImage(this, EngineImageType::OnMap, &this->Next()->Next()->sprite_cache.sprite_seq);
GetRotorImage(this, EIT_ON_MAP, &this->Next()->Next()->sprite_cache.sprite_seq);
}
}
@@ -1349,7 +1354,7 @@ static void CrashAirplane(Aircraft *v)
v->cargo.Truncate();
v->Next()->cargo.Truncate();
const Station *st = GetTargetAirportIfValid(v);
TileIndex vt = TileVirtXYClampedToMap(v->x_pos, v->y_pos);
TileIndex vt = TileVirtXY(v->x_pos, v->y_pos);
EncodedString headline;
if (st == nullptr) {
@@ -1415,8 +1420,8 @@ static void AircraftEntersTerminal(Aircraft *v)
v->last_station_visited = v->targetairport;
/* Check if station was ever visited before */
if (!st->had_vehicle_of_type.Test(StationVehicleType::Aircraft)) {
st->had_vehicle_of_type.Set(StationVehicleType::Aircraft);
if (!(st->had_vehicle_of_type & HVOT_AIRCRAFT)) {
st->had_vehicle_of_type |= HVOT_AIRCRAFT;
/* show newsitem of celebrating citizens */
AddVehicleNewsItem(
GetEncodedString(STR_NEWS_FIRST_AIRCRAFT_ARRIVAL, st->index),
@@ -1451,10 +1456,7 @@ static void AircraftLandAirplane(Aircraft *v)
}
/**
* Set the right pos when heading to other airports after takeoff
* @param v The aircraft to consider.
*/
/** set the right pos when heading to other airports after takeoff */
void AircraftNextAirportPos_and_Order(Aircraft *v)
{
if (v->current_order.IsType(OT_GOTO_STATION) || v->current_order.IsType(OT_GOTO_DEPOT)) {
@@ -1463,7 +1465,7 @@ void AircraftNextAirportPos_and_Order(Aircraft *v)
const Station *st = GetTargetAirportIfValid(v);
const AirportFTAClass *apc = st == nullptr ? GetAirport(AT_DUMMY) : st->airport.GetFTA();
Direction rotation = st == nullptr ? Direction::N : st->airport.rotation;
Direction rotation = st == nullptr ? DIR_N : st->airport.rotation;
v->pos = v->previous_pos = AircraftGetEntryPoint(v, apc, rotation);
}
@@ -1497,29 +1499,35 @@ void AircraftLeaveHangar(Aircraft *v, Direction exit_dir)
VehicleServiceInDepot(v);
v->LeaveUnbunchingDepot();
SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos);
InvalidateWindowData(WindowClass::VehicleDepot, v->tile);
SetWindowClassesDirty(WindowClass::AircraftList);
InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
SetWindowClassesDirty(WC_AIRCRAFT_LIST);
}
////////////////////////////////////////////////////////////////////////////////
/////////////////// AIRCRAFT MOVEMENT SCHEME ////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/** Aircraft arrives at the terminal. @copydoc AircraftStateHandler */
static void AircraftEventHandler_EnterTerminal(Aircraft *v, const AirportFTAClass *apc)
{
AircraftEntersTerminal(v);
v->state = apc->layout[v->pos].heading;
}
/** Aircraft arrived in an airport hangar. @copydoc AircraftStateHandler */
/**
* Aircraft arrived in an airport hangar.
* @param v Aircraft in the hangar.
* @param apc Airport description containing the hangar.
*/
static void AircraftEventHandler_EnterHangar(Aircraft *v, const AirportFTAClass *apc)
{
VehicleEnterDepot(v);
v->state = apc->layout[v->pos].heading;
}
/** Handle aircraft movement/decision making in an airport hangar. @copydoc AircraftStateHandler */
/**
* Handle aircraft movement/decision making in an airport hangar.
* @param v Aircraft in the hangar.
* @param apc Airport description containing the hangar.
*/
static void AircraftEventHandler_InHangar(Aircraft *v, const AirportFTAClass *apc)
{
/* if we just arrived, execute EnterHangar first */
@@ -1568,7 +1576,7 @@ static void AircraftEventHandler_InHangar(Aircraft *v, const AirportFTAClass *ap
AirportMove(v, apc);
}
/** At one of the Airport's Terminals. @copydoc AircraftStateHandler */
/** At one of the Airport's Terminals */
static void AircraftEventHandler_AtTerminal(Aircraft *v, const AirportFTAClass *apc)
{
/* if we just arrived, execute EnterTerminal first */
@@ -1583,7 +1591,7 @@ static void AircraftEventHandler_AtTerminal(Aircraft *v, const AirportFTAClass *
v->date_of_last_service_newgrf = TimerGameCalendar::date;
v->breakdowns_since_last_service = 0;
v->reliability = v->GetEngine()->reliability;
SetWindowDirty(WindowClass::VehicleDetails, v->index);
SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
}
}
return;
@@ -1623,30 +1631,31 @@ static void AircraftEventHandler_AtTerminal(Aircraft *v, const AirportFTAClass *
AirportMove(v, apc);
}
/** Aircraft is taking off (rolling). @copydoc AircraftStateHandler */
static void AircraftEventHandler_TakeOff(Aircraft *v, [[maybe_unused]] const AirportFTAClass *apc)
static void AircraftEventHandler_General(Aircraft *, const AirportFTAClass *)
{
FatalError("OK, you shouldn't be here, check your Airport Scheme!");
}
static void AircraftEventHandler_TakeOff(Aircraft *v, const AirportFTAClass *)
{
PlayAircraftSound(v); // play takeoffsound for airplanes
v->state = STARTTAKEOFF;
}
/** Aircraft is taking off (rotation). @copydoc AircraftStateHandler */
static void AircraftEventHandler_StartTakeOff(Aircraft *v, [[maybe_unused]] const AirportFTAClass *apc)
static void AircraftEventHandler_StartTakeOff(Aircraft *v, const AirportFTAClass *)
{
v->state = ENDTAKEOFF;
v->UpdateDeltaXY();
}
/** Aircraft has taken off. @copydoc AircraftStateHandler */
static void AircraftEventHandler_EndTakeOff(Aircraft *v, [[maybe_unused]] const AirportFTAClass *apc)
static void AircraftEventHandler_EndTakeOff(Aircraft *v, const AirportFTAClass *)
{
v->state = FLYING;
/* get the next position to go to, differs per airport */
AircraftNextAirportPos_and_Order(v);
}
/** Helicopter takes off. @copydoc AircraftStateHandler */
static void AircraftEventHandler_HeliTakeOff(Aircraft *v, [[maybe_unused]] const AirportFTAClass *apc)
static void AircraftEventHandler_HeliTakeOff(Aircraft *v, const AirportFTAClass *)
{
v->state = FLYING;
v->UpdateDeltaXY();
@@ -1656,12 +1665,12 @@ static void AircraftEventHandler_HeliTakeOff(Aircraft *v, [[maybe_unused]] const
/* Send the helicopter to a hangar if needed for replacement */
if (v->NeedsAutomaticServicing()) {
AutoRestoreBackup cur_company(_current_company, v->owner);
Command<Commands::SendVehicleToDepot>::Do(DoCommandFlag::Execute, v->index, DepotCommandFlag::Service, {});
Backup<CompanyID> cur_company(_current_company, v->owner);
Command<CMD_SEND_VEHICLE_TO_DEPOT>::Do(DoCommandFlag::Execute, v->index, DepotCommandFlag::Service, {});
cur_company.Restore();
}
}
/** Aircraft is flying around. @copydoc AircraftStateHandler */
static void AircraftEventHandler_Flying(Aircraft *v, const AirportFTAClass *apc)
{
Station *st = Station::Get(v->targetairport);
@@ -1682,7 +1691,7 @@ static void AircraftEventHandler_Flying(Aircraft *v, const AirportFTAClass *apc)
uint16_t tsubspeed = v->subspeed;
if (!AirportHasBlock(v, current, apc)) {
v->state = landingtype; // LANDING / HELILANDING
if (v->state == HELILANDING) v->flags.Set(VehicleAirFlag::HelicopterDirectDescent);
if (v->state == HELILANDING) SetBit(v->flags, VAF_HELI_DIRECT_DESCENT);
/* it's a bit dirty, but I need to set position to next position, otherwise
* if there are multiple runways, plane won't know which one it took (because
* they all have heading LANDING). And also occupy that block! */
@@ -1700,27 +1709,25 @@ static void AircraftEventHandler_Flying(Aircraft *v, const AirportFTAClass *apc)
v->pos = apc->layout[v->pos].next_position;
}
/** Aircraft is landing (touchdown). @copydoc AircraftStateHandler */
static void AircraftEventHandler_Landing(Aircraft *v, [[maybe_unused]] const AirportFTAClass *apc)
static void AircraftEventHandler_Landing(Aircraft *v, const AirportFTAClass *)
{
v->state = ENDLANDING;
AircraftLandAirplane(v); // maybe crash airplane
/* check if the aircraft needs to be replaced or renewed and send it to a hangar if needed */
if (v->NeedsAutomaticServicing()) {
AutoRestoreBackup cur_company(_current_company, v->owner);
Command<Commands::SendVehicleToDepot>::Do(DoCommandFlag::Execute, v->index, DepotCommandFlag::Service, {});
Backup<CompanyID> cur_company(_current_company, v->owner);
Command<CMD_SEND_VEHICLE_TO_DEPOT>::Do(DoCommandFlag::Execute, v->index, DepotCommandFlag::Service, {});
cur_company.Restore();
}
}
/** Helicopter is starting to land. @copydoc AircraftStateHandler */
static void AircraftEventHandler_HeliLanding(Aircraft *v, [[maybe_unused]] const AirportFTAClass *apc)
static void AircraftEventHandler_HeliLanding(Aircraft *v, const AirportFTAClass *)
{
v->state = HELIENDLANDING;
v->UpdateDeltaXY();
}
/** Aircraft is has landed. @copydoc AircraftStateHandler */
static void AircraftEventHandler_EndLanding(Aircraft *v, const AirportFTAClass *apc)
{
/* next block busy, don't do a thing, just wait */
@@ -1734,9 +1741,9 @@ static void AircraftEventHandler_EndLanding(Aircraft *v, const AirportFTAClass *
if (AirportFindFreeTerminal(v, apc)) return;
}
v->state = HANGAR;
}
/** Helicopter has landed. @copydoc AircraftStateHandler */
static void AircraftEventHandler_HeliEndLanding(Aircraft *v, const AirportFTAClass *apc)
{
/* next block busy, don't do a thing, just wait */
@@ -1760,11 +1767,10 @@ static void AircraftEventHandler_HeliEndLanding(Aircraft *v, const AirportFTACla
* @param v Aircraft to handle.
* @param apc Airport state machine.
*/
using AircraftStateHandler = void(Aircraft *v, const AirportFTAClass *apc);
typedef void AircraftStateHandler(Aircraft *v, const AirportFTAClass *apc);
/** Array of handler functions for each target of the aircraft. */
static AircraftStateHandler * const _aircraft_state_handlers[] = {
[](Aircraft *, const AirportFTAClass *) { NOT_REACHED(); }, // TO_ALL = 0
AircraftEventHandler_General, // TO_ALL = 0
AircraftEventHandler_InHangar, // HANGAR = 1
AircraftEventHandler_AtTerminal, // TERM1 = 2
AircraftEventHandler_AtTerminal, // TERM2 = 3
@@ -1862,13 +1868,7 @@ static bool AirportMove(Aircraft *v, const AirportFTAClass *apc)
NOT_REACHED();
}
/**
* Checks whether the next block the aircraft wants to travel on is busy.
* @param v The aircraft to consider.
* @param current_pos The current position in the state machine the aircraft is at.
* @param apc The airport's state machine.
* @return \c true iff the road ahead is busy, eg. you must wait before proceeding.
*/
/** returns true if the road ahead is busy, eg. you must wait before proceeding. */
static bool AirportHasBlock(Aircraft *v, const AirportFTA *current_pos, const AirportFTAClass *apc)
{
const AirportFTA *reference = &apc->layout[v->pos];
@@ -2075,9 +2075,9 @@ static bool AirportFindFreeHelipad(Aircraft *v, const AirportFTAClass *apc)
static void AircraftHandleDestTooFar(Aircraft *v, bool too_far)
{
if (too_far) {
if (!v->flags.Test(VehicleAirFlag::DestinationTooFar)) {
v->flags.Set(VehicleAirFlag::DestinationTooFar);
SetWindowWidgetDirty(WindowClass::VehicleView, v->index, WID_VV_START_STOP);
if (!HasBit(v->flags, VAF_DEST_TOO_FAR)) {
SetBit(v->flags, VAF_DEST_TOO_FAR);
SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, WID_VV_START_STOP);
AI::NewEvent(v->owner, new ScriptEventAircraftDestTooFar(v->index));
if (v->owner == _local_company) {
/* Post a news message. */
@@ -2087,20 +2087,14 @@ static void AircraftHandleDestTooFar(Aircraft *v, bool too_far)
return;
}
if (v->flags.Test(VehicleAirFlag::DestinationTooFar)) {
if (HasBit(v->flags, VAF_DEST_TOO_FAR)) {
/* Not too far anymore, clear flag and message. */
v->flags.Reset(VehicleAirFlag::DestinationTooFar);
SetWindowWidgetDirty(WindowClass::VehicleView, v->index, WID_VV_START_STOP);
ClrBit(v->flags, VAF_DEST_TOO_FAR);
SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, WID_VV_START_STOP);
DeleteVehicleNews(v->index, AdviceType::AircraftDestinationTooFar);
}
}
/**
* Event handler loop for a single aircraft.
* @param v Aircraft to process events for.
* @param loop How many times has this been called during this tick.
* @return \c true iff another loop can take place, i.e. the vehicle still exists.
*/
static bool AircraftEventHandler(Aircraft *v, int loop)
{
if (v->vehstatus.Test(VehState::Crashed)) {
@@ -2132,7 +2126,7 @@ static bool AircraftEventHandler(Aircraft *v, int loop)
}
}
if (!v->flags.Test(VehicleAirFlag::DestinationTooFar)) AirportGoToNextPosition(v);
if (!HasBit(v->flags, VAF_DEST_TOO_FAR)) AirportGoToNextPosition(v);
return true;
}
@@ -2141,7 +2135,7 @@ bool Aircraft::Tick()
{
if (!this->IsNormalAircraft()) return true;
PerformanceAccumulator framerate(PerformanceElement::GameLoopAircraft);
PerformanceAccumulator framerate(PFE_GL_AIRCRAFT);
this->tick_counter++;
@@ -2168,7 +2162,7 @@ bool Aircraft::Tick()
*/
Station *GetTargetAirportIfValid(const Aircraft *v)
{
assert(v->type == VehicleType::Aircraft);
assert(v->type == VEH_AIRCRAFT);
Station *st = Station::GetIfValid(v->targetairport);
if (st == nullptr) return nullptr;
@@ -2184,7 +2178,7 @@ void UpdateAirplanesOnNewStation(const Station *st)
{
/* only 1 station is updated per function call, so it is enough to get entry_point once */
const AirportFTAClass *ap = st->airport.GetFTA();
Direction rotation = st->airport.tile == INVALID_TILE ? Direction::N : st->airport.rotation;
Direction rotation = st->airport.tile == INVALID_TILE ? DIR_N : st->airport.rotation;
for (Aircraft *v : Aircraft::Iterate()) {
if (!v->IsNormalAircraft() || v->targetairport != st->index) continue;
@@ -2196,7 +2190,7 @@ void UpdateAirplanesOnNewStation(const Station *st)
if (o->IsType(OT_GOTO_DEPOT) && !o->GetDepotOrderType().Test(OrderDepotTypeFlag::PartOfOrders) && o->GetDestination() == st->index &&
(!st->airport.HasHangar() || !CanVehicleUseStation(v, st))) {
o->MakeDummy();
SetWindowWidgetDirty(WindowClass::VehicleView, v->index, WID_VV_START_STOP);
SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, WID_VV_START_STOP);
}
v->pos = v->previous_pos = AircraftGetEntryPoint(v, ap, rotation);
UpdateAircraftCache(v);
+5 -6
View File
@@ -34,14 +34,14 @@ void DrawAircraftDetails(const Aircraft *v, const Rect &r)
for (const Aircraft *u = v; u != nullptr; u = u->Next()) {
if (u->IsNormalAircraft()) {
DrawString(r.left, r.right, y, GetString(STR_VEHICLE_INFO_BUILT_VALUE, PackEngineNameDParam(u->engine_type, EngineNameContext::VehicleDetails), u->build_year, u->value));
y += GetCharacterHeight(FontSize::Normal);
y += GetCharacterHeight(FS_NORMAL);
if (u->Next()->cargo_cap != 0) {
DrawString(r.left, r.right, y, GetString(STR_VEHICLE_INFO_CAPACITY_CAPACITY, u->cargo_type, u->cargo_cap, u->Next()->cargo_type, u->Next()->cargo_cap, GetCargoSubtypeText(u)));
} else {
DrawString(r.left, r.right, y, GetString(STR_VEHICLE_INFO_CAPACITY, u->cargo_type, u->cargo_cap, GetCargoSubtypeText(u)));
}
y += GetCharacterHeight(FontSize::Normal) + WidgetDimensions::scaled.vsep_normal;
y += GetCharacterHeight(FS_NORMAL) + WidgetDimensions::scaled.vsep_normal;
}
if (u->cargo_cap != 0) {
@@ -50,7 +50,7 @@ void DrawAircraftDetails(const Aircraft *v, const Rect &r)
if (cargo_count != 0) {
/* Cargo names (fix pluralness) */
DrawString(r.left, r.right, y, GetString(STR_VEHICLE_DETAILS_CARGO_FROM, u->cargo_type, cargo_count, u->cargo.GetFirstStation()));
y += GetCharacterHeight(FontSize::Normal);
y += GetCharacterHeight(FS_NORMAL);
feeder_share += u->cargo.GetFeederShare();
}
}
@@ -66,14 +66,13 @@ void DrawAircraftDetails(const Aircraft *v, const Rect &r)
* @param v Front vehicle
* @param r Rect to draw at
* @param selection Selected vehicle to draw a frame around
* @param image_type Context where the image is being drawn.
*/
void DrawAircraftImage(const Vehicle *v, const Rect &r, VehicleID selection, EngineImageType image_type)
{
bool rtl = _current_text_dir == TD_RTL;
VehicleSpriteSeq seq;
v->GetImage(rtl ? Direction::E : Direction::W, image_type, &seq);
v->GetImage(rtl ? DIR_E : DIR_W, image_type, &seq);
Rect rect;
seq.GetBounds(&rect);
@@ -112,6 +111,6 @@ void DrawAircraftImage(const Vehicle *v, const Rect &r, VehicleID selection, Eng
x += x_offs;
y += UnScaleGUI(rect.top) - heli_offs;
Rect hr = {x, y, x + width - 1, y + UnScaleGUI(rect.Height()) + heli_offs - 1};
DrawFrameRect(hr.Expand(WidgetDimensions::scaled.bevel), Colours::White, FrameFlag::BorderOnly);
DrawFrameRect(hr.Expand(WidgetDimensions::scaled.bevel), COLOUR_WHITE, FrameFlag::BorderOnly);
}
}
+5 -7
View File
@@ -82,24 +82,24 @@ AirportMovingData RotateAirportMovingData(const AirportMovingData *orig, Directi
{
AirportMovingData amd;
amd.flags = orig->flags;
amd.direction = ChangeDir(orig->direction, static_cast<DirDiff>(rotation));
amd.direction = ChangeDir(orig->direction, (DirDiff)rotation);
switch (rotation) {
case Direction::N:
case DIR_N:
amd.x = orig->x;
amd.y = orig->y;
break;
case Direction::E:
case DIR_E:
amd.x = orig->y;
amd.y = num_tiles_y * TILE_SIZE - orig->x - 1;
break;
case Direction::S:
case DIR_S:
amd.x = num_tiles_x * TILE_SIZE - orig->x - 1;
amd.y = num_tiles_y * TILE_SIZE - orig->y - 1;
break;
case Direction::W:
case DIR_W:
amd.x = num_tiles_x * TILE_SIZE - orig->y - 1;
amd.y = orig->x;
break;
@@ -134,8 +134,6 @@ AirportFTAClass::AirportFTAClass(
* Get the number of elements of a source Airport state automata
* Since it is actually just a big array of AirportFTA types, we only
* know one element from the other by differing 'position' identifiers
* @param apFA The state machine builder.
* @return The number of elements.
*/
static uint16_t AirportGetNofElements(const AirportFTAbuildup *apFA)
{
+25 -29
View File
@@ -5,7 +5,7 @@
* 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 <https://www.gnu.org/licenses/old-licenses/gpl-2.0>.
*/
/** @file airport.h Various declarations for airports. */
/** @file airport.h Various declarations for airports */
#ifndef AIRPORT_H
#define AIRPORT_H
@@ -56,7 +56,6 @@ enum class AirportMovingDataFlag : uint8_t {
Hold, ///< Holding pattern movement (above the airport).
};
/** Bitset of \c AirportMovingDataFlag elements. */
using AirportMovingDataFlags = EnumBitSet<AirportMovingDataFlag, uint16_t>;
/** Movement States on Airports (headings target) */
@@ -97,41 +96,40 @@ enum class AirportBlock : uint8_t {
Term6 = 5, ///< Block belonging to terminal 6.
Helipad1 = 6, ///< Block belonging to helipad 1.
Helipad2 = 7, ///< Block belonging to helipad 2.
RunwayInOut = 8, ///< Runway used for landing and take-off (commuter / city airports).
RunwayIn = 8, ///< Runway used for landing (metropolitan / international / intercontinental airports).
AirportBusy = 8, ///< Complete airport is busy (small airport / heliport).
RunwayOut = 9, ///< Runway used for take off.
TaxiwayBusy = 10, ///< Taxiway is occupied (commuter / city airports / helistation).
OutWay = 11, ///< Holding point just before take off.
InWay = 12, ///< Holding point just after take off.
AirportEntrance = 13, ///< Entrance point before terminals.
TermGroup1 = 14, ///< First set of terminals.
TermGroup2 = 15, ///< Second set of terminals.
Hangar2Area = 16, ///< Area in front of the second hangar.
TermGroup2Enter1 = 17, ///< First holding point before terminal.
TermGroup2Enter2 = 18, ///< Second holding point before terminal.
TermGroup2Exit1 = 19, ///< First holding point after terminal.
TermGroup2Exit2 = 20, ///< Second holding point after terminal.
PreHelipad = 21, ///< Holding point for helicopter landings.
RunwayInOut = 8,
RunwayIn = 8,
AirportBusy = 8,
RunwayOut = 9,
TaxiwayBusy = 10,
OutWay = 11,
InWay = 12,
AirportEntrance = 13,
TermGroup1 = 14,
TermGroup2 = 15,
Hangar2Area = 16,
TermGroup2Enter1 = 17,
TermGroup2Enter2 = 18,
TermGroup2Exit1 = 19,
TermGroup2Exit2 = 20,
PreHelipad = 21,
/* blocks for new airports */
Term7 = 22, ///< Block belonging to terminal 7.
Term8 = 23, ///< Block belonging to terminal 8.
Helipad3 = 24, ///< Block belonging to helipad 3.
Hangar1Area = 26, ///< Area in front of the first hangar.
OutWay2 = 27, ///< Second holding point just before take off.
InWay2 = 28, ///< Second holding point just after take off.
RunwayIn2 = 29, ///< Second runway for landing.
RunwayOut2 = 10, ///< Second runway for take off. @note re-uses #AirportBlock::TaxiwayBusy
OutWay3 = 31, ///< Third holding point just before take off.
Hangar1Area = 26,
OutWay2 = 27,
InWay2 = 28,
RunwayIn2 = 29,
RunwayOut2 = 10, ///< @note re-uses #AirportBlock::TaxiwayBusy
HelipadGroup = 13, ///< @note re-uses #AirportBlock::AirportEntrance
OutWay3 = 31,
/* end of new blocks */
Nothing = 30, ///< Nothing is blocked, for example being in the hanger.
Nothing = 30,
Zeppeliner = 62, ///< Block for the zeppeliner disaster vehicle.
AirportClosed = 63, ///< Dummy block for indicating a closed airport.
};
/** Bitset of \c AirportBlock elements. */
using AirportBlocks = EnumBitSet<AirportBlock, uint64_t>;
/** A single location on an airport where aircraft can move to. */
@@ -166,8 +164,6 @@ public:
Helicopters = 1, ///< Can helicopters land on this airport type?
ShortStrip = 2, ///< This airport has a short landing strip, dangerous for fast aircraft.
};
/** Bitset of \c Flag elements. */
using Flags = EnumBitSet<Flag, uint8_t>;
AirportFTAClass(
+57 -62
View File
@@ -73,9 +73,9 @@ static void PlaceAirport(TileIndex tile)
auto proc = [=](bool test, StationID to_join) -> bool {
if (test) {
return Command<Commands::BuildAirport>::Do(CommandFlagsToDCFlags(GetCommandFlags<Commands::BuildAirport>()), tile, airport_type, layout, StationID::Invalid(), adjacent).Succeeded();
return Command<CMD_BUILD_AIRPORT>::Do(CommandFlagsToDCFlags(GetCommandFlags<CMD_BUILD_AIRPORT>()), tile, airport_type, layout, StationID::Invalid(), adjacent).Succeeded();
} else {
return Command<Commands::BuildAirport>::Post(STR_ERROR_CAN_T_BUILD_AIRPORT_HERE, CcBuildAirport, tile, airport_type, layout, to_join, adjacent);
return Command<CMD_BUILD_AIRPORT>::Post(STR_ERROR_CAN_T_BUILD_AIRPORT_HERE, CcBuildAirport, tile, airport_type, layout, to_join, adjacent);
}
};
@@ -84,7 +84,7 @@ static void PlaceAirport(TileIndex tile)
/** Airport build toolbar window handler. */
struct BuildAirToolbarWindow : Window {
WidgetID last_user_action = INVALID_WIDGET; ///< Last started user action.
WidgetID last_user_action = INVALID_WIDGET; // Last started user action.
BuildAirToolbarWindow(WindowDesc &desc, WindowNumber window_number) : Window(desc)
{
@@ -96,7 +96,7 @@ struct BuildAirToolbarWindow : Window {
void Close([[maybe_unused]] int data = 0) override
{
if (this->IsWidgetLowered(WID_AT_AIRPORT)) SetViewportCatchmentStation(nullptr, true);
if (_settings_client.gui.link_terraform_toolbar) CloseWindowById(WindowClass::ScenarioGenerateLandscape, 0, false);
if (_settings_client.gui.link_terraform_toolbar) CloseWindowById(WC_SCEN_LAND_GEN, 0, false);
this->Window::Close();
}
@@ -109,10 +109,10 @@ struct BuildAirToolbarWindow : Window {
{
if (!gui_scope) return;
bool can_build = CanBuildVehicleInfrastructure(VehicleType::Aircraft);
bool can_build = CanBuildVehicleInfrastructure(VEH_AIRCRAFT);
this->SetWidgetDisabledState(WID_AT_AIRPORT, !can_build);
if (!can_build) {
CloseWindowById(WindowClass::BuildStation, TransportType::Air);
CloseWindowById(WC_BUILD_STATION, TRANSPORT_AIR);
/* Show in the tooltip why this button is disabled. */
this->GetWidget<NWidgetCore>(WID_AT_AIRPORT)->SetToolTip(STR_TOOLBAR_DISABLED_NO_VEHICLE_AVAILABLE);
@@ -179,20 +179,20 @@ struct BuildAirToolbarWindow : Window {
this->RaiseButtons();
CloseWindowById(WindowClass::BuildStation, TransportType::Air);
CloseWindowById(WindowClass::JoinStation, 0);
CloseWindowById(WC_BUILD_STATION, TRANSPORT_AIR);
CloseWindowById(WC_SELECT_STATION, 0);
}
/**
* Handler for global hotkeys of the BuildAirToolbarWindow.
* @param hotkey Hotkey
* @return EventState::Handled if hotkey was accepted.
* @return ES_HANDLED if hotkey was accepted.
*/
static EventState AirportToolbarGlobalHotkeys(int hotkey)
{
if (_game_mode != GameMode::Normal) return EventState::NotHandled;
if (_game_mode != GM_NORMAL) return ES_NOT_HANDLED;
Window *w = ShowBuildAirToolbar();
if (w == nullptr) return EventState::NotHandled;
if (w == nullptr) return ES_NOT_HANDLED;
return w->OnHotkey(hotkey);
}
@@ -204,21 +204,20 @@ struct BuildAirToolbarWindow : Window {
static constexpr std::initializer_list<NWidgetPart> _nested_air_toolbar_widgets = {
NWidget(NWID_HORIZONTAL),
NWidget(WWT_CLOSEBOX, Colours::DarkGreen),
NWidget(WWT_CAPTION, Colours::DarkGreen), SetStringTip(STR_TOOLBAR_AIRCRAFT_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
NWidget(WWT_STICKYBOX, Colours::DarkGreen),
NWidget(WWT_CLOSEBOX, COLOUR_DARK_GREEN),
NWidget(WWT_CAPTION, COLOUR_DARK_GREEN), SetStringTip(STR_TOOLBAR_AIRCRAFT_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
NWidget(WWT_STICKYBOX, COLOUR_DARK_GREEN),
EndContainer(),
NWidget(NWID_HORIZONTAL),
NWidget(WWT_IMGBTN, Colours::DarkGreen, WID_AT_AIRPORT), SetFill(0, 1), SetToolbarMinimalSize(2), SetSpriteTip(SPR_IMG_AIRPORT, STR_TOOLBAR_AIRCRAFT_BUILD_AIRPORT_TOOLTIP),
NWidget(WWT_PANEL, Colours::DarkGreen), SetToolbarSpacerMinimalSize(), SetFill(1, 1), EndContainer(),
NWidget(WWT_IMGBTN, Colours::DarkGreen, WID_AT_DEMOLISH), SetFill(0, 1), SetToolbarMinimalSize(1), SetSpriteTip(SPR_IMG_DYNAMITE, STR_TOOLTIP_DEMOLISH_BUILDINGS_ETC),
NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_AT_AIRPORT), SetFill(0, 1), SetToolbarMinimalSize(2), SetSpriteTip(SPR_IMG_AIRPORT, STR_TOOLBAR_AIRCRAFT_BUILD_AIRPORT_TOOLTIP),
NWidget(WWT_PANEL, COLOUR_DARK_GREEN), SetToolbarSpacerMinimalSize(), SetFill(1, 1), EndContainer(),
NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_AT_DEMOLISH), SetFill(0, 1), SetToolbarMinimalSize(1), SetSpriteTip(SPR_IMG_DYNAMITE, STR_TOOLTIP_DEMOLISH_BUILDINGS_ETC),
EndContainer(),
};
/** Window definition for the air toolbar. */
static WindowDesc _air_toolbar_desc(
WindowPosition::Manual, "toolbar_air", 0, 0,
WindowClass::BuildToolbar, WindowClass::None,
WDP_MANUAL, "toolbar_air", 0, 0,
WC_BUILD_TOOLBAR, WC_NONE,
WindowDefaultFlag::Construction,
_nested_air_toolbar_widgets,
&BuildAirToolbarWindow::hotkeys
@@ -235,8 +234,8 @@ Window *ShowBuildAirToolbar()
{
if (!Company::IsValidID(_local_company)) return nullptr;
CloseWindowByClass(WindowClass::BuildToolbar);
return AllocateWindowDescFront<BuildAirToolbarWindow>(_air_toolbar_desc, TransportType::Air);
CloseWindowByClass(WC_BUILD_TOOLBAR);
return AllocateWindowDescFront<BuildAirToolbarWindow>(_air_toolbar_desc, TRANSPORT_AIR);
}
class BuildAirportWindow : public PickerWindowBase {
@@ -244,16 +243,13 @@ class BuildAirportWindow : public PickerWindowBase {
int line_height = 0;
Scrollbar *vscroll = nullptr;
/**
* Build a dropdown list of available airport classes.
* @return The constructed dropdown list.
*/
/** Build a dropdown list of available airport classes */
static DropDownList BuildAirportClassDropDown()
{
DropDownList list;
for (const auto &cls : AirportClass::Classes()) {
list.push_back(MakeDropDownListStringItem(cls.name, cls.Index().base()));
list.push_back(MakeDropDownListStringItem(cls.name, cls.Index()));
}
return list;
@@ -268,14 +264,14 @@ public:
this->vscroll->SetCapacity(5);
this->vscroll->SetPosition(0);
this->FinishInitNested(TransportType::Air);
this->FinishInitNested(TRANSPORT_AIR);
this->SetWidgetLoweredState(WID_AP_BTN_DONTHILIGHT, !_settings_client.gui.station_show_coverage);
this->SetWidgetLoweredState(WID_AP_BTN_DOHILIGHT, _settings_client.gui.station_show_coverage);
this->OnInvalidateData();
/* Ensure airport class is valid (changing NewGRFs). */
_selected_airport_class = Clamp(_selected_airport_class, AirportClassID::Begin(), static_cast<AirportClassID>(AirportClass::GetClassCount() - 1));
_selected_airport_class = Clamp(_selected_airport_class, APC_BEGIN, (AirportClassID)(AirportClass::GetClassCount() - 1));
const AirportClass *ac = AirportClass::Get(_selected_airport_class);
this->vscroll->SetCount(ac->GetSpecCount());
@@ -299,7 +295,7 @@ public:
void Close([[maybe_unused]] int data = 0) override
{
CloseWindowById(WindowClass::JoinStation, 0);
CloseWindowById(WC_SELECT_STATION, 0);
this->PickerWindowBase::Close();
}
@@ -348,7 +344,7 @@ public:
size.width = std::max(size.width, GetStringBoundingBox(as->name).width + padding.width);
}
this->line_height = GetCharacterHeight(FontSize::Normal) + padding.height;
this->line_height = GetCharacterHeight(FS_NORMAL) + padding.height;
size.height = 5 * this->line_height;
break;
}
@@ -398,9 +394,9 @@ public:
for (auto it = first; it != last; ++it) {
const AirportSpec *as = *it;
if (!as->IsAvailable()) {
GfxFillRect(row, PC_BLACK, FillRectMode::Checker);
GfxFillRect(row, PC_BLACK, FILLRECT_CHECKER);
}
DrawString(text, as->name, (static_cast<int>(as->index) == _selected_airport_index) ? TextColour::White : TextColour::Black);
DrawString(text, as->name, (static_cast<int>(as->index) == _selected_airport_index) ? TC_WHITE : TC_BLACK);
row = row.Translate(0, this->line_height);
text = text.Translate(0, this->line_height);
}
@@ -419,7 +415,7 @@ public:
const AirportSpec *as = AirportClass::Get(_selected_airport_class)->GetSpec(_selected_airport_index);
StringID string = GetAirportTextCallback(as, _selected_airport_layout, CBID_AIRPORT_ADDITIONAL_TEXT);
if (string != STR_UNDEFINED) {
DrawStringMultiLine(r, string, TextColour::Black);
DrawStringMultiLine(r, string, TC_BLACK);
}
}
break;
@@ -442,17 +438,17 @@ public:
if (_settings_game.economy.station_noise_level) {
/* show the noise of the selected airport */
DrawString(r, GetString(STR_STATION_BUILD_NOISE, as->noise_level));
r.top += GetCharacterHeight(FontSize::Normal) + WidgetDimensions::scaled.vsep_normal;
r.top += GetCharacterHeight(FS_NORMAL) + WidgetDimensions::scaled.vsep_normal;
}
if (_settings_game.economy.infrastructure_maintenance) {
Money monthly = _price[Price::InfrastructureAirport] * as->maintenance_cost >> 3;
Money monthly = _price[PR_INFRASTRUCTURE_AIRPORT] * as->maintenance_cost >> 3;
DrawString(r, GetString(TimerGameEconomy::UsingWallclockUnits() ? STR_STATION_BUILD_INFRASTRUCTURE_COST_PERIOD : STR_STATION_BUILD_INFRASTRUCTURE_COST_YEAR, monthly * 12));
r.top += GetCharacterHeight(FontSize::Normal) + WidgetDimensions::scaled.vsep_normal;
r.top += GetCharacterHeight(FS_NORMAL) + WidgetDimensions::scaled.vsep_normal;
}
/* strings such as 'Size' and 'Coverage Area' */
r.top = DrawBadgeNameList(r, as->badges, GrfSpecFeature::Airports) + WidgetDimensions::scaled.vsep_normal;
r.top = DrawBadgeNameList(r, as->badges, GSF_AIRPORTS) + WidgetDimensions::scaled.vsep_normal;
r.top = DrawStationCoverageAreaText(r, SCT_ALL, rad, false) + WidgetDimensions::scaled.vsep_normal;
r.top = DrawStationCoverageAreaText(r, SCT_ALL, rad, true);
}
@@ -485,7 +481,7 @@ public:
int w = as->size_x;
int h = as->size_y;
Direction rotation = as->layouts[_selected_airport_layout].rotation;
if (rotation == Direction::E || rotation == Direction::W) std::swap(w, h);
if (rotation == DIR_E || rotation == DIR_W) std::swap(w, h);
SetTileSelectSize(w, h);
this->preview_sprite = GetCustomAirportSprite(as, _selected_airport_layout);
@@ -502,7 +498,7 @@ public:
{
switch (widget) {
case WID_AP_CLASS_DROPDOWN:
ShowDropDownList(this, BuildAirportClassDropDown(), _selected_airport_class.base(), WID_AP_CLASS_DROPDOWN);
ShowDropDownList(this, BuildAirportClassDropDown(), _selected_airport_class, WID_AP_CLASS_DROPDOWN);
break;
case WID_AP_AIRPORT_LIST: {
@@ -584,52 +580,51 @@ public:
CheckRedrawStationCoverage(this);
}
const IntervalTimer<TimerGameCalendar> yearly_interval = {{TimerGameCalendar::Trigger::Year, TimerGameCalendar::Priority::None}, [this](auto) {
const IntervalTimer<TimerGameCalendar> yearly_interval = {{TimerGameCalendar::YEAR, TimerGameCalendar::Priority::NONE}, [this](auto) {
this->InvalidateData();
}};
};
static constexpr std::initializer_list<NWidgetPart> _nested_build_airport_widgets = {
NWidget(NWID_HORIZONTAL),
NWidget(WWT_CLOSEBOX, Colours::DarkGreen),
NWidget(WWT_CAPTION, Colours::DarkGreen), SetStringTip(STR_STATION_BUILD_AIRPORT_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
NWidget(WWT_CLOSEBOX, COLOUR_DARK_GREEN),
NWidget(WWT_CAPTION, COLOUR_DARK_GREEN), SetStringTip(STR_STATION_BUILD_AIRPORT_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
EndContainer(),
NWidget(WWT_PANEL, Colours::DarkGreen),
NWidget(WWT_PANEL, COLOUR_DARK_GREEN),
NWidget(NWID_VERTICAL), SetPIP(0, WidgetDimensions::unscaled.vsep_normal, 0), SetPadding(WidgetDimensions::unscaled.picker),
NWidget(NWID_VERTICAL), SetPIP(0, WidgetDimensions::unscaled.vsep_picker, 0),
NWidget(WWT_LABEL, Colours::Invalid), SetStringTip(STR_STATION_BUILD_AIRPORT_CLASS_LABEL), SetFill(1, 0),
NWidget(WWT_DROPDOWN, Colours::Grey, WID_AP_CLASS_DROPDOWN), SetFill(1, 0), SetToolTip(STR_STATION_BUILD_AIRPORT_TOOLTIP),
NWidget(WWT_EMPTY, Colours::Invalid, WID_AP_AIRPORT_SPRITE), SetFill(1, 0),
NWidget(WWT_LABEL, INVALID_COLOUR), SetStringTip(STR_STATION_BUILD_AIRPORT_CLASS_LABEL), SetFill(1, 0),
NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_AP_CLASS_DROPDOWN), SetFill(1, 0), SetToolTip(STR_STATION_BUILD_AIRPORT_TOOLTIP),
NWidget(WWT_EMPTY, INVALID_COLOUR, WID_AP_AIRPORT_SPRITE), SetFill(1, 0),
NWidget(NWID_HORIZONTAL),
NWidget(WWT_MATRIX, Colours::Grey, WID_AP_AIRPORT_LIST), SetFill(1, 0), SetMatrixDataTip(1, 5, STR_STATION_BUILD_AIRPORT_TOOLTIP), SetScrollbar(WID_AP_SCROLLBAR),
NWidget(NWID_VSCROLLBAR, Colours::Grey, WID_AP_SCROLLBAR),
NWidget(WWT_MATRIX, COLOUR_GREY, WID_AP_AIRPORT_LIST), SetFill(1, 0), SetMatrixDataTip(1, 5, STR_STATION_BUILD_AIRPORT_TOOLTIP), SetScrollbar(WID_AP_SCROLLBAR),
NWidget(NWID_VSCROLLBAR, COLOUR_GREY, WID_AP_SCROLLBAR),
EndContainer(),
NWidget(WWT_LABEL, Colours::Invalid), SetStringTip(STR_STATION_BUILD_ORIENTATION), SetFill(1, 0),
NWidget(WWT_LABEL, INVALID_COLOUR), SetStringTip(STR_STATION_BUILD_ORIENTATION), SetFill(1, 0),
NWidget(NWID_HORIZONTAL),
NWidget(WWT_PUSHARROWBTN, Colours::Grey, WID_AP_LAYOUT_DECREASE), SetMinimalSize(12, 0), SetArrowWidgetTypeTip(ArrowWidgetType::Decrease),
NWidget(WWT_LABEL, Colours::Invalid, WID_AP_LAYOUT_NUM), SetResize(1, 0), SetFill(1, 0),
NWidget(WWT_PUSHARROWBTN, Colours::Grey, WID_AP_LAYOUT_INCREASE), SetMinimalSize(12, 0), SetArrowWidgetTypeTip(ArrowWidgetType::Increase),
NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_AP_LAYOUT_DECREASE), SetMinimalSize(12, 0), SetArrowWidgetTypeTip(AWV_DECREASE),
NWidget(WWT_LABEL, INVALID_COLOUR, WID_AP_LAYOUT_NUM), SetResize(1, 0), SetFill(1, 0),
NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_AP_LAYOUT_INCREASE), SetMinimalSize(12, 0), SetArrowWidgetTypeTip(AWV_INCREASE),
EndContainer(),
NWidget(WWT_EMPTY, Colours::Invalid, WID_AP_EXTRA_TEXT), SetFill(1, 0), SetMinimalSize(150, 0),
NWidget(WWT_LABEL, Colours::Invalid), SetStringTip(STR_STATION_BUILD_COVERAGE_AREA_TITLE), SetFill(1, 0),
NWidget(WWT_EMPTY, INVALID_COLOUR, WID_AP_EXTRA_TEXT), SetFill(1, 0), SetMinimalSize(150, 0),
NWidget(WWT_LABEL, INVALID_COLOUR), SetStringTip(STR_STATION_BUILD_COVERAGE_AREA_TITLE), SetFill(1, 0),
NWidget(NWID_HORIZONTAL), SetPIP(14, 0, 14), SetPIPRatio(1, 0, 1),
NWidget(NWID_HORIZONTAL, NWidContainerFlag::EqualSize),
NWidget(WWT_TEXTBTN, Colours::Grey, WID_AP_BTN_DONTHILIGHT), SetMinimalSize(60, 12), SetFill(1, 0),
NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_AP_BTN_DONTHILIGHT), SetMinimalSize(60, 12), SetFill(1, 0),
SetStringTip(STR_STATION_BUILD_COVERAGE_OFF, STR_STATION_BUILD_COVERAGE_AREA_OFF_TOOLTIP),
NWidget(WWT_TEXTBTN, Colours::Grey, WID_AP_BTN_DOHILIGHT), SetMinimalSize(60, 12), SetFill(1, 0),
NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_AP_BTN_DOHILIGHT), SetMinimalSize(60, 12), SetFill(1, 0),
SetStringTip(STR_STATION_BUILD_COVERAGE_ON, STR_STATION_BUILD_COVERAGE_AREA_ON_TOOLTIP),
EndContainer(),
EndContainer(),
EndContainer(),
NWidget(WWT_EMPTY, Colours::Invalid, WID_AP_ACCEPTANCE), SetResize(0, 1), SetFill(1, 0), SetMinimalTextLines(2, WidgetDimensions::unscaled.vsep_normal),
NWidget(WWT_EMPTY, INVALID_COLOUR, WID_AP_ACCEPTANCE), SetResize(0, 1), SetFill(1, 0), SetMinimalTextLines(2, WidgetDimensions::unscaled.vsep_normal),
EndContainer(),
EndContainer(),
};
/** Window definition for the airport build window. */
static WindowDesc _build_airport_desc(
WindowPosition::Automatic, {}, 0, 0,
WindowClass::BuildStation, WindowClass::BuildToolbar,
WDP_AUTO, {}, 0, 0,
WC_BUILD_STATION, WC_BUILD_TOOLBAR,
WindowDefaultFlag::Construction,
_nested_build_airport_widgets
);
@@ -641,6 +636,6 @@ static void ShowBuildAirportPicker(Window *parent)
void InitializeAirportGui()
{
_selected_airport_class = AirportClassID::Begin();
_selected_airport_class = APC_BEGIN;
_selected_airport_index = -1;
}
+1 -1
View File
@@ -74,7 +74,7 @@ void AddAnimatedTile(TileIndex tile, bool mark_dirty)
*/
void AnimateAnimatedTiles()
{
PerformanceAccumulator landscape_framerate(PerformanceElement::GameLoopLandscape);
PerformanceAccumulator landscape_framerate(PFE_GL_LANDSCAPE);
for (auto it = std::begin(_animated_tiles); it != std::end(_animated_tiles); /* nothing */) {
TileIndex &tile = *it;

Some files were not shown because too many files have changed in this diff Show More