mirror of
https://github.com/OpenTTD/OpenTTD.git
synced 2025-08-14 10:09:11 +00:00
Compare commits
233 Commits
13.0-beta2
...
13.2.1
Author | SHA1 | Date | |
---|---|---|---|
|
402baa63f2 | ||
|
e80cb487e6 | ||
|
ee6e30ead9 | ||
|
4b7fcaca0f | ||
|
d4df692a70 | ||
|
9580aef49c | ||
4aded39db6 | |||
|
ed9895dbb5 | ||
ab0924f14e | |||
|
dea0f7e894 | ||
4949bd8cd5 | |||
|
374a51c766 | ||
|
57946c8507 | ||
|
0f81c20ee2 | ||
|
46d30aa970 | ||
|
4a7d93b2a6 | ||
|
2a0e2f1658 | ||
|
83a857afb6 | ||
b5a38cb14d | |||
|
00bcda7e33 | ||
|
6f7d44c3f4 | ||
|
045e81809a | ||
|
b8d66fc783 | ||
|
2ce9f640ef | ||
220b08b868 | |||
3ae1a80576 | |||
|
532007737e | ||
|
57a6233754 | ||
53709f0bdc | |||
81e5cd23e0 | |||
2d8d9c49c4 | |||
c3815359f1 | |||
|
89259af2e4 | ||
680eb6b516 | |||
|
a75a90b3af | ||
|
7969907116 | ||
|
4cc0c21182 | ||
|
df417168c6 | ||
|
b8eca7ddb1 | ||
|
0569331f6b | ||
|
439ecbc759 | ||
|
85d2f80817 | ||
|
e53caf8f01 | ||
|
b70df6eeda | ||
f895d7e43c | |||
28e845dbaf | |||
|
df1ba20403 | ||
|
4883d386da | ||
|
e16fcb286a | ||
|
ff7e8c284a | ||
|
08a5637f98 | ||
|
da20e0f6e6 | ||
|
78512af02e | ||
f4f35a1f5c | |||
|
7f987c8d3a | ||
|
090913c655 | ||
15a32faece | |||
|
353a6d4bfe | ||
808dfc0672 | |||
67bd6ab0ad | |||
4c756159fd | |||
d510e0baa8 | |||
4a1361b044 | |||
|
eaec433028 | ||
|
86beadc00b | ||
|
261d674866 | ||
f16a1107d2 | |||
4b40e93197 | |||
|
330a823c3b | ||
|
f1fdcd71f4 | ||
|
70d5683e53 | ||
|
430630e774 | ||
|
d78ab6a874 | ||
|
fedb77a56d | ||
|
803c523735 | ||
|
81b53b36c8 | ||
|
381f1ac777 | ||
|
4b7099fa68 | ||
addf30ecdf | |||
|
0604f571e1 | ||
|
617d794af6 | ||
|
da9f226f0b | ||
|
0fb6b3ca07 | ||
|
249141858c | ||
|
144d404f05 | ||
|
cf546c1917 | ||
|
63d607a316 | ||
|
80590af1cb | ||
|
2a787aa8b8 | ||
|
ac31c1043e | ||
|
2ebc601d97 | ||
|
9daec1cb30 | ||
|
6bd2b9c34f | ||
|
292ec1ced7 | ||
|
6b36f07eb8 | ||
|
f6170ec782 | ||
|
32d80d1a57 | ||
|
8aa4173bce | ||
|
ada33a6885 | ||
|
64bd824b48 | ||
|
ce9c4fc31f | ||
|
ff07af905a | ||
|
b5f8fcb280 | ||
|
bac7ad72b1 | ||
0cb3d85a87 | |||
|
05c0295d32 | ||
|
556e9e8434 | ||
|
4fe8d97623 | ||
|
c31195509f | ||
|
aa4cd1012f | ||
|
c9948a4517 | ||
|
d1daf34c91 | ||
|
18c701d274 | ||
|
3357aff494 | ||
|
08ca0746b1 | ||
|
1cb459de3f | ||
7e61264e3c | |||
|
75c37bf7c2 | ||
|
f88766c976 | ||
|
52319891ce | ||
|
0a5dc852bb | ||
5b27cd029f | |||
|
2104849fad | ||
|
25940cdbd1 | ||
|
f37b6cb17f | ||
|
87d74389fb | ||
|
557c5a8583 | ||
|
bd6a95c6b9 | ||
|
6d1331ebcd | ||
680fcb8611 | |||
|
548a9c41d5 | ||
4c6b6c9d33 | |||
c935a58272 | |||
|
88030d781b | ||
7ccfc4b143 | |||
|
f5193aeba2 | ||
|
ed65594868 | ||
|
b211a88b71 | ||
|
2414cf0e8f | ||
|
400addf7af | ||
|
4b123394cf | ||
|
8978f11146 | ||
5e22788664 | |||
|
22035b7eab | ||
|
67f02e20de | ||
|
012fd2be0d | ||
|
ffc1f7ce56 | ||
|
25f247047d | ||
|
f90156f74c | ||
|
918b2cb3ee | ||
|
fe30f66570 | ||
f7e2b6ef12 | |||
|
6caed5f15e | ||
|
7a18631291 | ||
9e56e16147 | |||
961e66df30 | |||
|
3d3ed87d99 | ||
4f26f6b8aa | |||
|
c53f29df53 | ||
|
35d55bd534 | ||
|
4ffe7e0477 | ||
|
8063fcb6e0 | ||
efa20dd969 | |||
cbf48c4dd9 | |||
74180efe7f | |||
3485709f53 | |||
898dadadb2 | |||
85814b29d4 | |||
94167dfd34 | |||
c11db7d593 | |||
d7f561a400 | |||
c8cc61d889 | |||
23eec0b7b3 | |||
|
3451c0a82c | ||
|
04ee86d3ac | ||
|
e6c857cdba | ||
|
af3df959c2 | ||
7b5edba76c | |||
a971eee2e0 | |||
138198e971 | |||
002fe67bef | |||
|
14c1266bbc | ||
|
d4c530904c | ||
f6e7e44169 | |||
|
c179c10048 | ||
|
a857ed8240 | ||
c962c77306 | |||
03c1b5169c | |||
dde15a403c | |||
|
2012998563 | ||
|
e5720325ff | ||
daaa058493 | |||
8599041ce4 | |||
|
888c9172e0 | ||
1eecbd39ed | |||
|
7425660b3e | ||
5eb7e1d3ab | |||
8adc47858d | |||
33eb9688cf | |||
|
c50fabb574 | ||
131b7f5127 | |||
|
0116a422ea | ||
|
d67259334a | ||
|
c7d7658004 | ||
|
1a05e95945 | ||
|
e0cb31ff07 | ||
|
4f9893cc98 | ||
c448eb04d8 | |||
|
2848483810 | ||
|
8db4892f49 | ||
|
ac12028278 | ||
|
7e7e1183cf | ||
|
fd5de3b366 | ||
|
49c121ec29 | ||
daacde4496 | |||
|
2756741575 | ||
|
5cef40591b | ||
|
d5fc423793 | ||
|
985f487065 | ||
53b827c460 | |||
|
13d2d11fa8 | ||
|
2c5eb206d4 | ||
|
ab9d77ebbe | ||
|
1c205b2cda | ||
1131608eb4 | |||
|
644012dea2 | ||
|
a7f78af4aa | ||
485368f607 | |||
|
3100c6e7d0 | ||
387c57b023 | |||
|
41c893dd4e | ||
eaf1e33bd7 | |||
|
978f5b9341 |
15
.github/workflows/ci-build.yml
vendored
15
.github/workflows/ci-build.yml
vendored
@@ -16,7 +16,7 @@ jobs:
|
||||
runs-on: ubuntu-20.04
|
||||
container:
|
||||
# If you change this version, change the number in the cache step too.
|
||||
image: emscripten/emsdk:2.0.31
|
||||
image: emscripten/emsdk:3.1.28
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
@@ -26,7 +26,7 @@ jobs:
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: /emsdk/upstream/emscripten/cache
|
||||
key: 2.0.31-${{ runner.os }}
|
||||
key: 3.1.28-${{ runner.os }}
|
||||
|
||||
- name: Patch Emscripten to support LZMA
|
||||
run: |
|
||||
@@ -162,12 +162,21 @@ jobs:
|
||||
|
||||
runs-on: macos-latest
|
||||
env:
|
||||
MACOSX_DEPLOYMENT_TARGET: 10.14
|
||||
MACOSX_DEPLOYMENT_TARGET: 10.13
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Install dependencies
|
||||
env:
|
||||
HOMEBREW_NO_AUTO_UPDATE: 1
|
||||
HOMEBREW_NO_INSTALL_CLEANUP: 1
|
||||
run: |
|
||||
brew install \
|
||||
pkg-config \
|
||||
# EOF
|
||||
|
||||
- name: Prepare cache key
|
||||
id: key
|
||||
run: |
|
||||
|
4
.github/workflows/preview_build.yml
vendored
4
.github/workflows/preview_build.yml
vendored
@@ -12,7 +12,7 @@ jobs:
|
||||
runs-on: ubuntu-20.04
|
||||
container:
|
||||
# If you change this version, change the number in the cache step too.
|
||||
image: emscripten/emsdk:2.0.31
|
||||
image: emscripten/emsdk:3.1.28
|
||||
|
||||
steps:
|
||||
- name: Update deployment status to in progress
|
||||
@@ -44,7 +44,7 @@ jobs:
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: /emsdk/upstream/emscripten/cache
|
||||
key: 2.0.31-${{ runner.os }}
|
||||
key: 3.1.28-${{ runner.os }}
|
||||
|
||||
- name: Patch Emscripten to support LZMA
|
||||
run: |
|
||||
|
85
.github/workflows/release-docs.yml
vendored
Normal file
85
.github/workflows/release-docs.yml
vendored
Normal file
@@ -0,0 +1,85 @@
|
||||
name: Release (Docs)
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
version:
|
||||
required: true
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
docs:
|
||||
name: Docs
|
||||
|
||||
runs-on: ubuntu-20.04
|
||||
|
||||
steps:
|
||||
- name: Download source
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: internal-source
|
||||
|
||||
- name: Unpack source
|
||||
run: |
|
||||
tar -xf source.tar.gz --strip-components=1
|
||||
|
||||
- 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: |
|
||||
mkdir -p ${GITHUB_WORKSPACE}/build
|
||||
cd ${GITHUB_WORKSPACE}/build
|
||||
|
||||
echo "::group::CMake"
|
||||
cmake ${GITHUB_WORKSPACE} \
|
||||
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
|
||||
-DOPTION_DOCS_ONLY=ON \
|
||||
# EOF
|
||||
echo "::endgroup::"
|
||||
|
||||
echo "::group::Build"
|
||||
cmake --build . --target docs
|
||||
echo "::endgroup::"
|
||||
|
||||
- name: Create bundles
|
||||
run: |
|
||||
BASENAME=openttd-${{ inputs.version }}
|
||||
|
||||
cd ${GITHUB_WORKSPACE}/build
|
||||
|
||||
mv docs/source ${BASENAME}-docs
|
||||
mv docs/ai-api ${BASENAME}-docs-ai
|
||||
mv docs/gs-api ${BASENAME}-docs-gs
|
||||
|
||||
mkdir -p bundles
|
||||
|
||||
echo "::group::Create docs bundle"
|
||||
tar --xz -cf bundles/${BASENAME}-docs.tar.xz ${BASENAME}-docs
|
||||
echo "::endgroup::"
|
||||
|
||||
echo "::group::Create AI API docs bundle"
|
||||
tar --xz -cf bundles/${BASENAME}-docs-ai.tar.xz ${BASENAME}-docs-ai
|
||||
echo "::endgroup::"
|
||||
|
||||
echo "::group::Create GameScript API docs bundle"
|
||||
tar --xz -cf bundles/${BASENAME}-docs-gs.tar.xz ${BASENAME}-docs-gs
|
||||
echo "::endgroup::"
|
||||
|
||||
- name: Store bundles
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: openttd-docs
|
||||
path: build/bundles/*.tar.xz
|
||||
retention-days: 5
|
100
.github/workflows/release-linux.yml
vendored
Normal file
100
.github/workflows/release-linux.yml
vendored
Normal file
@@ -0,0 +1,100 @@
|
||||
name: Release (Linux)
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
|
||||
jobs:
|
||||
linux:
|
||||
name: Linux (Generic)
|
||||
|
||||
runs-on: ubuntu-20.04
|
||||
container:
|
||||
# manylinux2014 is based on CentOS 7, but already has a lot of things
|
||||
# installed and preconfigured. It makes it easier to build OpenTTD.
|
||||
image: quay.io/pypa/manylinux2014_x86_64
|
||||
|
||||
steps:
|
||||
- name: Download source
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: internal-source
|
||||
|
||||
- name: Unpack source
|
||||
run: |
|
||||
tar -xf source.tar.gz --strip-components=1
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
echo "::group::Install dependencies"
|
||||
yum install -y \
|
||||
fontconfig-devel \
|
||||
freetype-devel \
|
||||
libicu-devel \
|
||||
libpng-devel \
|
||||
lzo-devel \
|
||||
SDL2-devel \
|
||||
wget \
|
||||
xz-devel \
|
||||
zlib-devel \
|
||||
# EOF
|
||||
echo "::endgroup::"
|
||||
|
||||
# The yum variant of fluidsynth depends on all possible audio drivers,
|
||||
# like jack, ALSA, pulseaudio, etc. This is not really useful for us,
|
||||
# as we route the output of fluidsynth back via our sound driver, and
|
||||
# as such do not use these audio driver outputs at all. So instead,
|
||||
# we compile fluidsynth ourselves, with as little dependencies as
|
||||
# possible. This currently means it picks up SDL2, but this is fine,
|
||||
# as we need SDL2 anyway.
|
||||
echo "::group::Install fluidsynth"
|
||||
wget https://github.com/FluidSynth/fluidsynth/archive/v2.1.6.tar.gz
|
||||
tar xf v2.1.6.tar.gz
|
||||
(
|
||||
cd fluidsynth-2.1.6
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_INSTALL_PREFIX=/usr
|
||||
cmake --build . -j $(nproc)
|
||||
cmake --install .
|
||||
)
|
||||
echo "::endgroup::"
|
||||
|
||||
- name: Install GCC problem matcher
|
||||
uses: ammaraskar/gcc-problem-matcher@master
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
mkdir -p build
|
||||
cd build
|
||||
|
||||
echo "::group::CMake"
|
||||
cmake ${GITHUB_WORKSPACE} \
|
||||
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
|
||||
-DOPTION_PACKAGE_DEPENDENCIES=ON \
|
||||
# EOF
|
||||
echo "::endgroup::"
|
||||
|
||||
echo "::group::Build"
|
||||
echo "Running on $(nproc) cores"
|
||||
cmake --build . -j $(nproc)
|
||||
echo "::endgroup::"
|
||||
|
||||
- name: Create bundles
|
||||
run: |
|
||||
cd ${GITHUB_WORKSPACE}/build
|
||||
echo "::group::Run CPack"
|
||||
cpack
|
||||
echo "::endgroup::"
|
||||
|
||||
echo "::group::Cleanup"
|
||||
# Remove the sha256 files CPack generates; we will do this ourself at
|
||||
# the end of this workflow.
|
||||
rm -f bundles/*.sha256
|
||||
echo "::endgroup::"
|
||||
|
||||
- name: Store bundles
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: openttd-linux-generic
|
||||
path: build/bundles
|
||||
retention-days: 5
|
198
.github/workflows/release-macos.yml
vendored
Normal file
198
.github/workflows/release-macos.yml
vendored
Normal file
@@ -0,0 +1,198 @@
|
||||
name: Release (MacOS)
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
|
||||
jobs:
|
||||
macos:
|
||||
name: MacOS
|
||||
|
||||
runs-on: macos-11
|
||||
env:
|
||||
MACOSX_DEPLOYMENT_TARGET: 10.13
|
||||
|
||||
steps:
|
||||
- name: Download source
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: internal-source
|
||||
|
||||
- name: Unpack source
|
||||
run: |
|
||||
tar -xf source.tar.gz --strip-components=1
|
||||
|
||||
- name: Install dependencies
|
||||
env:
|
||||
HOMEBREW_NO_AUTO_UPDATE: 1
|
||||
HOMEBREW_NO_INSTALL_CLEANUP: 1
|
||||
run: |
|
||||
brew install \
|
||||
pandoc \
|
||||
pkg-config \
|
||||
# EOF
|
||||
|
||||
- name: Prepare cache key
|
||||
id: key
|
||||
run: |
|
||||
echo "image=$ImageOS-$ImageVersion" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Enable vcpkg cache
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: /usr/local/share/vcpkg/installed
|
||||
key: ${{ steps.key.outputs.image }}-vcpkg-release-0 # Increase the number whenever dependencies are modified
|
||||
restore-keys: |
|
||||
${{ steps.key.outputs.image }}-vcpkg-release
|
||||
${{ steps.key.outputs.image }}-vcpkg-x64
|
||||
|
||||
- name: Prepare vcpkg
|
||||
run: |
|
||||
vcpkg install \
|
||||
liblzma:x64-osx \
|
||||
liblzma:arm64-osx \
|
||||
libpng:x64-osx \
|
||||
libpng:arm64-osx \
|
||||
lzo:x64-osx \
|
||||
lzo:arm64-osx \
|
||||
zlib:x64-osx \
|
||||
zlib:arm64-osx \
|
||||
# EOF
|
||||
|
||||
- name: Install GCC problem matcher
|
||||
uses: ammaraskar/gcc-problem-matcher@master
|
||||
|
||||
- name: Build tools
|
||||
run: |
|
||||
mkdir build-host
|
||||
cd build-host
|
||||
|
||||
echo "::group::CMake"
|
||||
cmake ${GITHUB_WORKSPACE} \
|
||||
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
|
||||
-DOPTION_TOOLS_ONLY=ON \
|
||||
# EOF
|
||||
echo "::endgroup::"
|
||||
|
||||
echo "::group::Build tools"
|
||||
echo "Running on $(sysctl -n hw.logicalcpu) cores"
|
||||
cmake --build . -j $(sysctl -n hw.logicalcpu) --target tools
|
||||
echo "::endgroup::"
|
||||
|
||||
- name: Import code signing certificates
|
||||
uses: Apple-Actions/import-codesign-certs@v1
|
||||
with:
|
||||
# The certificates in a PKCS12 file encoded as a base64 string
|
||||
p12-file-base64: ${{ secrets.APPLE_DEVELOPER_CERTIFICATE_P12_BASE64 }}
|
||||
# The password used to import the PKCS12 file.
|
||||
p12-password: ${{ secrets.APPLE_DEVELOPER_CERTIFICATE_PASSWORD }}
|
||||
# If this is run on a fork, there may not be a certificate set up - continue in this case
|
||||
continue-on-error: true
|
||||
|
||||
- name: Build arm64
|
||||
run: |
|
||||
mkdir build-arm64
|
||||
cd build-arm64
|
||||
|
||||
echo "::group::CMake"
|
||||
cmake ${GITHUB_WORKSPACE} \
|
||||
-DCMAKE_OSX_ARCHITECTURES=arm64 \
|
||||
-DVCPKG_TARGET_TRIPLET=arm64-osx \
|
||||
-DCMAKE_TOOLCHAIN_FILE=/usr/local/share/vcpkg/scripts/buildsystems/vcpkg.cmake \
|
||||
-DHOST_BINARY_DIR=${GITHUB_WORKSPACE}/build-host \
|
||||
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
|
||||
# EOF
|
||||
echo "::endgroup::"
|
||||
|
||||
echo "::group::Build"
|
||||
echo "Running on $(sysctl -n hw.logicalcpu) cores"
|
||||
cmake --build . -j $(sysctl -n hw.logicalcpu)
|
||||
echo "::endgroup::"
|
||||
|
||||
- name: Build x64
|
||||
run: |
|
||||
mkdir build-x64
|
||||
cd build-x64
|
||||
|
||||
echo "::group::CMake"
|
||||
cmake ${GITHUB_WORKSPACE} \
|
||||
-DCMAKE_OSX_ARCHITECTURES=x86_64 \
|
||||
-DVCPKG_TARGET_TRIPLET=x64-osx \
|
||||
-DCMAKE_TOOLCHAIN_FILE=/usr/local/share/vcpkg/scripts/buildsystems/vcpkg.cmake \
|
||||
-DHOST_BINARY_DIR=${GITHUB_WORKSPACE}/build-host \
|
||||
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
|
||||
-DCPACK_BUNDLE_APPLE_CERT_APP=${{ secrets.APPLE_DEVELOPER_CERTIFICATE_ID }} \
|
||||
"-DCPACK_BUNDLE_APPLE_CODESIGN_PARAMETER=--deep -f --options runtime" \
|
||||
-DAPPLE_UNIVERSAL_PACKAGE=1 \
|
||||
# EOF
|
||||
echo "::endgroup::"
|
||||
|
||||
echo "::group::Build"
|
||||
echo "Running on $(sysctl -n hw.logicalcpu) cores"
|
||||
cmake --build . -j $(sysctl -n hw.logicalcpu)
|
||||
echo "::endgroup::"
|
||||
|
||||
- name: Create bundles
|
||||
run: |
|
||||
cd build-x64
|
||||
|
||||
echo "::group::Create universal binary"
|
||||
# Combine the `openttd` binaries from each build into a single file
|
||||
lipo -create -output openttd-universal ../build-*/openttd
|
||||
mv openttd-universal openttd
|
||||
echo "::endgroup::"
|
||||
|
||||
echo "::group::Run CPack"
|
||||
cpack
|
||||
echo "::endgroup::"
|
||||
|
||||
echo "::group::Cleanup"
|
||||
# Remove the sha256 files CPack generates; we will do this ourself at
|
||||
# the end of this workflow.
|
||||
rm -f bundles/*.sha256
|
||||
echo "::endgroup::"
|
||||
|
||||
- name: Install gon
|
||||
env:
|
||||
HOMEBREW_NO_AUTO_UPDATE: 1
|
||||
HOMEBREW_NO_INSTALL_CLEANUP: 1
|
||||
run: |
|
||||
brew tap mitchellh/gon
|
||||
brew install mitchellh/gon/gon
|
||||
|
||||
- name: Notarize
|
||||
env:
|
||||
AC_USERNAME: ${{ secrets.APPLE_DEVELOPER_APP_USERNAME }}
|
||||
AC_PASSWORD: ${{ secrets.APPLE_DEVELOPER_APP_PASSWORD }}
|
||||
run: |
|
||||
cd build-x64
|
||||
../os/macosx/notarize.sh
|
||||
|
||||
- name: Build zip
|
||||
run: |
|
||||
cd build-x64
|
||||
|
||||
pushd _CPack_Packages/*/Bundle/openttd-*/
|
||||
|
||||
# Remove the Applications symlink from the staging folder
|
||||
rm -f Applications
|
||||
|
||||
# Remove the original dmg built by CPack to avoid a conflict when resolving
|
||||
# the zip_filename variable below
|
||||
rm -f ../*.dmg
|
||||
|
||||
zip_filename=(../openttd-*)
|
||||
|
||||
# Package up the existing, notarised .app into a zip file
|
||||
zip -r -9 ${zip_filename}.zip OpenTTD.app
|
||||
|
||||
popd
|
||||
|
||||
# Now move it into place to be uploaded
|
||||
mv _CPack_Packages/*/Bundle/openttd-*.zip bundles/
|
||||
|
||||
- name: Store bundles
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: openttd-macos-universal
|
||||
path: build-x64/bundles
|
||||
retention-days: 5
|
186
.github/workflows/release-source.yml
vendored
Normal file
186
.github/workflows/release-source.yml
vendored
Normal file
@@ -0,0 +1,186 @@
|
||||
name: Release (Source)
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
outputs:
|
||||
version:
|
||||
value: ${{ jobs.source.outputs.version }}
|
||||
is_tag:
|
||||
value: ${{ jobs.source.outputs.is_tag }}
|
||||
trigger_type:
|
||||
value: ${{ jobs.source.outputs.trigger_type }}
|
||||
folder:
|
||||
value: ${{ jobs.source.outputs.folder }}
|
||||
|
||||
jobs:
|
||||
source:
|
||||
name: Source
|
||||
|
||||
runs-on: ubuntu-20.04
|
||||
|
||||
outputs:
|
||||
version: ${{ steps.metadata.outputs.version }}
|
||||
is_tag: ${{ steps.metadata.outputs.is_tag }}
|
||||
trigger_type: ${{ steps.metadata.outputs.trigger_type }}
|
||||
folder: ${{ steps.metadata.outputs.folder }}
|
||||
|
||||
steps:
|
||||
- name: Checkout (Release)
|
||||
if: github.event_name == 'release'
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
# We generate a changelog; for this we need the full git log.
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Checkout (Manual)
|
||||
if: github.event_name == 'workflow_dispatch'
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
ref: ${{ github.event.inputs.ref }}
|
||||
# We generate a changelog; for this we need the full git log.
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Checkout (Trigger)
|
||||
if: github.event_name == 'repository_dispatch'
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
ref: ${{ github.event.client_payload.ref }}
|
||||
# We generate a changelog; for this we need the full git log.
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Check valid branch name
|
||||
run: |
|
||||
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
|
||||
REF="${{ github.event.inputs.ref }}"
|
||||
elif [ "${{ github.event_name }}" = "repository_dispatch" ]; then
|
||||
REF="${{ github.event.client_payload.ref }}"
|
||||
else
|
||||
REF="${{ github.ref }}"
|
||||
fi
|
||||
|
||||
# Check if we are a tag.
|
||||
if [ -n "$(git name-rev --name-only --tags --no-undefined HEAD 2>/dev/null || false)" ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Check if the checkout caused the branch to be named.
|
||||
if [ "$(git rev-parse --abbrev-ref HEAD)" != "HEAD" ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Check if this was a pull request.
|
||||
if [ -n "$(echo ${REF} | grep '^refs/pull/[0-9]*')" ]; then
|
||||
PULL=$(echo ${REF} | cut -d/ -f3)
|
||||
git checkout -b pr${PULL}
|
||||
fi
|
||||
|
||||
# Are we still in a detached state? Error out.
|
||||
if [ "$(git rev-parse --abbrev-ref HEAD)" == "HEAD" ]; then
|
||||
echo "The 'ref' given resulted in a checkout of a detached HEAD."
|
||||
echo "We cannot detect the version for these checkout accurate."
|
||||
echo ""
|
||||
echo "If you want to build a Pull Request, make sure you use 'refs/pull/NNN/head'."
|
||||
echo ""
|
||||
echo "Cancelling build, as without a version we cannot store the artifacts."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Generate metadata
|
||||
id: metadata
|
||||
run: |
|
||||
echo "::group::Prepare metadata files"
|
||||
cmake -DGENERATE_OTTDREV=1 -P cmake/scripts/FindVersion.cmake
|
||||
./.github/changelog.sh > .changelog
|
||||
TZ='UTC' date +"%Y-%m-%d %H:%M UTC" > .release_date
|
||||
cat .ottdrev | cut -f 1 -d$'\t' > .version
|
||||
|
||||
if [ $(cat .ottdrev | cut -f 5 -d$'\t') = '1' ]; then
|
||||
# Assume that all tags are always releases. Why else make a tag?
|
||||
IS_TAG="true"
|
||||
|
||||
FOLDER="${{ env.FOLDER_RELEASES }}"
|
||||
TRIGGER_TYPE="new-tag"
|
||||
else
|
||||
IS_TAG="false"
|
||||
|
||||
BRANCH=$(git symbolic-ref -q HEAD | sed 's@.*/@@')
|
||||
if [ -z "${BRANCH}" ]; then
|
||||
echo "Internal error: branch name is empty."
|
||||
echo "An earlier step should have prevented this from happening."
|
||||
echo "Cancelling build, as without a branch name we cannot store the artifacts"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "${BRANCH}" = "${{ env.NIGHTLIES_BRANCH }}" ]; then
|
||||
# The "master" branch is special, and we call a nightly.
|
||||
FOLDER="${{ env.FOLDER_NIGHTLIES }}/$(date +%Y)"
|
||||
TRIGGER_TYPE="new-master"
|
||||
else
|
||||
# All other branches, which can be builds of Pull Requests, are
|
||||
# put in their own folder.
|
||||
FOLDER="${{ env.FOLDER_BRANCHES }}/${BRANCH}"
|
||||
TRIGGER_TYPE="new-branch"
|
||||
fi
|
||||
fi
|
||||
|
||||
mkdir -p build/bundles
|
||||
cp .changelog build/bundles/changelog.txt
|
||||
cp .release_date build/bundles/released.txt
|
||||
cp README.md build/bundles/README.md
|
||||
echo "::endgroup::"
|
||||
|
||||
echo "Release Date: $(cat .release_date)"
|
||||
echo "Revision: $(cat .ottdrev)"
|
||||
echo "Version: $(cat .version)"
|
||||
echo "Is tag: ${IS_TAG}"
|
||||
echo "Folder on CDN: ${FOLDER}"
|
||||
echo "Workflow trigger: ${TRIGGER_TYPE}"
|
||||
|
||||
echo "version=$(cat .version)" >> $GITHUB_OUTPUT
|
||||
echo "is_tag=${IS_TAG}" >> $GITHUB_OUTPUT
|
||||
echo "folder=${FOLDER}" >> $GITHUB_OUTPUT
|
||||
echo "trigger_type=${TRIGGER_TYPE}" >> $GITHUB_OUTPUT
|
||||
env:
|
||||
NIGHTLIES_BRANCH: master
|
||||
FOLDER_RELEASES: openttd-releases
|
||||
FOLDER_NIGHTLIES: openttd-nightlies
|
||||
FOLDER_BRANCHES: openttd-branches
|
||||
|
||||
- name: Remove VCS information
|
||||
run: |
|
||||
rm -rf .git
|
||||
|
||||
- name: Create bundles
|
||||
run: |
|
||||
FOLDER_NAME=openttd-${{ steps.metadata.outputs.version }}
|
||||
|
||||
# Rename the folder to openttd-NNN
|
||||
mkdir ${FOLDER_NAME}
|
||||
find . -maxdepth 1 -not -name . -not -name build -not -name ${FOLDER_NAME} -exec mv {} ${FOLDER_NAME}/ \;
|
||||
|
||||
echo "::group::Create tarball (xz) bundle"
|
||||
tar --xz -cvf build/bundles/${FOLDER_NAME}-source.tar.xz ${FOLDER_NAME}
|
||||
echo "::endgroup::"
|
||||
|
||||
# This tarball is only to be used within this workflow.
|
||||
echo "::group::Create tarball (gz) bundle"
|
||||
tar --gzip -cvf source.tar.gz ${FOLDER_NAME}
|
||||
echo "::endgroup::"
|
||||
|
||||
echo "::group::Create zip bundle"
|
||||
zip -9 -r build/bundles/${FOLDER_NAME}-source.zip ${FOLDER_NAME}
|
||||
echo "::endgroup::"
|
||||
|
||||
- name: Store bundles
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: openttd-source
|
||||
path: build/bundles/*
|
||||
retention-days: 5
|
||||
|
||||
- name: Store source (for other jobs)
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: internal-source
|
||||
path: source.tar.gz
|
||||
retention-days: 1
|
192
.github/workflows/release-windows-store.yml
vendored
Normal file
192
.github/workflows/release-windows-store.yml
vendored
Normal file
@@ -0,0 +1,192 @@
|
||||
name: Release (Windows Store)
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
version:
|
||||
required: true
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
windows-store:
|
||||
name: Windows Store
|
||||
|
||||
runs-on: windows-latest
|
||||
|
||||
steps:
|
||||
- name: Download source
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: internal-source
|
||||
|
||||
- name: Unpack source
|
||||
shell: bash
|
||||
run: |
|
||||
tar -xf source.tar.gz --strip-components=1
|
||||
|
||||
- name: Download x86 build
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: openttd-windows-x86
|
||||
|
||||
- name: Download x64 build
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: openttd-windows-x64
|
||||
|
||||
- name: Download arm64 build
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: openttd-windows-arm64
|
||||
|
||||
- name: Unpack builds
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir builds
|
||||
cd builds
|
||||
|
||||
function extract {
|
||||
mkdir $1
|
||||
|
||||
# Extract the zip version of the release
|
||||
unzip ../openttd-*-windows-$2.zip -d $1
|
||||
|
||||
# Remove the extraneous directory
|
||||
mv $1/openttd-*-windows-$2/* $1/
|
||||
rmdir $1/openttd-*-windows-$2
|
||||
|
||||
# Move the openttd.exe to the '{arch}-binaries' folder
|
||||
mkdir $1-binaries
|
||||
mv $1/openttd.exe $1-binaries/
|
||||
}
|
||||
|
||||
extract x86 win32
|
||||
extract x64 win64
|
||||
extract arm64 arm64
|
||||
|
||||
# Use the "x86" folder as the source of the common binaries (lang files, etc) and remove the others
|
||||
mv x86 common-binaries
|
||||
rm -rf x64 arm64
|
||||
|
||||
- name: Install OpenGFX
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir -p builds/common-binaries/baseset
|
||||
cd builds/common-binaries/baseset
|
||||
|
||||
echo "::group::Download OpenGFX"
|
||||
curl -L https://cdn.openttd.org/opengfx-releases/7.1/opengfx-7.1-all.zip -o opengfx-all.zip
|
||||
echo "::endgroup::"
|
||||
|
||||
echo "::group::Unpack OpenGFX"
|
||||
unzip opengfx-all.zip
|
||||
tar xf opengfx-*.tar
|
||||
echo "::endgroup::"
|
||||
|
||||
rm -f opengfx-all.zip opengfx-*.tar
|
||||
|
||||
- name: Install OpenMSX
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir -p builds/common-binaries/baseset
|
||||
cd builds/common-binaries/baseset
|
||||
|
||||
echo "::group::Download OpenMSX"
|
||||
curl -L https://cdn.openttd.org/openmsx-releases/0.4.2/openmsx-0.4.2-all.zip -o openmsx-all.zip
|
||||
echo "::endgroup::"
|
||||
|
||||
echo "::group::Unpack OpenGFX"
|
||||
unzip openmsx-all.zip
|
||||
tar xf openmsx-*.tar
|
||||
echo "::endgroup::"
|
||||
|
||||
rm -f openmsx-all.zip openmsx-*.tar
|
||||
|
||||
- name: Install OpenSFX
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir -p builds/common-binaries/baseset/opensfx
|
||||
cd builds/common-binaries/baseset/opensfx
|
||||
|
||||
echo "::group::Download OpenSFX"
|
||||
curl -L https://cdn.openttd.org/opensfx-releases/1.0.3/opensfx-1.0.3-all.zip -o opensfx-all.zip
|
||||
echo "::endgroup::"
|
||||
|
||||
echo "::group::Unpack OpenSFX"
|
||||
unzip opensfx-all.zip
|
||||
tar xf opensfx-*.tar
|
||||
echo "::endgroup::"
|
||||
|
||||
rm -f opensfx-all.zip opensfx-*.tar
|
||||
|
||||
- name: Generate signing certificate
|
||||
shell: cmd
|
||||
run: |
|
||||
cd builds
|
||||
|
||||
REM We need to provide a signed .appx to the Windows Store, so generate a certificate with password 'password'
|
||||
call ..\os\windows\winstore\generate-key.bat "CN=78024DA8-4BE4-4C77-B12E-547BBF7359D2" password cert.pfx
|
||||
|
||||
- name: Generate assets
|
||||
shell: cmd
|
||||
run: |
|
||||
cd os\windows\winstore
|
||||
call generate-assets.bat
|
||||
|
||||
- name: Prepare manifests
|
||||
shell: cmd
|
||||
run: |
|
||||
cd builds
|
||||
mkdir manifests
|
||||
|
||||
REM Set the version environment variable
|
||||
call ..\os\windows\winstore\set-version.bat x86-binaries\openttd.exe
|
||||
|
||||
call ..\os\windows\winstore\prepare-manifests.bat manifests "CN=78024DA8-4BE4-4C77-B12E-547BBF7359D2" "57420OpenTTDDevelopers.OpenTTDofficial"
|
||||
|
||||
- name: Prepare binaries
|
||||
shell: bash
|
||||
run: |
|
||||
cd builds
|
||||
|
||||
# Copy the Windows Store assets
|
||||
mkdir common-binaries/Assets
|
||||
cp -R ../os/windows/winstore/assets-common/* common-binaries/Assets/
|
||||
|
||||
mkdir Assets
|
||||
cp -R ../os/windows/winstore/assets/* Assets/
|
||||
|
||||
cp manifests/*.xml .
|
||||
|
||||
- name: Build
|
||||
shell: cmd
|
||||
run: |
|
||||
REM Add the Windows SDK tools to the PATH
|
||||
|
||||
echo|set /p="SET VS_INSTALLDIR=" > _vspath.bat
|
||||
vswhere -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath >> _vspath.bat
|
||||
call _vspath.bat
|
||||
call "%VS_INSTALLDIR%\Common7\Tools\VsDevCmd.bat"
|
||||
|
||||
REM Set the version environment variable
|
||||
call os\windows\winstore\set-version.bat builds\x86-binaries\openttd.exe
|
||||
|
||||
cd builds
|
||||
mkdir output
|
||||
|
||||
REM Build and sign the package
|
||||
makeappx build /v /f PackagingLayout.xml /op output\ /bv %OTTD_VERSION% /pv %OTTD_VERSION% /ca
|
||||
SignTool sign /fd sha256 /a /f cert.pfx /p password "output\OpenTTD.appxbundle"
|
||||
|
||||
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.appxbundle bundles\internal\openttd-${{ inputs.version }}-windows-store.appxbundle
|
||||
|
||||
- name: Store bundles
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: openttd-windows-store
|
||||
path: builds/bundles
|
||||
retention-days: 5
|
201
.github/workflows/release-windows.yml
vendored
Normal file
201
.github/workflows/release-windows.yml
vendored
Normal file
@@ -0,0 +1,201 @@
|
||||
name: Release (Windows)
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
is_tag:
|
||||
required: true
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
windows:
|
||||
name: Windows
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- arch: x86
|
||||
host: x86
|
||||
- arch: x64
|
||||
host: x64
|
||||
- arch: arm64
|
||||
host: x64_arm64
|
||||
|
||||
runs-on: windows-latest
|
||||
|
||||
steps:
|
||||
- name: Download source
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: internal-source
|
||||
|
||||
- name: Unpack source
|
||||
shell: bash
|
||||
run: |
|
||||
tar -xf source.tar.gz --strip-components=1
|
||||
|
||||
- name: Install dependencies
|
||||
shell: bash
|
||||
run: |
|
||||
choco install pandoc
|
||||
|
||||
- name: Prepare cache key
|
||||
id: key
|
||||
shell: powershell
|
||||
run: |
|
||||
# Work around caching failure with GNU tar
|
||||
New-Item -Type Junction -Path vcpkg -Target c:\vcpkg
|
||||
|
||||
Write-Output "image=$env:ImageOS-$env:ImageVersion" >> $env:GITHUB_OUTPUT
|
||||
|
||||
- name: Enable vcpkg cache
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: vcpkg/installed
|
||||
key: ${{ steps.key.outputs.image }}-vcpkg-${{ matrix.arch }}-0 # Increase the number whenever dependencies are modified
|
||||
restore-keys: |
|
||||
${{ steps.key.outputs.image }}-vcpkg-${{ matrix.arch }}
|
||||
|
||||
- name: Prepare vcpkg
|
||||
shell: bash
|
||||
run: |
|
||||
vcpkg install --triplet=${{ matrix.arch }}-windows-static \
|
||||
liblzma \
|
||||
libpng \
|
||||
lzo \
|
||||
zlib \
|
||||
# EOF
|
||||
|
||||
- name: Install MSVC problem matcher
|
||||
uses: ammaraskar/msvc-problem-matcher@master
|
||||
|
||||
- name: Configure developer command prompt for tools
|
||||
uses: ilammy/msvc-dev-cmd@v1
|
||||
with:
|
||||
arch: x64
|
||||
|
||||
- name: Build tools
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir build-host
|
||||
cd build-host
|
||||
|
||||
echo "::group::CMake"
|
||||
cmake ${GITHUB_WORKSPACE} \
|
||||
-GNinja \
|
||||
-DOPTION_TOOLS_ONLY=ON \
|
||||
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
|
||||
# EOF
|
||||
echo "::endgroup::"
|
||||
|
||||
echo "::group::Build"
|
||||
cmake --build . --target tools
|
||||
echo "::endgroup::"
|
||||
|
||||
- name: Configure developer command prompt for ${{ matrix.arch }}
|
||||
uses: ilammy/msvc-dev-cmd@v1
|
||||
with:
|
||||
arch: ${{ matrix.host }}
|
||||
|
||||
- name: Import code signing certificate
|
||||
shell: powershell
|
||||
# If this is run on a fork, there may not be a certificate set up - continue in this case
|
||||
continue-on-error: true
|
||||
run: |
|
||||
$tempFile = [System.IO.Path]::GetTempFileName()
|
||||
$bytes = [System.Convert]::FromBase64String($env:WINDOWS_CERTIFICATE_P12)
|
||||
[IO.File]::WriteAllBytes($tempFile, $bytes)
|
||||
$pwd = ConvertTo-SecureString $env:WINDOWS_CERTIFICATE_PASSWORD -AsPlainText -Force
|
||||
Import-PfxCertificate -FilePath $tempFile -CertStoreLocation Cert:\CurrentUser\My -Password $pwd
|
||||
Remove-Item $tempFile
|
||||
env:
|
||||
WINDOWS_CERTIFICATE_P12: ${{ secrets.WINDOWS_CERTIFICATE_P12 }}
|
||||
WINDOWS_CERTIFICATE_PASSWORD: ${{ secrets.WINDOWS_CERTIFICATE_PASSWORD }}
|
||||
|
||||
- name: Build (with installer)
|
||||
if: inputs.is_tag == 'true'
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir build
|
||||
cd build
|
||||
|
||||
echo "::group::CMake"
|
||||
cmake ${GITHUB_WORKSPACE} \
|
||||
-GNinja \
|
||||
-DVCPKG_TARGET_TRIPLET=${{ matrix.arch }}-windows-static \
|
||||
-DCMAKE_TOOLCHAIN_FILE="c:\vcpkg\scripts\buildsystems\vcpkg.cmake" \
|
||||
-DOPTION_USE_NSIS=ON \
|
||||
-DHOST_BINARY_DIR=${GITHUB_WORKSPACE}/build-host \
|
||||
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
|
||||
-DWINDOWS_CERTIFICATE_COMMON_NAME="${WINDOWS_CERTIFICATE_COMMON_NAME}" \
|
||||
# EOF
|
||||
echo "::endgroup::"
|
||||
|
||||
echo "::group::Build"
|
||||
cmake --build .
|
||||
echo "::endgroup::"
|
||||
env:
|
||||
WINDOWS_CERTIFICATE_COMMON_NAME: ${{ secrets.WINDOWS_CERTIFICATE_COMMON_NAME }}
|
||||
|
||||
- name: Build (without installer)
|
||||
if: inputs.is_tag != 'true'
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir build
|
||||
cd build
|
||||
|
||||
echo "::group::CMake"
|
||||
cmake ${GITHUB_WORKSPACE} \
|
||||
-GNinja \
|
||||
-DVCPKG_TARGET_TRIPLET=${{ matrix.arch }}-windows-static \
|
||||
-DCMAKE_TOOLCHAIN_FILE="c:\vcpkg\scripts\buildsystems\vcpkg.cmake" \
|
||||
-DHOST_BINARY_DIR=${GITHUB_WORKSPACE}/build-host \
|
||||
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
|
||||
-DWINDOWS_CERTIFICATE_COMMON_NAME="${WINDOWS_CERTIFICATE_COMMON_NAME}" \
|
||||
# EOF
|
||||
echo "::endgroup::"
|
||||
|
||||
echo "::group::Build"
|
||||
cmake --build .
|
||||
echo "::endgroup::"
|
||||
env:
|
||||
WINDOWS_CERTIFICATE_COMMON_NAME: ${{ secrets.WINDOWS_CERTIFICATE_COMMON_NAME }}
|
||||
|
||||
- name: Create bundles
|
||||
shell: bash
|
||||
run: |
|
||||
cd ${GITHUB_WORKSPACE}/build
|
||||
echo "::group::Run CPack"
|
||||
cpack
|
||||
echo "::endgroup::"
|
||||
|
||||
echo "::group::Prepare PDB to be bundled"
|
||||
PDB=$(ls bundles/*.zip | cut -d/ -f2 | sed 's/.zip$/.pdb/')
|
||||
cp openttd.pdb bundles/${PDB}
|
||||
xz -9 bundles/${PDB}
|
||||
echo "::endgroup::"
|
||||
|
||||
echo "::group::Cleanup"
|
||||
# Remove the sha256 files CPack generates; we will do this ourself at
|
||||
# the end of this workflow.
|
||||
rm -f bundles/*.sha256
|
||||
echo "::endgroup::"
|
||||
|
||||
- name: Sign installer
|
||||
if: inputs.is_tag == 'true'
|
||||
shell: bash
|
||||
# If this is run on a fork, there may not be a certificate set up - continue in this case
|
||||
continue-on-error: true
|
||||
run: |
|
||||
cd ${GITHUB_WORKSPACE}/build/bundles
|
||||
../../os/windows/sign.bat *.exe "${WINDOWS_CERTIFICATE_COMMON_NAME}"
|
||||
env:
|
||||
WINDOWS_CERTIFICATE_COMMON_NAME: ${{ secrets.WINDOWS_CERTIFICATE_COMMON_NAME }}
|
||||
|
||||
- name: Store bundles
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: openttd-windows-${{ matrix.arch }}
|
||||
path: build/bundles
|
||||
retention-days: 5
|
1137
.github/workflows/release.yml
vendored
1137
.github/workflows/release.yml
vendored
File diff suppressed because it is too large
Load Diff
76
.github/workflows/upload-aws.yml
vendored
Normal file
76
.github/workflows/upload-aws.yml
vendored
Normal file
@@ -0,0 +1,76 @@
|
||||
name: Upload (AWS)
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
version:
|
||||
required: true
|
||||
type: string
|
||||
folder:
|
||||
required: true
|
||||
type: string
|
||||
trigger_type:
|
||||
required: true
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
upload:
|
||||
name: Upload (AWS)
|
||||
|
||||
runs-on: ubuntu-20.04
|
||||
|
||||
steps:
|
||||
- name: Download all bundles
|
||||
uses: actions/download-artifact@v3
|
||||
|
||||
- name: Calculate checksums
|
||||
run: |
|
||||
echo "::group::Move bundles to a single folder"
|
||||
mkdir bundles
|
||||
mv openttd-*/* bundles/
|
||||
echo "::endgroup::"
|
||||
|
||||
cd bundles
|
||||
for i in $(ls openttd-*); do
|
||||
echo "::group::Calculating checksums for ${i}"
|
||||
openssl dgst -r -md5 -hex $i > $i.md5sum
|
||||
openssl dgst -r -sha1 -hex $i > $i.sha1sum
|
||||
openssl dgst -r -sha256 -hex $i > $i.sha256sum
|
||||
echo "::endgroup::"
|
||||
done
|
||||
|
||||
# Some targets generate files that are meant for our-eyes-only.
|
||||
# They are stored in the "internal" folder, and contains bundles
|
||||
# for targets like Windows Store. No user has a benefit of knowing
|
||||
# they exist, hence: internal.
|
||||
if [ -e internal ]; then
|
||||
cd internal
|
||||
for i in $(ls openttd-*); do
|
||||
echo "::group::Calculating checksums for ${i}"
|
||||
openssl dgst -r -md5 -hex $i > $i.md5sum
|
||||
openssl dgst -r -sha1 -hex $i > $i.sha1sum
|
||||
openssl dgst -r -sha256 -hex $i > $i.sha256sum
|
||||
echo "::endgroup::"
|
||||
done
|
||||
fi
|
||||
|
||||
- name: Upload bundles to AWS
|
||||
run: |
|
||||
aws s3 cp --recursive --only-show-errors bundles/ s3://${{ secrets.CDN_S3_BUCKET }}/${{ inputs.folder }}/${{ inputs.version }}/
|
||||
|
||||
# We do not invalidate the CloudFront distribution here. The trigger
|
||||
# for "New OpenTTD release" first updated the manifest files and
|
||||
# creates an index.html. We invalidate after that, so everything
|
||||
# becomes visible at once.
|
||||
env:
|
||||
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
||||
AWS_DEFAULT_REGION: ${{ secrets.AWS_REGION }}
|
||||
|
||||
- name: Trigger 'New OpenTTD release'
|
||||
uses: peter-evans/repository-dispatch@v2
|
||||
with:
|
||||
token: ${{ secrets.DEPLOYMENT_TOKEN }}
|
||||
repository: OpenTTD/workflows
|
||||
event-type: ${{ inputs.trigger_type }}
|
||||
client-payload: '{"version": "${{ inputs.version }}", "folder": "${{ inputs.folder }}"}'
|
124
.github/workflows/upload-gog.yml
vendored
Normal file
124
.github/workflows/upload-gog.yml
vendored
Normal file
@@ -0,0 +1,124 @@
|
||||
name: Upload (GOG)
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
version:
|
||||
required: true
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
upload:
|
||||
name: Upload (GOG)
|
||||
|
||||
runs-on: ubuntu-20.04
|
||||
|
||||
steps:
|
||||
- name: Download all bundles
|
||||
uses: actions/download-artifact@v3
|
||||
|
||||
- name: Install GOG Galaxy Build Creator
|
||||
run: |
|
||||
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
|
||||
run: |
|
||||
mkdir -p gog/opengfx/baseset
|
||||
cd gog/opengfx/baseset
|
||||
|
||||
echo "::group::Download OpenGFX"
|
||||
curl -L https://cdn.openttd.org/opengfx-releases/7.1/opengfx-7.1-all.zip -o opengfx-all.zip
|
||||
echo "::endgroup::"
|
||||
|
||||
echo "::group::Unpack OpenGFX"
|
||||
unzip opengfx-all.zip
|
||||
echo "::endgroup::"
|
||||
|
||||
rm -f opengfx-all.zip
|
||||
|
||||
- name: Install OpenMSX
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir -p gog/openmsx/baseset
|
||||
cd gog/openmsx/baseset
|
||||
|
||||
echo "::group::Download OpenMSX"
|
||||
curl -L https://cdn.openttd.org/openmsx-releases/0.4.2/openmsx-0.4.2-all.zip -o openmsx-all.zip
|
||||
echo "::endgroup::"
|
||||
|
||||
echo "::group::Unpack OpenGFX"
|
||||
unzip openmsx-all.zip
|
||||
tar xf openmsx-*.tar
|
||||
echo "::endgroup::"
|
||||
|
||||
rm -f openmsx-all.zip openmsx-*.tar
|
||||
|
||||
- name: Install OpenSFX
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir -p gog/opensfx/baseset
|
||||
cd gog/opensfx/baseset
|
||||
|
||||
echo "::group::Download OpenSFX"
|
||||
curl -L https://cdn.openttd.org/opensfx-releases/1.0.3/opensfx-1.0.3-all.zip -o opensfx-all.zip
|
||||
echo "::endgroup::"
|
||||
|
||||
echo "::group::Unpack OpenSFX"
|
||||
unzip opensfx-all.zip
|
||||
tar xf opensfx-*.tar
|
||||
echo "::endgroup::"
|
||||
|
||||
rm -f opensfx-all.zip opensfx-*.tar
|
||||
|
||||
- name: Upload to GOG
|
||||
run: |
|
||||
echo "::group::Extracting source"
|
||||
mkdir source
|
||||
(
|
||||
cd source
|
||||
tar -xf ../internal-source/source.tar.gz --strip-components=1
|
||||
)
|
||||
echo "::endgroup::"
|
||||
|
||||
(
|
||||
cd gog
|
||||
|
||||
echo "::group::Prepare Win32"
|
||||
unzip ../openttd-windows-x86/openttd-*-windows-win32.zip
|
||||
mv openttd-*-windows-win32 win32
|
||||
echo "::endgroup::"
|
||||
|
||||
echo "::group::Prepare Win64"
|
||||
unzip ../openttd-windows-x64/openttd-*-windows-win64.zip
|
||||
mv openttd-*-windows-win64 win64
|
||||
echo "::endgroup::"
|
||||
|
||||
echo "::group::Prepare macOS"
|
||||
mkdir macos
|
||||
(
|
||||
cd macos
|
||||
unzip ../../openttd-macos-universal/openttd-*-macos-universal.zip
|
||||
)
|
||||
echo "::endgroup::"
|
||||
|
||||
echo "::group::Prepare Linux"
|
||||
tar xvf ../openttd-linux-generic/openttd-*-linux-generic-amd64.tar.xz
|
||||
mv openttd-*-linux-generic-amd64 linux
|
||||
echo "::endgroup::"
|
||||
|
||||
echo "::group::Preparing build files"
|
||||
cp ../source/os/gog/*.json .
|
||||
for json in $(ls *.json); do
|
||||
sed -i 's/VERSION/${{ inputs.version }}/g;s/CLIENT_ID/${{ secrets.GOG_CLIENT_ID }}/g;s/CLIENT_SECRET/${{ secrets.GOG_CLIENT_SECRET }}/g' ${json}
|
||||
done
|
||||
echo "::endgroup::"
|
||||
|
||||
echo "::group::Upload to GOG"
|
||||
../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::"
|
||||
)
|
82
.github/workflows/upload-steam.yml
vendored
Normal file
82
.github/workflows/upload-steam.yml
vendored
Normal file
@@ -0,0 +1,82 @@
|
||||
name: Upload (Steam)
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
version:
|
||||
required: true
|
||||
type: string
|
||||
trigger_type:
|
||||
required: true
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
upload:
|
||||
name: Upload (Steam)
|
||||
|
||||
runs-on: ubuntu-20.04
|
||||
|
||||
steps:
|
||||
- name: Download all bundles
|
||||
uses: actions/download-artifact@v3
|
||||
|
||||
- name: Setup steamcmd
|
||||
uses: CyberAndrii/setup-steamcmd@v1
|
||||
|
||||
- name: Generate Steam auth code
|
||||
id: steam-totp
|
||||
uses: CyberAndrii/steam-totp@v1
|
||||
with:
|
||||
shared_secret: ${{ secrets.STEAM_SHARED_SECRET }}
|
||||
|
||||
- name: Upload to Steam
|
||||
run: |
|
||||
echo "::group::Extracting source"
|
||||
mkdir source
|
||||
(
|
||||
cd source
|
||||
tar -xf ../internal-source/source.tar.gz --strip-components=1
|
||||
)
|
||||
echo "::endgroup::"
|
||||
|
||||
mkdir steam
|
||||
(
|
||||
cd steam
|
||||
|
||||
echo "::group::Prepare Win32"
|
||||
unzip ../openttd-windows-x86/openttd-*-windows-win32.zip
|
||||
mv openttd-*-windows-win32 steam-win32
|
||||
echo "::endgroup::"
|
||||
|
||||
echo "::group::Prepare Win64"
|
||||
unzip ../openttd-windows-x64/openttd-*-windows-win64.zip
|
||||
mv openttd-*-windows-win64 steam-win64
|
||||
echo "::endgroup::"
|
||||
|
||||
echo "::group::Prepare macOS"
|
||||
mkdir steam-macos
|
||||
(
|
||||
cd steam-macos
|
||||
unzip ../../openttd-macos-universal/openttd-*-macos-universal.zip
|
||||
)
|
||||
echo "::endgroup::"
|
||||
|
||||
echo "::group::Prepare Linux"
|
||||
tar xvf ../openttd-linux-generic/openttd-*-linux-generic-amd64.tar.xz
|
||||
mv openttd-*-linux-generic-amd64 steam-linux
|
||||
echo "::endgroup::"
|
||||
|
||||
echo "::group::Preparing build file"
|
||||
if [ "${{ inputs.trigger_type }}" = "new-tag" ]; then
|
||||
BRANCH="testing"
|
||||
else
|
||||
BRANCH="nightly"
|
||||
fi
|
||||
cat ../source/os/steam/release.vdf | sed 's/@@DESCRIPTION@@/openttd-${{ inputs.version }}/;s/@@BRANCH@@/'${BRANCH}'/' > release.vdf
|
||||
cat release.vdf
|
||||
echo "::endgroup::"
|
||||
|
||||
echo "::group::Upload to Steam"
|
||||
steamcmd +login ${{ secrets.STEAM_USERNAME }} ${{ secrets.STEAM_PASSWORD }} ${{ steps.steam-totp.outputs.code }} +run_app_build $(pwd)/release.vdf +quit
|
||||
echo "::endgroup::"
|
||||
)
|
@@ -5,7 +5,7 @@ if(NOT BINARY_NAME)
|
||||
endif()
|
||||
|
||||
project(${BINARY_NAME}
|
||||
VERSION 13.0
|
||||
VERSION 13.2
|
||||
)
|
||||
|
||||
if(CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR)
|
||||
@@ -22,7 +22,7 @@ if (EMSCRIPTEN)
|
||||
endif()
|
||||
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake")
|
||||
set(CMAKE_OSX_DEPLOYMENT_TARGET 10.14)
|
||||
set(CMAKE_OSX_DEPLOYMENT_TARGET 10.13)
|
||||
|
||||
# Use GNUInstallDirs to allow customisation
|
||||
# but set our own default data and bin dir
|
||||
@@ -280,6 +280,8 @@ if(NOT OPTION_DEDICATED)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
include(CheckAtomic)
|
||||
|
||||
if(APPLE)
|
||||
link_package(Iconv TARGET Iconv::Iconv)
|
||||
|
||||
|
@@ -41,7 +41,6 @@ INHERIT_DOCS = YES
|
||||
SEPARATE_MEMBER_PAGES = NO
|
||||
TAB_SIZE = 2
|
||||
ALIASES =
|
||||
TCL_SUBST =
|
||||
OPTIMIZE_OUTPUT_FOR_C = YES
|
||||
OPTIMIZE_OUTPUT_JAVA = NO
|
||||
OPTIMIZE_FOR_FORTRAN = NO
|
||||
@@ -155,7 +154,6 @@ VERBATIM_HEADERS = YES
|
||||
# Configuration options related to the alphabetical class index
|
||||
#---------------------------------------------------------------------------
|
||||
ALPHABETICAL_INDEX = NO
|
||||
COLS_IN_ALPHA_INDEX = 5
|
||||
IGNORE_PREFIX =
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options related to the HTML output
|
||||
@@ -224,7 +222,7 @@ LATEX_OUTPUT = latex
|
||||
LATEX_CMD_NAME = latex
|
||||
MAKEINDEX_CMD_NAME = makeindex
|
||||
COMPACT_LATEX = NO
|
||||
PAPER_TYPE = a4wide
|
||||
PAPER_TYPE = a4
|
||||
EXTRA_PACKAGES =
|
||||
LATEX_HEADER =
|
||||
LATEX_FOOTER =
|
||||
|
@@ -13,6 +13,7 @@ set(AI_COMPAT_SOURCE_FILES
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/compat_1.10.nut
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/compat_1.11.nut
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/compat_12.nut
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/compat_13.nut
|
||||
)
|
||||
|
||||
foreach(AI_COMPAT_SOURCE_FILE IN LISTS AI_COMPAT_SOURCE_FILES)
|
||||
|
@@ -10,6 +10,7 @@ set(GS_COMPAT_SOURCE_FILES
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/compat_1.10.nut
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/compat_1.11.nut
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/compat_12.nut
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/compat_13.nut
|
||||
)
|
||||
|
||||
foreach(GS_COMPAT_SOURCE_FILE IN LISTS GS_COMPAT_SOURCE_FILES)
|
||||
|
147
changelog.txt
147
changelog.txt
@@ -1,3 +1,150 @@
|
||||
13.2.1 (2023-06-11)
|
||||
------------------------------------------------------------------------
|
||||
Fix: [Win32] use full monitor resolution for fullscreen (#10985)
|
||||
|
||||
|
||||
13.2 (2023-06-10)
|
||||
------------------------------------------------------------------------
|
||||
Change: [Win32] position window in center of workspace of primary display (#10942)
|
||||
Change: Automatically disable hardware acceleration when GPU driver crashed the game last attempt (#10928)
|
||||
Change: [Linux] Default scroll mode to non-mouse-lock (#10920)
|
||||
Change: Include font style in font name for Freetype (#10879)
|
||||
Fix: Don't restore backed up vehicle name if it's no longer unique (#10979)
|
||||
Fix #10975: Train name wrongly marked as unique when joining trains (#10976)
|
||||
Fix: Crash when not even a single row fits for dropdowns on low resolution screens (#10934)
|
||||
Fix: Crash with tooltip on low resolution screens (#10933)
|
||||
Fix: Crash when window can't be placed on low resolution screens (#10932)
|
||||
Fix #10502: Apply engine refit before attaching free wagons (#10926)
|
||||
Fix: Wayland crash on startup due to Pango also using FontConfig (#10916)
|
||||
Fix: When syncing width of GUI items, take padding into account (#10915)
|
||||
Fix: Make dropdowns self-close when losing focus (#10912)
|
||||
Fix: Land info window maximum width was not scaled (#10894)
|
||||
Fix: Check max member count in squirrel classes (#10883)
|
||||
Fix: Ask FontConfig for the face index when opening fonts (#10878)
|
||||
Fix #10831: Level crossing parts left barred after crossing tile removal (#10874)
|
||||
Fix: Rail waypoint selection window not closed when parent windows closed (#10873)
|
||||
Fix #10846: [Script] Crash on trying to allocate an excessively large array (#10848)
|
||||
Fix: [Win32] Text line breaking did not properly handle punctuation characters (#10775)
|
||||
Fix: [Emscripten] Crash when saving games (#10758)
|
||||
Fix: [Win32] Wrong multi-line text layout due to incorrect whitespace handling (#10752)
|
||||
Fix #10741: Rail platforms left partially reserved after train crash (#10751)
|
||||
Fix: Shaded engines in purchase list incorrectly shaded (#10736)
|
||||
Fix #10735: [NewGRF] {POP_COLOUR} fails if string is drawn with extra flags (#10736)
|
||||
Fix #8177: Ships with max speed overflow to near-zero speed (#10695)
|
||||
Fix #10289: Don't silently fail when setting timetable start dates (#10690)
|
||||
Fix #8302: Improve "Maintenance intervals are in percents" helptext (#10686)
|
||||
Fix #10665: "No vehicles are available yet" message did not appear correctly on non-temperate climates (#10673)
|
||||
Fix #10630: Don't allow shifting service date earlier than year 0 (#10643)
|
||||
Fix #10637, #10638: Incorrect water infrastructure totals when building certain object types (#10639, #10640)
|
||||
Fix: Abort loading savegame if road vehicle is on invalid road type (#10622)
|
||||
|
||||
|
||||
13.1 (2023-04-10)
|
||||
------------------------------------------------------------------------
|
||||
Add: [NewGRF] Engine name callback for nested variants. (#10399)
|
||||
Fix: Improve main toolbar tooltips (#10616)
|
||||
Fix: [NewGRF] Additional validation for Action3 (+others) (#10601)
|
||||
Fix: Clear button for editbox didn't take account of padding (#10583)
|
||||
Fix: [Script] Access to enum/consts defined outside of main.nut (#10573)
|
||||
Fix #10568: Bogus warning when loading a save with a NewGRFs on dedicated servers (#10572)
|
||||
Fix #10554: Crash when scrolling in the autoreplace window with collapsed variants (#10555)
|
||||
Fix: Network server highlight invisible with RTL languages. (#10551)
|
||||
Fix: Client name was not being used as company manager name (#10535)
|
||||
Fix: Prevent road vehicles on crossing from crashing into the side of a train (#10496)
|
||||
Fix #10477: [macOS] Calculation for window sizes when using custom fonts was being rounded incorrectly (#10489)
|
||||
Fix #10486: Crash in debug window when GS started before AIs (#10487)
|
||||
Fix #10469: [Script] Negative numbers in League Table window were sorted incorrectly (#10471)
|
||||
Fix #10465: Crash on timeout if user never enters a password for server (#10466)
|
||||
Fix #10280, #10461: Crash on opening town windows as a spectator (#10462)
|
||||
Fix #10059: Script config values stored in the config file could cause crashes (#10444)
|
||||
|
||||
|
||||
13.0 (2023-02-05)
|
||||
------------------------------------------------------------------------
|
||||
Change #10077: Make maximum loan a positive multiple of the loan interval (#10355)
|
||||
Fix #10361: [Script] Don't try to give saved data to a dead script (#10433)
|
||||
Fix #10419: Water infrastructure accounting when building ship depots and docks (#10432)
|
||||
|
||||
|
||||
13.0-RC2 (2023-01-28)
|
||||
------------------------------------------------------------------------
|
||||
Feature: Press Ctrl to build a diagonal area of trees (#10342)
|
||||
Feature: Set a custom number of industries in map generation window (#10340)
|
||||
Change: Display font status as aa/noaa instead of true/false (#10352)
|
||||
Fix: [Script] Improved API documentation for scripts (#10413, #10412)
|
||||
Fix #10255: Reduce basic thickness of linkgraph GUI lines (#10410)
|
||||
Fix #10220: Don't select unselectable engine as default (#10404)
|
||||
Fix #10395: When loading old saves, don't forcibly bar level crossings (#10400)
|
||||
Fix #10377: Bad sorting of rail vehicles when primary variant is missing (#10378)
|
||||
Fix #10368: Server restarting game caused clients to hit assertion (#10369)
|
||||
Fix #10362: NewGRF bridges without speed limits (#10365)
|
||||
Fix #10363: CargoDist setting helptext shouldn't suggest symmetric distribution for diamonds in subtropic (#10364)
|
||||
Fix: [Script] Switch to OWNER_TOWN prevented OWNER_DEITY test during industry prospecting (#10360)
|
||||
Fix #10009: Bad overflow protection when taking out loans (#10359)
|
||||
Fix #9865: Removing files with the console always failed (#10357)
|
||||
Fix #10057: FallbackParagraphLayout fails to properly wrap (#10356)
|
||||
Fix #10177: Company list password padlock showed after switching to single player (#10354)
|
||||
Fix: Various Wide River issues (#10348)
|
||||
Fix: Link variants to parents when finalising engines (#10346)
|
||||
Fix #10333: Only show industry prospecting errors to local company (#10338)
|
||||
Fix #10335: Set initial scrollbar count for object GUI (#10336)
|
||||
Fix #10331: Starting new company during load must happen after AI start (#10332)
|
||||
Fix #10309: [SDL] Uninitialized width and height when turning off full screen (#10328)
|
||||
Fix #10032: Capacities of articulated vehicles in build window (#10326)
|
||||
Fix: Improve handling of corrupt NewGRF or image files (#10321, #10316)
|
||||
Fix: [NewGRF] Don't assume engclass 2 should be elrail (#10315)
|
||||
Fix: [Script] AIGroup.GetProfitLastYear could get values different than those displayed in GUI (#10227)
|
||||
Fix #10304: [Scripts] Don't start GS in intro game (#10305)
|
||||
Fix: [Script] Copy compat files for version 13 (#10303)
|
||||
|
||||
|
||||
13.0-RC1 (2023-01-01)
|
||||
------------------------------------------------------------------------
|
||||
Feature: 'font' console command to configure fonts within game (#10278)
|
||||
Feature: Ctrl-click to bulk edit timetable speeds/waiting times (#10265)
|
||||
Feature: [NewGRF] Vehicle variants in expandable purchase list (#10220)
|
||||
Feature: Expand all towns in the scenario editor (#10215)
|
||||
Add: [NewGRF] Slope-aware and roadtype-specific one-way sprites (#10282)
|
||||
Change: Display text files in black (#10291)
|
||||
Change: Make vehicle list dropdown buttons resize to fit strings (#10286)
|
||||
Change: [NewGRF] Support flipping shorter engines without explicit support (#10262)
|
||||
Change: Separate ground sprite from foundation sprite offsets (#10256)
|
||||
Change: Vertically centre sprite font relative to TrueType font (#10254)
|
||||
Change: [macOS] Set minimum macOS version to 10.13 (#10253)
|
||||
Change: Use lowered not disabled widget for current vehicle details tab (#10252)
|
||||
Change: Various improvements to NewGRF sprite aligner (#10249)
|
||||
Change: reset_engines console command now rerandomises introduction dates and reliability (#10220)
|
||||
Change: Show error message on failed industry prospecting (#10202)
|
||||
Fix: Local authority window rating list height ignored icon sizes (#10285)
|
||||
Fix #10150: Town signs could be truncated when using custom fonts (#10283)
|
||||
Fix #8971: Resize QueryStrings with interface scale change (#10281)
|
||||
Fix #10274: Crash when rescanning scripts with GS selected (#10276)
|
||||
Fix #10151: Use smaller padding for signs (#10272)
|
||||
Fix #10263: [Script] Restore tile validation for commands (#10269)
|
||||
Fix: Missing scrollbar for rail/roadtype dropdowns (#10264)
|
||||
Fix #10260: Incorrect rect height drawing image in vehicle details (#10261)
|
||||
Fix #10257: Incorrect catenary position on sloped bridge heads (#10258)
|
||||
Fix: Vertically centre chat prompt (#10250)
|
||||
Fix #10214: League and graph buttons in toolbar did not have a default action (#10246)
|
||||
Fix #10242: Allow a space for text shadow when clipping text (#10243)
|
||||
Fix #10206: Fully disable scripts in intro game (#10241)
|
||||
Fix #10218: Don't try to create river tiles along incorrect slopes (#10235)
|
||||
Fix #10208: [NewGRF] Allow using a specific underlay for road/tram tunnels (#10233)
|
||||
Fix #10224: Don't change fast-forward mode while saving (#10230)
|
||||
Fix #10147: Sound effect volume slider no longer set volume (#10228)
|
||||
Fix #10223: Crash when vehicle cloning fails on order cloning (#10225)
|
||||
Fix: Maximum space for engine preview image was never scaled (#10219)
|
||||
Fix #10216: Crash when upgrading savegame with crashed vehicles (#10217)
|
||||
Fix #10212: [Script] Nested ScriptAccounting scopes not restored properly (#10213)
|
||||
Fix #10114: Incorrect drag-highlight position with non-power-of-2 scaling (#10211)
|
||||
Fix #10198: Rearrange Intro GUI to make button rows narrower (#10203)
|
||||
Fix: Missing extra padding when drawing tooltip text (#10201)
|
||||
Fix: Bad alignment of button icons when using the original baseset (#10200)
|
||||
Fix: Signal icons incorrectly positioned in UI (#10199)
|
||||
Fix #10021: Object GUI resized when switching between different objects (#10196)
|
||||
Fix #9720: Delay start of GS/AI to after loading of savegame (#9745)
|
||||
|
||||
|
||||
13.0-beta2 (2022-11-27)
|
||||
------------------------------------------------------------------------
|
||||
Feature: Allow AI/GS to be fully modified in scenario editor (#10152)
|
||||
|
87
cmake/CheckAtomic.cmake
Normal file
87
cmake/CheckAtomic.cmake
Normal file
@@ -0,0 +1,87 @@
|
||||
# atomic builtins are required for threading support.
|
||||
|
||||
INCLUDE(CheckCXXSourceCompiles)
|
||||
INCLUDE(CheckLibraryExists)
|
||||
|
||||
# Sometimes linking against libatomic is required for atomic ops, if
|
||||
# the platform doesn't support lock-free atomics.
|
||||
|
||||
function(check_working_cxx_atomics varname)
|
||||
set(OLD_CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS})
|
||||
set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -std=c++17")
|
||||
check_cxx_source_compiles("
|
||||
#include <atomic>
|
||||
std::atomic<int> x;
|
||||
std::atomic<short> y;
|
||||
std::atomic<char> z;
|
||||
int main() {
|
||||
++z;
|
||||
++y;
|
||||
return ++x;
|
||||
}
|
||||
" ${varname})
|
||||
set(CMAKE_REQUIRED_FLAGS ${OLD_CMAKE_REQUIRED_FLAGS})
|
||||
endfunction(check_working_cxx_atomics)
|
||||
|
||||
function(check_working_cxx_atomics64 varname)
|
||||
set(OLD_CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS})
|
||||
set(CMAKE_REQUIRED_FLAGS "-std=c++17 ${CMAKE_REQUIRED_FLAGS}")
|
||||
check_cxx_source_compiles("
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
std::atomic<uint64_t> x (0);
|
||||
int main() {
|
||||
uint64_t i = x.load(std::memory_order_relaxed);
|
||||
(void)i;
|
||||
return 0;
|
||||
}
|
||||
" ${varname})
|
||||
set(CMAKE_REQUIRED_FLAGS ${OLD_CMAKE_REQUIRED_FLAGS})
|
||||
endfunction(check_working_cxx_atomics64)
|
||||
|
||||
|
||||
# Check for (non-64-bit) atomic operations.
|
||||
if(MSVC)
|
||||
set(HAVE_CXX_ATOMICS_WITHOUT_LIB True)
|
||||
else()
|
||||
# First check if atomics work without the library.
|
||||
check_working_cxx_atomics(HAVE_CXX_ATOMICS_WITHOUT_LIB)
|
||||
# If not, check if the library exists, and atomics work with it.
|
||||
if(NOT HAVE_CXX_ATOMICS_WITHOUT_LIB)
|
||||
check_library_exists(atomic __atomic_fetch_add_4 "" HAVE_LIBATOMIC)
|
||||
if(HAVE_LIBATOMIC)
|
||||
list(APPEND CMAKE_REQUIRED_LIBRARIES "atomic")
|
||||
check_working_cxx_atomics(HAVE_CXX_ATOMICS_WITH_LIB)
|
||||
if (NOT HAVE_CXX_ATOMICS_WITH_LIB)
|
||||
message(FATAL_ERROR "Host compiler must support std::atomic!")
|
||||
endif()
|
||||
else()
|
||||
message(FATAL_ERROR "Host compiler appears to require libatomic, but cannot find it.")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Check for 64 bit atomic operations.
|
||||
if(MSVC)
|
||||
set(HAVE_CXX_ATOMICS64_WITHOUT_LIB True)
|
||||
else()
|
||||
# First check if atomics work without the library.
|
||||
check_working_cxx_atomics64(HAVE_CXX_ATOMICS64_WITHOUT_LIB)
|
||||
# If not, check if the library exists, and atomics work with it.
|
||||
if(NOT HAVE_CXX_ATOMICS64_WITHOUT_LIB)
|
||||
check_library_exists(atomic __atomic_load_8 "" HAVE_CXX_LIBATOMICS64)
|
||||
if(HAVE_CXX_LIBATOMICS64)
|
||||
list(APPEND CMAKE_REQUIRED_LIBRARIES "atomic")
|
||||
check_working_cxx_atomics64(HAVE_CXX_ATOMICS64_WITH_LIB)
|
||||
if (NOT HAVE_CXX_ATOMICS64_WITH_LIB)
|
||||
message(FATAL_ERROR "Host compiler must support 64-bit std::atomic!")
|
||||
endif()
|
||||
else()
|
||||
message(FATAL_ERROR "Host compiler appears to require libatomic for 64-bit operations, but cannot find it.")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(HAVE_CXX_ATOMICS_WITH_LIB OR HAVE_CXX_ATOMICS64_WITH_LIB)
|
||||
target_link_libraries(openttd atomic)
|
||||
endif()
|
@@ -56,7 +56,7 @@ macro(compile_flags)
|
||||
|
||||
if(MSVC)
|
||||
add_compile_options(/W3)
|
||||
if(MSVC_VERSION GREATER 1929)
|
||||
if(MSVC_VERSION GREATER 1929 AND CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
|
||||
# Starting with version 19.30, there is an optimisation bug, see #9966 for details
|
||||
# This flag disables the broken optimisation to work around the bug
|
||||
add_compile_options(/d2ssa-rse-)
|
||||
@@ -136,6 +136,10 @@ macro(compile_flags)
|
||||
# -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"
|
||||
)
|
||||
endif()
|
||||
|
||||
|
@@ -56,7 +56,7 @@ function(set_options)
|
||||
|
||||
option(OPTION_DEDICATED "Build dedicated server only (no GUI)" OFF)
|
||||
option(OPTION_INSTALL_FHS "Install with Filesystem Hierarchy Standard folders" ${DEFAULT_OPTION_INSTALL_FHS})
|
||||
option(OPTION_USE_ASSERTS "Use assertions; leave enabled for nightlies, betas, and RCs" ON)
|
||||
option(OPTION_USE_ASSERTS "Use assertions; leave enabled for nightlies, betas, and RCs" OFF)
|
||||
if(EMSCRIPTEN)
|
||||
# Although pthreads is supported, it is not in a way yet that is
|
||||
# useful for us.
|
||||
|
@@ -342,7 +342,7 @@ foreach(LINE IN LISTS SOURCE_LINES)
|
||||
else()
|
||||
string(APPEND SQUIRREL_EXPORT "\n SQ${API_CLS}.PreRegister(engine, \"${API_SUPER_CLS}\");")
|
||||
endif()
|
||||
if(NOT "${SUPER_CLS}" STREQUAL "ScriptEvent")
|
||||
if(NOT "${SUPER_CLS}" MATCHES "^ScriptEvent")
|
||||
if("${CLS_PARAM_2}" STREQUAL "v")
|
||||
string(APPEND SQUIRREL_EXPORT "\n SQ${API_CLS}.AddSQAdvancedConstructor(engine);")
|
||||
else()
|
||||
|
Binary file not shown.
@@ -4,10 +4,24 @@
|
||||
// See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
-1 * 0 0C "One way road graphics"
|
||||
-1 * 3 05 09 06
|
||||
-1 sprites/oneway.png 8bpp 34 8 24 16 -12 -8 normal
|
||||
-1 sprites/oneway.png 8bpp 66 8 24 16 -12 -8 normal
|
||||
-1 sprites/oneway.png 8bpp 98 8 24 16 -12 -8 normal
|
||||
-1 sprites/oneway.png 8bpp 130 8 24 16 -12 -8 normal
|
||||
-1 sprites/oneway.png 8bpp 162 8 24 16 -12 -8 normal
|
||||
-1 sprites/oneway.png 8bpp 194 8 24 16 -12 -8 normal
|
||||
-1 * 3 05 09 12
|
||||
-1 sprites/oneway.png 8bpp 34 8 24 16 -10 -9 normal
|
||||
-1 sprites/oneway.png 8bpp 66 8 24 16 -13 -7 normal
|
||||
-1 sprites/oneway.png 8bpp 98 8 24 16 -12 -8 normal
|
||||
-1 sprites/oneway.png 8bpp 130 8 24 16 -15 -10 normal
|
||||
-1 sprites/oneway.png 8bpp 162 8 24 16 -12 -9 normal
|
||||
-1 sprites/oneway.png 8bpp 194 8 24 16 -11 -8 normal
|
||||
|
||||
-1 sprites/oneway.png 8bpp 34 40 24 16 -13 -10 normal
|
||||
-1 sprites/oneway.png 8bpp 66 40 24 16 -12 -8 normal
|
||||
-1 sprites/oneway.png 8bpp 98 40 24 16 -12 -9 normal
|
||||
-1 sprites/oneway.png 8bpp 130 40 24 16 -11 -8 normal
|
||||
-1 sprites/oneway.png 8bpp 162 40 24 16 -9 -10 normal
|
||||
-1 sprites/oneway.png 8bpp 194 40 24 16 -10 -9 normal
|
||||
|
||||
-1 sprites/oneway.png 8bpp 34 72 24 16 -8 -11 normal
|
||||
-1 sprites/oneway.png 8bpp 66 72 24 16 -11 -5 normal
|
||||
-1 sprites/oneway.png 8bpp 98 72 24 16 -12 -8 normal
|
||||
-1 sprites/oneway.png 8bpp 130 72 24 16 -12 -5 normal
|
||||
-1 sprites/oneway.png 8bpp 162 72 24 16 -14 -10 normal
|
||||
-1 sprites/oneway.png 8bpp 194 72 24 16 -12 -8 normal
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 7.7 KiB |
Binary file not shown.
Binary file not shown.
12
media/baseset/orig_extra/fix_gui_icons.nfo
Normal file
12
media/baseset/orig_extra/fix_gui_icons.nfo
Normal file
@@ -0,0 +1,12 @@
|
||||
// This file is part of OpenTTD.
|
||||
// OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
// OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
// See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
-1 * 0 0C "Fix alignment of button icons."
|
||||
|
||||
// Fix alignment of button icons.
|
||||
-1 * 11 0A 03 01 DC 02 01 E2 02 01 E6 02
|
||||
-1 sprites/fix_gui_icons.png 8bpp 8 13 20 20 0 0 normal nocrop
|
||||
-1 sprites/fix_gui_icons.png 8bpp 40 13 20 20 0 0 normal nocrop
|
||||
-1 sprites/fix_gui_icons.png 8bpp 72 13 20 20 0 0 normal nocrop
|
BIN
media/baseset/orig_extra/fix_gui_icons.png
Normal file
BIN
media/baseset/orig_extra/fix_gui_icons.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.5 KiB |
@@ -83,3 +83,4 @@
|
||||
#include "rivers/arctic.nfo"
|
||||
#include "rivers/tropic.nfo"
|
||||
#include "rivers/toyland.nfo"
|
||||
#include "fix_gui_icons.nfo"
|
||||
|
@@ -1,4 +1,4 @@
|
||||
FROM emscripten/emsdk:2.0.34
|
||||
FROM emscripten/emsdk:3.1.28
|
||||
|
||||
COPY emsdk-liblzma.patch /
|
||||
RUN cd /emsdk/upstream/emscripten && patch -p1 < /emsdk-liblzma.patch
|
||||
|
@@ -1,40 +1,38 @@
|
||||
## How to build with Emscripten
|
||||
|
||||
Building with Emscripten works with emsdk 2.0.31 and above.
|
||||
Please use docker with the supplied `Dockerfile` to build for emscripten.
|
||||
It takes care of a few things:
|
||||
- Use a version of emscripten we know works
|
||||
- Patch in LibLZMA support (as this is not supported by upstream)
|
||||
|
||||
Currently there is no LibLZMA support upstream; for this we suggest to apply
|
||||
the provided patch in this folder to your emsdk installation.
|
||||
|
||||
For convenience, a Dockerfile is supplied that does this patches for you
|
||||
against upstream emsdk docker. Best way to use it:
|
||||
|
||||
Build the docker image:
|
||||
First, build the docker image by navigating in the folder this `README.md` is in, and executing:
|
||||
```
|
||||
docker build -t emsdk-lzma .
|
||||
```
|
||||
|
||||
Build the host tools first:
|
||||
Next, navigate back to the root folder of this project.
|
||||
|
||||
Now we build the host tools first:
|
||||
```
|
||||
mkdir build-host
|
||||
docker run -it --rm -v $(pwd):$(pwd) -u $(id -u):$(id -g) --workdir $(pwd)/build-host emsdk-lzma cmake .. -DOPTION_TOOLS_ONLY=ON
|
||||
docker run -it --rm -v $(pwd):$(pwd) -u $(id -u):$(id -g) --workdir $(pwd)/build-host emsdk-lzma make -j5 tools
|
||||
docker run -it --rm -v $(pwd):$(pwd) -u $(id -u):$(id -g) --workdir $(pwd)/build-host emsdk-lzma make -j$(nproc) tools
|
||||
```
|
||||
|
||||
Next, build the game with emscripten:
|
||||
|
||||
Finally, we build the actual game:
|
||||
```
|
||||
mkdir build
|
||||
docker run -it --rm -v $(pwd):$(pwd) -u $(id -u):$(id -g) --workdir $(pwd)/build emsdk-lzma emcmake cmake .. -DHOST_BINARY_DIR=../build-host -DCMAKE_BUILD_TYPE=Release -DOPTION_USE_ASSERTS=OFF
|
||||
docker run -it --rm -v $(pwd):$(pwd) -u $(id -u):$(id -g) --workdir $(pwd)/build emsdk-lzma emmake make -j5
|
||||
docker run -it --rm -v $(pwd):$(pwd) -u $(id -u):$(id -g) --workdir $(pwd)/build emsdk-lzma emmake make -j$(nproc)
|
||||
```
|
||||
|
||||
And now you have in your build folder files like "openttd.html".
|
||||
In the `build` folder you will now see `openttd.html`.
|
||||
|
||||
To run it locally, you would have to start a local webserver, like:
|
||||
To run it locally, you would have to start a local webserver; something like:
|
||||
|
||||
```
|
||||
cd build
|
||||
python3 -m http.server
|
||||
````
|
||||
|
||||
Now you can play the game via http://127.0.0.1:8000/openttd.html .
|
||||
You can now play the game via http://127.0.0.1:8000/openttd.html .
|
||||
|
@@ -13,7 +13,7 @@ of emsdk.
|
||||
diff --git a/tools/settings.py b/tools/settings.py
|
||||
--- a/tools/settings.py
|
||||
+++ b/tools/settings.py
|
||||
@@ -38,6 +38,7 @@
|
||||
@@ -40,6 +40,7 @@ PORTS_SETTINGS = {
|
||||
'USE_SDL_NET',
|
||||
'USE_SDL_GFX',
|
||||
'USE_LIBJPEG',
|
||||
@@ -24,24 +24,23 @@ diff --git a/tools/settings.py b/tools/settings.py
|
||||
diff --git a/src/settings.js b/src/settings.js
|
||||
--- a/src/settings.js
|
||||
+++ b/src/settings.js
|
||||
@@ -1382,8 +1382,12 @@ var USE_BZIP2 = 0;
|
||||
// 1 = use libjpeg from emscripten-ports
|
||||
// [link]
|
||||
var USE_LIBJPEG = 0;
|
||||
@@ -1450,6 +1450,10 @@ var USE_GIFLIB = false;
|
||||
// [compile+link]
|
||||
var USE_LIBJPEG = false;
|
||||
|
||||
+// 1 = use liblzma from emscripten-ports
|
||||
+// [link]
|
||||
+var USE_LIBLZMA = 0;
|
||||
+// [compile+link]
|
||||
+var USE_LIBLZMA = false;
|
||||
+
|
||||
// 1 = use libpng from emscripten-ports
|
||||
// [link]
|
||||
var USE_LIBPNG = 0;
|
||||
// [compile+link]
|
||||
var USE_LIBPNG = false;
|
||||
|
||||
diff --git a/tools/ports/liblzma.py b/tools/ports/liblzma.py
|
||||
new file mode 100644
|
||||
--- /dev/null
|
||||
+++ b/tools/ports/liblzma.py
|
||||
@@ -0,0 +1,160 @@
|
||||
@@ -0,0 +1,151 @@
|
||||
+# Copyright 2020 The Emscripten Authors. All rights reserved.
|
||||
+# Emscripten is available under two separate licenses, the MIT license and the
|
||||
+# University of Illinois/NCSA Open Source License. Both these licenses can be
|
||||
@@ -52,8 +51,8 @@ new file mode 100644
|
||||
+import logging
|
||||
+from pathlib import Path
|
||||
+
|
||||
+VERSION = '5.2.5'
|
||||
+HASH = '7443674247deda2935220fbc4dfc7665e5bb5a260be8ad858c8bd7d7b9f0f868f04ea45e62eb17c0a5e6a2de7c7500ad2d201e2d668c48ca29bd9eea5a73a3ce'
|
||||
+VERSION = '5.4.0'
|
||||
+HASH = '29b2cd25bb5b234b329ffe9547692d2c29be393db9d8d4ce70a66dfdaebd54433e79a89d80c57e58cd4559c3c68b9845507d5fedf3eec1c528a81e3d9ddbd811'
|
||||
+
|
||||
+
|
||||
+def needed(settings):
|
||||
@@ -61,40 +60,31 @@ new file mode 100644
|
||||
+
|
||||
+
|
||||
+def get(ports, settings, shared):
|
||||
+ ports.fetch_project('liblzma', 'https://tukaani.org/xz/xz-' + VERSION + '.tar.gz', 'xz-' + VERSION, sha512hash=HASH)
|
||||
+ ports.fetch_project('liblzma', f'https://tukaani.org/xz/xz-{VERSION}.tar.gz', sha512hash=HASH)
|
||||
+
|
||||
+ def create(final):
|
||||
+ logging.info('building port: liblzma')
|
||||
+
|
||||
+ ports.clear_project_build('liblzma')
|
||||
+
|
||||
+ source_path = os.path.join(ports.get_dir(), 'liblzma', 'xz-' + VERSION)
|
||||
+ dest_path = os.path.join(ports.get_build_dir(), 'liblzma')
|
||||
+
|
||||
+ shared.try_delete(dest_path)
|
||||
+ os.makedirs(dest_path)
|
||||
+ shutil.rmtree(dest_path, ignore_errors=True)
|
||||
+ shutil.copytree(source_path, dest_path)
|
||||
+ source_path = os.path.join(ports.get_dir(), 'liblzma', f'xz-{VERSION}', 'src', 'liblzma')
|
||||
+ ports.write_file(os.path.join(source_path, 'config.h'), config_h)
|
||||
+ ports.install_headers(os.path.join(source_path, 'api'), pattern='lzma.h')
|
||||
+ ports.install_headers(os.path.join(source_path, 'api', 'lzma'), pattern='*.h', target='lzma')
|
||||
+
|
||||
+ build_flags = ['-DHAVE_CONFIG_H', '-DTUKLIB_SYMBOL_PREFIX=lzma_', '-fvisibility=hidden']
|
||||
+ exclude_dirs = ['xzdec', 'xz', 'lzmainfo']
|
||||
+ exclude_files = ['crc32_small.c', 'crc64_small.c', 'crc32_tablegen.c', 'crc64_tablegen.c', 'price_tablegen.c', 'fastpos_tablegen.c'
|
||||
+ exclude_files = ['crc32_small.c', 'crc64_small.c', 'crc32_tablegen.c', 'crc64_tablegen.c', 'price_tablegen.c', 'fastpos_tablegen.c',
|
||||
+ 'tuklib_exit.c', 'tuklib_mbstr_fw.c', 'tuklib_mbstr_width.c', 'tuklib_open_stdxxx.c', 'tuklib_progname.c']
|
||||
+ include_dirs_rel = ['../common', 'api', 'common', 'check', 'lz', 'rangecoder', 'lzma', 'delta', 'simple']
|
||||
+ include_dirs_rel = ['../common', 'api', 'check', 'common', 'delta', 'lz', 'lzma', 'rangecoder', 'simple']
|
||||
+
|
||||
+ Path(dest_path, os.path.join('src', 'config.h')).write_text(config_h)
|
||||
+ include_dirs = [os.path.join(source_path, p) for p in include_dirs_rel]
|
||||
+ ports.build_port(source_path, final, 'liblzma', flags=build_flags, exclude_files=exclude_files, includes=include_dirs)
|
||||
+
|
||||
+ include_dirs = [os.path.join(dest_path, 'src', 'liblzma', p) for p in include_dirs_rel]
|
||||
+ ports.build_port(os.path.join(dest_path, 'src'), final, flags=build_flags, exclude_dirs=exclude_dirs, exclude_files=exclude_files, includes=include_dirs)
|
||||
+
|
||||
+ ports.install_headers(os.path.join(dest_path, 'src', 'liblzma', 'api'), 'lzma.h')
|
||||
+ ports.install_headers(os.path.join(dest_path, 'src', 'liblzma', 'api', 'lzma'), '*.h', 'lzma')
|
||||
+
|
||||
+ return [shared.Cache.get_lib('liblzma.a', create, what='port')]
|
||||
+ return [shared.cache.get_lib('liblzma.a', create, what='port')]
|
||||
+
|
||||
+
|
||||
+def clear(ports, settings, shared):
|
||||
+ shared.Cache.erase_lib('liblzma.a')
|
||||
+ shared.cache.erase_lib('liblzma.a')
|
||||
+
|
||||
+
|
||||
+def process_args(ports):
|
||||
@@ -105,7 +95,7 @@ new file mode 100644
|
||||
+ return 'liblzma (USE_LIBLZMA=1; public domain)'
|
||||
+
|
||||
+
|
||||
+config_h = r'''
|
||||
+config_h = '''
|
||||
+#define ASSUME_RAM 128
|
||||
+#define ENABLE_NLS 1
|
||||
+#define HAVE_CHECK_CRC32 1
|
||||
@@ -177,9 +167,9 @@ new file mode 100644
|
||||
+#define PACKAGE "xz"
|
||||
+#define PACKAGE_BUGREPORT "lasse.collin@tukaani.org"
|
||||
+#define PACKAGE_NAME "XZ Utils"
|
||||
+#define PACKAGE_STRING "XZ Utils 5.2.5"
|
||||
+#define PACKAGE_STRING "XZ Utils 5.4.0"
|
||||
+#define PACKAGE_TARNAME "xz"
|
||||
+#define PACKAGE_VERSION "5.2.5"
|
||||
+#define PACKAGE_VERSION "5.4.0"
|
||||
+#define SIZEOF_SIZE_T 4
|
||||
+#define STDC_HEADERS 1
|
||||
+#define TUKLIB_CPUCORES_SYSCONF 1
|
||||
@@ -200,5 +190,5 @@ new file mode 100644
|
||||
+#ifndef __EXTENSIONS__
|
||||
+# define __EXTENSIONS__ 1
|
||||
+#endif
|
||||
+#define VERSION "5.2.5"
|
||||
+#define VERSION "5.4.0"
|
||||
+'''
|
||||
|
64
os/gog/linux.json
Executable file
64
os/gog/linux.json
Executable file
@@ -0,0 +1,64 @@
|
||||
{
|
||||
"project": {
|
||||
"baseProductId": "1293297882",
|
||||
"clientId": "CLIENT_ID",
|
||||
"clientSecret": "CLIENT_SECRET",
|
||||
"version": "VERSION",
|
||||
"installDirectory": "OpenTTD",
|
||||
"name": "OpenTTD",
|
||||
"platform": "gnu-linux",
|
||||
"tags": [
|
||||
"editor_v_1_4_0"
|
||||
],
|
||||
"languageMode": "together",
|
||||
"products": [
|
||||
{
|
||||
"name": "OpenTTD",
|
||||
"productId": "1293297882",
|
||||
"depots": [
|
||||
{
|
||||
"name": "Linux",
|
||||
"folder": "linux",
|
||||
"languages": [
|
||||
"en-US"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "OpenGFX",
|
||||
"folder": "opengfx",
|
||||
"languages": [
|
||||
"en-US"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "OpenMSX",
|
||||
"folder": "openmsx",
|
||||
"languages": [
|
||||
"en-US"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "OpenSFX",
|
||||
"folder": "opensfx",
|
||||
"languages": [
|
||||
"en-US"
|
||||
]
|
||||
}
|
||||
],
|
||||
"tasks": [
|
||||
{
|
||||
"type": "FileTask",
|
||||
"name": "OpenTTD",
|
||||
"languages": [
|
||||
"en-US"
|
||||
],
|
||||
"category": "game",
|
||||
"path": "openttd",
|
||||
"isPrimary": true
|
||||
}
|
||||
],
|
||||
"supportDepots": []
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
65
os/gog/macos.json
Executable file
65
os/gog/macos.json
Executable file
@@ -0,0 +1,65 @@
|
||||
{
|
||||
"project": {
|
||||
"baseProductId": "1293297882",
|
||||
"clientId": "CLIENT_ID",
|
||||
"clientSecret": "CLIENT_SECRET",
|
||||
"version": "VERSION",
|
||||
"installDirectory": "OpenTTD",
|
||||
"name": "OpenTTD",
|
||||
"platform": "osx",
|
||||
"tags": [
|
||||
"editor_v_1_4_0"
|
||||
],
|
||||
"languageMode": "together",
|
||||
"products": [
|
||||
{
|
||||
"name": "OpenTTD",
|
||||
"productId": "1293297882",
|
||||
"depots": [
|
||||
{
|
||||
"name": "MacOS",
|
||||
"folder": "macos",
|
||||
"languages": [
|
||||
"en-US"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "OpenGFX",
|
||||
"folder": "opengfx",
|
||||
"languages": [
|
||||
"en-US"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "OpenMSX",
|
||||
"folder": "openmsx",
|
||||
"languages": [
|
||||
"en-US"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "OpenSFX",
|
||||
"folder": "opensfx",
|
||||
"languages": [
|
||||
"en-US"
|
||||
]
|
||||
}
|
||||
],
|
||||
"tasks": [
|
||||
{
|
||||
"type": "FileTask",
|
||||
"name": "OpenTTD",
|
||||
"languages": [
|
||||
"en-US"
|
||||
],
|
||||
"category": "game",
|
||||
"path": "OpenTTD.app/Contents/MacOS/openttd",
|
||||
"isPrimary": true
|
||||
}
|
||||
],
|
||||
"supportDepots": []
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
94
os/gog/windows.json
Executable file
94
os/gog/windows.json
Executable file
@@ -0,0 +1,94 @@
|
||||
{
|
||||
"project": {
|
||||
"baseProductId": "1293297882",
|
||||
"clientId": "CLIENT_ID",
|
||||
"clientSecret": "CLIENT_SECRET",
|
||||
"version": "VERSION",
|
||||
"installDirectory": "OpenTTD",
|
||||
"name": "OpenTTD",
|
||||
"platform": "windows",
|
||||
"tags": [
|
||||
"editor_v_1_4_0"
|
||||
],
|
||||
"languageMode": "together",
|
||||
"products": [
|
||||
{
|
||||
"name": "OpenTTD",
|
||||
"productId": "1293297882",
|
||||
"depots": [
|
||||
{
|
||||
"name": "Win32",
|
||||
"folder": "win32",
|
||||
"languages": [
|
||||
"en-US"
|
||||
],
|
||||
"osBitness": [
|
||||
"32"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Win64",
|
||||
"folder": "win64",
|
||||
"languages": [
|
||||
"en-US"
|
||||
],
|
||||
"osBitness": [
|
||||
"64"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "OpenGFX",
|
||||
"folder": "opengfx",
|
||||
"languages": [
|
||||
"en-US"
|
||||
],
|
||||
"osBitness": [
|
||||
"32",
|
||||
"64"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "OpenMSX",
|
||||
"folder": "openmsx",
|
||||
"languages": [
|
||||
"en-US"
|
||||
],
|
||||
"osBitness": [
|
||||
"32",
|
||||
"64"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "OpenSFX",
|
||||
"folder": "opensfx",
|
||||
"languages": [
|
||||
"en-US"
|
||||
],
|
||||
"osBitness": [
|
||||
"32",
|
||||
"64"
|
||||
]
|
||||
}
|
||||
],
|
||||
"tasks": [
|
||||
{
|
||||
"type": "FileTask",
|
||||
"name": "OpenTTD",
|
||||
"languages": [
|
||||
"en-US"
|
||||
],
|
||||
"category": "game",
|
||||
"path": "openttd.exe",
|
||||
"isPrimary": true,
|
||||
"osBitness": [
|
||||
"32",
|
||||
"64"
|
||||
]
|
||||
}
|
||||
],
|
||||
"supportDepots": []
|
||||
}
|
||||
],
|
||||
"scriptInterpreter": true
|
||||
}
|
||||
}
|
@@ -32,6 +32,6 @@
|
||||
<key>NSHighResolutionCapable</key>
|
||||
<string>True</string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>10.14.0</string>
|
||||
<string>10.13.0</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
@@ -45,4 +45,4 @@ ResizeImage $logoPath 1240 600 "assets\Wide310x150Logo.png"
|
||||
|
||||
# Copy the logo for the store for the common package
|
||||
New-Item -Path "." -Name "assets-common" -ItemType "directory" -Force
|
||||
Copy-Item "assets\StoreLogo.png" -Destination "assets-common\StoreLogo.png"
|
||||
Copy-Item "assets\StoreLogo.png" -Destination "assets-common\StoreLogoCommon.png"
|
||||
|
@@ -562,6 +562,25 @@ function Regression::Prices()
|
||||
print(" BT_CLEAR_WATER: " + AITile.GetBuildCost(AITile.BT_CLEAR_WATER));
|
||||
}
|
||||
|
||||
function Regression::Commands()
|
||||
{
|
||||
print("");
|
||||
print("--Commands--");
|
||||
|
||||
print(" -Command accounting-");
|
||||
local test = AITestMode();
|
||||
local costs = AIAccounting();
|
||||
AITile.DemolishTile(2834);
|
||||
print(" Command cost: " + costs.GetCosts());
|
||||
{
|
||||
local inner = AIAccounting();
|
||||
print(" New inner cost scope: " + costs.GetCosts());
|
||||
AITile.DemolishTile(2835);
|
||||
print(" Further command cost: " + costs.GetCosts());
|
||||
}
|
||||
print(" Saved cost of outer scope: " + costs.GetCosts());
|
||||
}
|
||||
|
||||
function cost_callback(old_path, new_tile, new_direction, self) { if (old_path == null) return 0; return old_path.GetCost() + 1; }
|
||||
function estimate_callback(tile, direction, goals, self) { return goals[0] - tile; }
|
||||
function neighbours_callback(path, cur_tile, self) { return [[cur_tile + 1, 1]]; }
|
||||
@@ -1684,6 +1703,7 @@ function Regression::Vehicle()
|
||||
print(" BuildVehicle(): " + AIVehicle.BuildVehicle(33417, 153));
|
||||
print(" IsValidVehicle(12): " + AIVehicle.IsValidVehicle(12));
|
||||
print(" CloneVehicle(): " + AIVehicle.CloneVehicle(33417, 12, true));
|
||||
print(" BuildVehicle(): " + AIVehicle.BuildVehicle(-1, 153));
|
||||
|
||||
local bank_after = AICompany.GetBankBalance(AICompany.COMPANY_SELF);
|
||||
|
||||
@@ -1940,6 +1960,7 @@ function Regression::Start()
|
||||
/* Do this first as it gains maximum loan (which is faked to quite a lot). */
|
||||
this.Company();
|
||||
|
||||
this.Commands();
|
||||
this.Airport();
|
||||
this.Bridge();
|
||||
this.BridgeList();
|
||||
|
@@ -788,6 +788,13 @@ ERROR: IsEnd() is invalid as Begin() is never called
|
||||
GetQuarterlyPerformanceRating(): 0
|
||||
GetQuarterlyCompanyValue(): 0
|
||||
|
||||
--Commands--
|
||||
-Command accounting-
|
||||
Command cost: 7500
|
||||
New inner cost scope: 0
|
||||
Further command cost: 30
|
||||
Saved cost of outer scope: 7500
|
||||
|
||||
--AIAirport--
|
||||
IsHangarTile(): false
|
||||
IsAirportTile(): false
|
||||
@@ -9288,6 +9295,7 @@ ERROR: IsEnd() is invalid as Begin() is never called
|
||||
BuildVehicle(): 12
|
||||
IsValidVehicle(12): true
|
||||
CloneVehicle(): 13
|
||||
BuildVehicle(): 1048575
|
||||
--Accounting--
|
||||
GetCosts(): 11894
|
||||
Should be: 11894
|
||||
|
3
src/3rdparty/squirrel/squirrel/sqclass.cpp
vendored
3
src/3rdparty/squirrel/squirrel/sqclass.cpp
vendored
@@ -65,6 +65,9 @@ bool SQClass::NewSlot(SQSharedState *ss,const SQObjectPtr &key,const SQObjectPtr
|
||||
_defaultvalues[_member_idx(temp)].val = val;
|
||||
return true;
|
||||
}
|
||||
if (_members->CountUsed() >= MEMBER_MAX_COUNT) {
|
||||
return false;
|
||||
}
|
||||
if(type(val) == OT_CLOSURE || type(val) == OT_NATIVECLOSURE || bstatic) {
|
||||
SQInteger mmidx;
|
||||
if((type(val) == OT_CLOSURE || type(val) == OT_NATIVECLOSURE) &&
|
||||
|
1
src/3rdparty/squirrel/squirrel/sqclass.h
vendored
1
src/3rdparty/squirrel/squirrel/sqclass.h
vendored
@@ -18,6 +18,7 @@ typedef sqvector<SQClassMember> SQClassMemberVec;
|
||||
|
||||
#define MEMBER_TYPE_METHOD 0x01000000
|
||||
#define MEMBER_TYPE_FIELD 0x02000000
|
||||
#define MEMBER_MAX_COUNT 0x00FFFFFF
|
||||
|
||||
#define _ismethod(o) (_integer(o)&MEMBER_TYPE_METHOD)
|
||||
#define _isfield(o) (_integer(o)&MEMBER_TYPE_FIELD)
|
||||
|
2
src/3rdparty/squirrel/squirrel/sqstate.cpp
vendored
2
src/3rdparty/squirrel/squirrel/sqstate.cpp
vendored
@@ -450,7 +450,7 @@ void RefTable::Resize(SQUnsignedInteger size)
|
||||
SQUnsignedInteger oldnumofslots = _numofslots;
|
||||
AllocNodes(size);
|
||||
//rehash
|
||||
SQUnsignedInteger nfound = 0;
|
||||
[[maybe_unused]] SQUnsignedInteger nfound = 0;
|
||||
for(SQUnsignedInteger n = 0; n < oldnumofslots; n++) {
|
||||
if(type(t->obj) != OT_NULL) {
|
||||
//add back;
|
||||
|
7
src/3rdparty/squirrel/squirrel/squtils.h
vendored
7
src/3rdparty/squirrel/squirrel/squtils.h
vendored
@@ -2,6 +2,9 @@
|
||||
#ifndef _SQUTILS_H_
|
||||
#define _SQUTILS_H_
|
||||
|
||||
#include "../../fmt/format.h"
|
||||
#include "../../../script/script_fatalerror.hpp"
|
||||
|
||||
void *sq_vm_malloc(SQUnsignedInteger size);
|
||||
void *sq_vm_realloc(void *p,SQUnsignedInteger oldsize,SQUnsignedInteger size);
|
||||
void sq_vm_free(void *p,SQUnsignedInteger size);
|
||||
@@ -102,6 +105,10 @@ private:
|
||||
void _realloc(SQUnsignedInteger newsize)
|
||||
{
|
||||
newsize = (newsize > 0)?newsize:4;
|
||||
if (newsize > SIZE_MAX / sizeof(T)) {
|
||||
std::string msg = fmt::format("cannot resize to {}", newsize);
|
||||
throw Script_FatalError(msg);
|
||||
}
|
||||
_vals = (T*)SQ_REALLOC(_vals, _allocated * sizeof(T), newsize * sizeof(T));
|
||||
_allocated = (size_t)newsize;
|
||||
}
|
||||
|
@@ -128,11 +128,6 @@ public:
|
||||
*/
|
||||
static void Save(CompanyID company);
|
||||
|
||||
/**
|
||||
* Load data for an AI from a savegame.
|
||||
*/
|
||||
static void Load(CompanyID company, int version);
|
||||
|
||||
/**
|
||||
* Get the number of days before the next AI should start.
|
||||
*/
|
||||
|
@@ -57,6 +57,8 @@
|
||||
assert(c->ai_instance == nullptr);
|
||||
c->ai_instance = new AIInstance();
|
||||
c->ai_instance->Initialize(info);
|
||||
c->ai_instance->LoadOnStack(config->GetToLoadData());
|
||||
config->SetToLoadData(nullptr);
|
||||
|
||||
cur_company.Restore();
|
||||
|
||||
@@ -289,21 +291,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ void AI::Load(CompanyID company, int version)
|
||||
{
|
||||
if (!_networking || _network_server) {
|
||||
Company *c = Company::GetIfValid(company);
|
||||
assert(c != nullptr && c->ai_instance != nullptr);
|
||||
|
||||
Backup<CompanyID> cur_company(_current_company, company, FILE_LINE);
|
||||
c->ai_instance->Load(version);
|
||||
cur_company.Restore();
|
||||
} else {
|
||||
/* Read, but ignore, the load data */
|
||||
AIInstance::LoadEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ int AI::GetStartNextTime()
|
||||
{
|
||||
/* Find the first company which doesn't exist yet */
|
||||
|
@@ -142,11 +142,11 @@ struct AIListWindow : public Window {
|
||||
break;
|
||||
}
|
||||
case WID_AIL_INFO_BG: {
|
||||
AIInfo *selected_info = nullptr;
|
||||
ScriptInfo *selected_info = nullptr;
|
||||
int i = 0;
|
||||
for (const auto &item : *this->info_list) {
|
||||
i++;
|
||||
if (this->selected == i - 1) selected_info = static_cast<AIInfo *>(item.second);
|
||||
if (this->selected == i - 1) selected_info = static_cast<ScriptInfo *>(item.second);
|
||||
}
|
||||
/* Some info about the currently selected AI. */
|
||||
if (selected_info != nullptr) {
|
||||
@@ -437,7 +437,7 @@ struct AISettingsWindow : public Window {
|
||||
|
||||
if (this->clicked_row != num) {
|
||||
this->CloseChildWindows(WC_QUERY_STRING);
|
||||
HideDropDownMenu(this);
|
||||
this->CloseChildWindows(WC_DROPDOWN_MENU);
|
||||
this->clicked_row = num;
|
||||
this->clicked_dropdown = false;
|
||||
}
|
||||
@@ -452,7 +452,7 @@ struct AISettingsWindow : public Window {
|
||||
if (!bool_item && IsInsideMM(x, 0, SETTING_BUTTON_WIDTH) && config_item.complete_labels) {
|
||||
if (this->clicked_dropdown) {
|
||||
/* unclick the dropdown */
|
||||
HideDropDownMenu(this);
|
||||
this->CloseChildWindows(WC_DROPDOWN_MENU);
|
||||
this->clicked_dropdown = false;
|
||||
this->closing_dropdown = false;
|
||||
} else {
|
||||
@@ -501,7 +501,7 @@ struct AISettingsWindow : public Window {
|
||||
} else if (!bool_item && !config_item.complete_labels) {
|
||||
/* Display a query box so users can enter a custom value. */
|
||||
SetDParam(0, old_val);
|
||||
ShowQueryString(STR_JUST_INT, STR_CONFIG_SETTING_QUERY_CAPTION, 10, this, CS_NUMERAL, QSF_NONE);
|
||||
ShowQueryString(STR_JUST_INT, STR_CONFIG_SETTING_QUERY_CAPTION, INT32_DIGITS_WITH_SIGN_AND_TERMINATION, this, CS_NUMERAL_SIGNED, QSF_NONE);
|
||||
}
|
||||
this->SetDirty();
|
||||
break;
|
||||
@@ -564,7 +564,7 @@ struct AISettingsWindow : public Window {
|
||||
void OnInvalidateData(int data = 0, bool gui_scope = true) override
|
||||
{
|
||||
this->RebuildVisibleSettings();
|
||||
HideDropDownMenu(this);
|
||||
this->CloseChildWindows(WC_DROPDOWN_MENU);
|
||||
this->CloseChildWindows(WC_QUERY_STRING);
|
||||
}
|
||||
|
||||
@@ -1114,9 +1114,7 @@ struct AIDebugWindow : public Window {
|
||||
}
|
||||
if (this->autoscroll) {
|
||||
int scroll_pos = std::max(0, log->used - this->vscroll->GetCapacity());
|
||||
if (scroll_pos != this->vscroll->GetPosition()) {
|
||||
this->vscroll->SetPosition(scroll_pos);
|
||||
|
||||
if (this->vscroll->SetPosition(scroll_pos)) {
|
||||
/* We need a repaint */
|
||||
this->SetWidgetDirty(WID_AID_SCROLLBAR);
|
||||
this->SetWidgetDirty(WID_AID_LOG_PANEL);
|
||||
|
@@ -61,6 +61,9 @@ void AIInstance::Died()
|
||||
{
|
||||
ScriptInstance::Died();
|
||||
|
||||
/* Intro is not supposed to use AI, but it may have 'dummy' AI which instant dies. */
|
||||
if (_game_mode == GM_MENU) return;
|
||||
|
||||
ShowAIDebugWindow(_current_company);
|
||||
|
||||
const AIInfo *info = AIConfig::GetConfig(_current_company, AIConfig::SSS_FORCE_GAME)->GetInfo();
|
||||
@@ -95,11 +98,10 @@ ScriptInfo *AIInstance::FindLibrary(const char *library, int version)
|
||||
* DoCommand callback function for all commands executed by AIs.
|
||||
* @param cmd cmd as given to DoCommandPInternal.
|
||||
* @param result The result of the command.
|
||||
* @param tile The tile on which the command was executed.
|
||||
* @param data Command data as given to Command<>::Post.
|
||||
* @param result_data Additional returned data from the command.
|
||||
*/
|
||||
void CcAI(Commands cmd, const CommandCost &result, TileIndex tile, const CommandDataBuffer &data, CommandDataBuffer result_data)
|
||||
void CcAI(Commands cmd, const CommandCost &result, const CommandDataBuffer &data, CommandDataBuffer result_data)
|
||||
{
|
||||
/*
|
||||
* The company might not exist anymore. Check for this.
|
||||
@@ -110,7 +112,7 @@ void CcAI(Commands cmd, const CommandCost &result, TileIndex tile, const Command
|
||||
const Company *c = Company::GetIfValid(_current_company);
|
||||
if (c == nullptr || c->ai_instance == nullptr) return;
|
||||
|
||||
if (c->ai_instance->DoCommandCallback(result, tile, data, std::move(result_data), cmd)) {
|
||||
if (c->ai_instance->DoCommandCallback(result, data, std::move(result_data), cmd)) {
|
||||
c->ai_instance->Continue();
|
||||
}
|
||||
}
|
||||
|
@@ -10,6 +10,7 @@
|
||||
#include "../stdafx.h"
|
||||
#include "../debug.h"
|
||||
#include "../network/network.h"
|
||||
#include "../openttd.h"
|
||||
#include "../core/random_func.hpp"
|
||||
|
||||
#include "../script/squirrel_class.hpp"
|
||||
@@ -59,6 +60,11 @@ void AIScannerInfo::RegisterAPI(class Squirrel *engine)
|
||||
|
||||
AIInfo *AIScannerInfo::SelectRandomAI() const
|
||||
{
|
||||
if (_game_mode == GM_MENU) {
|
||||
Debug(script, 0, "The intro game should not use AI, loading 'dummy' AI.");
|
||||
return this->info_dummy;
|
||||
}
|
||||
|
||||
uint num_random_ais = 0;
|
||||
for (const auto &item : info_single_list) {
|
||||
AIInfo *i = static_cast<AIInfo *>(item.second);
|
||||
|
@@ -34,7 +34,7 @@ void DrawAircraftDetails(const Aircraft *v, const Rect &r)
|
||||
int y = r.top;
|
||||
for (const Aircraft *u = v; u != nullptr; u = u->Next()) {
|
||||
if (u->IsNormalAircraft()) {
|
||||
SetDParam(0, u->engine_type);
|
||||
SetDParam(0, PackEngineNameDParam(u->engine_type, EngineNameContext::VehicleDetails));
|
||||
SetDParam(1, u->build_year);
|
||||
SetDParam(2, u->value);
|
||||
DrawString(r.left, r.right, y, STR_VEHICLE_INFO_BUILT_VALUE);
|
||||
|
@@ -160,41 +160,6 @@ CargoArray GetCapacityOfArticulatedParts(EngineID engine)
|
||||
return capacity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the default cargoes and refits of an articulated vehicle.
|
||||
* The refits are linked to a cargo rather than an articulated part to prevent a long list of parts.
|
||||
* @param engine Model to investigate.
|
||||
* @param[out] cargoes Total amount of units that can be transported, summed by cargo.
|
||||
* @param[out] refits Whether a (possibly partial) refit for each cargo is possible.
|
||||
* @param cargo_type Selected refitted cargo type
|
||||
* @param cargo_capacity Capacity of selected refitted cargo type
|
||||
*/
|
||||
void GetArticulatedVehicleCargoesAndRefits(EngineID engine, CargoArray *cargoes, CargoTypes *refits, CargoID cargo_type, uint cargo_capacity)
|
||||
{
|
||||
cargoes->Clear();
|
||||
*refits = 0;
|
||||
|
||||
const Engine *e = Engine::Get(engine);
|
||||
|
||||
if (cargo_type < NUM_CARGO && cargo_capacity > 0) {
|
||||
(*cargoes)[cargo_type] += cargo_capacity;
|
||||
if (IsEngineRefittable(engine)) SetBit(*refits, cargo_type);
|
||||
}
|
||||
|
||||
if (!e->IsGroundVehicle() || !HasBit(e->info.callback_mask, CBM_VEHICLE_ARTIC_ENGINE)) return;
|
||||
|
||||
for (uint i = 1; i < MAX_ARTICULATED_PARTS; i++) {
|
||||
EngineID artic_engine = GetNextArticulatedPart(i, engine);
|
||||
if (artic_engine == INVALID_ENGINE) break;
|
||||
|
||||
cargo_capacity = GetVehicleDefaultCapacity(artic_engine, &cargo_type);
|
||||
if (cargo_type < NUM_CARGO && cargo_capacity > 0) {
|
||||
(*cargoes)[cargo_type] += cargo_capacity;
|
||||
if (IsEngineRefittable(artic_engine)) SetBit(*refits, cargo_type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether any of the articulated parts is refittable
|
||||
* @param engine the first part
|
||||
|
@@ -346,7 +346,7 @@ static CommandCost BuildReplacementVehicle(Vehicle *old_veh, Vehicle **new_vehic
|
||||
|
||||
/* Build the new vehicle */
|
||||
VehicleID new_veh_id;
|
||||
std::tie(cost, new_veh_id, std::ignore, std::ignore) = Command<CMD_BUILD_VEHICLE>::Do(DC_EXEC | DC_AUTOREPLACE, old_veh->tile, e, true, CT_INVALID, INVALID_CLIENT_ID);
|
||||
std::tie(cost, new_veh_id, std::ignore, std::ignore, std::ignore) = Command<CMD_BUILD_VEHICLE>::Do(DC_EXEC | DC_AUTOREPLACE, old_veh->tile, e, true, CT_INVALID, INVALID_CLIENT_ID);
|
||||
if (cost.Failed()) return cost;
|
||||
|
||||
Vehicle *new_veh = Vehicle::Get(new_veh_id);
|
||||
@@ -422,6 +422,7 @@ static CommandCost CopyHeadSpecificThings(Vehicle *old_head, Vehicle *new_head,
|
||||
if (cost.Succeeded() && old_head != new_head && (flags & DC_EXEC) != 0) {
|
||||
/* Copy other things which cannot be copied by a command and which shall not stay resetted from the build vehicle command */
|
||||
new_head->CopyVehicleConfigAndStatistics(old_head);
|
||||
GroupStatistics::AddProfitLastYear(new_head);
|
||||
|
||||
/* Switch vehicle windows/news to the new vehicle, so they are not closed/deleted when the old vehicle is sold */
|
||||
ChangeVehicleViewports(old_head->index, new_head->index);
|
||||
|
@@ -33,11 +33,11 @@
|
||||
|
||||
#include "safeguards.h"
|
||||
|
||||
void DrawEngineList(VehicleType type, const Rect &r, const GUIEngineList *eng_list, uint16 min, uint16 max, EngineID selected_id, bool show_count, GroupID selected_group);
|
||||
void DrawEngineList(VehicleType type, const Rect &r, const GUIEngineList &eng_list, uint16 min, uint16 max, EngineID selected_id, bool show_count, GroupID selected_group);
|
||||
|
||||
static bool EngineNumberSorter(const EngineID &a, const EngineID &b)
|
||||
static bool EngineNumberSorter(const GUIEngineListItem &a, const GUIEngineListItem &b)
|
||||
{
|
||||
return Engine::Get(a)->list_position < Engine::Get(b)->list_position;
|
||||
return Engine::Get(a.engine_id)->list_position < Engine::Get(b.engine_id)->list_position;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -113,6 +113,26 @@ class ReplaceVehicleWindow : public Window {
|
||||
return true;
|
||||
}
|
||||
|
||||
void AddChildren(const GUIEngineList &source, GUIEngineList &target, EngineID parent, int indent, int side)
|
||||
{
|
||||
for (const auto &item : source) {
|
||||
if (item.variant_id != parent || item.engine_id == parent) continue;
|
||||
|
||||
const Engine *e = Engine::Get(item.engine_id);
|
||||
EngineDisplayFlags flags = item.flags;
|
||||
if (e->display_last_variant != INVALID_ENGINE) flags &= ~EngineDisplayFlags::Shaded;
|
||||
target.emplace_back(e->display_last_variant == INVALID_ENGINE ? item.engine_id : e->display_last_variant, item.engine_id, flags, indent);
|
||||
|
||||
/* Add variants if not folded */
|
||||
if ((item.flags & (EngineDisplayFlags::HasVariants | EngineDisplayFlags::IsFolded)) == EngineDisplayFlags::HasVariants) {
|
||||
/* Add this engine again as a child */
|
||||
if ((item.flags & EngineDisplayFlags::Shaded) == EngineDisplayFlags::None) {
|
||||
target.emplace_back(item.engine_id, item.engine_id, EngineDisplayFlags::None, indent + 1);
|
||||
}
|
||||
AddChildren(source, target, item.engine_id, indent + 1, side);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate an engines list
|
||||
@@ -120,12 +140,12 @@ class ReplaceVehicleWindow : public Window {
|
||||
*/
|
||||
void GenerateReplaceVehList(bool draw_left)
|
||||
{
|
||||
std::vector<EngineID> variants;
|
||||
EngineID selected_engine = INVALID_ENGINE;
|
||||
VehicleType type = (VehicleType)this->window_number;
|
||||
byte side = draw_left ? 0 : 1;
|
||||
|
||||
GUIEngineList *list = &this->engines[side];
|
||||
list->clear();
|
||||
GUIEngineList list;
|
||||
|
||||
for (const Engine *e : Engine::IterateType(type)) {
|
||||
if (!draw_left && !this->show_hidden_engines && e->IsHidden(_local_company)) continue;
|
||||
@@ -155,15 +175,37 @@ class ReplaceVehicleWindow : public Window {
|
||||
if (!CheckAutoreplaceValidity(this->sel_engine[0], eid, _local_company)) continue;
|
||||
}
|
||||
|
||||
list->push_back(eid);
|
||||
EngineDisplayFlags flags = (side == 0) ? EngineDisplayFlags::None : e->display_flags;
|
||||
if (side == 1 && eid == this->sel_engine[0]) flags |= EngineDisplayFlags::Shaded;
|
||||
list.emplace_back(eid, e->info.variant_id, flags, 0);
|
||||
|
||||
if (side == 1 && e->info.variant_id != INVALID_ENGINE) variants.push_back(e->info.variant_id);
|
||||
if (eid == this->sel_engine[side]) selected_engine = eid; // The selected engine is still in the list
|
||||
}
|
||||
|
||||
if (side == 1) {
|
||||
/* ensure primary engine of variant group is in list */
|
||||
for (const auto &variant : variants) {
|
||||
if (std::find(list.begin(), list.end(), variant) == list.end()) {
|
||||
const Engine *e = Engine::Get(variant);
|
||||
list.emplace_back(variant, e->info.variant_id, e->display_flags | EngineDisplayFlags::Shaded, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this->sel_engine[side] = selected_engine; // update which engine we selected (the same or none, if it's not in the list anymore)
|
||||
if (draw_left) {
|
||||
EngList_Sort(list, &EngineNumberSorter);
|
||||
EngList_Sort(&list, &EngineNumberSorter);
|
||||
} else {
|
||||
_engine_sort_direction = this->descending_sort_order;
|
||||
EngList_Sort(list, _engine_sort_functions[this->window_number][this->sort_criteria]);
|
||||
EngList_Sort(&list, _engine_sort_functions[this->window_number][this->sort_criteria]);
|
||||
}
|
||||
|
||||
this->engines[side].clear();
|
||||
if (side == 1) {
|
||||
AddChildren(list, this->engines[side], INVALID_ENGINE, 0, side);
|
||||
} else {
|
||||
this->engines[side].swap(list);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,7 +219,7 @@ class ReplaceVehicleWindow : public Window {
|
||||
this->GenerateReplaceVehList(true);
|
||||
this->vscroll[0]->SetCount((uint)this->engines[0].size());
|
||||
if (this->reset_sel_engine && this->sel_engine[0] == INVALID_ENGINE && this->engines[0].size() != 0) {
|
||||
this->sel_engine[0] = this->engines[0][0];
|
||||
this->sel_engine[0] = this->engines[0][0].engine_id;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -198,8 +240,8 @@ class ReplaceVehicleWindow : public Window {
|
||||
this->vscroll[1]->SetCount((uint)this->engines[1].size());
|
||||
if (this->reset_sel_engine && this->sel_engine[1] != INVALID_ENGINE) {
|
||||
int position = 0;
|
||||
for (EngineID &eid : this->engines[1]) {
|
||||
if (eid == this->sel_engine[1]) break;
|
||||
for (const auto &item : this->engines[1]) {
|
||||
if (item.engine_id == this->sel_engine[1]) break;
|
||||
++position;
|
||||
}
|
||||
this->vscroll[1]->ScrollTowards(position);
|
||||
@@ -416,7 +458,7 @@ public:
|
||||
bool when_old = false;
|
||||
EngineID e = EngineReplacementForCompany(c, this->sel_engine[0], this->sel_group, &when_old);
|
||||
str = when_old ? STR_REPLACE_REPLACING_WHEN_OLD : STR_ENGINE_NAME;
|
||||
SetDParam(0, e);
|
||||
SetDParam(0, PackEngineNameDParam(e, EngineNameContext::PurchaseList));
|
||||
}
|
||||
} else {
|
||||
str = STR_REPLACE_NOT_REPLACING_VEHICLE_SELECTED;
|
||||
@@ -433,7 +475,7 @@ public:
|
||||
EngineID end = static_cast<EngineID>(std::min<size_t>(this->vscroll[side]->GetCapacity() + start, this->engines[side].size()));
|
||||
|
||||
/* Do the actual drawing */
|
||||
DrawEngineList((VehicleType)this->window_number, r, &this->engines[side], start, end, this->sel_engine[side], side == 0, this->sel_group);
|
||||
DrawEngineList((VehicleType)this->window_number, r, this->engines[side], start, end, this->sel_engine[side], side == 0, this->sel_group);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -481,8 +523,7 @@ public:
|
||||
const Engine *e = Engine::Get(this->sel_engine[side]);
|
||||
TestedEngineDetails ted;
|
||||
ted.cost = 0;
|
||||
ted.cargo = e->GetDefaultCargoType();
|
||||
ted.capacity = e->GetDisplayDefaultCapacity(&ted.mail_capacity);
|
||||
ted.FillDefaultCapacities(e);
|
||||
|
||||
const Rect r = this->GetWidget<NWidgetBase>(side == 0 ? WID_RV_LEFT_DETAILS : WID_RV_RIGHT_DETAILS)->GetCurrentRect()
|
||||
.Shrink(WidgetDimensions::scaled.frametext, WidgetDimensions::scaled.framerect);
|
||||
@@ -579,7 +620,22 @@ public:
|
||||
uint i = this->vscroll[click_side]->GetScrolledRowFromWidget(pt.y, this, widget);
|
||||
size_t engine_count = this->engines[click_side].size();
|
||||
|
||||
EngineID e = engine_count > i ? this->engines[click_side][i] : INVALID_ENGINE;
|
||||
EngineID e = INVALID_ENGINE;
|
||||
if (i < engine_count) {
|
||||
const auto &item = this->engines[click_side][i];
|
||||
const Rect r = this->GetWidget<NWidgetBase>(widget)->GetCurrentRect().Shrink(WidgetDimensions::scaled.matrix).WithWidth(WidgetDimensions::scaled.hsep_indent * (item.indent + 1), _current_text_dir == TD_RTL);
|
||||
if ((item.flags & EngineDisplayFlags::HasVariants) != EngineDisplayFlags::None && IsInsideMM(r.left, r.right, pt.x)) {
|
||||
/* toggle folded flag on engine */
|
||||
assert(item.variant_id != INVALID_ENGINE);
|
||||
Engine *engine = Engine::Get(item.variant_id);
|
||||
engine->display_flags ^= EngineDisplayFlags::IsFolded;
|
||||
|
||||
InvalidateWindowData(WC_REPLACE_VEHICLE, (VehicleType)this->window_number, 0); // Update the autoreplace window
|
||||
InvalidateWindowClassesData(WC_BUILD_VEHICLE); // The build windows needs updating as well
|
||||
return;
|
||||
}
|
||||
if ((item.flags & EngineDisplayFlags::Shaded) == EngineDisplayFlags::None) e = item.engine_id;
|
||||
}
|
||||
|
||||
/* If Ctrl is pressed on the left side and we don't have any engines of the selected type, stop autoreplacing.
|
||||
* This is most common when we have finished autoreplacing the engine and want to remove it from the list. */
|
||||
|
@@ -367,7 +367,12 @@ bool BmpReadHeader(BmpBuffer *buffer, BmpInfo *info, BmpData *data)
|
||||
info->palette_size = ReadDword(buffer); // number of colours in palette
|
||||
SkipBytes(buffer, header_size - 16); // skip the end of info header
|
||||
}
|
||||
if (info->palette_size == 0) info->palette_size = 1 << info->bpp;
|
||||
|
||||
uint maximum_palette_size = 1U << info->bpp;
|
||||
if (info->palette_size == 0) info->palette_size = maximum_palette_size;
|
||||
|
||||
/* More palette colours than palette indices is not supported. */
|
||||
if (info->palette_size > maximum_palette_size) return false;
|
||||
|
||||
data->palette = CallocT<Colour>(info->palette_size);
|
||||
|
||||
|
@@ -130,6 +130,23 @@ private:
|
||||
this->SetWidgetDirty(WID_BBS_BRIDGE_LIST);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the StringID to draw in the selection list and set the appropriate DParams.
|
||||
* @param bridge_data the bridge to get the StringID of.
|
||||
* @return the StringID.
|
||||
*/
|
||||
StringID GetBridgeSelectString(const BuildBridgeData &bridge_data) const
|
||||
{
|
||||
SetDParam(0, bridge_data.spec->material);
|
||||
SetDParam(1, bridge_data.spec->speed);
|
||||
SetDParam(2, bridge_data.cost);
|
||||
/* If the bridge has no meaningful speed limit, don't display it. */
|
||||
if (bridge_data.spec->speed == UINT16_MAX) {
|
||||
return _game_mode == GM_EDITOR ? STR_SELECT_BRIDGE_INFO_NAME : STR_SELECT_BRIDGE_INFO_NAME_COST;
|
||||
}
|
||||
return _game_mode == GM_EDITOR ? STR_SELECT_BRIDGE_INFO_NAME_MAX_SPEED : STR_SELECT_BRIDGE_INFO_NAME_MAX_SPEED_COST;
|
||||
}
|
||||
|
||||
public:
|
||||
BuildBridgeWindow(WindowDesc *desc, TileIndex start, TileIndex end, TransportType transport_type, byte road_rail_type, GUIBridgeList *bl) : Window(desc),
|
||||
start_tile(start),
|
||||
@@ -183,14 +200,9 @@ public:
|
||||
case WID_BBS_BRIDGE_LIST: {
|
||||
Dimension sprite_dim = {0, 0}; // Biggest bridge sprite dimension
|
||||
Dimension text_dim = {0, 0}; // Biggest text dimension
|
||||
for (int i = 0; i < (int)this->bridges->size(); i++) {
|
||||
const BridgeSpec *b = this->bridges->at(i).spec;
|
||||
sprite_dim = maxdim(sprite_dim, GetSpriteSize(b->sprite));
|
||||
|
||||
SetDParam(2, this->bridges->at(i).cost);
|
||||
SetDParam(1, b->speed);
|
||||
SetDParam(0, b->material);
|
||||
text_dim = maxdim(text_dim, GetStringBoundingBox(_game_mode == GM_EDITOR ? STR_SELECT_BRIDGE_SCENEDIT_INFO : STR_SELECT_BRIDGE_INFO));
|
||||
for (const BuildBridgeData &bridge_data : *this->bridges) {
|
||||
sprite_dim = maxdim(sprite_dim, GetSpriteSize(bridge_data.spec->sprite));
|
||||
text_dim = maxdim(text_dim, GetStringBoundingBox(GetBridgeSelectString(bridge_data)));
|
||||
}
|
||||
sprite_dim.height++; // Sprite is rendered one pixel down in the matrix field.
|
||||
text_dim.height++; // Allowing the bottom row pixels to be rendered on the edge of the matrix field.
|
||||
@@ -224,15 +236,10 @@ public:
|
||||
case WID_BBS_BRIDGE_LIST: {
|
||||
Rect tr = r.WithHeight(this->resize.step_height).Shrink(WidgetDimensions::scaled.matrix);
|
||||
for (int i = this->vscroll->GetPosition(); this->vscroll->IsVisible(i) && i < (int)this->bridges->size(); i++) {
|
||||
const BridgeSpec *b = this->bridges->at(i).spec;
|
||||
|
||||
SetDParam(2, this->bridges->at(i).cost);
|
||||
SetDParam(1, b->speed);
|
||||
SetDParam(0, b->material);
|
||||
|
||||
const BuildBridgeData &bridge_data = this->bridges->at(i);
|
||||
const BridgeSpec *b = bridge_data.spec;
|
||||
DrawSprite(b->sprite, b->pal, tr.left, tr.bottom - GetSpriteSize(b->sprite).height);
|
||||
DrawStringMultiLine(tr.Indent(this->bridgetext_offset, false),
|
||||
_game_mode == GM_EDITOR ? STR_SELECT_BRIDGE_SCENEDIT_INFO : STR_SELECT_BRIDGE_INFO);
|
||||
DrawStringMultiLine(tr.Indent(this->bridgetext_offset, false), GetBridgeSelectString(bridge_data));
|
||||
tr = tr.Translate(0, this->resize.step_height);
|
||||
}
|
||||
break;
|
||||
|
@@ -106,9 +106,9 @@ static CargoID _engine_sort_last_cargo_criteria[] = {CF_ANY, CF_ANY, CF_ANY, CF_
|
||||
* @param b second engine to compare
|
||||
* @return for descending order: returns true if a < b. Vice versa for ascending order
|
||||
*/
|
||||
static bool EngineNumberSorter(const EngineID &a, const EngineID &b)
|
||||
static bool EngineNumberSorter(const GUIEngineListItem &a, const GUIEngineListItem &b)
|
||||
{
|
||||
int r = Engine::Get(a)->list_position - Engine::Get(b)->list_position;
|
||||
int r = Engine::Get(a.engine_id)->list_position - Engine::Get(b.engine_id)->list_position;
|
||||
|
||||
return _engine_sort_direction ? r > 0 : r < 0;
|
||||
}
|
||||
@@ -119,10 +119,10 @@ static bool EngineNumberSorter(const EngineID &a, const EngineID &b)
|
||||
* @param b second engine to compare
|
||||
* @return for descending order: returns true if a < b. Vice versa for ascending order
|
||||
*/
|
||||
static bool EngineIntroDateSorter(const EngineID &a, const EngineID &b)
|
||||
static bool EngineIntroDateSorter(const GUIEngineListItem &a, const GUIEngineListItem &b)
|
||||
{
|
||||
const int va = Engine::Get(a)->intro_date;
|
||||
const int vb = Engine::Get(b)->intro_date;
|
||||
const int va = Engine::Get(a.engine_id)->intro_date;
|
||||
const int vb = Engine::Get(b.engine_id)->intro_date;
|
||||
const int r = va - vb;
|
||||
|
||||
/* Use EngineID to sort instead since we want consistent sorting */
|
||||
@@ -139,19 +139,20 @@ static EngineID _last_engine[2] = { INVALID_ENGINE, INVALID_ENGINE };
|
||||
* @param b second engine to compare
|
||||
* @return for descending order: returns true if a < b. Vice versa for ascending order
|
||||
*/
|
||||
static bool EngineNameSorter(const EngineID &a, const EngineID &b)
|
||||
static bool EngineNameSorter(const GUIEngineListItem &a, const GUIEngineListItem &b)
|
||||
{
|
||||
static char last_name[2][64] = { "", "" };
|
||||
|
||||
if (a != _last_engine[0]) {
|
||||
_last_engine[0] = a;
|
||||
SetDParam(0, a);
|
||||
if (a.engine_id != _last_engine[0]) {
|
||||
_last_engine[0] = a.engine_id;
|
||||
SetDParam(0, PackEngineNameDParam(a.engine_id, EngineNameContext::PurchaseList));
|
||||
|
||||
GetString(last_name[0], STR_ENGINE_NAME, lastof(last_name[0]));
|
||||
}
|
||||
|
||||
if (b != _last_engine[1]) {
|
||||
_last_engine[1] = b;
|
||||
SetDParam(0, b);
|
||||
if (b.engine_id != _last_engine[1]) {
|
||||
_last_engine[1] = b.engine_id;
|
||||
SetDParam(0, PackEngineNameDParam(b.engine_id, EngineNameContext::PurchaseList));
|
||||
GetString(last_name[1], STR_ENGINE_NAME, lastof(last_name[1]));
|
||||
}
|
||||
|
||||
@@ -168,10 +169,10 @@ static bool EngineNameSorter(const EngineID &a, const EngineID &b)
|
||||
* @param b second engine to compare
|
||||
* @return for descending order: returns true if a < b. Vice versa for ascending order
|
||||
*/
|
||||
static bool EngineReliabilitySorter(const EngineID &a, const EngineID &b)
|
||||
static bool EngineReliabilitySorter(const GUIEngineListItem &a, const GUIEngineListItem &b)
|
||||
{
|
||||
const int va = Engine::Get(a)->reliability;
|
||||
const int vb = Engine::Get(b)->reliability;
|
||||
const int va = Engine::Get(a.engine_id)->reliability;
|
||||
const int vb = Engine::Get(b.engine_id)->reliability;
|
||||
const int r = va - vb;
|
||||
|
||||
/* Use EngineID to sort instead since we want consistent sorting */
|
||||
@@ -185,10 +186,10 @@ static bool EngineReliabilitySorter(const EngineID &a, const EngineID &b)
|
||||
* @param b second engine to compare
|
||||
* @return for descending order: returns true if a < b. Vice versa for ascending order
|
||||
*/
|
||||
static bool EngineCostSorter(const EngineID &a, const EngineID &b)
|
||||
static bool EngineCostSorter(const GUIEngineListItem &a, const GUIEngineListItem &b)
|
||||
{
|
||||
Money va = Engine::Get(a)->GetCost();
|
||||
Money vb = Engine::Get(b)->GetCost();
|
||||
Money va = Engine::Get(a.engine_id)->GetCost();
|
||||
Money vb = Engine::Get(b.engine_id)->GetCost();
|
||||
int r = ClampToI32(va - vb);
|
||||
|
||||
/* Use EngineID to sort instead since we want consistent sorting */
|
||||
@@ -202,10 +203,10 @@ static bool EngineCostSorter(const EngineID &a, const EngineID &b)
|
||||
* @param b second engine to compare
|
||||
* @return for descending order: returns true if a < b. Vice versa for ascending order
|
||||
*/
|
||||
static bool EngineSpeedSorter(const EngineID &a, const EngineID &b)
|
||||
static bool EngineSpeedSorter(const GUIEngineListItem &a, const GUIEngineListItem &b)
|
||||
{
|
||||
int va = Engine::Get(a)->GetDisplayMaxSpeed();
|
||||
int vb = Engine::Get(b)->GetDisplayMaxSpeed();
|
||||
int va = Engine::Get(a.engine_id)->GetDisplayMaxSpeed();
|
||||
int vb = Engine::Get(b.engine_id)->GetDisplayMaxSpeed();
|
||||
int r = va - vb;
|
||||
|
||||
/* Use EngineID to sort instead since we want consistent sorting */
|
||||
@@ -219,10 +220,10 @@ static bool EngineSpeedSorter(const EngineID &a, const EngineID &b)
|
||||
* @param b second engine to compare
|
||||
* @return for descending order: returns true if a < b. Vice versa for ascending order
|
||||
*/
|
||||
static bool EnginePowerSorter(const EngineID &a, const EngineID &b)
|
||||
static bool EnginePowerSorter(const GUIEngineListItem &a, const GUIEngineListItem &b)
|
||||
{
|
||||
int va = Engine::Get(a)->GetPower();
|
||||
int vb = Engine::Get(b)->GetPower();
|
||||
int va = Engine::Get(a.engine_id)->GetPower();
|
||||
int vb = Engine::Get(b.engine_id)->GetPower();
|
||||
int r = va - vb;
|
||||
|
||||
/* Use EngineID to sort instead since we want consistent sorting */
|
||||
@@ -236,10 +237,10 @@ static bool EnginePowerSorter(const EngineID &a, const EngineID &b)
|
||||
* @param b second engine to compare
|
||||
* @return for descending order: returns true if a < b. Vice versa for ascending order
|
||||
*/
|
||||
static bool EngineTractiveEffortSorter(const EngineID &a, const EngineID &b)
|
||||
static bool EngineTractiveEffortSorter(const GUIEngineListItem &a, const GUIEngineListItem &b)
|
||||
{
|
||||
int va = Engine::Get(a)->GetDisplayMaxTractiveEffort();
|
||||
int vb = Engine::Get(b)->GetDisplayMaxTractiveEffort();
|
||||
int va = Engine::Get(a.engine_id)->GetDisplayMaxTractiveEffort();
|
||||
int vb = Engine::Get(b.engine_id)->GetDisplayMaxTractiveEffort();
|
||||
int r = va - vb;
|
||||
|
||||
/* Use EngineID to sort instead since we want consistent sorting */
|
||||
@@ -253,10 +254,10 @@ static bool EngineTractiveEffortSorter(const EngineID &a, const EngineID &b)
|
||||
* @param b second engine to compare
|
||||
* @return for descending order: returns true if a < b. Vice versa for ascending order
|
||||
*/
|
||||
static bool EngineRunningCostSorter(const EngineID &a, const EngineID &b)
|
||||
static bool EngineRunningCostSorter(const GUIEngineListItem &a, const GUIEngineListItem &b)
|
||||
{
|
||||
Money va = Engine::Get(a)->GetRunningCost();
|
||||
Money vb = Engine::Get(b)->GetRunningCost();
|
||||
Money va = Engine::Get(a.engine_id)->GetRunningCost();
|
||||
Money vb = Engine::Get(b.engine_id)->GetRunningCost();
|
||||
int r = ClampToI32(va - vb);
|
||||
|
||||
/* Use EngineID to sort instead since we want consistent sorting */
|
||||
@@ -270,10 +271,10 @@ static bool EngineRunningCostSorter(const EngineID &a, const EngineID &b)
|
||||
* @param b second engine to compare
|
||||
* @return for descending order: returns true if a < b. Vice versa for ascending order
|
||||
*/
|
||||
static bool EnginePowerVsRunningCostSorter(const EngineID &a, const EngineID &b)
|
||||
static bool EnginePowerVsRunningCostSorter(const GUIEngineListItem &a, const GUIEngineListItem &b)
|
||||
{
|
||||
const Engine *e_a = Engine::Get(a);
|
||||
const Engine *e_b = Engine::Get(b);
|
||||
const Engine *e_a = Engine::Get(a.engine_id);
|
||||
const Engine *e_b = Engine::Get(b.engine_id);
|
||||
uint p_a = e_a->GetPower();
|
||||
uint p_b = e_b->GetPower();
|
||||
Money r_a = e_a->GetRunningCost();
|
||||
@@ -312,13 +313,13 @@ static bool EnginePowerVsRunningCostSorter(const EngineID &a, const EngineID &b)
|
||||
* @param b second engine to compare
|
||||
* @return for descending order: returns true if a < b. Vice versa for ascending order
|
||||
*/
|
||||
static bool TrainEngineCapacitySorter(const EngineID &a, const EngineID &b)
|
||||
static bool TrainEngineCapacitySorter(const GUIEngineListItem &a, const GUIEngineListItem &b)
|
||||
{
|
||||
const RailVehicleInfo *rvi_a = RailVehInfo(a);
|
||||
const RailVehicleInfo *rvi_b = RailVehInfo(b);
|
||||
const RailVehicleInfo *rvi_a = RailVehInfo(a.engine_id);
|
||||
const RailVehicleInfo *rvi_b = RailVehInfo(b.engine_id);
|
||||
|
||||
int va = GetTotalCapacityOfArticulatedParts(a) * (rvi_a->railveh_type == RAILVEH_MULTIHEAD ? 2 : 1);
|
||||
int vb = GetTotalCapacityOfArticulatedParts(b) * (rvi_b->railveh_type == RAILVEH_MULTIHEAD ? 2 : 1);
|
||||
int va = GetTotalCapacityOfArticulatedParts(a.engine_id) * (rvi_a->railveh_type == RAILVEH_MULTIHEAD ? 2 : 1);
|
||||
int vb = GetTotalCapacityOfArticulatedParts(b.engine_id) * (rvi_b->railveh_type == RAILVEH_MULTIHEAD ? 2 : 1);
|
||||
int r = va - vb;
|
||||
|
||||
/* Use EngineID to sort instead since we want consistent sorting */
|
||||
@@ -332,10 +333,10 @@ static bool TrainEngineCapacitySorter(const EngineID &a, const EngineID &b)
|
||||
* @param b second engine to compare
|
||||
* @return for descending order: returns true if a < b. Vice versa for ascending order
|
||||
*/
|
||||
static bool TrainEnginesThenWagonsSorter(const EngineID &a, const EngineID &b)
|
||||
static bool TrainEnginesThenWagonsSorter(const GUIEngineListItem &a, const GUIEngineListItem &b)
|
||||
{
|
||||
int val_a = (RailVehInfo(a)->railveh_type == RAILVEH_WAGON ? 1 : 0);
|
||||
int val_b = (RailVehInfo(b)->railveh_type == RAILVEH_WAGON ? 1 : 0);
|
||||
int val_a = (RailVehInfo(a.engine_id)->railveh_type == RAILVEH_WAGON ? 1 : 0);
|
||||
int val_b = (RailVehInfo(b.engine_id)->railveh_type == RAILVEH_WAGON ? 1 : 0);
|
||||
int r = val_a - val_b;
|
||||
|
||||
/* Use EngineID to sort instead since we want consistent sorting */
|
||||
@@ -351,10 +352,10 @@ static bool TrainEnginesThenWagonsSorter(const EngineID &a, const EngineID &b)
|
||||
* @param b second engine to compare
|
||||
* @return for descending order: returns true if a < b. Vice versa for ascending order
|
||||
*/
|
||||
static bool RoadVehEngineCapacitySorter(const EngineID &a, const EngineID &b)
|
||||
static bool RoadVehEngineCapacitySorter(const GUIEngineListItem &a, const GUIEngineListItem &b)
|
||||
{
|
||||
int va = GetTotalCapacityOfArticulatedParts(a);
|
||||
int vb = GetTotalCapacityOfArticulatedParts(b);
|
||||
int va = GetTotalCapacityOfArticulatedParts(a.engine_id);
|
||||
int vb = GetTotalCapacityOfArticulatedParts(b.engine_id);
|
||||
int r = va - vb;
|
||||
|
||||
/* Use EngineID to sort instead since we want consistent sorting */
|
||||
@@ -370,10 +371,10 @@ static bool RoadVehEngineCapacitySorter(const EngineID &a, const EngineID &b)
|
||||
* @param b second engine to compare
|
||||
* @return for descending order: returns true if a < b. Vice versa for ascending order
|
||||
*/
|
||||
static bool ShipEngineCapacitySorter(const EngineID &a, const EngineID &b)
|
||||
static bool ShipEngineCapacitySorter(const GUIEngineListItem &a, const GUIEngineListItem &b)
|
||||
{
|
||||
const Engine *e_a = Engine::Get(a);
|
||||
const Engine *e_b = Engine::Get(b);
|
||||
const Engine *e_a = Engine::Get(a.engine_id);
|
||||
const Engine *e_b = Engine::Get(b.engine_id);
|
||||
|
||||
int va = e_a->GetDisplayDefaultCapacity();
|
||||
int vb = e_b->GetDisplayDefaultCapacity();
|
||||
@@ -392,10 +393,10 @@ static bool ShipEngineCapacitySorter(const EngineID &a, const EngineID &b)
|
||||
* @param b second engine to compare
|
||||
* @return for descending order: returns true if a < b. Vice versa for ascending order
|
||||
*/
|
||||
static bool AircraftEngineCargoSorter(const EngineID &a, const EngineID &b)
|
||||
static bool AircraftEngineCargoSorter(const GUIEngineListItem &a, const GUIEngineListItem &b)
|
||||
{
|
||||
const Engine *e_a = Engine::Get(a);
|
||||
const Engine *e_b = Engine::Get(b);
|
||||
const Engine *e_a = Engine::Get(a.engine_id);
|
||||
const Engine *e_b = Engine::Get(b.engine_id);
|
||||
|
||||
uint16 mail_a, mail_b;
|
||||
int va = e_a->GetDisplayDefaultCapacity(&mail_a);
|
||||
@@ -420,10 +421,10 @@ static bool AircraftEngineCargoSorter(const EngineID &a, const EngineID &b)
|
||||
* @param b second engine to compare
|
||||
* @return for descending order: returns true if a < b. Vice versa for ascending order
|
||||
*/
|
||||
static bool AircraftRangeSorter(const EngineID &a, const EngineID &b)
|
||||
static bool AircraftRangeSorter(const GUIEngineListItem &a, const GUIEngineListItem &b)
|
||||
{
|
||||
uint16 r_a = Engine::Get(a)->GetRange();
|
||||
uint16 r_b = Engine::Get(b)->GetRange();
|
||||
uint16 r_a = Engine::Get(a.engine_id)->GetRange();
|
||||
uint16 r_b = Engine::Get(b.engine_id)->GetRange();
|
||||
|
||||
int r = r_a - r_b;
|
||||
|
||||
@@ -537,14 +538,14 @@ const StringID _engine_sort_listing[][12] = {{
|
||||
}};
|
||||
|
||||
/** Filters vehicles by cargo and engine (in case of rail vehicle). */
|
||||
static bool CDECL CargoAndEngineFilter(const EngineID *eid, const CargoID cid)
|
||||
static bool CDECL CargoAndEngineFilter(const GUIEngineListItem *item, const CargoID cid)
|
||||
{
|
||||
if (cid == CF_ANY) {
|
||||
return true;
|
||||
} else if (cid == CF_ENGINES) {
|
||||
return Engine::Get(*eid)->GetPower() != 0;
|
||||
return Engine::Get(item->engine_id)->GetPower() != 0;
|
||||
} else {
|
||||
CargoTypes refit_mask = GetUnionOfArticulatedRefitMasks(*eid, true) & _standard_cargo_mask;
|
||||
CargoTypes refit_mask = GetUnionOfArticulatedRefitMasks(item->engine_id, true) & _standard_cargo_mask;
|
||||
return (cid == CF_NONE ? refit_mask == 0 : HasBit(refit_mask, cid));
|
||||
}
|
||||
}
|
||||
@@ -553,18 +554,29 @@ static GUIEngineList::FilterFunction * const _filter_funcs[] = {
|
||||
&CargoAndEngineFilter,
|
||||
};
|
||||
|
||||
static int DrawCargoCapacityInfo(int left, int right, int y, EngineID engine, TestedEngineDetails &te)
|
||||
static uint GetCargoWeight(const CargoArray &cap, VehicleType vtype)
|
||||
{
|
||||
CargoArray cap;
|
||||
CargoTypes refits;
|
||||
GetArticulatedVehicleCargoesAndRefits(engine, &cap, &refits, te.cargo, te.capacity);
|
||||
|
||||
uint weight = 0;
|
||||
for (CargoID c = 0; c < NUM_CARGO; c++) {
|
||||
if (cap[c] == 0) continue;
|
||||
if (cap[c] != 0) {
|
||||
if (vtype == VEH_TRAIN) {
|
||||
weight += CargoSpec::Get(c)->WeightOfNUnitsInTrain(cap[c]);
|
||||
} else {
|
||||
weight += CargoSpec::Get(c)->WeightOfNUnits(cap[c]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return weight;
|
||||
}
|
||||
|
||||
static int DrawCargoCapacityInfo(int left, int right, int y, TestedEngineDetails &te, bool refittable)
|
||||
{
|
||||
for (CargoID c = 0; c < NUM_CARGO; c++) {
|
||||
if (te.all_capacities[c] == 0) continue;
|
||||
|
||||
SetDParam(0, c);
|
||||
SetDParam(1, cap[c]);
|
||||
SetDParam(2, HasBit(refits, c) ? STR_PURCHASE_INFO_REFITTABLE : STR_EMPTY);
|
||||
SetDParam(1, te.all_capacities[c]);
|
||||
SetDParam(2, refittable ? STR_PURCHASE_INFO_REFITTABLE : STR_EMPTY);
|
||||
DrawString(left, right, y, STR_PURCHASE_INFO_CAPACITY);
|
||||
y += FONT_HEIGHT_NORMAL;
|
||||
}
|
||||
@@ -591,8 +603,7 @@ static int DrawRailWagonPurchaseInfo(int left, int right, int y, EngineID engine
|
||||
/* Wagon weight - (including cargo) */
|
||||
uint weight = e->GetDisplayWeight();
|
||||
SetDParam(0, weight);
|
||||
uint cargo_weight = (e->CanCarryCargo() ? CargoSpec::Get(te.cargo)->WeightOfNUnitsInTrain(te.capacity) : 0);
|
||||
SetDParam(1, cargo_weight + weight);
|
||||
SetDParam(1, GetCargoWeight(te.all_capacities, VEH_TRAIN) + weight);
|
||||
DrawString(left, right, y, STR_PURCHASE_INFO_WEIGHT_CWEIGHT);
|
||||
y += FONT_HEIGHT_NORMAL;
|
||||
|
||||
@@ -685,8 +696,7 @@ static int DrawRoadVehPurchaseInfo(int left, int right, int y, EngineID engine_n
|
||||
/* Road vehicle weight - (including cargo) */
|
||||
int16 weight = e->GetDisplayWeight();
|
||||
SetDParam(0, weight);
|
||||
uint cargo_weight = (e->CanCarryCargo() ? CargoSpec::Get(te.cargo)->WeightOfNUnits(te.capacity) : 0);
|
||||
SetDParam(1, cargo_weight + weight);
|
||||
SetDParam(1, GetCargoWeight(te.all_capacities, VEH_ROAD) + weight);
|
||||
DrawString(left, right, y, STR_PURCHASE_INFO_WEIGHT_CWEIGHT);
|
||||
y += FONT_HEIGHT_NORMAL;
|
||||
|
||||
@@ -868,6 +878,21 @@ static uint ShowAdditionalText(int left, int right, int y, EngineID engine)
|
||||
return result;
|
||||
}
|
||||
|
||||
void TestedEngineDetails::FillDefaultCapacities(const Engine *e)
|
||||
{
|
||||
this->cargo = e->GetDefaultCargoType();
|
||||
if (e->type == VEH_TRAIN || e->type == VEH_ROAD) {
|
||||
this->all_capacities = GetCapacityOfArticulatedParts(e->index);
|
||||
this->capacity = this->all_capacities[this->cargo];
|
||||
this->mail_capacity = 0;
|
||||
} else {
|
||||
this->capacity = e->GetDisplayDefaultCapacity(&this->mail_capacity);
|
||||
this->all_capacities[this->cargo] = this->capacity;
|
||||
this->all_capacities[CT_MAIL] = this->mail_capacity;
|
||||
}
|
||||
if (this->all_capacities.GetCount() == 0) this->cargo = CT_INVALID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw the purchase info details of a vehicle at a given location.
|
||||
* @param left,right,y location where to draw the info
|
||||
@@ -909,7 +934,7 @@ int DrawVehiclePurchaseInfo(int left, int right, int y, EngineID engine_number,
|
||||
|
||||
if (articulated_cargo) {
|
||||
/* Cargo type + capacity, or N/A */
|
||||
int new_y = DrawCargoCapacityInfo(left, right, y, engine_number, te);
|
||||
int new_y = DrawCargoCapacityInfo(left, right, y, te, refittable);
|
||||
|
||||
if (new_y == y) {
|
||||
SetDParam(0, CT_INVALID);
|
||||
@@ -962,21 +987,22 @@ int DrawVehiclePurchaseInfo(int left, int right, int y, EngineID engine_number,
|
||||
* @param show_count Whether to show the amount of engines or not
|
||||
* @param selected_group the group to list the engines of
|
||||
*/
|
||||
void DrawEngineList(VehicleType type, const Rect &r, const GUIEngineList *eng_list, uint16 min, uint16 max, EngineID selected_id, bool show_count, GroupID selected_group)
|
||||
void DrawEngineList(VehicleType type, const Rect &r, const GUIEngineList &eng_list, uint16 min, uint16 max, EngineID selected_id, bool show_count, GroupID selected_group)
|
||||
{
|
||||
static const int sprite_y_offsets[] = { -1, -1, -2, -2 };
|
||||
|
||||
/* Obligatory sanity checks! */
|
||||
assert(max <= eng_list->size());
|
||||
assert(max <= eng_list.size());
|
||||
|
||||
bool rtl = _current_text_dir == TD_RTL;
|
||||
int step_size = GetEngineListHeight(type);
|
||||
int sprite_left = GetVehicleImageCellSize(type, EIT_PURCHASE).extend_left;
|
||||
int sprite_right = GetVehicleImageCellSize(type, EIT_PURCHASE).extend_right;
|
||||
int sprite_width = sprite_left + sprite_right;
|
||||
int circle_width = std::max(GetScaledSpriteSize(SPR_CIRCLE_FOLDED).width, GetScaledSpriteSize(SPR_CIRCLE_UNFOLDED).width);
|
||||
int linecolour = _colour_gradient[COLOUR_ORANGE][4];
|
||||
|
||||
Rect ir = r.WithHeight(step_size).Shrink(WidgetDimensions::scaled.matrix);
|
||||
int sprite_x = ir.WithWidth(sprite_width, rtl).left + sprite_left;
|
||||
int sprite_y_offset = ScaleSpriteTrad(sprite_y_offsets[type]) + ir.Height() / 2;
|
||||
|
||||
Dimension replace_icon = {0, 0};
|
||||
@@ -987,7 +1013,7 @@ void DrawEngineList(VehicleType type, const Rect &r, const GUIEngineList *eng_li
|
||||
count_width = GetStringBoundingBox(STR_TINY_BLACK_COMA).width;
|
||||
}
|
||||
|
||||
Rect tr = ir.Indent(sprite_width + WidgetDimensions::scaled.hsep_wide, rtl); // Name position
|
||||
Rect tr = ir.Indent(circle_width + WidgetDimensions::scaled.hsep_normal + sprite_width + WidgetDimensions::scaled.hsep_wide, rtl); // Name position
|
||||
Rect cr = tr.Indent(replace_icon.width + WidgetDimensions::scaled.hsep_wide, !rtl).WithWidth(count_width, !rtl); // Count position
|
||||
Rect rr = tr.WithWidth(replace_icon.width, !rtl); // Replace icon position
|
||||
if (show_count) tr = tr.Indent(count_width + WidgetDimensions::scaled.hsep_normal + replace_icon.width + WidgetDimensions::scaled.hsep_wide, !rtl);
|
||||
@@ -998,22 +1024,40 @@ void DrawEngineList(VehicleType type, const Rect &r, const GUIEngineList *eng_li
|
||||
|
||||
int y = ir.top;
|
||||
for (; min < max; min++, y += step_size) {
|
||||
const EngineID engine = (*eng_list)[min];
|
||||
const auto &item = eng_list[min];
|
||||
uint indent = item.indent * WidgetDimensions::scaled.hsep_indent;
|
||||
bool has_variants = (item.flags & EngineDisplayFlags::HasVariants) != EngineDisplayFlags::None;
|
||||
bool is_folded = (item.flags & EngineDisplayFlags::IsFolded) != EngineDisplayFlags::None;
|
||||
bool shaded = (item.flags & EngineDisplayFlags::Shaded) != EngineDisplayFlags::None;
|
||||
/* Note: num_engines is only used in the autoreplace GUI, so it is correct to use _local_company here. */
|
||||
const uint num_engines = GetGroupNumEngines(_local_company, selected_group, engine);
|
||||
const uint num_engines = GetGroupNumEngines(_local_company, selected_group, item.engine_id);
|
||||
|
||||
const Engine *e = Engine::Get(engine);
|
||||
const Engine *e = Engine::Get(item.engine_id);
|
||||
bool hidden = HasBit(e->company_hidden, _local_company);
|
||||
StringID str = hidden ? STR_HIDDEN_ENGINE_NAME : STR_ENGINE_NAME;
|
||||
TextColour tc = (engine == selected_id) ? TC_WHITE : (TC_NO_SHADE | (hidden ? TC_GREY : TC_BLACK));
|
||||
TextColour tc = (item.engine_id == selected_id) ? TC_WHITE : ((hidden | shaded) ? (TC_GREY | TC_FORCED | TC_NO_SHADE) : TC_BLACK);
|
||||
|
||||
SetDParam(0, engine);
|
||||
DrawString(tr.left, tr.right, y + normal_text_y_offset, str, tc);
|
||||
DrawVehicleEngine(r.left, r.right, sprite_x, y + sprite_y_offset, engine, (show_count && num_engines == 0) ? PALETTE_CRASH : GetEnginePalette(engine, _local_company), EIT_PURCHASE);
|
||||
SetDParam(0, PackEngineNameDParam(item.engine_id, EngineNameContext::PurchaseList, item.indent));
|
||||
Rect itr = tr.Indent(indent, rtl);
|
||||
DrawString(itr.left, itr.right, y + normal_text_y_offset, str, tc);
|
||||
int sprite_x = ir.Indent(indent + circle_width + WidgetDimensions::scaled.hsep_normal, rtl).WithWidth(sprite_width, rtl).left + sprite_left;
|
||||
DrawVehicleEngine(r.left, r.right, sprite_x, y + sprite_y_offset, item.engine_id, (show_count && num_engines == 0) ? PALETTE_CRASH : GetEnginePalette(item.engine_id, _local_company), EIT_PURCHASE);
|
||||
if (show_count) {
|
||||
SetDParam(0, num_engines);
|
||||
DrawString(cr.left, cr.right, y + small_text_y_offset, STR_TINY_BLACK_COMA, TC_FROMSTRING, SA_RIGHT | SA_FORCE);
|
||||
if (EngineHasReplacementForCompany(Company::Get(_local_company), engine, selected_group)) DrawSprite(SPR_GROUP_REPLACE_ACTIVE, num_engines == 0 ? PALETTE_CRASH : PAL_NONE, rr.left, y + replace_icon_y_offset);
|
||||
if (EngineHasReplacementForCompany(Company::Get(_local_company), item.engine_id, selected_group)) DrawSprite(SPR_GROUP_REPLACE_ACTIVE, num_engines == 0 ? PALETTE_CRASH : PAL_NONE, rr.left, y + replace_icon_y_offset);
|
||||
}
|
||||
if (has_variants) {
|
||||
Rect fr = ir.Indent(indent, rtl).WithWidth(circle_width, rtl);
|
||||
DrawSpriteIgnorePadding(is_folded ? SPR_CIRCLE_FOLDED : SPR_CIRCLE_UNFOLDED, PAL_NONE, {fr.left, y, fr.right, y + ir.Height() - 1}, false, SA_CENTER);
|
||||
}
|
||||
if (indent > 0) {
|
||||
/* Draw tree lines */
|
||||
Rect fr = ir.Indent(indent - WidgetDimensions::scaled.hsep_indent, rtl).WithWidth(circle_width, rtl);
|
||||
int ycenter = y + normal_text_y_offset + FONT_HEIGHT_NORMAL / 2;
|
||||
bool continues = (min + 1U) < eng_list.size() && eng_list[min + 1].indent == item.indent;
|
||||
GfxDrawLine(fr.left + circle_width / 2, y - WidgetDimensions::scaled.matrix.top, fr.left + circle_width / 2, continues ? y - WidgetDimensions::scaled.matrix.top + step_size - 1 : ycenter, linecolour, WidgetDimensions::scaled.fullbevel.top);
|
||||
GfxDrawLine(fr.left + circle_width / 2, ycenter, fr.right, ycenter, linecolour, WidgetDimensions::scaled.fullbevel.top);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1078,6 +1122,27 @@ struct BuildVehicleWindow : Window {
|
||||
}
|
||||
}
|
||||
|
||||
void AddChildren(const GUIEngineList &source, EngineID parent, int indent)
|
||||
{
|
||||
for (const auto &item : source) {
|
||||
if (item.variant_id != parent || item.engine_id == parent) continue;
|
||||
|
||||
const Engine *e = Engine::Get(item.engine_id);
|
||||
EngineDisplayFlags flags = item.flags;
|
||||
if (e->display_last_variant != INVALID_ENGINE) flags &= ~EngineDisplayFlags::Shaded;
|
||||
this->eng_list.emplace_back(e->display_last_variant == INVALID_ENGINE ? item.engine_id : e->display_last_variant, item.engine_id, flags, indent);
|
||||
|
||||
/* Add variants if not folded */
|
||||
if ((item.flags & (EngineDisplayFlags::HasVariants | EngineDisplayFlags::IsFolded)) == EngineDisplayFlags::HasVariants) {
|
||||
/* Add this engine again as a child */
|
||||
if ((item.flags & EngineDisplayFlags::Shaded) == EngineDisplayFlags::None) {
|
||||
this->eng_list.emplace_back(item.engine_id, item.engine_id, EngineDisplayFlags::None, indent + 1);
|
||||
}
|
||||
AddChildren(source, item.engine_id, indent + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BuildVehicleWindow(WindowDesc *desc, TileIndex tile, VehicleType type) : Window(desc)
|
||||
{
|
||||
this->vehicle_type = type;
|
||||
@@ -1123,12 +1188,12 @@ struct BuildVehicleWindow : Window {
|
||||
|
||||
this->eng_list.ForceRebuild();
|
||||
this->GenerateBuildList(); // generate the list, since we need it in the next line
|
||||
/* Select the first engine in the list as default when opening the window */
|
||||
if (this->eng_list.size() > 0) {
|
||||
this->SelectEngine(this->eng_list[0]);
|
||||
} else {
|
||||
this->SelectEngine(INVALID_ENGINE);
|
||||
}
|
||||
|
||||
/* Select the first unshaded engine in the list as default when opening the window */
|
||||
EngineID engine = INVALID_ENGINE;
|
||||
auto it = std::find_if(this->eng_list.begin(), this->eng_list.end(), [&](GUIEngineListItem &item){ return (item.flags & EngineDisplayFlags::Shaded) == EngineDisplayFlags::None; });
|
||||
if (it != this->eng_list.end()) engine = it->engine_id;
|
||||
this->SelectEngine(engine);
|
||||
}
|
||||
|
||||
/** Set the filter type according to the depot type */
|
||||
@@ -1221,28 +1286,23 @@ struct BuildVehicleWindow : Window {
|
||||
if (this->sel_engine == INVALID_ENGINE) return;
|
||||
|
||||
const Engine *e = Engine::Get(this->sel_engine);
|
||||
if (!e->CanCarryCargo()) {
|
||||
this->te.cost = 0;
|
||||
this->te.cargo = CT_INVALID;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this->listview_mode) {
|
||||
/* Query for cost and refitted capacity */
|
||||
auto [ret, veh_id, refit_capacity, refit_mail] = Command<CMD_BUILD_VEHICLE>::Do(DC_QUERY_COST, this->window_number, this->sel_engine, true, cargo, INVALID_CLIENT_ID);
|
||||
auto [ret, veh_id, refit_capacity, refit_mail, cargo_capacities] = Command<CMD_BUILD_VEHICLE>::Do(DC_QUERY_COST, this->window_number, this->sel_engine, true, cargo, INVALID_CLIENT_ID);
|
||||
if (ret.Succeeded()) {
|
||||
this->te.cost = ret.GetCost() - e->GetCost();
|
||||
this->te.capacity = refit_capacity;
|
||||
this->te.mail_capacity = refit_mail;
|
||||
this->te.cargo = (cargo == CT_INVALID) ? e->GetDefaultCargoType() : cargo;
|
||||
this->te.all_capacities = cargo_capacities;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Purchase test was not possible or failed, fill in the defaults instead. */
|
||||
this->te.cost = 0;
|
||||
this->te.capacity = e->GetDisplayDefaultCapacity(&this->te.mail_capacity);
|
||||
this->te.cargo = e->GetDefaultCargoType();
|
||||
this->te.FillDefaultCapacities(e);
|
||||
}
|
||||
|
||||
void OnInit() override
|
||||
@@ -1257,7 +1317,7 @@ struct BuildVehicleWindow : Window {
|
||||
if (0 == this->eng_list.size()) { // no engine passed through the filter, invalidate the previously selected engine
|
||||
this->SelectEngine(INVALID_ENGINE);
|
||||
} else if (std::find(this->eng_list.begin(), this->eng_list.end(), this->sel_engine) == this->eng_list.end()) { // previously selected engine didn't pass the filter, select the first engine of the list
|
||||
this->SelectEngine(this->eng_list[0]);
|
||||
this->SelectEngine(this->eng_list[0].engine_id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1265,17 +1325,18 @@ struct BuildVehicleWindow : Window {
|
||||
bool FilterSingleEngine(EngineID eid)
|
||||
{
|
||||
CargoID filter_type = this->cargo_filter[this->cargo_filter_criteria];
|
||||
return CargoAndEngineFilter(&eid, filter_type);
|
||||
GUIEngineListItem item = {eid, eid, EngineDisplayFlags::None, 0};
|
||||
return CargoAndEngineFilter(&item, filter_type);
|
||||
}
|
||||
|
||||
/* Figure out what train EngineIDs to put in the list */
|
||||
void GenerateBuildTrainList()
|
||||
void GenerateBuildTrainList(GUIEngineList &list)
|
||||
{
|
||||
std::vector<EngineID> variants;
|
||||
EngineID sel_id = INVALID_ENGINE;
|
||||
int num_engines = 0;
|
||||
int num_wagons = 0;
|
||||
size_t num_engines = 0;
|
||||
|
||||
this->eng_list.clear();
|
||||
list.clear();
|
||||
|
||||
/* Make list of all available train engines and wagons.
|
||||
* Also check to see if the previously selected engine is still available,
|
||||
@@ -1292,17 +1353,22 @@ struct BuildVehicleWindow : Window {
|
||||
/* Filter now! So num_engines and num_wagons is valid */
|
||||
if (!FilterSingleEngine(eid)) continue;
|
||||
|
||||
this->eng_list.push_back(eid);
|
||||
|
||||
if (rvi->railveh_type != RAILVEH_WAGON) {
|
||||
num_engines++;
|
||||
} else {
|
||||
num_wagons++;
|
||||
}
|
||||
list.emplace_back(eid, e->info.variant_id, e->display_flags, 0);
|
||||
|
||||
if (rvi->railveh_type != RAILVEH_WAGON) num_engines++;
|
||||
if (e->info.variant_id != eid && e->info.variant_id != INVALID_ENGINE) variants.push_back(e->info.variant_id);
|
||||
if (eid == this->sel_engine) sel_id = eid;
|
||||
}
|
||||
|
||||
/* ensure primary engine of variant group is in list */
|
||||
for (const auto &variant : variants) {
|
||||
if (std::find(list.begin(), list.end(), variant) == list.end()) {
|
||||
const Engine *e = Engine::Get(variant);
|
||||
list.emplace_back(variant, e->info.variant_id, e->display_flags | EngineDisplayFlags::Shaded, 0);
|
||||
if (e->u.rail.railveh_type != RAILVEH_WAGON) num_engines++;
|
||||
}
|
||||
}
|
||||
|
||||
this->SelectEngine(sel_id);
|
||||
|
||||
/* invalidate cached values for name sorter - engine names could change */
|
||||
@@ -1310,14 +1376,14 @@ struct BuildVehicleWindow : Window {
|
||||
|
||||
/* make engines first, and then wagons, sorted by selected sort_criteria */
|
||||
_engine_sort_direction = false;
|
||||
EngList_Sort(&this->eng_list, TrainEnginesThenWagonsSorter);
|
||||
EngList_Sort(&list, TrainEnginesThenWagonsSorter);
|
||||
|
||||
/* and then sort engines */
|
||||
_engine_sort_direction = this->descending_sort_order;
|
||||
EngList_SortPartial(&this->eng_list, _engine_sort_functions[0][this->sort_criteria], 0, num_engines);
|
||||
EngList_SortPartial(&list, _engine_sort_functions[0][this->sort_criteria], 0, num_engines);
|
||||
|
||||
/* and finally sort wagons */
|
||||
EngList_SortPartial(&this->eng_list, _engine_sort_functions[0][this->sort_criteria], num_engines, num_wagons);
|
||||
EngList_SortPartial(&list, _engine_sort_functions[0][this->sort_criteria], num_engines, list.size() - num_engines);
|
||||
}
|
||||
|
||||
/* Figure out what road vehicle EngineIDs to put in the list */
|
||||
@@ -1333,7 +1399,7 @@ struct BuildVehicleWindow : Window {
|
||||
if (!IsEngineBuildable(eid, VEH_ROAD, _local_company)) continue;
|
||||
if (this->filter.roadtype != INVALID_ROADTYPE && !HasPowerOnRoad(e->u.road.roadtype, this->filter.roadtype)) continue;
|
||||
|
||||
this->eng_list.push_back(eid);
|
||||
this->eng_list.emplace_back(eid, e->info.variant_id, e->display_flags, 0);
|
||||
|
||||
if (eid == this->sel_engine) sel_id = eid;
|
||||
}
|
||||
@@ -1350,7 +1416,7 @@ struct BuildVehicleWindow : Window {
|
||||
if (!this->show_hidden_engines && e->IsHidden(_local_company)) continue;
|
||||
EngineID eid = e->index;
|
||||
if (!IsEngineBuildable(eid, VEH_SHIP, _local_company)) continue;
|
||||
this->eng_list.push_back(eid);
|
||||
this->eng_list.emplace_back(eid, e->info.variant_id, e->display_flags, 0);
|
||||
|
||||
if (eid == this->sel_engine) sel_id = eid;
|
||||
}
|
||||
@@ -1377,7 +1443,7 @@ struct BuildVehicleWindow : Window {
|
||||
/* First VEH_END window_numbers are fake to allow a window open for all different types at once */
|
||||
if (!this->listview_mode && !CanVehicleUseStation(eid, st)) continue;
|
||||
|
||||
this->eng_list.push_back(eid);
|
||||
this->eng_list.emplace_back(eid, e->info.variant_id, e->display_flags, 0);
|
||||
if (eid == this->sel_engine) sel_id = eid;
|
||||
}
|
||||
|
||||
@@ -1392,13 +1458,18 @@ struct BuildVehicleWindow : Window {
|
||||
/* Update filter type in case the road/railtype of the depot got converted */
|
||||
this->UpdateFilterByTile();
|
||||
|
||||
this->eng_list.clear();
|
||||
|
||||
GUIEngineList list;
|
||||
|
||||
switch (this->vehicle_type) {
|
||||
default: NOT_REACHED();
|
||||
case VEH_TRAIN:
|
||||
this->GenerateBuildTrainList();
|
||||
this->GenerateBuildTrainList(list);
|
||||
AddChildren(list, INVALID_ENGINE, 0);
|
||||
this->eng_list.shrink_to_fit();
|
||||
this->eng_list.RebuildDone();
|
||||
return; // trains should not reach the last sorting
|
||||
return;
|
||||
case VEH_ROAD:
|
||||
this->GenerateBuildRoadVehList();
|
||||
break;
|
||||
@@ -1412,9 +1483,23 @@ struct BuildVehicleWindow : Window {
|
||||
|
||||
this->FilterEngineList();
|
||||
|
||||
/* ensure primary engine of variant group is in list after filtering */
|
||||
std::vector<EngineID> variants;
|
||||
for (const auto &item : this->eng_list) {
|
||||
if (item.engine_id != item.variant_id && item.variant_id != INVALID_ENGINE) variants.push_back(item.variant_id);
|
||||
}
|
||||
for (const auto &variant : variants) {
|
||||
if (std::find(this->eng_list.begin(), this->eng_list.end(), variant) == this->eng_list.end()) {
|
||||
const Engine *e = Engine::Get(variant);
|
||||
this->eng_list.emplace_back(variant, e->info.variant_id, e->display_flags | EngineDisplayFlags::Shaded, 0);
|
||||
}
|
||||
}
|
||||
|
||||
_engine_sort_direction = this->descending_sort_order;
|
||||
EngList_Sort(&this->eng_list, _engine_sort_functions[this->vehicle_type][this->sort_criteria]);
|
||||
|
||||
this->eng_list.swap(list);
|
||||
AddChildren(list, INVALID_ENGINE, 0);
|
||||
this->eng_list.shrink_to_fit();
|
||||
this->eng_list.RebuildDone();
|
||||
}
|
||||
@@ -1440,7 +1525,23 @@ struct BuildVehicleWindow : Window {
|
||||
case WID_BV_LIST: {
|
||||
uint i = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_BV_LIST);
|
||||
size_t num_items = this->eng_list.size();
|
||||
this->SelectEngine((i < num_items) ? this->eng_list[i] : INVALID_ENGINE);
|
||||
EngineID e = INVALID_ENGINE;
|
||||
if (i < num_items) {
|
||||
const auto &item = this->eng_list[i];
|
||||
const Rect r = this->GetWidget<NWidgetBase>(widget)->GetCurrentRect().Shrink(WidgetDimensions::scaled.matrix).WithWidth(WidgetDimensions::scaled.hsep_indent * (item.indent + 1), _current_text_dir == TD_RTL);
|
||||
if ((item.flags & EngineDisplayFlags::HasVariants) != EngineDisplayFlags::None && IsInsideMM(r.left, r.right, pt.x)) {
|
||||
/* toggle folded flag on engine */
|
||||
assert(item.variant_id != INVALID_ENGINE);
|
||||
Engine *engine = Engine::Get(item.variant_id);
|
||||
engine->display_flags ^= EngineDisplayFlags::IsFolded;
|
||||
|
||||
InvalidateWindowData(WC_REPLACE_VEHICLE, this->vehicle_type, 0); // Update the autoreplace window
|
||||
InvalidateWindowClassesData(WC_BUILD_VEHICLE); // The build windows needs updating as well
|
||||
return;
|
||||
}
|
||||
if ((item.flags & EngineDisplayFlags::Shaded) == EngineDisplayFlags::None) e = item.engine_id;
|
||||
}
|
||||
this->SelectEngine(e);
|
||||
this->SetDirty();
|
||||
if (_ctrl_pressed) {
|
||||
this->OnClick(pt, WID_BV_SHOW_HIDE, 1);
|
||||
@@ -1476,6 +1577,20 @@ struct BuildVehicleWindow : Window {
|
||||
} else {
|
||||
Command<CMD_BUILD_VEHICLE>::Post(GetCmdBuildVehMsg(this->vehicle_type), CcBuildPrimaryVehicle, this->window_number, sel_eng, true, cargo, INVALID_CLIENT_ID);
|
||||
}
|
||||
|
||||
/* Update last used variant and refresh if necessary. */
|
||||
bool refresh = false;
|
||||
int recursion = 10; /* In case of infinite loop */
|
||||
for (Engine *e = Engine::Get(sel_eng); recursion > 0; e = Engine::Get(e->info.variant_id), --recursion) {
|
||||
refresh |= (e->display_last_variant != sel_eng);
|
||||
e->display_last_variant = sel_eng;
|
||||
if (e->info.variant_id == INVALID_ENGINE) break;
|
||||
}
|
||||
if (refresh) {
|
||||
InvalidateWindowData(WC_REPLACE_VEHICLE, this->vehicle_type, 0); // Update the autoreplace window
|
||||
InvalidateWindowClassesData(WC_BUILD_VEHICLE); // The build windows needs updating as well
|
||||
return;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -1484,7 +1599,7 @@ struct BuildVehicleWindow : Window {
|
||||
EngineID sel_eng = this->sel_engine;
|
||||
if (sel_eng != INVALID_ENGINE) {
|
||||
this->rename_engine = sel_eng;
|
||||
SetDParam(0, sel_eng);
|
||||
SetDParam(0, PackEngineNameDParam(sel_eng, EngineNameContext::Generic));
|
||||
ShowQueryString(STR_ENGINE_NAME, STR_QUERY_RENAME_TRAIN_TYPE_CAPTION + this->vehicle_type, MAX_LENGTH_ENGINE_NAME_CHARS, this, CS_ALPHANUMERAL, QSF_ENABLE_DEFAULT | QSF_LEN_IN_CHARS);
|
||||
}
|
||||
break;
|
||||
@@ -1589,7 +1704,7 @@ struct BuildVehicleWindow : Window {
|
||||
DrawEngineList(
|
||||
this->vehicle_type,
|
||||
r,
|
||||
&this->eng_list,
|
||||
this->eng_list,
|
||||
this->vscroll->GetPosition(),
|
||||
static_cast<uint16>(std::min<size_t>(this->vscroll->GetPosition() + this->vscroll->GetCapacity(), this->eng_list.size())),
|
||||
this->sel_engine,
|
||||
|
@@ -37,7 +37,7 @@ static const CommandCost CMD_ERROR = CommandCost(INVALID_STRING_ID);
|
||||
*/
|
||||
#define return_cmd_error(errcode) return CommandCost(errcode);
|
||||
|
||||
void NetworkSendCommand(Commands cmd, StringID err_message, CommandCallback *callback, CompanyID company, TileIndex location, const CommandDataBuffer &cmd_data);
|
||||
void NetworkSendCommand(Commands cmd, StringID err_message, CommandCallback *callback, CompanyID company, const CommandDataBuffer &cmd_data);
|
||||
|
||||
bool IsValidCommand(Commands cmd);
|
||||
CommandFlags GetCommandFlags(Commands cmd);
|
||||
@@ -215,20 +215,13 @@ public:
|
||||
* @param err_message Message prefix to show on error
|
||||
* @param callback A callback function to call after the command is finished
|
||||
* @param my_cmd indicator if the command is from a company or server (to display error messages for a user)
|
||||
* @param location Tile location for user feedback.
|
||||
* @param args Parameters for the command
|
||||
* @return \c true if the command succeeded, else \c false.
|
||||
*/
|
||||
template <typename Tcallback>
|
||||
static bool PostFromNet(StringID err_message, Tcallback *callback, bool my_cmd, TileIndex location, std::tuple<Targs...> args)
|
||||
static bool PostFromNet(StringID err_message, Tcallback *callback, bool my_cmd, std::tuple<Targs...> args)
|
||||
{
|
||||
if constexpr (std::is_same_v<TileIndex, std::tuple_element_t<0, decltype(args)>>) {
|
||||
/* Do not even think about executing out-of-bounds tile-commands. */
|
||||
TileIndex tile = std::get<0>(args);
|
||||
if (tile != 0 && (tile >= MapSize() || (!IsValidTile(tile) && (GetCommandFlags<Tcmd>() & CMD_ALL_TILES) == 0))) return false;
|
||||
}
|
||||
|
||||
return InternalPost(err_message, callback, my_cmd, true, location, std::move(args));
|
||||
return InternalPost(err_message, callback, my_cmd, true, std::move(args));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -242,12 +235,7 @@ public:
|
||||
{
|
||||
auto args_tuple = std::forward_as_tuple(args...);
|
||||
|
||||
TileIndex tile{};
|
||||
if constexpr (std::is_same_v<TileIndex, std::tuple_element_t<0, decltype(args_tuple)>>) {
|
||||
tile = std::get<0>(args_tuple);
|
||||
}
|
||||
|
||||
::NetworkSendCommand(Tcmd, err_message, nullptr, _current_company, tile, EndianBufferWriter<CommandDataBuffer>::FromValue(args_tuple));
|
||||
::NetworkSendCommand(Tcmd, err_message, nullptr, company, EndianBufferWriter<CommandDataBuffer>::FromValue(args_tuple));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -324,9 +312,9 @@ protected:
|
||||
} else if constexpr (std::is_same_v<Tcallback, CommandCallbackData>) {
|
||||
/* Generic callback that takes packed arguments as a buffer. */
|
||||
if constexpr (std::is_same_v<Tret, CommandCost>) {
|
||||
callback(Tcmd, ExtractCommandCost(res), tile, EndianBufferWriter<CommandDataBuffer>::FromValue(args), {});
|
||||
callback(Tcmd, ExtractCommandCost(res), EndianBufferWriter<CommandDataBuffer>::FromValue(args), {});
|
||||
} else {
|
||||
callback(Tcmd, ExtractCommandCost(res), tile, EndianBufferWriter<CommandDataBuffer>::FromValue(args), EndianBufferWriter<CommandDataBuffer>::FromValue(RemoveFirstTupleElement(res)));
|
||||
callback(Tcmd, ExtractCommandCost(res), EndianBufferWriter<CommandDataBuffer>::FromValue(args), EndianBufferWriter<CommandDataBuffer>::FromValue(RemoveFirstTupleElement(res)));
|
||||
}
|
||||
} else if constexpr (!std::is_same_v<Tret, CommandCost> && std::is_same_v<Tcallback *, typename CommandTraits<Tcmd>::RetCallbackProc>) {
|
||||
std::apply(callback, std::tuple_cat(std::make_tuple(Tcmd), res));
|
||||
@@ -405,7 +393,7 @@ protected:
|
||||
/* If we are in network, and the command is not from the network
|
||||
* send it to the command-queue and abort execution. */
|
||||
if (send_net) {
|
||||
::NetworkSendCommand(Tcmd, err_message, callback, _current_company, tile, EndianBufferWriter<CommandDataBuffer>::FromValue(args));
|
||||
::NetworkSendCommand(Tcmd, err_message, callback, _current_company, EndianBufferWriter<CommandDataBuffer>::FromValue(args));
|
||||
cur_company.Restore();
|
||||
|
||||
/* Don't return anything special here; no error, no costs.
|
||||
@@ -446,8 +434,13 @@ protected:
|
||||
template <Commands Tcmd, typename Tret, typename... Targs>
|
||||
struct CommandHelper<Tcmd, Tret(*)(DoCommandFlag, Targs...), false> : CommandHelper<Tcmd, Tret(*)(DoCommandFlag, Targs...), true>
|
||||
{
|
||||
/* Import Post overloads from our base class. */
|
||||
using CommandHelper<Tcmd, Tret(*)(DoCommandFlag, Targs...), true>::Post;
|
||||
/* Do not allow Post without explicit location. */
|
||||
static inline bool Post(StringID err_message, Targs... args) = delete;
|
||||
template <typename Tcallback>
|
||||
static inline bool Post(Tcallback *callback, Targs... args) = delete;
|
||||
static inline bool Post(Targs... args) = delete;
|
||||
template <typename Tcallback>
|
||||
static bool Post(StringID err_message, Tcallback *callback, Targs... args) = delete;
|
||||
|
||||
/**
|
||||
* Shortcut for Post when not using an error message.
|
||||
@@ -476,7 +469,6 @@ struct CommandHelper<Tcmd, Tret(*)(DoCommandFlag, Targs...), false> : CommandHel
|
||||
* commands that don't take a TileIndex by themselves.
|
||||
* @param err_message Message prefix to show on error
|
||||
* @param callback A callback function to call after the command is finished
|
||||
* @param location Tile location for user feedback.
|
||||
* @param args Parameters for the command
|
||||
* @return \c true if the command succeeded, else \c false.
|
||||
*/
|
||||
@@ -492,6 +484,6 @@ struct CommandHelper<Tcmd, Tret(*)(DoCommandFlag, Targs...), false> : CommandHel
|
||||
#endif
|
||||
|
||||
template <Commands Tcmd>
|
||||
using Command = CommandHelper<Tcmd, typename CommandTraits<Tcmd>::ProcType, std::is_same_v<TileIndex, std::tuple_element_t<0, typename CommandTraits<Tcmd>::Args>>>;
|
||||
using Command = CommandHelper<Tcmd, typename CommandTraits<Tcmd>::ProcType, (GetCommandFlags<Tcmd>() & CMD_LOCATION) == 0>;
|
||||
|
||||
#endif /* COMMAND_FUNC_H */
|
||||
|
@@ -331,6 +331,7 @@ enum Commands : uint16 {
|
||||
|
||||
CMD_MOVE_ORDER, ///< move an order
|
||||
CMD_CHANGE_TIMETABLE, ///< change the timetable for a vehicle
|
||||
CMD_BULK_CHANGE_TIMETABLE, ///< change the timetable for all orders of a vehicle
|
||||
CMD_SET_VEHICLE_ON_TIME, ///< set the vehicle on time feature (timetable)
|
||||
CMD_AUTOFILL_TIMETABLE, ///< autofill the timetable
|
||||
CMD_SET_TIMETABLE_START, ///< set the date that a timetable should start
|
||||
@@ -385,6 +386,7 @@ enum CommandFlags {
|
||||
CMD_DEITY = 0x100, ///< the command may be executed by COMPANY_DEITY
|
||||
CMD_STR_CTRL = 0x200, ///< the command's string may contain control strings
|
||||
CMD_NO_EST = 0x400, ///< the command is never estimated.
|
||||
CMD_LOCATION = 0x800, ///< the command has implicit location argument.
|
||||
};
|
||||
DECLARE_ENUM_AS_BIT_SET(CommandFlags)
|
||||
|
||||
@@ -476,6 +478,6 @@ typedef void CommandCallback(Commands cmd, const CommandCost &result, TileIndex
|
||||
* @param result_data Additional returned data from the command
|
||||
* @see CommandProc
|
||||
*/
|
||||
typedef void CommandCallbackData(Commands cmd, const CommandCost &result, TileIndex tile, const CommandDataBuffer &data, CommandDataBuffer result_data);
|
||||
typedef void CommandCallbackData(Commands cmd, const CommandCost &result, const CommandDataBuffer &data, CommandDataBuffer result_data);
|
||||
|
||||
#endif /* COMMAND_TYPE_H */
|
||||
|
@@ -560,7 +560,8 @@ Company *DoStartupNewCompany(bool is_ai, CompanyID company = INVALID_COMPANY)
|
||||
ResetCompanyLivery(c);
|
||||
_company_colours[c->index] = (Colours)c->colour;
|
||||
|
||||
c->money = c->current_loan = (std::min<int64>(INITIAL_LOAN, _economy.max_loan) * _economy.inflation_prices >> 16) / 50000 * 50000;
|
||||
/* Scale the initial loan based on the inflation rounded down to the loan interval. The maximum loan has already been inflation adjusted. */
|
||||
c->money = c->current_loan = std::min<int64>((INITIAL_LOAN * _economy.inflation_prices >> 16) / LOAN_INTERVAL * LOAN_INTERVAL, _economy.max_loan);
|
||||
|
||||
std::fill(c->share_owners.begin(), c->share_owners.end(), INVALID_OWNER);
|
||||
|
||||
|
@@ -796,7 +796,7 @@ public:
|
||||
/* Position scrollbar to selected group */
|
||||
for (uint i = 0; i < this->rows; i++) {
|
||||
if (this->groups[i]->index == sel) {
|
||||
this->vscroll->SetPosition(Clamp(i - this->vscroll->GetCapacity() / 2, 0, std::max(this->vscroll->GetCount() - this->vscroll->GetCapacity(), 0)));
|
||||
this->vscroll->SetPosition(i - this->vscroll->GetCapacity() / 2);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1909,18 +1909,18 @@ struct CompanyInfrastructureWindow : Window
|
||||
case WID_CI_RAIL_DESC: {
|
||||
uint lines = 1; // Starts at 1 because a line is also required for the section title
|
||||
|
||||
size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_INFRASTRUCTURE_VIEW_RAIL_SECT).width);
|
||||
size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_INFRASTRUCTURE_VIEW_RAIL_SECT).width + padding.width);
|
||||
|
||||
for (const auto &rt : _sorted_railtypes) {
|
||||
if (HasBit(this->railtypes, rt)) {
|
||||
lines++;
|
||||
SetDParam(0, GetRailTypeInfo(rt)->strings.name);
|
||||
size->width = std::max(size->width, GetStringBoundingBox(STR_WHITE_STRING).width + WidgetDimensions::scaled.hsep_indent);
|
||||
size->width = std::max(size->width, GetStringBoundingBox(STR_WHITE_STRING).width + padding.width + WidgetDimensions::scaled.hsep_indent);
|
||||
}
|
||||
}
|
||||
if (this->railtypes != RAILTYPES_NONE) {
|
||||
lines++;
|
||||
size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_INFRASTRUCTURE_VIEW_SIGNALS).width + WidgetDimensions::scaled.hsep_indent);
|
||||
size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_INFRASTRUCTURE_VIEW_SIGNALS).width + padding.width + WidgetDimensions::scaled.hsep_indent);
|
||||
}
|
||||
|
||||
size->height = std::max(size->height, lines * FONT_HEIGHT_NORMAL);
|
||||
@@ -1931,13 +1931,13 @@ struct CompanyInfrastructureWindow : Window
|
||||
case WID_CI_TRAM_DESC: {
|
||||
uint lines = 1; // Starts at 1 because a line is also required for the section title
|
||||
|
||||
size->width = std::max(size->width, GetStringBoundingBox(widget == WID_CI_ROAD_DESC ? STR_COMPANY_INFRASTRUCTURE_VIEW_ROAD_SECT : STR_COMPANY_INFRASTRUCTURE_VIEW_TRAM_SECT).width);
|
||||
size->width = std::max(size->width, GetStringBoundingBox(widget == WID_CI_ROAD_DESC ? STR_COMPANY_INFRASTRUCTURE_VIEW_ROAD_SECT : STR_COMPANY_INFRASTRUCTURE_VIEW_TRAM_SECT).width + padding.width);
|
||||
|
||||
for (const auto &rt : _sorted_roadtypes) {
|
||||
if (HasBit(this->roadtypes, rt) && RoadTypeIsRoad(rt) == (widget == WID_CI_ROAD_DESC)) {
|
||||
lines++;
|
||||
SetDParam(0, GetRoadTypeInfo(rt)->strings.name);
|
||||
size->width = std::max(size->width, GetStringBoundingBox(STR_WHITE_STRING).width + WidgetDimensions::scaled.hsep_indent);
|
||||
size->width = std::max(size->width, GetStringBoundingBox(STR_WHITE_STRING).width + padding.width + WidgetDimensions::scaled.hsep_indent);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1946,14 +1946,14 @@ struct CompanyInfrastructureWindow : Window
|
||||
}
|
||||
|
||||
case WID_CI_WATER_DESC:
|
||||
size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_INFRASTRUCTURE_VIEW_WATER_SECT).width);
|
||||
size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_INFRASTRUCTURE_VIEW_CANALS).width + WidgetDimensions::scaled.hsep_indent);
|
||||
size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_INFRASTRUCTURE_VIEW_WATER_SECT).width + padding.width);
|
||||
size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_INFRASTRUCTURE_VIEW_CANALS).width + padding.width + WidgetDimensions::scaled.hsep_indent);
|
||||
break;
|
||||
|
||||
case WID_CI_STATION_DESC:
|
||||
size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_INFRASTRUCTURE_VIEW_STATION_SECT).width);
|
||||
size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_INFRASTRUCTURE_VIEW_STATIONS).width + WidgetDimensions::scaled.hsep_indent);
|
||||
size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_INFRASTRUCTURE_VIEW_AIRPORTS).width + WidgetDimensions::scaled.hsep_indent);
|
||||
size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_INFRASTRUCTURE_VIEW_STATION_SECT).width + padding.width);
|
||||
size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_INFRASTRUCTURE_VIEW_STATIONS).width + padding.width + WidgetDimensions::scaled.hsep_indent);
|
||||
size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_INFRASTRUCTURE_VIEW_AIRPORTS).width + padding.width + WidgetDimensions::scaled.hsep_indent);
|
||||
break;
|
||||
|
||||
case WID_CI_RAIL_COUNT:
|
||||
@@ -2399,18 +2399,19 @@ struct CompanyWindow : Window
|
||||
case WID_C_DESC_VEHICLE_COUNTS:
|
||||
SetDParamMaxValue(0, 5000); // Maximum number of vehicles
|
||||
for (uint i = 0; i < lengthof(_company_view_vehicle_count_strings); i++) {
|
||||
size->width = std::max(size->width, GetStringBoundingBox(_company_view_vehicle_count_strings[i]).width);
|
||||
size->width = std::max(size->width, GetStringBoundingBox(_company_view_vehicle_count_strings[i]).width + padding.width);
|
||||
}
|
||||
break;
|
||||
|
||||
case WID_C_DESC_INFRASTRUCTURE_COUNTS:
|
||||
SetDParamMaxValue(0, UINT_MAX);
|
||||
size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_VIEW_INFRASTRUCTURE_RAIL).width);
|
||||
size->width = GetStringBoundingBox(STR_COMPANY_VIEW_INFRASTRUCTURE_RAIL).width;
|
||||
size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_VIEW_INFRASTRUCTURE_ROAD).width);
|
||||
size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_VIEW_INFRASTRUCTURE_WATER).width);
|
||||
size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_VIEW_INFRASTRUCTURE_STATION).width);
|
||||
size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_VIEW_INFRASTRUCTURE_AIRPORT).width);
|
||||
size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_VIEW_INFRASTRUCTURE_NONE).width);
|
||||
size->width += padding.width;
|
||||
break;
|
||||
|
||||
case WID_C_DESC_OWNERS: {
|
||||
@@ -2430,13 +2431,14 @@ struct CompanyWindow : Window
|
||||
case WID_C_GIVE_MONEY:
|
||||
case WID_C_COMPANY_PASSWORD:
|
||||
case WID_C_COMPANY_JOIN:
|
||||
size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_VIEW_VIEW_HQ_BUTTON).width);
|
||||
size->width = GetStringBoundingBox(STR_COMPANY_VIEW_VIEW_HQ_BUTTON).width;
|
||||
size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_VIEW_BUILD_HQ_BUTTON).width);
|
||||
size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_VIEW_RELOCATE_HQ).width);
|
||||
size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_VIEW_INFRASTRUCTURE_BUTTON).width);
|
||||
size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_VIEW_GIVE_MONEY_BUTTON).width);
|
||||
size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_VIEW_PASSWORD).width);
|
||||
size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_VIEW_JOIN).width);
|
||||
size->width += padding.width;
|
||||
break;
|
||||
|
||||
case WID_C_HAS_PASSWORD:
|
||||
|
@@ -23,6 +23,7 @@
|
||||
#include "settings_func.h"
|
||||
#include "fios.h"
|
||||
#include "fileio_func.h"
|
||||
#include "fontcache.h"
|
||||
#include "screenshot.h"
|
||||
#include "genworld.h"
|
||||
#include "strings_func.h"
|
||||
@@ -453,8 +454,8 @@ DEF_CONSOLE_CMD(ConRemove)
|
||||
_console_file_list.ValidateFileList();
|
||||
const FiosItem *item = _console_file_list.FindItem(file);
|
||||
if (item != nullptr) {
|
||||
if (!FiosDelete(item->name)) {
|
||||
IConsolePrint(CC_ERROR, "Failed to delete '{}'.", file);
|
||||
if (unlink(item->name) != 0) {
|
||||
IConsolePrint(CC_ERROR, "Failed to delete '{}'.", item->name);
|
||||
}
|
||||
} else {
|
||||
IConsolePrint(CC_ERROR, "'{}' could not be found.", file);
|
||||
@@ -1982,6 +1983,82 @@ DEF_CONSOLE_CMD(ConContent)
|
||||
}
|
||||
#endif /* defined(WITH_ZLIB) */
|
||||
|
||||
DEF_CONSOLE_CMD(ConFont)
|
||||
{
|
||||
if (argc == 0) {
|
||||
IConsolePrint(CC_HELP, "Manage the fonts configuration.");
|
||||
IConsolePrint(CC_HELP, "Usage 'font'.");
|
||||
IConsolePrint(CC_HELP, " Print out the fonts configuration.");
|
||||
IConsolePrint(CC_HELP, "Usage 'font [medium|small|large|mono] [<name>] [<size>] [aa|noaa]'.");
|
||||
IConsolePrint(CC_HELP, " Change the configuration for a font.");
|
||||
IConsolePrint(CC_HELP, " Omitting an argument will keep the current value.");
|
||||
IConsolePrint(CC_HELP, " Set <name> to \"\" for the sprite font (size and aa have no effect on sprite font).");
|
||||
return true;
|
||||
}
|
||||
|
||||
FontSize argfs;
|
||||
for (argfs = FS_BEGIN; argfs < FS_END; argfs++) {
|
||||
if (argc > 1 && strcasecmp(argv[1], FontSizeToName(argfs)) == 0) break;
|
||||
}
|
||||
|
||||
/* First argument must be a FontSize. */
|
||||
if (argc > 1 && argfs == FS_END) return false;
|
||||
|
||||
if (argc > 2) {
|
||||
FontCacheSubSetting *setting = GetFontCacheSubSetting(argfs);
|
||||
std::string font = setting->font;
|
||||
uint size = setting->size;
|
||||
bool aa = setting->aa;
|
||||
|
||||
byte arg_index = 2;
|
||||
|
||||
if (argc > arg_index) {
|
||||
/* We may encounter "aa" or "noaa" but it must be the last argument. */
|
||||
if (strcasecmp(argv[arg_index], "aa") == 0 || strcasecmp(argv[arg_index], "noaa") == 0) {
|
||||
aa = strncasecmp(argv[arg_index++], "no", 2) != 0;
|
||||
if (argc > arg_index) return false;
|
||||
} else {
|
||||
/* For <name> we want a string. */
|
||||
uint v;
|
||||
if (!GetArgumentInteger(&v, argv[arg_index])) {
|
||||
font = argv[arg_index++];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (argc > arg_index) {
|
||||
/* For <size> we want a number. */
|
||||
uint v;
|
||||
if (GetArgumentInteger(&v, argv[arg_index])) {
|
||||
size = v;
|
||||
arg_index++;
|
||||
}
|
||||
}
|
||||
|
||||
if (argc > arg_index) {
|
||||
/* Last argument must be "aa" or "noaa". */
|
||||
if (strcasecmp(argv[arg_index], "aa") != 0 && strcasecmp(argv[arg_index], "noaa") != 0) return false;
|
||||
aa = strncasecmp(argv[arg_index++], "no", 2) != 0;
|
||||
if (argc > arg_index) return false;
|
||||
}
|
||||
|
||||
SetFont(argfs, font, size, aa);
|
||||
}
|
||||
|
||||
for (FontSize fs = FS_BEGIN; fs < FS_END; fs++) {
|
||||
FontCache *fc = FontCache::Get(fs);
|
||||
FontCacheSubSetting *setting = GetFontCacheSubSetting(fs);
|
||||
/* Make sure all non sprite fonts are loaded. */
|
||||
if (!setting->font.empty() && !fc->HasParent()) {
|
||||
InitFontCache(fs == FS_MONO);
|
||||
fc = FontCache::Get(fs);
|
||||
}
|
||||
IConsolePrint(CC_DEFAULT, "{}: \"{}\" {} {} [\"{}\" {} {}]", FontSizeToName(fs), fc->GetFontName(), fc->GetFontSize(), GetFontAAState(fs) ? "aa" : "noaa", setting->font, setting->size, setting->aa ? "aa" : "noaa");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
DEF_CONSOLE_CMD(ConSetting)
|
||||
{
|
||||
if (argc == 0) {
|
||||
@@ -2480,6 +2557,7 @@ void IConsoleStdLibRegister()
|
||||
IConsole::CmdRegister("cd", ConChangeDirectory);
|
||||
IConsole::CmdRegister("pwd", ConPrintWorkingDirectory);
|
||||
IConsole::CmdRegister("clear", ConClearBuffer);
|
||||
IConsole::CmdRegister("font", ConFont);
|
||||
IConsole::CmdRegister("setting", ConSetting);
|
||||
IConsole::CmdRegister("setting_newgame", ConSettingNewgame);
|
||||
IConsole::CmdRegister("list_settings", ConListSettings);
|
||||
|
@@ -82,6 +82,32 @@ static inline T Clamp(const T a, const T min, const T max)
|
||||
return a;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clamp a value between an interval.
|
||||
*
|
||||
* This function returns a value which is between the given interval of
|
||||
* min and max. If the given value is in this interval the value itself
|
||||
* is returned otherwise the border of the interval is returned, according
|
||||
* which side of the interval was 'left'.
|
||||
*
|
||||
* @note If the min value is greater than the max, return value is the average of the min and max.
|
||||
* @param a The value to clamp/truncate.
|
||||
* @param min The minimum of the interval.
|
||||
* @param max the maximum of the interval.
|
||||
* @returns A value between min and max which is closest to a.
|
||||
*/
|
||||
template <typename T>
|
||||
static inline T SoftClamp(const T a, const T min, const T max)
|
||||
{
|
||||
if (min > max) {
|
||||
using U = std::make_unsigned_t<T>;
|
||||
return min - (U(min) - max) / 2;
|
||||
}
|
||||
if (a <= min) return min;
|
||||
if (a >= max) return max;
|
||||
return a;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clamp an integer between an interval.
|
||||
*
|
||||
|
@@ -176,6 +176,9 @@ public:
|
||||
inline constexpr bool operator <= (const int other) const { return !(*this > other); }
|
||||
|
||||
inline constexpr operator T () const { return this->m_value; }
|
||||
|
||||
static inline constexpr OverflowSafeInt<T> max() { return T_MAX; }
|
||||
static inline constexpr OverflowSafeInt<T> min() { return T_MIN; }
|
||||
};
|
||||
|
||||
|
||||
|
@@ -185,10 +185,10 @@ char *CrashLog::LogConfiguration(char *buffer, const char *last) const
|
||||
" Medium: %s\n"
|
||||
" Large: %s\n"
|
||||
" Mono: %s\n\n",
|
||||
FontCache::Get(FS_SMALL)->GetFontName(),
|
||||
FontCache::Get(FS_NORMAL)->GetFontName(),
|
||||
FontCache::Get(FS_LARGE)->GetFontName(),
|
||||
FontCache::Get(FS_MONO)->GetFontName()
|
||||
FontCache::Get(FS_SMALL)->GetFontName().c_str(),
|
||||
FontCache::Get(FS_NORMAL)->GetFontName().c_str(),
|
||||
FontCache::Get(FS_LARGE)->GetFontName().c_str(),
|
||||
FontCache::Get(FS_MONO)->GetFontName().c_str()
|
||||
);
|
||||
|
||||
buffer += seprintf(buffer, last, "AI Configuration (local: %i) (current: %i):\n", (int)_local_company, (int)_current_company);
|
||||
|
@@ -15,9 +15,16 @@
|
||||
#include "video/video_driver.hpp"
|
||||
#include "string_func.h"
|
||||
#include "table/strings.h"
|
||||
#include "fileio_func.h"
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
|
||||
#ifdef _WIN32
|
||||
# include <windows.h>
|
||||
#else
|
||||
# include <unistd.h>
|
||||
#endif /* _WIN32 */
|
||||
|
||||
#include "safeguards.h"
|
||||
|
||||
std::string _ini_videodriver; ///< The video driver a stored in the configuration file.
|
||||
@@ -32,6 +39,8 @@ std::string _ini_musicdriver; ///< The music driver a stored in the confi
|
||||
std::string _ini_blitter; ///< The blitter as stored in the configuration file.
|
||||
bool _blitter_autodetected; ///< Was the blitter autodetected or specified by the user?
|
||||
|
||||
static const std::string HWACCELERATION_TEST_FILE = "hwaccel.dat"; ///< Filename to test if we crashed last time we tried to use hardware acceleration.
|
||||
|
||||
/**
|
||||
* Get a string parameter the list of parameters.
|
||||
* @param parm The parameters.
|
||||
@@ -115,6 +124,27 @@ bool DriverFactoryBase::SelectDriverImpl(const std::string &name, Driver::Type t
|
||||
|
||||
if (type == Driver::DT_VIDEO && !_video_hw_accel && d->UsesHardwareAcceleration()) continue;
|
||||
|
||||
if (type == Driver::DT_VIDEO && _video_hw_accel && d->UsesHardwareAcceleration()) {
|
||||
/* Check if we have already tried this driver in last run.
|
||||
* If it is here, it most likely means we crashed. So skip
|
||||
* hardware acceleration. */
|
||||
auto filename = FioFindFullPath(BASE_DIR, HWACCELERATION_TEST_FILE.c_str());
|
||||
if (!filename.empty()) {
|
||||
unlink(filename.c_str());
|
||||
|
||||
Debug(driver, 1, "Probing {} driver '{}' skipped due to earlier crash", GetDriverTypeName(type), d->name);
|
||||
|
||||
_video_hw_accel = false;
|
||||
ErrorMessageData msg(STR_VIDEO_DRIVER_ERROR, STR_VIDEO_DRIVER_ERROR_HARDWARE_ACCELERATION_CRASH, true);
|
||||
ScheduleErrorMessage(msg);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Write empty file to note we are attempting hardware acceleration. */
|
||||
auto f = FioFOpenFile(HWACCELERATION_TEST_FILE.c_str(), "w", BASE_DIR);
|
||||
FioFCloseFile(f);
|
||||
}
|
||||
|
||||
Driver *oldd = *GetActiveDriver(type);
|
||||
Driver *newd = d->CreateInstance();
|
||||
*GetActiveDriver(type) = newd;
|
||||
@@ -132,7 +162,7 @@ bool DriverFactoryBase::SelectDriverImpl(const std::string &name, Driver::Type t
|
||||
|
||||
if (type == Driver::DT_VIDEO && _video_hw_accel && d->UsesHardwareAcceleration()) {
|
||||
_video_hw_accel = false;
|
||||
ErrorMessageData msg(STR_VIDEO_DRIVER_ERROR, STR_VIDEO_DRIVER_ERROR_NO_HARDWARE_ACCELERATION);
|
||||
ErrorMessageData msg(STR_VIDEO_DRIVER_ERROR, STR_VIDEO_DRIVER_ERROR_NO_HARDWARE_ACCELERATION, true);
|
||||
ScheduleErrorMessage(msg);
|
||||
}
|
||||
}
|
||||
@@ -179,6 +209,18 @@ bool DriverFactoryBase::SelectDriverImpl(const std::string &name, Driver::Type t
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark the current video driver as operational.
|
||||
*/
|
||||
void DriverFactoryBase::MarkVideoDriverOperational()
|
||||
{
|
||||
/* As part of the detection whether the GPU driver crashes the game,
|
||||
* and as we are operational now, remove the hardware acceleration
|
||||
* test-file. */
|
||||
auto filename = FioFindFullPath(BASE_DIR, HWACCELERATION_TEST_FILE.c_str());
|
||||
if (!filename.empty()) unlink(filename.c_str());
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a human readable list of available drivers, grouped by type.
|
||||
* @param p The buffer to write to.
|
||||
|
@@ -102,6 +102,8 @@ private:
|
||||
|
||||
static bool SelectDriverImpl(const std::string &name, Driver::Type type);
|
||||
|
||||
static void MarkVideoDriverOperational();
|
||||
|
||||
protected:
|
||||
DriverFactoryBase(Driver::Type type, int priority, const char *name, const char *description);
|
||||
|
||||
|
@@ -757,8 +757,8 @@ bool AddInflation(bool check_year)
|
||||
*/
|
||||
void RecomputePrices()
|
||||
{
|
||||
/* Setup maximum loan */
|
||||
_economy.max_loan = ((uint64)_settings_game.difficulty.max_loan * _economy.inflation_prices >> 16) / 50000 * 50000;
|
||||
/* Setup maximum loan as a rounded down multiple of LOAN_INTERVAL. */
|
||||
_economy.max_loan = ((uint64)_settings_game.difficulty.max_loan * _economy.inflation_prices >> 16) / LOAN_INTERVAL * LOAN_INTERVAL;
|
||||
|
||||
/* Setup price bases */
|
||||
for (Price i = PR_BEGIN; i < PR_END; i++) {
|
||||
@@ -1494,7 +1494,7 @@ static void HandleStationRefit(Vehicle *v, CargoArray &consist_capleft, Station
|
||||
if (st->goods[cid].cargo.HasCargoFor(next_station)) {
|
||||
/* Try to find out if auto-refitting would succeed. In case the refit is allowed,
|
||||
* the returned refit capacity will be greater than zero. */
|
||||
auto [cc, refit_capacity, mail_capacity] = Command<CMD_REFIT_VEHICLE>::Do(DC_QUERY_COST, v_start->index, cid, 0xFF, true, false, 1); // Auto-refit and only this vehicle including artic parts.
|
||||
auto [cc, refit_capacity, mail_capacity, cargo_capacities] = Command<CMD_REFIT_VEHICLE>::Do(DC_QUERY_COST, v_start->index, cid, 0xFF, true, false, 1); // Auto-refit and only this vehicle including artic parts.
|
||||
/* Try to balance different loadable cargoes between parts of the consist, so that
|
||||
* all of them can be loaded. Avoid a situation where all vehicles suddenly switch
|
||||
* to the first loadable cargo for which there is only one packet. If the capacities
|
||||
|
@@ -485,10 +485,11 @@ static void DrawRailCatenaryRailway(const TileInfo *ti)
|
||||
/*
|
||||
* The "wire"-sprite position is inside the tile, i.e. 0 <= sss->?_offset < TILE_SIZE.
|
||||
* Therefore it is safe to use GetSlopePixelZ() for the elevation.
|
||||
* Also note that the result of GetSlopePixelZ() is very special for bridge-ramps.
|
||||
* Also note that the result of GetSlopePixelZ() is very special for bridge-ramps, so we round the result up or
|
||||
* down to the nearest full height change.
|
||||
*/
|
||||
AddSortableSpriteToDraw(wire_base + sss->image_offset, PAL_NONE, ti->x + sss->x_offset, ti->y + sss->y_offset,
|
||||
sss->x_size, sss->y_size, sss->z_size, GetSlopePixelZ(ti->x + sss->x_offset, ti->y + sss->y_offset) + sss->z_offset,
|
||||
sss->x_size, sss->y_size, sss->z_size, (GetSlopePixelZ(ti->x + sss->x_offset, ti->y + sss->y_offset) + 4) / 8 * 8 + sss->z_offset,
|
||||
IsTransparencySet(TO_CATENARY));
|
||||
}
|
||||
}
|
||||
@@ -596,16 +597,14 @@ void SettingsDisableElrail(int32 new_value)
|
||||
{
|
||||
bool disable = (new_value != 0);
|
||||
|
||||
/* we will now walk through all electric train engines and change their railtypes if it is the wrong one*/
|
||||
const RailType old_railtype = disable ? RAILTYPE_ELECTRIC : RAILTYPE_RAIL;
|
||||
/* pick appropriate railtype for elrail engines depending on setting */
|
||||
const RailType new_railtype = disable ? RAILTYPE_RAIL : RAILTYPE_ELECTRIC;
|
||||
|
||||
/* walk through all train engines */
|
||||
for (Engine *e : Engine::IterateType(VEH_TRAIN)) {
|
||||
RailVehicleInfo *rv_info = &e->u.rail;
|
||||
/* if it is an electric rail engine and its railtype is the wrong one */
|
||||
if (rv_info->engclass == 2 && rv_info->railtype == old_railtype) {
|
||||
/* change it to the proper one */
|
||||
/* update railtype of engines intended to use elrail */
|
||||
if (rv_info->intended_railtype == RAILTYPE_ELECTRIC) {
|
||||
rv_info->railtype = new_railtype;
|
||||
}
|
||||
}
|
||||
|
@@ -73,6 +73,7 @@ Engine::Engine(VehicleType type, EngineID base)
|
||||
this->grf_prop.local_id = base;
|
||||
this->list_position = base;
|
||||
this->preview_company = INVALID_COMPANY;
|
||||
this->display_last_variant = INVALID_ENGINE;
|
||||
|
||||
/* Check if this base engine is within the original engine data range */
|
||||
if (base >= _engine_counts[type]) {
|
||||
@@ -93,6 +94,8 @@ Engine::Engine(VehicleType type, EngineID base)
|
||||
}
|
||||
/* Set cargo aging period to the default value. */
|
||||
this->info.cargo_age_period = CARGO_AGING_TICKS;
|
||||
/* Not a variant */
|
||||
this->info.variant_id = INVALID_ENGINE;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -535,7 +538,7 @@ void SetupEngines()
|
||||
_engine_pool.CleanPool();
|
||||
|
||||
assert(_engine_mngr.size() >= _engine_mngr.NUM_DEFAULT_ENGINES);
|
||||
uint index = 0;
|
||||
[[maybe_unused]] uint index = 0;
|
||||
for (const EngineIDMapping &eid : _engine_mngr) {
|
||||
/* Assert is safe; there won't be more than 256 original vehicles
|
||||
* in any case, and we just cleaned the pool. */
|
||||
@@ -559,13 +562,32 @@ static bool IsWagon(EngineID index)
|
||||
return e->type == VEH_TRAIN && e->u.rail.railveh_type == RAILVEH_WAGON;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure engine is not set as the last used variant for any other engine.
|
||||
* @param engine_id Engine being removed.
|
||||
* @param type Type of engine.
|
||||
*/
|
||||
static void ClearLastVariant(EngineID engine_id, VehicleType type)
|
||||
{
|
||||
for (Engine *e : Engine::IterateType(type)) {
|
||||
if (e->display_last_variant == engine_id) e->display_last_variant = INVALID_ENGINE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update #Engine::reliability and (if needed) update the engine GUIs.
|
||||
* @param e %Engine to update.
|
||||
*/
|
||||
static void CalcEngineReliability(Engine *e)
|
||||
void CalcEngineReliability(Engine *e, bool new_month)
|
||||
{
|
||||
uint age = e->age;
|
||||
/* Get source engine for reliability age. This is normally our engine unless variant reliability syncing is requested. */
|
||||
Engine *re = e;
|
||||
while (re->info.variant_id != INVALID_ENGINE && re->info.variant_id != re->index && (re->info.extra_flags & ExtraEngineFlags::SyncReliability) != ExtraEngineFlags::None) {
|
||||
re = Engine::Get(re->info.variant_id);
|
||||
}
|
||||
|
||||
uint age = re->age;
|
||||
if (new_month && re->index > e->index && age != MAX_DAY) age++; /* parent variant's age has not yet updated. */
|
||||
|
||||
/* Check for early retirement */
|
||||
if (e->company_avail != 0 && !_settings_game.vehicle.never_expire_vehicles && e->info.base_life != 0xFF) {
|
||||
@@ -574,6 +596,7 @@ static void CalcEngineReliability(Engine *e)
|
||||
if (retire_early != 0 && age >= retire_early_max_age) {
|
||||
/* Early retirement is enabled and we're past the date... */
|
||||
e->company_avail = 0;
|
||||
ClearLastVariant(e->index, e->type);
|
||||
AddRemoveEngineFromAutoreplaceAndBuildWindows(e->type);
|
||||
}
|
||||
}
|
||||
@@ -594,10 +617,10 @@ static void CalcEngineReliability(Engine *e)
|
||||
e->company_avail = 0;
|
||||
e->reliability = e->reliability_final;
|
||||
/* Kick this engine out of the lists */
|
||||
ClearLastVariant(e->index, e->type);
|
||||
AddRemoveEngineFromAutoreplaceAndBuildWindows(e->type);
|
||||
}
|
||||
SetWindowClassesDirty(WC_BUILD_VEHICLE); // Update to show the new reliability
|
||||
SetWindowClassesDirty(WC_REPLACE_VEHICLE);
|
||||
|
||||
}
|
||||
|
||||
/** Compute the value for #_year_engine_aging_stops. */
|
||||
@@ -625,8 +648,9 @@ void SetYearEngineAgingStops()
|
||||
* Start/initialise one engine.
|
||||
* @param e The engine to initialise.
|
||||
* @param aging_date The date used for age calculations.
|
||||
* @param seed Random seed.
|
||||
*/
|
||||
void StartupOneEngine(Engine *e, Date aging_date)
|
||||
void StartupOneEngine(Engine *e, Date aging_date, uint32 seed)
|
||||
{
|
||||
const EngineInfo *ei = &e->info;
|
||||
|
||||
@@ -639,7 +663,7 @@ void StartupOneEngine(Engine *e, Date aging_date)
|
||||
* Make sure they use the same randomisation of the date. */
|
||||
SavedRandomSeeds saved_seeds;
|
||||
SaveRandomSeeds(&saved_seeds);
|
||||
SetRandomSeed(_settings_game.game_creation.generation_seed ^
|
||||
SetRandomSeed(_settings_game.game_creation.generation_seed ^ seed ^
|
||||
ei->base_intro ^
|
||||
e->type ^
|
||||
e->GetGRFID());
|
||||
@@ -655,7 +679,17 @@ void StartupOneEngine(Engine *e, Date aging_date)
|
||||
e->flags |= ENGINE_AVAILABLE;
|
||||
}
|
||||
|
||||
RestoreRandomSeeds(saved_seeds);
|
||||
/* Get parent variant index for syncing reliability via random seed. */
|
||||
const Engine *re = e;
|
||||
while (re->info.variant_id != INVALID_ENGINE && re->info.variant_id != re->index && (re->info.extra_flags & ExtraEngineFlags::SyncReliability) != ExtraEngineFlags::None) {
|
||||
re = Engine::Get(re->info.variant_id);
|
||||
}
|
||||
|
||||
SetRandomSeed(_settings_game.game_creation.generation_seed ^ seed ^
|
||||
(re->index << 16) ^ (re->info.base_intro << 12) ^ (re->info.decay_speed << 8) ^
|
||||
(re->info.lifelength << 4) ^ re->info.retire_early ^
|
||||
e->type ^
|
||||
e->GetGRFID());
|
||||
|
||||
r = Random();
|
||||
e->reliability_start = GB(r, 16, 14) + 0x7AE0;
|
||||
@@ -667,9 +701,9 @@ void StartupOneEngine(Engine *e, Date aging_date)
|
||||
e->duration_phase_2 = GB(r, 5, 4) + ei->base_life * 12 - 96;
|
||||
e->duration_phase_3 = GB(r, 9, 7) + 120;
|
||||
|
||||
e->reliability_spd_dec = ei->decay_speed << 2;
|
||||
RestoreRandomSeeds(saved_seeds);
|
||||
|
||||
CalcEngineReliability(e);
|
||||
e->reliability_spd_dec = ei->decay_speed << 2;
|
||||
|
||||
/* prevent certain engines from ever appearing. */
|
||||
if (!HasBit(ei->climates, _settings_game.game_creation.landscape)) {
|
||||
@@ -686,9 +720,13 @@ void StartupEngines()
|
||||
{
|
||||
/* Aging of vehicles stops, so account for that when starting late */
|
||||
const Date aging_date = std::min(_date, ConvertYMDToDate(_year_engine_aging_stops, 0, 1));
|
||||
uint32 seed = Random();
|
||||
|
||||
for (Engine *e : Engine::Iterate()) {
|
||||
StartupOneEngine(e, aging_date);
|
||||
StartupOneEngine(e, aging_date, seed);
|
||||
}
|
||||
for (Engine *e : Engine::Iterate()) {
|
||||
CalcEngineReliability(e, false);
|
||||
}
|
||||
|
||||
/* Update the bitmasks for the vehicle lists */
|
||||
@@ -699,6 +737,9 @@ void StartupEngines()
|
||||
|
||||
/* Invalidate any open purchase lists */
|
||||
InvalidateWindowClassesData(WC_BUILD_VEHICLE);
|
||||
|
||||
SetWindowClassesDirty(WC_BUILD_VEHICLE);
|
||||
SetWindowClassesDirty(WC_REPLACE_VEHICLE);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -747,6 +788,7 @@ static void DisableEngineForCompany(EngineID eid, CompanyID company)
|
||||
}
|
||||
|
||||
if (company == _local_company) {
|
||||
ClearLastVariant(e->index, e->type);
|
||||
AddRemoveEngineFromAutoreplaceAndBuildWindows(e->type);
|
||||
}
|
||||
}
|
||||
@@ -755,8 +797,9 @@ static void DisableEngineForCompany(EngineID eid, CompanyID company)
|
||||
* Company \a company accepts engine \a eid for preview.
|
||||
* @param eid Engine being accepted (is under preview).
|
||||
* @param company Current company previewing the engine.
|
||||
* @param recursion_depth Recursion depth to avoid infinite loop.
|
||||
*/
|
||||
static void AcceptEnginePreview(EngineID eid, CompanyID company)
|
||||
static void AcceptEnginePreview(EngineID eid, CompanyID company, int recursion_depth = 0)
|
||||
{
|
||||
Engine *e = Engine::Get(eid);
|
||||
|
||||
@@ -771,6 +814,16 @@ static void AcceptEnginePreview(EngineID eid, CompanyID company)
|
||||
* we have to use the GUI-scope scheduling of InvalidateWindowData.
|
||||
*/
|
||||
InvalidateWindowData(WC_ENGINE_PREVIEW, eid);
|
||||
|
||||
/* Don't search for variants to include if we are 10 levels deep already. */
|
||||
if (recursion_depth >= 10) return;
|
||||
|
||||
/* Find variants to be included in preview. */
|
||||
for (Engine *ve : Engine::IterateType(e->type)) {
|
||||
if (ve->index != eid && ve->info.variant_id == eid && (ve->info.extra_flags & ExtraEngineFlags::JoinPreview) != ExtraEngineFlags::None) {
|
||||
AcceptEnginePreview(ve->index, company, recursion_depth + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -994,9 +1047,9 @@ static void NewVehicleAvailable(Engine *e)
|
||||
if (!IsVehicleTypeDisabled(e->type, true)) AI::BroadcastNewEvent(new ScriptEventEngineAvailable(index));
|
||||
|
||||
/* Only provide the "New Vehicle available" news paper entry, if engine can be built. */
|
||||
if (!IsVehicleTypeDisabled(e->type, false)) {
|
||||
if (!IsVehicleTypeDisabled(e->type, false) && (e->info.extra_flags & ExtraEngineFlags::NoNews) == ExtraEngineFlags::None) {
|
||||
SetDParam(0, GetEngineCategoryName(index));
|
||||
SetDParam(1, index);
|
||||
SetDParam(1, PackEngineNameDParam(index, EngineNameContext::PreviewNews));
|
||||
AddNewsItem(STR_NEWS_NEW_VEHICLE_NOW_AVAILABLE_WITH_TYPE, NT_NEW_VEHICLES, NF_VEHICLE, NR_ENGINE, index);
|
||||
}
|
||||
|
||||
@@ -1013,11 +1066,13 @@ static void NewVehicleAvailable(Engine *e)
|
||||
void EnginesMonthlyLoop()
|
||||
{
|
||||
if (_cur_year < _year_engine_aging_stops) {
|
||||
bool refresh = false;
|
||||
for (Engine *e : Engine::Iterate()) {
|
||||
/* Age the vehicle */
|
||||
if ((e->flags & ENGINE_AVAILABLE) && e->age != MAX_DAY) {
|
||||
e->age++;
|
||||
CalcEngineReliability(e);
|
||||
CalcEngineReliability(e, true);
|
||||
refresh = true;
|
||||
}
|
||||
|
||||
/* Do not introduce invalid engines */
|
||||
@@ -1036,6 +1091,9 @@ void EnginesMonthlyLoop()
|
||||
/* Do not introduce new rail wagons */
|
||||
if (IsWagon(e->index)) continue;
|
||||
|
||||
/* Engine has no preview */
|
||||
if ((e->info.extra_flags & ExtraEngineFlags::NoPreview) != ExtraEngineFlags::None) continue;
|
||||
|
||||
/* Show preview dialog to one of the companies. */
|
||||
e->flags |= ENGINE_EXCLUSIVE_PREVIEW;
|
||||
e->preview_company = INVALID_COMPANY;
|
||||
@@ -1044,6 +1102,11 @@ void EnginesMonthlyLoop()
|
||||
}
|
||||
|
||||
InvalidateWindowClassesData(WC_BUILD_VEHICLE); // rebuild the purchase list (esp. when sorted by reliability)
|
||||
|
||||
if (refresh) {
|
||||
SetWindowClassesDirty(WC_BUILD_VEHICLE);
|
||||
SetWindowClassesDirty(WC_REPLACE_VEHICLE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1176,6 +1239,9 @@ void CheckEngines()
|
||||
for (const Engine *e : Engine::Iterate()) {
|
||||
if (!e->IsEnabled()) continue;
|
||||
|
||||
/* Don't consider train wagons, we need a powered engine available. */
|
||||
if (e->type == VEH_TRAIN && e->u.rail.railveh_type == RAILVEH_WAGON) continue;
|
||||
|
||||
/* We have an available engine... yay! */
|
||||
if ((e->flags & ENGINE_AVAILABLE) != 0 && e->company_avail != 0) return;
|
||||
|
||||
|
@@ -21,6 +21,15 @@ struct WagonOverride {
|
||||
const SpriteGroup *group;
|
||||
};
|
||||
|
||||
/** Flags used client-side in the purchase/autorenew engine list. */
|
||||
enum class EngineDisplayFlags : byte {
|
||||
None = 0, ///< No flag set.
|
||||
HasVariants = (1U << 0), ///< Set if engine has variants.
|
||||
IsFolded = (1U << 1), ///< Set if display of variants should be folded (hidden).
|
||||
Shaded = (1U << 2), ///< Set if engine should be masked.
|
||||
};
|
||||
DECLARE_ENUM_AS_BIT_SET(EngineDisplayFlags)
|
||||
|
||||
typedef Pool<Engine, EngineID, 64, 64000> EnginePool;
|
||||
extern EnginePool _engine_pool;
|
||||
|
||||
@@ -45,6 +54,9 @@ struct Engine : EnginePool::PoolItem<&_engine_pool> {
|
||||
uint8 original_image_index; ///< Original vehicle image index, thus the image index of the overridden vehicle
|
||||
VehicleType type; ///< %Vehicle type, ie #VEH_ROAD, #VEH_TRAIN, etc.
|
||||
|
||||
EngineDisplayFlags display_flags; ///< NOSAVE client-side-only display flags for build engine list.
|
||||
EngineID display_last_variant; ///< NOSAVE client-side-only last variant selected.
|
||||
|
||||
EngineInfo info;
|
||||
|
||||
union {
|
||||
|
@@ -24,9 +24,9 @@ extern const uint8 _engine_offsets[4];
|
||||
|
||||
bool IsEngineBuildable(EngineID engine, VehicleType type, CompanyID company);
|
||||
bool IsEngineRefittable(EngineID engine);
|
||||
void GetArticulatedVehicleCargoesAndRefits(EngineID engine, CargoArray *cargoes, CargoTypes *refits, CargoID cargo_type, uint cargo_capacity);
|
||||
void SetYearEngineAgingStops();
|
||||
void StartupOneEngine(Engine *e, Date aging_date);
|
||||
void CalcEngineReliability(Engine *e, bool new_month);
|
||||
void StartupOneEngine(Engine *e, Date aging_date, uint32 seed);
|
||||
|
||||
uint GetTotalCapacityOfArticulatedParts(EngineID engine);
|
||||
|
||||
|
@@ -24,6 +24,7 @@
|
||||
#include "ship.h"
|
||||
#include "aircraft.h"
|
||||
#include "engine_cmd.h"
|
||||
#include "zoom_func.h"
|
||||
|
||||
#include "widgets/engine_widget.h"
|
||||
|
||||
@@ -94,7 +95,7 @@ struct EnginePreviewWindow : Window {
|
||||
case VEH_SHIP: GetShipSpriteSize( engine, x, y, x_offs, y_offs, image_type); break;
|
||||
case VEH_AIRCRAFT: GetAircraftSpriteSize(engine, x, y, x_offs, y_offs, image_type); break;
|
||||
}
|
||||
this->vehicle_space = std::max<int>(40, y - y_offs);
|
||||
this->vehicle_space = std::max<int>(ScaleSpriteTrad(40), y - y_offs);
|
||||
|
||||
size->width = std::max(size->width, x - x_offs);
|
||||
SetDParam(0, GetEngineCategoryName(engine));
|
||||
@@ -111,7 +112,7 @@ struct EnginePreviewWindow : Window {
|
||||
SetDParam(0, GetEngineCategoryName(engine));
|
||||
int y = DrawStringMultiLine(r, STR_ENGINE_PREVIEW_MESSAGE, TC_FROMSTRING, SA_HOR_CENTER | SA_TOP) + WidgetDimensions::scaled.vsep_wide;
|
||||
|
||||
SetDParam(0, engine);
|
||||
SetDParam(0, PackEngineNameDParam(engine, EngineNameContext::PreviewNews));
|
||||
DrawString(r.left, r.right, y, STR_ENGINE_NAME, TC_BLACK, SA_HOR_CENTER);
|
||||
y += FONT_HEIGHT_NORMAL;
|
||||
|
||||
@@ -336,7 +337,7 @@ void EngList_Sort(GUIEngineList *el, EngList_SortTypeFunction compare)
|
||||
* @param begin start of sorting
|
||||
* @param num_items count of items to be sorted
|
||||
*/
|
||||
void EngList_SortPartial(GUIEngineList *el, EngList_SortTypeFunction compare, uint begin, uint num_items)
|
||||
void EngList_SortPartial(GUIEngineList *el, EngList_SortTypeFunction compare, size_t begin, size_t num_items)
|
||||
{
|
||||
if (num_items < 2) return;
|
||||
assert(begin < el->size());
|
||||
|
@@ -14,12 +14,25 @@
|
||||
#include "sortlist_type.h"
|
||||
#include "gfx_type.h"
|
||||
#include "vehicle_type.h"
|
||||
#include "engine_base.h"
|
||||
|
||||
typedef GUIList<EngineID, CargoID> GUIEngineList;
|
||||
struct GUIEngineListItem {
|
||||
EngineID engine_id; ///< Engine to display in build purchase list
|
||||
EngineID variant_id; ///< Variant group of the engine.
|
||||
EngineDisplayFlags flags; ///< Flags for toggling/drawing (un)folded status and controlling indentation.
|
||||
int8 indent; ///< Display indentation level.
|
||||
|
||||
typedef bool EngList_SortTypeFunction(const EngineID&, const EngineID&); ///< argument type for #EngList_Sort.
|
||||
GUIEngineListItem(EngineID engine_id, EngineID variant_id, EngineDisplayFlags flags, int indent) : engine_id(engine_id), variant_id(variant_id), flags(flags), indent(indent) {}
|
||||
|
||||
/* Used when searching list only by engine_id. */
|
||||
bool operator == (const EngineID &other) const { return this->engine_id == other; }
|
||||
};
|
||||
|
||||
typedef GUIList<GUIEngineListItem, CargoID> GUIEngineList;
|
||||
|
||||
typedef bool EngList_SortTypeFunction(const GUIEngineListItem&, const GUIEngineListItem&); ///< argument type for #EngList_Sort.
|
||||
void EngList_Sort(GUIEngineList *el, EngList_SortTypeFunction compare);
|
||||
void EngList_SortPartial(GUIEngineList *el, EngList_SortTypeFunction compare, uint begin, uint num_items);
|
||||
void EngList_SortPartial(GUIEngineList *el, EngList_SortTypeFunction compare, size_t begin, size_t num_items);
|
||||
|
||||
StringID GetEngineCategoryName(EngineID engine);
|
||||
StringID GetEngineInfoString(EngineID engine);
|
||||
|
@@ -43,7 +43,8 @@ struct RailVehicleInfo {
|
||||
byte image_index;
|
||||
RailVehicleTypes railveh_type;
|
||||
byte cost_factor; ///< Purchase cost factor; For multiheaded engines the sum of both engine prices.
|
||||
RailType railtype;
|
||||
RailType railtype; ///< Railtype, mangled if elrail is disabled.
|
||||
RailType intended_railtype; ///< Intended railtype, regardless of elrail being enabled or disabled.
|
||||
uint16 max_speed; ///< Maximum speed (1 unit = 1/1.6 mph = 1 km-ish/h)
|
||||
uint16 power; ///< Power of engine (hp); For multiheaded engines the sum of both engine powers.
|
||||
uint16 weight; ///< Weight of vehicle (tons); For multiheaded engines the weight of each single engine.
|
||||
@@ -126,6 +127,15 @@ struct RoadVehicleInfo {
|
||||
RoadType roadtype; ///< Road type
|
||||
};
|
||||
|
||||
enum class ExtraEngineFlags : uint32 {
|
||||
None = 0,
|
||||
NoNews = (1U << 0), ///< No 'new vehicle' news will be generated.
|
||||
NoPreview = (1U << 1), ///< No exclusive preview will be offered.
|
||||
JoinPreview = (1U << 2), ///< Engine will join exclusive preview with variant parent.
|
||||
SyncReliability = (1U << 3), ///< Engine reliability will be synced with variant parent.
|
||||
};
|
||||
DECLARE_ENUM_AS_BIT_SET(ExtraEngineFlags);
|
||||
|
||||
/**
|
||||
* Information about a vehicle
|
||||
* @see table/engines.h
|
||||
@@ -140,11 +150,13 @@ struct EngineInfo {
|
||||
CargoID cargo_type;
|
||||
CargoTypes refit_mask;
|
||||
byte refit_cost;
|
||||
byte misc_flags; ///< Miscellaneous flags. @see EngineMiscFlags
|
||||
byte callback_mask; ///< Bitmask of vehicle callbacks that have to be called
|
||||
int8 retire_early; ///< Number of years early to retire vehicle
|
||||
StringID string_id; ///< Default name of engine
|
||||
byte misc_flags; ///< Miscellaneous flags. @see EngineMiscFlags
|
||||
uint16 callback_mask; ///< Bitmask of vehicle callbacks that have to be called
|
||||
int8 retire_early; ///< Number of years early to retire vehicle
|
||||
StringID string_id; ///< Default name of engine
|
||||
uint16 cargo_age_period; ///< Number of ticks before carried cargo is aged.
|
||||
EngineID variant_id; ///< Engine variant ID. If set, will be treated specially in purchase lists.
|
||||
ExtraEngineFlags extra_flags;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -155,7 +167,7 @@ enum EngineMiscFlags {
|
||||
EF_ROAD_TRAM = 0, ///< Road vehicle is a tram/light rail vehicle
|
||||
EF_USES_2CC = 1, ///< Vehicle uses two company colours
|
||||
EF_RAIL_IS_MU = 2, ///< Rail vehicle is a multiple-unit (DMU/EMU)
|
||||
EF_RAIL_FLIPS = 3, ///< Rail vehicle can be flipped in the depot
|
||||
EF_RAIL_FLIPS = 3, ///< Rail vehicle has old depot-flip handling
|
||||
EF_AUTO_REFIT = 4, ///< Automatic refitting is allowed
|
||||
EF_NO_DEFAULT_CARGO_MULTIPLIER = 5, ///< Use the new capacity algorithm. The default cargotype of the vehicle does not affect capacity multipliers. CB 15 is also called in purchase list.
|
||||
EF_NO_BREAKDOWN_SMOKE = 6, ///< Do not show black smoke during a breakdown.
|
||||
@@ -170,6 +182,22 @@ enum EngineFlags {
|
||||
ENGINE_EXCLUSIVE_PREVIEW = 2, ///< This vehicle is in the exclusive preview stage, either being used or being offered to a company.
|
||||
};
|
||||
|
||||
/**
|
||||
* Contexts an engine name can be shown in.
|
||||
*/
|
||||
enum EngineNameContext : uint8 {
|
||||
Generic = 0x00, ///< No specific context available.
|
||||
VehicleDetails = 0x11, ///< Name is shown in the vehicle details GUI.
|
||||
PurchaseList = 0x20, ///< Name is shown in the purchase list (including autoreplace window).
|
||||
PreviewNews = 0x21, ///< Name is shown in exclusive preview or newspaper.
|
||||
};
|
||||
|
||||
/** Combine an engine ID and a name context to an engine name dparam. */
|
||||
inline uint64 PackEngineNameDParam(EngineID engine_id, EngineNameContext context, uint32 extra_data = 0)
|
||||
{
|
||||
return engine_id | (static_cast<uint64>(context) << 32) | (static_cast<uint64>(extra_data) << 40);
|
||||
}
|
||||
|
||||
static const uint MAX_LENGTH_ENGINE_NAME_CHARS = 32; ///< The maximum length of an engine name in characters including '\0'
|
||||
|
||||
static const EngineID INVALID_ENGINE = 0xFFFF; ///< Constant denoting an invalid engine.
|
||||
|
@@ -418,7 +418,7 @@ uint TarScanner::DoScan(Subdirectory sd)
|
||||
|
||||
/* static */ uint TarScanner::DoScan(TarScanner::Mode mode)
|
||||
{
|
||||
Debug(misc, 1, "Scanning for tars");
|
||||
Debug(misc, 2, "Scanning for tars");
|
||||
TarScanner fs;
|
||||
uint num = 0;
|
||||
if (mode & TarScanner::BASESET) {
|
||||
@@ -439,7 +439,7 @@ uint TarScanner::DoScan(Subdirectory sd)
|
||||
num += fs.DoScan(SCENARIO_DIR);
|
||||
num += fs.DoScan(HEIGHTMAP_DIR);
|
||||
}
|
||||
Debug(misc, 1, "Scan complete, found {} files", num);
|
||||
Debug(misc, 2, "Scan complete, found {} files", num);
|
||||
return num;
|
||||
}
|
||||
|
||||
@@ -571,7 +571,7 @@ bool TarScanner::AddFile(const std::string &filename, size_t basepath_length, co
|
||||
|
||||
/* Only allow relative links */
|
||||
if (link[0] == PATHSEPCHAR) {
|
||||
Debug(misc, 1, "Ignoring absolute link in tar: {} -> {}", name, link);
|
||||
Debug(misc, 5, "Ignoring absolute link in tar: {} -> {}", name, link);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -597,7 +597,7 @@ bool TarScanner::AddFile(const std::string &filename, size_t basepath_length, co
|
||||
} else if (strcmp(pos, "..") == 0) {
|
||||
/* level up */
|
||||
if (dest[0] == '\0') {
|
||||
Debug(misc, 1, "Ignoring link pointing outside of data directory: {} -> {}", name, link);
|
||||
Debug(misc, 5, "Ignoring link pointing outside of data directory: {} -> {}", name, link);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -652,7 +652,7 @@ bool TarScanner::AddFile(const std::string &filename, size_t basepath_length, co
|
||||
pos += skip;
|
||||
}
|
||||
|
||||
Debug(misc, 1, "Found tar '{}' with {} new files", filename, num);
|
||||
Debug(misc, 4, "Found tar '{}' with {} new files", filename, num);
|
||||
fclose(f);
|
||||
|
||||
/* Resolve file links and store directory links.
|
||||
@@ -690,7 +690,7 @@ bool ExtractTar(const std::string &tar_filename, Subdirectory subdir)
|
||||
|
||||
/* The file doesn't have a sub directory! */
|
||||
if (dirname.empty()) {
|
||||
Debug(misc, 1, "Extracting {} failed; archive rejected, the contents must be in a sub directory", tar_filename);
|
||||
Debug(misc, 3, "Extracting {} failed; archive rejected, the contents must be in a sub directory", tar_filename);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -987,7 +987,7 @@ void DeterminePaths(const char *exe, bool only_local_path)
|
||||
|
||||
for (Searchpath sp : _valid_searchpaths) {
|
||||
if (sp == SP_WORKING_DIR && !_do_scan_working_directory) continue;
|
||||
Debug(misc, 4, "{} added as search path", _searchpaths[sp]);
|
||||
Debug(misc, 3, "{} added as search path", _searchpaths[sp]);
|
||||
}
|
||||
|
||||
std::string config_dir;
|
||||
@@ -1020,7 +1020,7 @@ void DeterminePaths(const char *exe, bool only_local_path)
|
||||
_config_file = config_dir + "openttd.cfg";
|
||||
}
|
||||
|
||||
Debug(misc, 3, "{} found as config directory", config_dir);
|
||||
Debug(misc, 1, "{} found as config directory", config_dir);
|
||||
|
||||
_highscore_file = config_dir + "hs.dat";
|
||||
extern std::string _hotkeys_file;
|
||||
@@ -1056,7 +1056,7 @@ void DeterminePaths(const char *exe, bool only_local_path)
|
||||
FioCreateDirectory(_personal_dir);
|
||||
#endif
|
||||
|
||||
Debug(misc, 3, "{} found as personal directory", _personal_dir);
|
||||
Debug(misc, 1, "{} found as personal directory", _personal_dir);
|
||||
|
||||
static const Subdirectory default_subdirs[] = {
|
||||
SAVE_DIR, AUTOSAVE_DIR, SCENARIO_DIR, HEIGHTMAP_DIR, BASESET_DIR, NEWGRF_DIR, AI_DIR, AI_LIBRARY_DIR, GAME_DIR, GAME_LIBRARY_DIR, SCREENSHOT_DIR
|
||||
@@ -1068,7 +1068,7 @@ void DeterminePaths(const char *exe, bool only_local_path)
|
||||
|
||||
/* If we have network we make a directory for the autodownloading of content */
|
||||
_searchpaths[SP_AUTODOWNLOAD_DIR] = _personal_dir + "content_download" PATHSEP;
|
||||
Debug(misc, 4, "{} added as search path", _searchpaths[SP_AUTODOWNLOAD_DIR]);
|
||||
Debug(misc, 3, "{} added as search path", _searchpaths[SP_AUTODOWNLOAD_DIR]);
|
||||
FioCreateDirectory(_searchpaths[SP_AUTODOWNLOAD_DIR]);
|
||||
FillValidSearchPaths(only_local_path);
|
||||
|
||||
|
@@ -13,6 +13,11 @@
|
||||
#include "blitter/factory.hpp"
|
||||
#include "gfx_layout.h"
|
||||
#include "fontcache/spritefontcache.h"
|
||||
#include "openttd.h"
|
||||
#include "settings_func.h"
|
||||
#include "strings_func.h"
|
||||
#include "viewport_func.h"
|
||||
#include "window_func.h"
|
||||
|
||||
#include "safeguards.h"
|
||||
|
||||
@@ -70,15 +75,51 @@ bool GetFontAAState(FontSize size, bool check_blitter)
|
||||
/* AA is only supported for 32 bpp */
|
||||
if (check_blitter && BlitterFactory::GetCurrentBlitter()->GetScreenDepth() != 32) return false;
|
||||
|
||||
switch (size) {
|
||||
default: NOT_REACHED();
|
||||
case FS_NORMAL: return _fcsettings.medium.aa;
|
||||
case FS_SMALL: return _fcsettings.small.aa;
|
||||
case FS_LARGE: return _fcsettings.large.aa;
|
||||
case FS_MONO: return _fcsettings.mono.aa;
|
||||
}
|
||||
return GetFontCacheSubSetting(size)->aa;
|
||||
}
|
||||
|
||||
void SetFont(FontSize fontsize, const std::string& font, uint size, bool aa)
|
||||
{
|
||||
FontCacheSubSetting *setting = GetFontCacheSubSetting(fontsize);
|
||||
bool changed = false;
|
||||
|
||||
if (setting->font != font) {
|
||||
setting->font = font;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (setting->size != size) {
|
||||
setting->size = size;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (setting->aa != aa) {
|
||||
setting->aa = aa;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (!changed) return;
|
||||
|
||||
if (fontsize != FS_MONO) {
|
||||
/* Try to reload only the modified font. */
|
||||
FontCacheSettings backup = _fcsettings;
|
||||
for (FontSize fs = FS_BEGIN; fs < FS_END; fs++) {
|
||||
if (fs == fontsize) continue;
|
||||
FontCache *fc = FontCache::Get(fs);
|
||||
GetFontCacheSubSetting(fs)->font = fc->HasParent() ? fc->GetFontName() : "";
|
||||
}
|
||||
CheckForMissingGlyphs();
|
||||
_fcsettings = backup;
|
||||
} else {
|
||||
InitFontCache(true);
|
||||
}
|
||||
|
||||
LoadStringWidthTable();
|
||||
UpdateAllVirtCoords();
|
||||
ReInitAllWindows(true);
|
||||
|
||||
if (_save_config) SaveToConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* (Re)initialize the font cache related things, i.e. load the non-sprite fonts.
|
||||
|
@@ -29,12 +29,12 @@ protected:
|
||||
int descender; ///< The descender value of the font.
|
||||
int units_per_em; ///< The units per EM value of the font.
|
||||
|
||||
static int GetDefaultFontHeight(FontSize fs);
|
||||
|
||||
public:
|
||||
FontCache(FontSize fs);
|
||||
virtual ~FontCache();
|
||||
|
||||
static int GetDefaultFontHeight(FontSize fs);
|
||||
|
||||
/**
|
||||
* Get the FontSize of the font.
|
||||
* @return The FontSize.
|
||||
@@ -132,7 +132,7 @@ public:
|
||||
* Get the name of this font.
|
||||
* @return The name of the font.
|
||||
*/
|
||||
virtual const char *GetFontName() = 0;
|
||||
virtual std::string GetFontName() = 0;
|
||||
|
||||
/**
|
||||
* Get the font cache of a given font size.
|
||||
@@ -218,10 +218,27 @@ struct FontCacheSettings {
|
||||
|
||||
extern FontCacheSettings _fcsettings;
|
||||
|
||||
/**
|
||||
* Get the settings of a given font size.
|
||||
* @param fs The font size to look up.
|
||||
* @return The settings.
|
||||
*/
|
||||
static inline FontCacheSubSetting *GetFontCacheSubSetting(FontSize fs)
|
||||
{
|
||||
switch (fs) {
|
||||
default: NOT_REACHED();
|
||||
case FS_SMALL: return &_fcsettings.small;
|
||||
case FS_NORMAL: return &_fcsettings.medium;
|
||||
case FS_LARGE: return &_fcsettings.large;
|
||||
case FS_MONO: return &_fcsettings.mono;
|
||||
}
|
||||
}
|
||||
|
||||
void InitFontCache(bool monospace);
|
||||
void UninitFontCache();
|
||||
bool HasAntialiasedFonts();
|
||||
|
||||
bool GetFontAAState(FontSize size, bool check_blitter = true);
|
||||
void SetFont(FontSize fontsize, const std::string &font, uint size, bool aa);
|
||||
|
||||
#endif /* FONTCACHE_H */
|
||||
|
@@ -33,16 +33,16 @@ private:
|
||||
FT_Face face; ///< The font face associated with this font.
|
||||
|
||||
void SetFontSize(FontSize fs, FT_Face face, int pixels);
|
||||
virtual const void *InternalGetFontTable(uint32 tag, size_t &length);
|
||||
virtual const Sprite *InternalGetGlyph(GlyphID key, bool aa);
|
||||
const void *InternalGetFontTable(uint32 tag, size_t &length) override;
|
||||
const Sprite *InternalGetGlyph(GlyphID key, bool aa) override;
|
||||
|
||||
public:
|
||||
FreeTypeFontCache(FontSize fs, FT_Face face, int pixels);
|
||||
~FreeTypeFontCache();
|
||||
virtual void ClearFontCache();
|
||||
virtual GlyphID MapCharToGlyph(WChar key);
|
||||
virtual const char *GetFontName() { return face->family_name; }
|
||||
virtual bool IsBuiltInFont() { return false; }
|
||||
void ClearFontCache() override;
|
||||
GlyphID MapCharToGlyph(WChar key) override;
|
||||
std::string GetFontName() override { return fmt::format("{}, {}", face->family_name, face->style_name); }
|
||||
bool IsBuiltInFont() override { return false; }
|
||||
};
|
||||
|
||||
FT_Library _library = nullptr;
|
||||
@@ -65,14 +65,14 @@ void FreeTypeFontCache::SetFontSize(FontSize fs, FT_Face face, int pixels)
|
||||
{
|
||||
if (pixels == 0) {
|
||||
/* Try to determine a good height based on the minimal height recommended by the font. */
|
||||
int scaled_height = ScaleGUITrad(this->GetDefaultFontHeight(this->fs));
|
||||
int scaled_height = ScaleGUITrad(FontCache::GetDefaultFontHeight(this->fs));
|
||||
pixels = scaled_height;
|
||||
|
||||
TT_Header *head = (TT_Header *)FT_Get_Sfnt_Table(this->face, ft_sfnt_head);
|
||||
if (head != nullptr) {
|
||||
/* Font height is minimum height plus the difference between the default
|
||||
* height for this font size and the small size. */
|
||||
int diff = scaled_height - ScaleGUITrad(this->GetDefaultFontHeight(FS_SMALL));
|
||||
int diff = scaled_height - ScaleGUITrad(FontCache::GetDefaultFontHeight(FS_SMALL));
|
||||
/* Clamp() is not used as scaled_height could be greater than MAX_FONT_SIZE, which is not permitted in Clamp(). */
|
||||
pixels = std::min(std::max(std::min<int>(head->Lowest_Rec_PPEM, MAX_FONT_MIN_REC_SIZE) + diff, scaled_height), MAX_FONT_SIZE);
|
||||
}
|
||||
@@ -122,14 +122,7 @@ void FreeTypeFontCache::SetFontSize(FontSize fs, FT_Face face, int pixels)
|
||||
*/
|
||||
void LoadFreeTypeFont(FontSize fs)
|
||||
{
|
||||
FontCacheSubSetting *settings = nullptr;
|
||||
switch (fs) {
|
||||
default: NOT_REACHED();
|
||||
case FS_SMALL: settings = &_fcsettings.small; break;
|
||||
case FS_NORMAL: settings = &_fcsettings.medium; break;
|
||||
case FS_LARGE: settings = &_fcsettings.large; break;
|
||||
case FS_MONO: settings = &_fcsettings.mono; break;
|
||||
}
|
||||
FontCacheSubSetting *settings = GetFontCacheSubSetting(fs);
|
||||
|
||||
if (settings->font.empty()) return;
|
||||
|
||||
@@ -197,8 +190,7 @@ void LoadFreeTypeFont(FontSize fs)
|
||||
|
||||
FT_Done_Face(face);
|
||||
|
||||
static const char *SIZE_TO_NAME[] = { "medium", "small", "large", "mono" };
|
||||
ShowInfoF("Unable to use '%s' for %s font, FreeType reported error 0x%X, using sprite font instead", font_name, SIZE_TO_NAME[fs], error);
|
||||
ShowInfoF("Unable to use '%s' for %s font, FreeType reported error 0x%X, using sprite font instead", font_name, FontSizeToName(fs), error);
|
||||
return;
|
||||
|
||||
found_face:
|
||||
|
@@ -28,8 +28,8 @@ static const int ASCII_LETTERSTART = 32; ///< First printable ASCII letter.
|
||||
SpriteFontCache::SpriteFontCache(FontSize fs) : FontCache(fs), glyph_to_spriteid_map(nullptr)
|
||||
{
|
||||
this->InitializeUnicodeGlyphMap();
|
||||
this->height = ScaleGUITrad(this->GetDefaultFontHeight(this->fs));
|
||||
this->ascender = (this->height - ScaleSpriteTrad(this->GetDefaultFontHeight(this->fs))) / 2;
|
||||
this->height = ScaleGUITrad(FontCache::GetDefaultFontHeight(this->fs));
|
||||
this->ascender = (this->height - ScaleSpriteTrad(FontCache::GetDefaultFontHeight(this->fs))) / 2;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -105,8 +105,8 @@ void SpriteFontCache::ClearGlyphToSpriteMap()
|
||||
void SpriteFontCache::ClearFontCache()
|
||||
{
|
||||
Layouter::ResetFontCache(this->fs);
|
||||
this->height = ScaleGUITrad(this->GetDefaultFontHeight(this->fs));
|
||||
this->ascender = (this->height - ScaleSpriteTrad(this->GetDefaultFontHeight(this->fs))) / 2;
|
||||
this->height = ScaleGUITrad(FontCache::GetDefaultFontHeight(this->fs));
|
||||
this->ascender = (this->height - ScaleSpriteTrad(FontCache::GetDefaultFontHeight(this->fs))) / 2;
|
||||
}
|
||||
|
||||
const Sprite *SpriteFontCache::GetGlyph(GlyphID key)
|
||||
|
@@ -31,7 +31,7 @@ public:
|
||||
virtual bool GetDrawGlyphShadow();
|
||||
virtual GlyphID MapCharToGlyph(WChar key) { assert(IsPrintable(key)); return SPRITE_GLYPH | key; }
|
||||
virtual const void *GetFontTable(uint32 tag, size_t &length) { length = 0; return nullptr; }
|
||||
virtual const char *GetFontName() { return "sprite"; }
|
||||
virtual std::string GetFontName() { return "sprite"; }
|
||||
virtual bool IsBuiltInFont() { return true; }
|
||||
};
|
||||
|
||||
|
@@ -87,11 +87,6 @@ public:
|
||||
*/
|
||||
static void Save();
|
||||
|
||||
/**
|
||||
* Load data for a GameScript from a savegame.
|
||||
*/
|
||||
static void Load(int version);
|
||||
|
||||
/** Wrapper function for GameScanner::GetConsoleList */
|
||||
static std::string GetConsoleList(bool newest_only = false);
|
||||
/** Wrapper function for GameScanner::GetConsoleLibraryList */
|
||||
|
@@ -73,6 +73,9 @@
|
||||
{
|
||||
if (Game::instance != nullptr) return;
|
||||
|
||||
/* Don't start GameScripts in intro */
|
||||
if (_game_mode == GM_MENU) return;
|
||||
|
||||
/* Clients shouldn't start GameScripts */
|
||||
if (_networking && !_network_server) return;
|
||||
|
||||
@@ -88,6 +91,8 @@
|
||||
Game::info = info;
|
||||
Game::instance = new GameInstance();
|
||||
Game::instance->Initialize(info);
|
||||
Game::instance->LoadOnStack(config->GetToLoadData());
|
||||
config->SetToLoadData(nullptr);
|
||||
|
||||
cur_company.Restore();
|
||||
|
||||
@@ -199,6 +204,7 @@
|
||||
InvalidateWindowData(WC_AI_LIST, 0, 1);
|
||||
SetWindowClassesDirty(WC_AI_DEBUG);
|
||||
InvalidateWindowClassesData(WC_AI_SETTINGS);
|
||||
InvalidateWindowClassesData(WC_GAME_OPTIONS);
|
||||
}
|
||||
|
||||
|
||||
@@ -213,18 +219,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ void Game::Load(int version)
|
||||
{
|
||||
if (Game::instance != nullptr && (!_networking || _network_server)) {
|
||||
Backup<CompanyID> cur_company(_current_company, OWNER_DEITY, FILE_LINE);
|
||||
Game::instance->Load(version);
|
||||
cur_company.Restore();
|
||||
} else {
|
||||
/* Read, but ignore, the load data */
|
||||
GameInstance::LoadEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ std::string Game::GetConsoleList(bool newest_only)
|
||||
{
|
||||
return Game::scanner_info->GetConsoleList(newest_only);
|
||||
|
@@ -301,7 +301,7 @@ struct GSConfigWindow : public Window {
|
||||
|
||||
if (this->clicked_row != num) {
|
||||
this->CloseChildWindows(WC_QUERY_STRING);
|
||||
HideDropDownMenu(this);
|
||||
this->CloseChildWindows(WC_DROPDOWN_MENU);
|
||||
this->clicked_row = num;
|
||||
this->clicked_dropdown = false;
|
||||
}
|
||||
@@ -316,7 +316,7 @@ struct GSConfigWindow : public Window {
|
||||
if (!bool_item && IsInsideMM(x, 0, SETTING_BUTTON_WIDTH) && config_item.complete_labels) {
|
||||
if (this->clicked_dropdown) {
|
||||
/* unclick the dropdown */
|
||||
HideDropDownMenu(this);
|
||||
this->CloseChildWindows(WC_DROPDOWN_MENU);
|
||||
this->clicked_dropdown = false;
|
||||
this->closing_dropdown = false;
|
||||
} else {
|
||||
@@ -365,7 +365,7 @@ struct GSConfigWindow : public Window {
|
||||
} else if (!bool_item && !config_item.complete_labels) {
|
||||
/* Display a query box so users can enter a custom value. */
|
||||
SetDParam(0, old_val);
|
||||
ShowQueryString(STR_JUST_INT, STR_CONFIG_SETTING_QUERY_CAPTION, 10, this, CS_NUMERAL, QSF_NONE);
|
||||
ShowQueryString(STR_JUST_INT, STR_CONFIG_SETTING_QUERY_CAPTION, INT32_DIGITS_WITH_SIGN_AND_TERMINATION, this, CS_NUMERAL_SIGNED, QSF_NONE);
|
||||
}
|
||||
this->SetDirty();
|
||||
break;
|
||||
@@ -434,7 +434,7 @@ struct GSConfigWindow : public Window {
|
||||
this->SetWidgetDisabledState(WID_GSC_TEXTFILE + tft, GameConfig::GetConfig()->GetTextfile(tft, (CompanyID)OWNER_DEITY) == nullptr);
|
||||
}
|
||||
this->RebuildVisibleSettings();
|
||||
HideDropDownMenu(this);
|
||||
this->CloseChildWindows(WC_DROPDOWN_MENU);
|
||||
this->CloseChildWindows(WC_QUERY_STRING);
|
||||
}
|
||||
private:
|
||||
|
@@ -84,13 +84,12 @@ void GameInstance::Died()
|
||||
* DoCommand callback function for all commands executed by Game Scripts.
|
||||
* @param cmd cmd as given to DoCommandPInternal.
|
||||
* @param result The result of the command.
|
||||
* @param tile The tile on which the command was executed.
|
||||
* @param data Command data as given to Command<>::Post.
|
||||
* @param result_data Additional returned data from the command.
|
||||
*/
|
||||
void CcGame(Commands cmd, const CommandCost &result, TileIndex tile, const CommandDataBuffer &data, CommandDataBuffer result_data)
|
||||
void CcGame(Commands cmd, const CommandCost &result, const CommandDataBuffer &data, CommandDataBuffer result_data)
|
||||
{
|
||||
if (Game::GetGameInstance()->DoCommandCallback(result, tile, data, std::move(result_data), cmd)) {
|
||||
if (Game::GetGameInstance()->DoCommandCallback(result, data, std::move(result_data), cmd)) {
|
||||
Game::GetGameInstance()->Continue();
|
||||
}
|
||||
}
|
||||
|
@@ -32,6 +32,7 @@
|
||||
#include "video/video_driver.hpp"
|
||||
#include "ai/ai_gui.hpp"
|
||||
#include "game/game_gui.hpp"
|
||||
#include "industry.h"
|
||||
|
||||
#include "widgets/genworld_widget.h"
|
||||
|
||||
@@ -399,7 +400,7 @@ static const StringID _smoothness[] = {STR_CONFIG_SETTING_ROUGHNESS_OF_TERRAIN_
|
||||
static const StringID _rotation[] = {STR_CONFIG_SETTING_HEIGHTMAP_ROTATION_COUNTER_CLOCKWISE, STR_CONFIG_SETTING_HEIGHTMAP_ROTATION_CLOCKWISE, INVALID_STRING_ID};
|
||||
static const StringID _landscape[] = {STR_CONFIG_SETTING_LAND_GENERATOR_ORIGINAL, STR_CONFIG_SETTING_LAND_GENERATOR_TERRA_GENESIS, INVALID_STRING_ID};
|
||||
static const StringID _num_towns[] = {STR_NUM_VERY_LOW, STR_NUM_LOW, STR_NUM_NORMAL, STR_NUM_HIGH, STR_NUM_CUSTOM, INVALID_STRING_ID};
|
||||
static const StringID _num_inds[] = {STR_FUNDING_ONLY, STR_MINIMAL, STR_NUM_VERY_LOW, STR_NUM_LOW, STR_NUM_NORMAL, STR_NUM_HIGH, INVALID_STRING_ID};
|
||||
static const StringID _num_inds[] = {STR_FUNDING_ONLY, STR_MINIMAL, STR_NUM_VERY_LOW, STR_NUM_LOW, STR_NUM_NORMAL, STR_NUM_HIGH, STR_NUM_CUSTOM, INVALID_STRING_ID};
|
||||
static const StringID _variety[] = {STR_VARIETY_NONE, STR_VARIETY_VERY_LOW, STR_VARIETY_LOW, STR_VARIETY_MEDIUM, STR_VARIETY_HIGH, STR_VARIETY_VERY_HIGH, INVALID_STRING_ID};
|
||||
|
||||
static_assert(lengthof(_num_inds) == ID_END + 1);
|
||||
@@ -461,8 +462,19 @@ struct GenerateLandscapeWindow : public Window {
|
||||
break;
|
||||
}
|
||||
|
||||
case WID_GL_INDUSTRY_PULLDOWN: SetDParam(0, _game_mode == GM_EDITOR ? STR_CONFIG_SETTING_OFF : _num_inds[_settings_newgame.difficulty.industry_density]); break;
|
||||
case WID_GL_INDUSTRY_PULLDOWN:
|
||||
if (_game_mode == GM_EDITOR) {
|
||||
SetDParam(0, STR_CONFIG_SETTING_OFF);
|
||||
} else if (_settings_newgame.difficulty.industry_density == ID_CUSTOM) {
|
||||
SetDParam(0, STR_NUM_CUSTOM_NUMBER);
|
||||
SetDParam(1, _settings_newgame.game_creation.custom_industry_number);
|
||||
} else {
|
||||
SetDParam(0, _num_inds[_settings_newgame.difficulty.industry_density]);
|
||||
}
|
||||
break;
|
||||
|
||||
case WID_GL_LANDSCAPE_PULLDOWN: SetDParam(0, _landscape[_settings_newgame.game_creation.land_generator]); break;
|
||||
|
||||
case WID_GL_TERRAIN_PULLDOWN:
|
||||
if (_settings_newgame.difficulty.terrain_type == CUSTOM_TERRAIN_TYPE_NUMBER_DIFFICULTY) {
|
||||
SetDParam(0, STR_TERRAIN_TYPE_CUSTOM_VALUE);
|
||||
@@ -619,7 +631,12 @@ struct GenerateLandscapeWindow : public Window {
|
||||
*size = maxdim(*size, GetStringBoundingBox(STR_NUM_CUSTOM_NUMBER));
|
||||
break;
|
||||
|
||||
case WID_GL_INDUSTRY_PULLDOWN: strs = _num_inds; break;
|
||||
case WID_GL_INDUSTRY_PULLDOWN:
|
||||
strs = _num_inds;
|
||||
SetDParamMaxValue(0, IndustryPool::MAX_SIZE);
|
||||
*size = maxdim(*size, GetStringBoundingBox(STR_NUM_CUSTOM_NUMBER));
|
||||
break;
|
||||
|
||||
case WID_GL_LANDSCAPE_PULLDOWN: strs = _landscape; break;
|
||||
|
||||
case WID_GL_TERRAIN_PULLDOWN:
|
||||
@@ -919,7 +936,15 @@ struct GenerateLandscapeWindow : public Window {
|
||||
}
|
||||
break;
|
||||
|
||||
case WID_GL_INDUSTRY_PULLDOWN: _settings_newgame.difficulty.industry_density = index; break;
|
||||
case WID_GL_INDUSTRY_PULLDOWN:
|
||||
if ((uint)index == ID_CUSTOM) {
|
||||
this->widget_id = widget;
|
||||
SetDParam(0, _settings_newgame.game_creation.custom_industry_number);
|
||||
ShowQueryString(STR_JUST_INT, STR_MAPGEN_NUMBER_OF_INDUSTRIES, 5, this, CS_NUMERAL, QSF_NONE);
|
||||
}
|
||||
_settings_newgame.difficulty.industry_density = index;
|
||||
break;
|
||||
|
||||
case WID_GL_TERRAIN_PULLDOWN: {
|
||||
if ((uint)index == CUSTOM_TERRAIN_TYPE_NUMBER_DIFFICULTY) {
|
||||
this->widget_id = widget;
|
||||
@@ -959,6 +984,7 @@ struct GenerateLandscapeWindow : public Window {
|
||||
case WID_GL_SNOW_COVERAGE_TEXT: value = DEF_SNOW_COVERAGE; break;
|
||||
case WID_GL_DESERT_COVERAGE_TEXT: value = DEF_DESERT_COVERAGE; break;
|
||||
case WID_GL_TOWN_PULLDOWN: value = 1; break;
|
||||
case WID_GL_INDUSTRY_PULLDOWN: value = 1; break;
|
||||
case WID_GL_TERRAIN_PULLDOWN: value = MIN_MAP_HEIGHT_LIMIT; break;
|
||||
case WID_GL_WATER_PULLDOWN: value = CUSTOM_SEA_LEVEL_MIN_PERCENTAGE; break;
|
||||
default: NOT_REACHED();
|
||||
@@ -990,6 +1016,10 @@ struct GenerateLandscapeWindow : public Window {
|
||||
_settings_newgame.game_creation.custom_town_number = Clamp(value, 1, CUSTOM_TOWN_MAX_NUMBER);
|
||||
break;
|
||||
|
||||
case WID_GL_INDUSTRY_PULLDOWN:
|
||||
_settings_newgame.game_creation.custom_industry_number = Clamp(value, 1, IndustryPool::MAX_SIZE);
|
||||
break;
|
||||
|
||||
case WID_GL_TERRAIN_PULLDOWN:
|
||||
_settings_newgame.game_creation.custom_terrain_type = Clamp(value, MIN_CUSTOM_TERRAIN_TYPE, GetMapHeightLimit());
|
||||
break;
|
||||
@@ -1410,7 +1440,7 @@ struct GenerateProgressWindow : public Window {
|
||||
|
||||
case WID_GP_PROGRESS_TEXT:
|
||||
for (uint i = 0; i < GWP_CLASS_COUNT; i++) {
|
||||
size->width = std::max(size->width, GetStringBoundingBox(_generation_class_table[i]).width);
|
||||
size->width = std::max(size->width, GetStringBoundingBox(_generation_class_table[i]).width + padding.width);
|
||||
}
|
||||
size->height = FONT_HEIGHT_NORMAL * 2 + WidgetDimensions::scaled.vsep_normal;
|
||||
break;
|
||||
|
75
src/gfx.cpp
75
src/gfx.cpp
@@ -922,6 +922,21 @@ Dimension GetStringBoundingBox(StringID strid, FontSize start_fontsize)
|
||||
return GetStringBoundingBox(buffer, start_fontsize);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get maximum width of a list of strings.
|
||||
* @param list List of strings, terminated with INVALID_STRING_ID.
|
||||
* @param fontsize Font size to use.
|
||||
* @return Width of longest string within the list.
|
||||
*/
|
||||
uint GetStringListWidth(const StringID *list, FontSize fontsize)
|
||||
{
|
||||
uint width = 0;
|
||||
for (const StringID *str = list; *str != INVALID_STRING_ID; str++) {
|
||||
width = std::max(width, GetStringBoundingBox(*str, fontsize).width);
|
||||
}
|
||||
return width;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the leading corner of a character in a single-line string relative
|
||||
* to the start of the string.
|
||||
@@ -1927,77 +1942,39 @@ void SetAnimatedMouseCursor(const AnimCursor *table)
|
||||
}
|
||||
|
||||
/**
|
||||
* Update cursor position on mouse movement for relative modes.
|
||||
* Update cursor position based on a relative change.
|
||||
*
|
||||
* @param delta_x How much change in the X position.
|
||||
* @param delta_y How much change in the Y position.
|
||||
*/
|
||||
void CursorVars::UpdateCursorPositionRelative(int delta_x, int delta_y)
|
||||
{
|
||||
if (this->fix_at) {
|
||||
this->delta.x = delta_x;
|
||||
this->delta.y = delta_y;
|
||||
} else {
|
||||
int last_position_x = this->pos.x;
|
||||
int last_position_y = this->pos.y;
|
||||
assert(this->fix_at);
|
||||
|
||||
this->pos.x = Clamp(this->pos.x + delta_x, 0, _cur_resolution.width - 1);
|
||||
this->pos.y = Clamp(this->pos.y + delta_y, 0, _cur_resolution.height - 1);
|
||||
|
||||
this->delta.x = last_position_x - this->pos.x;
|
||||
this->delta.y = last_position_y - this->pos.y;
|
||||
|
||||
this->dirty = true;
|
||||
}
|
||||
this->delta.x = delta_x;
|
||||
this->delta.y = delta_y;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update cursor position on mouse movement.
|
||||
* @param x New X position.
|
||||
* @param y New Y position.
|
||||
* @param queued_warp True, if the OS queues mouse warps after pending mouse movement events.
|
||||
* False, if the warp applies instantaneous.
|
||||
* @return true, if the OS cursor position should be warped back to this->pos.
|
||||
*/
|
||||
bool CursorVars::UpdateCursorPosition(int x, int y, bool queued_warp)
|
||||
bool CursorVars::UpdateCursorPosition(int x, int y)
|
||||
{
|
||||
/* Detecting relative mouse movement is somewhat tricky.
|
||||
* - There may be multiple mouse move events in the video driver queue (esp. when OpenTTD lags a bit).
|
||||
* - When we request warping the mouse position (return true), a mouse move event is appended at the end of the queue.
|
||||
*
|
||||
* So, when this->fix_at is active, we use the following strategy:
|
||||
* - The first movement triggers the warp to reset the mouse position.
|
||||
* - Subsequent events have to compute movement relative to the previous event.
|
||||
* - The relative movement is finished, when we receive the event matching the warp.
|
||||
*/
|
||||
this->delta.x = x - this->pos.x;
|
||||
this->delta.y = y - this->pos.y;
|
||||
|
||||
if (x == this->pos.x && y == this->pos.y) {
|
||||
/* Warp finished. */
|
||||
this->queued_warp = false;
|
||||
}
|
||||
|
||||
this->delta.x = x - (this->queued_warp ? this->last_position.x : this->pos.x);
|
||||
this->delta.y = y - (this->queued_warp ? this->last_position.y : this->pos.y);
|
||||
|
||||
this->last_position.x = x;
|
||||
this->last_position.y = y;
|
||||
|
||||
bool need_warp = false;
|
||||
if (this->fix_at) {
|
||||
if (this->delta.x != 0 || this->delta.y != 0) {
|
||||
/* Trigger warp.
|
||||
* Note: We also trigger warping again, if there is already a pending warp.
|
||||
* This makes it more tolerant about the OS or other software in between
|
||||
* botchering the warp. */
|
||||
this->queued_warp = queued_warp;
|
||||
need_warp = true;
|
||||
}
|
||||
return this->delta.x != 0 || this->delta.y != 0;
|
||||
} else if (this->pos.x != x || this->pos.y != y) {
|
||||
this->queued_warp = false; // Cancel warping, we are no longer confining the position.
|
||||
this->dirty = true;
|
||||
this->pos.x = x;
|
||||
this->pos.y = y;
|
||||
}
|
||||
return need_warp;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ChangeResInGame(int width, int height)
|
||||
|
@@ -148,6 +148,7 @@ static inline void GfxFillRect(const Rect &r, int colour, FillRectMode mode = FI
|
||||
Dimension GetStringBoundingBox(const char *str, FontSize start_fontsize = FS_NORMAL);
|
||||
Dimension GetStringBoundingBox(const std::string &str, FontSize start_fontsize = FS_NORMAL);
|
||||
Dimension GetStringBoundingBox(StringID strid, FontSize start_fontsize = FS_NORMAL);
|
||||
uint GetStringListWidth(const StringID *list, FontSize fontsize = FS_NORMAL);
|
||||
int GetStringHeight(const char *str, int maxw, FontSize fontsize = FS_NORMAL);
|
||||
int GetStringHeight(StringID str, int maxw);
|
||||
int GetStringLineCount(StringID str, int maxw);
|
||||
|
@@ -11,6 +11,7 @@
|
||||
#include "gfx_layout.h"
|
||||
#include "string_func.h"
|
||||
#include "strings_func.h"
|
||||
#include "zoom_func.h"
|
||||
#include "debug.h"
|
||||
|
||||
#include "table/control_codes.h"
|
||||
@@ -345,11 +346,11 @@ FallbackParagraphLayout::FallbackVisualRun::FallbackVisualRun(Font *font, const
|
||||
for (int i = 0; i < this->glyph_count; i++) {
|
||||
this->glyphs[i] = font->fc->MapCharToGlyph(chars[i]);
|
||||
if (isbuiltin) {
|
||||
this->positions[2 * i + 1] = font->fc->GetAscender(); // Apply sprite font's ascender.
|
||||
this->positions[2 * i + 1] = font->fc->GetAscender(); // Apply sprite font's ascender.
|
||||
} else if (chars[i] >= SCC_SPRITE_START && chars[i] <= SCC_SPRITE_END) {
|
||||
this->positions[2 * i + 1] = font->fc->GetAscender() - font->fc->GetGlyph(this->glyphs[i])->height - 1; // Align sprite glyphs to font baseline.
|
||||
this->positions[2 * i + 1] = (font->fc->GetHeight() - ScaleSpriteTrad(FontCache::GetDefaultFontHeight(font->fc->GetSize()))) / 2; // Align sprite font to centre
|
||||
} else {
|
||||
this->positions[2 * i + 1] = 0; // No ascender adjustment.
|
||||
this->positions[2 * i + 1] = 0; // No ascender adjustment.
|
||||
}
|
||||
this->positions[2 * i + 2] = this->positions[2 * i] + font->fc->GetGlyphWidth(this->glyphs[i]);
|
||||
this->glyph_to_char[i] = i;
|
||||
@@ -551,8 +552,6 @@ std::unique_ptr<const ParagraphLayouter::Line> FallbackParagraphLayout::NextLine
|
||||
|
||||
next_run = this->buffer_begin + iter->first;
|
||||
begin = this->buffer;
|
||||
|
||||
last_space = nullptr;
|
||||
}
|
||||
|
||||
if (IsWhitespace(c)) last_space = this->buffer;
|
||||
@@ -590,7 +589,7 @@ std::unique_ptr<const ParagraphLayouter::Line> FallbackParagraphLayout::NextLine
|
||||
this->buffer++;
|
||||
}
|
||||
|
||||
if (l->size() == 0 || last_char - begin != 0) {
|
||||
if (l->size() == 0 || last_char - begin > 0) {
|
||||
int w = l->GetWidth();
|
||||
l->emplace_back(iter->second, begin, last_char - begin, w);
|
||||
}
|
||||
|
@@ -47,7 +47,8 @@ struct FontState {
|
||||
*/
|
||||
inline void SetColour(TextColour c)
|
||||
{
|
||||
assert(c >= TC_BLUE && c <= TC_BLACK);
|
||||
assert((c & TC_COLOUR_MASK) >= TC_BLUE && (c & TC_COLOUR_MASK) <= TC_BLACK);
|
||||
assert((c & (TC_COLOUR_MASK | TC_FLAGS_MASK)) == c);
|
||||
if ((this->cur_colour & TC_FORCED) == 0) this->cur_colour = c;
|
||||
}
|
||||
|
||||
|
@@ -144,11 +144,7 @@ struct CursorVars {
|
||||
bool vehchain; ///< vehicle chain is dragged
|
||||
|
||||
void UpdateCursorPositionRelative(int delta_x, int delta_y);
|
||||
bool UpdateCursorPosition(int x, int y, bool queued_warp);
|
||||
|
||||
private:
|
||||
bool queued_warp;
|
||||
Point last_position;
|
||||
bool UpdateCursorPosition(int x, int y);
|
||||
};
|
||||
|
||||
/** Data about how and where to blit pixels. */
|
||||
@@ -214,6 +210,13 @@ enum FontSize {
|
||||
};
|
||||
DECLARE_POSTFIX_INCREMENT(FontSize)
|
||||
|
||||
static inline const char *FontSizeToName(FontSize fs)
|
||||
{
|
||||
static const char *SIZE_TO_NAME[] = { "medium", "small", "large", "mono" };
|
||||
assert(fs < FS_END);
|
||||
return SIZE_TO_NAME[fs];
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to only draw a part of the sprite.
|
||||
* Draw the subsprite in the rect (sprite_x_offset + left, sprite_y_offset + top) to (sprite_x_offset + right, sprite_y_offset + bottom).
|
||||
@@ -273,6 +276,9 @@ enum TextColour {
|
||||
TC_IS_PALETTE_COLOUR = 0x100, ///< Colour value is already a real palette colour index, not an index of a StringColour.
|
||||
TC_NO_SHADE = 0x200, ///< Do not add shading to this text colour.
|
||||
TC_FORCED = 0x400, ///< Ignore colour changes from strings.
|
||||
|
||||
TC_COLOUR_MASK = 0xFF, ///< Mask to test if TextColour (without flags) is within limits.
|
||||
TC_FLAGS_MASK = 0x700, ///< Mask to test if TextColour (with flags) is within limits.
|
||||
};
|
||||
DECLARE_ENUM_AS_BIT_SET(TextColour)
|
||||
|
||||
|
20
src/group.h
20
src/group.h
@@ -23,15 +23,14 @@ extern GroupPool _group_pool; ///< Pool of groups.
|
||||
|
||||
/** Statistics and caches on the vehicles in a group. */
|
||||
struct GroupStatistics {
|
||||
uint16 num_vehicle; ///< Number of vehicles.
|
||||
Money profit_last_year; ///< Sum of profits for all vehicles.
|
||||
Money profit_last_year_min_age; ///< Sum of profits for vehicles considered for profit statistics.
|
||||
uint16 *num_engines; ///< Caches the number of engines of each type the company owns.
|
||||
|
||||
uint16 num_vehicle; ///< Number of vehicles.
|
||||
uint16 num_vehicle_min_age; ///< Number of vehicles considered for profit statistics;
|
||||
bool autoreplace_defined; ///< Are any autoreplace rules set?
|
||||
bool autoreplace_finished; ///< Have all autoreplacement finished?
|
||||
|
||||
uint16 num_profit_vehicle; ///< Number of vehicles considered for profit statistics;
|
||||
Money profit_last_year; ///< Sum of profits for all vehicles.
|
||||
|
||||
GroupStatistics();
|
||||
~GroupStatistics();
|
||||
|
||||
@@ -39,8 +38,10 @@ struct GroupStatistics {
|
||||
|
||||
void ClearProfits()
|
||||
{
|
||||
this->num_profit_vehicle = 0;
|
||||
this->profit_last_year = 0;
|
||||
|
||||
this->num_vehicle_min_age = 0;
|
||||
this->profit_last_year_min_age = 0;
|
||||
}
|
||||
|
||||
void ClearAutoreplace()
|
||||
@@ -55,7 +56,8 @@ struct GroupStatistics {
|
||||
|
||||
static void CountVehicle(const Vehicle *v, int delta);
|
||||
static void CountEngine(const Vehicle *v, int delta);
|
||||
static void VehicleReachedProfitAge(const Vehicle *v);
|
||||
static void AddProfitLastYear(const Vehicle *v);
|
||||
static void VehicleReachedMinAge(const Vehicle *v);
|
||||
|
||||
static void UpdateProfits();
|
||||
static void UpdateAfterLoad();
|
||||
@@ -104,8 +106,8 @@ static inline bool IsAllGroupID(GroupID id_g)
|
||||
|
||||
uint GetGroupNumEngines(CompanyID company, GroupID id_g, EngineID id_e);
|
||||
uint GetGroupNumVehicle(CompanyID company, GroupID id_g, VehicleType type);
|
||||
uint GetGroupNumProfitVehicle(CompanyID company, GroupID id_g, VehicleType type);
|
||||
Money GetGroupProfitLastYear(CompanyID company, GroupID id_g, VehicleType type);
|
||||
uint GetGroupNumVehicleMinAge(CompanyID company, GroupID id_g, VehicleType type);
|
||||
Money GetGroupProfitLastYearMinAge(CompanyID company, GroupID id_g, VehicleType type);
|
||||
|
||||
void SetTrainGroupID(Train *v, GroupID grp);
|
||||
void UpdateTrainGroupID(Train *v);
|
||||
|
@@ -43,8 +43,9 @@ GroupStatistics::~GroupStatistics()
|
||||
void GroupStatistics::Clear()
|
||||
{
|
||||
this->num_vehicle = 0;
|
||||
this->num_profit_vehicle = 0;
|
||||
this->profit_last_year = 0;
|
||||
this->num_vehicle_min_age = 0;
|
||||
this->profit_last_year_min_age = 0;
|
||||
|
||||
/* This is also called when NewGRF change. So the number of engines might have changed. Reallocate. */
|
||||
free(this->num_engines);
|
||||
@@ -136,13 +137,15 @@ void GroupStatistics::Clear()
|
||||
GroupStatistics &stats = GroupStatistics::Get(v);
|
||||
|
||||
stats_all.num_vehicle += delta;
|
||||
stats_all.profit_last_year += v->GetDisplayProfitLastYear() * delta;
|
||||
stats.num_vehicle += delta;
|
||||
stats.profit_last_year += v->GetDisplayProfitLastYear() * delta;
|
||||
|
||||
if (v->age > VEHICLE_PROFIT_MIN_AGE) {
|
||||
stats_all.num_profit_vehicle += delta;
|
||||
stats_all.profit_last_year += v->GetDisplayProfitLastYear() * delta;
|
||||
stats.num_profit_vehicle += delta;
|
||||
stats.profit_last_year += v->GetDisplayProfitLastYear() * delta;
|
||||
stats_all.num_vehicle_min_age += delta;
|
||||
stats_all.profit_last_year_min_age += v->GetDisplayProfitLastYear() * delta;
|
||||
stats.num_vehicle_min_age += delta;
|
||||
stats.profit_last_year_min_age += v->GetDisplayProfitLastYear() * delta;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,19 +162,31 @@ void GroupStatistics::Clear()
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a vehicle to the profit sum of its group.
|
||||
* Add a vehicle's last year profit to the profit sum of its group.
|
||||
*/
|
||||
/* static */ void GroupStatistics::VehicleReachedProfitAge(const Vehicle *v)
|
||||
/* static */ void GroupStatistics::AddProfitLastYear(const Vehicle *v)
|
||||
{
|
||||
GroupStatistics &stats_all = GroupStatistics::GetAllGroup(v);
|
||||
GroupStatistics &stats = GroupStatistics::Get(v);
|
||||
|
||||
stats_all.num_profit_vehicle++;
|
||||
stats_all.profit_last_year += v->GetDisplayProfitLastYear();
|
||||
stats.num_profit_vehicle++;
|
||||
stats.profit_last_year += v->GetDisplayProfitLastYear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a vehicle to the profit sum of its group.
|
||||
*/
|
||||
/* static */ void GroupStatistics::VehicleReachedMinAge(const Vehicle *v)
|
||||
{
|
||||
GroupStatistics &stats_all = GroupStatistics::GetAllGroup(v);
|
||||
GroupStatistics &stats = GroupStatistics::Get(v);
|
||||
|
||||
stats_all.num_vehicle_min_age++;
|
||||
stats_all.profit_last_year_min_age += v->GetDisplayProfitLastYear();
|
||||
stats.num_vehicle_min_age++;
|
||||
stats.profit_last_year_min_age += v->GetDisplayProfitLastYear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Recompute the profits for all groups.
|
||||
*/
|
||||
@@ -191,7 +206,10 @@ void GroupStatistics::Clear()
|
||||
}
|
||||
|
||||
for (const Vehicle *v : Vehicle::Iterate()) {
|
||||
if (v->IsPrimaryVehicle() && v->age > VEHICLE_PROFIT_MIN_AGE) GroupStatistics::VehicleReachedProfitAge(v);
|
||||
if (v->IsPrimaryVehicle()) {
|
||||
GroupStatistics::AddProfitLastYear(v);
|
||||
if (v->age > VEHICLE_PROFIT_MIN_AGE) GroupStatistics::VehicleReachedMinAge(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -789,30 +807,30 @@ uint GetGroupNumVehicle(CompanyID company, GroupID id_g, VehicleType type)
|
||||
* @param type The vehicle type of the group
|
||||
* @return The number of vehicles above profit minimum age in the group
|
||||
*/
|
||||
uint GetGroupNumProfitVehicle(CompanyID company, GroupID id_g, VehicleType type)
|
||||
uint GetGroupNumVehicleMinAge(CompanyID company, GroupID id_g, VehicleType type)
|
||||
{
|
||||
uint count = 0;
|
||||
for (const Group *g : Group::Iterate()) {
|
||||
if (g->parent == id_g) count += GetGroupNumProfitVehicle(company, g->index, type);
|
||||
if (g->parent == id_g) count += GetGroupNumVehicleMinAge(company, g->index, type);
|
||||
}
|
||||
return count + GroupStatistics::Get(company, id_g, type).num_profit_vehicle;
|
||||
return count + GroupStatistics::Get(company, id_g, type).num_vehicle_min_age;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get last year's profit for the group with GroupID
|
||||
* id_g and its sub-groups.
|
||||
* Get last year's profit of vehicles above minimum age
|
||||
* for the group with GroupID id_g and its sub-groups.
|
||||
* @param company The company the group belongs to
|
||||
* @param id_g The GroupID of the group used
|
||||
* @param type The vehicle type of the group
|
||||
* @return Last year's profit for the group
|
||||
* @return Last year's profit of vehicles above minimum age for the group
|
||||
*/
|
||||
Money GetGroupProfitLastYear(CompanyID company, GroupID id_g, VehicleType type)
|
||||
Money GetGroupProfitLastYearMinAge(CompanyID company, GroupID id_g, VehicleType type)
|
||||
{
|
||||
Money sum = 0;
|
||||
for (const Group *g : Group::Iterate()) {
|
||||
if (g->parent == id_g) sum += GetGroupProfitLastYear(company, g->index, type);
|
||||
if (g->parent == id_g) sum += GetGroupProfitLastYearMinAge(company, g->index, type);
|
||||
}
|
||||
return sum + GroupStatistics::Get(company, id_g, type).profit_last_year;
|
||||
return sum + GroupStatistics::Get(company, id_g, type).profit_last_year_min_age;
|
||||
}
|
||||
|
||||
void RemoveAllGroupsForCompany(const CompanyID company)
|
||||
|
@@ -27,6 +27,7 @@
|
||||
#include "gui.h"
|
||||
#include "group_cmd.h"
|
||||
#include "vehicle_cmd.h"
|
||||
#include "gfx_func.h"
|
||||
|
||||
#include "widgets/group_widget.h"
|
||||
|
||||
@@ -72,15 +73,21 @@ static const NWidgetPart _nested_group_widgets[] = {
|
||||
/* right part */
|
||||
NWidget(NWID_VERTICAL),
|
||||
NWidget(NWID_HORIZONTAL),
|
||||
NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_GL_GROUP_BY_ORDER), SetMinimalSize(81, 12), SetDataTip(STR_STATION_VIEW_GROUP, STR_TOOLTIP_GROUP_ORDER),
|
||||
NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_GL_GROUP_BY_DROPDOWN), SetMinimalSize(167, 12), SetDataTip(0x0, STR_TOOLTIP_GROUP_ORDER),
|
||||
NWidget(WWT_PANEL, COLOUR_GREY), SetMinimalSize(12, 12), SetResize(1, 0), EndContainer(),
|
||||
EndContainer(),
|
||||
NWidget(NWID_HORIZONTAL),
|
||||
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_GL_SORT_BY_ORDER), SetMinimalSize(81, 12), SetDataTip(STR_BUTTON_SORT_BY, STR_TOOLTIP_SORT_ORDER),
|
||||
NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_GL_SORT_BY_DROPDOWN), SetMinimalSize(167, 12), SetDataTip(0x0, STR_TOOLTIP_SORT_CRITERIA),
|
||||
NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_GL_FILTER_BY_CARGO), SetMinimalSize(167, 12), SetDataTip(STR_JUST_STRING, STR_TOOLTIP_FILTER_CRITERIA),
|
||||
NWidget(WWT_PANEL, COLOUR_GREY), SetMinimalSize(12, 12), SetResize(1, 0), EndContainer(),
|
||||
NWidget(NWID_VERTICAL),
|
||||
NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_GL_GROUP_BY_ORDER), SetFill(1, 0), SetMinimalSize(0, 12), SetDataTip(STR_STATION_VIEW_GROUP, STR_TOOLTIP_GROUP_ORDER),
|
||||
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_GL_SORT_BY_ORDER), SetFill(1, 0), SetMinimalSize(0, 12), SetDataTip(STR_BUTTON_SORT_BY, STR_TOOLTIP_SORT_ORDER),
|
||||
EndContainer(),
|
||||
NWidget(NWID_VERTICAL),
|
||||
NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_GL_GROUP_BY_DROPDOWN), SetFill(1, 0), SetMinimalSize(0, 12), SetDataTip(0x0, STR_TOOLTIP_GROUP_ORDER),
|
||||
NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_GL_SORT_BY_DROPDOWN), SetFill(1, 0), SetMinimalSize(0, 12), SetDataTip(0x0, STR_TOOLTIP_SORT_CRITERIA),
|
||||
EndContainer(),
|
||||
NWidget(NWID_VERTICAL),
|
||||
NWidget(WWT_PANEL, COLOUR_GREY), SetMinimalSize(0, 12), SetResize(1, 0), EndContainer(),
|
||||
NWidget(NWID_HORIZONTAL),
|
||||
NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_GL_FILTER_BY_CARGO), SetMinimalSize(0, 12), SetDataTip(STR_JUST_STRING, STR_TOOLTIP_FILTER_CRITERIA),
|
||||
NWidget(WWT_PANEL, COLOUR_GREY), SetMinimalSize(0, 12), SetResize(1, 0), EndContainer(),
|
||||
EndContainer(),
|
||||
EndContainer(),
|
||||
EndContainer(),
|
||||
NWidget(NWID_HORIZONTAL),
|
||||
NWidget(WWT_MATRIX, COLOUR_GREY, WID_GL_LIST_VEHICLE), SetMinimalSize(248, 0), SetMatrixDataTip(1, 0, STR_NULL), SetResize(1, 1), SetFill(1, 0), SetScrollbar(WID_GL_LIST_VEHICLE_SCROLLBAR),
|
||||
@@ -295,13 +302,13 @@ private:
|
||||
/* draw the profit icon */
|
||||
x = rtl ? x - WidgetDimensions::scaled.hsep_normal - this->column_size[VGC_PROFIT].width : x + WidgetDimensions::scaled.hsep_normal + this->column_size[VGC_AUTOREPLACE].width;
|
||||
SpriteID spr;
|
||||
uint num_profit_vehicle = GetGroupNumProfitVehicle(this->vli.company, g_id, this->vli.vtype);
|
||||
Money profit_last_year = GetGroupProfitLastYear(this->vli.company, g_id, this->vli.vtype);
|
||||
if (num_profit_vehicle == 0) {
|
||||
uint num_vehicle_min_age = GetGroupNumVehicleMinAge(this->vli.company, g_id, this->vli.vtype);
|
||||
Money profit_last_year_min_age = GetGroupProfitLastYearMinAge(this->vli.company, g_id, this->vli.vtype);
|
||||
if (num_vehicle_min_age == 0) {
|
||||
spr = SPR_PROFIT_NA;
|
||||
} else if (profit_last_year < 0) {
|
||||
} else if (profit_last_year_min_age < 0) {
|
||||
spr = SPR_PROFIT_NEGATIVE;
|
||||
} else if (profit_last_year < VEHICLE_PROFIT_THRESHOLD * num_profit_vehicle) {
|
||||
} else if (profit_last_year_min_age < VEHICLE_PROFIT_THRESHOLD * num_vehicle_min_age) {
|
||||
spr = SPR_PROFIT_SOME;
|
||||
} else {
|
||||
spr = SPR_PROFIT_LOT;
|
||||
@@ -406,6 +413,20 @@ public:
|
||||
size->height = 4 * resize->height;
|
||||
break;
|
||||
|
||||
case WID_GL_GROUP_BY_DROPDOWN:
|
||||
size->width = GetStringListWidth(this->vehicle_group_by_names) + padding.width;
|
||||
break;
|
||||
|
||||
case WID_GL_SORT_BY_DROPDOWN:
|
||||
size->width = GetStringListWidth(this->vehicle_group_none_sorter_names);
|
||||
size->width = std::max(size->width, GetStringListWidth(this->vehicle_group_shared_orders_sorter_names));
|
||||
size->width += padding.width;
|
||||
break;
|
||||
|
||||
case WID_GL_FILTER_BY_CARGO:
|
||||
size->width = GetStringListWidth(this->cargo_filter_texts) + padding.width;
|
||||
break;
|
||||
|
||||
case WID_GL_MANAGE_VEHICLES_DROPDOWN: {
|
||||
Dimension d = this->GetActionDropdownSize(true, true);
|
||||
d.height += padding.height;
|
||||
@@ -440,7 +461,7 @@ public:
|
||||
|
||||
if (!(IsAllGroupID(this->vli.index) || IsDefaultGroupID(this->vli.index) || Group::IsValidID(this->vli.index))) {
|
||||
this->vli.index = ALL_GROUP;
|
||||
HideDropDownMenu(this);
|
||||
this->CloseChildWindows(WC_DROPDOWN_MENU);
|
||||
}
|
||||
this->SetDirty();
|
||||
}
|
||||
@@ -491,7 +512,7 @@ public:
|
||||
/* The drop down menu is out, *but* it may not be used, retract it. */
|
||||
if (this->vehicles.size() == 0 && this->IsWidgetLowered(WID_GL_MANAGE_VEHICLES_DROPDOWN)) {
|
||||
this->RaiseWidget(WID_GL_MANAGE_VEHICLES_DROPDOWN);
|
||||
HideDropDownMenu(this);
|
||||
this->CloseChildWindows(WC_DROPDOWN_MENU);
|
||||
}
|
||||
|
||||
/* Disable all lists management button when the list is empty */
|
||||
@@ -860,7 +881,7 @@ public:
|
||||
uint id_g = this->group_sb->GetScrolledRowFromWidget(pt.y, this, WID_GL_LIST_GROUP);
|
||||
GroupID new_g = id_g >= this->groups.size() ? NEW_GROUP : this->groups[id_g]->index;
|
||||
|
||||
Command<CMD_ADD_VEHICLE_GROUP>::Post(STR_ERROR_GROUP_CAN_T_ADD_VEHICLE, new_g == NEW_GROUP ? CcAddVehicleNewGroup : nullptr, 0, new_g, vindex, _ctrl_pressed || this->grouping == GB_SHARED_ORDERS);
|
||||
Command<CMD_ADD_VEHICLE_GROUP>::Post(STR_ERROR_GROUP_CAN_T_ADD_VEHICLE, new_g == NEW_GROUP ? CcAddVehicleNewGroup : nullptr, new_g, vindex, _ctrl_pressed || this->grouping == GB_SHARED_ORDERS);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@@ -126,7 +126,7 @@ struct EndGameWindow : EndGameHighScoreBaseWindow {
|
||||
void Close() override
|
||||
{
|
||||
if (!_networking) Command<CMD_PAUSE>::Post(PM_PAUSED_NORMAL, false); // unpause
|
||||
ShowHighscoreTable(this->window_number, this->rank);
|
||||
if (_game_mode != GM_MENU) ShowHighscoreTable(this->window_number, this->rank);
|
||||
this->EndGameHighScoreBaseWindow::Close();
|
||||
}
|
||||
|
||||
|
@@ -2040,12 +2040,14 @@ CommandCost CmdBuildIndustry(DoCommandFlag flags, TileIndex tile, IndustryType i
|
||||
Industry *ind = nullptr;
|
||||
if (deity_prospect || (_game_mode != GM_EDITOR && _current_company != OWNER_DEITY && _settings_game.construction.raw_industry_construction == 2 && indspec->IsRawIndustry())) {
|
||||
if (flags & DC_EXEC) {
|
||||
/* Prospected industries are build as OWNER_TOWN to not e.g. be build on owned land of the founder */
|
||||
Backup<CompanyID> cur_company(_current_company, OWNER_TOWN, FILE_LINE);
|
||||
/* Prospecting has a chance to fail, however we cannot guarantee that something can
|
||||
* be built on the map, so the chance gets lower when the map is fuller, but there
|
||||
* is nothing we can really do about that. */
|
||||
if (deity_prospect || Random() <= indspec->prospecting_chance) {
|
||||
bool prospect_success = deity_prospect || Random() <= indspec->prospecting_chance;
|
||||
if (prospect_success) {
|
||||
/* Prospected industries are build as OWNER_TOWN to not e.g. be build on owned land of the founder */
|
||||
IndustryAvailabilityCallType calltype = _current_company == OWNER_DEITY ? IACT_RANDOMCREATION : IACT_PROSPECTCREATION;
|
||||
Backup<CompanyID> cur_company(_current_company, OWNER_TOWN, FILE_LINE);
|
||||
for (int i = 0; i < 5000; i++) {
|
||||
/* We should not have more than one Random() in a function call
|
||||
* because parameter evaluation order is not guaranteed in the c++ standard
|
||||
@@ -2056,13 +2058,20 @@ CommandCost CmdBuildIndustry(DoCommandFlag flags, TileIndex tile, IndustryType i
|
||||
/* Check now each layout, starting with the random one */
|
||||
for (size_t j = 0; j < num_layouts; j++) {
|
||||
layout = (layout + 1) % num_layouts;
|
||||
ret = CreateNewIndustryHelper(tile, it, flags, indspec, layout, random_var8f, random_initial_bits, cur_company.GetOriginalValue(), _current_company == OWNER_DEITY ? IACT_RANDOMCREATION : IACT_PROSPECTCREATION, &ind);
|
||||
ret = CreateNewIndustryHelper(tile, it, flags, indspec, layout, random_var8f, random_initial_bits, cur_company.GetOriginalValue(), calltype, &ind);
|
||||
if (ret.Succeeded()) break;
|
||||
}
|
||||
if (ret.Succeeded()) break;
|
||||
}
|
||||
cur_company.Restore();
|
||||
}
|
||||
if (ret.Failed() && IsLocalCompany()) {
|
||||
if (prospect_success) {
|
||||
ShowErrorMessage(STR_ERROR_CAN_T_PROSPECT_INDUSTRY, STR_ERROR_NO_SUITABLE_PLACES_FOR_PROSPECTING, WL_INFO);
|
||||
} else {
|
||||
ShowErrorMessage(STR_ERROR_CAN_T_PROSPECT_INDUSTRY, STR_ERROR_PROSPECTING_WAS_UNLUCKY, WL_INFO);
|
||||
}
|
||||
}
|
||||
cur_company.Restore();
|
||||
}
|
||||
} else {
|
||||
size_t layout = first_layout;
|
||||
@@ -2228,10 +2237,14 @@ static uint GetNumberOfIndustries()
|
||||
25, // low
|
||||
55, // normal
|
||||
80, // high
|
||||
0, // custom
|
||||
};
|
||||
|
||||
assert(lengthof(numof_industry_table) == ID_END);
|
||||
uint difficulty = (_game_mode != GM_EDITOR) ? _settings_game.difficulty.industry_density : (uint)ID_VERY_LOW;
|
||||
|
||||
if (difficulty == ID_CUSTOM) return std::min<uint>(IndustryPool::MAX_SIZE, _settings_game.game_creation.custom_industry_number);
|
||||
|
||||
return std::min<uint>(IndustryPool::MAX_SIZE, ScaleByMapSize(numof_industry_table[difficulty]));
|
||||
}
|
||||
|
||||
|
@@ -380,7 +380,7 @@ static const NWidgetPart _nested_select_game_widgets[] = {
|
||||
NWidget(WWT_PANEL, COLOUR_BROWN),
|
||||
NWidget(NWID_SPACER), SetMinimalSize(0, 8),
|
||||
|
||||
/* 'generate game' and 'load game' buttons */
|
||||
/* 'New Game' and 'Load Game' buttons */
|
||||
NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
|
||||
NWidget(WWT_PUSHTXTBTN, COLOUR_ORANGE, WID_SGI_GENERATE_GAME), SetMinimalSize(158, 12),
|
||||
SetDataTip(STR_INTRO_NEW_GAME, STR_INTRO_TOOLTIP_NEW_GAME), SetPadding(0, 0, 0, 10), SetFill(1, 0),
|
||||
@@ -390,7 +390,7 @@ static const NWidgetPart _nested_select_game_widgets[] = {
|
||||
|
||||
NWidget(NWID_SPACER), SetMinimalSize(0, 6),
|
||||
|
||||
/* 'play scenario' and 'play heightmap' buttons */
|
||||
/* 'Play Scenario' and 'Play Heightmap' buttons */
|
||||
NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
|
||||
NWidget(WWT_PUSHTXTBTN, COLOUR_ORANGE, WID_SGI_PLAY_SCENARIO), SetMinimalSize(158, 12),
|
||||
SetDataTip(STR_INTRO_PLAY_SCENARIO, STR_INTRO_TOOLTIP_PLAY_SCENARIO), SetPadding(0, 0, 0, 10), SetFill(1, 0),
|
||||
@@ -400,7 +400,7 @@ static const NWidgetPart _nested_select_game_widgets[] = {
|
||||
|
||||
NWidget(NWID_SPACER), SetMinimalSize(0, 6),
|
||||
|
||||
/* 'edit scenario' and 'play multiplayer' buttons */
|
||||
/* 'Scenario Editor' and 'Multiplayer' buttons */
|
||||
NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
|
||||
NWidget(WWT_PUSHTXTBTN, COLOUR_ORANGE, WID_SGI_EDIT_SCENARIO), SetMinimalSize(158, 12),
|
||||
SetDataTip(STR_INTRO_SCENARIO_EDITOR, STR_INTRO_TOOLTIP_SCENARIO_EDITOR), SetPadding(0, 0, 0, 10), SetFill(1, 0),
|
||||
@@ -410,7 +410,7 @@ static const NWidgetPart _nested_select_game_widgets[] = {
|
||||
|
||||
NWidget(NWID_SPACER), SetMinimalSize(0, 7),
|
||||
|
||||
/* climate selection buttons */
|
||||
/* Climate selection buttons */
|
||||
NWidget(NWID_HORIZONTAL),
|
||||
NWidget(NWID_SPACER), SetMinimalSize(10, 0), SetFill(1, 0),
|
||||
NWidget(WWT_IMGBTN_2, COLOUR_ORANGE, WID_SGI_TEMPERATE_LANDSCAPE), SetMinimalSize(77, 55),
|
||||
@@ -439,7 +439,7 @@ static const NWidgetPart _nested_select_game_widgets[] = {
|
||||
EndContainer(),
|
||||
EndContainer(),
|
||||
|
||||
/* 'game options' and 'advanced settings' buttons */
|
||||
/* 'Game Options' and 'Settings' buttons */
|
||||
NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
|
||||
NWidget(WWT_PUSHTXTBTN, COLOUR_ORANGE, WID_SGI_OPTIONS), SetMinimalSize(158, 12),
|
||||
SetDataTip(STR_INTRO_GAME_OPTIONS, STR_INTRO_TOOLTIP_GAME_OPTIONS), SetPadding(0, 0, 0, 10), SetFill(1, 0),
|
||||
@@ -449,29 +449,35 @@ static const NWidgetPart _nested_select_game_widgets[] = {
|
||||
|
||||
NWidget(NWID_SPACER), SetMinimalSize(0, 6),
|
||||
|
||||
/* 'AO settings', 'Game Script settings', and 'newgrf settings' buttons */
|
||||
/* 'AI Settings' and 'Game Script Settings' buttons */
|
||||
NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
|
||||
NWidget(WWT_PUSHTXTBTN, COLOUR_ORANGE, WID_SGI_AI_SETTINGS), SetMinimalSize(105, 12),
|
||||
NWidget(WWT_PUSHTXTBTN, COLOUR_ORANGE, WID_SGI_AI_SETTINGS), SetMinimalSize(158, 12),
|
||||
SetDataTip(STR_INTRO_AI_SETTINGS, STR_INTRO_TOOLTIP_AI_SETTINGS), SetPadding(0, 0, 0, 10), SetFill(1, 0),
|
||||
NWidget(WWT_PUSHTXTBTN, COLOUR_ORANGE, WID_SGI_GS_SETTINGS), SetMinimalSize(106, 12),
|
||||
SetDataTip(STR_INTRO_GAMESCRIPT_SETTINGS, STR_INTRO_TOOLTIP_GAMESCRIPT_SETTINGS), SetPadding(0, 0, 0, 0), SetFill(1, 0),
|
||||
NWidget(WWT_PUSHTXTBTN, COLOUR_ORANGE, WID_SGI_GRF_SETTINGS), SetMinimalSize(105, 12),
|
||||
NWidget(WWT_PUSHTXTBTN, COLOUR_ORANGE, WID_SGI_GS_SETTINGS), SetMinimalSize(158, 12),
|
||||
SetDataTip(STR_INTRO_GAMESCRIPT_SETTINGS, STR_INTRO_TOOLTIP_GAMESCRIPT_SETTINGS), SetPadding(0, 10, 0, 0), SetFill(1, 0),
|
||||
EndContainer(),
|
||||
|
||||
NWidget(NWID_SPACER), SetMinimalSize(0, 6),
|
||||
|
||||
/* 'Check Online Content' and 'NewGRF Settings' buttons */
|
||||
NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
|
||||
NWidget(WWT_PUSHTXTBTN, COLOUR_ORANGE, WID_SGI_CONTENT_DOWNLOAD), SetMinimalSize(158, 12),
|
||||
SetDataTip(STR_INTRO_ONLINE_CONTENT, STR_INTRO_TOOLTIP_ONLINE_CONTENT), SetPadding(0, 0, 0, 10), SetFill(1, 0),
|
||||
NWidget(WWT_PUSHTXTBTN, COLOUR_ORANGE, WID_SGI_GRF_SETTINGS), SetMinimalSize(158, 12),
|
||||
SetDataTip(STR_INTRO_NEWGRF_SETTINGS, STR_INTRO_TOOLTIP_NEWGRF_SETTINGS), SetPadding(0, 10, 0, 0), SetFill(1, 0),
|
||||
EndContainer(),
|
||||
|
||||
NWidget(NWID_SPACER), SetMinimalSize(0, 6),
|
||||
|
||||
/* 'online content' and 'highscore' buttons */
|
||||
NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
|
||||
NWidget(WWT_PUSHTXTBTN, COLOUR_ORANGE, WID_SGI_CONTENT_DOWNLOAD), SetMinimalSize(158, 12),
|
||||
SetDataTip(STR_INTRO_ONLINE_CONTENT, STR_INTRO_TOOLTIP_ONLINE_CONTENT), SetPadding(0, 0, 0, 10), SetFill(1, 0),
|
||||
NWidget(WWT_PUSHTXTBTN, COLOUR_ORANGE, WID_SGI_HIGHSCORE), SetMinimalSize(158, 12),
|
||||
SetDataTip(STR_INTRO_HIGHSCORE, STR_INTRO_TOOLTIP_HIGHSCORE), SetPadding(0, 10, 0, 0), SetFill(1, 0),
|
||||
/* 'Highscore Table' button */
|
||||
NWidget(NWID_HORIZONTAL),
|
||||
NWidget(WWT_PUSHTXTBTN, COLOUR_ORANGE, WID_SGI_HIGHSCORE), SetMinimalSize(316, 12),
|
||||
SetDataTip(STR_INTRO_HIGHSCORE, STR_INTRO_TOOLTIP_HIGHSCORE), SetPadding(0, 10, 0, 10), SetFill(1, 0),
|
||||
EndContainer(),
|
||||
|
||||
NWidget(NWID_SPACER), SetMinimalSize(0, 6),
|
||||
|
||||
/* 'exit program' button */
|
||||
/* 'Exit' button */
|
||||
NWidget(NWID_HORIZONTAL),
|
||||
NWidget(NWID_SPACER), SetFill(1, 0),
|
||||
NWidget(WWT_PUSHTXTBTN, COLOUR_ORANGE, WID_SGI_EXIT), SetMinimalSize(128, 12),
|
||||
|
@@ -500,7 +500,7 @@ void DrawFoundation(TileInfo *ti, Foundation f)
|
||||
if (!IsNonContinuousFoundation(f)) {
|
||||
/* Lower part of foundation */
|
||||
AddSortableSpriteToDraw(
|
||||
leveled_base + (ti->tileh & ~SLOPE_STEEP), PAL_NONE, ti->x, ti->y, 16, 16, 7, ti->z
|
||||
leveled_base + (ti->tileh & ~SLOPE_STEEP), PAL_NONE, ti->x, ti->y, TILE_SIZE, TILE_SIZE, TILE_HEIGHT - 1, ti->z
|
||||
);
|
||||
}
|
||||
|
||||
@@ -512,38 +512,44 @@ void DrawFoundation(TileInfo *ti, Foundation f)
|
||||
byte inclined = highest_corner * 2 + (f == FOUNDATION_INCLINED_Y ? 1 : 0);
|
||||
|
||||
AddSortableSpriteToDraw(inclined_base + inclined, PAL_NONE, ti->x, ti->y,
|
||||
f == FOUNDATION_INCLINED_X ? 16 : 1,
|
||||
f == FOUNDATION_INCLINED_Y ? 16 : 1,
|
||||
f == FOUNDATION_INCLINED_X ? TILE_SIZE : 1,
|
||||
f == FOUNDATION_INCLINED_Y ? TILE_SIZE : 1,
|
||||
TILE_HEIGHT, ti->z
|
||||
);
|
||||
OffsetGroundSprite(31, 9);
|
||||
OffsetGroundSprite(0, 0);
|
||||
} else if (IsLeveledFoundation(f)) {
|
||||
AddSortableSpriteToDraw(leveled_base + SlopeWithOneCornerRaised(highest_corner), PAL_NONE, ti->x, ti->y, 16, 16, 7, ti->z - TILE_HEIGHT);
|
||||
OffsetGroundSprite(31, 1);
|
||||
AddSortableSpriteToDraw(leveled_base + SlopeWithOneCornerRaised(highest_corner), PAL_NONE, ti->x, ti->y, TILE_SIZE, TILE_SIZE, TILE_HEIGHT - 1, ti->z - TILE_HEIGHT);
|
||||
OffsetGroundSprite(0, -(int)TILE_HEIGHT);
|
||||
} else if (f == FOUNDATION_STEEP_LOWER) {
|
||||
/* one corner raised */
|
||||
OffsetGroundSprite(31, 1);
|
||||
OffsetGroundSprite(0, -(int)TILE_HEIGHT);
|
||||
} else {
|
||||
/* halftile foundation */
|
||||
int x_bb = (((highest_corner == CORNER_W) || (highest_corner == CORNER_S)) ? 8 : 0);
|
||||
int y_bb = (((highest_corner == CORNER_S) || (highest_corner == CORNER_E)) ? 8 : 0);
|
||||
int x_bb = (((highest_corner == CORNER_W) || (highest_corner == CORNER_S)) ? TILE_SIZE / 2 : 0);
|
||||
int y_bb = (((highest_corner == CORNER_S) || (highest_corner == CORNER_E)) ? TILE_SIZE / 2 : 0);
|
||||
|
||||
AddSortableSpriteToDraw(halftile_base + highest_corner, PAL_NONE, ti->x + x_bb, ti->y + y_bb, 8, 8, 7, ti->z + TILE_HEIGHT);
|
||||
OffsetGroundSprite(31, 9);
|
||||
AddSortableSpriteToDraw(halftile_base + highest_corner, PAL_NONE, ti->x + x_bb, ti->y + y_bb, TILE_SIZE / 2, TILE_SIZE / 2, TILE_HEIGHT - 1, ti->z + TILE_HEIGHT);
|
||||
/* Reposition ground sprite back to original position after bounding box change above. This is similar to
|
||||
* RemapCoords() but without zoom scaling. */
|
||||
Point pt = {(y_bb - x_bb) * 2, y_bb + x_bb};
|
||||
OffsetGroundSprite(-pt.x, -pt.y);
|
||||
}
|
||||
} else {
|
||||
if (IsLeveledFoundation(f)) {
|
||||
/* leveled foundation */
|
||||
AddSortableSpriteToDraw(leveled_base + ti->tileh, PAL_NONE, ti->x, ti->y, 16, 16, 7, ti->z);
|
||||
OffsetGroundSprite(31, 1);
|
||||
AddSortableSpriteToDraw(leveled_base + ti->tileh, PAL_NONE, ti->x, ti->y, TILE_SIZE, TILE_SIZE, TILE_HEIGHT - 1, ti->z);
|
||||
OffsetGroundSprite(0, -(int)TILE_HEIGHT);
|
||||
} else if (IsNonContinuousFoundation(f)) {
|
||||
/* halftile foundation */
|
||||
Corner halftile_corner = GetHalftileFoundationCorner(f);
|
||||
int x_bb = (((halftile_corner == CORNER_W) || (halftile_corner == CORNER_S)) ? 8 : 0);
|
||||
int y_bb = (((halftile_corner == CORNER_S) || (halftile_corner == CORNER_E)) ? 8 : 0);
|
||||
int x_bb = (((halftile_corner == CORNER_W) || (halftile_corner == CORNER_S)) ? TILE_SIZE / 2 : 0);
|
||||
int y_bb = (((halftile_corner == CORNER_S) || (halftile_corner == CORNER_E)) ? TILE_SIZE / 2 : 0);
|
||||
|
||||
AddSortableSpriteToDraw(halftile_base + halftile_corner, PAL_NONE, ti->x + x_bb, ti->y + y_bb, 8, 8, 7, ti->z);
|
||||
OffsetGroundSprite(31, 9);
|
||||
AddSortableSpriteToDraw(halftile_base + halftile_corner, PAL_NONE, ti->x + x_bb, ti->y + y_bb, TILE_SIZE / 2, TILE_SIZE / 2, TILE_HEIGHT - 1, ti->z);
|
||||
/* Reposition ground sprite back to original position after bounding box change above. This is similar to
|
||||
* RemapCoords() but without zoom scaling. */
|
||||
Point pt = {(y_bb - x_bb) * 2, y_bb + x_bb};
|
||||
OffsetGroundSprite(-pt.x, -pt.y);
|
||||
} else if (IsSpecialRailFoundation(f)) {
|
||||
/* anti-zig-zag foundation */
|
||||
SpriteID spr;
|
||||
@@ -554,18 +560,18 @@ void DrawFoundation(TileInfo *ti, Foundation f)
|
||||
/* tile-slope = sloped along X/Y, foundation-slope = three corners raised */
|
||||
spr = inclined_base + 2 * GetRailFoundationCorner(f) + ((ti->tileh == SLOPE_SW || ti->tileh == SLOPE_NE) ? 1 : 0);
|
||||
}
|
||||
AddSortableSpriteToDraw(spr, PAL_NONE, ti->x, ti->y, 16, 16, 7, ti->z);
|
||||
OffsetGroundSprite(31, 9);
|
||||
AddSortableSpriteToDraw(spr, PAL_NONE, ti->x, ti->y, TILE_SIZE, TILE_SIZE, TILE_HEIGHT - 1, ti->z);
|
||||
OffsetGroundSprite(0, 0);
|
||||
} else {
|
||||
/* inclined foundation */
|
||||
byte inclined = GetHighestSlopeCorner(ti->tileh) * 2 + (f == FOUNDATION_INCLINED_Y ? 1 : 0);
|
||||
|
||||
AddSortableSpriteToDraw(inclined_base + inclined, PAL_NONE, ti->x, ti->y,
|
||||
f == FOUNDATION_INCLINED_X ? 16 : 1,
|
||||
f == FOUNDATION_INCLINED_Y ? 16 : 1,
|
||||
f == FOUNDATION_INCLINED_X ? TILE_SIZE : 1,
|
||||
f == FOUNDATION_INCLINED_Y ? TILE_SIZE : 1,
|
||||
TILE_HEIGHT, ti->z
|
||||
);
|
||||
OffsetGroundSprite(31, 9);
|
||||
OffsetGroundSprite(0, 0);
|
||||
}
|
||||
ti->z += ApplyPixelFoundationToSlope(f, &ti->tileh);
|
||||
}
|
||||
@@ -1089,7 +1095,7 @@ static bool RiverMakeWider(TileIndex tile, void *data)
|
||||
/* If the tile is at height 0 after terraforming but the ocean hasn't flooded yet, don't build river. */
|
||||
if (GetTileMaxZ(tile) == 0) return false;
|
||||
|
||||
TileIndex origin_tile = *(TileIndex *)data;;
|
||||
TileIndex origin_tile = *(TileIndex *)data;
|
||||
Slope cur_slope = GetTileSlope(tile);
|
||||
Slope desired_slope = GetTileSlope(origin_tile); // Initialize matching the origin tile as a shortcut if no terraforming is needed.
|
||||
|
||||
@@ -1127,7 +1133,7 @@ static bool RiverMakeWider(TileIndex tile, void *data)
|
||||
}
|
||||
}
|
||||
/* If we find an adjacent river tile, remember it. We'll terraform to match it later if we don't find a slope. */
|
||||
if (IsTileFlat(tile)) flat_river_found = true;
|
||||
if (IsTileFlat(other_tile)) flat_river_found = true;
|
||||
}
|
||||
}
|
||||
/* We didn't find either an inclined or flat river, so we're climbing the wrong slope. Bail out. */
|
||||
@@ -1176,17 +1182,57 @@ static bool RiverMakeWider(TileIndex tile, void *data)
|
||||
Command<CMD_TERRAFORM_LAND>::Do(DC_EXEC | DC_AUTO, tile, to_change, true);
|
||||
}
|
||||
}
|
||||
/* Update cur_slope after possibly terraforming. */
|
||||
cur_slope = GetTileSlope(tile);
|
||||
}
|
||||
|
||||
/* Sloped rivers need water both upstream and downstream. */
|
||||
if (IsInclinedSlope(cur_slope)) {
|
||||
DiagDirection slope_direction = GetInclinedSlopeDirection(cur_slope);
|
||||
|
||||
TileIndex upstream_tile = TileAddByDiagDir(tile, slope_direction);
|
||||
TileIndex downstream_tile = TileAddByDiagDir(tile, ReverseDiagDir(slope_direction));
|
||||
|
||||
/* Don't look outside the map. */
|
||||
if (!IsValidTile(upstream_tile) || !IsValidTile(downstream_tile)) return false;
|
||||
|
||||
/* Downstream might be new ocean created by our terraforming, and it hasn't flooded yet. */
|
||||
bool downstream_is_ocean = GetTileZ(downstream_tile) == 0 && (GetTileSlope(downstream_tile) == SLOPE_FLAT || IsSlopeWithOneCornerRaised(GetTileSlope(downstream_tile)));
|
||||
|
||||
/* If downstream is dry, flat, and not ocean, try making it a river tile. */
|
||||
if (!IsWaterTile(downstream_tile) && !downstream_is_ocean) {
|
||||
/* If the tile upstream isn't flat, don't bother. */
|
||||
if (GetTileSlope(downstream_tile) != SLOPE_FLAT) return false;
|
||||
|
||||
MakeRiver(downstream_tile, Random());
|
||||
MarkTileDirtyByTile(downstream_tile);
|
||||
|
||||
/* Remove desert directly around the river tile. */
|
||||
TileIndex cur_tile = downstream_tile;
|
||||
CircularTileSearch(&cur_tile, RIVER_OFFSET_DESERT_DISTANCE, RiverModifyDesertZone, nullptr);
|
||||
}
|
||||
|
||||
/* If upstream is dry and flat, try making it a river tile. */
|
||||
if (!IsWaterTile(upstream_tile)) {
|
||||
/* If the tile upstream isn't flat, don't bother. */
|
||||
if (GetTileSlope(upstream_tile) != SLOPE_FLAT) return false;
|
||||
|
||||
MakeRiver(upstream_tile, Random());
|
||||
MarkTileDirtyByTile(upstream_tile);
|
||||
|
||||
/* Remove desert directly around the river tile. */
|
||||
TileIndex cur_tile = upstream_tile;
|
||||
CircularTileSearch(&cur_tile, RIVER_OFFSET_DESERT_DISTANCE, RiverModifyDesertZone, nullptr);
|
||||
}
|
||||
}
|
||||
/* Update cur_slope after possibly terraforming. */
|
||||
cur_slope = GetTileSlope(tile);
|
||||
|
||||
/* If the tile slope matches the desired slope, add a river tile. */
|
||||
if (cur_slope == desired_slope) {
|
||||
MakeRiver(tile, Random());
|
||||
MarkTileDirtyByTile(tile);
|
||||
|
||||
/* Remove desert directly around the river tile. */
|
||||
TileIndex cur_tile = tile;
|
||||
MarkTileDirtyByTile(cur_tile);
|
||||
CircularTileSearch(&cur_tile, RIVER_OFFSET_DESERT_DISTANCE, RiverModifyDesertZone, nullptr);
|
||||
}
|
||||
|
||||
@@ -1289,7 +1335,7 @@ static void River_FoundEndNode(AyStar *aystar, OpenListNode *current)
|
||||
current_river_length = DistanceManhattan(data->spring, tile);
|
||||
radius = std::min(3u, (current_river_length / (long_river_length / 3u)) + 1u);
|
||||
|
||||
if (radius > 1) CircularTileSearch(&tile, radius + RandomRange(1), RiverMakeWider, (void *)&path->node.tile);
|
||||
if (radius > 1) CircularTileSearch(&tile, radius, RiverMakeWider, (void *)&path->node.tile);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1460,6 +1506,9 @@ static void CreateRivers()
|
||||
}
|
||||
}
|
||||
|
||||
/* Widening rivers may have left some tiles requiring to be watered. */
|
||||
ConvertGroundTilesIntoWaterTiles();
|
||||
|
||||
/* Run tile loop to update the ground density. */
|
||||
for (uint i = 0; i != 256; i++) {
|
||||
if (i % 64 == 0) IncreaseGeneratingWorldProgress(GWP_RIVER);
|
||||
|
@@ -1145,6 +1145,7 @@ STR_CONFIG_SETTING_HORIZONTAL_POS_RIGHT :Regs
|
||||
|
||||
STR_CONFIG_SETTING_MAXIMUM_INITIAL_LOAN :Maksimum aanvanklike lening: {STRING}
|
||||
STR_CONFIG_SETTING_MAXIMUM_INITIAL_LOAN_HELPTEXT :Maksimum bedrag wat 'n maatskappy kan leen (sonder die inagneming van inflasie)
|
||||
###setting-zero-is-special
|
||||
|
||||
STR_CONFIG_SETTING_INTEREST_RATE :Rentekoers: {STRING}
|
||||
STR_CONFIG_SETTING_INTEREST_RATE_HELPTEXT :Lening rentekoers: beheer ook inflasie indien aangeskakel
|
||||
@@ -2482,8 +2483,6 @@ STR_BUILD_SIGNAL_DRAG_SIGNALS_DENSITY_INCREASE_TOOLTIP :{BLACK}Verhoog
|
||||
STR_SELECT_RAIL_BRIDGE_CAPTION :{WHITE}Kies Spoor Brug
|
||||
STR_SELECT_ROAD_BRIDGE_CAPTION :{WHITE}Selekteer Pad Brug
|
||||
STR_SELECT_BRIDGE_SELECTION_TOOLTIP :{BLACK}Brugkeuring - klik op jou verkose brug om dit te bou
|
||||
STR_SELECT_BRIDGE_INFO :{GOLD}{STRING},{} {VELOCITY} {WHITE}{CURRENCY_LONG}
|
||||
STR_SELECT_BRIDGE_SCENEDIT_INFO :{GOLD}{STRING},{} {VELOCITY}
|
||||
STR_BRIDGE_NAME_SUSPENSION_STEEL :Kabelstut, Staal
|
||||
STR_BRIDGE_NAME_GIRDER_STEEL :Balk, Staal
|
||||
STR_BRIDGE_NAME_CANTILEVER_STEEL :Vrydraer, Staal
|
||||
@@ -3054,6 +3053,10 @@ STR_SPRITE_ALIGNER_PREVIOUS_BUTTON :{BLACK}Vorige s
|
||||
STR_SPRITE_ALIGNER_PREVIOUS_TOOLTIP :{BLACK}Gaan na vorige normale sprite, en ignoreer enige pseudo/her-kleur/font sprite en spring terug na die einde
|
||||
STR_SPRITE_ALIGNER_SPRITE_TOOLTIP :{BLACK}Voorstelling van geselekteerde sprite. Die belyning word geignoreer waneer sprite geteken word
|
||||
STR_SPRITE_ALIGNER_MOVE_TOOLTIP :{BLACK}Beweeg die sprite rond, verander die X en Y afwyking. Ctrl-klik om die sprite agt lengtes rond te beweeg op 'n slag
|
||||
|
||||
###length 2
|
||||
|
||||
|
||||
STR_SPRITE_ALIGNER_RESET_BUTTON :{BLACK}Relatiewe herstel
|
||||
STR_SPRITE_ALIGNER_RESET_TOOLTIP :{BLACK}Herstel die huidige relatiewe verplasing
|
||||
STR_SPRITE_ALIGNER_OFFSETS_ABS :{BLACK}X verplasing: {NUM}, Y verplasing: {NUM} (Werklik)
|
||||
|
@@ -199,6 +199,7 @@ STR_UNITS_POWER_IMPERIAL :{COMMA}{NBSP}ح
|
||||
STR_UNITS_POWER_METRIC :{COMMA}{NBSP}حصان
|
||||
STR_UNITS_POWER_SI :{COMMA}{NBSP}ك واط
|
||||
|
||||
STR_UNITS_POWER_METRIC_TO_WEIGHT_SI :{DECIMAL}{NBSP}hp/Mg
|
||||
|
||||
STR_UNITS_WEIGHT_SHORT_IMPERIAL :{COMMA}{NBSP} طن
|
||||
STR_UNITS_WEIGHT_SHORT_METRIC :{COMMA}{NBSP}طن
|
||||
@@ -221,6 +222,7 @@ STR_UNITS_FORCE_METRIC :{COMMA}{NBSP}ك
|
||||
STR_UNITS_FORCE_SI :{COMMA}{NBSP} كيلو نيوتن
|
||||
|
||||
STR_UNITS_HEIGHT_IMPERIAL :{COMMA}{NBSP} قدم
|
||||
STR_UNITS_HEIGHT_METRIC :{COMMA}{NBSP}م
|
||||
STR_UNITS_HEIGHT_SI :{COMMA}{NBSP} متر
|
||||
|
||||
# Common window strings
|
||||
@@ -929,6 +931,7 @@ STR_GAME_OPTIONS_CURRENCY_KRW :وون كوري
|
||||
STR_GAME_OPTIONS_CURRENCY_ZAR :راند جنوب أفريقيا (ZAR)
|
||||
STR_GAME_OPTIONS_CURRENCY_CUSTOM :مخصص ...
|
||||
STR_GAME_OPTIONS_CURRENCY_GEL :(GEL) لاري جورجي
|
||||
STR_GAME_OPTIONS_CURRENCY_NTD :الدولار التايواني الجديد (NTD)
|
||||
STR_GAME_OPTIONS_CURRENCY_CNY :(CNY) الرنمينبي الصيني
|
||||
STR_GAME_OPTIONS_CURRENCY_HKD :(HKD) دولار هونج كونج
|
||||
STR_GAME_OPTIONS_CURRENCY_INR :الروبية الهندية (INR)
|
||||
@@ -956,11 +959,15 @@ STR_GAME_OPTIONS_RESOLUTION_TOOLTIP :{BLACK}اختر
|
||||
STR_GAME_OPTIONS_RESOLUTION_OTHER :اخرى
|
||||
|
||||
STR_GAME_OPTIONS_VIDEO_ACCELERATION :{BLACK}تسريع الأجهزة
|
||||
STR_GAME_OPTIONS_VIDEO_ACCELERATION_TOOLTIP :{BLACK}حدد هذا المربع للسماح لـ OpenTTD بمحاولة استخدام تسريع الأجهزة. سيتم تطبيق الإعداد الذي تم تغييره فقط عند إعادة تشغيل اللعبة
|
||||
STR_GAME_OPTIONS_VIDEO_ACCELERATION_TOOLTIP :{BLACK}اخترهذا المربع للسماح لـ OpenTTD بمحاولة استخدام تسريع الأجهزة. سيتم تطبيق الإعداد الذي تم تغييره فقط عند إعادة تشغيل اللعبة
|
||||
STR_GAME_OPTIONS_VIDEO_ACCELERATION_RESTART :{WHITE}لن يعمل الإعداد إلا بعد إعادة تشغيل اللعبة
|
||||
|
||||
STR_GAME_OPTIONS_VIDEO_VSYNC :{BLACK}VSync
|
||||
STR_GAME_OPTIONS_VIDEO_VSYNC_TOOLTIP :{BLACK}حدد هذا المربع لمزامنة الشاشة (v-sync). سيتم تطبيق الإعداد الذي تم تغييره فقط عند إعادة تشغيل اللعبة. يعمل فقط مع تمكين تسريع الأجهزة (hardware acceleration)
|
||||
|
||||
|
||||
STR_GAME_OPTIONS_GUI_SCALE_AUTO :{BLACK}الكشف التلقائي عن الحجم
|
||||
STR_GAME_OPTIONS_GUI_SCALE_AUTO_TOOLTIP :{BLACK}اختر هذا المربع لكشف حجم الواجهة تلقائيا
|
||||
|
||||
|
||||
|
||||
@@ -1092,6 +1099,7 @@ STR_CONFIG_SETTING_EXPAND_ALL :{BLACK}مدد
|
||||
STR_CONFIG_SETTING_COLLAPSE_ALL :{BLACK}إسحب الكل
|
||||
STR_CONFIG_SETTING_RESET_ALL :{BLACK}اعادة ضبط جميع القيم
|
||||
STR_CONFIG_SETTING_NO_EXPLANATION_AVAILABLE_HELPTEXT :(لا يوجد تفسير متوفر)
|
||||
STR_CONFIG_SETTING_RESET_ALL_CONFIRMATION_DIALOG_CAPTION :{WHITE}تحذير!
|
||||
|
||||
STR_CONFIG_SETTING_RESTRICT_CATEGORY :{BLACK} فئة:
|
||||
STR_CONFIG_SETTING_RESTRICT_TYPE :{BLACK}نوع:
|
||||
@@ -1129,9 +1137,11 @@ STR_CONFIG_SETTING_HORIZONTAL_POS_LEFT :يسار
|
||||
STR_CONFIG_SETTING_HORIZONTAL_POS_CENTER :متوسط
|
||||
STR_CONFIG_SETTING_HORIZONTAL_POS_RIGHT :يمين
|
||||
|
||||
###setting-zero-is-special
|
||||
|
||||
STR_CONFIG_SETTING_INTEREST_RATE_HELPTEXT :سعر الفائدة على القرض يتحكم أيضًا في التضخم ، إذا تم تمكينه
|
||||
|
||||
STR_CONFIG_SETTING_RUNNING_COSTS_HELPTEXT :تحديد مستوى تكاليف الصيانة والتشغيل للمركبات والبنية التحتية
|
||||
|
||||
STR_CONFIG_SETTING_CONSTRUCTION_SPEED ::سرعة البناء {STRING}
|
||||
|
||||
@@ -1146,6 +1156,7 @@ STR_CONFIG_SETTING_SUBSIDY_DURATION_VALUE :{NUM} سنة
|
||||
|
||||
STR_CONFIG_SETTING_RECESSIONS :حالات الركود: {STRING}
|
||||
|
||||
STR_CONFIG_SETTING_TRAIN_REVERSING :عدم السماح بعودة القطار إلى الخلف في المحطات: {STRING}
|
||||
|
||||
STR_CONFIG_SETTING_DISASTERS :الكوارث: {STRING}
|
||||
|
||||
@@ -1180,9 +1191,11 @@ STR_CONFIG_SETTING_DISTANT_JOIN_STATIONS :السماح ب
|
||||
STR_CONFIG_SETTING_INFLATION :التضخم: {STRING}
|
||||
STR_CONFIG_SETTING_INFLATION_HELPTEXT :تمكين التضخم في الاقتصاد ، حيث ترتفع التكاليف بشكل أسرع قليلاً من المدفوعات
|
||||
|
||||
STR_CONFIG_SETTING_MAX_BRIDGE_LENGTH :أقصى طول للجسر: {STRING}
|
||||
|
||||
STR_CONFIG_SETTING_MAX_BRIDGE_HEIGHT_HELPTEXT :الارتفاع الأقصى لبناء الجسور
|
||||
|
||||
STR_CONFIG_SETTING_MAX_TUNNEL_LENGTH_HELPTEXT :الطول الأقصى لبناء الأنفاق
|
||||
|
||||
STR_CONFIG_SETTING_RAW_INDUSTRY_CONSTRUCTION_METHOD :طريقة بناء المصانع الأولية يدوية : {STRING}
|
||||
###length 3
|
||||
@@ -1269,6 +1282,7 @@ STR_CONFIG_SETTING_ERRMSG_DURATION_VALUE :{COMMA} ثان
|
||||
###setting-zero-is-special
|
||||
|
||||
STR_CONFIG_SETTING_POPULATION_IN_LABEL :أعرض عدد السكان مع الاسم على العلامة: {STRING}
|
||||
STR_CONFIG_SETTING_POPULATION_IN_LABEL_HELPTEXT :اعرض عدد سكان البلدات في علماتها على الخريطة
|
||||
|
||||
STR_CONFIG_SETTING_GRAPH_LINE_THICKNESS :سمك الخطوط في العرض: {STRING}
|
||||
|
||||
@@ -1347,6 +1361,7 @@ STR_CONFIG_SETTING_SMALLMAP_LAND_COLOUR_DARK_GREEN :اخضر غام
|
||||
STR_CONFIG_SETTING_SMALLMAP_LAND_COLOUR_VIOLET :بنفسجي
|
||||
|
||||
###length 4
|
||||
STR_CONFIG_SETTING_LINKGRAPH_COLOURS_GREY_TO_RED :رمادي إلى أحمر
|
||||
|
||||
###length 4
|
||||
|
||||
@@ -1373,6 +1388,7 @@ STR_CONFIG_SETTING_SCROLLWHEEL_OFF :عدم استخ
|
||||
STR_CONFIG_SETTING_OSK_ACTIVATION :كيبورد على الشاشة: {STRING}
|
||||
###length 4
|
||||
STR_CONFIG_SETTING_OSK_ACTIVATION_DISABLED :غير مفعل
|
||||
STR_CONFIG_SETTING_OSK_ACTIVATION_SINGLE_CLICK_FOCUS :نقرة واحدة (عند التركيز)
|
||||
|
||||
###length 3
|
||||
STR_CONFIG_SETTING_USE_RELAY_SERVICE_ALLOW :سماح
|
||||
@@ -1391,7 +1407,7 @@ STR_CONFIG_SETTING_AUTOSAVE_HELPTEXT :اختر الو
|
||||
STR_CONFIG_SETTING_DATE_FORMAT_IN_SAVE_NAMES :استخدم {STRING} نمط التاريخ لاسم اللعبة المحفوظة
|
||||
###length 3
|
||||
STR_CONFIG_SETTING_DATE_FORMAT_IN_SAVE_NAMES_LONG :طويل - 31 ديسمبر 2008
|
||||
STR_CONFIG_SETTING_DATE_FORMAT_IN_SAVE_NAMES_SHORT :قصير - 31 - 12 - 2008
|
||||
STR_CONFIG_SETTING_DATE_FORMAT_IN_SAVE_NAMES_SHORT :قصير (31-12-2008)
|
||||
STR_CONFIG_SETTING_DATE_FORMAT_IN_SAVE_NAMES_ISO :ايزو 31-12-2008
|
||||
|
||||
STR_CONFIG_SETTING_PAUSE_ON_NEW_GAME :إيقاف اللعبة تلقائيا عند بدأ لعبة جديدة: {STRING}
|
||||
@@ -1463,6 +1479,7 @@ STR_CONFIG_SETTING_MAX_AIRCRAFT_HELPTEXT :الحد الأ
|
||||
STR_CONFIG_SETTING_MAX_SHIPS :الحد الأعلى لعدد السفن لكل شركة: {STRING}
|
||||
|
||||
STR_CONFIG_SETTING_AI_BUILDS_TRAINS :حظر القطارات على الحاسوب: {STRING}
|
||||
STR_CONFIG_SETTING_AI_BUILDS_TRAINS_HELPTEXT :يؤدي تمكين هذا الإعداد إلى جعل بناء القطارات مستحيلًا لللاعب الآلي
|
||||
|
||||
STR_CONFIG_SETTING_AI_BUILDS_ROAD_VEHICLES :حظر العربات على الكمبيوتر: {STRING}
|
||||
|
||||
@@ -1477,6 +1494,7 @@ STR_CONFIG_SETTING_AI_IN_MULTIPLAYER :السماح ب
|
||||
STR_CONFIG_SETTING_AI_IN_MULTIPLAYER_HELPTEXT :السماح بلاعبين كمبيوترالذكاء الصناعي بالمشاركة في الالعاب الجماعية.
|
||||
|
||||
STR_CONFIG_SETTING_SCRIPT_MAX_OPCODES :#opcodes قبل تعليق الاسكربت: {STRING}
|
||||
STR_CONFIG_SETTING_SCRIPT_MAX_MEMORY_VALUE :{COMMA} MiB
|
||||
|
||||
STR_CONFIG_SETTING_SERVINT_ISPERCENT :فترات الصيانة بالنسبة المئوية : {STRING}
|
||||
|
||||
@@ -1490,6 +1508,7 @@ STR_CONFIG_SETTING_WAGONSPEEDLIMITS :السماح ب
|
||||
STR_CONFIG_SETTING_DISABLE_ELRAILS :تعطيل سكة القطار الكهربائي: {STRING}
|
||||
|
||||
STR_CONFIG_SETTING_NEWS_ARRIVAL_FIRST_VEHICLE_OWN :وصول اول مركبة لمحطة اللاعب: {STRING}
|
||||
STR_CONFIG_SETTING_NEWS_ARRIVAL_FIRST_VEHICLE_OWN_HELPTEXT :عرض جريدة عندما تصل السيارة الأولى إلى محطة اللاعب الجديد
|
||||
|
||||
STR_CONFIG_SETTING_NEWS_ARRIVAL_FIRST_VEHICLE_OTHER :وصول اول مركبة لمحطة الخصوم: {STRING}
|
||||
|
||||
@@ -1543,6 +1562,7 @@ STR_CONFIG_SETTING_ENDING_YEAR_ZERO :لا تنتهي
|
||||
STR_CONFIG_SETTING_ECONOMY_TYPE_FROZEN :مجمد
|
||||
|
||||
STR_CONFIG_SETTING_ALLOW_SHARES :السماح بشراء حصص من الشركات الاخرى: {STRING}
|
||||
STR_CONFIG_SETTING_ALLOW_SHARES_HELPTEXT :عند التمكين ، السماح بشراء وبيع أسهم الشركة. ستكون الأسهم متاحة فقط للشركات التي بلغت سنًا معينة
|
||||
|
||||
|
||||
|
||||
@@ -1551,11 +1571,13 @@ STR_CONFIG_SETTING_DRAG_SIGNALS_DENSITY :عند السح
|
||||
STR_CONFIG_SETTING_SEMAPHORE_BUILD_BEFORE_DATE :اتاحة استخدام الأشارات بالأعلام قبل :{STRING}
|
||||
|
||||
STR_CONFIG_SETTING_CYCLE_SIGNAL_TYPES :الدوران خلال الاشارات: {STRING}
|
||||
STR_CONFIG_SETTING_CYCLE_SIGNAL_TYPES_HELPTEXT :حدد أنواع الإشارات التي تريد التنقل خلالها عند النقر (Ctrl+Click) فوق إشارة مبنية باستخدام أداة الإشارة
|
||||
###length 2
|
||||
STR_CONFIG_SETTING_CYCLE_SIGNAL_PBS :اشارات الطريق فقط
|
||||
STR_CONFIG_SETTING_CYCLE_SIGNAL_ALL :الكل واضح
|
||||
|
||||
###length 2
|
||||
STR_CONFIG_SETTING_SIGNAL_GUI_MODE_PATH :اشارات الطريق فقط
|
||||
|
||||
STR_CONFIG_SETTING_TOWN_LAYOUT :تصميم الطرق للمدن الجديدة : {STRING}
|
||||
###length 5
|
||||
@@ -1577,6 +1599,7 @@ STR_CONFIG_SETTING_TOWN_FOUNDING_FORBIDDEN :محذور
|
||||
STR_CONFIG_SETTING_TOWN_FOUNDING_ALLOWED :مسموح
|
||||
STR_CONFIG_SETTING_TOWN_FOUNDING_ALLOWED_CUSTOM_LAYOUT :مسموح - نمط مدينة قابل للتعديل
|
||||
|
||||
STR_CONFIG_SETTING_TOWN_CARGOGENMODE :توليد البضائع في المدينة: {STRING}
|
||||
###length 2
|
||||
STR_CONFIG_SETTING_TOWN_CARGOGENMODE_ORIGINAL :تربيعي (أصلي)
|
||||
STR_CONFIG_SETTING_TOWN_CARGOGENMODE_BITCOUNT :خطي
|
||||
@@ -1587,9 +1610,11 @@ STR_CONFIG_SETTING_EXTRA_TREE_PLACEMENT_SPREAD_RAINFOREST :تنموا ول
|
||||
|
||||
STR_CONFIG_SETTING_TOOLBAR_POS :موقع شريط الأدوات الرئيسي: {STRING}
|
||||
STR_CONFIG_SETTING_STATUSBAR_POS :موقع شريط المعلومات: {STRING}
|
||||
STR_CONFIG_SETTING_SNAP_RADIUS :دائرة تثبيت النافذة: {STRING}
|
||||
###setting-zero-is-special
|
||||
STR_CONFIG_SETTING_SNAP_RADIUS_DISABLED :غير مفعل
|
||||
STR_CONFIG_SETTING_SOFT_LIMIT :حدود نعومة النوافذ - غير ملتصقة - :{STRING}
|
||||
STR_CONFIG_SETTING_SOFT_LIMIT_HELPTEXT :عدد النوافذ المفتوحة غير اللاصقة قبل أن يتم إغلاق النوافذ القديمة تلقائيًا لإفساح المجال للنوافذ الجديدة
|
||||
###setting-zero-is-special
|
||||
STR_CONFIG_SETTING_SOFT_LIMIT_DISABLED :غير مفعل
|
||||
|
||||
@@ -1615,6 +1640,7 @@ STR_CONFIG_SETTING_TOWN_GROWTH_FAST :سريع
|
||||
STR_CONFIG_SETTING_TOWN_GROWTH_VERY_FAST :سريع جدا
|
||||
|
||||
STR_CONFIG_SETTING_LARGER_TOWNS_HELPTEXT :عدد المدن التي ستصبح مدينة كبيرة، وبالتالي اي مدينة تبدء اكبر وتنمو بطريقة سريعة.
|
||||
STR_CONFIG_SETTING_LARGER_TOWNS_VALUE :1 في {COMMA}
|
||||
###setting-zero-is-special
|
||||
STR_CONFIG_SETTING_CITY_SIZE_MULTIPLIER :مضاعف المدن المبدئي: {STRING}
|
||||
|
||||
@@ -1626,26 +1652,29 @@ STR_CONFIG_SETTING_LINKGRAPH_ACCURACY_HELPTEXT :كلما قمت
|
||||
|
||||
STR_CONFIG_SETTING_DEMAND_SIZE_HELPTEXT :ضبط هذا إلى أقل من 100٪ يؤدي إلى جعل التوزيع المتماثل يتصرف مثل التوزيع غير المتماثل. سيتم إعادة شحنات أقل غصبا إذا تم إرسال مبلغ معين إلى المحطة. إذا قمت بتعيينه على 0٪ ، فإن التوزيع المتماثل يتصرف تمامًا مثل التوزيع غير المتماثل
|
||||
|
||||
STR_CONFIG_SETTING_SHORT_PATH_SATURATION :تشبع المسارات القصيرة قبل استخدام المسارات عالية القدرة: {STRING}
|
||||
|
||||
STR_CONFIG_SETTING_LOCALISATION_UNITS_VELOCITY :وحدات السرعة: {STRING}
|
||||
###length 4
|
||||
STR_CONFIG_SETTING_LOCALISATION_UNITS_VELOCITY_METRIC :(متري (كم / ساعة
|
||||
STR_CONFIG_SETTING_LOCALISATION_UNITS_VELOCITY_IMPERIAL :إمبراطوري (ميل/ساعة)
|
||||
STR_CONFIG_SETTING_LOCALISATION_UNITS_VELOCITY_METRIC :متري (كم / ساعة)
|
||||
STR_CONFIG_SETTING_LOCALISATION_UNITS_VELOCITY_SI :متري (م / ثانية)
|
||||
|
||||
STR_CONFIG_SETTING_LOCALISATION_UNITS_POWER :وحدات الطاقة في السيارة: {STRING}
|
||||
###length 3
|
||||
|
||||
###length 3
|
||||
STR_CONFIG_SETTING_LOCALISATION_UNITS_WEIGHT_METRIC :(متري (طن
|
||||
STR_CONFIG_SETTING_LOCALISATION_UNITS_WEIGHT_METRIC :متري (طن)
|
||||
|
||||
###length 3
|
||||
|
||||
STR_CONFIG_SETTING_LOCALISATION_UNITS_FORCE ::وحدات جهد الجر {STRING}
|
||||
###length 3
|
||||
STR_CONFIG_SETTING_LOCALISATION_UNITS_FORCE_METRIC :متري (كغ - قوة)
|
||||
|
||||
###length 3
|
||||
STR_CONFIG_SETTING_LOCALISATION_UNITS_HEIGHT_IMPERIAL :إمبراطوري (قدم)
|
||||
STR_CONFIG_SETTING_LOCALISATION_UNITS_HEIGHT_METRIC :م) متري)
|
||||
STR_CONFIG_SETTING_LOCALISATION_UNITS_HEIGHT_METRIC :متري (م)
|
||||
|
||||
STR_CONFIG_SETTING_GRAPHICS :رسوميات {ORANGE}
|
||||
STR_CONFIG_SETTING_SOUND :{ORANGE}الصوت
|
||||
@@ -1669,11 +1698,13 @@ STR_CONFIG_SETTING_ENVIRONMENT_INDUSTRIES :{ORANGE} مصا
|
||||
STR_CONFIG_SETTING_ENVIRONMENT_CARGODIST :توزيع البضائع{ORANGE}
|
||||
STR_CONFIG_SETTING_AI :{ORANGE}المتنافسين
|
||||
STR_CONFIG_SETTING_AI_NPC :{ORANGE} لاعبين الحاسوب
|
||||
STR_CONFIG_SETTING_NETWORK :{ORANGE}شبكة الاتصال
|
||||
|
||||
STR_CONFIG_SETTING_PATHFINDER_FOR_TRAINS :موجد الطريق- قصاص الطريق - للقطارات:{STRING}
|
||||
STR_CONFIG_SETTING_PATHFINDER_FOR_ROAD_VEHICLES :موجد الطريق (قصاص الأثر) للعربات: {STRING}
|
||||
STR_CONFIG_SETTING_PATHFINDER_FOR_ROAD_VEHICLES_HELPTEXT :مكتشف المسار لاستخدامه لعربات الطرق
|
||||
STR_CONFIG_SETTING_PATHFINDER_FOR_SHIPS :موجد طريق العبور للسفن: {STRING}
|
||||
STR_CONFIG_SETTING_PATHFINDER_FOR_SHIPS_HELPTEXT :مكتشف المسار لاستخدامه للسفن
|
||||
STR_CONFIG_SETTING_REVERSE_AT_SIGNALS :العكس عند الإشارات: {STRING}
|
||||
###length 2
|
||||
STR_CONFIG_SETTING_PATHFINDER_NPF :NPF
|
||||
@@ -1756,6 +1787,7 @@ STR_CHEAT_CHANGE_COMPANY :{LTBLUE}الع
|
||||
STR_CHEAT_EXTRA_DYNAMITE :{LTBLUE}الجرافة السحرية - لازالة المصانع و الاجسام غير القابلة للازالة.{ORANGE}{STRING}
|
||||
STR_CHEAT_CROSSINGTUNNELS :{LTBLUE}يمكن للانفاق ان تتقاطع: {ORANGE}{STRING}
|
||||
STR_CHEAT_NO_JETCRASH :{LTBLUE}الطائرات النفاثه لا تتحطم (كثيراً) فى المطارات الصغيره: {ORANGE}{STRING}
|
||||
STR_CHEAT_EDIT_MAX_HL_QUERY_CAPT :{WHITE}تعدبل أقصى ارتفاع لتضاريس الخريطة
|
||||
STR_CHEAT_CHANGE_DATE :{LTBLUE}عدل التاريخ {ORANGE} {DATE_SHORT}
|
||||
STR_CHEAT_CHANGE_DATE_QUERY_CAPT :{WHITE}غير السنة الحالية
|
||||
STR_CHEAT_SETUP_PROD :{LTBLUE}تفعيل تغيير قيمة الانتاج: {ORANGE}{STRING}
|
||||
@@ -1951,6 +1983,9 @@ STR_NETWORK_COMPANY_LIST_SPECTATE :شاهد
|
||||
|
||||
# Network client list
|
||||
STR_NETWORK_CLIENT_LIST_CAPTION :{WHITE}اللاعبون عبر الإنترنت
|
||||
STR_NETWORK_CLIENT_LIST_SERVER_NAME :{BLACK}الاسم
|
||||
STR_NETWORK_CLIENT_LIST_PLAYER_NAME :{BLACK}الاسم
|
||||
STR_NETWORK_CLIENT_LIST_SPECTATORS :المشاهدين
|
||||
STR_NETWORK_CLIENT_LIST_NEW_COMPANY :(شركة جديدة)
|
||||
|
||||
# Matches ConnectionType
|
||||
@@ -1961,6 +1996,7 @@ STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_BAN :منع
|
||||
|
||||
STR_NETWORK_CLIENT_LIST_ASK_CLIENT_KICK :{YELLOW}هل أنت متأكد أنك تريد طرد اللاعب '{STRING}'؟
|
||||
STR_NETWORK_CLIENT_LIST_ASK_CLIENT_BAN :{YELLOW}هل أنت متأكد أنك تريد حظر اللاعب '{STRING}'؟
|
||||
STR_NETWORK_CLIENT_LIST_ASK_COMPANY_RESET :{YELLOW}هل أنت متأكد أنك تريد حذف شركة '{COMPANY}'؟
|
||||
|
||||
STR_NETWORK_ASK_RELAY_NO :{BLACK}لا
|
||||
|
||||
@@ -2012,6 +2048,7 @@ STR_NETWORK_ERROR_KICKED :{WHITE}لقد
|
||||
STR_NETWORK_ERROR_KICK_MESSAGE :{WHITE}السبب: {STRING}
|
||||
STR_NETWORK_ERROR_CHEATER :{WHITE}الغش ليس مسموحا به في هذه اللعبة
|
||||
STR_NETWORK_ERROR_TOO_MANY_COMMANDS :{WHITE}أنت تقوم بإرسال العديد من الأوامر للخادم
|
||||
STR_NETWORK_ERROR_INVALID_CLIENT_NAME :{WHITE}اسم اللاعب الخاص بك غير صالح
|
||||
|
||||
STR_NETWORK_ERROR_CLIENT_GUI_LOST_CONNECTION_CAPTION :{WHITE}احتمال فقد الاتصال
|
||||
STR_NETWORK_ERROR_CLIENT_GUI_LOST_CONNECTION :{WHITE}اخر {NUM} ثواني لم تصل بيانات من السيرفر
|
||||
@@ -2251,8 +2288,6 @@ STR_BUILD_SIGNAL_DRAG_SIGNALS_DENSITY_INCREASE_TOOLTIP :{BLACK}زياد
|
||||
STR_SELECT_RAIL_BRIDGE_CAPTION :{WHITE}اختر جسر السكة
|
||||
STR_SELECT_ROAD_BRIDGE_CAPTION :{WHITE}اختر الجسر المناسب
|
||||
STR_SELECT_BRIDGE_SELECTION_TOOLTIP :{BLACK}بناء الجسور - اختر الجسر المفضل لديك لبنائة
|
||||
STR_SELECT_BRIDGE_INFO :{GOLD}{STRING},{} {VELOCITY} {WHITE}{CURRENCY_LONG}
|
||||
STR_SELECT_BRIDGE_SCENEDIT_INFO :{GOLD}{STRING},{} {VELOCITY}
|
||||
STR_BRIDGE_NAME_SUSPENSION_STEEL :تعليق, حديدي
|
||||
STR_BRIDGE_NAME_GIRDER_STEEL :العارضة, حديدية
|
||||
STR_BRIDGE_NAME_CANTILEVER_STEEL :الحامل, حديدي
|
||||
@@ -2429,6 +2464,7 @@ STR_FUND_INDUSTRY_CAPTION :{WHITE}مول
|
||||
STR_FUND_INDUSTRY_SELECTION_TOOLTIP :{BLACK}اختر المصنع المناسب من القائمة
|
||||
STR_FUND_INDUSTRY_MANY_RANDOM_INDUSTRIES :{BLACK}إنشاء المصانع العشوائية
|
||||
STR_FUND_INDUSTRY_MANY_RANDOM_INDUSTRIES_TOOLTIP :{BLACK}غطي الخريطة عشوائيا بالمصانع
|
||||
STR_FUND_INDUSTRY_MANY_RANDOM_INDUSTRIES_CAPTION :{WHITE}إنشاء المصانع العشوائية
|
||||
STR_FUND_INDUSTRY_INDUSTRY_BUILD_COST :{BLACK}التكلفة: {YELLOW}{CURRENCY_LONG}
|
||||
STR_FUND_INDUSTRY_PROSPECT_NEW_INDUSTRY :{BLACK} الاحتمالية
|
||||
STR_FUND_INDUSTRY_BUILD_NEW_INDUSTRY :{BLACK}بناء
|
||||
@@ -2594,6 +2630,9 @@ STR_FRAMERATE_GAMESCRIPT :{BLACK} مخط
|
||||
STR_FRAMERATE_AI :{BLACK} AI {NUM} {STRING}
|
||||
|
||||
###length 15
|
||||
STR_FRAMETIME_CAPTION_GAMELOOP :تكرارات اللعبة
|
||||
STR_FRAMETIME_CAPTION_GL_TRAINS : تكتكة القطار
|
||||
STR_FRAMETIME_CAPTION_GL_LINKGRAPH :تأخر ارتباط الرسم البياني
|
||||
STR_FRAMETIME_CAPTION_SOUND :اختلاط الصوت
|
||||
STR_FRAMETIME_CAPTION_GAMESCRIPT :كتابة اللعبة
|
||||
|
||||
@@ -2638,7 +2677,9 @@ STR_MAPGEN_NUMBER_OF_INDUSTRIES :{BLACK}عدد
|
||||
STR_MAPGEN_HEIGHTMAP_HEIGHT_UP :{BLACK}قم بزيادة أقصى ارتفاع لأعلى قمة على الخريطة بواحد
|
||||
STR_MAPGEN_SNOW_COVERAGE :{BLACK}تغطية الثلج:
|
||||
STR_MAPGEN_SNOW_COVERAGE_DOWN :{BLACK}تقليل تغطية الثلوج بنسبة عشرة بالمائة
|
||||
STR_MAPGEN_SNOW_COVERAGE_TEXT :{BLACK}{NUM}%
|
||||
STR_MAPGEN_DESERT_COVERAGE :{BLACK}مدى تغطيت السحراء:
|
||||
STR_MAPGEN_DESERT_COVERAGE_DOWN :{BLACK}تقليل التغطية الصحراوية بنسبة عشرة بالمائة
|
||||
STR_MAPGEN_DESERT_COVERAGE_TEXT :{BLACK}{NUM}%
|
||||
STR_MAPGEN_LAND_GENERATOR :{BLACK}مولد الخريطة:
|
||||
STR_MAPGEN_TERRAIN_TYPE :{BLACK} نوع التضاريس
|
||||
@@ -2804,7 +2845,13 @@ STR_SPRITE_ALIGNER_GOTO_TOOLTIP :{BLACK}اذهب
|
||||
STR_SPRITE_ALIGNER_PREVIOUS_BUTTON :{BLACK}العفريتة السابقة.
|
||||
STR_SPRITE_ALIGNER_PREVIOUS_TOOLTIP :{BLACK}نابع للعفريتة الطبيعية التالية، تجاوز اي عفريتة موقوفة/ مصبوغة/مخطوطة و انهي في البداية.
|
||||
STR_SPRITE_ALIGNER_SPRITE_TOOLTIP :{BLACK}عرض العفريتة المختارة حاليا. يتم تجاهل الموائمة عند رسم هذا العفريت.
|
||||
STR_SPRITE_ALIGNER_MOVE_TOOLTIP :{BLACK}حرك العفريتة في الجوار، غير الاحداثيات س ، ص.
|
||||
STR_SPRITE_ALIGNER_MOVE_TOOLTIP :{BLACK}حرك الرسمة، وقم بتغيير إزاحة X وY. قم ب Ctrl+Click لتحريك الكائن ثماني وحدات في المرة الواحدة
|
||||
|
||||
###length 2
|
||||
|
||||
STR_SPRITE_ALIGNER_CROSSHAIR :{BLACK}الخطوت المتقاطعة
|
||||
|
||||
STR_SPRITE_ALIGNER_OFFSETS_REL :{BLACK}الازاحة (X): {NUM}، الازاحة (Y): {NUM} (نسبي)
|
||||
STR_SPRITE_ALIGNER_PICKER_BUTTON :{BLACK}اختر عفريتة
|
||||
STR_SPRITE_ALIGNER_PICKER_TOOLTIP :{BLACK}اختر عفريتة من اي مكان في الشاشة.
|
||||
|
||||
@@ -2969,9 +3016,11 @@ STR_GOAL_QUESTION_CAPTION_ERROR :{YELLOW}خطا
|
||||
|
||||
# Goal Question button list
|
||||
###length 18
|
||||
STR_GOAL_QUESTION_BUTTON_OK :موافقة
|
||||
STR_GOAL_QUESTION_BUTTON_NO :لا
|
||||
STR_GOAL_QUESTION_BUTTON_YES :نعم
|
||||
STR_GOAL_QUESTION_BUTTON_RETRY :إعادة المحاولة
|
||||
STR_GOAL_QUESTION_BUTTON_CONTINUE :الاستمرار
|
||||
STR_GOAL_QUESTION_BUTTON_RESTART :إعادة تشغيل
|
||||
STR_GOAL_QUESTION_BUTTON_POSTPONE :تأجيل
|
||||
STR_GOAL_QUESTION_BUTTON_SURRENDER :استسلام
|
||||
@@ -3022,8 +3071,10 @@ STR_STATION_VIEW_GROUP :{BLACK}جمع
|
||||
STR_STATION_VIEW_WAITING_AMOUNT :الكمية: في الانتظار
|
||||
STR_STATION_VIEW_PLANNED_AMOUNT :المبلغ: تم تخطيطه
|
||||
STR_STATION_VIEW_FROM :{YELLOW}{CARGO_SHORT} من {STATION}
|
||||
STR_STATION_VIEW_VIA :{YELLOW}{CARGO_SHORT} من خلال {STATION}
|
||||
STR_STATION_VIEW_TO :{YELLOW}{CARGO_SHORT} إلى {STATION}
|
||||
STR_STATION_VIEW_TO_ANY :{RED}{CARGO_SHORT} إلى أي محطة
|
||||
STR_STATION_VIEW_NONSTOP :{YELLOW}{CARGO_SHORT} بدون توقف
|
||||
|
||||
STR_STATION_VIEW_GROUP_V_S_D :عبر-المصدر-الوجهة
|
||||
|
||||
@@ -3170,6 +3221,7 @@ STR_INDUSTRY_VIEW_TRANSPORTED :{YELLOW}{CARGO_
|
||||
STR_INDUSTRY_VIEW_LOCATION_TOOLTIP :{BLACK}وسط الشاشة على المصنع
|
||||
STR_INDUSTRY_VIEW_PRODUCTION_LEVEL :{BLACK}نسبة الانتاج: {YELLOW}{COMMA}%
|
||||
|
||||
STR_INDUSTRY_VIEW_PRODUCES_N_CARGO :{BLACK}ينتج: {YELLOW}{STRING}{STRING}
|
||||
|
||||
STR_INDUSTRY_VIEW_REQUIRES :{BLACK}:يتطلب
|
||||
STR_INDUSTRY_VIEW_ACCEPT_CARGO_AMOUNT :{YELLOW}{STRING}{BLACK}: {CARGO_SHORT} تنتظر{STRING}
|
||||
@@ -3276,16 +3328,19 @@ STR_PURCHASE_INFO_COST :{BLACK}التك
|
||||
STR_PURCHASE_INFO_COST_REFIT :{BLACK}التكلفة: {GOLD}{CURRENCY_LONG}{BLACK} (تكلفة التغيير: {GOLD}{CURRENCY_LONG}{BLACK})
|
||||
STR_PURCHASE_INFO_WEIGHT_CWEIGHT :{BLACK}الوزن: {GOLD}{WEIGHT_SHORT} ({WEIGHT_SHORT})
|
||||
STR_PURCHASE_INFO_COST_SPEED :{BLACK}التكلفة: {GOLD}{CURRENCY_LONG}{BLACK} السرعة : {GOLD}{VELOCITY}
|
||||
STR_PURCHASE_INFO_COST_REFIT_SPEED :{BLACK}التكلفة: {GOLD}{CURRENCY_LONG}{BLACK} (تكلفة التغير: {GOLD}{CURRENCY_LONG}{BLACK}) السرعة: {GOLD}{VELOCITY}
|
||||
STR_PURCHASE_INFO_AIRCRAFT_CAPACITY :{BLACK} السعة {GOLD}{CARGO_LONG}, {CARGO_LONG}
|
||||
STR_PURCHASE_INFO_PWAGPOWER_PWAGWEIGHT :{BLACK} العربات ذات الطاقة: {GOLD}+{POWER}{BLACK} الوزن: {GOLD}+{WEIGHT_SHORT}
|
||||
STR_PURCHASE_INFO_REFITTABLE_TO :{BLACK}يمكن تعديلها الى: {GOLD}{STRING}
|
||||
STR_PURCHASE_INFO_ALL_TYPES :جميع انواع الحمولة
|
||||
STR_PURCHASE_INFO_NONE :بدون
|
||||
STR_PURCHASE_INFO_ENGINES_ONLY :محركات فقط
|
||||
STR_PURCHASE_INFO_ALL_BUT :الكل الا {CARGO_LIST}
|
||||
STR_PURCHASE_INFO_MAX_TE :{BLACK}تأثير الجذب القصى: {GOLD}{FORCE}
|
||||
STR_PURCHASE_INFO_AIRCRAFT_TYPE :{BLACK}نوع الطائرة:{GOLD}{STRING}
|
||||
|
||||
###length 3
|
||||
STR_CARGO_TYPE_FILTER_NONE :لا شيء
|
||||
|
||||
###length VEHICLE_TYPES
|
||||
STR_BUY_VEHICLE_TRAIN_LIST_TOOLTIP :{BLACK}قائمة اختيار القطارات - اضغط على العربة لعرض معلوماتها
|
||||
@@ -3301,6 +3356,7 @@ STR_BUY_VEHICLE_AIRCRAFT_BUY_VEHICLE_BUTTON :{BLACK}شراء
|
||||
|
||||
###length VEHICLE_TYPES
|
||||
STR_BUY_VEHICLE_ROAD_VEHICLE_BUY_REFIT_VEHICLE_BUTTON :{BLACK}شراء العربة وتجديد بضائعها
|
||||
STR_BUY_VEHICLE_SHIP_BUY_REFIT_VEHICLE_BUTTON :{BLACK}شراء وإعادة تجهيز السفينة
|
||||
|
||||
###length VEHICLE_TYPES
|
||||
STR_BUY_VEHICLE_TRAIN_BUY_VEHICLE_TOOLTIP :{BLACK}شراء العربة الموضحة
|
||||
@@ -3486,6 +3542,7 @@ STR_REPLACE_TRAM_VEHICLES :مركبات ا
|
||||
|
||||
STR_REPLACE_REMOVE_WAGON :{BLACK} إزالة العربة ({STRING}): {ORANGE}{STRING}
|
||||
STR_REPLACE_REMOVE_WAGON_HELP :{BLACK} المحافظة على طول القطار بازالة عربات ابتداء من المقدمة عند التبديل - عندما يكون التبدل ينتج قطارا اطول.
|
||||
STR_REPLACE_REMOVE_WAGON_GROUP_HELP :{STRING}. Ctrl + Click للتطبيق أيضًا على المجموعات الفرعية
|
||||
|
||||
# Vehicle view
|
||||
STR_VEHICLE_VIEW_CAPTION :{WHITE}{VEHICLE}
|
||||
@@ -3764,6 +3821,7 @@ STR_ORDER_REFIT_STOP_ORDER :هيئت الى
|
||||
STR_ORDER_STOP_ORDER :توقف
|
||||
|
||||
STR_ORDER_GO_TO_STATION :{STRING} {STATION} {STRING}
|
||||
STR_ORDER_GO_TO_STATION_CAN_T_USE_STATION :{PUSH_COLOUR}{RED}(لا يمكن استخدام المحطة){POP_COLOUR} {STRING} {STATION} {STRING}
|
||||
|
||||
STR_ORDER_IMPLICIT :(تلقائى)
|
||||
|
||||
@@ -3818,13 +3876,14 @@ STR_TIMETABLE_ORDER_VIEW_TOOLTIP :{BLACK}حول
|
||||
STR_TIMETABLE_TOOLTIP :{BLACK}جدولة الأوامر - اضغط على الامر لاظهارة
|
||||
|
||||
STR_TIMETABLE_NO_TRAVEL :لا رحيل
|
||||
STR_TIMETABLE_NOT_TIMETABLEABLE :المغادرة (ذاتي: بواسطة الامر اليدوي التالي)
|
||||
STR_TIMETABLE_NOT_TIMETABLEABLE :المغادرة (ذاتي: مجدول بواسطة الامر اليدوي التالي)
|
||||
STR_TIMETABLE_TRAVEL_NOT_TIMETABLED :مسافر (غير مجدوله)
|
||||
STR_TIMETABLE_TRAVEL_NOT_TIMETABLED_SPEED :سافر(بدون جدول زمني) مع تقريبا {2:VELOCITY}
|
||||
STR_TIMETABLE_TRAVEL_FOR :مسافر لـ {STRING}
|
||||
STR_TIMETABLE_TRAVEL_FOR_SPEED :سافر لي {STRING} بمعدل {VELOCITY}
|
||||
STR_TIMETABLE_TRAVEL_FOR_ESTIMATED :سافر(إلى {STRING}, بدون جدول زمني)
|
||||
STR_TIMETABLE_TRAVEL_FOR_SPEED_ESTIMATED :سافر (إلى {STRING}, بدون الجدول الزمني) مع تقريبا {VELOCITY}
|
||||
STR_TIMETABLE_STAY_FOR_ESTIMATED :(البقاء ل{STRING}، ليس مجدول)
|
||||
STR_TIMETABLE_STAY_FOR :ويبقى لـ {STRING}
|
||||
STR_TIMETABLE_AND_TRAVEL_FOR :ويسافر لـ {STRING}
|
||||
STR_TIMETABLE_DAYS :{COMMA}يوم
|
||||
@@ -3900,7 +3959,10 @@ STR_ERROR_AI_PLEASE_REPORT_CRASH :{WHITE}واحد
|
||||
STR_ERROR_AI_DEBUG_SERVER_ONLY :{YELLOW} شاشة اخطاء الذكاء الصناعي متوفرة فقط للخادم (سرڤر)
|
||||
|
||||
# AI configuration window
|
||||
STR_AI_CONFIG_GAMELIST_TOOLTIP :{BLACK}مخطوطات اللعبة الذي سيتم تحميله في اللعبة التالية
|
||||
STR_AI_CONFIG_CAPTION_AI :{WHITE}اعدادات االذكاء الاصطناعي
|
||||
STR_AI_CONFIG_CAPTION_GAMESCRIPT :{WHITE}تكوين مخطوط اللعبة
|
||||
STR_AI_CONFIG_GAMELIST_TOOLTIP :{BLACK}النص البرمجي الخاص باللعبة الذي سيتم تحميله في اللعبة التالية
|
||||
STR_AI_CONFIG_AILIST_TOOLTIP :{BLACK}الذكاء الاصطناعي الذي سيتم تحميله في اللعبة التالية
|
||||
STR_AI_CONFIG_HUMAN_PLAYER :لاعب انساني
|
||||
STR_AI_CONFIG_RANDOM_AI :ذكاء صناعي عشوائي
|
||||
STR_AI_CONFIG_NONE :(لا شيء)
|
||||
@@ -3915,6 +3977,7 @@ STR_AI_CONFIG_AI :{SILVER} الذ
|
||||
|
||||
STR_AI_CONFIG_CHANGE_AI :الذكاء الاصطناعي
|
||||
STR_AI_CONFIG_CHANGE_GAMESCRIPT :مخطوط اللعبة
|
||||
STR_AI_CONFIG_CHANGE_TOOLTIP :{BLACK}تحميل نص برمجي آخر
|
||||
STR_AI_CONFIG_CONFIGURE :{BLACK} اعداد
|
||||
STR_AI_CONFIG_CONFIGURE_TOOLTIP :{BLACK} اعداد خواص الذكاء الصناعي
|
||||
|
||||
@@ -3930,6 +3993,7 @@ STR_AI_LIST_ACCEPT_TOOLTIP :{BLACK} اخت
|
||||
STR_AI_LIST_CANCEL :{BLACK} الغاء
|
||||
STR_AI_LIST_CANCEL_TOOLTIP :{BLACK} لا تغير الذكاء الصناعي
|
||||
|
||||
STR_SCREENSHOT_SCREENSHOT :{BLACK}لقطة شاشة عادية
|
||||
STR_SCREENSHOT_ZOOMIN_SCREENSHOT :{BLACK}التقط لقطة شاشة كاملة
|
||||
STR_SCREENSHOT_WORLD_SCREENSHOT :{BLACK}صور الخاريطة الكاملة
|
||||
|
||||
@@ -4185,6 +4249,8 @@ STR_ERROR_DEPOT_WRONG_DEPOT_TYPE :مستودع خ
|
||||
STR_ERROR_TRAIN_TOO_LONG_AFTER_REPLACEMENT :{WHITE}{VEHICLE} طويل للغايه بعد اﻹستبدال
|
||||
STR_ERROR_AUTOREPLACE_NOTHING_TO_DO :{WHITE}لا يوجد نظام مطبق للتبديل/ التجديد
|
||||
STR_ERROR_AUTOREPLACE_MONEY_LIMIT :(حدود التكلفة للتبديل)
|
||||
STR_ERROR_AUTOREPLACE_INCOMPATIBLE_CARGO :{WHITE}العربة الجديدة لا تستطيع حمل {STRING}
|
||||
STR_ERROR_AUTOREPLACE_INCOMPATIBLE_REFIT :{WHITE}لا يمكن تجديد السيارة الجديدة بالترتيب {NUM}
|
||||
|
||||
# Rail construction errors
|
||||
STR_ERROR_IMPOSSIBLE_TRACK_COMBINATION :{WHITE}تركيبة سكك غير صالحة
|
||||
@@ -4213,6 +4279,7 @@ STR_ERROR_CAN_T_REMOVE_ROAD_FROM :{WHITE}تعذر
|
||||
STR_ERROR_CAN_T_REMOVE_TRAMWAY_FROM :{WHITE}تعذر إزاله سكه الترام من هنا...
|
||||
STR_ERROR_THERE_IS_NO_ROAD :{WHITE}... لا يوجد طريق
|
||||
STR_ERROR_THERE_IS_NO_TRAMWAY :{WHITE}... لا يوجد سكه ترام هنا
|
||||
STR_ERROR_NO_SUITABLE_ROAD :{WHITE}لا يوجد طريق مناسب
|
||||
STR_ERROR_NO_SUITABLE_TRAMWAY :{WHITE}لا يوجد ترام مناسب
|
||||
|
||||
# Waterway construction errors
|
||||
|
@@ -1108,6 +1108,7 @@ STR_CONFIG_SETTING_HORIZONTAL_POS_RIGHT :Eskuina
|
||||
|
||||
STR_CONFIG_SETTING_MAXIMUM_INITIAL_LOAN :Gehienezko mailegua joko hasieran: {STRING}
|
||||
STR_CONFIG_SETTING_MAXIMUM_INITIAL_LOAN_HELPTEXT :Konpainiek eskatu dezaketen gehienezko mailegua(inflazioa kontua hartu gabe)
|
||||
###setting-zero-is-special
|
||||
|
||||
STR_CONFIG_SETTING_INTEREST_RATE :Interes tasa: {STRING}
|
||||
STR_CONFIG_SETTING_INTEREST_RATE_HELPTEXT :Maileguen interes tasa; inflazioa ere kontrolatuko du, gaitzen bada
|
||||
@@ -2354,8 +2355,6 @@ STR_BUILD_SIGNAL_DRAG_SIGNALS_DENSITY_INCREASE_TOOLTIP :{BLACK}Seinale
|
||||
STR_SELECT_RAIL_BRIDGE_CAPTION :{WHITE}Trenbide zubia aukeratu
|
||||
STR_SELECT_ROAD_BRIDGE_CAPTION :{WHITE}Errepide zubia aukeratu
|
||||
STR_SELECT_BRIDGE_SELECTION_TOOLTIP :{BLACK}Zubi aukerak - eraiki nahi duzun zubian klik egin
|
||||
STR_SELECT_BRIDGE_INFO :{GOLD}{STRING},{} {VELOCITY} {WHITE}{CURRENCY_LONG}
|
||||
STR_SELECT_BRIDGE_SCENEDIT_INFO :{GOLD}{STRING},{} {VELOCITY}
|
||||
STR_BRIDGE_NAME_SUSPENSION_STEEL :Altzairuzko zubi esekia
|
||||
STR_BRIDGE_NAME_GIRDER_STEEL :Altzairuzko habe zubia
|
||||
STR_BRIDGE_NAME_CANTILEVER_STEEL :Altzairuzko mentsula zubia
|
||||
@@ -2876,6 +2875,10 @@ STR_SPRITE_ALIGNER_PREVIOUS_BUTTON :{BLACK}Aldez au
|
||||
STR_SPRITE_ALIGNER_PREVIOUS_TOOLTIP :{BLACK}Aurreko grafiko arruntera joan, pseudo/birkoloretu/grafiko tipoak desgaituz
|
||||
STR_SPRITE_ALIGNER_SPRITE_TOOLTIP :{BLACK}Orain aukeratutako grafikoaren aurkezpena. Alineazioa ez da kontua hartzen grafiko hau egiterakoan
|
||||
STR_SPRITE_ALIGNER_MOVE_TOOLTIP :{BLACK}Sprite-a mugitu ingurunean, X eta Y-ren desplazamenduak aldatuz. Ctrl+Klik sprite-a zortzi unitatero mugitzeko
|
||||
|
||||
###length 2
|
||||
|
||||
|
||||
STR_SPRITE_ALIGNER_RESET_BUTTON :{BLACK}Erlatiboa berezarri
|
||||
STR_SPRITE_ALIGNER_RESET_TOOLTIP :{BLACK}Momentuan dauden desplazamendu erlatiboak erreseteatu
|
||||
STR_SPRITE_ALIGNER_OFFSETS_ABS :{BLACK}X desplazamendua: {NUM}, Y desplazamendua: {NUM} (Absolutua)
|
||||
|
@@ -1456,6 +1456,7 @@ STR_CONFIG_SETTING_HORIZONTAL_POS_RIGHT :правару
|
||||
|
||||
STR_CONFIG_SETTING_MAXIMUM_INITIAL_LOAN :Максымальная сума пазыкі: {STRING}
|
||||
STR_CONFIG_SETTING_MAXIMUM_INITIAL_LOAN_HELPTEXT :Максымальная сума пазыкі для кампаніі (без уліку інфляцыі)
|
||||
###setting-zero-is-special
|
||||
|
||||
STR_CONFIG_SETTING_INTEREST_RATE :Адсоткавая стаўка: {STRING}
|
||||
STR_CONFIG_SETTING_INTEREST_RATE_HELPTEXT :Адсоткавая стаўка па пазыках; таксама кантралюе інфляцыю, калі тая ўключана
|
||||
@@ -2830,8 +2831,6 @@ STR_BUILD_SIGNAL_DRAG_SIGNALS_DENSITY_INCREASE_TOOLTIP :{BLACK}Павя
|
||||
STR_SELECT_RAIL_BRIDGE_CAPTION :{WHITE}Выберыце чыгуначны мост
|
||||
STR_SELECT_ROAD_BRIDGE_CAPTION :{WHITE}Выберыце аўтамабiльны мост
|
||||
STR_SELECT_BRIDGE_SELECTION_TOOLTIP :{BLACK}Выбар моста — пстрыкніце па малюнку моста, які Вы хочаце пабудаваць
|
||||
STR_SELECT_BRIDGE_INFO :{GOLD}{STRING},{} {VELOCITY} {WHITE}{CURRENCY_LONG}
|
||||
STR_SELECT_BRIDGE_SCENEDIT_INFO :{GOLD}{STRING},{} {VELOCITY}
|
||||
STR_BRIDGE_NAME_SUSPENSION_STEEL :Падвесны сталёвы
|
||||
STR_BRIDGE_NAME_GIRDER_STEEL :Бэлечны сталёвы
|
||||
STR_BRIDGE_NAME_CANTILEVER_STEEL :Кансольны сталёвы
|
||||
@@ -3404,6 +3403,10 @@ STR_SPRITE_ALIGNER_PREVIOUS_BUTTON :{BLACK}Папя
|
||||
STR_SPRITE_ALIGNER_PREVIOUS_TOOLTIP :{BLACK}Перайсьці да папярэдняга звычайнага спрайта, прапускаючы змяняючыя колер, шрыфтавыя, псэўдаспрайты. Пераход з пачатку сьпісу да апошняга спрайта.
|
||||
STR_SPRITE_ALIGNER_SPRITE_TOOLTIP :{BLACK}Прадстаўленьне выбранага спрайта. Выраўноўваньне не ўлічваецца пры прарысоўцы гэтага спрайта.
|
||||
STR_SPRITE_ALIGNER_MOVE_TOOLTIP :{BLACK}Рухайце спрайт, зьмяняючы зрушэньне па X і па Y. Ctrl+пстрычка, каб зрушыць спрайт на восем адзінак за раз
|
||||
|
||||
###length 2
|
||||
|
||||
|
||||
STR_SPRITE_ALIGNER_RESET_BUTTON :{BLACK}Скід зрушэння
|
||||
STR_SPRITE_ALIGNER_RESET_TOOLTIP :{BLACK}Скінуць значэнні адноснага зрушэння
|
||||
STR_SPRITE_ALIGNER_OFFSETS_ABS :{BLACK}Зрушэнне X: {NUM}; зрушэнне Y: {NUM} (абсалютнае)
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user