diff --git a/.github/workflows/ci-build.yml b/.github/workflows/ci-build.yml index 86d915ba3a..0a97098251 100644 --- a/.github/workflows/ci-build.yml +++ b/.github/workflows/ci-build.yml @@ -88,11 +88,11 @@ jobs: echo "::group::Install dependencies" sudo apt-get install -y --no-install-recommends \ + liballegro4-dev \ libfontconfig-dev \ libicu-dev \ liblzma-dev \ liblzo2-dev \ - libsdl1.2-dev \ libsdl2-dev \ zlib1g-dev \ # EOF @@ -154,8 +154,9 @@ jobs: uses: lukka/run-vcpkg@v6 with: vcpkgDirectory: '/usr/local/share/vcpkg' - doNotUpdateVcpkg: true - vcpkgArguments: 'freetype liblzma lzo' + doNotUpdateVcpkg: false + vcpkgGitCommitId: 2a42024b53ebb512fb5dd63c523338bf26c8489c + vcpkgArguments: 'liblzma libpng lzo' vcpkgTriplet: '${{ matrix.arch }}-osx' - name: Install OpenGFX @@ -200,11 +201,10 @@ jobs: strategy: fail-fast: false matrix: - include: - - arch: x86 - - arch: x64 + os: [windows-latest, windows-2016] + arch: [x86, x64] - runs-on: windows-latest + runs-on: ${{ matrix.os }} steps: - name: Checkout diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4003787fc5..8829c43483 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -265,7 +265,95 @@ jobs: retention-days: 5 linux: - name: Linux + name: Linux (Generic) + needs: source + + 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@v2 + 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 \ + 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 + make -j$(nproc) + make 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" + make -j$(nproc) package + echo "::endgroup::" + + # Remove the sha256 files CPack generates; we will do this ourself at + # the end of this workflow. + rm -f bundles/*.sha256 + + - name: Store bundles + uses: actions/upload-artifact@v2 + with: + name: openttd-linux-generic + path: build/bundles + retention-days: 5 + + linux-distro: + name: Linux (Distros) needs: source if: needs.source.outputs.is_tag == 'true' @@ -373,6 +461,13 @@ jobs: 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 + # The following step can be removed when the build VM is updated with a revision of # vcpkg dating from roughly 01/01/2021 or later. At that point, `doNotUpdateVcpkg` # can be set to `true` and the `vcpkgGitCommitId` can be removed. @@ -387,7 +482,7 @@ jobs: vcpkgDirectory: '/usr/local/share/vcpkg' doNotUpdateVcpkg: false vcpkgGitCommitId: 2a42024b53ebb512fb5dd63c523338bf26c8489c - vcpkgArguments: 'freetype:x64-osx liblzma:x64-osx lzo:x64-osx freetype:arm64-osx liblzma:arm64-osx lzo:arm64-osx' + vcpkgArguments: 'liblzma:x64-osx libpng:x64-osx lzo:x64-osx liblzma:arm64-osx libpng:arm64-osx lzo:arm64-osx' - name: Build tools run: | @@ -550,6 +645,11 @@ jobs: run: | tar -xf source.tar.gz --strip-components=1 + - name: Install dependencies + shell: bash + run: | + choco install pandoc + # "restore-cache" which is done by "run-vcpkg" uses Windows tar. # A git clone on windows marks a few files as read-only; when Windows tar # tries to extract the cache over this folder, it fails, despite the files @@ -628,11 +728,12 @@ jobs: retention-days: 5 upload: - name: Upload + name: Upload (AWS) needs: - source - docs - linux + - linux-distro - macos - windows @@ -641,7 +742,7 @@ jobs: # "always()" is important here, it is the keyword to use to stop skipping # this job if any dependency is skipped. It looks a bit silly, but it is # how GitHub Actions work ;) - if: always() && needs.source.result == 'success' && needs.docs.result == 'success' && (needs.linux.result == 'success' || needs.linux.result == 'skipped') && needs.macos.result == 'success' && needs.windows.result == 'success' + if: always() && needs.source.result == 'success' && needs.docs.result == 'success' && needs.linux.result == 'success' && (needs.linux-distro.result == 'success' || needs.linux-distro.result == 'skipped') && needs.macos.result == 'success' && needs.windows.result == 'success' runs-on: ubuntu-20.04 @@ -685,3 +786,80 @@ jobs: repository: OpenTTD/workflows event-type: ${{ needs.source.outputs.trigger_type }} client-payload: '{"version": "${{ needs.source.outputs.version }}", "folder": "${{ needs.source.outputs.folder }}"}' + + upload-steam: + name: Upload (Steam) + needs: + - source + - linux + - macos + - windows + + if: needs.source.outputs.trigger_type == 'new-master' || needs.source.outputs.trigger_type == 'new-tag' + + runs-on: ubuntu-20.04 + + steps: + - name: Download all bundles + uses: actions/download-artifact@v2 + + - 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 [ "${{ needs.source.outputs.trigger_type }}" = "new-tag" ]; then + BRANCH="testing" + else + BRANCH="nightly" + fi + cat ../source/os/steam/release.vdf | sed 's/@@DESCRIPTION@@/openttd-${{ needs.source.outputs.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::" + ) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6164310b8f..f4a39072c5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.6) +cmake_minimum_required(VERSION 3.9) if(NOT BINARY_NAME) set(BINARY_NAME openttd) @@ -18,10 +18,13 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake") set(CMAKE_OSX_DEPLOYMENT_TARGET 10.9) # Use GNUInstallDirs to allow customisation -# but set our own default data dir +# but set our own default data and bin dir if(NOT CMAKE_INSTALL_DATADIR) set(CMAKE_INSTALL_DATADIR "share/games") endif() +if(NOT CMAKE_INSTALL_BINDIR) + set(CMAKE_INSTALL_BINDIR "games") +endif() include(GNUInstallDirs) include(Options) @@ -98,8 +101,8 @@ find_package(PNG) if(NOT WIN32) find_package(Allegro) - find_package(Freetype) if(NOT APPLE) + find_package(Freetype) find_package(SDL2) if(NOT SDL2_FOUND) find_package(SDL) @@ -113,6 +116,7 @@ if(NOT WIN32) find_library(AUDIOTOOLBOX_LIBRARY AudioToolbox) find_library(AUDIOUNIT_LIBRARY AudioUnit) find_library(COCOA_LIBRARY Cocoa) + find_library(QUARTZCORE_LIBRARY QuartzCore) find_package(MacUcontext) endif() @@ -156,13 +160,8 @@ find_package(Xaudio2) find_package(Grfcodec) -# IPO is only properly supported from CMake 3.9. Despite the fact we are -# CMake 3.5, still enable IPO if we detect we are 3.9+. -if(POLICY CMP0069) - cmake_policy(SET CMP0069 NEW) - include(CheckIPOSupported) - check_ipo_supported(RESULT IPO_FOUND) -endif() +include(CheckIPOSupported) +check_ipo_supported(RESULT IPO_FOUND) show_options() @@ -181,6 +180,30 @@ if(APPLE) if(NOT COCOA_LIBRARY) message(FATAL_ERROR "Cocoa is required for this platform") endif() + if(NOT QUARTZCORE_LIBRARY) + message(FATAL_ERROR "QuartzCore is required for this platform") + endif() +endif() + +if(OPTION_PACKAGE_DEPENDENCIES) + if(NOT UNIX) + message(FATAL_ERROR "Can only package dependencies on Linux") + endif() + if(OPTION_INSTALL_FHS) + message(FATAL_ERROR "Cannot install in FHS folders when we are packaging dependencies") + endif() + if(${CMAKE_VERSION} VERSION_LESS "3.16.0") + message(FATAL_ERROR "OPTION_PACKAGE_DEPENDENCIES can only work with CMake 3.16+; you are using ${CMAKE_VERSION}") + endif() + + # If we are packaging dependencies, we do two things: + # 1) set the RPATH to include $ORIGIN/lib; $ORIGIN (that literal string) + # is a Linux indicator for "path where application is". In CMake, we + # have to do this before add_executable() is executed. + # 2) copy the libraries that we compile against to the "lib" folder. + # This is done in InstallAndPackage.cmake. + set(CMAKE_INSTALL_RPATH "\$ORIGIN/lib") + set(CMAKE_BUILD_WITH_INSTALL_RPATH ON) endif() include(SourceList) @@ -226,6 +249,11 @@ else() ) endif() +if(MSVC) + # Add DPI manifest to project; other WIN32 targets get this via ottdres.rc + target_sources(openttd PRIVATE "${CMAKE_SOURCE_DIR}/os/windows/openttd.manifest") +endif() + add_subdirectory(${CMAKE_SOURCE_DIR}/src) add_subdirectory(${CMAKE_SOURCE_DIR}/media/baseset) add_subdirectory(${CMAKE_SOURCE_DIR}/bin) @@ -285,6 +313,7 @@ if(APPLE) ${AUDIOTOOLBOX_LIBRARY} ${AUDIOUNIT_LIBRARY} ${COCOA_LIBRARY} + ${QUARTZCORE_LIBRARY} ) add_definitions( @@ -373,6 +402,10 @@ endif() include(CreateRegression) create_regression() +if(APPLE OR WIN32) + find_package(Pandoc) +endif() + include(InstallAndPackage) get_property(CFG_DEFS DIRECTORY . PROPERTY COMPILE_OPTIONS) diff --git a/COMPILING.md b/COMPILING.md index 145b244753..5e642dc874 100644 --- a/COMPILING.md +++ b/COMPILING.md @@ -57,6 +57,9 @@ To install both the x64 (64bit) and x86 (32bit) variants (though only one is nec ``` You can open the folder (as a CMake project). CMake will be detected, and you can compile from there. +If libraries are installed but not found, you need to set VCPKG_TARGET_TRIPLET in CMake parameters. +For Visual Studio 2017 you also need to set CMAKE_TOOLCHAIN_FILE. +(Typical values are shown in the MSVC project file command line example) Alternatively, you can create a MSVC project file via CMake. For this either download CMake from https://cmake.org/download/ or use the version @@ -73,6 +76,7 @@ in the build folder are MSVC project files. MSVC can rebuild the project files himself via the `ZERO_CHECK` project. ## All other platforms +Minimum required version of CMake is 3.9. ```bash mkdir build diff --git a/cmake/CompileFlags.cmake b/cmake/CompileFlags.cmake index 42a0891e01..cc4b5baef6 100644 --- a/cmake/CompileFlags.cmake +++ b/cmake/CompileFlags.cmake @@ -27,9 +27,6 @@ macro(compile_flags) # Enable multi-threaded compilation. add_compile_options(/MP) endif() - - # Add DPI manifest to project; other WIN32 targets get this via ottdres.rc - list(APPEND GENERATED_SOURCE_FILES "${CMAKE_SOURCE_DIR}/os/windows/openttd.manifest") endif() # Add some -D flags for Debug builds. We cannot use add_definitions(), because diff --git a/cmake/FindAllegro.cmake b/cmake/FindAllegro.cmake index 3c90d2c4e7..5d873dd3e1 100644 --- a/cmake/FindAllegro.cmake +++ b/cmake/FindAllegro.cmake @@ -31,7 +31,7 @@ The following cache variables may also be set: #]=======================================================================] find_package(PkgConfig QUIET) -pkg_check_modules(PC_Allegro QUIET allegro) +pkg_check_modules(PC_Allegro QUIET allegro<5) find_path(Allegro_INCLUDE_DIR NAMES allegro.h diff --git a/cmake/FindICU.cmake b/cmake/FindICU.cmake index b110dbf404..d12f36b0ad 100644 --- a/cmake/FindICU.cmake +++ b/cmake/FindICU.cmake @@ -1,3 +1,8 @@ +# CMake provides a FindICU module since version 3.7. +# But it doesn't use pkgconfig, doesn't set expected variables, +# And it returns incomplete dependencies if only some modules are searched. + + #[=======================================================================[.rst: FindICU ------- diff --git a/cmake/FindPandoc.cmake b/cmake/FindPandoc.cmake new file mode 100644 index 0000000000..3a42851dd7 --- /dev/null +++ b/cmake/FindPandoc.cmake @@ -0,0 +1,3 @@ +if(NOT EXISTS ${PANDOC_EXECUTABLE}) + find_program(PANDOC_EXECUTABLE pandoc) +endif() diff --git a/cmake/InstallAndPackage.cmake b/cmake/InstallAndPackage.cmake index de36f74a05..1e666a7b4f 100644 --- a/cmake/InstallAndPackage.cmake +++ b/cmake/InstallAndPackage.cmake @@ -96,7 +96,14 @@ set(CPACK_PACKAGE_HOMEPAGE_URL "https://www.openttd.org/") set(CPACK_PACKAGE_CONTACT "OpenTTD ") set(CPACK_PACKAGE_INSTALL_DIRECTORY "OpenTTD") set(CPACK_PACKAGE_CHECKSUM "SHA256") -set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_SOURCE_DIR}/COPYING.md") + +if((APPLE OR WIN32) AND EXISTS ${PANDOC_EXECUTABLE}) + execute_process(COMMAND ${PANDOC_EXECUTABLE} "${CMAKE_SOURCE_DIR}/COPYING.md" -s -o "${CMAKE_BINARY_DIR}/COPYING.rtf") + set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_BINARY_DIR}/COPYING.rtf") +else() + set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_SOURCE_DIR}/COPYING.md") +endif() + set(CPACK_RESOURCE_FILE_README "${CMAKE_SOURCE_DIR}/README.md") set(CPACK_MONOLITHIC_INSTALL YES) set(CPACK_PACKAGE_EXECUTABLES "openttd;OpenTTD") @@ -124,8 +131,12 @@ elseif(UNIX) # With FHS, we can create deb/rpm/... Without it, they would be horribly broken # and not work. The other way around is also true; with FHS they are not # usable, and only flat formats work. - if(NOT OPTION_INSTALL_FHS) + if(OPTION_PACKAGE_DEPENDENCIES) + set(CPACK_GENERATOR "TXZ") + set(PLATFORM "generic") + elseif(NOT OPTION_INSTALL_FHS) set(CPACK_GENERATOR "TXZ") + set(PLATFORM "unknown") else() find_program(LSB_RELEASE_EXEC lsb_release) execute_process(COMMAND ${LSB_RELEASE_EXEC} -is @@ -150,7 +161,7 @@ elseif(UNIX) string(REGEX MATCH "ID=(.*)" _ ${OS_RELEASE_CONTENTS}) set(DISTRO_ID ${CMAKE_MATCH_1}) if(DISTRO_ID STREQUAL "arch") - set(PLATFORM "generic") + set(PLATFORM "arch") set(CPACK_GENERATOR "TXZ") else() set(UNSUPPORTED_PLATFORM_NAME "Linux distribution '${DISTRO_ID}' from /etc/os-release") @@ -164,12 +175,39 @@ elseif(UNIX) set(CPACK_GENERATOR "TXZ") message(WARNING "Unknown ${UNSUPPORTED_PLATFORM_NAME} found for packaging; can only pack to a txz. Please consider creating a Pull Request to add support for this distribution.") endif() - - set(CPACK_PACKAGE_FILE_NAME "openttd-#CPACK_PACKAGE_VERSION#-linux-${PLATFORM}-${CPACK_SYSTEM_NAME}") endif() + set(CPACK_PACKAGE_FILE_NAME "openttd-#CPACK_PACKAGE_VERSION#-linux-${PLATFORM}-${CPACK_SYSTEM_NAME}") + else() message(FATAL_ERROR "Unknown OS found for packaging; please consider creating a Pull Request to add support for this OS.") endif() include(CPack) + +if(OPTION_PACKAGE_DEPENDENCIES) + # Install all dependencies we can resolve, with the exception of ones that + # every Linux machine should have. This should make this build as generic + # as possible, where it runs on any machine with the same or newer libc + # than the one this is compiled with. + # We copy these libraries into lib/ folder, so they can be found on game + # startup. See comment in root CMakeLists.txt for how this works exactly. + install(CODE [[ + file(GET_RUNTIME_DEPENDENCIES + RESOLVED_DEPENDENCIES_VAR DEPENDENCIES + UNRESOLVED_DEPENDENCIES_VAR UNRESOLVED_DEPENDENCIES + EXECUTABLES openttd + POST_EXCLUDE_REGEXES "ld-linux|libc.so|libdl.so|libm.so|libgcc_s.so|libpthread.so|librt.so|libstdc...so") + file(INSTALL + DESTINATION "${CMAKE_INSTALL_PREFIX}/lib" + FILES ${DEPENDENCIES} + FOLLOW_SYMLINK_CHAIN) + + # This should not be possible, but error out when a dependency cannot + # be resolved. + list(LENGTH UNRESOLVED_DEPENDENCIES UNRESOLVED_LENGTH) + if(${UNRESOLVED_LENGTH} GREATER 0) + message(FATAL_ERROR "Unresolved dependencies: ${UNRESOLVED_DEPENDENCIES}") + endif() + ]]) +endif() diff --git a/cmake/Options.cmake b/cmake/Options.cmake index c94a193b32..42d1127906 100644 --- a/cmake/Options.cmake +++ b/cmake/Options.cmake @@ -44,7 +44,9 @@ endfunction() # set_options() # function(set_options) - if(UNIX AND NOT APPLE) + option(OPTION_PACKAGE_DEPENDENCIES "Copy dependencies into lib/ for easy packaging (Linux only)" OFF) + + if(UNIX AND NOT APPLE AND NOT OPTION_PACKAGE_DEPENDENCIES) set(DEFAULT_OPTION_INSTALL_FHS ON) else() set(DEFAULT_OPTION_INSTALL_FHS OFF) @@ -76,6 +78,7 @@ endfunction() # show_options() # function(show_options) + message(STATUS "Option Package Dependencies - ${OPTION_PACKAGE_DEPENDENCIES}") message(STATUS "Option Dedicated - ${OPTION_DEDICATED}") message(STATUS "Option Install FHS - ${OPTION_INSTALL_FHS}") message(STATUS "Option Use assert - ${OPTION_USE_ASSERTS}") diff --git a/known-bugs.txt b/known-bugs.txt index a687dff4a5..ddfe7aa0c1 100644 --- a/known-bugs.txt +++ b/known-bugs.txt @@ -409,3 +409,12 @@ Involuntary cargo exchange with cargodist via neutral station [#6114]: shared station make the order "no unload" and if you're unloading make it "no load". Cargodist will then figure out that it should not create such a route. + +Incorrect ending year displayed in end of game newspaper [#8625] + The ending year of the game is configurable, but the date displayed in + the newspaper at the end of the game is part of the graphics, not text. + So to fix this would involve fixing the graphics in every baseset, + including the original. Additionally, basesets are free to put this + text in different positions (which they do), making a proper solution + to this infinitely more complex for a part of the game that fewer than + 1% of players ever see. diff --git a/os/emscripten/pre.js b/os/emscripten/pre.js index 5cbd899e04..1563e4f95b 100644 --- a/os/emscripten/pre.js +++ b/os/emscripten/pre.js @@ -71,6 +71,34 @@ Module.preRun.push(function() { * add_server("localhost", 3979); */ } + var leftButtonDown = false; + document.addEventListener("mousedown", e => { + if (e.button == 0) { + leftButtonDown = true; + } + }); + document.addEventListener("mouseup", e => { + if (e.button == 0) { + leftButtonDown = false; + } + }); + window.openttd_open_url = function(url, url_len) { + const url_string = UTF8ToString(url, url_len); + function openWindow() { + document.removeEventListener("mouseup", openWindow); + window.open(url_string, '_blank'); + } + /* Trying to open the URL while the mouse is down results in the button getting stuck, so wait for the + * mouse to be released before opening it. However, when OpenTTD is lagging, the mouse can get released + * before the button click even registers, so check for that, and open the URL immediately if that's the + * case. */ + if (leftButtonDown) { + document.addEventListener("mouseup", openWindow); + } else { + openWindow(); + } + } + /* https://github.com/emscripten-core/emscripten/pull/12995 implements this * properly. Till that time, we use a polyfill. */ SOCKFS.websocket_sock_ops.createPeer_ = SOCKFS.websocket_sock_ops.createPeer; diff --git a/os/macosx/Info.plist.in b/os/macosx/Info.plist.in index 04d6ea6d33..359d28de8d 100644 --- a/os/macosx/Info.plist.in +++ b/os/macosx/Info.plist.in @@ -29,5 +29,7 @@ Copyright 2004-${CURRENT_YEAR} The OpenTTD team NSPrincipalClass NSApplication + NSHighResolutionCapable + True diff --git a/os/steam/release.vdf b/os/steam/release.vdf new file mode 100644 index 0000000000..ab549c3886 --- /dev/null +++ b/os/steam/release.vdf @@ -0,0 +1,57 @@ +"AppBuild" +{ + "AppID" "1536610" + "Desc" "@@DESCRIPTION@@" + + "SetLive" "@@BRANCH@@" + + "ContentRoot" "./" + "BuildOutput" "build/" + + "Depots" + { + "1536613" + { + "ContentRoot" "./steam-win32" + "FileMapping" + { + "LocalPath" "*" + "DepotPath" "." + "recursive" "1" + } + } + + "1536612" + { + "ContentRoot" "./steam-win64" + "FileMapping" + { + "LocalPath" "*" + "DepotPath" "." + "recursive" "1" + } + } + + "1536614" + { + "ContentRoot" "./steam-macos" + "FileMapping" + { + "LocalPath" "*" + "DepotPath" "." + "recursive" "1" + } + } + + "1536615" + { + "ContentRoot" "./steam-linux" + "FileMapping" + { + "LocalPath" "*" + "DepotPath" "." + "recursive" "1" + } + } + } +} diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 449b225eae..660b283737 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -164,7 +164,7 @@ add_files( fios_gui.cpp fontcache.cpp fontcache.h - fontdetection.cpp + fontcache_internal.h fontdetection.h framerate_gui.cpp framerate_type.h diff --git a/src/bootstrap_gui.cpp b/src/bootstrap_gui.cpp index a0f040d1aa..7ec391b47d 100644 --- a/src/bootstrap_gui.cpp +++ b/src/bootstrap_gui.cpp @@ -11,7 +11,7 @@ #include "base_media_base.h" #include "blitter/factory.hpp" -#if defined(WITH_FREETYPE) || defined(WITH_UNISCRIBE) +#if defined(WITH_FREETYPE) || defined(WITH_UNISCRIBE) || defined(WITH_COCOA) #include "core/geometry_func.hpp" #include "fontcache.h" @@ -218,7 +218,7 @@ bool HandleBootstrap() if (BlitterFactory::GetCurrentBlitter()->GetScreenDepth() == 0) goto failure; /* If there is no network or no freetype, then there is nothing we can do. Go straight to failure. */ -#if (defined(_WIN32) && defined(WITH_UNISCRIBE)) || (defined(WITH_FREETYPE) && (defined(WITH_FONTCONFIG) || defined(__APPLE__))) +#if (defined(_WIN32) && defined(WITH_UNISCRIBE)) || (defined(WITH_FREETYPE) && (defined(WITH_FONTCONFIG) || defined(__APPLE__))) || defined(WITH_COCOA) if (!_network_available) goto failure; /* First tell the game we're bootstrapping. */ diff --git a/src/company_cmd.cpp b/src/company_cmd.cpp index 374eba6619..34fa29be09 100644 --- a/src/company_cmd.cpp +++ b/src/company_cmd.cpp @@ -36,6 +36,7 @@ #include "story_base.h" #include "zoning.h" #include "tbtr_template_vehicle_func.h" +#include "widgets/statusbar_widget.h" #include "table/strings.h" @@ -193,7 +194,7 @@ void InvalidateCompanyWindows(const Company *company) { CompanyID cid = company->index; - if (cid == _local_company) SetWindowWidgetDirty(WC_STATUS_BAR, 0, 2); + if (cid == _local_company) SetWindowWidgetDirty(WC_STATUS_BAR, 0, WID_S_RIGHT); SetWindowDirty(WC_FINANCES, cid); } diff --git a/src/console_cmds.cpp b/src/console_cmds.cpp index ebf9dd204a..403edf193d 100644 --- a/src/console_cmds.cpp +++ b/src/console_cmds.cpp @@ -1324,7 +1324,7 @@ DEF_CONSOLE_CMD(ConReloadAI) } /* First kill the company of the AI, then start a new one. This should start the current AI again */ - DoCommandP(0, CCA_DELETE | company_id << 16 | CRR_MANUAL << 24, 0,CMD_COMPANY_CTRL); + DoCommandP(0, CCA_DELETE | company_id << 16 | CRR_MANUAL << 24, 0, CMD_COMPANY_CTRL); DoCommandP(0, CCA_NEW_AI | company_id << 16, 0, CMD_COMPANY_CTRL); IConsolePrint(CC_DEFAULT, "AI reloaded."); diff --git a/src/core/geometry_func.cpp b/src/core/geometry_func.cpp index 3403e39b00..e6670422ed 100644 --- a/src/core/geometry_func.cpp +++ b/src/core/geometry_func.cpp @@ -40,3 +40,25 @@ Dimension adddim(const Dimension &d1, const Dimension &d2) d.height = d1.height + d2.height; return d; } + +/** + * Compute the bounding rectangle around two rectangles. + * @param r1 First rectangle. + * @param r2 Second rectangle. + * @return The bounding rectangle, the smallest rectangle that contains both arguments. + */ +Rect BoundingRect(const Rect &r1, const Rect &r2) +{ + /* If either the first or the second is empty, return the other. */ + if (IsEmptyRect(r1)) return r2; + if (IsEmptyRect(r2)) return r1; + + Rect r; + + r.top = std::min(r1.top, r2.top); + r.bottom = std::max(r1.bottom, r2.bottom); + r.left = std::min(r1.left, r2.left); + r.right = std::max(r1.right, r2.right); + + return r; +} diff --git a/src/core/geometry_func.hpp b/src/core/geometry_func.hpp index 09d9d08b12..fd3473d3bf 100644 --- a/src/core/geometry_func.hpp +++ b/src/core/geometry_func.hpp @@ -15,4 +15,16 @@ Dimension maxdim(const Dimension &d1, const Dimension &d2); Dimension adddim(const Dimension &d1, const Dimension &d2); +/** + * Check if a rectangle is empty. + * @param r Rectangle to check. + * @return True if and only if the rectangle doesn't define space. + */ +static inline bool IsEmptyRect(const Rect &r) +{ + return (r.left | r.top | r.right | r.bottom) == 0; +} + +Rect BoundingRect(const Rect &r1, const Rect &r2); + #endif /* GEOMETRY_FUNC_HPP */ diff --git a/src/currency.cpp b/src/currency.cpp index 5c758c48df..46c478d0d9 100644 --- a/src/currency.cpp +++ b/src/currency.cpp @@ -66,6 +66,7 @@ static const CurrencySpec origin_currency_specs[CURRENCY_END] = { { 8, "", CF_NOEURO, u8"\u00a5", "", 0, STR_GAME_OPTIONS_CURRENCY_CNY }, ///< chinese renminbi { 10, "", CF_NOEURO, "HKD" NBSP, "", 0, STR_GAME_OPTIONS_CURRENCY_HKD }, ///< hong kong dollar { 90, "", CF_NOEURO, u8"\u20b9", "", 0, STR_GAME_OPTIONS_CURRENCY_INR }, ///< Indian Rupee + { 19, "", CF_NOEURO, "Rp", "", 0, STR_GAME_OPTIONS_CURRENCY_IDR }, ///< Indonesian Rupiah }; /** Array of currencies used by the system */ diff --git a/src/currency.h b/src/currency.h index c4c3db0249..37d1c66b92 100644 --- a/src/currency.h +++ b/src/currency.h @@ -11,6 +11,7 @@ #define CURRENCY_H #include "date_type.h" +#include "string_func.h" #include "strings_type.h" static const int CF_NOEURO = 0; ///< Currency never switches to the Euro (as far as known). @@ -62,6 +63,7 @@ enum Currencies { CURRENCY_CNY, ///< Chinese Renminbi CURRENCY_HKD, ///< Hong Kong Dollar CURRENCY_INR, ///< Indian Rupee + CURRENCY_IDR, ///< Indonesian Rupiah CURRENCY_END, ///< always the last item }; @@ -83,6 +85,15 @@ struct CurrencySpec { */ byte symbol_pos; StringID name; + + CurrencySpec() = default; + + CurrencySpec(uint16 rate, const char *separator, Year to_euro, const char *prefix, const char *suffix, byte symbol_pos, StringID name) : rate(rate), to_euro(to_euro), symbol_pos(symbol_pos), name(name) + { + strecpy(this->separator, separator, lastof(this->separator)); + strecpy(this->prefix, prefix, lastof(this->prefix)); + strecpy(this->suffix, suffix, lastof(this->suffix)); + } }; extern CurrencySpec _currency_specs[CURRENCY_END]; diff --git a/src/date.cpp b/src/date.cpp index de84c967bb..c8bb6be88e 100644 --- a/src/date.cpp +++ b/src/date.cpp @@ -21,6 +21,7 @@ #include "newgrf_profiling.h" #include "console_func.h" #include "debug.h" +#include "widgets/statusbar_widget.h" #include "safeguards.h" @@ -275,7 +276,7 @@ static void OnNewDay() IndustryDailyLoop(); if (!_settings_time.time_in_minutes || _settings_client.gui.date_with_time > 0) { - SetWindowWidgetDirty(WC_STATUS_BAR, 0, 0); + SetWindowWidgetDirty(WC_STATUS_BAR, 0, WID_S_LEFT); } EnginesDailyLoop(); diff --git a/src/debug.h b/src/debug.h index d14fa7b06f..9e54184357 100644 --- a/src/debug.h +++ b/src/debug.h @@ -134,6 +134,15 @@ const char *GetLogPrefix(); /** The real time in the game. */ extern uint32 _realtime_tick; +inline void IncreaseRealtimeTick(uint32 increase) +{ +#if defined(__GNUC__) || defined(__clang__) + __atomic_add_fetch(&_realtime_tick, increase, __ATOMIC_RELAXED); +#else + _realtime_tick += increase; +#endif +} + void ClearDesyncMsgLog(); void LogDesyncMsg(std::string msg); char *DumpDesyncMsgLog(char *buffer, const char *last); diff --git a/src/fios.cpp b/src/fios.cpp index bf621f4d2d..4e4fce82b9 100644 --- a/src/fios.cpp +++ b/src/fios.cpp @@ -17,6 +17,7 @@ #include "network/network_content.h" #include "screenshot.h" #include "string_func.h" +#include "strings_func.h" #include "tar_type.h" #include #include @@ -376,7 +377,8 @@ static void FiosGetFileList(SaveLoadOperation fop, fios_getlist_callback_proc *c fios->type = FIOS_TYPE_PARENT; fios->mtime = 0; strecpy(fios->name, "..", lastof(fios->name)); - strecpy(fios->title, ".. (Parent directory)", lastof(fios->title)); + SetDParamStr(0, ".."); + GetString(fios->title, STR_SAVELOAD_PARENT_DIRECTORY, lastof(fios->title)); } /* Show subdirectories */ @@ -392,7 +394,9 @@ static void FiosGetFileList(SaveLoadOperation fop, fios_getlist_callback_proc *c fios->type = FIOS_TYPE_DIR; fios->mtime = 0; strecpy(fios->name, d_name, lastof(fios->name)); - seprintf(fios->title, lastof(fios->title), "%s" PATHSEP " (Directory)", d_name); + std::string dirname = std::string(d_name) + PATHSEP; + SetDParamStr(0, dirname.c_str()); + GetString(fios->title, STR_SAVELOAD_DIRECTORY, lastof(fios->title)); str_validate(fios->title, lastof(fios->title)); } } diff --git a/src/fontcache.cpp b/src/fontcache.cpp index 18e7eb39d7..329bd151f4 100644 --- a/src/fontcache.cpp +++ b/src/fontcache.cpp @@ -9,6 +9,7 @@ #include "stdafx.h" #include "fontcache.h" +#include "fontcache_internal.h" #include "fontdetection.h" #include "blitter/factory.hpp" #include "core/math_func.hpp" @@ -31,6 +32,8 @@ static const int ASCII_LETTERSTART = 32; ///< First printable ASCII letter. static const int _default_font_height[FS_END] = {10, 6, 18, 10}; static const int _default_font_ascender[FS_END] = { 8, 5, 15, 8}; +FreeTypeSettings _freetype; + /** * Create a new font cache. * @param fs The size of the font. @@ -53,6 +56,11 @@ FontCache::~FontCache() Layouter::ResetFontCache(this->fs); } +int FontCache::GetDefaultFontHeight(FontSize fs) +{ + return _default_font_height[fs]; +} + /** Font cache for fonts that are based on a freetype font. */ class SpriteFontCache : public FontCache { private: @@ -195,73 +203,6 @@ void UpdateFontHeightCache() } } -#if defined(WITH_FREETYPE) || defined(_WIN32) - -FreeTypeSettings _freetype; - -static const int MAX_FONT_SIZE = 72; ///< Maximum font size. - -static const byte FACE_COLOUR = 1; -static const byte SHADOW_COLOUR = 2; - -/** Font cache for fonts that are based on a TrueType font. */ -class TrueTypeFontCache : public FontCache { -protected: - int req_size; ///< Requested font size. - int used_size; ///< Used font size. - - typedef SmallMap > FontTable; ///< Table with font table cache - FontTable font_tables; ///< Cached font tables. - - /** Container for information about a glyph. */ - struct GlyphEntry { - Sprite *sprite; ///< The loaded sprite. - byte width; ///< The width of the glyph. - bool duplicate; ///< Whether this glyph entry is a duplicate, i.e. may this be freed? - }; - - /** - * The glyph cache. This is structured to reduce memory consumption. - * 1) There is a 'segment' table for each font size. - * 2) Each segment table is a discrete block of characters. - * 3) Each block contains 256 (aligned) characters sequential characters. - * - * The cache is accessed in the following way: - * For character 0x0041 ('A'): glyph_to_sprite[0x00][0x41] - * For character 0x20AC (Euro): glyph_to_sprite[0x20][0xAC] - * - * Currently only 256 segments are allocated, "limiting" us to 65536 characters. - * This can be simply changed in the two functions Get & SetGlyphPtr. - */ - GlyphEntry **glyph_to_sprite; - - GlyphEntry *GetGlyphPtr(GlyphID key); - void SetGlyphPtr(GlyphID key, const GlyphEntry *glyph, bool duplicate = false); - - virtual const void *InternalGetFontTable(uint32 tag, size_t &length) = 0; - virtual const Sprite *InternalGetGlyph(GlyphID key, bool aa) = 0; - -public: - TrueTypeFontCache(FontSize fs, int pixels); - virtual ~TrueTypeFontCache(); - virtual int GetFontSize() const { return this->used_size; } - virtual SpriteID GetUnicodeGlyph(WChar key) { return this->parent->GetUnicodeGlyph(key); } - virtual void SetUnicodeGlyph(WChar key, SpriteID sprite) { this->parent->SetUnicodeGlyph(key, sprite); } - - virtual void InitializeUnicodeGlyphMap() - { - this->parent->InitializeUnicodeGlyphMap(); - font_height_cache[this->fs] = this->GetHeight(); - } - - virtual const Sprite *GetGlyph(GlyphID key); - virtual const void *GetFontTable(uint32 tag, size_t &length); - virtual void ClearFontCache(); - virtual uint GetGlyphWidth(GlyphID key); - virtual bool GetDrawGlyphShadow(); - virtual bool IsBuiltInFont() { return false; } -}; - /** * Create a new TrueTypeFontCache. * @param fs The font size that is going to be cached. @@ -333,7 +274,7 @@ void TrueTypeFontCache::SetGlyphPtr(GlyphID key, const GlyphEntry *glyph, bool d this->glyph_to_sprite[GB(key, 8, 8)][GB(key, 0, 8)].duplicate = duplicate; } -static void *AllocateFont(size_t size) +void *AllocateFont(size_t size) { return MallocT(size); } @@ -496,7 +437,7 @@ void FreeTypeFontCache::SetFontSize(FontSize fs, FT_Face face, int pixels) /* Font height is minimum height plus the difference between the default * height for this font size and the small size. */ int diff = scaled_height - ScaleFontTrad(_default_font_height[FS_SMALL]); - pixels = Clamp(std::min(head->Lowest_Rec_PPEM, 20u) + diff, scaled_height, MAX_FONT_SIZE); + pixels = Clamp(std::min(head->Lowest_Rec_PPEM, MAX_FONT_MIN_REC_SIZE) + diff, scaled_height, MAX_FONT_SIZE); } } else { pixels = ScaleFontTrad(pixels); @@ -666,7 +607,7 @@ const Sprite *FreeTypeFontCache::InternalGetGlyph(GlyphID key, bool aa) uint height = std::max(1U, (uint)slot->bitmap.rows + (this->fs == FS_NORMAL)); /* Limit glyph size to prevent overflows later on. */ - if (width > 256 || height > 256) usererror("Font glyph is too large"); + if (width > MAX_GLYPH_DIM || height > MAX_GLYPH_DIM) usererror("Font glyph is too large"); /* FreeType has rendered the glyph, now we allocate a sprite and copy the image into it */ SpriteLoader::Sprite sprite; @@ -735,338 +676,8 @@ const void *FreeTypeFontCache::InternalGetFontTable(uint32 tag, size_t &length) return result; } -#elif defined(_WIN32) - -#include "os/windows/win32.h" -#ifndef ANTIALIASED_QUALITY -#define ANTIALIASED_QUALITY 4 -#endif - -/** Font cache for fonts that are based on a Win32 font. */ -class Win32FontCache : public TrueTypeFontCache { -private: - LOGFONT logfont; ///< Logical font information for selecting the font face. - HFONT font = nullptr; ///< The font face associated with this font. - HDC dc = nullptr; ///< Cached GDI device context. - HGDIOBJ old_font; ///< Old font selected into the GDI context. - SIZE glyph_size; ///< Maximum size of regular glyphs. - - void SetFontSize(FontSize fs, int pixels); - virtual const void *InternalGetFontTable(uint32 tag, size_t &length); - virtual const Sprite *InternalGetGlyph(GlyphID key, bool aa); - -public: - Win32FontCache(FontSize fs, const LOGFONT &logfont, int pixels); - ~Win32FontCache(); - virtual void ClearFontCache(); - virtual GlyphID MapCharToGlyph(WChar key); - virtual const char *GetFontName() { return WIDE_TO_MB(this->logfont.lfFaceName); } - virtual bool IsBuiltInFont() { return false; } - virtual void *GetOSHandle() { return &this->logfont; } -}; - - -/** - * Create a new Win32FontCache. - * @param fs The font size that is going to be cached. - * @param logfont The font that has to be loaded. - * @param pixels The number of pixels this font should be high. - */ -Win32FontCache::Win32FontCache(FontSize fs, const LOGFONT &logfont, int pixels) : TrueTypeFontCache(fs, pixels), logfont(logfont) -{ - this->dc = CreateCompatibleDC(nullptr); - this->SetFontSize(fs, pixels); -} - -Win32FontCache::~Win32FontCache() -{ - this->ClearFontCache(); - DeleteDC(this->dc); - DeleteObject(this->font); -} - -void Win32FontCache::SetFontSize(FontSize fs, int pixels) -{ - if (pixels == 0) { - /* Try to determine a good height based on the minimal height recommended by the font. */ - int scaled_height = ScaleFontTrad(_default_font_height[this->fs]); - pixels = scaled_height; - - HFONT temp = CreateFontIndirect(&this->logfont); - if (temp != nullptr) { - HGDIOBJ old = SelectObject(this->dc, temp); - - UINT size = GetOutlineTextMetrics(this->dc, 0, nullptr); - LPOUTLINETEXTMETRIC otm = (LPOUTLINETEXTMETRIC)AllocaM(BYTE, size); - GetOutlineTextMetrics(this->dc, size, otm); - - /* Font height is minimum height plus the difference between the default - * height for this font size and the small size. */ - int diff = scaled_height - ScaleFontTrad(_default_font_height[FS_SMALL]); - pixels = Clamp(std::min(otm->otmusMinimumPPEM, 20u) + diff, scaled_height, MAX_FONT_SIZE); - - SelectObject(dc, old); - DeleteObject(temp); - } - } else { - pixels = ScaleFontTrad(pixels); - } - this->used_size = pixels; - - /* Create GDI font handle. */ - this->logfont.lfHeight = -pixels; - this->logfont.lfWidth = 0; - this->logfont.lfOutPrecision = ANTIALIASED_QUALITY; - - if (this->font != nullptr) { - SelectObject(dc, this->old_font); - DeleteObject(this->font); - } - this->font = CreateFontIndirect(&this->logfont); - this->old_font = SelectObject(this->dc, this->font); - - /* Query the font metrics we needed. */ - UINT otmSize = GetOutlineTextMetrics(this->dc, 0, nullptr); - POUTLINETEXTMETRIC otm = (POUTLINETEXTMETRIC)AllocaM(BYTE, otmSize); - GetOutlineTextMetrics(this->dc, otmSize, otm); - - this->units_per_em = otm->otmEMSquare; - this->ascender = otm->otmTextMetrics.tmAscent; - this->descender = otm->otmTextMetrics.tmDescent; - this->height = this->ascender + this->descender; - this->glyph_size.cx = otm->otmTextMetrics.tmMaxCharWidth; - this->glyph_size.cy = otm->otmTextMetrics.tmHeight; - - font_height_cache[this->fs] = this->GetHeight(); - - DEBUG(freetype, 2, "Loaded font '%s' with size %d", FS2OTTD((LPTSTR)((BYTE *)otm + (ptrdiff_t)otm->otmpFullName)), pixels); -} - -/** - * Reset cached glyphs. - */ -void Win32FontCache::ClearFontCache() -{ - /* GUI scaling might have changed, determine font size anew if it was automatically selected. */ - if (this->font != nullptr) this->SetFontSize(this->fs, this->req_size); - - this->TrueTypeFontCache::ClearFontCache(); -} - -/* virtual */ const Sprite *Win32FontCache::InternalGetGlyph(GlyphID key, bool aa) -{ - GLYPHMETRICS gm; - MAT2 mat = { {0, 1}, {0, 0}, {0, 0}, {0, 1} }; - - /* Make a guess for the needed memory size. */ - DWORD size = this->glyph_size.cy * Align(aa ? this->glyph_size.cx : std::max(this->glyph_size.cx / 8l, 1l), 4); // Bitmap data is DWORD-aligned rows. - byte *bmp = AllocaM(byte, size); - size = GetGlyphOutline(this->dc, key, GGO_GLYPH_INDEX | (aa ? GGO_GRAY8_BITMAP : GGO_BITMAP), &gm, size, bmp, &mat); - - if (size == GDI_ERROR) { - /* No dice with the guess. First query size of needed glyph memory, then allocate the - * memory and query again. This dance is necessary as some glyphs will only render with - * the exact matching size; e.g. the space glyph has no pixels and must be requested - * with size == 0, anything else fails. Unfortunately, a failed call doesn't return any - * info about the size and thus the triple GetGlyphOutline()-call. */ - size = GetGlyphOutline(this->dc, key, GGO_GLYPH_INDEX | (aa ? GGO_GRAY8_BITMAP : GGO_BITMAP), &gm, 0, nullptr, &mat); - if (size == GDI_ERROR) usererror("Unable to render font glyph"); - bmp = AllocaM(byte, size); - GetGlyphOutline(this->dc, key, GGO_GLYPH_INDEX | (aa ? GGO_GRAY8_BITMAP : GGO_BITMAP), &gm, size, bmp, &mat); - } - - /* Add 1 pixel for the shadow on the medium font. Our sprite must be at least 1x1 pixel. */ - uint width = std::max(1U, (uint)gm.gmBlackBoxX + (this->fs == FS_NORMAL)); - uint height = std::max(1U, (uint)gm.gmBlackBoxY + (this->fs == FS_NORMAL)); - - /* Limit glyph size to prevent overflows later on. */ - if (width > 256 || height > 256) usererror("Font glyph is too large"); - - /* GDI has rendered the glyph, now we allocate a sprite and copy the image into it. */ - SpriteLoader::Sprite sprite; - sprite.AllocateData(ZOOM_LVL_NORMAL, width * height); - sprite.type = ST_FONT; - sprite.width = width; - sprite.height = height; - sprite.x_offs = gm.gmptGlyphOrigin.x; - sprite.y_offs = this->ascender - gm.gmptGlyphOrigin.y; - - if (size > 0) { - /* All pixel data returned by GDI is in the form of DWORD-aligned rows. - * For a non anti-aliased glyph, the returned bitmap has one bit per pixel. - * For anti-aliased rendering, GDI uses the strange value range of 0 to 64, - * inclusively. To map this to 0 to 255, we shift left by two and then - * subtract one. */ - uint pitch = Align(aa ? gm.gmBlackBoxX : std::max(gm.gmBlackBoxX / 8u, 1u), 4); - - /* Draw shadow for medium size. */ - if (this->fs == FS_NORMAL && !aa) { - for (uint y = 0; y < gm.gmBlackBoxY; y++) { - for (uint x = 0; x < gm.gmBlackBoxX; x++) { - if (aa ? (bmp[x + y * pitch] > 0) : HasBit(bmp[(x / 8) + y * pitch], 7 - (x % 8))) { - sprite.data[1 + x + (1 + y) * sprite.width].m = SHADOW_COLOUR; - sprite.data[1 + x + (1 + y) * sprite.width].a = aa ? (bmp[x + y * pitch] << 2) - 1 : 0xFF; - } - } - } - } - - for (uint y = 0; y < gm.gmBlackBoxY; y++) { - for (uint x = 0; x < gm.gmBlackBoxX; x++) { - if (aa ? (bmp[x + y * pitch] > 0) : HasBit(bmp[(x / 8) + y * pitch], 7 - (x % 8))) { - sprite.data[x + y * sprite.width].m = FACE_COLOUR; - sprite.data[x + y * sprite.width].a = aa ? (bmp[x + y * pitch] << 2) - 1 : 0xFF; - } - } - } - } - - GlyphEntry new_glyph; - new_glyph.sprite = BlitterFactory::GetCurrentBlitter()->Encode(&sprite, AllocateFont); - new_glyph.width = gm.gmCellIncX; - - this->SetGlyphPtr(key, &new_glyph); - - return new_glyph.sprite; -} - -/* virtual */ GlyphID Win32FontCache::MapCharToGlyph(WChar key) -{ - assert(IsPrintable(key)); - - if (key >= SCC_SPRITE_START && key <= SCC_SPRITE_END) { - return this->parent->MapCharToGlyph(key); - } - - /* Convert characters outside of the BMP into surrogate pairs. */ - WCHAR chars[2]; - if (key >= 0x010000U) { - chars[0] = (WCHAR)(((key - 0x010000U) >> 10) + 0xD800); - chars[1] = (WCHAR)(((key - 0x010000U) & 0x3FF) + 0xDC00); - } else { - chars[0] = (WCHAR)(key & 0xFFFF); - } - - WORD glyphs[2] = {0, 0}; - GetGlyphIndicesW(this->dc, chars, key >= 0x010000U ? 2 : 1, glyphs, GGI_MARK_NONEXISTING_GLYPHS); - - return glyphs[0] != 0xFFFF ? glyphs[0] : 0; -} - -/* virtual */ const void *Win32FontCache::InternalGetFontTable(uint32 tag, size_t &length) -{ - DWORD len = GetFontData(this->dc, tag, 0, nullptr, 0); - - void *result = nullptr; - if (len != GDI_ERROR && len > 0) { - result = MallocT(len); - GetFontData(this->dc, tag, 0, result, len); - } - - length = len; - return result; -} - -/** - * Loads the GDI font. - * If a GDI font description is present, e.g. from the automatic font - * fallback search, use it. Otherwise, try to resolve it by font name. - * @param fs The font size to load. - */ -static void LoadWin32Font(FontSize fs) -{ - static const char *SIZE_TO_NAME[] = { "medium", "small", "large", "mono" }; - - FreeTypeSubSetting *settings = nullptr; - switch (fs) { - default: NOT_REACHED(); - case FS_SMALL: settings = &_freetype.small; break; - case FS_NORMAL: settings = &_freetype.medium; break; - case FS_LARGE: settings = &_freetype.large; break; - case FS_MONO: settings = &_freetype.mono; break; - } - - if (StrEmpty(settings->font)) return; - - LOGFONT logfont; - MemSetT(&logfont, 0); - logfont.lfPitchAndFamily = fs == FS_MONO ? FIXED_PITCH : VARIABLE_PITCH; - logfont.lfCharSet = DEFAULT_CHARSET; - logfont.lfOutPrecision = OUT_OUTLINE_PRECIS; - logfont.lfClipPrecision = CLIP_DEFAULT_PRECIS; - - if (settings->os_handle != nullptr) { - logfont = *(const LOGFONT *)settings->os_handle; - } else if (strchr(settings->font, '.') != nullptr) { - /* Might be a font file name, try load it. */ - - TCHAR fontPath[MAX_PATH] = {}; - - /* See if this is an absolute path. */ - if (FileExists(settings->font)) { - convert_to_fs(settings->font, fontPath, lengthof(fontPath), false); - } else { - /* Scan the search-paths to see if it can be found. */ - std::string full_font = FioFindFullPath(BASE_DIR, settings->font); - if (!full_font.empty()) { - convert_to_fs(full_font.c_str(), fontPath, lengthof(fontPath), false); - } - } - - if (fontPath[0] != 0) { - if (AddFontResourceEx(fontPath, FR_PRIVATE, 0) != 0) { - /* Try a nice little undocumented function first for getting the internal font name. - * Some documentation is found at: http://www.undocprint.org/winspool/getfontresourceinfo */ - typedef BOOL(WINAPI * PFNGETFONTRESOURCEINFO)(LPCTSTR, LPDWORD, LPVOID, DWORD); -#ifdef UNICODE - static PFNGETFONTRESOURCEINFO GetFontResourceInfo = (PFNGETFONTRESOURCEINFO)GetProcAddress(GetModuleHandle(_T("Gdi32")), "GetFontResourceInfoW"); -#else - static PFNGETFONTRESOURCEINFO GetFontResourceInfo = (PFNGETFONTRESOURCEINFO)GetProcAddress(GetModuleHandle(_T("Gdi32")), "GetFontResourceInfoA"); -#endif - - if (GetFontResourceInfo != nullptr) { - /* Try to query an array of LOGFONTs that describe the file. */ - DWORD len = 0; - if (GetFontResourceInfo(fontPath, &len, nullptr, 2) && len >= sizeof(LOGFONT)) { - LOGFONT *buf = (LOGFONT *)AllocaM(byte, len); - if (GetFontResourceInfo(fontPath, &len, buf, 2)) { - logfont = *buf; // Just use first entry. - } - } - } - - /* No dice yet. Use the file name as the font face name, hoping it matches. */ - if (logfont.lfFaceName[0] == 0) { - TCHAR fname[_MAX_FNAME]; - _tsplitpath(fontPath, nullptr, nullptr, fname, nullptr); - - _tcsncpy_s(logfont.lfFaceName, lengthof(logfont.lfFaceName), fname, _TRUNCATE); - logfont.lfWeight = strcasestr(settings->font, " bold") != nullptr || strcasestr(settings->font, "-bold") != nullptr ? FW_BOLD : FW_NORMAL; // Poor man's way to allow selecting bold fonts. - } - } else { - ShowInfoF("Unable to load file '%s' for %s font, using default windows font selection instead", settings->font, SIZE_TO_NAME[fs]); - } - } - } - - if (logfont.lfFaceName[0] == 0) { - logfont.lfWeight = strcasestr(settings->font, " bold") != nullptr ? FW_BOLD : FW_NORMAL; // Poor man's way to allow selecting bold fonts. - convert_to_fs(settings->font, logfont.lfFaceName, lengthof(logfont.lfFaceName), false); - } - - HFONT font = CreateFontIndirect(&logfont); - if (font == nullptr) { - ShowInfoF("Unable to use '%s' for %s font, Win32 reported error 0x%lX, using sprite font instead", settings->font, SIZE_TO_NAME[fs], GetLastError()); - return; - } - DeleteObject(font); - - new Win32FontCache(fs, logfont, settings->size); -} - #endif /* WITH_FREETYPE */ -#endif /* defined(WITH_FREETYPE) || defined(_WIN32) */ /** * (Re)initialize the freetype related things, i.e. load the non-sprite fonts. @@ -1083,7 +694,11 @@ void InitFreeType(bool monospace) #ifdef WITH_FREETYPE LoadFreeTypeFont(fs); #elif defined(_WIN32) + extern void LoadWin32Font(FontSize fs); LoadWin32Font(fs); +#elif defined(WITH_COCOA) + extern void LoadCoreTextFont(FontSize fs); + LoadCoreTextFont(fs); #endif } } @@ -1103,3 +718,12 @@ void UninitFreeType() _library = nullptr; #endif /* WITH_FREETYPE */ } + +#if !defined(_WIN32) && !defined(__APPLE__) && !defined(WITH_FONTCONFIG) && !defined(WITH_COCOA) + +#ifdef WITH_FREETYPE +FT_Error GetFontByFaceName(const char *font_name, FT_Face *face) { return FT_Err_Cannot_Open_Resource; } +#endif /* WITH_FREETYPE */ + +bool SetFallbackFont(FreeTypeSettings *settings, const char *language_isocode, int winlangid, MissingGlyphSearcher *callback) { return false; } +#endif /* !defined(_WIN32) && !defined(__APPLE__) && !defined(WITH_FONTCONFIG) && !defined(WITH_COCOA) */ diff --git a/src/fontcache.h b/src/fontcache.h index 940c19c361..72c311f06b 100644 --- a/src/fontcache.h +++ b/src/fontcache.h @@ -32,6 +32,9 @@ protected: int ascender; ///< The ascender value of the font. 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(); @@ -131,7 +134,7 @@ public: * Get the native OS font handle, if there is one. * @return Opaque OS font handle. */ - virtual void *GetOSHandle() + virtual const void *GetOSHandle() { return nullptr; } @@ -213,15 +216,13 @@ static inline bool GetDrawGlyphShadow(FontSize size) return FontCache::Get(size)->GetDrawGlyphShadow(); } -#if defined(WITH_FREETYPE) || defined(_WIN32) - /** Settings for a single freetype font. */ struct FreeTypeSubSetting { char font[MAX_PATH]; ///< The name of the font, or path to the font. uint size; ///< The (requested) size of the font. bool aa; ///< Whether to do anti aliasing or not. - const void *os_handle = nullptr; ///< Optional native OS font info. + const void *os_handle = nullptr; ///< Optional native OS font info. Only valid during font search. }; /** Settings for the freetype fonts. */ @@ -234,8 +235,6 @@ struct FreeTypeSettings { extern FreeTypeSettings _freetype; -#endif /* defined(WITH_FREETYPE) || defined(_WIN32) */ - void InitFreeType(bool monospace); void UninitFreeType(); diff --git a/src/fontcache_internal.h b/src/fontcache_internal.h new file mode 100644 index 0000000000..ffa2378065 --- /dev/null +++ b/src/fontcache_internal.h @@ -0,0 +1,86 @@ +/* + * This file is part of OpenTTD. + * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. + * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . + */ + +/** @file fontcache_internal.h Support types and functions for platform-specific font support. */ + +#ifndef FONTCACHE_INTERNAL_H +#define FONTCACHE_INTERNAL_H + +#include "core/smallmap_type.hpp" +#include "fontcache.h" + + +static const int MAX_FONT_SIZE = 72; ///< Maximum font size. + +static const byte FACE_COLOUR = 1; +static const byte SHADOW_COLOUR = 2; + +/** Font cache for fonts that are based on a TrueType font. */ +class TrueTypeFontCache : public FontCache { +protected: + static constexpr int MAX_GLYPH_DIM = 256; ///< Maximum glyph dimensions. + static constexpr uint MAX_FONT_MIN_REC_SIZE = 20u; ///< Upper limit for the recommended font size in case a font file contains nonsensical values. + + int req_size; ///< Requested font size. + int used_size; ///< Used font size. + + typedef SmallMap > FontTable; ///< Table with font table cache + FontTable font_tables; ///< Cached font tables. + + /** Container for information about a glyph. */ + struct GlyphEntry { + Sprite *sprite; ///< The loaded sprite. + byte width; ///< The width of the glyph. + bool duplicate; ///< Whether this glyph entry is a duplicate, i.e. may this be freed? + }; + + /** + * The glyph cache. This is structured to reduce memory consumption. + * 1) There is a 'segment' table for each font size. + * 2) Each segment table is a discrete block of characters. + * 3) Each block contains 256 (aligned) characters sequential characters. + * + * The cache is accessed in the following way: + * For character 0x0041 ('A'): glyph_to_sprite[0x00][0x41] + * For character 0x20AC (Euro): glyph_to_sprite[0x20][0xAC] + * + * Currently only 256 segments are allocated, "limiting" us to 65536 characters. + * This can be simply changed in the two functions Get & SetGlyphPtr. + */ + GlyphEntry **glyph_to_sprite; + + GlyphEntry *GetGlyphPtr(GlyphID key); + void SetGlyphPtr(GlyphID key, const GlyphEntry *glyph, bool duplicate = false); + + virtual const void *InternalGetFontTable(uint32 tag, size_t &length) = 0; + virtual const Sprite *InternalGetGlyph(GlyphID key, bool aa) = 0; + +public: + TrueTypeFontCache(FontSize fs, int pixels); + virtual ~TrueTypeFontCache(); + int GetFontSize() const override { return this->used_size; } + SpriteID GetUnicodeGlyph(WChar key) override { return this->parent->GetUnicodeGlyph(key); } + void SetUnicodeGlyph(WChar key, SpriteID sprite) override { this->parent->SetUnicodeGlyph(key, sprite); } + + virtual void InitializeUnicodeGlyphMap() + { + this->parent->InitializeUnicodeGlyphMap(); + font_height_cache[this->fs] = this->GetHeight(); + } + + + const Sprite *GetGlyph(GlyphID key) override; + const void *GetFontTable(uint32 tag, size_t &length) override; + void ClearFontCache() override; + uint GetGlyphWidth(GlyphID key) override; + bool GetDrawGlyphShadow() override; + bool IsBuiltInFont() override { return false; } +}; + +void *AllocateFont(size_t size); + +#endif /* FONTCACHE_INTERNAL_H */ diff --git a/src/fontdetection.h b/src/fontdetection.h index 70b2fe236e..9cf39d2b07 100644 --- a/src/fontdetection.h +++ b/src/fontdetection.h @@ -27,7 +27,6 @@ FT_Error GetFontByFaceName(const char *font_name, FT_Face *face); #endif /* WITH_FREETYPE */ -#if defined(WITH_FREETYPE) || defined(_WIN32) /** * We would like to have a fallback font as the current one * doesn't contain all characters we need. @@ -38,8 +37,6 @@ FT_Error GetFontByFaceName(const char *font_name, FT_Face *face); * @param callback The function to call to check for missing glyphs. * @return true if a font has been set, false otherwise. */ -bool SetFallbackFont(FreeTypeSettings *settings, const char *language_isocode, int winlangid, class MissingGlyphSearcher *callback); - -#endif /* defined(WITH_FREETYPE) || defined(WIN32)*/ +bool SetFallbackFont(struct FreeTypeSettings *settings, const char *language_isocode, int winlangid, class MissingGlyphSearcher *callback); #endif diff --git a/src/framerate_gui.cpp b/src/framerate_gui.cpp index c324fde2e3..26a40c4516 100644 --- a/src/framerate_gui.cpp +++ b/src/framerate_gui.cpp @@ -151,6 +151,10 @@ namespace { /* Total duration covered by collected points */ TimingMeasurement total = 0; + /* We have nothing to compare the first point against */ + point--; + if (point < 0) point = NUM_FRAMERATE_POINTS - 1; + while (point != last_point) { /* Only record valid data points, but pretend the gaps in measurements aren't there */ if (this->durations[point] != INVALID_DURATION) { @@ -185,7 +189,7 @@ namespace { PerformanceData(1), // PFE_ACC_GL_AIRCRAFT PerformanceData(1), // PFE_GL_LANDSCAPE PerformanceData(1), // PFE_GL_LINKGRAPH - PerformanceData(GL_RATE), // PFE_DRAWING + PerformanceData(1000.0 / 30), // PFE_DRAWING PerformanceData(1), // PFE_ACC_DRAWWORLD PerformanceData(60.0), // PFE_VIDEO PerformanceData(1000.0 * 8192 / 44100), // PFE_SOUND @@ -464,7 +468,7 @@ struct FramerateWindow : Window { this->speed_gameloop.SetRate(gl_rate / _pf_data[PFE_GAMELOOP].expected_rate, 1.0); if (this->small) return; // in small mode, this is everything needed - this->rate_drawing.SetRate(_pf_data[PFE_DRAWING].GetRate(), _pf_data[PFE_DRAWING].expected_rate); + this->rate_drawing.SetRate(_pf_data[PFE_DRAWING].GetRate(), _settings_client.gui.refresh_rate); int new_active = 0; for (PerformanceElement e = PFE_FIRST; e < PFE_MAX; e++) { diff --git a/src/genworld_gui.cpp b/src/genworld_gui.cpp index b392d36ef0..58cc6a4c2d 100644 --- a/src/genworld_gui.cpp +++ b/src/genworld_gui.cpp @@ -28,6 +28,8 @@ #include "saveload/saveload.h" #include "progress.h" #include "error.h" +#include "newgrf_townname.h" +#include "townname_type.h" #include "widgets/genworld_widget.h" @@ -81,11 +83,11 @@ static const NWidgetPart _nested_generate_landscape_widgets[] = { NWidget(NWID_VERTICAL, NC_EQUALSIZE), SetPIP(0, 4, 0), NWidget(WWT_TEXT, COLOUR_ORANGE), SetDataTip(STR_MAPGEN_MAPSIZE, STR_MAPGEN_MAPSIZE_TOOLTIP), SetFill(1, 1), NWidget(WWT_TEXT, COLOUR_ORANGE), SetDataTip(STR_MAPGEN_LAND_GENERATOR, STR_NULL), SetFill(1, 1), - NWidget(WWT_TEXT, COLOUR_ORANGE), SetDataTip(STR_MAPGEN_NUMBER_OF_TOWNS, STR_NULL), SetFill(1, 1), NWidget(WWT_TEXT, COLOUR_ORANGE), SetDataTip(STR_MAPGEN_TERRAIN_TYPE, STR_NULL), SetFill(1, 1), NWidget(WWT_TEXT, COLOUR_ORANGE), SetDataTip(STR_MAPGEN_VARIETY, STR_NULL), SetFill(1, 1), NWidget(WWT_TEXT, COLOUR_ORANGE), SetDataTip(STR_MAPGEN_QUANTITY_OF_SEA_LAKES, STR_NULL), SetFill(1, 1), - NWidget(WWT_TEXT, COLOUR_ORANGE), SetDataTip(STR_MAPGEN_TREE_PLACER, STR_NULL), SetFill(1, 1), + NWidget(WWT_TEXT, COLOUR_ORANGE), SetDataTip(STR_MAPGEN_NUMBER_OF_TOWNS, STR_NULL), SetFill(1, 1), + NWidget(WWT_TEXT, COLOUR_ORANGE), SetDataTip(STR_MAPGEN_NUMBER_OF_INDUSTRIES, STR_NULL), SetFill(1, 1), NWidget(WWT_TEXT, COLOUR_ORANGE), SetDataTip(STR_MAPGEN_BORDER_TYPE, STR_NULL), SetFill(1, 1), EndContainer(), /* Widgets at the right of the labels. */ @@ -97,11 +99,11 @@ static const NWidgetPart _nested_generate_landscape_widgets[] = { NWidget(WWT_DROPDOWN, COLOUR_ORANGE, WID_GL_MAPSIZE_Y_PULLDOWN), SetDataTip(STR_JUST_INT, STR_MAPGEN_MAPSIZE_TOOLTIP), SetFill(1, 0), EndContainer(), NWidget(WWT_DROPDOWN, COLOUR_ORANGE, WID_GL_LANDSCAPE_PULLDOWN), SetDataTip(STR_JUST_STRING, STR_NULL), SetFill(1, 0), - NWidget(WWT_DROPDOWN, COLOUR_ORANGE, WID_GL_TOWN_PULLDOWN), SetDataTip(STR_JUST_STRING, STR_NULL), SetFill(1, 0), NWidget(WWT_DROPDOWN, COLOUR_ORANGE, WID_GL_TERRAIN_PULLDOWN), SetDataTip(STR_JUST_STRING, STR_NULL), SetFill(1, 0), NWidget(WWT_DROPDOWN, COLOUR_ORANGE, WID_GL_VARIETY_PULLDOWN), SetDataTip(STR_JUST_STRING, STR_NULL), SetFill(1, 0), NWidget(WWT_DROPDOWN, COLOUR_ORANGE, WID_GL_WATER_PULLDOWN), SetDataTip(STR_JUST_STRING, STR_NULL), SetFill(1, 0), - NWidget(WWT_DROPDOWN, COLOUR_ORANGE, WID_GL_TREE_PULLDOWN), SetDataTip(STR_JUST_STRING, STR_NULL), SetFill(1, 0), + NWidget(WWT_DROPDOWN, COLOUR_ORANGE, WID_GL_TOWN_PULLDOWN), SetDataTip(STR_JUST_STRING, STR_NULL), SetFill(1, 0), + NWidget(WWT_DROPDOWN, COLOUR_ORANGE, WID_GL_INDUSTRY_PULLDOWN), SetDataTip(STR_JUST_STRING, STR_NULL), SetFill(1, 0), NWidget(WWT_TEXTBTN, COLOUR_ORANGE, WID_GL_BORDERS_RANDOM), SetDataTip(STR_JUST_STRING, STR_NULL), SetFill(1, 0), EndContainer(), EndContainer(), @@ -111,9 +113,9 @@ static const NWidgetPart _nested_generate_landscape_widgets[] = { NWidget(WWT_TEXT, COLOUR_ORANGE), SetDataTip(STR_MAPGEN_MAX_HEIGHTLEVEL, STR_NULL), SetFill(1, 1), NWidget(WWT_TEXT, COLOUR_ORANGE), SetDataTip(STR_MAPGEN_SNOW_LINE_HEIGHT, STR_NULL), SetFill(1, 1), NWidget(WWT_TEXT, COLOUR_ORANGE), SetDataTip(STR_MAPGEN_DATE, STR_NULL), SetFill(1, 1), - NWidget(WWT_TEXT, COLOUR_ORANGE), SetDataTip(STR_MAPGEN_NUMBER_OF_INDUSTRIES, STR_NULL), SetFill(1, 1), NWidget(WWT_TEXT, COLOUR_ORANGE), SetDataTip(STR_MAPGEN_SMOOTHNESS, STR_NULL), SetFill(1, 1), NWidget(WWT_TEXT, COLOUR_ORANGE), SetDataTip(STR_MAPGEN_QUANTITY_OF_RIVERS, STR_NULL), SetFill(1, 1), + NWidget(WWT_TEXT, COLOUR_ORANGE), SetDataTip(STR_GAME_OPTIONS_TOWN_NAMES_FRAME, STR_NULL), SetFill(1, 1), EndContainer(), NWidget(NWID_VERTICAL, NC_EQUALSIZE), SetPIP(0, 4, 0), /* Max. heightlevel. */ @@ -134,9 +136,9 @@ static const NWidgetPart _nested_generate_landscape_widgets[] = { NWidget(WWT_PUSHTXTBTN, COLOUR_ORANGE, WID_GL_START_DATE_TEXT), SetDataTip(STR_BLACK_DATE_LONG, STR_NULL), SetFill(1, 0), NWidget(WWT_IMGBTN, COLOUR_ORANGE, WID_GL_START_DATE_UP), SetDataTip(SPR_ARROW_UP, STR_SCENEDIT_TOOLBAR_TOOLTIP_MOVE_THE_STARTING_DATE_FORWARD), SetFill(0, 1), EndContainer(), - NWidget(WWT_DROPDOWN, COLOUR_ORANGE, WID_GL_INDUSTRY_PULLDOWN), SetDataTip(STR_JUST_STRING, STR_NULL), SetFill(1, 0), NWidget(WWT_DROPDOWN, COLOUR_ORANGE, WID_GL_SMOOTHNESS_PULLDOWN), SetDataTip(STR_JUST_STRING, STR_NULL), SetFill(1, 0), NWidget(WWT_DROPDOWN, COLOUR_ORANGE, WID_GL_RIVER_PULLDOWN), SetDataTip(STR_JUST_STRING, STR_NULL), SetFill(1, 0), + NWidget(WWT_DROPDOWN, COLOUR_ORANGE, WID_GL_TOWNNAME_DROPDOWN), SetDataTip(STR_BLACK_STRING, STR_GAME_OPTIONS_TOWN_NAMES_DROPDOWN_TOOLTIP), SetFill(1, 0), EndContainer(), EndContainer(), NWidget(WWT_PUSHTXTBTN, COLOUR_GREEN, WID_GL_GENERATE_BUTTON), SetMinimalSize(84, 0), SetDataTip(STR_MAPGEN_GENERATE, STR_NULL), SetFill(1, 1), @@ -198,18 +200,19 @@ static const NWidgetPart _nested_heightmap_load_widgets[] = { /* Labels at the left side. */ NWidget(NWID_VERTICAL, NC_EQUALSIZE), SetPIP(0, 4, 0), NWidget(WWT_TEXT, COLOUR_ORANGE), SetDataTip(STR_MAPGEN_HEIGHTMAP_NAME, STR_NULL), SetFill(1, 1), + NWidget(WWT_TEXT, COLOUR_ORANGE), SetDataTip(STR_MAPGEN_HEIGHTMAP_SIZE_LABEL, STR_NULL), SetFill(1, 1), NWidget(WWT_TEXT, COLOUR_ORANGE), SetDataTip(STR_MAPGEN_MAPSIZE, STR_NULL), SetFill(1, 1), NWidget(WWT_TEXT, COLOUR_ORANGE), SetDataTip(STR_MAPGEN_HEIGHTMAP_ROTATION, STR_NULL), SetFill(1, 1), NWidget(WWT_TEXT, COLOUR_ORANGE), SetDataTip(STR_MAPGEN_NUMBER_OF_TOWNS, STR_NULL), SetFill(1, 1), NWidget(WWT_TEXT, COLOUR_ORANGE), SetDataTip(STR_MAPGEN_NUMBER_OF_INDUSTRIES, STR_NULL), SetFill(1, 1), NWidget(WWT_TEXT, COLOUR_ORANGE), SetDataTip(STR_MAPGEN_QUANTITY_OF_RIVERS, STR_NULL), SetFill(1, 1), - NWidget(WWT_TEXT, COLOUR_ORANGE), SetDataTip(STR_MAPGEN_TREE_PLACER, STR_NULL), SetFill(1, 1), EndContainer(), /* Widgets at the right of the labels. */ NWidget(NWID_VERTICAL), SetPIP(0, 4, 0), NWidget(WWT_EMPTY, COLOUR_ORANGE, WID_GL_HEIGHTMAP_NAME_TEXT), SetFill(1, 0), NWidget(NWID_HORIZONTAL), SetPIP(0, 5, 0), NWidget(NWID_VERTICAL, NC_EQUALSIZE), SetPIP(0, 4, 0), + NWidget(WWT_TEXT, COLOUR_ORANGE, WID_GL_HEIGHTMAP_SIZE_TEXT), SetDataTip(STR_MAPGEN_HEIGHTMAP_SIZE, STR_NULL), SetFill(1, 0), /* Mapsize X * Y. */ NWidget(NWID_HORIZONTAL), SetPIP(0, 4, 0), NWidget(WWT_DROPDOWN, COLOUR_ORANGE, WID_GL_MAPSIZE_X_PULLDOWN), SetDataTip(STR_JUST_INT, STR_NULL), SetFill(1, 0), @@ -220,33 +223,32 @@ static const NWidgetPart _nested_heightmap_load_widgets[] = { NWidget(WWT_DROPDOWN, COLOUR_ORANGE, WID_GL_TOWN_PULLDOWN), SetDataTip(STR_JUST_STRING, STR_NULL), SetFill(1, 0), NWidget(WWT_DROPDOWN, COLOUR_ORANGE, WID_GL_INDUSTRY_PULLDOWN), SetDataTip(STR_JUST_STRING, STR_NULL), SetFill(1, 0), NWidget(WWT_DROPDOWN, COLOUR_ORANGE, WID_GL_RIVER_PULLDOWN), SetDataTip(STR_JUST_STRING, STR_NULL), SetFill(1, 0), - NWidget(WWT_DROPDOWN, COLOUR_ORANGE, WID_GL_TREE_PULLDOWN), SetDataTip(STR_JUST_STRING, STR_NULL), SetFill(1, 0), EndContainer(), NWidget(NWID_VERTICAL), SetPIP(0, 4, 0), NWidget(NWID_HORIZONTAL), SetPIP(0, 3, 0), NWidget(NWID_VERTICAL, NC_EQUALSIZE), SetPIP(0, 4, 0), - NWidget(WWT_TEXT, COLOUR_ORANGE), SetDataTip(STR_MAPGEN_HEIGHTMAP_SIZE_LABEL, STR_NULL), SetFill(1, 1), - NWidget(WWT_TEXT, COLOUR_ORANGE), SetDataTip(STR_MAPGEN_SNOW_LINE_HEIGHT, STR_NULL), SetFill(1, 1), NWidget(WWT_TEXT, COLOUR_ORANGE), SetDataTip(STR_MAPGEN_MAX_HEIGHTLEVEL, STR_NULL), SetFill(1, 1), + NWidget(WWT_TEXT, COLOUR_ORANGE), SetDataTip(STR_MAPGEN_SNOW_LINE_HEIGHT, STR_NULL), SetFill(1, 1), NWidget(WWT_TEXT, COLOUR_ORANGE), SetDataTip(STR_MAPGEN_DATE, STR_NULL), SetFill(1, 1), + NWidget(WWT_TEXT, COLOUR_ORANGE), SetDataTip(STR_GAME_OPTIONS_TOWN_NAMES_FRAME, STR_NULL), SetFill(1, 1), EndContainer(), NWidget(NWID_VERTICAL, NC_EQUALSIZE), SetPIP(0, 4, 0), - NWidget(WWT_TEXT, COLOUR_ORANGE, WID_GL_HEIGHTMAP_SIZE_TEXT), SetDataTip(STR_MAPGEN_HEIGHTMAP_SIZE, STR_NULL), SetFill(1, 0), - NWidget(NWID_HORIZONTAL), - NWidget(WWT_IMGBTN, COLOUR_ORANGE, WID_GL_SNOW_LEVEL_DOWN), SetDataTip(SPR_ARROW_DOWN, STR_MAPGEN_SNOW_LINE_DOWN), SetFill(0, 1), - NWidget(WWT_TEXTBTN, COLOUR_ORANGE, WID_GL_SNOW_LEVEL_TEXT), SetDataTip(STR_BLACK_INT, STR_NULL), SetFill(1, 0), - NWidget(WWT_IMGBTN, COLOUR_ORANGE, WID_GL_SNOW_LEVEL_UP), SetDataTip(SPR_ARROW_UP, STR_MAPGEN_SNOW_LINE_UP), SetFill(0, 1), - EndContainer(), NWidget(NWID_HORIZONTAL), NWidget(WWT_IMGBTN, COLOUR_ORANGE, WID_GL_MAX_HEIGHTLEVEL_DOWN), SetDataTip(SPR_ARROW_DOWN, STR_MAPGEN_MAX_HEIGHTLEVEL_DOWN), SetFill(0, 1), NWidget(WWT_TEXTBTN, COLOUR_ORANGE, WID_GL_MAX_HEIGHTLEVEL_TEXT), SetDataTip(STR_BLACK_INT, STR_NULL), SetFill(1, 0), NWidget(WWT_IMGBTN, COLOUR_ORANGE, WID_GL_MAX_HEIGHTLEVEL_UP), SetDataTip(SPR_ARROW_UP, STR_MAPGEN_MAX_HEIGHTLEVEL_UP), SetFill(0, 1), EndContainer(), + NWidget(NWID_HORIZONTAL), + NWidget(WWT_IMGBTN, COLOUR_ORANGE, WID_GL_SNOW_LEVEL_DOWN), SetDataTip(SPR_ARROW_DOWN, STR_MAPGEN_SNOW_LINE_DOWN), SetFill(0, 1), + NWidget(WWT_TEXTBTN, COLOUR_ORANGE, WID_GL_SNOW_LEVEL_TEXT), SetDataTip(STR_BLACK_INT, STR_NULL), SetFill(1, 0), + NWidget(WWT_IMGBTN, COLOUR_ORANGE, WID_GL_SNOW_LEVEL_UP), SetDataTip(SPR_ARROW_UP, STR_MAPGEN_SNOW_LINE_UP), SetFill(0, 1), + EndContainer(), NWidget(NWID_HORIZONTAL), NWidget(WWT_IMGBTN, COLOUR_ORANGE, WID_GL_START_DATE_DOWN), SetDataTip(SPR_ARROW_DOWN, STR_SCENEDIT_TOOLBAR_TOOLTIP_MOVE_THE_STARTING_DATE_BACKWARD), SetFill(0, 1), NWidget(WWT_PUSHTXTBTN, COLOUR_ORANGE, WID_GL_START_DATE_TEXT), SetDataTip(STR_BLACK_DATE_LONG, STR_NULL), SetFill(1, 0), NWidget(WWT_IMGBTN, COLOUR_ORANGE, WID_GL_START_DATE_UP), SetDataTip(SPR_ARROW_UP, STR_SCENEDIT_TOOLBAR_TOOLTIP_MOVE_THE_STARTING_DATE_FORWARD), SetFill(0, 1), EndContainer(), + NWidget(WWT_DROPDOWN, COLOUR_ORANGE, WID_GL_TOWNNAME_DROPDOWN), SetDataTip(STR_BLACK_STRING, STR_GAME_OPTIONS_TOWN_NAMES_DROPDOWN_TOOLTIP), SetFill(1, 0), EndContainer(), EndContainer(), NWidget(WWT_PUSHTXTBTN, COLOUR_GREEN, WID_GL_GENERATE_BUTTON), SetMinimalSize(84, 0), SetDataTip(STR_MAPGEN_GENERATE, STR_NULL), SetFill(1, 1), @@ -319,11 +321,38 @@ static DropDownList BuildMapsizeDropDown(int other_dimension) return list; } +static DropDownList BuildTownNameDropDown() +{ + DropDownList list; + + /* Add and sort newgrf townnames generators */ + const auto &grf_names = GetGRFTownNameList(); + for (uint i = 0; i < grf_names.size(); i++) { + list.emplace_back(new DropDownListStringItem(grf_names[i], BUILTIN_TOWNNAME_GENERATOR_COUNT + i, false)); + } + std::sort(list.begin(), list.end(), DropDownListStringItem::NatSortFunc); + + size_t newgrf_size = list.size(); + /* Insert newgrf_names at the top of the list */ + if (newgrf_size > 0) { + list.emplace_back(new DropDownListItem(-1, false)); // separator line + newgrf_size++; + } + + /* Add and sort original townnames generators */ + for (uint i = 0; i < BUILTIN_TOWNNAME_GENERATOR_COUNT; i++) { + list.emplace_back(new DropDownListStringItem(STR_GAME_OPTIONS_TOWN_NAME_ORIGINAL_ENGLISH + i, i, false)); + } + std::sort(list.begin() + newgrf_size, list.end(), DropDownListStringItem::NatSortFunc); + + return list; +} + + static const StringID _elevations[] = {STR_TERRAIN_TYPE_VERY_FLAT, STR_TERRAIN_TYPE_FLAT, STR_TERRAIN_TYPE_HILLY, STR_TERRAIN_TYPE_MOUNTAINOUS, STR_TERRAIN_TYPE_ALPINIST, INVALID_STRING_ID}; static const StringID _sea_lakes[] = {STR_SEA_LEVEL_VERY_LOW, STR_SEA_LEVEL_LOW, STR_SEA_LEVEL_MEDIUM, STR_SEA_LEVEL_HIGH, STR_SEA_LEVEL_CUSTOM, INVALID_STRING_ID}; static const StringID _rivers[] = {STR_RIVERS_NONE, STR_RIVERS_FEW, STR_RIVERS_MODERATE, STR_RIVERS_LOT, INVALID_STRING_ID}; static const StringID _smoothness[] = {STR_CONFIG_SETTING_ROUGHNESS_OF_TERRAIN_VERY_SMOOTH, STR_CONFIG_SETTING_ROUGHNESS_OF_TERRAIN_SMOOTH, STR_CONFIG_SETTING_ROUGHNESS_OF_TERRAIN_ROUGH, STR_CONFIG_SETTING_ROUGHNESS_OF_TERRAIN_VERY_ROUGH, INVALID_STRING_ID}; -static const StringID _tree_placer[] = {STR_CONFIG_SETTING_TREE_PLACER_NONE, STR_CONFIG_SETTING_TREE_PLACER_ORIGINAL, STR_CONFIG_SETTING_TREE_PLACER_IMPROVED, INVALID_STRING_ID}; 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}; @@ -357,10 +386,9 @@ struct GenerateLandscapeWindow : public Window { SetDropDownColor(); - /* Disable town, industry and trees in SE */ + /* Disable town and industry in SE */ this->SetWidgetDisabledState(WID_GL_TOWN_PULLDOWN, _game_mode == GM_EDITOR); this->SetWidgetDisabledState(WID_GL_INDUSTRY_PULLDOWN, _game_mode == GM_EDITOR); - this->SetWidgetDisabledState(WID_GL_TREE_PULLDOWN, _game_mode == GM_EDITOR); this->OnInvalidateData(); } @@ -386,9 +414,17 @@ struct GenerateLandscapeWindow : public Window { } break; + case WID_GL_TOWNNAME_DROPDOWN: { + uint gen = _settings_newgame.game_creation.town_name; + StringID name = gen < BUILTIN_TOWNNAME_GENERATOR_COUNT ? + STR_GAME_OPTIONS_TOWN_NAME_ORIGINAL_ENGLISH + gen : + GetGRFTownNameName(gen - BUILTIN_TOWNNAME_GENERATOR_COUNT); + SetDParam(0, name); + 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_LANDSCAPE_PULLDOWN: SetDParam(0, _landscape[_settings_newgame.game_creation.land_generator]); break; - case WID_GL_TREE_PULLDOWN: SetDParam(0, _tree_placer[_settings_newgame.game_creation.tree_placer]); break; case WID_GL_TERRAIN_PULLDOWN: SetDParam(0, _elevations[_settings_newgame.difficulty.terrain_type]); break; case WID_GL_WATER_PULLDOWN: @@ -513,7 +549,6 @@ struct GenerateLandscapeWindow : public Window { case WID_GL_INDUSTRY_PULLDOWN: strs = _num_inds; break; case WID_GL_LANDSCAPE_PULLDOWN: strs = _landscape; break; - case WID_GL_TREE_PULLDOWN: strs = _tree_placer; break; case WID_GL_TERRAIN_PULLDOWN: strs = _elevations; break; case WID_GL_WATER_PULLDOWN: strs = _sea_lakes; @@ -584,6 +619,10 @@ struct GenerateLandscapeWindow : public Window { ShowDropDownMenu(this, _num_towns, _settings_newgame.difficulty.number_towns, WID_GL_TOWN_PULLDOWN, 0, 0); break; + case WID_GL_TOWNNAME_DROPDOWN: // Townname generator + ShowDropDownList(this, BuildTownNameDropDown(), _settings_newgame.game_creation.town_name, WID_GL_TOWNNAME_DROPDOWN); + break; + case WID_GL_INDUSTRY_PULLDOWN: // Number of industries ShowDropDownMenu(this, _num_inds, _settings_newgame.difficulty.industry_density, WID_GL_INDUSTRY_PULLDOWN, 0, 0); break; @@ -671,10 +710,6 @@ struct GenerateLandscapeWindow : public Window { ShowQueryString(STR_JUST_INT, STR_MAPGEN_SNOW_LINE_QUERY_CAPT, 4, this, CS_NUMERAL, QSF_ENABLE_DEFAULT); break; - case WID_GL_TREE_PULLDOWN: // Tree placer - ShowDropDownMenu(this, _tree_placer, _settings_newgame.game_creation.tree_placer, WID_GL_TREE_PULLDOWN, 0, 0); - break; - case WID_GL_LANDSCAPE_PULLDOWN: // Landscape generator ShowDropDownMenu(this, _landscape, _settings_newgame.game_creation.land_generator, WID_GL_LANDSCAPE_PULLDOWN, 0, 0); break; @@ -760,7 +795,6 @@ struct GenerateLandscapeWindow : public Window { _settings_newgame.game_creation.map_y = index; SetDropDownColor(); break; - case WID_GL_TREE_PULLDOWN: _settings_newgame.game_creation.tree_placer = index; break; case WID_GL_RIVER_PULLDOWN: _settings_newgame.game_creation.amount_of_rivers = index; break; case WID_GL_SMOOTHNESS_PULLDOWN: _settings_newgame.game_creation.tgen_smoothness = index; break; case WID_GL_VARIETY_PULLDOWN: _settings_newgame.game_creation.variety = index; break; @@ -783,6 +817,13 @@ struct GenerateLandscapeWindow : public Window { _settings_newgame.difficulty.number_towns = index; break; + case WID_GL_TOWNNAME_DROPDOWN: // Town names + if (_game_mode == GM_MENU || Town::GetNumItems() == 0) { + _settings_newgame.game_creation.town_name = index; + SetWindowDirty(WC_GAME_OPTIONS, WN_GAME_OPTIONS_GAME_OPTIONS); + } + break; + case WID_GL_INDUSTRY_PULLDOWN: _settings_newgame.difficulty.industry_density = index; break; case WID_GL_TERRAIN_PULLDOWN: _settings_newgame.difficulty.terrain_type = index; break; @@ -1203,7 +1244,7 @@ struct GenWorldStatus { StringID cls; uint current; uint total; - int timer; + std::chrono::steady_clock::time_point timer; }; static GenWorldStatus _gws; @@ -1309,7 +1350,7 @@ void PrepareGenerateWorldProgress() _gws.current = 0; _gws.total = 0; _gws.percent = 0; - _gws.timer = 0; // Forces to paint the progress window immediately + _gws.timer = std::chrono::steady_clock::now() - std::chrono::milliseconds(MODAL_PROGRESS_REDRAW_TIMEOUT * 2); // Ensure we draw on first update } /** @@ -1344,12 +1385,7 @@ static void _SetGeneratingWorldProgress(GenWorldProgress cls, uint progress, uin } /* Don't update the screen too often. So update it once in every once in a while... */ -#if defined(__GNUC__) || defined(__clang__) - const uint32 now = __atomic_load_n(&_realtime_tick, __ATOMIC_RELAXED); -#else - const uint32 now = _realtime_tick; -#endif - if (!_network_dedicated && _gws.timer != 0 && now - _gws.timer < MODAL_PROGRESS_REDRAW_TIMEOUT) return; + if (!_network_dedicated && std::chrono::steady_clock::now() - _gws.timer < std::chrono::milliseconds(MODAL_PROGRESS_REDRAW_TIMEOUT)) return; /* Percentage is about the number of completed tasks, so 'current - 1' */ _gws.percent = percent_table[cls] + (percent_table[cls + 1] - percent_table[cls]) * (_gws.current == 0 ? 0 : _gws.current - 1) / _gws.total; @@ -1385,11 +1421,7 @@ static void _SetGeneratingWorldProgress(GenWorldProgress cls, uint progress, uin _modal_progress_work_mutex.lock(); _modal_progress_paint_mutex.unlock(); -#if defined(__GNUC__) || defined(__clang__) - _gws.timer = __atomic_load_n(&_realtime_tick, __ATOMIC_RELAXED); -#else - _gws.timer = _realtime_tick; -#endif + _gws.timer = std::chrono::steady_clock::now(); } /** diff --git a/src/gfx.cpp b/src/gfx.cpp index 71e976fb25..b21cc93654 100644 --- a/src/gfx.cpp +++ b/src/gfx.cpp @@ -86,6 +86,10 @@ static ReusableBuffer _cursor_backup; ZoomLevel _gui_zoom; ///< GUI Zoom level ZoomLevel _font_zoom; ///< Font Zoom level +int8 _gui_zoom_cfg; ///< GUI zoom level in config. +int8 _font_zoom_cfg; ///< Font zoom level in config. + + /** * The rect for repaint. * @@ -1546,13 +1550,8 @@ void DrawDirtyBlocks() _modal_progress_paint_mutex.unlock(); _modal_progress_work_mutex.unlock(); - /* Wait a while and update _realtime_tick so we are given the rights */ - if (!is_first_modal_progress_loop) SleepWhileModalProgress(MODAL_PROGRESS_REDRAW_TIMEOUT); -#if defined(__GNUC__) || defined(__clang__) - __atomic_add_fetch(&_realtime_tick, MODAL_PROGRESS_REDRAW_TIMEOUT, __ATOMIC_RELAXED); -#else - _realtime_tick += MODAL_PROGRESS_REDRAW_TIMEOUT; -#endif + /* Wait a while and hope the modal gives us a bit of time to draw the GUI. */ + if (!is_first_modal_progress_loop) CSleep(MODAL_PROGRESS_REDRAW_TIMEOUT); /* Modal progress thread may need blitter access while we are waiting for it. */ VideoDriver::GetInstance()->ReleaseBlitterLock(); @@ -2182,3 +2181,23 @@ void SortResolutions() { std::sort(_resolutions.begin(), _resolutions.end()); } + +/** + * Resolve GUI zoom level, if auto-suggestion is requested. + */ +void UpdateGUIZoom() +{ + /* Determine real GUI zoom to use. */ + if (_gui_zoom_cfg == ZOOM_LVL_CFG_AUTO) { + _gui_zoom = static_cast(Clamp(VideoDriver::GetInstance()->GetSuggestedUIZoom(), _settings_client.gui.zoom_min, _settings_client.gui.zoom_max)); + } else { + _gui_zoom = static_cast(_gui_zoom_cfg); + } + + /* Determine real font zoom to use. */ + if (_font_zoom_cfg == ZOOM_LVL_CFG_AUTO) { + _font_zoom = static_cast(VideoDriver::GetInstance()->GetSuggestedUIZoom()); + } else { + _font_zoom = static_cast(_font_zoom_cfg); + } +} diff --git a/src/gfx_func.h b/src/gfx_func.h index 489592a0cc..91cd2ff091 100644 --- a/src/gfx_func.h +++ b/src/gfx_func.h @@ -80,6 +80,7 @@ void UpdateWindows(); void DrawMouseCursor(); void ScreenSizeChanged(); void GameSizeChanged(); +void UpdateGUIZoom(); void UndrawMouseCursor(); /** Size of the buffer used for drawing strings. */ diff --git a/src/goal.cpp b/src/goal.cpp index 954d2f7bf7..8ce8e23371 100644 --- a/src/goal.cpp +++ b/src/goal.cpp @@ -260,8 +260,9 @@ CommandCost CmdGoalQuestion(TileIndex tile, DoCommandFlag flags, uint32 p1, uint } else { if (company != INVALID_COMPANY && !Company::IsValidID(company)) return CMD_ERROR; } - if (CountBits(button_mask) < 1 || CountBits(button_mask) > 3) return CMD_ERROR; - if (type >= GOAL_QUESTION_TYPE_COUNT) return CMD_ERROR; + uint min_buttons = (type == GQT_QUESTION ? 1 : 0); + if (CountBits(button_mask) < min_buttons || CountBits(button_mask) > 3) return CMD_ERROR; + if (type >= GQT_END) return CMD_ERROR; if (flags & DC_EXEC) { if (is_client) { diff --git a/src/goal_gui.cpp b/src/goal_gui.cpp index b51c488a14..86f73dd0bc 100644 --- a/src/goal_gui.cpp +++ b/src/goal_gui.cpp @@ -375,10 +375,16 @@ struct GoalQuestionWindow : public Window { if (n == 3) break; } this->buttons = n; - assert(this->buttons > 0 && this->buttons < 4); + assert(this->buttons < 4); this->CreateNestedTree(); - this->GetWidget(WID_GQ_BUTTONS)->SetDisplayedPlane(this->buttons - 1); + if (this->buttons == 0) { + this->GetWidget(WID_GQ_BUTTONS)->SetDisplayedPlane(SZSP_HORIZONTAL); + this->GetWidget(WID_GQ_BUTTON_SPACER)->SetDisplayedPlane(SZSP_HORIZONTAL); + } else { + this->GetWidget(WID_GQ_BUTTONS)->SetDisplayedPlane(this->buttons - 1); + this->GetWidget(WID_GQ_BUTTON_SPACER)->SetDisplayedPlane(0); + } this->FinishInitNested(window_number); } @@ -463,7 +469,9 @@ static const NWidgetPart _nested_goal_question_widgets_question[] = { NWidget(WWT_PUSHTXTBTN, COLOUR_LIGHT_BLUE, WID_GQ_BUTTON_3), SetDataTip(STR_BLACK_STRING, STR_NULL), SetFill(1, 0), EndContainer(), EndContainer(), - NWidget(NWID_SPACER), SetMinimalSize(0, 8), + NWidget(NWID_SELECTION, INVALID_COLOUR, WID_GQ_BUTTON_SPACER), + NWidget(NWID_SPACER), SetMinimalSize(0, 8), + EndContainer(), EndContainer(), }; @@ -488,7 +496,9 @@ static const NWidgetPart _nested_goal_question_widgets_info[] = { NWidget(WWT_PUSHTXTBTN, COLOUR_LIGHT_BLUE, WID_GQ_BUTTON_3), SetDataTip(STR_BLACK_STRING, STR_NULL), SetFill(1, 0), EndContainer(), EndContainer(), - NWidget(NWID_SPACER), SetMinimalSize(0, 8), + NWidget(NWID_SELECTION, INVALID_COLOUR, WID_GQ_BUTTON_SPACER), + NWidget(NWID_SPACER), SetMinimalSize(0, 8), + EndContainer(), EndContainer(), }; @@ -513,7 +523,9 @@ static const NWidgetPart _nested_goal_question_widgets_warning[] = { NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_GQ_BUTTON_3), SetDataTip(STR_BLACK_STRING, STR_NULL), SetFill(1, 0), EndContainer(), EndContainer(), - NWidget(NWID_SPACER), SetMinimalSize(0, 8), + NWidget(NWID_SELECTION, INVALID_COLOUR, WID_GQ_BUTTON_SPACER), + NWidget(NWID_SPACER), SetMinimalSize(0, 8), + EndContainer(), EndContainer(), }; @@ -538,7 +550,9 @@ static const NWidgetPart _nested_goal_question_widgets_error[] = { NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_GQ_BUTTON_3), SetDataTip(STR_BLACK_STRING, STR_NULL), SetFill(1, 0), EndContainer(), EndContainer(), - NWidget(NWID_SPACER), SetMinimalSize(0, 8), + NWidget(NWID_SELECTION, INVALID_COLOUR, WID_GQ_BUTTON_SPACER), + NWidget(NWID_SPACER), SetMinimalSize(0, 8), + EndContainer(), EndContainer(), }; @@ -578,6 +592,6 @@ static WindowDesc _goal_question_list_desc[] = { */ void ShowGoalQuestion(uint16 id, byte type, uint32 button_mask, const char *question) { - assert(type < GOAL_QUESTION_TYPE_COUNT); + assert(type < GQT_END); new GoalQuestionWindow(&_goal_question_list_desc[type], id, type == 3 ? TC_WHITE : TC_BLACK, button_mask, question); } diff --git a/src/goal_type.h b/src/goal_type.h index b422e14d99..5bbd8c258c 100644 --- a/src/goal_type.h +++ b/src/goal_type.h @@ -13,7 +13,14 @@ #include "core/enum_type.hpp" static const uint32 GOAL_QUESTION_BUTTON_COUNT = 18; ///< Amount of buttons available. -static const byte GOAL_QUESTION_TYPE_COUNT = 4; ///< Amount of question types. + +enum GoalQuestionType : byte { + GQT_QUESTION = 0, + GQT_INFORMATION = 1, + GQT_WARNING = 2, + GQT_ERROR = 3, + GQT_END = 4, +}; /** Types of goal destinations */ enum GoalType : byte { diff --git a/src/graph_gui.cpp b/src/graph_gui.cpp index 5783955e0a..acaadee693 100644 --- a/src/graph_gui.cpp +++ b/src/graph_gui.cpp @@ -165,7 +165,8 @@ struct ValuesInterval { struct BaseGraphWindow : Window { protected: static const int GRAPH_MAX_DATASETS = 64; - static const int GRAPH_AXIS_LINE_COLOUR = PC_BLACK; + static const int GRAPH_AXIS_LINE_COLOUR = GREY_SCALE(1); + static const int GRAPH_ZERO_LINE_COLOUR = GREY_SCALE(8); static const int GRAPH_NUM_MONTHS = 24; ///< Number of months displayed in the graph. static const int MIN_GRAPH_NUM_LINES_Y = 9; ///< Minimal number of horizontal lines to draw. @@ -292,7 +293,7 @@ protected: static_assert(GRAPH_MAX_DATASETS >= (int)NUM_CARGO && GRAPH_MAX_DATASETS >= (int)MAX_COMPANIES); assert(this->num_vert_lines > 0); - byte grid_colour = _colour_gradient[COLOUR_GREY][4]; + byte grid_colour = GREY_SCALE(3); /* Rect r will be adjusted to contain just the graph, with labels being * placed outside the area. */ @@ -352,7 +353,7 @@ protected: /* Draw the x axis. */ y = x_axis_offset + r.top; - GfxFillRect(r.left, y, r.right, y, GRAPH_AXIS_LINE_COLOUR); + GfxFillRect(r.left, y, r.right, y, GRAPH_ZERO_LINE_COLOUR); /* Find the largest value that will be drawn. */ if (this->num_on_x_axis == 0) return; diff --git a/src/group_gui.cpp b/src/group_gui.cpp index 7ea19c171d..a8f1a93a9b 100644 --- a/src/group_gui.cpp +++ b/src/group_gui.cpp @@ -820,9 +820,9 @@ public: assert(vehgroup.NumVehicles() > 0); v = vehgroup.vehicles_begin[0]; /* - No VehicleClicked(v) support for now, because don't want - to enable any contextual actions except perhaps clicking/ctrl-clicking to clone orders. - */ + * No VehicleClicked(v) support for now, because don't want + * to enable any contextual actions except perhaps clicking/ctrl-clicking to clone orders. + */ break; } diff --git a/src/highscore_gui.cpp b/src/highscore_gui.cpp index a473424f81..b1c428a768 100644 --- a/src/highscore_gui.cpp +++ b/src/highscore_gui.cpp @@ -183,7 +183,7 @@ struct HighScoreWindow : EndGameHighScoreBaseWindow { this->SetupHighScoreEndWindow(); Point pt = this->GetTopLeft(ScaleGUITrad(640), ScaleGUITrad(480)); - SetDParam(0, ORIGINAL_END_YEAR); + SetDParam(0, _settings_game.game_creation.ending_year); DrawStringMultiLine(pt.x + ScaleGUITrad(70), pt.x + ScaleGUITrad(570), pt.y, pt.y + ScaleGUITrad(140), !_networking ? STR_HIGHSCORE_TOP_COMPANIES_WHO_REACHED : STR_HIGHSCORE_TOP_COMPANIES_NETWORK_GAME, TC_FROMSTRING, SA_CENTER); /* Draw Highscore peepz */ diff --git a/src/industry_gui.cpp b/src/industry_gui.cpp index 419ebef972..3ce2d6f321 100644 --- a/src/industry_gui.cpp +++ b/src/industry_gui.cpp @@ -37,6 +37,7 @@ #include "smallmap_gui.h" #include "widgets/dropdown_type.h" #include "widgets/industry_widget.h" +#include "clear_map.h" #include "table/strings.h" @@ -244,6 +245,14 @@ static const NWidgetPart _nested_build_industry_widgets[] = { NWidget(WWT_DEFSIZEBOX, COLOUR_DARK_GREEN), NWidget(WWT_STICKYBOX, COLOUR_DARK_GREEN), EndContainer(), + NWidget(NWID_SELECTION, COLOUR_DARK_GREEN, WID_DPI_SCENARIO_EDITOR_PANE), + NWidget(NWID_VERTICAL), + NWidget(WWT_TEXTBTN, COLOUR_DARK_GREEN, WID_DPI_CREATE_RANDOM_INDUSTRIES_WIDGET), SetMinimalSize(0, 12), SetFill(1, 0), SetResize(1, 0), + SetDataTip(STR_FUND_INDUSTRY_MANY_RANDOM_INDUSTRIES, STR_FUND_INDUSTRY_MANY_RANDOM_INDUSTRIES_TOOLTIP), + NWidget(WWT_TEXTBTN, COLOUR_DARK_GREEN, WID_DPI_REMOVE_ALL_INDUSTRIES_WIDGET), SetMinimalSize(0, 12), SetFill(1, 0), SetResize(1, 0), + SetDataTip(STR_FUND_INDUSTRY_REMOVE_ALL_INDUSTRIES, STR_FUND_INDUSTRY_REMOVE_ALL_INDUSTRIES_TOOLTIP), + EndContainer(), + EndContainer(), NWidget(NWID_HORIZONTAL), NWidget(WWT_MATRIX, COLOUR_DARK_GREEN, WID_DPI_MATRIX_WIDGET), SetMatrixDataTip(1, 0, STR_FUND_INDUSTRY_SELECTION_TOOLTIP), SetFill(1, 0), SetResize(1, 1), SetScrollbar(WID_DPI_SCROLLBAR), NWidget(NWID_VSCROLLBAR, COLOUR_DARK_GREEN, WID_DPI_SCROLLBAR), @@ -289,11 +298,6 @@ class BuildIndustryWindow : public Window { this->enabled[i] = false; } - if (_game_mode == GM_EDITOR) { // give room for the Many Random "button" - this->index[this->count] = INVALID_INDUSTRYTYPE; - this->enabled[this->count] = true; - this->count++; - } /* Fill the arrays with industries. * The tests performed after the enabled allow to load the industries * In the same way they are inserted by grf (if any) @@ -392,6 +396,13 @@ public: this->FinishInitNested(0); this->SetButtons(); + + /* Show scenario editor tools in editor. */ + if (_game_mode != GM_EDITOR) { + auto *se_tools = this->GetWidget(WID_DPI_SCENARIO_EDITOR_PANE); + se_tools->SetDisplayedPlane(SZSP_HORIZONTAL); + this->ReInit(); + } } void OnInit() override @@ -578,9 +589,53 @@ public: } } + static void AskManyRandomIndustriesCallback(Window *w, bool confirmed) + { + if (!confirmed) return; + + if (Town::GetNumItems() == 0) { + ShowErrorMessage(STR_ERROR_CAN_T_GENERATE_INDUSTRIES, STR_ERROR_MUST_FOUND_TOWN_FIRST, WL_INFO); + } else { + extern void GenerateIndustries(); + _generating_world = true; + GenerateIndustries(); + _generating_world = false; + } + } + + static void AskRemoveAllIndustriesCallback(Window *w, bool confirmed) + { + if (!confirmed) return; + + for (Industry *industry : Industry::Iterate()) delete industry; + + /* Clear farmland. */ + for (TileIndex tile = 0; tile < MapSize(); tile++) { + if (IsTileType(tile, MP_CLEAR) && GetRawClearGround(tile) == CLEAR_FIELDS) { + MakeClear(tile, CLEAR_GRASS, 3); + } + } + + MarkWholeScreenDirty(); + } + void OnClick(Point pt, int widget, int click_count) override { switch (widget) { + case WID_DPI_CREATE_RANDOM_INDUSTRIES_WIDGET: { + assert(_game_mode == GM_EDITOR); + this->HandleButtonClick(WID_DPI_CREATE_RANDOM_INDUSTRIES_WIDGET); + ShowQuery(STR_FUND_INDUSTRY_MANY_RANDOM_INDUSTRIES_CAPTION, STR_FUND_INDUSTRY_MANY_RANDOM_INDUSTRIES_QUERY, nullptr, AskManyRandomIndustriesCallback); + break; + } + + case WID_DPI_REMOVE_ALL_INDUSTRIES_WIDGET: { + assert(_game_mode == GM_EDITOR); + this->HandleButtonClick(WID_DPI_REMOVE_ALL_INDUSTRIES_WIDGET); + ShowQuery(STR_FUND_INDUSTRY_REMOVE_ALL_INDUSTRIES_CAPTION, STR_FUND_INDUSTRY_REMOVE_ALL_INDUSTRIES_QUERY, nullptr, AskRemoveAllIndustriesCallback); + break; + } + case WID_DPI_MATRIX_WIDGET: { int y = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_DPI_MATRIX_WIDGET); if (y < this->count) { // Is it within the boundaries of available data? @@ -610,22 +665,13 @@ public: break; case WID_DPI_FUND_WIDGET: { - if (this->selected_type == INVALID_INDUSTRYTYPE) { - this->HandleButtonClick(WID_DPI_FUND_WIDGET); - - if (Town::GetNumItems() == 0) { - ShowErrorMessage(STR_ERROR_CAN_T_GENERATE_INDUSTRIES, STR_ERROR_MUST_FOUND_TOWN_FIRST, WL_INFO); + if (this->selected_type != INVALID_INDUSTRYTYPE) { + if (_game_mode != GM_EDITOR && _settings_game.construction.raw_industry_construction == 2 && GetIndustrySpec(this->selected_type)->IsRawIndustry()) { + DoCommandP(0, this->selected_type, InteractiveRandom(), CMD_BUILD_INDUSTRY | CMD_MSG(STR_ERROR_CAN_T_CONSTRUCT_THIS_INDUSTRY)); + this->HandleButtonClick(WID_DPI_FUND_WIDGET); } else { - extern void GenerateIndustries(); - _generating_world = true; - GenerateIndustries(); - _generating_world = false; + HandlePlacePushButton(this, WID_DPI_FUND_WIDGET, SPR_CURSOR_INDUSTRY, HT_RECT); } - } else if (_game_mode != GM_EDITOR && _settings_game.construction.raw_industry_construction == 2 && GetIndustrySpec(this->selected_type)->IsRawIndustry()) { - DoCommandP(0, this->selected_type, InteractiveRandom(), CMD_BUILD_INDUSTRY | CMD_MSG(STR_ERROR_CAN_T_CONSTRUCT_THIS_INDUSTRY)); - this->HandleButtonClick(WID_DPI_FUND_WIDGET); - } else { - HandlePlacePushButton(this, WID_DPI_FUND_WIDGET, SPR_CURSOR_INDUSTRY, HT_RECT); } break; } diff --git a/src/lang/CMakeLists.txt b/src/lang/CMakeLists.txt index 1a8191f82e..3041ef27d2 100644 --- a/src/lang/CMakeLists.txt +++ b/src/lang/CMakeLists.txt @@ -1,4 +1,9 @@ +set(MASTER_LANG_FILE + ${CMAKE_CURRENT_SOURCE_DIR}/english.txt +) + set(LANG_SOURCE_FILES + ${MASTER_LANG_FILE} ${CMAKE_CURRENT_SOURCE_DIR}/afrikaans.txt ${CMAKE_CURRENT_SOURCE_DIR}/arabic_egypt.txt ${CMAKE_CURRENT_SOURCE_DIR}/basque.txt @@ -10,7 +15,6 @@ set(LANG_SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/czech.txt ${CMAKE_CURRENT_SOURCE_DIR}/danish.txt ${CMAKE_CURRENT_SOURCE_DIR}/dutch.txt - ${CMAKE_CURRENT_SOURCE_DIR}/english.txt ${CMAKE_CURRENT_SOURCE_DIR}/english_AU.txt ${CMAKE_CURRENT_SOURCE_DIR}/english_US.txt ${CMAKE_CURRENT_SOURCE_DIR}/esperanto.txt @@ -75,7 +79,7 @@ foreach(LANG_SOURCE_FILE IN LISTS LANG_SOURCE_FILES) -s ${CMAKE_CURRENT_SOURCE_DIR} -d ${LANG_BINARY_DIR} ${LANG_SOURCE_FILE} - DEPENDS hoststrgen + DEPENDS hoststrgen ${MASTER_LANG_FILE} MAIN_DEPENDENCY ${LANG_SOURCE_FILE} DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/english.txt WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} @@ -104,7 +108,7 @@ add_custom_command_timestamp(OUTPUT ${TABLE_BINARY_DIR}/strings.h COMMAND hoststrgen -s ${CMAKE_CURRENT_SOURCE_DIR} -d ${TABLE_BINARY_DIR} - DEPENDS hoststrgen ${LANG_SOURCE_FILES} + DEPENDS hoststrgen ${MASTER_LANG_FILE} WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} COMMENT "Generating table/strings.h" ) diff --git a/src/lang/afrikaans.txt b/src/lang/afrikaans.txt index bb95f36985..4905880d56 100644 --- a/src/lang/afrikaans.txt +++ b/src/lang/afrikaans.txt @@ -939,8 +939,6 @@ STR_GAME_OPTIONS_CURRENCY_CNY :Chinese Renminb STR_GAME_OPTIONS_CURRENCY_HKD :Hong Kong Dollar (HKD) ############ end of currency region -STR_GAME_OPTIONS_ROAD_VEHICLES_FRAME :{BLACK}Pad voertuie -STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_TOOLTIP :{BLACK}Kies kant van pad waarop voertuie ry STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_LEFT :Bestuur aan linkerkant STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_RIGHT :Bestuur aan regterkant @@ -2794,7 +2792,6 @@ STR_MAPGEN_SNOW_LINE_HEIGHT :{BLACK}Sneeu ly STR_MAPGEN_SNOW_LINE_UP :{BLACK}Beweeg die sneeu lyn een op STR_MAPGEN_SNOW_LINE_DOWN :{BLACK}Beweeg die sneeu lyn een af STR_MAPGEN_LAND_GENERATOR :{BLACK}Land genereerder: -STR_MAPGEN_TREE_PLACER :{BLACK}Boom algoritme: STR_MAPGEN_TERRAIN_TYPE :{BLACK}Terrein tipe: STR_MAPGEN_QUANTITY_OF_SEA_LAKES :{BLACK}Seevlak: STR_MAPGEN_QUANTITY_OF_RIVERS :{BLACK}Riviere: diff --git a/src/lang/arabic_egypt.txt b/src/lang/arabic_egypt.txt index f2c8cea6a8..ed4c817095 100644 --- a/src/lang/arabic_egypt.txt +++ b/src/lang/arabic_egypt.txt @@ -886,8 +886,6 @@ STR_GAME_OPTIONS_CURRENCY_ZAR :راند جنو STR_GAME_OPTIONS_CURRENCY_CUSTOM :مخصص ... ############ end of currency region -STR_GAME_OPTIONS_ROAD_VEHICLES_FRAME :{BLACK}العربات البرية -STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_TOOLTIP :{BLACK}اختار الجانب الذي تسير فيه المركبات STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_LEFT :القيادة على اليسار STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_RIGHT :القيادة على اليمين @@ -2363,7 +2361,6 @@ STR_MAPGEN_SNOW_LINE_HEIGHT :{BLACK} ارت STR_MAPGEN_SNOW_LINE_UP :{BLACK}ارفع خط الثلج مستوى واحد STR_MAPGEN_SNOW_LINE_DOWN :{BLACK}خفض خط الثلج مستوى واحد STR_MAPGEN_LAND_GENERATOR :{BLACK}مولد الخريطة: -STR_MAPGEN_TREE_PLACER :{BLACK} لوغاريثم الشجر: STR_MAPGEN_TERRAIN_TYPE :{BLACK} نوع التضاريس STR_MAPGEN_QUANTITY_OF_SEA_LAKES :{BLACK}مستوى البحر STR_MAPGEN_QUANTITY_OF_RIVERS :{BLACK}انهار: diff --git a/src/lang/basque.txt b/src/lang/basque.txt index c2982d45dc..799ae8bd0b 100644 --- a/src/lang/basque.txt +++ b/src/lang/basque.txt @@ -910,8 +910,6 @@ STR_GAME_OPTIONS_CURRENCY_GEL :Lari Georgiarra STR_GAME_OPTIONS_CURRENCY_IRR :Rial Iraniarra (IRR) ############ end of currency region -STR_GAME_OPTIONS_ROAD_VEHICLES_FRAME :{BLACK}Errepideko garraioak -STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_TOOLTIP :{BLACK}Aukeratu ibilgailuen gidatzeko norabidea STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_LEFT :Ezkerretik gidatu STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_RIGHT :Eskuinetik gidatu @@ -2613,7 +2611,6 @@ STR_MAPGEN_SNOW_LINE_HEIGHT :{BLACK}Elur gar STR_MAPGEN_SNOW_LINE_UP :{BLACK}Igo puntu batean elur garaiera STR_MAPGEN_SNOW_LINE_DOWN :{BLACK}Jeitsi puntu batean elur garaiera STR_MAPGEN_LAND_GENERATOR :{BLACK}Paisaia sortzailea: -STR_MAPGEN_TREE_PLACER :{BLACK}Zuhaitz algoritmoa: STR_MAPGEN_TERRAIN_TYPE :{BLACK}Lur mota: STR_MAPGEN_QUANTITY_OF_SEA_LAKES :{BLACK}Itsaso kopurua: STR_MAPGEN_QUANTITY_OF_RIVERS :{BLACK}Errekak: diff --git a/src/lang/belarusian.txt b/src/lang/belarusian.txt index f469198f5c..e060967be3 100644 --- a/src/lang/belarusian.txt +++ b/src/lang/belarusian.txt @@ -1248,8 +1248,6 @@ STR_GAME_OPTIONS_CURRENCY_HKD :Ганконс STR_GAME_OPTIONS_CURRENCY_INR :Індыйская рупія (INR) ############ end of currency region -STR_GAME_OPTIONS_ROAD_VEHICLES_FRAME :{BLACK}Дарожны рух -STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_TOOLTIP :{BLACK}Па якім баку дарогі рухаецца аўтатранспарт STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_LEFT :Езьдзіць леваруч STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_RIGHT :Езьдзіць праваруч @@ -3115,7 +3113,6 @@ STR_MAPGEN_SNOW_LINE_HEIGHT :{BLACK}Вышы STR_MAPGEN_SNOW_LINE_UP :{BLACK}Падняць сьнегавую лінію STR_MAPGEN_SNOW_LINE_DOWN :{BLACK}Апусьціць сьнегавую лінію STR_MAPGEN_LAND_GENERATOR :{BLACK}Ґенэратар ляндшафту: -STR_MAPGEN_TREE_PLACER :{BLACK}Альґарытм дрэваў: STR_MAPGEN_TERRAIN_TYPE :{BLACK}Тып ляндшафту: STR_MAPGEN_QUANTITY_OF_SEA_LAKES :{BLACK}Колькасьць азёраў/мораў: STR_MAPGEN_QUANTITY_OF_RIVERS :{BLACK}Колькасьць рэк: diff --git a/src/lang/brazilian_portuguese.txt b/src/lang/brazilian_portuguese.txt index 634cb003c0..f9a5bd432d 100644 --- a/src/lang/brazilian_portuguese.txt +++ b/src/lang/brazilian_portuguese.txt @@ -741,6 +741,7 @@ STR_SMALLMAP_LEGENDA_DOCK :{TINY_FONT}{BLA STR_SMALLMAP_LEGENDA_ROUGH_LAND :{TINY_FONT}{BLACK}Terreno Irregular STR_SMALLMAP_LEGENDA_GRASS_LAND :{TINY_FONT}{BLACK}Gramado STR_SMALLMAP_LEGENDA_BARE_LAND :{TINY_FONT}{BLACK}Terreno Descoberto +STR_SMALLMAP_LEGENDA_RAINFOREST :{TINY_FONT}{BLACK}Floresta Tropical STR_SMALLMAP_LEGENDA_FIELDS :{TINY_FONT}{BLACK}Campos STR_SMALLMAP_LEGENDA_TREES :{TINY_FONT}{BLACK}Árvores STR_SMALLMAP_LEGENDA_ROCKS :{TINY_FONT}{BLACK}Rochas @@ -940,8 +941,6 @@ STR_GAME_OPTIONS_CURRENCY_HKD :Dólar de Hong STR_GAME_OPTIONS_CURRENCY_INR :Rúpia Indiana (INR) ############ end of currency region -STR_GAME_OPTIONS_ROAD_VEHICLES_FRAME :{BLACK}Automóveis -STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_TOOLTIP :{BLACK}Selecionar lado da rodovia para automóveis dirigirem STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_LEFT :Dirigem na esquerda STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_RIGHT :Dirigem na direita @@ -1608,6 +1607,7 @@ STR_CONFIG_SETTING_TOWN_CARGOGENMODE_BITCOUNT :Linear STR_CONFIG_SETTING_EXTRA_TREE_PLACEMENT :Posicionamento de árvores: {STRING} STR_CONFIG_SETTING_EXTRA_TREE_PLACEMENT_HELPTEXT :Controla o nascimento aleatório de árvores durante o jogo. Isso pode afetar indústrias que dependem do crescimento de árvores, como serrarias STR_CONFIG_SETTING_EXTRA_TREE_PLACEMENT_NO_SPREAD :Crescer sem espalhar {RED}(afeta serrarias) +STR_CONFIG_SETTING_EXTRA_TREE_PLACEMENT_NO_GROWTH_NO_SPREAD :Não crescer, não espalhar {RED}(afeta serrarias) STR_CONFIG_SETTING_TOOLBAR_POS :Posição da barra de ferramentas principal: {STRING} STR_CONFIG_SETTING_TOOLBAR_POS_HELPTEXT :Posição horizontal da barra de ferramentas principal no topo da tela @@ -1674,6 +1674,7 @@ STR_CONFIG_SETTING_LOCALISATION_UNITS_VELOCITY_HELPTEXT :Sempre que uma STR_CONFIG_SETTING_LOCALISATION_UNITS_VELOCITY_IMPERIAL :Imperial (mph) STR_CONFIG_SETTING_LOCALISATION_UNITS_VELOCITY_METRIC :Métrico (km/h) STR_CONFIG_SETTING_LOCALISATION_UNITS_VELOCITY_SI :SI (m/s) +STR_CONFIG_SETTING_LOCALISATION_UNITS_VELOCITY_GAMEUNITS :Unidades de jogo (quadr./dia) STR_CONFIG_SETTING_LOCALISATION_UNITS_POWER :Unidade de potência veicular: {STRING} STR_CONFIG_SETTING_LOCALISATION_UNITS_POWER_HELPTEXT :Sempre que a potência de um veículo for exibida na interface de usuário, será exibida na unidade selecionada @@ -1806,12 +1807,13 @@ STR_INTRO_TRANSLATION :{BLACK}Faltam { # Quit window STR_QUIT_CAPTION :{WHITE}Sair +STR_QUIT_ARE_YOU_SURE_YOU_WANT_TO_EXIT_OPENTTD :{YELLOW}Tem a certeza que quer sair do OpenTTD? STR_QUIT_YES :{BLACK}Sim STR_QUIT_NO :{BLACK}Não # Abandon game STR_ABANDON_GAME_CAPTION :{WHITE}Abandonar jogo -STR_ABANDON_GAME_QUERY :{YELLOW}Você tem certeza que deseja abandonar este jogo? +STR_ABANDON_GAME_QUERY :{YELLOW}Você tem certeza que quer abandonar este jogo? STR_ABANDON_SCENARIO_QUERY :{YELLOW}Você tem certeza que quer abandonar este cenário? # Cheat window @@ -1969,6 +1971,7 @@ STR_NETWORK_SERVER_LIST_REFRESH :{BLACK}Atualiza STR_NETWORK_SERVER_LIST_REFRESH_TOOLTIP :{BLACK}Atualiza as informações sobre o servidor STR_NETWORK_SERVER_LIST_SEARCH_SERVER_INTERNET :{BLACK}Procurar na internet +STR_NETWORK_SERVER_LIST_SEARCH_SERVER_LAN :{BLACK}Buscar na LAN STR_NETWORK_SERVER_LIST_ADD_SERVER :{BLACK}Adicionar servidor STR_NETWORK_SERVER_LIST_ADD_SERVER_TOOLTIP :{BLACK}Adiciona um servidor à lista que será sempre verificada se existem jogos ocorrendo STR_NETWORK_SERVER_LIST_START_SERVER :{BLACK}Iniciar servidor @@ -2199,6 +2202,7 @@ STR_NETWORK_SERVER_MESSAGE_GAME_REASON_NOT_ENOUGH_PLAYERS :número de joga STR_NETWORK_SERVER_MESSAGE_GAME_REASON_CONNECTING_CLIENTS :clientes conectando-se STR_NETWORK_SERVER_MESSAGE_GAME_REASON_MANUAL :manual STR_NETWORK_SERVER_MESSAGE_GAME_REASON_GAME_SCRIPT :{G=m}script do jogo +STR_NETWORK_SERVER_MESSAGE_GAME_REASON_LINK_GRAPH :aguardando atualização do gráfico de links ############ End of leave-in-this-order STR_NETWORK_MESSAGE_CLIENT_LEAVING :saindo STR_NETWORK_MESSAGE_CLIENT_JOINED :*** {STRING} entrou no jogo @@ -2327,6 +2331,7 @@ STR_JOIN_WAYPOINT_CAPTION :{WHITE}Unir pon STR_JOIN_WAYPOINT_CREATE_SPLITTED_WAYPOINT :{YELLOW}Construir um ponto de controle separado # Generic toolbar +STR_TOOLBAR_DISABLED_NO_VEHICLE_AVAILABLE :{BLACK}Desativado pois não há nenhum veículo disponível para essa infraestrutura # Rail construction toolbar STR_RAIL_TOOLBAR_RAILROAD_CONSTRUCTION_CAPTION :Construir ferrovias @@ -2522,6 +2527,10 @@ STR_TREES_RANDOM_TYPE :{BLACK}Árvores STR_TREES_RANDOM_TYPE_TOOLTIP :{BLACK}Plantar árvores de tipo aleatório, Shift alterna entre construção/preço estimado STR_TREES_RANDOM_TREES_BUTTON :{BLACK}Plantar Aleatoriamente STR_TREES_RANDOM_TREES_TOOLTIP :{BLACK}Planta árvores aleatoriamente pelo terreno +STR_TREES_MODE_NORMAL_BUTTON :{BLACK}Normal +STR_TREES_MODE_FOREST_SM_BUTTON :{BLACK}Bosque +STR_TREES_MODE_FOREST_SM_TOOLTIP :{BLACK}Planta pequenas florestas ao arrastar pelo terreno. +STR_TREES_MODE_FOREST_LG_BUTTON :{BLACK}Floresta STR_TREES_MODE_FOREST_LG_TOOLTIP :{BLACK}Planta florestas grandes ao arrastar pelo terreno. # Land generation window (SE) @@ -2533,10 +2542,10 @@ STR_TERRAFORM_TOOLTIP_DECREASE_SIZE_OF_LAND_AREA :{BLACK}Diminui STR_TERRAFORM_TOOLTIP_GENERATE_RANDOM_LAND :{BLACK}Gera terreno aleatório STR_TERRAFORM_SE_NEW_WORLD :{BLACK}Criar novo cenário STR_TERRAFORM_RESET_LANDSCAPE :{BLACK}Limpar terreno -STR_TERRAFORM_RESET_LANDSCAPE_TOOLTIP :{BLACK}Remover todas as propriedades da companhia do mapa +STR_TERRAFORM_RESET_LANDSCAPE_TOOLTIP :{BLACK}Remove todas as propriedades da empresa do mapa STR_QUERY_RESET_LANDSCAPE_CAPTION :{WHITE}Limpar Terreno -STR_RESET_LANDSCAPE_CONFIRMATION_TEXT :{WHITE}Tem certeza que deseja remover todas as propriedades da companhia? +STR_RESET_LANDSCAPE_CONFIRMATION_TEXT :{WHITE}Tem certeza que quer remover todas as propriedades da empresa? # Town generation window (SE) STR_FOUND_TOWN_CAPTION :{WHITE}Gerar Cidades @@ -2575,10 +2584,14 @@ STR_FUND_INDUSTRY_CAPTION :{WHITE}Financia STR_FUND_INDUSTRY_SELECTION_TOOLTIP :{BLACK}Escolha a indústria apropriada STR_FUND_INDUSTRY_MANY_RANDOM_INDUSTRIES :Várias indústrias aleatórias STR_FUND_INDUSTRY_MANY_RANDOM_INDUSTRIES_TOOLTIP :{BLACK}Cobrir o mapa com indústrias colocadas aleatoriamente +STR_FUND_INDUSTRY_MANY_RANDOM_INDUSTRIES_CAPTION :{WHITE}Criar indústrias aleatórias +STR_FUND_INDUSTRY_MANY_RANDOM_INDUSTRIES_QUERY :{YELLOW}Você tem certeza que quer criar várias indústrias aleatoriamente? STR_FUND_INDUSTRY_INDUSTRY_BUILD_COST :{BLACK}Custo: {YELLOW}{CURRENCY_LONG} STR_FUND_INDUSTRY_PROSPECT_NEW_INDUSTRY :{BLACK}Prosperir STR_FUND_INDUSTRY_BUILD_NEW_INDUSTRY :{BLACK}Construir STR_FUND_INDUSTRY_FUND_NEW_INDUSTRY :{BLACK}Fundar +STR_FUND_INDUSTRY_REMOVE_ALL_INDUSTRIES :{BLACK}Remover todas as indústrias +STR_FUND_INDUSTRY_REMOVE_ALL_INDUSTRIES_CAPTION :{WHITE}Remover todas as indústrias # Industry cargoes window STR_INDUSTRY_CARGOES_INDUSTRY_CAPTION :{WHITE}Cadeia de indústrias para a indústria: {STRING} @@ -2826,7 +2839,6 @@ STR_MAPGEN_SNOW_LINE_HEIGHT :{BLACK}Linha da STR_MAPGEN_SNOW_LINE_UP :{BLACK}Aumentar a linha da neve em um ponto STR_MAPGEN_SNOW_LINE_DOWN :{BLACK}Diminuir a linha da neve em um ponto STR_MAPGEN_LAND_GENERATOR :{BLACK}Gerador de terra -STR_MAPGEN_TREE_PLACER :{BLACK}Algorítimo de árvores STR_MAPGEN_TERRAIN_TYPE :{BLACK}Tipo de terreno STR_MAPGEN_QUANTITY_OF_SEA_LAKES :{BLACK}Nível do mar: STR_MAPGEN_QUANTITY_OF_RIVERS :{BLACK}Rios: @@ -3069,6 +3081,7 @@ STR_SIGN_LIST_MATCH_CASE_TOOLTIP :{BLACK}Ativa/De # Sign window STR_EDIT_SIGN_CAPTION :{WHITE}Editar texto da placa +STR_EDIT_SIGN_LOCATION_TOOLTIP :{BLACK}Centraliza janela no local da placa. Ctrl+Clique abre uma nova janela no local da placa STR_EDIT_SIGN_NEXT_SIGN_TOOLTIP :{BLACK}Próxima Placa STR_EDIT_SIGN_PREVIOUS_SIGN_TOOLTIP :{BLACK}Placa anterior @@ -3280,7 +3293,7 @@ STR_STATION_VIEW_CLOSE_AIRPORT_TOOLTIP :{BLACK}Impedir STR_WAYPOINT_VIEW_CAPTION :{WHITE}{WAYPOINT} STR_WAYPOINT_VIEW_CENTER_TOOLTIP :{BLACK}Centrar visão no local do ponto de caminho. Ctrl+Clique abre uma nova janela no local do ponto de caminho STR_WAYPOINT_VIEW_CHANGE_WAYPOINT_NAME :{BLACK}Renomear ponto de controle -STR_BUOY_VIEW_CENTER_TOOLTIP :{BLACK}Centrar visão no local da bóia. Ctrl+Clique abre uma nova janela no local da bóia +STR_BUOY_VIEW_CENTER_TOOLTIP :{BLACK}Centraliza janela no local da bóia. Ctrl+Clique abre uma nova janela no local da bóia STR_BUOY_VIEW_CHANGE_BUOY_NAME :{BLACK}Renomear bóia STR_EDIT_WAYPOINT_NAME :{WHITE}Editar nome do ponto de controle @@ -3345,7 +3358,7 @@ STR_COMPANY_VIEW_RELOCATE_HQ :{BLACK}Mover se STR_COMPANY_VIEW_RELOCATE_COMPANY_HEADQUARTERS :{BLACK}Reconstruir sede da empresa em outro local por 1% do valor da empresa. Shift+Clique mostra o preço estimado sem reconstruir a sede STR_COMPANY_VIEW_INFRASTRUCTURE_BUTTON :{BLACK}Detalhes STR_COMPANY_VIEW_INFRASTRUCTURE_TOOLTIP :{BLACK}Ver informações detalhadas de infraestrutura -STR_COMPANY_VIEW_GIVE_MONEY_BUTTON :{BLACK}Dar dinheiro +STR_COMPANY_VIEW_GIVE_MONEY_TOOLTIP :{BLACK}Dá dinheiro a essa empresa STR_COMPANY_VIEW_NEW_FACE_BUTTON :{BLACK}Novo Rosto STR_COMPANY_VIEW_NEW_FACE_TOOLTIP :{BLACK}Selecionar novo rosto para o presidente @@ -3469,7 +3482,7 @@ STR_GROUP_LIVERY_TOOLTIP :{BLACK}Alterar STR_GROUP_REPLACE_PROTECTION_TOOLTIP :{BLACK}Clique para excluir esse grupo da substituição automática STR_QUERY_GROUP_DELETE_CAPTION :{WHITE}Remover grupo -STR_GROUP_DELETE_QUERY_TEXT :{WHITE}Tem certeza que deseja remover esse grupo e seus descendentes? +STR_GROUP_DELETE_QUERY_TEXT :{WHITE}Tem certeza que quer remover esse grupo e seus descendentes? STR_GROUP_ADD_SHARED_VEHICLE :Adicionar veículos compartilhados STR_GROUP_REMOVE_ALL_VEHICLES :Remover todos os veículos @@ -3534,19 +3547,19 @@ STR_BUY_VEHICLE_SHIP_BUY_VEHICLE_BUTTON :{BLACK}Comprar STR_BUY_VEHICLE_AIRCRAFT_BUY_VEHICLE_BUTTON :{BLACK}Comprar Aeronave STR_BUY_VEHICLE_TRAIN_BUY_REFIT_VEHICLE_BUTTON :{BLACK}Comprar e reequipar veículo -STR_BUY_VEHICLE_ROAD_VEHICLE_BUY_REFIT_VEHICLE_BUTTON :{BLACK}Comprar e Trocar Veículos +STR_BUY_VEHICLE_ROAD_VEHICLE_BUY_REFIT_VEHICLE_BUTTON :{BLACK}Comprar e Reequipar Veículo STR_BUY_VEHICLE_SHIP_BUY_REFIT_VEHICLE_BUTTON :{BLACK}Comprar e Repor Navio STR_BUY_VEHICLE_AIRCRAFT_BUY_REFIT_VEHICLE_BUTTON :{BLACK}Comprar e reequipar aeronaves -STR_BUY_VEHICLE_TRAIN_BUY_VEHICLE_TOOLTIP :{BLACK}Compra o veículo ferroviário selecionado. Shift+Clique mostra preço estimado sem a compra -STR_BUY_VEHICLE_ROAD_VEHICLE_BUY_VEHICLE_TOOLTIP :{BLACK}Compra o veículo selecionado. Shift+Clique mostra preço estimado sem a compra +STR_BUY_VEHICLE_TRAIN_BUY_VEHICLE_TOOLTIP :{BLACK}Compra o veículo ferroviário selecionado. Shift+Clique mostra preço estimado +STR_BUY_VEHICLE_ROAD_VEHICLE_BUY_VEHICLE_TOOLTIP :{BLACK}Compra o veículo selecionado. Shift+Clique mostra preço estimado STR_BUY_VEHICLE_SHIP_BUY_VEHICLE_TOOLTIP :{BLACK}Compra a embarcação selecionada. Shift+Clique mostra preço estimado sem a compra STR_BUY_VEHICLE_AIRCRAFT_BUY_VEHICLE_TOOLTIP :{BLACK}Compra a aeronave selecionada. Shift+Clique mostra o preço estimado sem a compra -STR_BUY_VEHICLE_TRAIN_BUY_REFIT_VEHICLE_TOOLTIP :{BLACK}Compre e troque o trem destacado. Shift+Click mostra os custos estimados sem comprar +STR_BUY_VEHICLE_TRAIN_BUY_REFIT_VEHICLE_TOOLTIP :{BLACK}Compra e troque o trem destacado. Shift+Click mostra o custo estimado STR_BUY_VEHICLE_ROAD_VEHICLE_BUY_REFIT_VEHICLE_TOOLTIP :{BLACK}Compre e troque o veículo terrestre destacado. Shift+Clique mostra o custo estimado sem comprá-lo -STR_BUY_VEHICLE_SHIP_BUY_REFIT_VEHICLE_TOOLTIP :Compre e reponha o navio em destaque. Shift+Clique mostra o custo estimado sem comprar -STR_BUY_VEHICLE_AIRCRAFT_BUY_REFIT_VEHICLE_TOOLTIP :{BLACK}Comprar e repor a aeronave destacada. Shift+Clique mostra o custo estimado sem comprar +STR_BUY_VEHICLE_SHIP_BUY_REFIT_VEHICLE_TOOLTIP :Compre e reequipa o navio selecionado. Shift+Clique mostra o custo estimado +STR_BUY_VEHICLE_AIRCRAFT_BUY_REFIT_VEHICLE_TOOLTIP :{BLACK}Compra e reequipa a aeronave selecionada. Shift+Clique mostra o custo estimado STR_BUY_VEHICLE_TRAIN_RENAME_BUTTON :{BLACK}Renomear STR_BUY_VEHICLE_ROAD_VEHICLE_RENAME_BUTTON :{BLACK}Renomear @@ -3632,9 +3645,9 @@ STR_DEPOT_CLONE_SHIP_DEPOT_INFO :{BLACK}Isso ir STR_DEPOT_CLONE_AIRCRAFT_INFO_HANGAR_WINDOW :{BLACK}Isso irá comprar uma cópia de uma aeronave. Clique nesse botão e depois em uma aeronave dentro ou fora do hangar. Ctrl+Clique compartilhará as ordens. Shift+Clique mostra o preço estimado sem a compra STR_DEPOT_TRAIN_LOCATION_TOOLTIP :{BLACK}Centrar visão no local de um depósito ferroviário. Ctrl+Clique abre uma nova janela no local do depósito de trem -STR_DEPOT_ROAD_VEHICLE_LOCATION_TOOLTIP :{BLACK}Centralizar visualização na localização da garagem. Ctrl+Clique abre uma nova janela de visualização -STR_DEPOT_SHIP_LOCATION_TOOLTIP :{BLACK}Centrar visão no local do depósito naval. Ctrl+Clique abre uma nova janela no local do depósito naval -STR_DEPOT_AIRCRAFT_LOCATION_TOOLTIP :{BLACK}Centrar visão no local do hangar. Ctrl+Clique abre uma nova janela no local do hangar +STR_DEPOT_ROAD_VEHICLE_LOCATION_TOOLTIP :{BLACK}Centraliza a janela na localização da garagem. Ctrl+Clique abre uma nova janela na localização da garagem. +STR_DEPOT_SHIP_LOCATION_TOOLTIP :{BLACK}Centraliza visão no local do depósito naval. Ctrl+Clique abre uma nova janela no local do depósito naval +STR_DEPOT_AIRCRAFT_LOCATION_TOOLTIP :{BLACK}Centraliza a janela no local do hangar. Ctrl+Clique abre uma nova janela no local do hangar STR_DEPOT_VEHICLE_ORDER_LIST_TRAIN_TOOLTIP :{BLACK}Obtém uma lista de todos os trens com este depósito em suas ordens STR_DEPOT_VEHICLE_ORDER_LIST_ROAD_VEHICLE_TOOLTIP :{BLACK}Obtém uma lista de todos os automóveis com esta garagem em suas ordens @@ -3724,6 +3737,9 @@ STR_REPLACE_REMOVE_WAGON_HELP :{BLACK}Faz auto # Vehicle view STR_VEHICLE_VIEW_CAPTION :{WHITE}{VEHICLE} +STR_VEHICLE_VIEW_ROAD_VEHICLE_CENTER_TOOLTIP :{BLACK}Centraliza a janela na localização do veículo. Clique duplo para seguir o veículo. Ctrl+Clique abre uma nova janela na localização do veículo. +STR_VEHICLE_VIEW_SHIP_CENTER_TOOLTIP :{BLACK}Centraliza a janela na localização do navio. Clique duplo para seguir o navio. Ctrl+Clique para abrir uma nova janela na localização do navio. +STR_VEHICLE_VIEW_AIRCRAFT_CENTER_TOOLTIP :{BLACK}Centraliza a janela na localização da aeronave. Clique duplo para seguir a aeronave. Ctrl+Clique abre uma nova janela na localização da aeronave. STR_VEHICLE_VIEW_TRAIN_SEND_TO_DEPOT_TOOLTIP :{BLACK}Enviar trem para o depósito STR_VEHICLE_VIEW_ROAD_VEHICLE_SEND_TO_DEPOT_TOOLTIP :{BLACK}Enviar veículo para a garagem. CTRL+clique irá apenas reparar @@ -4336,7 +4352,7 @@ STR_ERROR_INDUSTRY_TOO_CLOSE :{WHITE}... muit STR_ERROR_MUST_FOUND_TOWN_FIRST :{WHITE}... deve construir uma cidade antes STR_ERROR_ONLY_ONE_ALLOWED_PER_TOWN :{WHITE}... só é permitido uma por cidade STR_ERROR_CAN_ONLY_BE_BUILT_IN_TOWNS_WITH_POPULATION_OF_1200 :{WHITE}... só pode ser construído em cidades com pelo menos 1200 habitantes -STR_ERROR_CAN_ONLY_BE_BUILT_IN_RAINFOREST :{WHITE}... só pode ser construído em zonas florestais +STR_ERROR_CAN_ONLY_BE_BUILT_IN_RAINFOREST :{WHITE}... só pode ser construído em florestas tropicais STR_ERROR_CAN_ONLY_BE_BUILT_IN_DESERT :{WHITE}... só pode ser construído em áreas desérticas STR_ERROR_CAN_ONLY_BE_BUILT_IN_TOWNS :{WHITE}... só se pode ser construído em cidades (substituindo casas) STR_ERROR_CAN_ONLY_BE_BUILT_NEAR_TOWN_CENTER :{WHITE}... só pode ser construído próximo aos centros de cidades @@ -4433,6 +4449,7 @@ STR_ERROR_DEPOT_WRONG_DEPOT_TYPE :Tipo de depósi STR_ERROR_TRAIN_TOO_LONG_AFTER_REPLACEMENT :{WHITE}{VEHICLE} fica grande demais para ser substituído STR_ERROR_AUTOREPLACE_NOTHING_TO_DO :{WHITE}Regras de autosubstituição/renovação não estão ativadas STR_ERROR_AUTOREPLACE_MONEY_LIMIT :(limite de dinheiro) +STR_ERROR_AUTOREPLACE_INCOMPATIBLE_REFIT :{WHITE}Novo veículo não pode ser reequipado na ordem {NUM} # Rail construction errors STR_ERROR_IMPOSSIBLE_TRACK_COMBINATION :{WHITE}Impossível combinação de linhas diff --git a/src/lang/bulgarian.txt b/src/lang/bulgarian.txt index 1b673da54b..17220e1ab3 100644 --- a/src/lang/bulgarian.txt +++ b/src/lang/bulgarian.txt @@ -918,8 +918,6 @@ STR_GAME_OPTIONS_CURRENCY_NTD :Нов тайв STR_GAME_OPTIONS_CURRENCY_HKD :Хонгконгски долар (HKD) ############ end of currency region -STR_GAME_OPTIONS_ROAD_VEHICLES_FRAME :{BLACK}Движение по пътищата -STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_TOOLTIP :{BLACK}Избор посоката на движение по пътищата STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_LEFT :ляво STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_RIGHT :дясно @@ -2675,7 +2673,6 @@ STR_MAPGEN_SNOW_LINE_HEIGHT :{BLACK}Снеж STR_MAPGEN_SNOW_LINE_UP :{BLACK}Преместване снежната линия нагоре STR_MAPGEN_SNOW_LINE_DOWN :{BLACK}Преместване снежната линия надолу STR_MAPGEN_LAND_GENERATOR :{BLACK}Земегенератор: -STR_MAPGEN_TREE_PLACER :{BLACK}Дърворазсад: STR_MAPGEN_TERRAIN_TYPE :{BLACK}Тип на терен: STR_MAPGEN_QUANTITY_OF_SEA_LAKES :{BLACK}Морско ниво: STR_MAPGEN_QUANTITY_OF_RIVERS :{BLACK}Реки: diff --git a/src/lang/catalan.txt b/src/lang/catalan.txt index 40d8e59f7e..c2ca2a68a1 100644 --- a/src/lang/catalan.txt +++ b/src/lang/catalan.txt @@ -950,8 +950,6 @@ STR_GAME_OPTIONS_CURRENCY_HKD :Dòlar de Hong STR_GAME_OPTIONS_CURRENCY_INR :Rúpia índia (INR) ############ end of currency region -STR_GAME_OPTIONS_ROAD_VEHICLES_FRAME :{BLACK}Vehicles de carretera -STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_TOOLTIP :{BLACK}Escull el sentit de circulació dels automòbils STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_LEFT :Conducció per l'esquerra STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_RIGHT :Conducció per la dreta @@ -1006,6 +1004,7 @@ STR_GAME_OPTIONS_RESOLUTION_OTHER :altres STR_GAME_OPTIONS_GUI_ZOOM_FRAME :{BLACK}Mida de la interfície STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_TOOLTIP :{BLACK}Escull la mida dels elements de la interfície +STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_AUTO :(detecta automàticament) STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_NORMAL :Normal STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_2X_ZOOM :Doble STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_4X_ZOOM :Quàdruple @@ -1013,6 +1012,7 @@ STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_4X_ZOOM :Quàdruple STR_GAME_OPTIONS_FONT_ZOOM :{BLACK}Mida de la lletra STR_GAME_OPTIONS_FONT_ZOOM_DROPDOWN_TOOLTIP :{BLACK}Seleccioneu la mida de les fonts de la interfície. +STR_GAME_OPTIONS_FONT_ZOOM_DROPDOWN_AUTO :(detecta automàticament) STR_GAME_OPTIONS_FONT_ZOOM_DROPDOWN_NORMAL :Normal STR_GAME_OPTIONS_FONT_ZOOM_DROPDOWN_2X_ZOOM :Doble STR_GAME_OPTIONS_FONT_ZOOM_DROPDOWN_4X_ZOOM :Quàdruple @@ -2607,12 +2607,18 @@ STR_FOUND_TOWN_SELECT_LAYOUT_RANDOM :{BLACK}Aleatori # Fund new industry window STR_FUND_INDUSTRY_CAPTION :{WHITE}Finança una nova indústria STR_FUND_INDUSTRY_SELECTION_TOOLTIP :{BLACK}Escolleix el tipus d'indústria adequada d'aquesta llista -STR_FUND_INDUSTRY_MANY_RANDOM_INDUSTRIES :Moltes indústries aleatòries +STR_FUND_INDUSTRY_MANY_RANDOM_INDUSTRIES :{BLACK}Crea indústries aleatòries STR_FUND_INDUSTRY_MANY_RANDOM_INDUSTRIES_TOOLTIP :{BLACK}Omple el mapa amb indústries situades aleatòriament +STR_FUND_INDUSTRY_MANY_RANDOM_INDUSTRIES_CAPTION :{WHITE}Crea indústries aleatòries +STR_FUND_INDUSTRY_MANY_RANDOM_INDUSTRIES_QUERY :{YELLOW}Esteu segur que voleu crear moltes indústries aleatòries? STR_FUND_INDUSTRY_INDUSTRY_BUILD_COST :{BLACK}Cost: {YELLOW}{CURRENCY_LONG} STR_FUND_INDUSTRY_PROSPECT_NEW_INDUSTRY :{BLACK}Prospecciona STR_FUND_INDUSTRY_BUILD_NEW_INDUSTRY :{BLACK}Construeix STR_FUND_INDUSTRY_FUND_NEW_INDUSTRY :{BLACK}Finança +STR_FUND_INDUSTRY_REMOVE_ALL_INDUSTRIES :{BLACK}Treu totes les indústries +STR_FUND_INDUSTRY_REMOVE_ALL_INDUSTRIES_TOOLTIP :{BLACK}Treu totes les indústries que hi ha actualment al mapa. +STR_FUND_INDUSTRY_REMOVE_ALL_INDUSTRIES_CAPTION :{WHITE}Treu totes les indústries +STR_FUND_INDUSTRY_REMOVE_ALL_INDUSTRIES_QUERY :{YELLOW}Esteu segur que voleu treure totes les indústries? # Industry cargoes window STR_INDUSTRY_CARGOES_INDUSTRY_CAPTION :{WHITE}Cadena industrial per la indústria {STRING} @@ -2861,7 +2867,6 @@ STR_MAPGEN_SNOW_LINE_HEIGHT :{BLACK}Alçada STR_MAPGEN_SNOW_LINE_UP :{BLACK}Mou els estatges amb neu un pas amunt STR_MAPGEN_SNOW_LINE_DOWN :{BLACK}Mou els estatges amb neu un pas avall STR_MAPGEN_LAND_GENERATOR :{BLACK}Generació de terrenys: -STR_MAPGEN_TREE_PLACER :{BLACK}Algoritme d'arbres: STR_MAPGEN_TERRAIN_TYPE :{BLACK}Tipus de terreny: STR_MAPGEN_QUANTITY_OF_SEA_LAKES :{BLACK}Nivell de mar: STR_MAPGEN_QUANTITY_OF_RIVERS :{BLACK}Quantitat de rius: diff --git a/src/lang/croatian.txt b/src/lang/croatian.txt index dd1be79180..03ff511662 100644 --- a/src/lang/croatian.txt +++ b/src/lang/croatian.txt @@ -1035,8 +1035,6 @@ STR_GAME_OPTIONS_CURRENCY_HKD :Hongkonški Dol STR_GAME_OPTIONS_CURRENCY_INR :Indijska Rupija (INR) ############ end of currency region -STR_GAME_OPTIONS_ROAD_VEHICLES_FRAME :{BLACK}Cestovna vozila -STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_TOOLTIP :{BLACK}Odaberi na kojoj će strani ceste vozila prometovati STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_LEFT :Vozi na lijevoj strani STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_RIGHT :Vozi na desnoj strani @@ -2917,7 +2915,6 @@ STR_MAPGEN_SNOW_LINE_HEIGHT :{BLACK}Visina l STR_MAPGEN_SNOW_LINE_UP :{BLACK}Pomakni visinu linije snijega za jedan gore STR_MAPGEN_SNOW_LINE_DOWN :{BLACK}Pomakni visinu linije snijega za jedan dolje STR_MAPGEN_LAND_GENERATOR :{BLACK}Izrađivač zemljišta: -STR_MAPGEN_TREE_PLACER :{BLACK} Algoritam za drveće: STR_MAPGEN_TERRAIN_TYPE :{BLACK}Vrsta terena: STR_MAPGEN_QUANTITY_OF_SEA_LAKES :{BLACK}Razina mora: STR_MAPGEN_QUANTITY_OF_RIVERS :{BLACK}Rijeke: diff --git a/src/lang/czech.txt b/src/lang/czech.txt index 15580290eb..ccb09a7024 100644 --- a/src/lang/czech.txt +++ b/src/lang/czech.txt @@ -1035,8 +1035,6 @@ STR_GAME_OPTIONS_CURRENCY_HKD :Hongkongský do STR_GAME_OPTIONS_CURRENCY_INR :Indická rupie (INR) ############ end of currency region -STR_GAME_OPTIONS_ROAD_VEHICLES_FRAME :{BLACK}Silniční vozidla jezdí -STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_TOOLTIP :{BLACK}Výběr strany, na které jezdí silnični vozidla STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_LEFT :Vlevo STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_RIGHT :Vpravo @@ -3446,7 +3444,6 @@ STR_MAPGEN_SNOW_LINE_HEIGHT :{BLACK}Výška STR_MAPGEN_SNOW_LINE_UP :{BLACK}Zvýšit sněžnou čáru o jednu úroveň STR_MAPGEN_SNOW_LINE_DOWN :{BLACK}Snížit sněžnou čáru o jednu úroveň STR_MAPGEN_LAND_GENERATOR :{BLACK}Generátor krajiny: -STR_MAPGEN_TREE_PLACER :{BLACK}Sázení stromů: STR_MAPGEN_TERRAIN_TYPE :{BLACK}Typ krajiny: STR_MAPGEN_QUANTITY_OF_SEA_LAKES :{BLACK}Četnost jezer: STR_MAPGEN_QUANTITY_OF_RIVERS :{BLACK}Řeky: diff --git a/src/lang/danish.txt b/src/lang/danish.txt index a26e3486e1..1046565825 100644 --- a/src/lang/danish.txt +++ b/src/lang/danish.txt @@ -940,8 +940,6 @@ STR_GAME_OPTIONS_CURRENCY_HKD :Hong Kong Dolla STR_GAME_OPTIONS_CURRENCY_INR :Indisk rupi (INR) ############ end of currency region -STR_GAME_OPTIONS_ROAD_VEHICLES_FRAME :{BLACK}Vejkøretøjer -STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_TOOLTIP :{BLACK}Vælg den side af vejen, køretøjer skal kører i STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_LEFT :Kør i venstre side STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_RIGHT :Kør i højre side @@ -2822,7 +2820,6 @@ STR_MAPGEN_SNOW_LINE_HEIGHT :{BLACK}Snegræn STR_MAPGEN_SNOW_LINE_UP :{BLACK}Flyt snegrænsen een op STR_MAPGEN_SNOW_LINE_DOWN :{BLACK}Flyt snegrænsen een ned STR_MAPGEN_LAND_GENERATOR :{BLACK}Terrængenerator: -STR_MAPGEN_TREE_PLACER :{BLACK}Træalgoritme: STR_MAPGEN_TERRAIN_TYPE :{BLACK}Terræntype: STR_MAPGEN_QUANTITY_OF_SEA_LAKES :{BLACK}Havniveau STR_MAPGEN_QUANTITY_OF_RIVERS :{BLACK}Floder: diff --git a/src/lang/dutch.txt b/src/lang/dutch.txt index 38ecc0aaa8..9c8329d6db 100644 --- a/src/lang/dutch.txt +++ b/src/lang/dutch.txt @@ -949,8 +949,6 @@ STR_GAME_OPTIONS_CURRENCY_HKD :Hong Kong Dolla STR_GAME_OPTIONS_CURRENCY_INR :Indiase rupee (INR) ############ end of currency region -STR_GAME_OPTIONS_ROAD_VEHICLES_FRAME :{BLACK}Wegvoertuigen -STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_TOOLTIP :{BLACK}Kiest aan welke kant van de weg voertuigen moeten rijden STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_LEFT :Links rijden STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_RIGHT :Rechts rijden @@ -1005,6 +1003,7 @@ STR_GAME_OPTIONS_RESOLUTION_OTHER :Anders STR_GAME_OPTIONS_GUI_ZOOM_FRAME :{BLACK}Menupuntgrootte STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_TOOLTIP :{BLACK}Kiest de grootte van bedieningselementen +STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_AUTO :(automatische detectie) STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_NORMAL :Normaal STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_2X_ZOOM :Dubbele grootte STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_4X_ZOOM :4x Grootte @@ -1012,6 +1011,7 @@ STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_4X_ZOOM :4x Grootte STR_GAME_OPTIONS_FONT_ZOOM :{BLACK}Lettergrootte STR_GAME_OPTIONS_FONT_ZOOM_DROPDOWN_TOOLTIP :{BLACK}Hiermee kies je hoe groot de letters op het scherm moeten zijn +STR_GAME_OPTIONS_FONT_ZOOM_DROPDOWN_AUTO :(auto-detect)Status: Vermist STR_GAME_OPTIONS_FONT_ZOOM_DROPDOWN_NORMAL :Normaal STR_GAME_OPTIONS_FONT_ZOOM_DROPDOWN_2X_ZOOM :Dubbele grootte STR_GAME_OPTIONS_FONT_ZOOM_DROPDOWN_4X_ZOOM :Viervoudige grootte @@ -2606,12 +2606,18 @@ STR_FOUND_TOWN_SELECT_LAYOUT_RANDOM :{BLACK}Willekeu # Fund new industry window STR_FUND_INDUSTRY_CAPTION :{WHITE}Nieuwe industrie financieren STR_FUND_INDUSTRY_SELECTION_TOOLTIP :{BLACK}Kiest de gewenste industrie in de lijst -STR_FUND_INDUSTRY_MANY_RANDOM_INDUSTRIES :Veel willekeurige industrieën +STR_FUND_INDUSTRY_MANY_RANDOM_INDUSTRIES :{BLACK}Willekeurige industrieën maken STR_FUND_INDUSTRY_MANY_RANDOM_INDUSTRIES_TOOLTIP :{BLACK}Bedek de kaart met willekeurig geplaatste industrieën +STR_FUND_INDUSTRY_MANY_RANDOM_INDUSTRIES_CAPTION :{WHITE}Willekeurige industrieën maken +STR_FUND_INDUSTRY_MANY_RANDOM_INDUSTRIES_QUERY :{YELLOW}Weet je zeker dat je veel willekeurige industrieën wilt hebben? STR_FUND_INDUSTRY_INDUSTRY_BUILD_COST :{BLACK}Kosten: {YELLOW}{CURRENCY_LONG} STR_FUND_INDUSTRY_PROSPECT_NEW_INDUSTRY :{BLACK}Onderzoeken STR_FUND_INDUSTRY_BUILD_NEW_INDUSTRY :{BLACK}Bouw STR_FUND_INDUSTRY_FUND_NEW_INDUSTRY :{BLACK}Betaal +STR_FUND_INDUSTRY_REMOVE_ALL_INDUSTRIES :{BLACK}Alle industrieën verwijderen +STR_FUND_INDUSTRY_REMOVE_ALL_INDUSTRIES_TOOLTIP :{BLACK}Alle industrieën op de kaart verwijderen +STR_FUND_INDUSTRY_REMOVE_ALL_INDUSTRIES_CAPTION :{WHITE}Alle industrieën verwijderen +STR_FUND_INDUSTRY_REMOVE_ALL_INDUSTRIES_QUERY :{YELLOW}Weet je zeker dat je alle industrieën wilt verwijderen? # Industry cargoes window STR_INDUSTRY_CARGOES_INDUSTRY_CAPTION :{WHITE}Industrieketen voor {STRING} industrie @@ -2842,6 +2848,8 @@ STR_SAVELOAD_DETAIL_GRFSTATUS :{SILVER}NewGRF: STR_SAVELOAD_FILTER_TITLE :{BLACK}Filtertekenreeks: STR_SAVELOAD_OVERWRITE_TITLE :{WHITE}Bestand overschrijven STR_SAVELOAD_OVERWRITE_WARNING :{YELLOW}Weet je zeker dat je het bestaande bestand wilt overschrijven? +STR_SAVELOAD_DIRECTORY :{STRING} (Map) +STR_SAVELOAD_PARENT_DIRECTORY :{STRING} (Bovenliggende map) STR_SAVELOAD_OSKTITLE :{BLACK}Voer een naam in voor het spel dat moet worden opgeslagen @@ -2860,7 +2868,6 @@ STR_MAPGEN_SNOW_LINE_HEIGHT :{BLACK}Hoogte v STR_MAPGEN_SNOW_LINE_UP :{BLACK}Verhoog de sneeuwgrens met één STR_MAPGEN_SNOW_LINE_DOWN :{BLACK}Verlaag de sneeuwgrens met één STR_MAPGEN_LAND_GENERATOR :{BLACK}Landgenerator: -STR_MAPGEN_TREE_PLACER :{BLACK}Bosalgoritme: STR_MAPGEN_TERRAIN_TYPE :{BLACK}Terreintype: STR_MAPGEN_QUANTITY_OF_SEA_LAKES :{BLACK}Zeeniveau: STR_MAPGEN_QUANTITY_OF_RIVERS :{BLACK}Rivieren: diff --git a/src/lang/english.txt b/src/lang/english.txt index 6dac2fbb06..85d1850431 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -622,7 +622,7 @@ STR_COMPANY_LEAGUE_PERFORMANCE_TITLE_TRANSPORT_COORDINATOR :Transport Coord STR_COMPANY_LEAGUE_PERFORMANCE_TITLE_ROUTE_SUPERVISOR :Route Supervisor STR_COMPANY_LEAGUE_PERFORMANCE_TITLE_DIRECTOR :Director STR_COMPANY_LEAGUE_PERFORMANCE_TITLE_CHIEF_EXECUTIVE :Chief Executive -STR_COMPANY_LEAGUE_PERFORMANCE_TITLE_CHAIRMAN :Chairman +STR_COMPANY_LEAGUE_PERFORMANCE_TITLE_CHAIRMAN :Chairperson STR_COMPANY_LEAGUE_PERFORMANCE_TITLE_PRESIDENT :President STR_COMPANY_LEAGUE_PERFORMANCE_TITLE_TYCOON :Tycoon @@ -705,7 +705,7 @@ STR_PLAYLIST_TOOLTIP_CLICK_TO_REMOVE_TRACK :{BLACK}Click on STR_HIGHSCORE_TOP_COMPANIES_WHO_REACHED :{BIG_FONT}{BLACK}Top companies who reached {NUM} STR_HIGHSCORE_TOP_COMPANIES_NETWORK_GAME :{BIG_FONT}{BLACK}Company League Table in {NUM} STR_HIGHSCORE_POSITION :{BIG_FONT}{BLACK}{COMMA}. -STR_HIGHSCORE_PERFORMANCE_TITLE_BUSINESSMAN :Businessman +STR_HIGHSCORE_PERFORMANCE_TITLE_BUSINESSMAN :Businessperson STR_HIGHSCORE_PERFORMANCE_TITLE_ENTREPRENEUR :Entrepreneur STR_HIGHSCORE_PERFORMANCE_TITLE_INDUSTRIALIST :Industrialist STR_HIGHSCORE_PERFORMANCE_TITLE_CAPITALIST :Capitalist @@ -959,10 +959,9 @@ STR_GAME_OPTIONS_CURRENCY_NTD :New Taiwan Doll STR_GAME_OPTIONS_CURRENCY_CNY :Chinese Renminbi (CNY) STR_GAME_OPTIONS_CURRENCY_HKD :Hong Kong Dollar (HKD) STR_GAME_OPTIONS_CURRENCY_INR :Indian Rupee (INR) +STR_GAME_OPTIONS_CURRENCY_IDR :Indonesian Rupiah (IDR) ############ end of currency region -STR_GAME_OPTIONS_ROAD_VEHICLES_FRAME :{BLACK}Road vehicles -STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_TOOLTIP :{BLACK}Select side of road for vehicles to drive on STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_LEFT :Drive on left STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_RIGHT :Drive on right @@ -1017,6 +1016,7 @@ STR_GAME_OPTIONS_RESOLUTION_OTHER :other STR_GAME_OPTIONS_GUI_ZOOM_FRAME :{BLACK}Interface size STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_TOOLTIP :{BLACK}Select the interface element size to use +STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_AUTO :(auto-detect) STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_NORMAL :Normal STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_2X_ZOOM :Double size STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_4X_ZOOM :Quad size @@ -1024,6 +1024,7 @@ STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_4X_ZOOM :Quad size STR_GAME_OPTIONS_FONT_ZOOM :{BLACK}Font size STR_GAME_OPTIONS_FONT_ZOOM_DROPDOWN_TOOLTIP :{BLACK}Select the interface font size to use +STR_GAME_OPTIONS_FONT_ZOOM_DROPDOWN_AUTO :(auto-detect) STR_GAME_OPTIONS_FONT_ZOOM_DROPDOWN_NORMAL :Normal STR_GAME_OPTIONS_FONT_ZOOM_DROPDOWN_2X_ZOOM :Double size STR_GAME_OPTIONS_FONT_ZOOM_DROPDOWN_4X_ZOOM :Quad size @@ -3291,12 +3292,18 @@ STR_FOUND_TOWN_SELECT_LAYOUT_RANDOM :{BLACK}Random # Fund new industry window STR_FUND_INDUSTRY_CAPTION :{WHITE}Fund new industry STR_FUND_INDUSTRY_SELECTION_TOOLTIP :{BLACK}Choose the appropriate industry from this list -STR_FUND_INDUSTRY_MANY_RANDOM_INDUSTRIES :Many random industries +STR_FUND_INDUSTRY_MANY_RANDOM_INDUSTRIES :{BLACK}Create random industries STR_FUND_INDUSTRY_MANY_RANDOM_INDUSTRIES_TOOLTIP :{BLACK}Cover the map with randomly placed industries +STR_FUND_INDUSTRY_MANY_RANDOM_INDUSTRIES_CAPTION :{WHITE}Create random industries +STR_FUND_INDUSTRY_MANY_RANDOM_INDUSTRIES_QUERY :{YELLOW}Are you sure you want to create many random industries? STR_FUND_INDUSTRY_INDUSTRY_BUILD_COST :{BLACK}Cost: {YELLOW}{CURRENCY_LONG} STR_FUND_INDUSTRY_PROSPECT_NEW_INDUSTRY :{BLACK}Prospect STR_FUND_INDUSTRY_BUILD_NEW_INDUSTRY :{BLACK}Build STR_FUND_INDUSTRY_FUND_NEW_INDUSTRY :{BLACK}Fund +STR_FUND_INDUSTRY_REMOVE_ALL_INDUSTRIES :{BLACK}Remove all industries +STR_FUND_INDUSTRY_REMOVE_ALL_INDUSTRIES_TOOLTIP :{BLACK}Remove all industries currently present on the map +STR_FUND_INDUSTRY_REMOVE_ALL_INDUSTRIES_CAPTION :{WHITE}Remove all industries +STR_FUND_INDUSTRY_REMOVE_ALL_INDUSTRIES_QUERY :{YELLOW}Are you sure you want to remove all industries? # Industry cargoes window STR_INDUSTRY_CARGOES_INDUSTRY_CAPTION :{WHITE}Industry chain for {STRING} industry @@ -3541,6 +3548,8 @@ STR_SAVELOAD_DETAIL_GRFSTATUS :{SILVER}NewGRF: STR_SAVELOAD_FILTER_TITLE :{BLACK}Filter string: STR_SAVELOAD_OVERWRITE_TITLE :{WHITE}Overwrite File STR_SAVELOAD_OVERWRITE_WARNING :{YELLOW}Are you sure you want to overwrite the existing file? +STR_SAVELOAD_DIRECTORY :{RAW_STRING} (Directory) +STR_SAVELOAD_PARENT_DIRECTORY :{RAW_STRING} (Parent directory) STR_SAVELOAD_UNKNOWN_ID :{YELLOW}This file may be from a different game. STR_SAVELOAD_DIFFERENT_ID :{YELLOW}This file is from a different game. STR_SAVELOAD_OVERWRITE_TITLE_DIFFERENT_ID :{WHITE}Overwrite File (Different Game) @@ -3563,7 +3572,6 @@ STR_MAPGEN_SNOW_LINE_HEIGHT :{BLACK}Snow lin STR_MAPGEN_SNOW_LINE_UP :{BLACK}Move the snow line height one up STR_MAPGEN_SNOW_LINE_DOWN :{BLACK}Move the snow line height one down STR_MAPGEN_LAND_GENERATOR :{BLACK}Land generator: -STR_MAPGEN_TREE_PLACER :{BLACK}Tree algorithm: STR_MAPGEN_TERRAIN_TYPE :{BLACK}Terrain type: STR_MAPGEN_QUANTITY_OF_SEA_LAKES :{BLACK}Sea level: STR_MAPGEN_QUANTITY_OF_RIVERS :{BLACK}Rivers: diff --git a/src/lang/english_AU.txt b/src/lang/english_AU.txt index 2757c46d64..b78f80e54d 100644 --- a/src/lang/english_AU.txt +++ b/src/lang/english_AU.txt @@ -909,8 +909,6 @@ STR_GAME_OPTIONS_CURRENCY_GEL :Georgian Lari ( STR_GAME_OPTIONS_CURRENCY_IRR :Iranian Rial (IRR) ############ end of currency region -STR_GAME_OPTIONS_ROAD_VEHICLES_FRAME :{BLACK}Road vehicles -STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_TOOLTIP :{BLACK}Select side of road for vehicles to drive on STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_LEFT :Drive on left STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_RIGHT :Drive on right @@ -2670,7 +2668,6 @@ STR_MAPGEN_SNOW_LINE_HEIGHT :{BLACK}Snow lin STR_MAPGEN_SNOW_LINE_UP :{BLACK}Move the snow line height one up STR_MAPGEN_SNOW_LINE_DOWN :{BLACK}Move the snow line height one down STR_MAPGEN_LAND_GENERATOR :{BLACK}Land generator: -STR_MAPGEN_TREE_PLACER :{BLACK}Tree algorithm: STR_MAPGEN_TERRAIN_TYPE :{BLACK}Terrain type: STR_MAPGEN_QUANTITY_OF_SEA_LAKES :{BLACK}Sea level: STR_MAPGEN_QUANTITY_OF_RIVERS :{BLACK}Rivers: diff --git a/src/lang/english_US.txt b/src/lang/english_US.txt index e5523db16d..62f7dcecd3 100644 --- a/src/lang/english_US.txt +++ b/src/lang/english_US.txt @@ -938,8 +938,6 @@ STR_GAME_OPTIONS_CURRENCY_CNY :Chinese Renminb STR_GAME_OPTIONS_CURRENCY_HKD :Hong Kong Dollar (HKD) ############ end of currency region -STR_GAME_OPTIONS_ROAD_VEHICLES_FRAME :{BLACK}Road vehicles -STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_TOOLTIP :{BLACK}Select side of road for vehicles to drive on STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_LEFT :Drive on left STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_RIGHT :Drive on right @@ -2817,7 +2815,6 @@ STR_MAPGEN_SNOW_LINE_HEIGHT :{BLACK}Snow lin STR_MAPGEN_SNOW_LINE_UP :{BLACK}Move the snow line height up one STR_MAPGEN_SNOW_LINE_DOWN :{BLACK}Move the snow line height down one STR_MAPGEN_LAND_GENERATOR :{BLACK}Land generator: -STR_MAPGEN_TREE_PLACER :{BLACK}Tree algorithm: STR_MAPGEN_TERRAIN_TYPE :{BLACK}Terrain type: STR_MAPGEN_QUANTITY_OF_SEA_LAKES :{BLACK}Sea level: STR_MAPGEN_QUANTITY_OF_RIVERS :{BLACK}Rivers: diff --git a/src/lang/esperanto.txt b/src/lang/esperanto.txt index 5fea11b8e8..c1c78d64da 100644 --- a/src/lang/esperanto.txt +++ b/src/lang/esperanto.txt @@ -902,8 +902,6 @@ STR_GAME_OPTIONS_CURRENCY_GEL :Kartvela Lario STR_GAME_OPTIONS_CURRENCY_IRR :Irana Rialo (IRR) ############ end of currency region -STR_GAME_OPTIONS_ROAD_VEHICLES_FRAME :{BLACK}Stratveturiloj -STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_TOOLTIP :{BLACK}Elektu stratflankon kie veturu veturiloj STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_LEFT :Veturu maldekstre STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_RIGHT :Veturu dekstre @@ -2278,7 +2276,6 @@ STR_MAPGEN_SNOW_LINE_HEIGHT :{BLACK}Neĝlini STR_MAPGEN_SNOW_LINE_UP :{BLACK}Altigu la neĝlinion per unu STR_MAPGEN_SNOW_LINE_DOWN :{BLACK}Malaltigu la neĝlinion per unu STR_MAPGEN_LAND_GENERATOR :{BLACK}Landgenerilo: -STR_MAPGEN_TREE_PLACER :{BLACK}Arba algoritmo: STR_MAPGEN_TERRAIN_TYPE :{BLACK}Terentipo: STR_MAPGEN_QUANTITY_OF_SEA_LAKES :{BLACK}Marnivelo: STR_MAPGEN_QUANTITY_OF_RIVERS :{BLACK}Riveroj: diff --git a/src/lang/estonian.txt b/src/lang/estonian.txt index 04dd581a03..a6bb5b2364 100644 --- a/src/lang/estonian.txt +++ b/src/lang/estonian.txt @@ -387,7 +387,7 @@ STR_TOOLBAR_TOOLTIP_FORWARD :{BLACK}Kiirenda STR_TOOLBAR_TOOLTIP_OPTIONS :{BLACK}Seadistus STR_TOOLBAR_TOOLTIP_SAVE_GAME_ABANDON_GAME :{BLACK}Mängu salvestamine/jätkamine, mängust lahkumine, väljumine STR_TOOLBAR_TOOLTIP_DISPLAY_MAP :{BLACK}Ava kaart, vaateaken või siltide loend -STR_TOOLBAR_TOOLTIP_DISPLAY_TOWN_DIRECTORY :{BLACK}Ava linnade register +STR_TOOLBAR_TOOLTIP_DISPLAY_TOWN_DIRECTORY :{BLACK}Ava asustuste register STR_TOOLBAR_TOOLTIP_DISPLAY_SUBSIDIES :{BLACK}Ava toetuste loend STR_TOOLBAR_TOOLTIP_DISPLAY_LIST_OF_COMPANY_STATIONS :{BLACK}Avab ettevõtte jaamade registri STR_TOOLBAR_TOOLTIP_DISPLAY_COMPANY_FINANCES :{BLACK}Ava ettevõtte rahavoogude aruanne @@ -421,9 +421,9 @@ STR_SCENEDIT_TOOLBAR_SCENARIO_EDITOR :{YELLOW}Stsenaa STR_SCENEDIT_TOOLBAR_TOOLTIP_MOVE_THE_STARTING_DATE_BACKWARD :{BLACK}Alguse liigutamine 1 aasta võrra tagasi STR_SCENEDIT_TOOLBAR_TOOLTIP_MOVE_THE_STARTING_DATE_FORWARD :{BLACK}Alguse liigutamine 1 aasta võrra edasi STR_SCENEDIT_TOOLBAR_TOOLTIP_SET_DATE :{BLACK}Alustamise aasta valikuks klõpsi siin -STR_SCENEDIT_TOOLBAR_TOOLTIP_DISPLAY_MAP_TOWN_DIRECTORY :{BLACK}Ava kaart, linnade register +STR_SCENEDIT_TOOLBAR_TOOLTIP_DISPLAY_MAP_TOWN_DIRECTORY :{BLACK}Ava kaart, asustuste register STR_SCENEDIT_TOOLBAR_LANDSCAPE_GENERATION :{BLACK}Maastiku tekitamine -STR_SCENEDIT_TOOLBAR_TOWN_GENERATION :{BLACK}Linnade tekitamine +STR_SCENEDIT_TOOLBAR_TOWN_GENERATION :{BLACK}Asustuste tekitamine STR_SCENEDIT_TOOLBAR_INDUSTRY_GENERATION :{BLACK}Tööstuste tekitamine STR_SCENEDIT_TOOLBAR_ROAD_CONSTRUCTION :{BLACK}Maanteede ehitamine STR_SCENEDIT_TOOLBAR_TRAM_CONSTRUCTION :{BLACK}Trammiteede ehitamine @@ -447,7 +447,7 @@ STR_SETTINGS_MENU_CONFIG_SETTINGS_TREE :Seaded STR_SETTINGS_MENU_SCRIPT_SETTINGS :AI/GameScripti seaded STR_SETTINGS_MENU_NEWGRF_SETTINGS :NewGRF-i seadistus STR_SETTINGS_MENU_TRANSPARENCY_OPTIONS :Läbipaistvuse seadistus -STR_SETTINGS_MENU_TOWN_NAMES_DISPLAYED :Näidatavad linnanimed +STR_SETTINGS_MENU_TOWN_NAMES_DISPLAYED :Näidatavad asustuste nimed STR_SETTINGS_MENU_STATION_NAMES_DISPLAYED :Näidatavad jaamanimed STR_SETTINGS_MENU_WAYPOINTS_DISPLAYED :Näidatavad meldepunktid STR_SETTINGS_MENU_SIGNS_DISPLAYED :Näidatavad sildid @@ -473,8 +473,8 @@ STR_MAP_MENU_LINGRAPH_LEGEND :Kaubavoo legend STR_MAP_MENU_SIGN_LIST :Siltide register ############ range for town menu starts -STR_TOWN_MENU_TOWN_DIRECTORY :Linnade register -STR_TOWN_MENU_FOUND_TOWN :Raja linn +STR_TOWN_MENU_TOWN_DIRECTORY :Asustuste register +STR_TOWN_MENU_FOUND_TOWN :Raja asustus ############ range ends here ############ range for subsidies menu starts @@ -811,12 +811,12 @@ STR_SMALLMAP_LEGENDA_TREES :{TINY_FONT}{BLA STR_SMALLMAP_LEGENDA_ROCKS :{TINY_FONT}{BLACK}Kivid STR_SMALLMAP_LEGENDA_WATER :{TINY_FONT}{BLACK}Vesi STR_SMALLMAP_LEGENDA_NO_OWNER :{TINY_FONT}{BLACK}Omanikuta -STR_SMALLMAP_LEGENDA_TOWNS :{TINY_FONT}{BLACK}Linnad +STR_SMALLMAP_LEGENDA_TOWNS :{TINY_FONT}{BLACK}Asustused STR_SMALLMAP_LEGENDA_INDUSTRIES :{TINY_FONT}{BLACK}Tööstused STR_SMALLMAP_LEGENDA_DESERT :{TINY_FONT}{BLACK}Kõrb STR_SMALLMAP_LEGENDA_SNOW :{TINY_FONT}{BLACK}Lumi -STR_SMALLMAP_TOOLTIP_TOGGLE_TOWN_NAMES_ON_OFF :{BLACK}Linnanimede sisse- ja välja lülitamine +STR_SMALLMAP_TOOLTIP_TOGGLE_TOWN_NAMES_ON_OFF :{BLACK}Asustuste nimede sisse- ja välja lülitamine STR_SMALLMAP_CENTER :{BLACK}Näita väikekaardil praegust asukohta STR_SMALLMAP_INDUSTRY :{TINY_FONT}{STRING} ({NUM}) STR_SMALLMAP_LINKSTATS :{TINY_FONT}{STRING} @@ -868,7 +868,7 @@ STR_NEWS_DISASTER_SMALL_UFO :{BIG_FONT}{BLAC STR_NEWS_DISASTER_AIRPLANE_OIL_REFINERY :{BIG_FONT}{BLACK}Naftatöötlustehase plahvatus {TOWN} lähedal! STR_NEWS_DISASTER_HELICOPTER_FACTORY :{BIG_FONT}{BLACK}Tehas purunes teadmata põhjustel {TOWN} lähedal! STR_NEWS_DISASTER_BIG_UFO :{BIG_FONT}{BLACK}'UFO' maandus {TOWN} lähedal! -STR_NEWS_DISASTER_COAL_MINE_SUBSIDENCE :{BIG_FONT}{BLACK}Söekaevanduse vajumine jättis endast linna {TOWN} lähedal purustused! +STR_NEWS_DISASTER_COAL_MINE_SUBSIDENCE :{BIG_FONT}{BLACK}Söekaevanduse vajumine jättis endast asustuse {TOWN} lähedal purustused! STR_NEWS_DISASTER_FLOOD_VEHICLE :{BIG_FONT}{BLACK}Üleujutused!{}Vähemalt {COMMA} kadunut arvatakse surnuks peale olulist üleujutust! STR_NEWS_COMPANY_IN_TROUBLE_TITLE :{BIG_FONT}{BLACK}Ettevõttel on probleeme! @@ -878,15 +878,15 @@ STR_NEWS_COMPANY_MERGER_DESCRIPTION :{BIG_FONT}{BLAC STR_NEWS_COMPANY_BANKRUPT_TITLE :{BIG_FONT}{BLACK}Pankrot! STR_NEWS_COMPANY_BANKRUPT_DESCRIPTION :{BIG_FONT}{BLACK}{STRING} suleti asutajate poolt ja kõik varad müüakse maha! STR_NEWS_COMPANY_LAUNCH_TITLE :{BIG_FONT}{BLACK}Loodi uus ettevõte! -STR_NEWS_COMPANY_LAUNCH_DESCRIPTION :{BIG_FONT}{BLACK}Ettevõte {STRING} alustas linna {TOWN} lähedal ehitustöid! +STR_NEWS_COMPANY_LAUNCH_DESCRIPTION :{BIG_FONT}{BLACK}Ettevõte {STRING} alustas asustuse {TOWN} lähedal ehitustöid! STR_NEWS_MERGER_TAKEOVER_TITLE :{BIG_FONT}{BLACK}{STRING} võeti üle ettevõtte {STRING} poolt! STR_PRESIDENT_NAME_MANAGER :{BLACK}{PRESIDENT_NAME}{}(President) -STR_NEWS_NEW_TOWN :{BLACK}{BIG_FONT}{STRING} rahastas uue linna {TOWN} rajamist! +STR_NEWS_NEW_TOWN :{BLACK}{BIG_FONT}{STRING} rahastas uue asustuse {TOWN} rajamist! STR_NEWS_NEW_TOWN_UNSPONSORED :{BLACK}{BIG_FONT}Uus linn nimega {TOWN} on asutatud! -STR_NEWS_INDUSTRY_CONSTRUCTION :{BIG_FONT}{BLACK}Uus {STRING} on linna {TOWN} lähedal ehitamisel! -STR_NEWS_INDUSTRY_PLANTED :{BIG_FONT}{BLACK}Uus {STRING} on istutatud linna {TOWN} lähedale! +STR_NEWS_INDUSTRY_CONSTRUCTION :{BIG_FONT}{BLACK}Uus {STRING} on asustuse {TOWN} lähedal ehitamisel! +STR_NEWS_INDUSTRY_PLANTED :{BIG_FONT}{BLACK}Uus {STRING} on istutatud asustuse {TOWN} lähedale! STR_NEWS_INDUSTRY_CLOSURE_GENERAL :{BIG_FONT}{BLACK}{STRING} juhtkond teatab sulgemisest! STR_NEWS_INDUSTRY_CLOSURE_SUPPLY_PROBLEMS :{BIG_FONT}{BLACK}{STRING} teatab sulgemisest varustusprobleemide tõttu! @@ -941,15 +941,15 @@ STR_NEWS_STATION_NOW_ACCEPTS_CARGO_AND_CARGO :{WHITE}Jaam {ST STR_NEWS_OFFER_OF_SUBSIDY_EXPIRED :{BIG_FONT}{BLACK}Toetuse pakkumine lõppes:{}{}{STRING.g} kohast {STRING} kohta {STRING} veoste eest ei maksta enam toetusi STR_NEWS_SUBSIDY_WITHDRAWN_SERVICE :{BIG_FONT}{BLACK}Toetuse maksmise aeg sai läbi:{}{}{STRING}kohast {STRING} kohta {STRING} eest ei maksta enam edaspidi toetust -STR_NEWS_SERVICE_SUBSIDY_OFFERED :{BIG_FONT}{BLACK}Veoteenusele pakutakse toetust:{}{}{STRING.g}veo eest linnast {STRING} linna {STRING}. Esimesele teenusepakkujale makstakse aasta läbi toetusi! +STR_NEWS_SERVICE_SUBSIDY_OFFERED :{BIG_FONT}{BLACK}Veoteenusele pakutakse toetust:{}{}{STRING.g}veo eest asustusest {STRING} asustusse {STRING}. Esimesele teenusepakkujale makstakse aasta läbi toetusi! STR_NEWS_SERVICE_SUBSIDY_AWARDED_HALF :{BIG_FONT}{BLACK}Teenusetoetust makstakse ettevõttele {STRING}!{}{}{STRING} kohast {STRING} kohta {STRING} teenuse eest makstakse järgmisel aastal 50% rohkem! STR_NEWS_SERVICE_SUBSIDY_AWARDED_DOUBLE :{BIG_FONT}{BLACK}Teenusetoetust makstakse ettevõttele {STRING}!{}{}{STRING} kohast {STRING} kohta {STRING} teenuse eest makstakse järgmisel aastal kahekordselt! STR_NEWS_SERVICE_SUBSIDY_AWARDED_TRIPLE :{BIG_FONT}{BLACK}Teenusetoetust makstakse ettevõttele {STRING}!{}{}{STRING} kohast {STRING} kohta {STRING} teenuse eest makstakse järgmisel aastal kolmekordselt! STR_NEWS_SERVICE_SUBSIDY_AWARDED_QUADRUPLE :{BIG_FONT}{BLACK}Teenusetoetust makstakse ettevõttele {STRING}!{}{}{STRING} kohast {STRING} kohta {STRING} teenuse eest makstakse järgmisel aastal neljakordselt! -STR_NEWS_ROAD_REBUILDING :{BIG_FONT}{BLACK}Liikluskaos linnas {TOWN}!{}{}Ettevõtte {STRING} poolt rahastatud teedeehitus tekitab 6 kuu jooksul maanteedel liiklushäireid! +STR_NEWS_ROAD_REBUILDING :{BIG_FONT}{BLACK}Liikluskaos asustuses {TOWN}!{}{}Ettevõtte {STRING} poolt rahastatud teede ümberehitus tekitab 6 kuu jooksul maanteedel liiklushäireid! STR_NEWS_EXCLUSIVE_RIGHTS_TITLE :{BIG_FONT}{BLACK}Transpordimonopol! -STR_NEWS_EXCLUSIVE_RIGHTS_DESCRIPTION :{BIG_FONT}{BLACK}Kohalik linnavõim{TOWN} allkirjastab lepingu {STRING} transpordi ainuõiguseks üheks aastaks! +STR_NEWS_EXCLUSIVE_RIGHTS_DESCRIPTION :{BIG_FONT}{BLACK}Asustuse {TOWN} omavalitsus allkirjastab lepingu {STRING} veoste ainuõiguseks üheks aastaks! # Extra view window STR_EXTRA_VIEWPORT_TITLE :{WHITE}Vaateaken {COMMA} @@ -1006,13 +1006,11 @@ STR_GAME_OPTIONS_CURRENCY_HKD :Hong Kongi doll STR_GAME_OPTIONS_CURRENCY_INR :India ruupia (INR) ############ end of currency region -STR_GAME_OPTIONS_ROAD_VEHICLES_FRAME :{BLACK}Sõidukid -STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_TOOLTIP :{BLACK}Vali, kummal pool teed mootorsõidukid liiklevad STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_LEFT :Vasakpoolne liiklus STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_RIGHT :Parempoolne liiklus -STR_GAME_OPTIONS_TOWN_NAMES_FRAME :{BLACK}Linnanimed -STR_GAME_OPTIONS_TOWN_NAMES_DROPDOWN_TOOLTIP :{BLACK}Vali linnanimede stiil +STR_GAME_OPTIONS_TOWN_NAMES_FRAME :{BLACK}Asulate nimed +STR_GAME_OPTIONS_TOWN_NAMES_DROPDOWN_TOOLTIP :{BLACK}Vali asulanimede stiil ############ start of townname region STR_GAME_OPTIONS_TOWN_NAME_ORIGINAL_ENGLISH :Inglise (originaalne) @@ -1245,7 +1243,7 @@ STR_CONFIG_SETTING_TRAIN_REVERSING :Keela rongide STR_CONFIG_SETTING_TRAIN_REVERSING_HELPTEXT :Kui on aktiveeritud, siis ei pööra rongid vahejaamades ringi isegi siis kui ringi pööramisel oleks teekond järgmisse sihtpunkti lühem STR_CONFIG_SETTING_DISASTERS :Katastroofid: {STRING} STR_CONFIG_SETTING_DISASTERS_HELPTEXT :Lülita sisse/välja katastroofid, mis aeg-ajalt hävitavad sõidukeid ja taristut -STR_CONFIG_SETTING_CITY_APPROVAL :Linnavalitusese suhtumine pinnase restruktureerimisse: {STRING} +STR_CONFIG_SETTING_CITY_APPROVAL :Astuse omavalitsuse suhtumine pinnase restruktureerimisse: {STRING} STR_CONFIG_SETTING_CITY_APPROVAL_HELPTEXT :Vali kui suurt mõju tekitavad müra ning keskkonna muudatused ettevõtte mainele ja edasistele ehitustoimingutele antud piirkonnas STR_CONFIG_SETTING_MAX_HEIGHTLEVEL :Maksimaalne mägede kõrgus: {STRING} @@ -1257,8 +1255,8 @@ STR_CONFIG_SETTING_CATCHMENT :Reaalsemad veek STR_CONFIG_SETTING_CATCHMENT_HELPTEXT :Erineva suurusega püüdmisala erinevat tüüpi jaamadele ja lennujaamadele. STR_CONFIG_SETTING_SERVE_NEUTRAL_INDUSTRIES :Ettevõtte jaamad teenindavad ka liidetud, erapooletu jaamaga tööstuseid: {STRING} STR_CONFIG_SETTING_SERVE_NEUTRAL_INDUSTRIES_HELPTEXT :Kui lubatud, saavad liidetud jaamaga tööstuseid (nagu naftaplatvormid) teenindada ka lähedalolevad ettevõttele kuuluvad jaamad. Muidu võivad neid tööstuseid teenindada vaid nendega liidetud jaamad. Lähedale ehitatud ettevõtte jaamad neid teenindada ei saa, ning liidetud jaamad ei teeninda teisi tööstuseid -STR_CONFIG_SETTING_EXTRADYNAMITE :Linnateede, -sildade, -tunnelite jne lammutamine: {STRING} -STR_CONFIG_SETTING_EXTRADYNAMITE_HELPTEXT :Lihtsusta linnale kuuluva taristu ja majade eemaldamist +STR_CONFIG_SETTING_EXTRADYNAMITE :Asulateede, -sildade, -tunnelite jne lammutamine: {STRING} +STR_CONFIG_SETTING_EXTRADYNAMITE_HELPTEXT :Lihtsusta asulatele kuuluva taristu ja hoonete eemaldamist STR_CONFIG_SETTING_TRAIN_LENGTH :Rongi pikkuse ülempiir: {STRING} STR_CONFIG_SETTING_TRAIN_LENGTH_HELPTEXT :Määrab rongide pikkuse ülempiiri STR_CONFIG_SETTING_TILE_LENGTH :{COMMA} ruut{P 0 "" u} @@ -1292,8 +1290,8 @@ STR_CONFIG_SETTING_RAW_INDUSTRY_CONSTRUCTION_METHOD_NORMAL :Nagu kõik tö STR_CONFIG_SETTING_RAW_INDUSTRY_CONSTRUCTION_METHOD_PROSPECTING :Uuringutega STR_CONFIG_SETTING_INDUSTRY_PLATFORM :Tasane ala ümber tehaste: {STRING} STR_CONFIG_SETTING_INDUSTRY_PLATFORM_HELPTEXT :Summa kui palju tasast maad on ümber tehase. See tagab et ruumi jääb saadavale ehitiste jaoks -STR_CONFIG_SETTING_MULTIPINDTOWN :Samalaadsed tööstused ühes linnas: {STRING} -STR_CONFIG_SETTING_MULTIPINDTOWN_HELPTEXT :Tavaliselt, linn ei taha rohkem kui ühte tööstustharu igast tüübist. See seade lubab rohkem kui ühe tööstusharu samasse linna +STR_CONFIG_SETTING_MULTIPINDTOWN :Mitu samalaadset tööstust ühe asula piires: {STRING} +STR_CONFIG_SETTING_MULTIPINDTOWN_HELPTEXT :Tavaliselt asula ei taha rohkem kui ühte tööstustharu igast liigist. Selle seadega lubatakse rohkem kui üks samalaadne tööstus ühe asula piires STR_CONFIG_SETTING_SIGNALSIDE :Signaale näidatakse: {STRING} STR_CONFIG_SETTING_SIGNALSIDE_HELPTEXT :Vali kummale poole rada signaalid paigutatakse STR_CONFIG_SETTING_SIGNALSIDE_LEFT :Vasakul @@ -1315,13 +1313,13 @@ STR_CONFIG_SETTING_AUTOSCROLL_MAIN_VIEWPORT_FULLSCREEN :Põhivaates, ai STR_CONFIG_SETTING_AUTOSCROLL_MAIN_VIEWPORT :Põhivaates STR_CONFIG_SETTING_AUTOSCROLL_EVERY_VIEWPORT :Igas vaates STR_CONFIG_SETTING_BRIBE :Altkäemaksud kohalikele omavalitsustele: {STRING} -STR_CONFIG_SETTING_BRIBE_HELPTEXT :Võimaldab linna omavalitsusele altkäemaksu pakkuda. Vahele jäämise korral võetakse ettevõttelt kuueks kuuks ära õigus tegutseda linnas +STR_CONFIG_SETTING_BRIBE_HELPTEXT :Võimaldab asulate omavalitsustele altkäemaksu pakkuda. Tabamise korral võetakse ettevõttelt kuueks kuuks ära õigus asulas tegutseda STR_CONFIG_SETTING_ALLOW_EXCLUSIVE :Ainuveoõiguste ostmine: {STRING} -STR_CONFIG_SETTING_ALLOW_EXCLUSIVE_HELPTEXT :Kui ettevõte ostab linnalt kaubaveo ainuõiguse, siis teised ettevõtted ei saa üks aasta uusi kaupu ega reisijaid +STR_CONFIG_SETTING_ALLOW_EXCLUSIVE_HELPTEXT :Kui ettevõte ostab asulalt kaubaveo ainuõiguse, siis teised ettevõtted ei saa üks aasta uusi kaupu ega reisijaid STR_CONFIG_SETTING_ALLOW_FUND_BUILDINGS :Luba ehitiste rahastamine: {STRING} -STR_CONFIG_SETTING_ALLOW_FUND_BUILDINGS_HELPTEXT :Võimaldab ettevõttel linna kinnisvaraehitust rahastada +STR_CONFIG_SETTING_ALLOW_FUND_BUILDINGS_HELPTEXT :Võimaldab ettevõttel asulate kinnisvaraehitust rahastada STR_CONFIG_SETTING_ALLOW_FUND_ROAD :Kohaliku omavalitsuse teede rekonstrueerimise rahastamine: {STRING} -STR_CONFIG_SETTING_ALLOW_FUND_ROAD_HELPTEXT :Võimaldab ettevõtetel linna tee-ehitustöid rahastada, šaboteerides sellega autode kaubavedu +STR_CONFIG_SETTING_ALLOW_FUND_ROAD_HELPTEXT :Võimaldab ettevõtetel asulate tee-ehitustöid rahastada, šaboteerides sellega autode kaubavedu STR_CONFIG_SETTING_ALLOW_GIVE_MONEY :Raha saatmine teistele ettevõtetele: {STRING} STR_CONFIG_SETTING_ALLOW_GIVE_MONEY_HELPTEXT :Luba mitmikmängus kanda raha erinevate ettevõtete vahel STR_CONFIG_SETTING_FREIGHT_TRAINS :Raskete rongide simuleerimiseks kasutatav raskustegur: {STRING} @@ -1334,8 +1332,8 @@ STR_CONFIG_SETTING_PLANE_CRASHES_HELPTEXT :Määrab lennu STR_CONFIG_SETTING_PLANE_CRASHES_NONE :Ei STR_CONFIG_SETTING_PLANE_CRASHES_REDUCED :Vähem STR_CONFIG_SETTING_PLANE_CRASHES_NORMAL :Keskmiselt -STR_CONFIG_SETTING_STOP_ON_TOWN_ROAD :Läbisõidupeatused linnateedel: {STRING} -STR_CONFIG_SETTING_STOP_ON_TOWN_ROAD_HELPTEXT :Lubab linnale kuuluvatel teedel ehitada läbisõidupeatuseid +STR_CONFIG_SETTING_STOP_ON_TOWN_ROAD :Läbisõidupeatused asulate kuuluvatel teedel: {STRING} +STR_CONFIG_SETTING_STOP_ON_TOWN_ROAD_HELPTEXT :Lubab asulatele kuuluvatele teedele ehitada läbisõidupeatuseid STR_CONFIG_SETTING_STOP_ON_COMPETITOR_ROAD :Läbisõidupeatused konkurentide teedel: {STRING} STR_CONFIG_SETTING_STOP_ON_COMPETITOR_ROAD_HELPTEXT :Lubab teisele ettevõttele kuulaval teel ehitada läbisõidupeatuseid STR_CONFIG_SETTING_DYNAMIC_ENGINES_EXISTING_VEHICLES :{WHITE}Sõidukite olemasolul ei saa seda seadet muuta @@ -1374,8 +1372,8 @@ STR_CONFIG_SETTING_HOVER_DELAY :Vihjeid näidat STR_CONFIG_SETTING_HOVER_DELAY_HELPTEXT :Viivitus, kui kaua peab hiirega liidese osale osutama, enne kui selle kohta vihjet näidatakse. On ka võimalik vihjeid parema hiireklõpsuga näidata STR_CONFIG_SETTING_HOVER_DELAY_VALUE :{COMMA} sekundilist osutamist STR_CONFIG_SETTING_HOVER_DELAY_DISABLED :Paremklõpsu -STR_CONFIG_SETTING_POPULATION_IN_LABEL :Linna rahvaarv nimesildil: {STRING} -STR_CONFIG_SETTING_POPULATION_IN_LABEL_HELPTEXT :Näitab linna nimesildil rahvaarvu +STR_CONFIG_SETTING_POPULATION_IN_LABEL :Asula rahvaarv nimesildil: {STRING} +STR_CONFIG_SETTING_POPULATION_IN_LABEL_HELPTEXT :Näitab asula nimesildil rahvaarvu STR_CONFIG_SETTING_GRAPH_LINE_THICKNESS :Graafikujoone laius: {STRING} STR_CONFIG_SETTING_GRAPH_LINE_THICKNESS_HELPTEXT :Graafikutes kasutatava joone laius. Peenem joon on täpsem, laiem joon on paremini näha ja värvid on selgemini eristatavad STR_CONFIG_SETTING_SHOW_NEWGRF_NAME :Näita sõidukiehitusaknas NewGRF-i nime: {STRING} @@ -1394,7 +1392,7 @@ STR_CONFIG_SETTING_INDUSTRY_DENSITY_HELPTEXT :Määrab, kui p STR_CONFIG_SETTING_OIL_REF_EDGE_DISTANCE :Maksimaalne kaugus kaardi servast naftatöötlustehase jaoks: {STRING} STR_CONFIG_SETTING_OIL_REF_EDGE_DISTANCE_HELPTEXT :Naftatöötlemistehased ehitatakse ainult kaardi piirile, see on rannikul saartega mängitaval kaardil. STR_CONFIG_SETTING_SNOWLINE_HEIGHT :Lumepiiri kõrgus: {STRING} -STR_CONFIG_SETTING_SNOWLINE_HEIGHT_HELPTEXT :Määra, millisel kõrgusel algab sub-arktiline maastik. Lumi mõjutab ka tööstuste loomist ja linna kasvamise tingimusi +STR_CONFIG_SETTING_SNOWLINE_HEIGHT_HELPTEXT :Määra, millisel kõrgusel algab sub-arktiline maastik. Lumi mõjutab ka tööstuste loomist ja asulate kasvamise nõudeid STR_CONFIG_SETTING_ROUGHNESS_OF_TERRAIN :Maastiku järskus (ainult TerraGenesis) : {STRING} STR_CONFIG_SETTING_ROUGHNESS_OF_TERRAIN_HELPTEXT :(TerraGenesis only) Vali mägede tihedus: Laugetel maastikel on vähem, laiema ulatusega mägesid. Mägistel maastikel on palju mägesid, mis võivad näha välja samasugused STR_CONFIG_SETTING_ROUGHNESS_OF_TERRAIN_VERY_SMOOTH :Väga lauge @@ -1526,7 +1524,7 @@ STR_CONFIG_SETTING_SOUND_DISASTER_HELPTEXT :Õnnetustes ja STR_CONFIG_SETTING_SOUND_VEHICLE :Sõidukitel: {STRING} STR_CONFIG_SETTING_SOUND_VEHICLE_HELPTEXT :Kostuvad sõidukite helid STR_CONFIG_SETTING_SOUND_AMBIENT :Taustahelid: {STRING} -STR_CONFIG_SETTING_SOUND_AMBIENT_HELPTEXT :Maastikult, tööstustest ja linnadest kostuvad taustahelid +STR_CONFIG_SETTING_SOUND_AMBIENT_HELPTEXT :Maastikult, tööstustest ja asulatest kostuvad taustahelid STR_CONFIG_SETTING_DISABLE_UNSUITABLE_BUILDING :Sobivate sõidukite puudumisel on taristu ehitamine keelatud: {STRING} STR_CONFIG_SETTING_DISABLE_UNSUITABLE_BUILDING_HELPTEXT :Sisse lülitamisel on taristu ehitamine võimalik ainult juhul, kui sellega seonduvad sõidukid on juba saadaval. Väldib mõttetut raha ja aja kulu, kui ehitatakse taristu, mida ei saa kasutada @@ -1654,26 +1652,26 @@ STR_CONFIG_SETTING_CYCLE_SIGNAL_NORMAL :Ainult tavaline STR_CONFIG_SETTING_CYCLE_SIGNAL_PBS :Ainult täiustatud STR_CONFIG_SETTING_CYCLE_SIGNAL_ALL :Kõik -STR_CONFIG_SETTING_TOWN_LAYOUT :Uute linnade tänavaplaneering: {STRING} -STR_CONFIG_SETTING_TOWN_LAYOUT_HELPTEXT :Teedevõrgu paigutus linnades +STR_CONFIG_SETTING_TOWN_LAYOUT :Uute asulate teeplaneering: {STRING} +STR_CONFIG_SETTING_TOWN_LAYOUT_HELPTEXT :Teedevõrgu paigutus asulates STR_CONFIG_SETTING_TOWN_LAYOUT_DEFAULT :Esialgne STR_CONFIG_SETTING_TOWN_LAYOUT_BETTER_ROADS :Paremad teed STR_CONFIG_SETTING_TOWN_LAYOUT_2X2_GRID :2x2 võrgustik STR_CONFIG_SETTING_TOWN_LAYOUT_3X3_GRID :3x3 võrgustik STR_CONFIG_SETTING_TOWN_LAYOUT_RANDOM :Suvaline -STR_CONFIG_SETTING_ALLOW_TOWN_ROADS :Linnapoolne teedeehitus: {STRING} -STR_CONFIG_SETTING_ALLOW_TOWN_ROADS_HELPTEXT :Luba linnadel ehitada teid, et soodustada kasvu. Lülita välja, et keelata linnadel rajada teid -STR_CONFIG_SETTING_ALLOW_TOWN_LEVEL_CROSSINGS :Linnadel on lubatud ehitada ülesõidukohti: {STRING} -STR_CONFIG_SETTING_ALLOW_TOWN_LEVEL_CROSSINGS_HELPTEXT :Selle seade sisse lülitamine lubab linnadel ehitada ülesõidukohti -STR_CONFIG_SETTING_NOISE_LEVEL :Lennujaamade mürapiirangud linnades: {STRING} -STR_CONFIG_SETTING_NOISE_LEVEL_HELPTEXT :Kui seade ei ole sisse lülitatud, siis igasse linna on lubatud ehitada kuni kaks lennuvälja. Kui see seade on lubatud, siis lennuväljade arvu ülempiiri määrab linna müratundlikkus, mis sõltub rahvaarvust ja lennuvälja suurusest ja kaugusest -STR_CONFIG_SETTING_TOWN_FOUNDING :Linnade rajamine keset mängu: {STRING} -STR_CONFIG_SETTING_TOWN_FOUNDING_HELPTEXT :Võimaldab ettevõttel uusi linnu rajada +STR_CONFIG_SETTING_ALLOW_TOWN_ROADS :Asulatel on lubatud teederajamine: {STRING} +STR_CONFIG_SETTING_ALLOW_TOWN_ROADS_HELPTEXT :Luba asulatele teederajamine, et soodustada kasvu. Lülita välja, et keelata asulatele teederajamine +STR_CONFIG_SETTING_ALLOW_TOWN_LEVEL_CROSSINGS :Asulatel on lubatud ehitada ülesõidukohti: {STRING} +STR_CONFIG_SETTING_ALLOW_TOWN_LEVEL_CROSSINGS_HELPTEXT :Selle seade sisse lülitamine lubab asulatel ehitada ülesõidukohti +STR_CONFIG_SETTING_NOISE_LEVEL :Lennujaamade mürapiirangud asulates: {STRING} +STR_CONFIG_SETTING_NOISE_LEVEL_HELPTEXT :Kui seade ei ole sisse lülitatud, siis igasse asulasse on lubatud ehitada kuni kaks lennuvälja. Kui see seade on lubatud, siis lennuväljade arvu ülempiiri määrab asustuse müratundlikkus, mis sõltub rahvaarvust ja lennuvälja suurusest ja kaugusest +STR_CONFIG_SETTING_TOWN_FOUNDING :Asulate rajamine keset mängu: {STRING} +STR_CONFIG_SETTING_TOWN_FOUNDING_HELPTEXT :Võimaldab ettevõttel uusi asulaid rajada STR_CONFIG_SETTING_TOWN_FOUNDING_FORBIDDEN :Keelatud STR_CONFIG_SETTING_TOWN_FOUNDING_ALLOWED :Lubatud -STR_CONFIG_SETTING_TOWN_FOUNDING_ALLOWED_CUSTOM_LAYOUT :Lubatud, koos linnaskeemi valimisega -STR_CONFIG_SETTING_TOWN_CARGOGENMODE :Kauba tekkimine linnades: {STRING} -STR_CONFIG_SETTING_TOWN_CARGOGENMODE_HELPTEXT :Kui palju kaupa toodavad majad linnades, võrreldes linnaelanike arvuga.{}Ruutfunktsioon: kaks korda suurem linn tekitab neli korda rohkem reisijaid.{}Lineaarne: kaks korda suurem linn tekitab kaks korda rohkem reisijaid. +STR_CONFIG_SETTING_TOWN_FOUNDING_ALLOWED_CUSTOM_LAYOUT :Lubatud, koos asulaskeemi valimisega +STR_CONFIG_SETTING_TOWN_CARGOGENMODE :Kauba tekkimine asulates: {STRING} +STR_CONFIG_SETTING_TOWN_CARGOGENMODE_HELPTEXT :Kui palju kaupa toodavad majad asulates, võrreldes elanike arvuga.{}Ruutfunktsioon: kaks korda suurem asula tekitab neli korda rohkem reisijaid.{}Lineaarne: kaks korda suurem asula tekitab kaks korda rohkem reisijaid. STR_CONFIG_SETTING_TOWN_CARGOGENMODE_ORIGINAL :Ruutfunktsioon (algupärane) STR_CONFIG_SETTING_TOWN_CARGOGENMODE_BITCOUNT :Lineaarne @@ -1706,18 +1704,18 @@ STR_CONFIG_SETTING_ZOOM_LVL_NORMAL :Tavaline STR_CONFIG_SETTING_ZOOM_LVL_OUT_2X :2x STR_CONFIG_SETTING_ZOOM_LVL_OUT_4X :4x STR_CONFIG_SETTING_ZOOM_LVL_OUT_8X :8x -STR_CONFIG_SETTING_TOWN_GROWTH :Linna kasvutempo: {STRING} -STR_CONFIG_SETTING_TOWN_GROWTH_HELPTEXT :Linna kasvamise kiirus +STR_CONFIG_SETTING_TOWN_GROWTH :Asulate kasvutempo: {STRING} +STR_CONFIG_SETTING_TOWN_GROWTH_HELPTEXT :Asulate kasvutempo STR_CONFIG_SETTING_TOWN_GROWTH_NONE :Pole STR_CONFIG_SETTING_TOWN_GROWTH_SLOW :Aeglane STR_CONFIG_SETTING_TOWN_GROWTH_NORMAL :Normaalne STR_CONFIG_SETTING_TOWN_GROWTH_FAST :Kiire STR_CONFIG_SETTING_TOWN_GROWTH_VERY_FAST :Väga Kiire -STR_CONFIG_SETTING_LARGER_TOWNS :Proportsioon asulatest millest kasvavad linnad: {STRING} +STR_CONFIG_SETTING_LARGER_TOWNS :Linnadeks kasvavate asulate osakaal: {STRING} STR_CONFIG_SETTING_LARGER_TOWNS_HELPTEXT :Asulate arv mis kasvavad tulevikus linnadeks, elik asulate arv mis on alguses suuremad ja kasvavad kiiremini kui ülejäänud asulad STR_CONFIG_SETTING_LARGER_TOWNS_VALUE :1 iga {COMMA} kohta STR_CONFIG_SETTING_LARGER_TOWNS_DISABLED :Mitte ühtegi -STR_CONFIG_SETTING_CITY_SIZE_MULTIPLIER :Algne linnade suurustegur: {STRING} +STR_CONFIG_SETTING_CITY_SIZE_MULTIPLIER :Algne linnade suuruskordaja: {STRING} STR_CONFIG_SETTING_CITY_SIZE_MULTIPLIER_HELPTEXT :Keskmine linna suurus võrreldes tavalise asulaga mängu alguses STR_CONFIG_SETTING_LINKGRAPH_INTERVAL :Jaotusgraafikuid uuendatakse igal {STRING}. päeval @@ -1798,8 +1796,8 @@ STR_CONFIG_SETTING_LIMITATIONS :{ORANGE}Piirang STR_CONFIG_SETTING_ACCIDENTS :{ORANGE}Katastroofid / Õnnetused STR_CONFIG_SETTING_GENWORLD :{ORANGE}Maailma tekitamine STR_CONFIG_SETTING_ENVIRONMENT :{ORANGE}Keskkond -STR_CONFIG_SETTING_ENVIRONMENT_AUTHORITIES :{ORANGE}Linnavõimud -STR_CONFIG_SETTING_ENVIRONMENT_TOWNS :{ORANGE}Linnad +STR_CONFIG_SETTING_ENVIRONMENT_AUTHORITIES :{ORANGE}Omavalitsus +STR_CONFIG_SETTING_ENVIRONMENT_TOWNS :{ORANGE}Asulad STR_CONFIG_SETTING_ENVIRONMENT_INDUSTRIES :{ORANGE}Tööstused STR_CONFIG_SETTING_ENVIRONMENT_CARGODIST :{ORANGE}Kaubajaotus STR_CONFIG_SETTING_AI :{ORANGE}Konkurendid @@ -2630,31 +2628,31 @@ STR_QUERY_RESET_LANDSCAPE_CAPTION :{WHITE}Nulli ma STR_RESET_LANDSCAPE_CONFIRMATION_TEXT :{WHITE}Kas oled kindel, et tahad lammutada kõik mängija omandid? # Town generation window (SE) -STR_FOUND_TOWN_CAPTION :{WHITE}Linnade tekitamine -STR_FOUND_TOWN_NEW_TOWN_BUTTON :{BLACK}Uus linn -STR_FOUND_TOWN_NEW_TOWN_TOOLTIP :{BLACK}Ehita uus linn. Shift+klõpsuga kuvatakse eeldatav maksumus -STR_FOUND_TOWN_RANDOM_TOWN_BUTTON :{BLACK}Suvaline linn -STR_FOUND_TOWN_RANDOM_TOWN_TOOLTIP :{BLACK}Ehita linn suvalisse kohta -STR_FOUND_TOWN_MANY_RANDOM_TOWNS :{BLACK}Palju suvalisi linnu -STR_FOUND_TOWN_RANDOM_TOWNS_TOOLTIP :{BLACK}Kaardi katmine suvaliselt asetatud linnadega - -STR_FOUND_TOWN_NAME_TITLE :{YELLOW}Linnanimi: -STR_FOUND_TOWN_NAME_EDITOR_TITLE :{BLACK}Sisesta linnanimi -STR_FOUND_TOWN_NAME_EDITOR_HELP :{BLACK}Klõpsa linnale nime andmiseks +STR_FOUND_TOWN_CAPTION :{WHITE}Asulate tekitamine +STR_FOUND_TOWN_NEW_TOWN_BUTTON :{BLACK}Uus asula +STR_FOUND_TOWN_NEW_TOWN_TOOLTIP :{BLACK}Raja uus asula. Shift+klõpsuga kuvatakse eeldatav maksumus +STR_FOUND_TOWN_RANDOM_TOWN_BUTTON :{BLACK}Suvaline asula +STR_FOUND_TOWN_RANDOM_TOWN_TOOLTIP :{BLACK}Raja asula suvalisse kohta +STR_FOUND_TOWN_MANY_RANDOM_TOWNS :{BLACK}Palju suvalisi asulaid +STR_FOUND_TOWN_RANDOM_TOWNS_TOOLTIP :{BLACK}Kaardi katmine suvaliselt paigutatud asulatega + +STR_FOUND_TOWN_NAME_TITLE :{YELLOW}Asula nimi: +STR_FOUND_TOWN_NAME_EDITOR_TITLE :{BLACK}Sisesta asula nimi +STR_FOUND_TOWN_NAME_EDITOR_HELP :{BLACK}Klõpsa asulale nime andmiseks STR_FOUND_TOWN_NAME_RANDOM_BUTTON :{BLACK}Suvaline nimi STR_FOUND_TOWN_NAME_RANDOM_TOOLTIP :{BLACK}Loo suvaline nimi -STR_FOUND_TOWN_INITIAL_SIZE_TITLE :{YELLOW}Linna suurus: +STR_FOUND_TOWN_INITIAL_SIZE_TITLE :{YELLOW}Asula suurus: STR_FOUND_TOWN_INITIAL_SIZE_SMALL_BUTTON :{BLACK}Väike STR_FOUND_TOWN_INITIAL_SIZE_MEDIUM_BUTTON :{BLACK}Keskmine STR_FOUND_TOWN_INITIAL_SIZE_LARGE_BUTTON :{BLACK}Suur STR_FOUND_TOWN_SIZE_RANDOM :{BLACK}Suvaline -STR_FOUND_TOWN_INITIAL_SIZE_TOOLTIP :{BLACK}Vali linna suurus +STR_FOUND_TOWN_INITIAL_SIZE_TOOLTIP :{BLACK}Vali asula suurus STR_FOUND_TOWN_CITY :{BLACK}Linn -STR_FOUND_TOWN_CITY_TOOLTIP :{BLACK}Suuremad linnad kasvavad kiiremini kui väikesed{}Sõltuvalt seadetest on need ka alguses suuremad +STR_FOUND_TOWN_CITY_TOOLTIP :{BLACK}Linnad kasvavad kiiremini, kui tavalised asulad{}Sõltuvalt seadetest on need ka alguses suuremad -STR_FOUND_TOWN_ROAD_LAYOUT :{YELLOW}Linna teeplaneering -STR_FOUND_TOWN_SELECT_TOWN_ROAD_LAYOUT :{BLACK}Vali linnas kasutatav teeplaneering +STR_FOUND_TOWN_ROAD_LAYOUT :{YELLOW}Asula teeplaneering +STR_FOUND_TOWN_SELECT_TOWN_ROAD_LAYOUT :{BLACK}Vali selle asula teeplaneering STR_FOUND_TOWN_SELECT_LAYOUT_ORIGINAL :{BLACK}Algne STR_FOUND_TOWN_SELECT_LAYOUT_BETTER_ROADS :{BLACK}Paremad teed STR_FOUND_TOWN_SELECT_LAYOUT_2X2_GRID :{BLACK}2x2 ruudustik @@ -2908,7 +2906,7 @@ STR_MAPGEN_WORLD_GENERATION_CAPTION :{WHITE}Maailma STR_MAPGEN_MAPSIZE :{BLACK}Kaardi mõõtmed: STR_MAPGEN_MAPSIZE_TOOLTIP :{BLACK}Määrab maakaardi mõõtmed ruutudes. Ruute, mida saab kasutada, on pisut vähem STR_MAPGEN_BY :{BLACK}* -STR_MAPGEN_NUMBER_OF_TOWNS :{BLACK}Linnade sagedus: +STR_MAPGEN_NUMBER_OF_TOWNS :{BLACK}Asustusi: STR_MAPGEN_DATE :{BLACK}Algus: STR_MAPGEN_NUMBER_OF_INDUSTRIES :{BLACK}Tööstuste sagedus: STR_MAPGEN_MAX_HEIGHTLEVEL :{BLACK}Maksimaalne kaardi kõrgus: @@ -2918,7 +2916,6 @@ STR_MAPGEN_SNOW_LINE_HEIGHT :{BLACK}Lumepiir STR_MAPGEN_SNOW_LINE_UP :{BLACK}Lumepiiri liigutamine ühe võrra üles STR_MAPGEN_SNOW_LINE_DOWN :{BLACK}Lumepiiri liigutamine ühe võrra alla STR_MAPGEN_LAND_GENERATOR :{BLACK}Maatekituse meetod: -STR_MAPGEN_TREE_PLACER :{BLACK}Puude algoritm: STR_MAPGEN_TERRAIN_TYPE :{BLACK}Maapinna tüüp: STR_MAPGEN_QUANTITY_OF_SEA_LAKES :{BLACK}Merepinna tase: STR_MAPGEN_QUANTITY_OF_RIVERS :{BLACK}Jõgesid: @@ -3168,11 +3165,11 @@ STR_EDIT_SIGN_PREVIOUS_SIGN_TOOLTIP :{BLACK}Mine eel STR_EDIT_SIGN_SIGN_OSKTITLE :{BLACK}Sisesta sildile nimi # Town directory window -STR_TOWN_DIRECTORY_CAPTION :{WHITE}Linnad +STR_TOWN_DIRECTORY_CAPTION :{WHITE}Asustused STR_TOWN_DIRECTORY_NONE :{ORANGE}- Puudub - STR_TOWN_DIRECTORY_TOWN :{ORANGE}{TOWN}{BLACK} ({COMMA}) STR_TOWN_DIRECTORY_CITY :{ORANGE}{TOWN}{YELLOW} (Linn){BLACK} ({COMMA}) -STR_TOWN_DIRECTORY_LIST_TOOLTIP :{BLACK}Linnanimed - vajuta linnanimele, et viia vaade linnale. Ctrl+klõps avab uue vaate linna asukohast +STR_TOWN_DIRECTORY_LIST_TOOLTIP :{BLACK}Astuste nimed - vajuta nimele, et viia vaade asustusele. Ctrl+klõps avab uue vaate linna asukohas STR_TOWN_POPULATION :{BLACK}Maailma rahvastik: {COMMA} # Town view window @@ -3180,25 +3177,25 @@ STR_TOWN_VIEW_TOWN_CAPTION :{WHITE}{TOWN} STR_TOWN_VIEW_CITY_CAPTION :{WHITE}{TOWN} (Linn) STR_TOWN_VIEW_POPULATION_HOUSES :{BLACK}Rahvaarv: {ORANGE}{COMMA}{BLACK} Ehitisi: {ORANGE}{COMMA} STR_TOWN_VIEW_CARGO_LAST_MONTH_MAX :{BLACK}{CARGO_LIST} eelmisel kuul: {ORANGE}{COMMA}{BLACK} Kuni: {ORANGE}{COMMA} -STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH :{BLACK}Veoseid linna kasvamiseks: +STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH :{BLACK}Asula kasvuks nõutud veosed: STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_GENERAL :{ORANGE}{STRING}{RED} vajalik STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_WINTER :{ORANGE}{STRING}{BLACK} on talvel vajalik STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_DELIVERED_GENERAL :{ORANGE}{STRING}{GREEN} äraveetud STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED :{ORANGE}{CARGO_TINY} / {CARGO_LONG}{RED} (ikka veel vaja) STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_DELIVERED :{ORANGE}{CARGO_TINY} / {CARGO_LONG}{GREEN} (äraveetud) -STR_TOWN_VIEW_TOWN_GROWS_EVERY :{BLACK}Linn kasvab {ORANGE}{COMMA}{BLACK} päevaga -STR_TOWN_VIEW_TOWN_GROWS_EVERY_FUNDED :{BLACK}Linn kasvab iga {ORANGE}{COMMA}{BLACK} päeva tagant (rahastatud) -STR_TOWN_VIEW_TOWN_GROW_STOPPED :{BLACK}Linn {RED}ei{BLACK} kasva -STR_TOWN_VIEW_NOISE_IN_TOWN :{BLACK}Mürapiirang linnas: {ORANGE}{COMMA}{BLACK} suurim: {ORANGE}{COMMA} -STR_TOWN_VIEW_CENTER_TOOLTIP :{BLACK}Vaate viimine linnale +STR_TOWN_VIEW_TOWN_GROWS_EVERY :{BLACK}Asustus kasvab iga {ORANGE}{COMMA}{NBSP}päev{P "" a} +STR_TOWN_VIEW_TOWN_GROWS_EVERY_FUNDED :{BLACK}Asustus kasvab iga {ORANGE}{COMMA}{BLACK}{NBSP}päev{P "" a} (rahastatud) +STR_TOWN_VIEW_TOWN_GROW_STOPPED :{BLACK}Asustus {RED}ei{BLACK} kasva +STR_TOWN_VIEW_NOISE_IN_TOWN :{BLACK}Mürapiirang asustuses: {ORANGE}{COMMA}{BLACK} suurim: {ORANGE}{COMMA} +STR_TOWN_VIEW_CENTER_TOOLTIP :{BLACK}Keskenda vaade asustusele. Ctrl+klõps avab asustuse kohal uue vaate STR_TOWN_VIEW_LOCAL_AUTHORITY_BUTTON :{BLACK}Omavalitsus STR_TOWN_VIEW_LOCAL_AUTHORITY_TOOLTIP :{BLACK}Näita teavet kohaliku omavalitsuse kohta STR_TOWN_VIEW_RENAME_TOOLTIP :{BLACK}Muuda linna nime STR_TOWN_VIEW_EXPAND_BUTTON :{BLACK}Laienda -STR_TOWN_VIEW_EXPAND_TOOLTIP :{BLACK}Linna suuruse suurendamine +STR_TOWN_VIEW_EXPAND_TOOLTIP :{BLACK}Asustuse suurendamine STR_TOWN_VIEW_DELETE_BUTTON :{BLACK}Kustuta -STR_TOWN_VIEW_DELETE_TOOLTIP :{BLACK}Hävita see linn täielikult +STR_TOWN_VIEW_DELETE_TOOLTIP :{BLACK}Hävita see asustus täielikult STR_TOWN_VIEW_RENAME_TOWN_BUTTON :Ümbernimeta linn @@ -3209,7 +3206,7 @@ STR_LOCAL_AUTHORITY_ZONE_TOOLTIP :{BLACK}Näita k STR_LOCAL_AUTHORITY_COMPANY_RATINGS :{BLACK}Ettevõtete hinnangud: STR_LOCAL_AUTHORITY_COMPANY_RATING :{YELLOW}{COMPANY} {COMPANY_NUM}: {ORANGE}{STRING} STR_LOCAL_AUTHORITY_ACTIONS_TITLE :{BLACK}Pakutavad toimingud: -STR_LOCAL_AUTHORITY_ACTIONS_TOOLTIP :{BLACK}Nimekiri asjadest, mida saab siin linnas teha - klõpsa esemel, et saada rohkem andmeid +STR_LOCAL_AUTHORITY_ACTIONS_TOOLTIP :{BLACK}Nimekiri asjadest, mida saab selles asulas teha - täpsustused esemele klõpsatel STR_LOCAL_AUTHORITY_DO_IT_BUTTON :{BLACK}Soorita toiming STR_LOCAL_AUTHORITY_DO_IT_TOOLTIP :{BLACK}Soorita ülalpool valitud toiming @@ -3222,14 +3219,14 @@ STR_LOCAL_AUTHORITY_ACTION_NEW_BUILDINGS :Rahasta uute ho STR_LOCAL_AUTHORITY_ACTION_EXCLUSIVE_TRANSPORT :Osta monopoolsed veoõigused STR_LOCAL_AUTHORITY_ACTION_BRIBE :Paku kohalikule omavalitsusele altkäemaksu -STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_SMALL_ADVERTISING :{YELLOW} Pisikese kohaliku reklaamikampaania korraldamine meelitab rohkem reisijaid ja kaupu sinu veoteenuseid kasutama.{} Hind: {CURRENCY_LONG} -STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_MEDIUM_ADVERTISING :{YELLOW} Keskmise reklaamikampaania korraldamine, et meelitada rohkem reisijaid ja kaupu sinu teenuste juurede.{} Hind: {CURRENCY_LONG} -STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_LARGE_ADVERTISING :{YELLOW} Suure reklaamikampaania korraldamine, et meelitada rohkem reisijaid ja kaupu sinu teenuste juurede.{} Hind: {CURRENCY_LONG} -STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_ROAD_RECONSTRUCTION :{YELLOW} Teehoiutööde rahastamine. Põhjustab linnatänavatel tõsiseid liiklusprobleeme kuni 6 kuuks.{} Hind: {CURRENCY_LONG} -STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_STATUE_OF_COMPANY :{YELLOW} Oma ettevõtte auks kuju ehitamine.{} Hind: {CURRENCY_LONG} -STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_NEW_BUILDINGS :{YELLOW} Uute ärihoonete ehitamise rahastamine selles linnas.{} Hind: {CURRENCY_LONG} -STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_EXCLUSIVE_TRANSPORT :{YELLOW} Üheks aastaks monopoolsete veoõiguste ostmine selles linnas. Linna omavalitsus lubab reisijate ja kauba veoks kasutada ainult sinu jaamasid.{} Hind: {CURRENCY_LONG} -STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_BRIBE :{YELLOW} Linna omavalitusele ettevõtte hinnangu tõstmise eesmärgil altkäemaksu pakkumine. Vahele jäädes tuleb trahvi maksta.{} Hind: {CURRENCY_LONG} +STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_SMALL_ADVERTISING :{YELLOW} Pisikese kohaliku reklaamikampaania korraldamine meelitab rohkem reisijaid ja kaupu sinu veoteenuseid kasutama.{} Maksumus: {CURRENCY_LONG} +STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_MEDIUM_ADVERTISING :{YELLOW} Keskmise reklaamikampaania korraldamine, et meelitada rohkem reisijaid ja kaupu sinu teenuste juurede.{} Maksumus: {CURRENCY_LONG} +STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_LARGE_ADVERTISING :{YELLOW} Suure reklaamikampaania korraldamine, et meelitada rohkem reisijaid ja kaupu sinu teenuste juurede.{} Maksumus: {CURRENCY_LONG} +STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_ROAD_RECONSTRUCTION :{YELLOW} Linna teedevõrgu ümberehituse rahastamine. Põhjustab liikluses tõsiseid häireid kuni 6 kuuks.{} Maksumus: {CURRENCY_LONG} +STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_STATUE_OF_COMPANY :{YELLOW} Oma ettevõtte auks kuju ehitamine.{} Maksumus: {CURRENCY_LONG} +STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_NEW_BUILDINGS :{YELLOW} Uute ärihoonete ehitamise rahastamine selles asulas.{} Maksumus: {CURRENCY_LONG} +STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_EXCLUSIVE_TRANSPORT :{YELLOW} Üheks aastaks monopoolsete veoõiguste ostmine selles asulas. Asula omavalitsus lubab reisijate ja kauba veoks kasutada ainult sinu jaamasid.{} Maksumus: {CURRENCY_LONG} +STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_BRIBE :{YELLOW} Kohalikule omavalitusele ettevõtte hinnangu tõstmise eesmärgil altkäemaksu pakkumine. Vahele jäädes tuleb trahvi maksta.{} Maksumus: {CURRENCY_LONG} # Goal window STR_GOALS_CAPTION :{WHITE}{COMPANY} eesmärgid @@ -3242,7 +3239,7 @@ STR_GOALS_SPECTATOR_NONE :{ORANGE}- Kehte STR_GOALS_PROGRESS :{ORANGE}{STRING} STR_GOALS_PROGRESS_COMPLETE :{GREEN}{STRING} STR_GOALS_COMPANY_TITLE :{BLACK}Ettevõtte eesmärgid: -STR_GOALS_TOOLTIP_CLICK_ON_SERVICE_TO_CENTER :{BLACK}Klõpsa eesmärgil, et liigutada vaateaken tööstuse/linna/ruudu asukohale. Ctrl+klõps avab uue vaateakna tööstuse/linna/ruudu asukohas +STR_GOALS_TOOLTIP_CLICK_ON_SERVICE_TO_CENTER :{BLACK}Klõpsa eesmärgil, et liigutada vaateaken tööstuse/asula/ruudu asukohale. Ctrl+klõps avab uue vaateakna tööstuse/asula/ruudu asukohas # Goal question window STR_GOAL_QUESTION_CAPTION_QUESTION :{BLACK}Küsimus @@ -3278,7 +3275,7 @@ STR_SUBSIDIES_OFFERED_FROM_TO :{ORANGE}{STRING STR_SUBSIDIES_NONE :{ORANGE}Mitte ühtegi STR_SUBSIDIES_SUBSIDISED_TITLE :{BLACK}Juba toetatavad veoteenused: STR_SUBSIDIES_SUBSIDISED_FROM_TO :{ORANGE}{STRING} kohast {STRING} kohta {STRING}{YELLOW} ({COMPANY}{YELLOW}, kuni {DATE_SHORT}) -STR_SUBSIDIES_TOOLTIP_CLICK_ON_SERVICE_TO_CENTER :{BLACK}Vajuta pakkumisele, et näha linna või tööstuse asukohta. Ctrl+klõps avab uue vaate ettevõtte/linna asukohast +STR_SUBSIDIES_TOOLTIP_CLICK_ON_SERVICE_TO_CENTER :{BLACK}Vajuta pakkumisele, et keskendada vaade tööstusele/asustusele. Ctrl+klõps avab uue vaate ettevõtte/asustuse asukohas # Story book window STR_STORY_BOOK_CAPTION :{WHITE}«{COMPANY}» juturaamat @@ -3314,8 +3311,8 @@ STR_STATION_VIEW_ACCEPTS_BUTTON :{BLACK}Võtab v STR_STATION_VIEW_ACCEPTS_TOOLTIP :{BLACK}Näita vastuvõetavate veoste nimistut STR_STATION_VIEW_ACCEPTS_CARGO :{BLACK}Võtab vastu: {WHITE}{CARGO_LIST} -STR_STATION_VIEW_EXCLUSIVE_RIGHTS_SELF :{BLACK}Antud jaamal on eksklusiivsed transpordiõigused selles linnas. -STR_STATION_VIEW_EXCLUSIVE_RIGHTS_COMPANY :{YELLOW}{COMPANY}{BLACK} ostis eksklusiivsed transpordiõigused antud linnas. +STR_STATION_VIEW_EXCLUSIVE_RIGHTS_SELF :{BLACK}Sellel jaamal on asulas vedude ainuõigus. +STR_STATION_VIEW_EXCLUSIVE_RIGHTS_COMPANY :{YELLOW}{COMPANY}{BLACK} ostis vedude ainuõiguse selles asustuses. STR_STATION_VIEW_RATINGS_BUTTON :{BLACK}Hinnangud STR_STATION_VIEW_RATINGS_TOOLTIP :{BLACK}Näita jaamahinnangut @@ -3875,7 +3872,7 @@ STR_VEHICLE_STATUS_HEADING_FOR_STATION_VEL :{LTBLUE}Siht: { STR_VEHICLE_STATUS_NO_ORDERS_VEL :{LTBLUE}Sihitu, {VELOCITY} STR_VEHICLE_STATUS_HEADING_FOR_WAYPOINT_VEL :{LTBLUE}Suundub {WAYPOINT}, {VELOCITY} STR_VEHICLE_STATUS_HEADING_FOR_DEPOT_VEL :{ORANGE}Suundub {DEPOT}, {VELOCITY} -STR_VEHICLE_STATUS_HEADING_FOR_DEPOT_SERVICE_VEL :{LTBLUE}Teenus kohas {DEPOT}, {VELOCITY} +STR_VEHICLE_STATUS_HEADING_FOR_DEPOT_SERVICE_VEL :{LTBLUE}Järelevaatus: {DEPOT}, {VELOCITY} # Vehicle stopped/started animations STR_VEHICLE_COMMAND_STOPPED_SMALL :{TINY_FONT}{RED}peatatud @@ -4337,8 +4334,8 @@ STR_GAME_SAVELOAD_NOT_AVAILABLE :Majandus->Linnad +STR_ERROR_TOWN_EXPAND_WARN_NO_ROADS :{WHITE}Asula ei ehita teid. Teedeehituse lubamiseks Põhjalik seadistus->Majandus->Asulad STR_ERROR_ROAD_WORKS_IN_PROGRESS :{WHITE}Teede ehitamine -STR_ERROR_TOWN_CAN_T_DELETE :{WHITE}Seda linna ei saa kustutada...{}Jaam või depoo viitab linnale või mõnd linna omanduses olevat tükki ei saa kustutada -STR_ERROR_STATUE_NO_SUITABLE_PLACE :{WHITE}... linnakeskuses ei leidu kujule sobivat kohta +STR_ERROR_TOWN_CAN_T_DELETE :{WHITE}Seda asulat ei saa kõrvaldada...{}Jaam või depoo viitab asulale, või asulale kuuluvat ruutu ei saa kõrvaldada +STR_ERROR_STATUE_NO_SUITABLE_PLACE :{WHITE}... asula keskuses ei leidu kujule sobivat kohta # Industry related errors STR_ERROR_TOO_MANY_INDUSTRIES :{WHITE}... liiga palju tööstuseid @@ -4440,13 +4437,13 @@ STR_ERROR_CAN_T_GENERATE_INDUSTRIES :{WHITE}Tööstu STR_ERROR_CAN_T_BUILD_HERE :{WHITE}{STRING} ei saa siia ehitada... STR_ERROR_CAN_T_CONSTRUCT_THIS_INDUSTRY :{WHITE}Seda tüüpi tööstust ei saa siia ehitada... STR_ERROR_INDUSTRY_TOO_CLOSE :{WHITE}... liiga lähedal mõnele teisele tööstusele -STR_ERROR_MUST_FOUND_TOWN_FIRST :{WHITE}... enne pead linna ehitama -STR_ERROR_ONLY_ONE_ALLOWED_PER_TOWN :{WHITE}... iga linna kohta lubatud ainult üks -STR_ERROR_CAN_ONLY_BE_BUILT_IN_TOWNS_WITH_POPULATION_OF_1200 :{WHITE}... saab ainult ehitada linna, mille rahvastikuarv ületab 1200 +STR_ERROR_MUST_FOUND_TOWN_FIRST :{WHITE}... enne pead asula rajama +STR_ERROR_ONLY_ONE_ALLOWED_PER_TOWN :{WHITE}... iga asula kohta lubatud ainult üks +STR_ERROR_CAN_ONLY_BE_BUILT_IN_TOWNS_WITH_POPULATION_OF_1200 :{WHITE}... saab ainult ehitada asulatesse, mille elanikearv ületab 1200 STR_ERROR_CAN_ONLY_BE_BUILT_IN_RAINFOREST :{WHITE}... saab ehitada ainult vihmametsadesse STR_ERROR_CAN_ONLY_BE_BUILT_IN_DESERT :{WHITE}... saab ehitada ainult kõrbesse -STR_ERROR_CAN_ONLY_BE_BUILT_IN_TOWNS :{WHITE}... saab ainult linnadesse ehitada -STR_ERROR_CAN_ONLY_BE_BUILT_NEAR_TOWN_CENTER :{WHITE}... saab ainult linna keskpunkti lähedale ehitada +STR_ERROR_CAN_ONLY_BE_BUILT_IN_TOWNS :{WHITE}... saab ainult asulatesse ehitada (majade asemele) +STR_ERROR_CAN_ONLY_BE_BUILT_NEAR_TOWN_CENTER :{WHITE}... saab ainult asulekeskuste lähedale ehitada STR_ERROR_CAN_ONLY_BE_BUILT_IN_LOW_AREAS :{WHITE}... saab ainult madalatele aladele ehitada STR_ERROR_CAN_ONLY_BE_POSITIONED :{WHITE}... saab ainult ehitada kaardi serva STR_ERROR_FOREST_CAN_ONLY_BE_PLANTED :{WHITE}... metsa saab istutada ainult lumepiirist kõrgemale @@ -4474,7 +4471,7 @@ STR_ERROR_TOO_MANY_TRUCK_STOPS :{WHITE}Liiga pa STR_ERROR_TOO_CLOSE_TO_ANOTHER_DOCK :{WHITE}Liiga lähedal teisele dokile STR_ERROR_TOO_CLOSE_TO_ANOTHER_AIRPORT :{WHITE}Liiga lähedal teisele lennuväljale STR_ERROR_CAN_T_RENAME_STATION :{WHITE}Ei saa jaama ümbernimetada... -STR_ERROR_DRIVE_THROUGH_ON_TOWN_ROAD :{WHITE}... see tee on omatud linna poolt +STR_ERROR_DRIVE_THROUGH_ON_TOWN_ROAD :{WHITE}... see on asulale kuuluv tee STR_ERROR_DRIVE_THROUGH_DIRECTION :{WHITE}... tee on vales suunas STR_ERROR_DRIVE_THROUGH_CORNER :{WHITE}... nurgad ei saa läbisõidupeatustes olla STR_ERROR_DRIVE_THROUGH_JUNCTION :{WHITE}... ristmikud ei saa olla läbisõidupeatustes @@ -4754,7 +4751,7 @@ STR_TOWN_BUILDING_NAME_OFFICE_BLOCK_1 :Büroohoone STR_TOWN_BUILDING_NAME_SMALL_BLOCK_OF_FLATS_1 :Väike kortermaja STR_TOWN_BUILDING_NAME_CHURCH_1 :Kirik STR_TOWN_BUILDING_NAME_LARGE_OFFICE_BLOCK_1 :Suur büroohoone -STR_TOWN_BUILDING_NAME_TOWN_HOUSES_1 :Linna majad +STR_TOWN_BUILDING_NAME_TOWN_HOUSES_1 :Asustuse majad STR_TOWN_BUILDING_NAME_HOTEL_1 :Hotell STR_TOWN_BUILDING_NAME_STATUE_1 :Kuju STR_TOWN_BUILDING_NAME_FOUNTAIN_1 :Purskkaev diff --git a/src/lang/faroese.txt b/src/lang/faroese.txt index e9a25aa47c..b70871e667 100644 --- a/src/lang/faroese.txt +++ b/src/lang/faroese.txt @@ -890,8 +890,6 @@ STR_GAME_OPTIONS_CURRENCY_GEL :Georgiskur Lari STR_GAME_OPTIONS_CURRENCY_IRR :Iranskur Rial ############ end of currency region -STR_GAME_OPTIONS_ROAD_VEHICLES_FRAME :{BLACK}Akfør -STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_TOOLTIP :{BLACK}Áset hvørja síðu á vegnum akfør skulu koyra á STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_LEFT :Vinstrakoyring STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_RIGHT :Høgrakoyring @@ -2439,7 +2437,6 @@ STR_MAPGEN_SNOW_LINE_HEIGHT :{BLACK}Hædd á STR_MAPGEN_SNOW_LINE_UP :{BLACK}Flyt kava linjuna eitt upp STR_MAPGEN_SNOW_LINE_DOWN :{BLACK}Flyt kava linjuna eitt niður STR_MAPGEN_LAND_GENERATOR :{BLACK}Lendis framleiðari: -STR_MAPGEN_TREE_PLACER :{BLACK}Træ algoritma: STR_MAPGEN_TERRAIN_TYPE :{BLACK}Slag av lendi: STR_MAPGEN_QUANTITY_OF_SEA_LAKES :{BLACK}Sjóvarmáli: STR_MAPGEN_QUANTITY_OF_RIVERS :{BLACK}Løkar: diff --git a/src/lang/finnish.txt b/src/lang/finnish.txt index fefed924a0..867ac4d59b 100644 --- a/src/lang/finnish.txt +++ b/src/lang/finnish.txt @@ -949,8 +949,6 @@ STR_GAME_OPTIONS_CURRENCY_HKD :Hongkongin doll STR_GAME_OPTIONS_CURRENCY_INR :Intian rupia (INR) ############ end of currency region -STR_GAME_OPTIONS_ROAD_VEHICLES_FRAME :{BLACK}Kulkuneuvot -STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_TOOLTIP :{BLACK}Valitse kummalla puolella tietä ajetaan STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_LEFT :Vasemmanpuolinen liikenne STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_RIGHT :Oikeanpuolinen liikenne @@ -1005,6 +1003,7 @@ STR_GAME_OPTIONS_RESOLUTION_OTHER :muu STR_GAME_OPTIONS_GUI_ZOOM_FRAME :{BLACK}Käyttöliittymän koko STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_TOOLTIP :{BLACK}Valitse käyttöliittymäelementtien koko +STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_AUTO :(tunnista automaattisesti) STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_NORMAL :Tavallinen STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_2X_ZOOM :Kaksinkertainen STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_4X_ZOOM :Nelinkertainen @@ -1012,6 +1011,7 @@ STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_4X_ZOOM :Nelinkertainen STR_GAME_OPTIONS_FONT_ZOOM :{BLACK}Kirjasinkoko STR_GAME_OPTIONS_FONT_ZOOM_DROPDOWN_TOOLTIP :{BLACK}Valitse käyttöliittymän kirjasinkoko +STR_GAME_OPTIONS_FONT_ZOOM_DROPDOWN_AUTO :(tunnista automaattisesti) STR_GAME_OPTIONS_FONT_ZOOM_DROPDOWN_NORMAL :Normaali STR_GAME_OPTIONS_FONT_ZOOM_DROPDOWN_2X_ZOOM :Kaksinkertainen koko STR_GAME_OPTIONS_FONT_ZOOM_DROPDOWN_4X_ZOOM :Nelinkertainen koko @@ -2606,12 +2606,18 @@ STR_FOUND_TOWN_SELECT_LAYOUT_RANDOM :{BLACK}Sattuman # Fund new industry window STR_FUND_INDUSTRY_CAPTION :{WHITE}Rakenna uutta teollisuutta STR_FUND_INDUSTRY_SELECTION_TOOLTIP :{BLACK}Valitse tehdas listasta -STR_FUND_INDUSTRY_MANY_RANDOM_INDUSTRIES :Monta satunnaista teollisuusaluetta +STR_FUND_INDUSTRY_MANY_RANDOM_INDUSTRIES :{BLACK}Luo satunnaisia tuotantolaitoksia STR_FUND_INDUSTRY_MANY_RANDOM_INDUSTRIES_TOOLTIP :{BLACK}Sijoita teollisuuslaitokset satunnaisesti +STR_FUND_INDUSTRY_MANY_RANDOM_INDUSTRIES_CAPTION :{WHITE}Luo satunnaista teollisuutta +STR_FUND_INDUSTRY_MANY_RANDOM_INDUSTRIES_QUERY :{YELLOW}Oletko varma, että haluat luoda monta satunnaista tuotantolaitosta? STR_FUND_INDUSTRY_INDUSTRY_BUILD_COST :{BLACK}Kustannus: {YELLOW}{CURRENCY_LONG} STR_FUND_INDUSTRY_PROSPECT_NEW_INDUSTRY :{BLACK}Koekaivaus STR_FUND_INDUSTRY_BUILD_NEW_INDUSTRY :{BLACK}Rakenna STR_FUND_INDUSTRY_FUND_NEW_INDUSTRY :{BLACK}Perusta +STR_FUND_INDUSTRY_REMOVE_ALL_INDUSTRIES :{BLACK}Poista kaikki tuotantolaitokset +STR_FUND_INDUSTRY_REMOVE_ALL_INDUSTRIES_TOOLTIP :{BLACK}Poista kaikki kartalla olevat tuotantolaitokset +STR_FUND_INDUSTRY_REMOVE_ALL_INDUSTRIES_CAPTION :{WHITE}Poista kaikki tuotantolaitokset +STR_FUND_INDUSTRY_REMOVE_ALL_INDUSTRIES_QUERY :{YELLOW}Oletko varma, että haluat poistaa kaikki tuotantolaitokset? # Industry cargoes window STR_INDUSTRY_CARGOES_INDUSTRY_CAPTION :{WHITE}Teollisuusketju teollisuudelle {STRING} @@ -2842,6 +2848,8 @@ STR_SAVELOAD_DETAIL_GRFSTATUS :{SILVER}NewGRF: STR_SAVELOAD_FILTER_TITLE :{BLACK}Suodatinteksti: STR_SAVELOAD_OVERWRITE_TITLE :{WHITE}Tiedosto on jo olemassa STR_SAVELOAD_OVERWRITE_WARNING :{YELLOW}Haluatko varmasti korvata olemassaolevan tiedoston? +STR_SAVELOAD_DIRECTORY :{STRING} (hakemisto) +STR_SAVELOAD_PARENT_DIRECTORY :{STRING} (ylähakemisto) STR_SAVELOAD_OSKTITLE :{BLACK}Syötä nimi tallennustiedostolle @@ -2860,7 +2868,6 @@ STR_MAPGEN_SNOW_LINE_HEIGHT :{BLACK}Lumiraja STR_MAPGEN_SNOW_LINE_UP :{BLACK}Siirrä lumirajaa yksi taso ylöspäin STR_MAPGEN_SNOW_LINE_DOWN :{BLACK}Siirrä lumirajaa yksi taso alaspäin STR_MAPGEN_LAND_GENERATOR :{BLACK}Maastogeneraattori: -STR_MAPGEN_TREE_PLACER :{BLACK}Puiden algoritmi: STR_MAPGEN_TERRAIN_TYPE :{BLACK}Maaston tyyppi: STR_MAPGEN_QUANTITY_OF_SEA_LAKES :{BLACK}Merenpinta: STR_MAPGEN_QUANTITY_OF_RIVERS :{BLACK}Joet: @@ -3507,7 +3514,7 @@ STR_GROUP_REPLACE_PROTECTION_TOOLTIP :{BLACK}Poista r STR_QUERY_GROUP_DELETE_CAPTION :{WHITE}Poista ryhmä STR_GROUP_DELETE_QUERY_TEXT :{WHITE}Haluatko varmasti poistaa tämän ryhmän ja sen alaryhmät? -STR_GROUP_ADD_SHARED_VEHICLE :Lisää jaettuja kulkuneuvoja +STR_GROUP_ADD_SHARED_VEHICLE :Lisää jaetut kulkuneuvot STR_GROUP_REMOVE_ALL_VEHICLES :Poista kaikki kulkuneuvot STR_GROUP_RENAME_CAPTION :{BLACK}Nimeä ryhmä diff --git a/src/lang/french.txt b/src/lang/french.txt index a260a4280b..ab7d15b7e5 100644 --- a/src/lang/french.txt +++ b/src/lang/french.txt @@ -950,8 +950,6 @@ STR_GAME_OPTIONS_CURRENCY_HKD :Dollar de Hong STR_GAME_OPTIONS_CURRENCY_INR :Roupie indienne (INR) ############ end of currency region -STR_GAME_OPTIONS_ROAD_VEHICLES_FRAME :{BLACK}Véhicules routiers -STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_TOOLTIP :{BLACK}Choisir le côté de la route pour la conduite STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_LEFT :Conduite à gauche STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_RIGHT :Conduite à droite @@ -1006,6 +1004,7 @@ STR_GAME_OPTIONS_RESOLUTION_OTHER :autre STR_GAME_OPTIONS_GUI_ZOOM_FRAME :{BLACK}Taille d'interface STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_TOOLTIP :{BLACK}Choisir la taille d'élément d'interface à utiliser +STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_AUTO :(auto-détection) STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_NORMAL :Normal STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_2X_ZOOM :Taille double STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_4X_ZOOM :Taille quadruple @@ -1013,6 +1012,7 @@ STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_4X_ZOOM :Taille quadrupl STR_GAME_OPTIONS_FONT_ZOOM :{BLACK}Taille de police STR_GAME_OPTIONS_FONT_ZOOM_DROPDOWN_TOOLTIP :{BLACK}Choisir la taille de police d'interface à utiliser +STR_GAME_OPTIONS_FONT_ZOOM_DROPDOWN_AUTO :(auto-détection) STR_GAME_OPTIONS_FONT_ZOOM_DROPDOWN_NORMAL :Normal STR_GAME_OPTIONS_FONT_ZOOM_DROPDOWN_2X_ZOOM :Taille double STR_GAME_OPTIONS_FONT_ZOOM_DROPDOWN_4X_ZOOM :Taille quadruple @@ -2607,12 +2607,18 @@ STR_FOUND_TOWN_SELECT_LAYOUT_RANDOM :{BLACK}Aléatoi # Fund new industry window STR_FUND_INDUSTRY_CAPTION :{WHITE}Financer une nouvelle industrie STR_FUND_INDUSTRY_SELECTION_TOOLTIP :{BLACK}Choisir l'industrie appropriée dans cette liste -STR_FUND_INDUSTRY_MANY_RANDOM_INDUSTRIES :Beaucoup d'industries au hasard +STR_FUND_INDUSTRY_MANY_RANDOM_INDUSTRIES :{BLACK}Créer des industries au hasard STR_FUND_INDUSTRY_MANY_RANDOM_INDUSTRIES_TOOLTIP :{BLACK}Couvrir la carte d'industries placées au hasard +STR_FUND_INDUSTRY_MANY_RANDOM_INDUSTRIES_CAPTION :{WHITE}Créer des industries au hasard +STR_FUND_INDUSTRY_MANY_RANDOM_INDUSTRIES_QUERY :{YELLOW}Êtes-vous sûr de vouloir créer beaucoup d'industries au hasard{NBSP}? STR_FUND_INDUSTRY_INDUSTRY_BUILD_COST :{BLACK}Prix{NBSP}: {YELLOW}{CURRENCY_LONG} STR_FUND_INDUSTRY_PROSPECT_NEW_INDUSTRY :{BLACK}Prospecter STR_FUND_INDUSTRY_BUILD_NEW_INDUSTRY :{BLACK}Construire STR_FUND_INDUSTRY_FUND_NEW_INDUSTRY :{BLACK}Fonder +STR_FUND_INDUSTRY_REMOVE_ALL_INDUSTRIES :{BLACK}Enlever toutes les industries +STR_FUND_INDUSTRY_REMOVE_ALL_INDUSTRIES_TOOLTIP :{BLACK}Enlève toutes les industries présentes sur la carte +STR_FUND_INDUSTRY_REMOVE_ALL_INDUSTRIES_CAPTION :{WHITE}Enlever toutes les industries +STR_FUND_INDUSTRY_REMOVE_ALL_INDUSTRIES_QUERY :{YELLOW}Êtes-vous sûr de vouloir enlever toutes les industries{NBSP}? # Industry cargoes window STR_INDUSTRY_CARGOES_INDUSTRY_CAPTION :{WHITE}Chaîne industrielle pour l'industrie «{NBSP}{STRING}{NBSP}» @@ -2843,6 +2849,8 @@ STR_SAVELOAD_DETAIL_GRFSTATUS :{SILVER}NewGRF{ STR_SAVELOAD_FILTER_TITLE :{BLACK}Filtre{NBSP}: STR_SAVELOAD_OVERWRITE_TITLE :{WHITE}Écraser le fichier STR_SAVELOAD_OVERWRITE_WARNING :{YELLOW}Êtes-vous sûr de vouloir écraser le fichier existant{NBSP}? +STR_SAVELOAD_DIRECTORY :{STRING} (Répertoire) +STR_SAVELOAD_PARENT_DIRECTORY :{STRING} (Répertoire parent) STR_SAVELOAD_OSKTITLE :{BLACK}Entrer un nom pour la sauvegarde @@ -2861,7 +2869,6 @@ STR_MAPGEN_SNOW_LINE_HEIGHT :{BLACK}Alt. d'e STR_MAPGEN_SNOW_LINE_UP :{BLACK}Augmenter l'altitude d'enneigement STR_MAPGEN_SNOW_LINE_DOWN :{BLACK}Baisser l'altitude d'enneigement STR_MAPGEN_LAND_GENERATOR :{BLACK}Générateur{NBSP}: -STR_MAPGEN_TREE_PLACER :{BLACK}Ajout des arbres{NBSP}: STR_MAPGEN_TERRAIN_TYPE :{BLACK}Type de terrain{NBSP}: STR_MAPGEN_QUANTITY_OF_SEA_LAKES :{BLACK}Niveau de la mer{NBSP}: STR_MAPGEN_QUANTITY_OF_RIVERS :{BLACK}Nb. de rivières{NBSP}: @@ -4696,7 +4703,7 @@ STR_TOWN_BUILDING_NAME_OFFICE_BLOCK_1 :Immeuble de bur STR_TOWN_BUILDING_NAME_SMALL_BLOCK_OF_FLATS_1 :Petit immeuble résidentiel STR_TOWN_BUILDING_NAME_CHURCH_1 :Église STR_TOWN_BUILDING_NAME_LARGE_OFFICE_BLOCK_1 :Gros immeuble de bureaux -STR_TOWN_BUILDING_NAME_TOWN_HOUSES_1 :Bâtiments municipaux +STR_TOWN_BUILDING_NAME_TOWN_HOUSES_1 :Maisons de ville STR_TOWN_BUILDING_NAME_HOTEL_1 :Hôtel STR_TOWN_BUILDING_NAME_STATUE_1 :Statue STR_TOWN_BUILDING_NAME_FOUNTAIN_1 :Fontaine diff --git a/src/lang/gaelic.txt b/src/lang/gaelic.txt index abdbfa13b5..7790cac726 100644 --- a/src/lang/gaelic.txt +++ b/src/lang/gaelic.txt @@ -1123,8 +1123,6 @@ STR_GAME_OPTIONS_CURRENCY_GEL :Lari Cairtbheil STR_GAME_OPTIONS_CURRENCY_IRR :Rial Iorànach (IRR) ############ end of currency region -STR_GAME_OPTIONS_ROAD_VEHICLES_FRAME :{BLACK}Carbadan-rathaid -STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_TOOLTIP :{BLACK}Tagh taobh an rathaid air a dhràibheas na carbadan STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_LEFT :Draibheadh air an taobh chlì STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_RIGHT :Draibheadh air an taobh deas @@ -2971,7 +2969,6 @@ STR_MAPGEN_SNOW_LINE_HEIGHT :{BLACK}Àirde n STR_MAPGEN_SNOW_LINE_UP :{BLACK}Gluais an loidhne-shneachda suas aon cheum STR_MAPGEN_SNOW_LINE_DOWN :{BLACK}Gluais an loidhne-shneachda sìos aon cheum STR_MAPGEN_LAND_GENERATOR :{BLACK}Gineadair crutha-thìre: -STR_MAPGEN_TREE_PLACER :{BLACK}Algairim chraobhan: STR_MAPGEN_TERRAIN_TYPE :{BLACK}Seòrsa a' chrutha-thìre: STR_MAPGEN_QUANTITY_OF_SEA_LAKES :{BLACK}Àirde na mara: STR_MAPGEN_QUANTITY_OF_RIVERS :{BLACK}Aibhnichean: diff --git a/src/lang/galician.txt b/src/lang/galician.txt index a3ae4ce5e2..66e463a7da 100644 --- a/src/lang/galician.txt +++ b/src/lang/galician.txt @@ -938,8 +938,6 @@ STR_GAME_OPTIONS_CURRENCY_CNY :Renminbi chiné STR_GAME_OPTIONS_CURRENCY_HKD :Dólar de Hong Kong (HKD) ############ end of currency region -STR_GAME_OPTIONS_ROAD_VEHICLES_FRAME :{BLACK}Vehículos de estrada -STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_TOOLTIP :{BLACK}Selecciona o lado da estrada polo que circularán os vehículos STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_LEFT :Conducir pola esquerda STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_RIGHT :Conducir pola dereita @@ -2812,7 +2810,6 @@ STR_MAPGEN_SNOW_LINE_HEIGHT :{BLACK}Cota de STR_MAPGEN_SNOW_LINE_UP :{BLACK}Subir a cota de neve unha altura STR_MAPGEN_SNOW_LINE_DOWN :{BLACK}Baixar a cota de neve unha altura STR_MAPGEN_LAND_GENERATOR :{BLACK}Xerador de terreo: -STR_MAPGEN_TREE_PLACER :{BLACK}Algoritmo para árbores: STR_MAPGEN_TERRAIN_TYPE :{BLACK}Tipo de terreo: STR_MAPGEN_QUANTITY_OF_SEA_LAKES :{BLACK}Nivel do mar: STR_MAPGEN_QUANTITY_OF_RIVERS :{BLACK}Ríos: diff --git a/src/lang/german.txt b/src/lang/german.txt index 6349058ade..dd1e6f7ae2 100644 --- a/src/lang/german.txt +++ b/src/lang/german.txt @@ -241,7 +241,7 @@ STR_TOOLTIP_CATCHMENT :{BLACK}Anzeige STR_TOOLTIP_CLOSE_WINDOW :{BLACK}Fenster schließen STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS :{BLACK}Titelleiste klicken und ziehen, um das Fenster zu verschieben -STR_TOOLTIP_SHADE :{BLACK}Fenster einklappen - Nur die Titelleiste zeigen +STR_TOOLTIP_SHADE :{BLACK}Fenster einklappen – Nur die Titelleiste zeigen STR_TOOLTIP_DEBUG :{BLACK}Zeige Daten für NewGRF-Fehlerbeseitigung STR_TOOLTIP_DEFSIZE :{BLACK}Standardgröße des Fensters wieder herstellen. Strg+Klick um die aktuelle Größe als Standard zu speichern STR_TOOLTIP_STICKY :{BLACK}Fenster wird nicht durch die „Schließe alle Fenster“-Taste geschlossen. Strg+Klick, um den Status als Standard zu speichern @@ -719,7 +719,7 @@ STR_HIGHSCORE_COMPANY_ACHIEVES_STATUS :{BIG_FONT}{BLAC STR_HIGHSCORE_PRESIDENT_OF_COMPANY_ACHIEVES_STATUS :{BIG_FONT}{WHITE}{PRESIDENT_NAME} von {COMPANY} erreicht „{STRING}“-Status! # Smallmap window -STR_SMALLMAP_CAPTION :{WHITE}Weltkarte - {STRING} +STR_SMALLMAP_CAPTION :{WHITE}Weltkarte – {STRING} STR_SMALLMAP_TYPE_CONTOURS :Höhenrelief STR_SMALLMAP_TYPE_VEHICLES :Fahrzeuge @@ -798,7 +798,7 @@ STR_STATUSBAR_SAVING_GAME :{RED}* * SPEI # News message history STR_MESSAGE_HISTORY :{WHITE}Nachrichtenchronik STR_MESSAGE_HISTORY_TOOLTIP :{BLACK}Eine Liste der aktuellen Nachrichten -STR_MESSAGE_NEWS_FORMAT :{STRING} - {STRING} +STR_MESSAGE_NEWS_FORMAT :{STRING} – {STRING} STR_NEWS_MESSAGE_CAPTION :{WHITE}Mitteilung STR_NEWS_CUSTOM_ITEM :{BIG_FONT}{BLACK}{STRING} @@ -885,7 +885,7 @@ STR_NEWS_VEHICLE_AUTORENEW_FAILED :{WHITE}Automati STR_NEWS_NEW_VEHICLE_NOW_AVAILABLE :{BIG_FONT}{BLACK}Neue{G r "" s ""} {STRING} jetzt erhältlich! STR_NEWS_NEW_VEHICLE_TYPE :{BIG_FONT}{BLACK}{ENGINE} -STR_NEWS_NEW_VEHICLE_NOW_AVAILABLE_WITH_TYPE :{BLACK}Neue{G r "" s ""} {STRING} jetzt erhältlich! - {ENGINE} +STR_NEWS_NEW_VEHICLE_NOW_AVAILABLE_WITH_TYPE :{BLACK}Neue{G r "" s ""} {STRING} jetzt erhältlich! – {ENGINE} STR_NEWS_SHOW_VEHICLE_GROUP_TOOLTIP :{BLACK}Das Gruppenfenster mit der bereits ausgewählten Fahrzeuggruppe öffnen @@ -961,8 +961,6 @@ STR_GAME_OPTIONS_CURRENCY_HKD :Hongkong-Dollar STR_GAME_OPTIONS_CURRENCY_INR :Indische Rupie (INR) ############ end of currency region -STR_GAME_OPTIONS_ROAD_VEHICLES_FRAME :{BLACK}Fahrzeuge -STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_TOOLTIP :{BLACK}Wähle, auf welcher Straßenseite der Verkehr fahren soll STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_LEFT :Linksverkehr STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_RIGHT :Rechtsverkehr @@ -1017,6 +1015,7 @@ STR_GAME_OPTIONS_RESOLUTION_OTHER :Andere STR_GAME_OPTIONS_GUI_ZOOM_FRAME :{BLACK}Größe der Bedienelemente STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_TOOLTIP :{BLACK}Wähle die Größe der Bedienelemente +STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_AUTO :(automatisch erkennen) STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_NORMAL :Normal STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_2X_ZOOM :Doppelt STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_4X_ZOOM :Vierfach @@ -1024,6 +1023,7 @@ STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_4X_ZOOM :Vierfach STR_GAME_OPTIONS_FONT_ZOOM :{BLACK}Schriftgröße STR_GAME_OPTIONS_FONT_ZOOM_DROPDOWN_TOOLTIP :{BLACK}Die Schriftgröße der Spieloberfläche auswählen +STR_GAME_OPTIONS_FONT_ZOOM_DROPDOWN_AUTO :(automatisch erkennen) STR_GAME_OPTIONS_FONT_ZOOM_DROPDOWN_NORMAL :Normal STR_GAME_OPTIONS_FONT_ZOOM_DROPDOWN_2X_ZOOM :Zweifach STR_GAME_OPTIONS_FONT_ZOOM_DROPDOWN_4X_ZOOM :Vierfach @@ -2063,7 +2063,7 @@ STR_CONFIG_SETTING_SCENARIO_HOUSE_IGNORE_ZONES_WITHIN_TOWN :Innerhalb des E STR_CONFIG_SETTING_SCENARIO_HOUSE_IGNORE_ZONES_ANYWHERE :Überall # Config errors -STR_CONFIG_ERROR :{WHITE}Fehler in der Konfigurations-Datei... +STR_CONFIG_ERROR :{WHITE}Fehler in der Konfigurations-Datei ... STR_CONFIG_ERROR_ARRAY :{WHITE}... Fehler im Array „{STRING}“ STR_CONFIG_ERROR_INVALID_VALUE :{WHITE}... ungültiger Wert „{STRING}“ für „{STRING}“ STR_CONFIG_ERROR_TRAILING_CHARACTERS :{WHITE}... angehängte Zeichen an der Einstellung „{STRING}“ @@ -2071,8 +2071,8 @@ STR_CONFIG_ERROR_DUPLICATE_GRFID :{WHITE}... igno STR_CONFIG_ERROR_INVALID_GRF :{WHITE}... ignoriere ungültiges NewGRF „{STRING}“: {STRING} STR_CONFIG_ERROR_INVALID_GRF_NOT_FOUND :nicht gefunden STR_CONFIG_ERROR_INVALID_GRF_UNSAFE :ungeignet für statischen Gebrauch -STR_CONFIG_ERROR_INVALID_GRF_SYSTEM :System - NewGRF -STR_CONFIG_ERROR_INVALID_GRF_INCOMPATIBLE :inkompatibel zu dieser OpenTTD - Version +STR_CONFIG_ERROR_INVALID_GRF_SYSTEM :System-NewGRF +STR_CONFIG_ERROR_INVALID_GRF_INCOMPATIBLE :inkompatibel zu dieser OpenTTD-Version STR_CONFIG_ERROR_INVALID_GRF_UNKNOWN :unbekannt STR_CONFIG_ERROR_INVALID_SAVEGAME_COMPRESSION_LEVEL :{WHITE}... Kompressionsgrad „{STRING}“ ist ungültig STR_CONFIG_ERROR_INVALID_SAVEGAME_COMPRESSION_ALGORITHM :{WHITE}... „{STRING}“ nicht als Format für Spielstände verfügbar. Nutze stattdessen „{STRING}“ @@ -2158,7 +2158,7 @@ STR_CHEAT_INFLATION_INCOME :{LTBLUE}Ändere STR_CHEAT_INFLATION_INCOME_QUERY_CAPT :{WHITE}Ändert den Faktor der Inflation des Einkommens # Livery window -STR_LIVERY_CAPTION :{WHITE}{COMPANY} - Farbschema +STR_LIVERY_CAPTION :{WHITE}{COMPANY} – Farbschema STR_LIVERY_GENERAL_TOOLTIP :{BLACK}Allgemeines Farbschema anzeigen STR_LIVERY_TRAIN_TOOLTIP :{BLACK}Zugfarbschema anzeigen @@ -2258,7 +2258,7 @@ STR_NETWORK_SERVER_LIST_ENTER_NAME_TOOLTIP :{BLACK}Dieser N STR_NETWORK_SERVER_LIST_GAME_NAME :{BLACK}Name STR_NETWORK_SERVER_LIST_GAME_NAME_TOOLTIP :{BLACK}Name des Spiels -STR_NETWORK_SERVER_LIST_GENERAL_ONLINE :{BLACK}{COMMA}/{COMMA} - {COMMA}/{COMMA} +STR_NETWORK_SERVER_LIST_GENERAL_ONLINE :{BLACK}{COMMA}/{COMMA} – {COMMA}/{COMMA} STR_NETWORK_SERVER_LIST_CLIENTS_CAPTION :{BLACK}Teilnehmer STR_NETWORK_SERVER_LIST_CLIENTS_CAPTION_TOOLTIP :{BLACK}Teilnehmer anwesend/max.{}Firmen anwesend/max. STR_NETWORK_SERVER_LIST_MAP_SIZE_SHORT :{BLACK}{COMMA}x{COMMA} @@ -2275,7 +2275,7 @@ STR_NETWORK_SERVER_LIST_LAST_JOINED_SERVER :{BLACK}Zuletzt STR_NETWORK_SERVER_LIST_CLICK_TO_SELECT_LAST :{BLACK}Zuletzt besuchten Server auswählen STR_NETWORK_SERVER_LIST_GAME_INFO :{SILVER}SPIEL-INFO -STR_NETWORK_SERVER_LIST_CLIENTS :{SILVER}Teilnehmer: {WHITE}{COMMA} / {COMMA} - {COMMA} / {COMMA} +STR_NETWORK_SERVER_LIST_CLIENTS :{SILVER}Teilnehmer: {WHITE}{COMMA} / {COMMA} – {COMMA} / {COMMA} STR_NETWORK_SERVER_LIST_LANGUAGE :{SILVER}Sprache: {WHITE}{STRING} STR_NETWORK_SERVER_LIST_LANDSCAPE :{SILVER}Landschaft: {WHITE}{STRING} STR_NETWORK_SERVER_LIST_MAP_SIZE :{SILVER}Spielfeldgröße: {WHITE}{COMMA}x{COMMA} @@ -3058,7 +3058,7 @@ STR_ERROR_INVALID_SIGNAL :{WHITE}Ungülti # Bridge selection window STR_SELECT_RAIL_BRIDGE_CAPTION :{WHITE}Eisenbahnbrücke auswählen STR_SELECT_ROAD_BRIDGE_CAPTION :{WHITE}Straßenbrücke wählen -STR_SELECT_BRIDGE_SELECTION_TOOLTIP :{BLACK}Brückenauswahl - Auf die gewünschte Brücke klicken, um sie zu bauen +STR_SELECT_BRIDGE_SELECTION_TOOLTIP :{BLACK}Brückenauswahl – Auf die gewünschte Brücke klicken, um sie zu bauen STR_SELECT_BRIDGE_INFO :{GOLD}{STRING},{} {VELOCITY} {WHITE}{CURRENCY_LONG} STR_SELECT_BRIDGE_SCENEDIT_INFO :{GOLD}{STRING},{} {VELOCITY} STR_BRIDGE_NAME_SUSPENSION_STEEL :Stahlhängebrücke @@ -3272,12 +3272,18 @@ STR_FOUND_TOWN_SELECT_LAYOUT_RANDOM :{BLACK}Zufall # Fund new industry window STR_FUND_INDUSTRY_CAPTION :{WHITE}Neue Industrie finanzieren STR_FUND_INDUSTRY_SELECTION_TOOLTIP :{BLACK}Gewünschte Industrie aus der Liste auswählen -STR_FUND_INDUSTRY_MANY_RANDOM_INDUSTRIES :Viele zufällige Industrien +STR_FUND_INDUSTRY_MANY_RANDOM_INDUSTRIES :{BLACK}Zufallsindustrien erzeugen STR_FUND_INDUSTRY_MANY_RANDOM_INDUSTRIES_TOOLTIP :{BLACK}Das Spielfeld mit zufällig platzierten Industrien füllen +STR_FUND_INDUSTRY_MANY_RANDOM_INDUSTRIES_CAPTION :{WHITE}Zufallsindustrien erzeugen +STR_FUND_INDUSTRY_MANY_RANDOM_INDUSTRIES_QUERY :{YELLOW}Möchten Sie wirklich viele zufällige Industrien erzeugen? STR_FUND_INDUSTRY_INDUSTRY_BUILD_COST :{BLACK}Kosten: {YELLOW}{CURRENCY_LONG} STR_FUND_INDUSTRY_PROSPECT_NEW_INDUSTRY :{BLACK}Prospektieren STR_FUND_INDUSTRY_BUILD_NEW_INDUSTRY :{BLACK}Bauen STR_FUND_INDUSTRY_FUND_NEW_INDUSTRY :{BLACK}Finanzieren +STR_FUND_INDUSTRY_REMOVE_ALL_INDUSTRIES :{BLACK}Alle Industrien entfernen +STR_FUND_INDUSTRY_REMOVE_ALL_INDUSTRIES_TOOLTIP :{BLACK}Alle Industrien, die sich momentan auf der Karte befinden, entfernen +STR_FUND_INDUSTRY_REMOVE_ALL_INDUSTRIES_CAPTION :{WHITE}Alle Industrien entfernen +STR_FUND_INDUSTRY_REMOVE_ALL_INDUSTRIES_QUERY :{YELLOW}Sind Sie sich sicher, dass Sie alle Industrien entfernen wollen? # Industry cargoes window STR_INDUSTRY_CARGOES_INDUSTRY_CAPTION :{WHITE}Produktionskette für {STRING} @@ -3435,7 +3441,7 @@ STR_LAI_OBJECT_DESCRIPTION_COMPANY_OWNED_LAND :Firmeneigenes G STR_ABOUT_OPENTTD :{WHITE}OpenTTD STR_ABOUT_ORIGINAL_COPYRIGHT :{BLACK}Ursprüngliches Copyright {COPYRIGHT} 1995 Chris Sawyer, alle Rechte vorbehalten STR_ABOUT_VERSION :{BLACK}OpenTTD-Version {REV} -STR_ABOUT_COPYRIGHT_OPENTTD :{BLACK}OpenTTD {COPYRIGHT} 2002-{STRING} OpenTTD-Team +STR_ABOUT_COPYRIGHT_OPENTTD :{BLACK}OpenTTD {COPYRIGHT} 2002–{STRING} OpenTTD-Team # Framerate display window STR_FRAMERATE_CAPTION :{WHITE}Bildwiederholrate @@ -3522,6 +3528,8 @@ STR_SAVELOAD_DETAIL_GRFSTATUS :{SILVER}NewGRF: STR_SAVELOAD_FILTER_TITLE :{BLACK}Suchtext: STR_SAVELOAD_OVERWRITE_TITLE :{WHITE}Datei überschreiben STR_SAVELOAD_OVERWRITE_WARNING :{YELLOW}Existierende Datei wirklich überschreiben? +STR_SAVELOAD_DIRECTORY :{STRING} (Verzeichnis) +STR_SAVELOAD_PARENT_DIRECTORY :{STRING} (Überverzeichnis) STR_SAVELOAD_UNKNOWN_ID :{YELLOW}Diese Datei ist möglicherweise von einem anderen Spiel. STR_SAVELOAD_DIFFERENT_ID :{YELLOW}Diese Datei ist von einem anderen Spiel. STR_SAVELOAD_OVERWRITE_TITLE_DIFFERENT_ID :{WHITE}Datei überschreiben (Anderes Spiel) @@ -3544,7 +3552,6 @@ STR_MAPGEN_SNOW_LINE_HEIGHT :{BLACK}Schneegr STR_MAPGEN_SNOW_LINE_UP :{BLACK}Schneegrenze um eine Stufe erhöhen STR_MAPGEN_SNOW_LINE_DOWN :{BLACK}Schneegrenze um eine Stufe senken STR_MAPGEN_LAND_GENERATOR :{BLACK}Landgenerator: -STR_MAPGEN_TREE_PLACER :{BLACK}Baumalgorithmus: STR_MAPGEN_TERRAIN_TYPE :{BLACK}Landschaftstyp: STR_MAPGEN_QUANTITY_OF_SEA_LAKES :{BLACK}Gewässermenge: STR_MAPGEN_QUANTITY_OF_RIVERS :{BLACK}Flüsse: @@ -3604,9 +3611,9 @@ STR_GENERATION_PREPARING_GAME :{BLACK}Spiel wi # NewGRF settings STR_NEWGRF_SETTINGS_CAPTION :{WHITE}NewGRF-Einstellungen -STR_NEWGRF_SETTINGS_INFO_TITLE :{WHITE}Detailierte NewGRF - Informationen -STR_NEWGRF_SETTINGS_ACTIVE_LIST :{WHITE}Aktivierte NewGRF - Dateien -STR_NEWGRF_SETTINGS_INACTIVE_LIST :{WHITE}Inaktive NewGRF - Dateien +STR_NEWGRF_SETTINGS_INFO_TITLE :{WHITE}Detailierte NewGRF-Informationen +STR_NEWGRF_SETTINGS_ACTIVE_LIST :{WHITE}Aktivierte NewGRF-Dateien +STR_NEWGRF_SETTINGS_INACTIVE_LIST :{WHITE}Inaktive NewGRF-Dateien STR_NEWGRF_SETTINGS_SELECT_PRESET :{ORANGE}Wähle Voreinstellung STR_NEWGRF_FILTER_TITLE :{ORANGE}Filter: STR_NEWGRF_SETTINGS_PRESET_LIST_TOOLTIP :{BLACK}Ausgewählte Voreinstellung laden @@ -3676,7 +3683,7 @@ STR_NEWGRF_PARAMETERS_SETTING :{STRING}: {ORAN STR_NEWGRF_PARAMETERS_NUM_PARAM :{LTBLUE}Anzahl Parameter: {ORANGE}{NUM} # NewGRF inspect window -STR_NEWGRF_INSPECT_CAPTION :{WHITE}Inspiziere - {STRING} +STR_NEWGRF_INSPECT_CAPTION :{WHITE}Inspiziere – {STRING} STR_NEWGRF_INSPECT_PARENT_BUTTON :{BLACK}übergeordnetes Objekt STR_NEWGRF_INSPECT_PARENT_TOOLTIP :{BLACK}Inspiziere das Objekt im übergeordneten Geltungsbereich STR_NEWGRF_INSPECT_REFRESH :{BLACK}A @@ -3725,12 +3732,10 @@ STR_NEWGRF_ERROR_AFTER_TRANSLATED_FILE :der NewGRF-Date STR_NEWGRF_ERROR_TOO_MANY_NEWGRFS_LOADED :Zu viele NewGRFs geladen STR_NEWGRF_ERROR_STATIC_GRF_CAUSES_DESYNC :Das Laden von {1:STRING} als statisches NewGRF mit {STRING} könnte Synchronisationsfehler hervorrufen STR_NEWGRF_ERROR_UNEXPECTED_SPRITE :Unerwartetes Sprite (Sprite {3:NUM}) -STR_NEWGRF_ERROR_UNKNOWN_PROPERTY :Unbekannte Action 0 - Eigenschaft {4:HEX} (Sprite {3:NUM}) - - +STR_NEWGRF_ERROR_UNKNOWN_PROPERTY :Unbekannte Action-0-Eigenschaft {4:HEX} (Sprite {3:NUM}) STR_NEWGRF_ERROR_INVALID_ID :Zugriff auf eine ungültige ID (Sprite {3:NUM}) STR_NEWGRF_ERROR_CORRUPT_SPRITE :{YELLOW}{STRING} enthält fehlerhafte Grafiken. Diese werden als Fragezeichen (?) dargestellt -STR_NEWGRF_ERROR_MULTIPLE_ACTION_8 :Enthält mehrere Action 8 - Einträge (Sprite {3:NUM}) +STR_NEWGRF_ERROR_MULTIPLE_ACTION_8 :Enthält mehrere Action-8-Einträge (Sprite {3:NUM}) STR_NEWGRF_ERROR_READ_BOUNDS :Lesezugriff über das Ende des Pseudosprites hinaus (Sprite {3:NUM}) STR_NEWGRF_ERROR_GRM_FAILED :Die angeforderte GRF-Ressource ist nicht verfügbar (Sprite {3:NUM}) STR_NEWGRF_ERROR_FORCEFULLY_DISABLED :{1:STRING} wurde von {STRING} deaktiviert @@ -3785,7 +3790,7 @@ STR_INVALID_VEHICLE :auto_select) { pos = strecpy(pos, "do=searchgrfid&q=", last); diff --git a/src/network/network_gui.cpp b/src/network/network_gui.cpp index 8e51c51a51..e176a2ef4f 100644 --- a/src/network/network_gui.cpp +++ b/src/network/network_gui.cpp @@ -347,7 +347,7 @@ protected: if (r == 0) r = a->info.use_password - b->info.use_password; /* Finally sort on the number of clients of the server in reverse order. */ - return (r != 0) ? r < 0: !NGameClientSorter(a, b); + return (r != 0) ? r < 0 : !NGameClientSorter(a, b); } /** Sort the server list */ diff --git a/src/newgrf_engine.cpp b/src/newgrf_engine.cpp index 5dfa93cb5f..fc75d9b8cc 100644 --- a/src/newgrf_engine.cpp +++ b/src/newgrf_engine.cpp @@ -898,16 +898,29 @@ static uint32 VehicleGetVariable(Vehicle *v, const VehicleScopeResolver *object, } } - /* General vehicle properties */ + /* + * General vehicle properties + * + * Some parts of the TTD Vehicle structure are omitted for various reasons + * (see http://marcin.ttdpatch.net/sv1codec/TTD-locations.html#_VehicleArray) + */ switch (variable - 0x80) { case 0x00: return v->type + 0x10; case 0x01: return MapOldSubType(v); + case 0x02: break; // not implemented + case 0x03: break; // not implemented case 0x04: return v->index; case 0x05: return GB(v->index, 8, 8); + case 0x06: break; // not implemented + case 0x07: break; // not implemented + case 0x08: break; // not implemented + case 0x09: break; // not implemented case 0x0A: return v->current_order.MapOldOrder(); case 0x0B: return v->current_order.GetDestination(); case 0x0C: return v->GetNumOrders(); case 0x0D: return v->cur_real_order_index; + case 0x0E: break; // not implemented + case 0x0F: break; // not implemented case 0x10: case 0x11: { uint ticks; @@ -948,14 +961,31 @@ static uint32 VehicleGetVariable(Vehicle *v, const VehicleScopeResolver *object, case 0x1D: return GB(v->y_pos, 8, 8); case 0x1E: return v->z_pos; case 0x1F: return object->info_view ? DIR_W : v->direction; + case 0x20: break; // not implemented + case 0x21: break; // not implemented + case 0x22: break; // not implemented + case 0x23: break; // not implemented + case 0x24: break; // not implemented + case 0x25: break; // not implemented + case 0x26: break; // not implemented + case 0x27: break; // not implemented case 0x28: return 0; // cur_image is a potential desyncer due to Action1 in static NewGRFs. case 0x29: return 0; // cur_image is a potential desyncer due to Action1 in static NewGRFs. + case 0x2A: break; // not implemented + case 0x2B: break; // not implemented + case 0x2C: break; // not implemented + case 0x2D: break; // not implemented + case 0x2E: break; // not implemented + case 0x2F: break; // not implemented + case 0x30: break; // not implemented + case 0x31: break; // not implemented case 0x32: return v->vehstatus; case 0x33: return 0; // non-existent high byte of vehstatus case 0x34: return v->type == VEH_AIRCRAFT ? (v->cur_speed * 10) / 128 : v->cur_speed; case 0x35: return GB(v->type == VEH_AIRCRAFT ? (v->cur_speed * 10) / 128 : v->cur_speed, 8, 8); case 0x36: return v->subspeed; case 0x37: return v->acceleration; + case 0x38: break; // not implemented case 0x39: return v->cargo_type; case 0x3A: return v->cargo_cap; case 0x3B: return GB(v->cargo_cap, 8, 8); @@ -993,13 +1023,43 @@ static uint32 VehicleGetVariable(Vehicle *v, const VehicleScopeResolver *object, case 0x58: return GB(ClampToI32(v->GetDisplayProfitLastYear()), 16, 16); case 0x59: return GB(ClampToI32(v->GetDisplayProfitLastYear()), 24, 8); case 0x5A: return v->Next() == nullptr ? INVALID_VEHICLE : v->Next()->index; + case 0x5B: break; // not implemented case 0x5C: return ClampToI32(v->value); case 0x5D: return GB(ClampToI32(v->value), 8, 24); case 0x5E: return GB(ClampToI32(v->value), 16, 16); case 0x5F: return GB(ClampToI32(v->value), 24, 8); + case 0x60: break; // not implemented + case 0x61: break; // not implemented + case 0x62: break; // vehicle specific, see below + case 0x63: break; // not implemented + case 0x64: break; // vehicle specific, see below + case 0x65: break; // vehicle specific, see below + case 0x66: break; // vehicle specific, see below + case 0x67: break; // vehicle specific, see below + case 0x68: break; // vehicle specific, see below + case 0x69: break; // vehicle specific, see below + case 0x6A: break; // not implemented + case 0x6B: break; // not implemented + case 0x6C: break; // not implemented + case 0x6D: break; // not implemented + case 0x6E: break; // not implemented + case 0x6F: break; // not implemented + case 0x70: break; // not implemented + case 0x71: break; // not implemented case 0x72: return v->cargo_subtype; + case 0x73: break; // vehicle specific, see below + case 0x74: break; // vehicle specific, see below + case 0x75: break; // vehicle specific, see below + case 0x76: break; // vehicle specific, see below + case 0x77: break; // vehicle specific, see below + case 0x78: break; // not implemented + case 0x79: break; // not implemented case 0x7A: return v->random_bits; case 0x7B: return v->waiting_triggers; + case 0x7C: break; // vehicle specific, see below + case 0x7D: break; // vehicle specific, see below + case 0x7E: break; // not implemented + case 0x7F: break; // vehicle specific, see below } /* Vehicle specific properties */ diff --git a/src/newgrf_townname.cpp b/src/newgrf_townname.cpp index 207114f61d..a7bc4deeaf 100644 --- a/src/newgrf_townname.cpp +++ b/src/newgrf_townname.cpp @@ -17,9 +17,12 @@ #include "core/alloc_func.hpp" #include "string_func.h" +#include "table/strings.h" + #include "safeguards.h" static GRFTownName *_grf_townnames = nullptr; +static std::vector _grf_townname_names; GRFTownName *GetGRFTownName(uint32 grfid) { @@ -101,16 +104,24 @@ char *GRFTownNameGenerate(char *buf, uint32 grfid, uint16 gen, uint32 seed, cons return buf; } -StringID *GetGRFTownNameList() + +/** Allocate memory for the NewGRF town names. */ +void InitGRFTownGeneratorNames() { - int nb_names = 0, n = 0; - for (GRFTownName *t = _grf_townnames; t != nullptr; t = t->next) nb_names += t->nb_gen; - StringID *list = MallocT(nb_names + 1); + _grf_townname_names.clear(); for (GRFTownName *t = _grf_townnames; t != nullptr; t = t->next) { - for (int j = 0; j < t->nb_gen; j++) list[n++] = t->name[j]; + for (int j = 0; j < t->nb_gen; j++) _grf_townname_names.push_back(t->name[j]); } - list[n] = INVALID_STRING_ID; - return list; +} + +const std::vector& GetGRFTownNameList() +{ + return _grf_townname_names; +} + +StringID GetGRFTownNameName(uint gen) +{ + return gen < _grf_townname_names.size() ? _grf_townname_names[gen] : STR_UNDEFINED; } void CleanUpGRFTownNames() diff --git a/src/newgrf_townname.h b/src/newgrf_townname.h index 6406e74345..d8fc462aae 100644 --- a/src/newgrf_townname.h +++ b/src/newgrf_townname.h @@ -13,6 +13,7 @@ #ifndef NEWGRF_TOWNNAME_H #define NEWGRF_TOWNNAME_H +#include #include "strings_type.h" struct NamePart { @@ -45,9 +46,11 @@ GRFTownName *AddGRFTownName(uint32 grfid); GRFTownName *GetGRFTownName(uint32 grfid); void DelGRFTownName(uint32 grfid); void CleanUpGRFTownNames(); -StringID *GetGRFTownNameList(); char *GRFTownNameGenerate(char *buf, uint32 grfid, uint16 gen, uint32 seed, const char *last); uint32 GetGRFTownNameId(int gen); uint16 GetGRFTownNameType(int gen); +StringID GetGRFTownNameName(uint gen); + +const std::vector& GetGRFTownNameList(); #endif /* NEWGRF_TOWNNAME_H */ diff --git a/src/news_gui.cpp b/src/news_gui.cpp index 6030a56ab4..f964cd938c 100644 --- a/src/news_gui.cpp +++ b/src/news_gui.cpp @@ -34,6 +34,7 @@ #include "settings_internal.h" #include "guitimer_func.h" #include "group_gui.h" +#include "zoom_func.h" #include "widgets/news_widget.h" @@ -1173,8 +1174,8 @@ struct MessageHistoryWindow : Window { bool rtl = _current_text_dir == TD_RTL; uint date_left = rtl ? r.right - WD_FRAMERECT_RIGHT - this->date_width : r.left + WD_FRAMERECT_LEFT; uint date_right = rtl ? r.right - WD_FRAMERECT_RIGHT : r.left + WD_FRAMERECT_LEFT + this->date_width; - uint news_left = rtl ? r.left + WD_FRAMERECT_LEFT : r.left + WD_FRAMERECT_LEFT + this->date_width + WD_FRAMERECT_RIGHT; - uint news_right = rtl ? r.right - WD_FRAMERECT_RIGHT - this->date_width - WD_FRAMERECT_RIGHT : r.right - WD_FRAMERECT_RIGHT; + uint news_left = rtl ? r.left + WD_FRAMERECT_LEFT : r.left + WD_FRAMERECT_LEFT + this->date_width + WD_FRAMERECT_RIGHT + ScaleFontTrad(5); + uint news_right = rtl ? r.right - WD_FRAMERECT_RIGHT - this->date_width - WD_FRAMERECT_RIGHT - ScaleFontTrad(5) : r.right - WD_FRAMERECT_RIGHT; for (int n = this->vscroll->GetCapacity(); n > 0; n--) { SetDParam(0, ni->date); DrawString(date_left, date_right, y, STR_SHORT_DATE); diff --git a/src/object_cmd.cpp b/src/object_cmd.cpp index afa8d3d9ad..3d7812cbdb 100644 --- a/src/object_cmd.cpp +++ b/src/object_cmd.cpp @@ -643,7 +643,10 @@ static CommandCost ClearTile_Object(TileIndex tile, DoCommandFlag flags) return_cmd_error(STR_ERROR_OWNED_BY); } else if ((spec->flags & OBJECT_FLAG_CANNOT_REMOVE) != 0 && (spec->flags & OBJECT_FLAG_AUTOREMOVE) == 0) { /* In the game editor or with cheats we can remove, otherwise we can't. */ - if (!_cheats.magic_bulldozer.value) return CMD_ERROR; + if (!_cheats.magic_bulldozer.value) { + if (type == OBJECT_HQ) return_cmd_error(STR_ERROR_COMPANY_HEADQUARTERS_IN); + return CMD_ERROR; + } /* Removing with the cheat costs more in TTDPatch / the specs. */ cost.MultiplyCost(25); diff --git a/src/object_gui.cpp b/src/object_gui.cpp index fec784a652..f7d981048c 100644 --- a/src/object_gui.cpp +++ b/src/object_gui.cpp @@ -12,6 +12,10 @@ #include "newgrf.h" #include "newgrf_object.h" #include "newgrf_text.h" +#include "querystring_gui.h" +#include "sortlist_type.h" +#include "stringfilter_type.h" +#include "string_func.h" #include "strings_func.h" #include "viewport_func.h" #include "tilehighlight_func.h" @@ -25,23 +29,35 @@ #include "safeguards.h" -static ObjectClassID _selected_object_class; ///< the currently visible object class -static int _selected_object_index; ///< the index of the selected object in the current class or -1 -static uint8 _selected_object_view; ///< the view of the selected object +static const ObjectClass *_selected_object_class; ///< Currently selected available object class. +static int _selected_object_index; ///< Index of the currently selected object if existing, else \c -1. +static uint8 _selected_object_view; ///< the view of the selected object /** The window used for building objects. */ class BuildObjectWindow : public Window { - static const int OBJECT_MARGIN = 4; ///< The margin (in pixels) around an object. - int line_height; ///< The height of a single line. - int info_height; ///< The height of the info box. - Scrollbar *vscroll; ///< The scrollbar. + typedef GUIList GUIObjectClassList; ///< Type definition for the list to hold available object classes. + + static const uint EDITBOX_MAX_SIZE = 16; ///< The maximum number of characters for the filter edit box. + static const int OBJECT_MARGIN = 4; ///< The margin (in pixels) around an object. + + int line_height; ///< The height of a single line. + int info_height; ///< The height of the info box. + Scrollbar* vscroll; ///< The scrollbar. + + static Listing last_sorting; ///< Default sorting of #GUIObjectClassList. + static Filtering last_filtering; ///< Default filtering of #GUIObjectClassList. + static GUIObjectClassList::SortFunction * const sorter_funcs[]; ///< Sort functions of the #GUIObjectClassList. + static GUIObjectClassList::FilterFunction * const filter_funcs[]; ///< Filter functions of the #GUIObjectClassList. + GUIObjectClassList object_classes; ///< Available object classes. + StringFilter string_filter; ///< Filter for available objects. + QueryString filter_editbox; ///< Filter editbox. /** Scroll #WID_BO_CLASS_LIST so that the selected object class is visible. */ void EnsureSelectedObjectClassIsVisible() { uint pos = 0; - for (int i = 0; i < _selected_object_class; i++) { - if (ObjectClass::Get((ObjectClassID) i)->GetUISpecCount() == 0) continue; + for (auto objclass : this->object_classes) { + if (objclass == _selected_object_class) break; pos++; } this->vscroll->ScrollTowards(pos); @@ -55,10 +71,9 @@ class BuildObjectWindow : public Window { { if (_selected_object_index == -1) return false; - ObjectClass *sel_objclass = ObjectClass::Get(_selected_object_class); - if ((int)sel_objclass->GetSpecCount() <= _selected_object_index) return false; + if ((int)_selected_object_class->GetSpecCount() <= _selected_object_index) return false; - return sel_objclass->GetSpec(_selected_object_index)->IsAvailable(); + return _selected_object_class->GetSpec(_selected_object_index)->IsAvailable(); } /** @@ -72,42 +87,132 @@ class BuildObjectWindow : public Window { } public: - BuildObjectWindow(WindowDesc *desc, WindowNumber number) : Window(desc), info_height(1) + BuildObjectWindow(WindowDesc *desc, WindowNumber number) : Window(desc), info_height(1), filter_editbox(EDITBOX_MAX_SIZE) { this->CreateNestedTree(); + this->vscroll = this->GetScrollbar(WID_BO_SCROLLBAR); - this->FinishInitNested(number); - ResetObjectToPlace(); + this->querystrings[WID_BO_FILTER] = &this->filter_editbox; - this->vscroll->SetPosition(0); - this->vscroll->SetCount(ObjectClass::GetUIClassCount()); + this->object_classes.SetListing(this->last_sorting); + this->object_classes.SetFiltering(this->last_filtering); + this->object_classes.SetSortFuncs(this->sorter_funcs); + this->object_classes.SetFilterFuncs(this->filter_funcs); + this->object_classes.ForceRebuild(); + + BuildObjectClassesAvailable(); + SelectClassAndObject(); + + this->FinishInitNested(number); NWidgetMatrix *matrix = this->GetWidget(WID_BO_SELECT_MATRIX); matrix->SetScrollbar(this->GetScrollbar(WID_BO_SELECT_SCROLL)); - this->SelectOtherClass(_selected_object_class); + this->SetFocusedWidget(WID_BO_FILTER); + this->GetWidget(WID_BO_OBJECT_MATRIX)->SetCount(4); + + ResetObjectToPlace(); + + this->vscroll->SetCount((int) this->object_classes.size()); + + EnsureSelectedObjectClassIsVisible(); + + this->InvalidateData(); + } + + /** Sort object classes by class name. */ + static bool NameSorter(const ObjectClass *const& a, const ObjectClass *const& b) + { + char buffer_a[DRAW_STRING_BUFFER]; + GetString(buffer_a, a->name, lastof(buffer_a)); + + char buffer_b[DRAW_STRING_BUFFER]; + GetString(buffer_b, b->name, lastof(buffer_b)); + + return strnatcmp(buffer_a, buffer_b, true) < 0; // Sort by name (natural sorting). + } + + /** Filter object classes by class name. */ + static bool CDECL TagNameFilter(const ObjectClass *const* oc, StringFilter& filter) + { + char buffer[DRAW_STRING_BUFFER]; + GetString(buffer, (*oc)->name, lastof(buffer)); + + filter.ResetState(); + filter.AddLine(buffer); + return filter.GetState(); + } + + /** Builds the filter list of available object classes. */ + void BuildObjectClassesAvailable() + { + if (!this->object_classes.NeedRebuild()) return; + + this->object_classes.clear(); + + for (uint i = 0; i < ObjectClass::GetClassCount(); i++) { + ObjectClass *objclass = ObjectClass::Get((ObjectClassID)i); + if (objclass->GetUISpecCount() == 0) continue; // Is this needed here? + object_classes.push_back(objclass); + } + + this->object_classes.Filter(this->string_filter); + this->object_classes.shrink_to_fit(); + this->object_classes.RebuildDone(); + this->object_classes.Sort(); + + this->vscroll->SetCount((uint)this->object_classes.size()); + } + + /** + * Checks if the previously selected current object class and object + * can be shown as selected to the user when the dialog is opened. + */ + void SelectClassAndObject() + { + assert(!this->object_classes.empty()); // object GUI should be disabled elsewise + if (_selected_object_class == nullptr) { + /* This happens during the first time the window is open during the game life cycle. */ + this->SelectOtherClass(this->object_classes[0]); + } else { + /* Check if the previously selected object class is not available anymore as a + * result of starting a new game without the corresponding NewGRF. */ + bool available = false; + for (uint i = 0; ObjectClass::GetClassCount(); ++i) { + ObjectClass* objclass = ObjectClass::Get((ObjectClassID)i); + if (objclass == _selected_object_class) { + available = true; + break; + } + } + + if (available) { + this->SelectOtherClass(_selected_object_class); + } else { + this->SelectOtherClass(this->object_classes[0]); + } + } + if (this->CanRestoreSelectedObject()) { this->SelectOtherObject(_selected_object_index); } else { this->SelectFirstAvailableObject(true); } - assert(ObjectClass::Get(_selected_object_class)->GetUISpecCount() > 0); // object GUI should be disables elsewise - this->EnsureSelectedObjectClassIsVisible(); - this->GetWidget(WID_BO_OBJECT_MATRIX)->SetCount(4); + assert(_selected_object_class->GetUISpecCount() > 0); // object GUI should be disabled elsewise } void SetStringParameters(int widget) const override { switch (widget) { case WID_BO_OBJECT_NAME: { - const ObjectSpec *spec = ObjectClass::Get(_selected_object_class)->GetSpec(_selected_object_index); + const ObjectSpec *spec = _selected_object_class->GetSpec(_selected_object_index); SetDParam(0, spec != nullptr ? spec->name : STR_EMPTY); break; } case WID_BO_OBJECT_SIZE: { - const ObjectSpec *spec = ObjectClass::Get(_selected_object_class)->GetSpec(_selected_object_index); + const ObjectSpec *spec = _selected_object_class->GetSpec(_selected_object_index); int size = spec == nullptr ? 0 : spec->size; SetDParam(0, GB(size, HasBit(_selected_object_view, 0) ? 4 : 0, 4)); SetDParam(1, GB(size, HasBit(_selected_object_view, 0) ? 0 : 4, 4)); @@ -122,8 +227,7 @@ public: { switch (widget) { case WID_BO_CLASS_LIST: { - for (uint i = 0; i < ObjectClass::GetClassCount(); i++) { - ObjectClass *objclass = ObjectClass::Get((ObjectClassID)i); + for (auto objclass : this->object_classes) { if (objclass->GetUISpecCount() == 0) continue; size->width = std::max(size->width, GetStringBoundingBox(objclass->name).width); } @@ -142,7 +246,7 @@ public: case WID_BO_OBJECT_MATRIX: { /* Get the right amount of buttons based on the current spec. */ - const ObjectSpec *spec = ObjectClass::Get(_selected_object_class)->GetSpec(_selected_object_index); + const ObjectSpec *spec = _selected_object_class->GetSpec(_selected_object_index); if (spec != nullptr) { if (spec->views >= 2) size->width += resize->width; if (spec->views >= 4) size->height += resize->height; @@ -182,7 +286,7 @@ public: } /* Get the right size for the single widget based on the current spec. */ - const ObjectSpec *spec = ObjectClass::Get(_selected_object_class)->GetSpec(_selected_object_index); + const ObjectSpec *spec = _selected_object_class->GetSpec(_selected_object_index); if (spec != nullptr) { if (spec->views >= 2) size->width = size->width / 2 - 1; if (spec->views >= 4) size->height = size->height / 2 - 1; @@ -214,19 +318,20 @@ public: case WID_BO_CLASS_LIST: { int y = r.top; uint pos = 0; - for (uint i = 0; i < ObjectClass::GetClassCount(); i++) { - ObjectClass *objclass = ObjectClass::Get((ObjectClassID)i); + for (auto objclass : this->object_classes) { if (objclass->GetUISpecCount() == 0) continue; if (!this->vscroll->IsVisible(pos++)) continue; DrawString(r.left + WD_MATRIX_LEFT, r.right - WD_MATRIX_RIGHT, y + WD_MATRIX_TOP, objclass->name, - ((int)i == _selected_object_class) ? TC_WHITE : TC_BLACK); + (objclass == _selected_object_class) ? TC_WHITE : TC_BLACK); y += this->line_height; } break; } case WID_BO_OBJECT_SPRITE: { - const ObjectSpec *spec = ObjectClass::Get(_selected_object_class)->GetSpec(_selected_object_index); + if (_selected_object_index == -1) break; + + const ObjectSpec *spec = _selected_object_class->GetSpec(_selected_object_index); if (spec == nullptr) break; /* Height of the selection matrix. @@ -254,10 +359,9 @@ public: } case WID_BO_SELECT_IMAGE: { - ObjectClass *objclass = ObjectClass::Get(_selected_object_class); - int obj_index = objclass->GetIndexFromUI(GB(widget, 16, 16)); + int obj_index = _selected_object_class->GetIndexFromUI(GB(widget, 16, 16)); if (obj_index < 0) break; - const ObjectSpec *spec = objclass->GetSpec(obj_index); + const ObjectSpec *spec = _selected_object_class->GetSpec(obj_index); if (spec == nullptr) break; if (!spec->IsAvailable()) { @@ -282,7 +386,7 @@ public: } case WID_BO_INFO: { - const ObjectSpec *spec = ObjectClass::Get(_selected_object_class)->GetSpec(_selected_object_index); + const ObjectSpec *spec = _selected_object_class->GetSpec(_selected_object_index); if (spec == nullptr) break; /* Get the extra message for the GUI */ @@ -315,12 +419,12 @@ public: /** * Select the specified object class. - * @param object_class_index Object class index to select. + * @param object_class Object class select. */ - void SelectOtherClass(ObjectClassID object_class_index) + void SelectOtherClass(const ObjectClass *object_class) { - _selected_object_class = object_class_index; - this->GetWidget(WID_BO_SELECT_MATRIX)->SetCount(ObjectClass::Get(_selected_object_class)->GetUISpecCount()); + _selected_object_class = object_class; + this->GetWidget(WID_BO_SELECT_MATRIX)->SetCount(_selected_object_class->GetUISpecCount()); } /** @@ -331,7 +435,7 @@ public: { _selected_object_index = object_index; if (_selected_object_index != -1) { - const ObjectSpec *spec = ObjectClass::Get(_selected_object_class)->GetSpec(_selected_object_index); + const ObjectSpec *spec = _selected_object_class->GetSpec(_selected_object_index); _selected_object_view = std::min(_selected_object_view, spec->views - 1); this->ReInit(); } else { @@ -355,7 +459,7 @@ public: if (_selected_object_index == -1) { SetTileSelectSize(1, 1); } else { - const ObjectSpec *spec = ObjectClass::Get(_selected_object_class)->GetSpec(_selected_object_index); + const ObjectSpec *spec = _selected_object_class->GetSpec(_selected_object_index); int w = GB(spec->size, HasBit(_selected_object_view, 0) ? 4 : 0, 4); int h = GB(spec->size, HasBit(_selected_object_view, 0) ? 0 : 4, 4); SetTileSelectSize(w, h); @@ -364,11 +468,11 @@ public: /** * Update buttons to show the selection to the user. - * @param sel_class The class of the selected object. + * @param object_class The class of the selected object. * @param sel_index Index of the object to select, or \c -1 . * @param sel_view View of the object to select. */ - void UpdateButtons(ObjectClassID sel_class, int sel_index, uint sel_view) + void UpdateButtons(const ObjectClass *object_class, int sel_index, uint sel_view) { int view_number, object_number; if (sel_index == -1) { @@ -376,7 +480,7 @@ public: object_number = -1; } else { view_number = sel_view; - object_number = ObjectClass::Get(sel_class)->GetUIFromIndex(sel_index); + object_number = object_class->GetUIFromIndex(sel_index); } this->GetWidget(WID_BO_OBJECT_MATRIX)->SetClicked(view_number); @@ -385,6 +489,13 @@ public: this->SetDirty(); } + void OnInvalidateData(int data = 0, bool gui_scope = true) override + { + if (!gui_scope) return; + + this->BuildObjectClassesAvailable(); + } + void OnResize() override { this->vscroll->SetCapacityFromWidget(this, WID_BO_CLASS_LIST); @@ -395,17 +506,16 @@ public: switch (GB(widget, 0, 16)) { case WID_BO_CLASS_LIST: { int num_clicked = this->vscroll->GetPosition() + (pt.y - this->nested_array[widget]->pos_y) / this->line_height; - if (num_clicked >= (int)ObjectClass::GetUIClassCount()) break; + if (num_clicked >= (int)this->object_classes.size()) break; - this->SelectOtherClass(ObjectClass::GetUIClass(num_clicked)); + this->SelectOtherClass(this->object_classes[num_clicked]); this->SelectFirstAvailableObject(false); break; } case WID_BO_SELECT_IMAGE: { - ObjectClass *objclass = ObjectClass::Get(_selected_object_class); - int num_clicked = objclass->GetIndexFromUI(GB(widget, 16, 16)); - if (num_clicked >= 0 && objclass->GetSpec(num_clicked)->IsAvailable()) this->SelectOtherObject(num_clicked); + int num_clicked = _selected_object_class->GetIndexFromUI(GB(widget, 16, 16)); + if (num_clicked >= 0 && _selected_object_class->GetSpec(num_clicked)->IsAvailable()) this->SelectOtherObject(num_clicked); break; } @@ -420,7 +530,7 @@ public: void OnPlaceObject(Point pt, TileIndex tile) override { - const ObjectSpec *spec = ObjectClass::Get(_selected_object_class)->GetSpec(_selected_object_index); + const ObjectSpec *spec = _selected_object_class->GetSpec(_selected_object_index); if (spec == nullptr) return; if (_settings_game.construction.build_object_area_permitted && spec->size == 0x11) { VpStartPlaceSizing(tile, VPM_X_AND_Y, DDSP_BUILD_OBJECT); @@ -453,13 +563,20 @@ public: if (TileY(end_tile) == MapMaxY()) end_tile += TileDiffXY(0, -1); } DoCommandP(end_tile, start_tile, - (ObjectClass::Get(_selected_object_class)->GetSpec(_selected_object_index)->Index() << 3) | (_selected_object_view << 1) | (_ctrl_pressed ? 1 : 0), + (_selected_object_class->GetSpec(_selected_object_index)->Index() << 3) | (_selected_object_view << 1) | (_ctrl_pressed ? 1 : 0), CMD_BUILD_OBJECT_AREA | CMD_MSG(STR_ERROR_CAN_T_BUILD_OBJECT), CcTerraform); break; } } } + void OnEditboxChanged(int wid) override + { + string_filter.SetFilterTerm(this->filter_editbox.text.buf); + this->object_classes.SetFilterState(!string_filter.IsEmpty()); + this->object_classes.ForceRebuild(); + this->InvalidateData(); + } /** * Select the first available object. @@ -469,9 +586,8 @@ public: void SelectFirstAvailableObject(bool change_class) { /* First try to select an object in the selected class. */ - ObjectClass *sel_objclass = ObjectClass::Get(_selected_object_class); - for (uint i = 0; i < sel_objclass->GetSpecCount(); i++) { - const ObjectSpec *spec = sel_objclass->GetSpec(i); + for (uint i = 0; i < _selected_object_class->GetSpecCount(); i++) { + const ObjectSpec *spec = _selected_object_class->GetSpec(i); if (spec->IsAvailable()) { this->SelectOtherObject(i); return; @@ -480,12 +596,11 @@ public: if (change_class) { /* If that fails, select the first available object * from a random class. */ - for (ObjectClassID j = OBJECT_CLASS_BEGIN; j < OBJECT_CLASS_MAX; j++) { - ObjectClass *objclass = ObjectClass::Get(j); + for (auto objclass : this->object_classes) { for (uint i = 0; i < objclass->GetSpecCount(); i++) { const ObjectSpec *spec = objclass->GetSpec(i); if (spec->IsAvailable()) { - this->SelectOtherClass(j); + this->SelectOtherClass(objclass); this->SelectOtherObject(i); return; } @@ -493,11 +608,11 @@ public: } } /* If all objects are unavailable, select nothing... */ - if (ObjectClass::Get(_selected_object_class)->GetUISpecCount() == 0) { + if (_selected_object_class->GetUISpecCount() == 0) { /* ... but make sure that the class is not empty. */ - for (ObjectClassID j = OBJECT_CLASS_BEGIN; j < OBJECT_CLASS_MAX; j++) { - if (ObjectClass::Get(j)->GetUISpecCount() > 0) { - this->SelectOtherClass(j); + for (auto objclass : this->object_classes) { + if (objclass->GetUISpecCount() > 0) { + this->SelectOtherClass(objclass); break; } } @@ -506,6 +621,17 @@ public: } }; +Listing BuildObjectWindow::last_sorting = { false, 0 }; +Filtering BuildObjectWindow::last_filtering = { false, 0 }; + +BuildObjectWindow::GUIObjectClassList::SortFunction* const BuildObjectWindow::sorter_funcs[] = { + &NameSorter, +}; + +BuildObjectWindow::GUIObjectClassList::FilterFunction* const BuildObjectWindow::filter_funcs[] = { + &TagNameFilter, +}; + static const NWidgetPart _nested_build_object_widgets[] = { NWidget(NWID_HORIZONTAL), NWidget(WWT_CLOSEBOX, COLOUR_DARK_GREEN), @@ -515,6 +641,11 @@ static const NWidgetPart _nested_build_object_widgets[] = { NWidget(WWT_PANEL, COLOUR_DARK_GREEN), NWidget(NWID_HORIZONTAL), SetPadding(2, 0, 0, 0), NWidget(NWID_VERTICAL), + NWidget(NWID_HORIZONTAL), SetPadding(0, 5, 2, 5), + NWidget(WWT_TEXT, COLOUR_DARK_GREEN), SetFill(0, 1), SetDataTip(STR_LIST_FILTER_TITLE, STR_NULL), + NWidget(WWT_EDITBOX, COLOUR_GREY, WID_BO_FILTER), SetFill(1, 0), SetResize(1, 0), + SetDataTip(STR_LIST_FILTER_OSKTITLE, STR_LIST_FILTER_TOOLTIP), + EndContainer(), NWidget(NWID_HORIZONTAL), SetPadding(0, 5, 2, 5), NWidget(WWT_MATRIX, COLOUR_GREY, WID_BO_CLASS_LIST), SetFill(1, 0), SetMatrixDataTip(1, 0, STR_OBJECT_BUILD_CLASS_TOOLTIP), SetScrollbar(WID_BO_SCROLLBAR), NWidget(NWID_VSCROLLBAR, COLOUR_GREY, WID_BO_SCROLLBAR), @@ -567,5 +698,5 @@ void ShowBuildObjectPicker() /** Reset all data of the object GUI. */ void InitializeObjectGui() { - _selected_object_class = (ObjectClassID)0; + _selected_object_class = nullptr; } diff --git a/src/openttd.cpp b/src/openttd.cpp index 5d1e7ef0fa..0b2eed731c 100644 --- a/src/openttd.cpp +++ b/src/openttd.cpp @@ -925,6 +925,7 @@ int openttd_main(int argc, char *argv[]) /* Initialize the zoom level of the screen to normal */ _screen.zoom = ZOOM_LVL_NORMAL; + UpdateGUIZoom(); NetworkStartUp(); // initialize network-core @@ -1931,7 +1932,6 @@ void GameLoop() if (_game_mode == GM_BOOTSTRAP) { /* Check for UDP stuff */ if (_network_available) NetworkBackgroundLoop(); - InputLoop(); return; } @@ -1964,8 +1964,6 @@ void GameLoop() StateGameLoop(); } - InputLoop(); - SoundDriver::GetInstance()->MainLoop(); MusicLoop(); } diff --git a/src/order_gui.cpp b/src/order_gui.cpp index bb337a65cb..06f102f2b1 100644 --- a/src/order_gui.cpp +++ b/src/order_gui.cpp @@ -1360,17 +1360,18 @@ private: /** * Handle the click on the full load button. - * @param load_type the way to load. + * @param load_type Load flag to apply. If matches existing load type, toggles to default of 'load if possible'. + * @param toggle If we toggle or not (used for hotkey behavior) */ - void OrderClick_FullLoad(int load_type) + void OrderClick_FullLoad(OrderLoadFlags load_type, bool toggle = false) { VehicleOrderID sel_ord = this->OrderGetSel(); const Order *order = this->vehicle->GetOrder(sel_ord); - if (order == nullptr || (order->GetLoadType() == load_type && load_type != OLFB_CARGO_TYPE_LOAD)) return; + if (order == nullptr) return; - if (load_type < 0) { - load_type = order->GetLoadType() == OLF_LOAD_IF_POSSIBLE ? OLF_FULL_LOAD_ANY : OLF_LOAD_IF_POSSIBLE; + if (toggle && order->GetLoadType() == load_type) { + load_type = OLF_LOAD_IF_POSSIBLE; // reset to 'default' } if (order->GetLoadType() != load_type) { this->ModifyOrder(sel_ord, MOF_LOAD | (load_type << 4)); @@ -1379,14 +1380,6 @@ private: if (load_type == OLFB_CARGO_TYPE_LOAD) ShowCargoTypeOrdersWindow(this->vehicle, this, sel_ord, CTOWV_LOAD); } - /** - * Handle the 'no loading' hotkey - */ - void OrderHotkey_NoLoad() - { - this->OrderClick_FullLoad(OLFB_NO_LOAD); - } - /** * Handle the click on the service. */ @@ -1432,17 +1425,20 @@ private: /** * Handle the click on the unload button. + * @param unload_type Unload flag to apply. If matches existing unload type, toggles to default of 'unload if possible'. + * @param toggle If we toggle or not (used for hotkey behavior) */ - void OrderClick_Unload(int unload_type) + void OrderClick_Unload(OrderUnloadFlags unload_type, bool toggle = false) { VehicleOrderID sel_ord = this->OrderGetSel(); const Order *order = this->vehicle->GetOrder(sel_ord); - if (order == nullptr || (order->GetUnloadType() == unload_type && unload_type != OUFB_CARGO_TYPE_UNLOAD)) return; + if (order == nullptr) return; - if (unload_type < 0) { - unload_type = order->GetUnloadType() == OUF_UNLOAD_IF_POSSIBLE ? OUFB_UNLOAD : OUF_UNLOAD_IF_POSSIBLE; + if (toggle && order->GetUnloadType() == unload_type) { + unload_type = OUF_UNLOAD_IF_POSSIBLE; } + if (order->GetUnloadType() == unload_type && unload_type != OUFB_CARGO_TYPE_UNLOAD) return; // If we still match, do nothing if (order->GetUnloadType() != unload_type) { this->ModifyOrder(sel_ord, MOF_UNLOAD | (unload_type << 4)); @@ -1452,27 +1448,11 @@ private: /* Transfer orders with leave empty as default */ this->ModifyOrder(sel_ord, MOF_LOAD | (OLFB_NO_LOAD << 4), false); this->SetWidgetDirty(WID_O_FULL_LOAD); - } else if(unload_type == OUFB_CARGO_TYPE_UNLOAD) { + } else if (unload_type == OUFB_CARGO_TYPE_UNLOAD) { ShowCargoTypeOrdersWindow(this->vehicle, this, sel_ord, CTOWV_UNLOAD); } } - /** - * Handle the transfer hotkey - */ - void OrderHotkey_Transfer() - { - this->OrderClick_Unload(OUFB_TRANSFER); - } - - /** - * Handle the 'no unload' hotkey - */ - void OrderHotkey_NoUnload() - { - this->OrderClick_Unload(OUFB_NO_UNLOAD); - } - /** * Handle the click on the nonstop button. * @param non_stop what non-stop type to use; -1 to use the 'next' one. @@ -2366,7 +2346,7 @@ public: case WID_O_FULL_LOAD: if (this->GetWidget(widget)->ButtonHit(pt)) { - this->OrderClick_FullLoad(-1); + this->OrderClick_FullLoad(OLF_FULL_LOAD_ANY, true); } else { ShowDropDownMenu(this, _order_full_load_drowdown, this->vehicle->GetOrder(this->OrderGetSel())->GetLoadType(), WID_O_FULL_LOAD, 0, 0xE2 /* 1110 0010 */, 0, DDSF_LOST_FOCUS); } @@ -2374,7 +2354,7 @@ public: case WID_O_UNLOAD: if (this->GetWidget(widget)->ButtonHit(pt)) { - this->OrderClick_Unload(-1); + this->OrderClick_Unload(OUFB_UNLOAD, true); } else { ShowDropDownMenu(this, _order_unload_drowdown, this->vehicle->GetOrder(this->OrderGetSel())->GetUnloadType(), WID_O_UNLOAD, 0, 0xE8 /* 1110 1000 */, 0, DDSF_LOST_FOCUS); } @@ -2603,11 +2583,11 @@ public: break; case WID_O_FULL_LOAD: - this->OrderClick_FullLoad(index); + this->OrderClick_FullLoad((OrderLoadFlags)index); break; case WID_O_UNLOAD: - this->OrderClick_Unload(index); + this->OrderClick_Unload((OrderUnloadFlags)index); break; case WID_O_GOTO: @@ -2709,17 +2689,17 @@ public: if (this->vehicle->owner != _local_company) return ES_NOT_HANDLED; switch (hotkey) { - case OHK_SKIP: this->OrderClick_Skip(); break; - case OHK_DELETE: this->OrderClick_Delete(); break; + case OHK_SKIP: this->OrderClick_Skip(); break; + case OHK_DELETE: this->OrderClick_Delete(); break; case OHK_GOTO: this->OrderClick_Goto(OPOS_GOTO); break; - case OHK_NONSTOP: this->OrderClick_Nonstop(-1); break; - case OHK_FULLLOAD: this->OrderClick_FullLoad(-1); break; - case OHK_UNLOAD: this->OrderClick_Unload(-1); break; - case OHK_NEAREST_DEPOT: this->OrderClick_NearestDepot(); break; - case OHK_ALWAYS_SERVICE: this->OrderClick_Service(-1); break; - case OHK_TRANSFER: this->OrderHotkey_Transfer(); break; - case OHK_NO_UNLOAD: this->OrderHotkey_NoUnload(); break; - case OHK_NO_LOAD: this->OrderHotkey_NoLoad(); break; + case OHK_NONSTOP: this->OrderClick_Nonstop(-1); break; + case OHK_FULLLOAD: this->OrderClick_FullLoad(OLF_FULL_LOAD_ANY, true); break; + case OHK_UNLOAD: this->OrderClick_Unload(OUFB_UNLOAD, true); break; + case OHK_NEAREST_DEPOT: this->OrderClick_NearestDepot(); break; + case OHK_ALWAYS_SERVICE: this->OrderClick_Service(-1); break; + case OHK_TRANSFER: this->OrderClick_Unload(OUFB_TRANSFER, true); break; + case OHK_NO_UNLOAD: this->OrderClick_Unload(OUFB_NO_UNLOAD, true); break; + case OHK_NO_LOAD: this->OrderClick_FullLoad(OLFB_NO_LOAD, true); break; default: return ES_NOT_HANDLED; } return ES_HANDLED; diff --git a/src/os/macosx/CMakeLists.txt b/src/os/macosx/CMakeLists.txt index e6b6c237b0..645a057c7d 100644 --- a/src/os/macosx/CMakeLists.txt +++ b/src/os/macosx/CMakeLists.txt @@ -1,10 +1,10 @@ add_files( crashlog_osx.cpp + font_osx.cpp + font_osx.h macos.h macos.mm osx_stdafx.h - splash.cpp - splash.h string_osx.cpp string_osx.h CONDITION APPLE diff --git a/src/os/macosx/font_osx.cpp b/src/os/macosx/font_osx.cpp new file mode 100644 index 0000000000..0a1b9e99ab --- /dev/null +++ b/src/os/macosx/font_osx.cpp @@ -0,0 +1,424 @@ +/* + * This file is part of OpenTTD. + * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. + * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . + */ + +/** @file font_osx.cpp Functions related to font handling on MacOS. */ + +#include "../../stdafx.h" +#include "../../debug.h" +#include "font_osx.h" +#include "../../blitter/factory.hpp" +#include "../../fileio_func.h" +#include "../../fontdetection.h" +#include "../../string_func.h" +#include "../../strings_func.h" +#include "../../zoom_func.h" +#include "macos.h" +#include + +#include "../../table/control_codes.h" + +#include "safeguards.h" + + +#ifdef WITH_FREETYPE + +#include +#include FT_FREETYPE_H + +extern FT_Library _library; + + +FT_Error GetFontByFaceName(const char *font_name, FT_Face *face) +{ + FT_Error err = FT_Err_Cannot_Open_Resource; + + /* Get font reference from name. */ + UInt8 file_path[PATH_MAX]; + OSStatus os_err = -1; + CFAutoRelease name(CFStringCreateWithCString(kCFAllocatorDefault, font_name, kCFStringEncodingUTF8)); + + /* Simply creating the font using CTFontCreateWithNameAndSize will *always* return + * something, no matter the name. As such, we can't use it to check for existence. + * We instead query the list of all font descriptors that match the given name which + * does not do this stupid name fallback. */ + CFAutoRelease name_desc(CTFontDescriptorCreateWithNameAndSize(name.get(), 0.0)); + CFAutoRelease mandatory_attribs(CFSetCreate(kCFAllocatorDefault, const_cast(reinterpret_cast(&kCTFontNameAttribute)), 1, &kCFTypeSetCallBacks)); + CFAutoRelease descs(CTFontDescriptorCreateMatchingFontDescriptors(name_desc.get(), mandatory_attribs.get())); + + /* Loop over all matches until we can get a path for one of them. */ + for (CFIndex i = 0; descs.get() != nullptr && i < CFArrayGetCount(descs.get()) && os_err != noErr; i++) { + CFAutoRelease font(CTFontCreateWithFontDescriptor((CTFontDescriptorRef)CFArrayGetValueAtIndex(descs.get(), i), 0.0, nullptr)); + CFAutoRelease fontURL((CFURLRef)CTFontCopyAttribute(font.get(), kCTFontURLAttribute)); + if (CFURLGetFileSystemRepresentation(fontURL.get(), true, file_path, lengthof(file_path))) os_err = noErr; + } + + if (os_err == noErr) { + DEBUG(freetype, 3, "Font path for %s: %s", font_name, file_path); + err = FT_New_Face(_library, (const char *)file_path, 0, face); + } + + return err; +} + +#endif /* WITH_FREETYPE */ + + +bool SetFallbackFont(FreeTypeSettings *settings, const char *language_isocode, int winlangid, MissingGlyphSearcher *callback) +{ + /* Determine fallback font using CoreText. This uses the language isocode + * to find a suitable font. CoreText is available from 10.5 onwards. */ + char lang[16]; + if (strcmp(language_isocode, "zh_TW") == 0) { + /* Traditional Chinese */ + strecpy(lang, "zh-Hant", lastof(lang)); + } else if (strcmp(language_isocode, "zh_CN") == 0) { + /* Simplified Chinese */ + strecpy(lang, "zh-Hans", lastof(lang)); + } else { + /* Just copy the first part of the isocode. */ + strecpy(lang, language_isocode, lastof(lang)); + char *sep = strchr(lang, '_'); + if (sep != nullptr) *sep = '\0'; + } + + /* Create a font descriptor matching the wanted language and latin (english) glyphs. + * Can't use CFAutoRelease here for everything due to the way the dictionary has to be created. */ + CFStringRef lang_codes[2]; + lang_codes[0] = CFStringCreateWithCString(kCFAllocatorDefault, lang, kCFStringEncodingUTF8); + lang_codes[1] = CFSTR("en"); + CFArrayRef lang_arr = CFArrayCreate(kCFAllocatorDefault, (const void **)lang_codes, lengthof(lang_codes), &kCFTypeArrayCallBacks); + CFAutoRelease lang_attribs(CFDictionaryCreate(kCFAllocatorDefault, const_cast(reinterpret_cast(&kCTFontLanguagesAttribute)), (const void **)&lang_arr, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)); + CFAutoRelease lang_desc(CTFontDescriptorCreateWithAttributes(lang_attribs.get())); + CFRelease(lang_arr); + CFRelease(lang_codes[0]); + + /* Get array of all font descriptors for the wanted language. */ + CFAutoRelease mandatory_attribs(CFSetCreate(kCFAllocatorDefault, const_cast(reinterpret_cast(&kCTFontLanguagesAttribute)), 1, &kCFTypeSetCallBacks)); + CFAutoRelease descs(CTFontDescriptorCreateMatchingFontDescriptors(lang_desc.get(), mandatory_attribs.get())); + + bool result = false; + for (int tries = 0; tries < 2; tries++) { + for (CFIndex i = 0; descs.get() != nullptr && i < CFArrayGetCount(descs.get()); i++) { + CTFontDescriptorRef font = (CTFontDescriptorRef)CFArrayGetValueAtIndex(descs.get(), i); + + /* Get font traits. */ + CFAutoRelease traits((CFDictionaryRef)CTFontDescriptorCopyAttribute(font, kCTFontTraitsAttribute)); + CTFontSymbolicTraits symbolic_traits; + CFNumberGetValue((CFNumberRef)CFDictionaryGetValue(traits.get(), kCTFontSymbolicTrait), kCFNumberIntType, &symbolic_traits); + + /* Skip symbol fonts and vertical fonts. */ + if ((symbolic_traits & kCTFontClassMaskTrait) == (CTFontStylisticClass)kCTFontSymbolicClass || (symbolic_traits & kCTFontVerticalTrait)) continue; + /* Skip bold fonts (especially Arial Bold, which looks worse than regular Arial). */ + if (symbolic_traits & kCTFontBoldTrait) continue; + /* Select monospaced fonts if asked for. */ + if (((symbolic_traits & kCTFontMonoSpaceTrait) == kCTFontMonoSpaceTrait) != callback->Monospace()) continue; + + /* Get font name. */ + char name[128]; + CFAutoRelease font_name((CFStringRef)CTFontDescriptorCopyAttribute(font, kCTFontDisplayNameAttribute)); + CFStringGetCString(font_name.get(), name, lengthof(name), kCFStringEncodingUTF8); + + /* Serif fonts usually look worse on-screen with only small + * font sizes. As such, we try for a sans-serif font first. + * If we can't find one in the first try, try all fonts. */ + if (tries == 0 && (symbolic_traits & kCTFontClassMaskTrait) != (CTFontStylisticClass)kCTFontSansSerifClass) continue; + + /* There are some special fonts starting with an '.' and the last + * resort font that aren't usable. Skip them. */ + if (name[0] == '.' || strncmp(name, "LastResort", 10) == 0) continue; + + /* Save result. */ + callback->SetFontNames(settings, name); + if (!callback->FindMissingGlyphs()) { + DEBUG(freetype, 2, "CT-Font for %s: %s", language_isocode, name); + result = true; + break; + } + } + } + + if (!result) { + /* For some OS versions, the font 'Arial Unicode MS' does not report all languages it + * supports. If we didn't find any other font, just try it, maybe we get lucky. */ + callback->SetFontNames(settings, "Arial Unicode MS"); + result = !callback->FindMissingGlyphs(); + } + + callback->FindMissingGlyphs(); + return result; +} + + +CoreTextFontCache::CoreTextFontCache(FontSize fs, CFAutoRelease &&font, int pixels) : TrueTypeFontCache(fs, pixels), font_desc(std::move(font)) +{ + this->SetFontSize(pixels); +} + +/** + * Reset cached glyphs. + */ +void CoreTextFontCache::ClearFontCache() +{ + /* GUI scaling might have changed, determine font size anew if it was automatically selected. */ + if (this->font) this->SetFontSize(this->req_size); + + this->TrueTypeFontCache::ClearFontCache(); +} + +void CoreTextFontCache::SetFontSize(int pixels) +{ + if (pixels == 0) { + /* Try to determine a good height based on the height recommended by the font. */ + int scaled_height = ScaleFontTrad(this->GetDefaultFontHeight(this->fs)); + pixels = scaled_height; + + CFAutoRelease font(CTFontCreateWithFontDescriptor(this->font_desc.get(), 0.0f, nullptr)); + if (font) { + float min_size = 0.0f; + + /* The 'head' TrueType table contains information about the + * 'smallest readable size in pixels'. Try to read it, if + * that doesn't work, we use the default OS font size instead. + * + * Reference: https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6head.html */ + CFAutoRelease data(CTFontCopyTable(font.get(), kCTFontTableHead, kCTFontTableOptionNoOptions)); + if (data) { + uint16_t lowestRecPPEM; // At offset 46 of the 'head' TrueType table. + CFDataGetBytes(data.get(), CFRangeMake(46, sizeof(lowestRecPPEM)), (UInt8 *)&lowestRecPPEM); + min_size = CFSwapInt16BigToHost(lowestRecPPEM); // TrueType data is always big-endian. + } else { + CFAutoRelease size((CFNumberRef)CTFontCopyAttribute(font.get(), kCTFontSizeAttribute)); + CFNumberGetValue(size.get(), kCFNumberFloatType, &min_size); + } + + /* Font height is minimum height plus the difference between the default + * height for this font size and the small size. */ + int diff = scaled_height - ScaleFontTrad(this->GetDefaultFontHeight(FS_SMALL)); + pixels = Clamp(std::min(min_size, MAX_FONT_MIN_REC_SIZE) + diff, scaled_height, MAX_FONT_SIZE); + } + } else { + pixels = ScaleFontTrad(pixels); + } + this->used_size = pixels; + + this->font.reset(CTFontCreateWithFontDescriptor(this->font_desc.get(), pixels, nullptr)); + + /* Query the font metrics we needed. We generally round all values up to + * make sure we don't inadvertently cut off a row or column of pixels, + * except when determining glyph to glyph advances. */ + this->units_per_em = CTFontGetUnitsPerEm(this->font.get()); + this->ascender = (int)std::ceil(CTFontGetAscent(this->font.get())); + this->descender = -(int)std::ceil(CTFontGetDescent(this->font.get())); + this->height = this->ascender - this->descender; + + /* Get real font name. */ + char name[128]; + CFAutoRelease font_name((CFStringRef)CTFontCopyAttribute(this->font.get(), kCTFontDisplayNameAttribute)); + CFStringGetCString(font_name.get(), name, lengthof(name), kCFStringEncodingUTF8); + this->font_name = name; + + DEBUG(freetype, 2, "Loaded font '%s' with size %d", this->font_name.c_str(), pixels); +} + +GlyphID CoreTextFontCache::MapCharToGlyph(WChar key) +{ + assert(IsPrintable(key)); + + if (key >= SCC_SPRITE_START && key <= SCC_SPRITE_END) { + return this->parent->MapCharToGlyph(key); + } + + /* Convert characters outside of the Basic Multilingual Plane into surrogate pairs. */ + UniChar chars[2]; + if (key >= 0x010000U) { + chars[0] = (UniChar)(((key - 0x010000U) >> 10) + 0xD800); + chars[1] = (UniChar)(((key - 0x010000U) & 0x3FF) + 0xDC00); + } else { + chars[0] = (UniChar)(key & 0xFFFF); + } + + CGGlyph glyph[2] = {0, 0}; + if (CTFontGetGlyphsForCharacters(this->font.get(), chars, glyph, key >= 0x010000U ? 2 : 1)) { + return glyph[0]; + } + + return 0; +} + +const void *CoreTextFontCache::InternalGetFontTable(uint32 tag, size_t &length) +{ + CFAutoRelease data(CTFontCopyTable(this->font.get(), (CTFontTableTag)tag, kCTFontTableOptionNoOptions)); + if (!data) return nullptr; + + length = CFDataGetLength(data.get()); + auto buf = MallocT(length); + + CFDataGetBytes(data.get(), CFRangeMake(0, (CFIndex)length), buf); + return buf; +} + +const Sprite *CoreTextFontCache::InternalGetGlyph(GlyphID key, bool use_aa) +{ + /* Get glyph size. */ + CGGlyph glyph = (CGGlyph)key; + CGRect bounds = CGRectNull; + if (MacOSVersionIsAtLeast(10, 8, 0)) { + bounds = CTFontGetOpticalBoundsForGlyphs(this->font.get(), &glyph, nullptr, 1, 0); + } else { + bounds = CTFontGetBoundingRectsForGlyphs(this->font.get(), kCTFontOrientationDefault, &glyph, nullptr, 1); + } + if (CGRectIsNull(bounds)) usererror("Unable to render font glyph"); + + uint bb_width = (uint)std::ceil(bounds.size.width) + 1; // Sometimes the glyph bounds are too tight and cut of the last pixel after rounding. + uint bb_height = (uint)std::ceil(bounds.size.height); + + /* Add 1 pixel for the shadow on the medium font. Our sprite must be at least 1x1 pixel. */ + uint width = std::max(1U, bb_width + (this->fs == FS_NORMAL ? 1 : 0)); + uint height = std::max(1U, bb_height + (this->fs == FS_NORMAL ? 1 : 0)); + + /* Limit glyph size to prevent overflows later on. */ + if (width > MAX_GLYPH_DIM || height > MAX_GLYPH_DIM) usererror("Font glyph is too large"); + + SpriteLoader::Sprite sprite; + sprite.AllocateData(ZOOM_LVL_NORMAL, width * height); + sprite.type = ST_FONT; + sprite.width = width; + sprite.height = height; + sprite.x_offs = (int16)std::round(CGRectGetMinX(bounds)); + sprite.y_offs = this->ascender - (int16)std::ceil(CGRectGetMaxY(bounds)); + + if (bounds.size.width > 0) { + /* Glyph is not a white-space glyph. Render it to a bitmap context. */ + + /* We only need the alpha channel, as we apply our own colour constants to the sprite. */ + int pitch = Align(bb_width, 16); + byte *bmp = CallocT(bb_height * pitch); + CFAutoRelease context(CGBitmapContextCreate(bmp, bb_width, bb_height, 8, pitch, nullptr, kCGImageAlphaOnly)); + /* Set antialias according to requirements. */ + CGContextSetAllowsAntialiasing(context.get(), use_aa); + CGContextSetAllowsFontSubpixelPositioning(context.get(), use_aa); + CGContextSetAllowsFontSubpixelQuantization(context.get(), !use_aa); + CGContextSetShouldSmoothFonts(context.get(), false); + + float offset = 0.5f; // CoreText uses 0.5 as pixel centers. We want pixel alignment. + CGPoint pos{offset - bounds.origin.x, offset - bounds.origin.y}; + CTFontDrawGlyphs(this->font.get(), &glyph, &pos, 1, context.get()); + + /* Draw shadow for medium size. */ + if (this->fs == FS_NORMAL && !use_aa) { + for (uint y = 0; y < bb_height; y++) { + for (uint x = 0; x < bb_width; x++) { + if (bmp[y * pitch + x] > 0) { + sprite.data[1 + x + (1 + y) * sprite.width].m = SHADOW_COLOUR; + sprite.data[1 + x + (1 + y) * sprite.width].a = use_aa ? bmp[x + y * pitch] : 0xFF; + } + } + } + } + + /* Extract pixel data. */ + for (uint y = 0; y < bb_height; y++) { + for (uint x = 0; x < bb_width; x++) { + if (bmp[y * pitch + x] > 0) { + sprite.data[x + y * sprite.width].m = FACE_COLOUR; + sprite.data[x + y * sprite.width].a = use_aa ? bmp[x + y * pitch] : 0xFF; + } + } + } + } + + GlyphEntry new_glyph; + new_glyph.sprite = BlitterFactory::GetCurrentBlitter()->Encode(&sprite, AllocateFont); + new_glyph.width = (byte)std::round(CTFontGetAdvancesForGlyphs(this->font.get(), kCTFontOrientationDefault, &glyph, nullptr, 1)); + this->SetGlyphPtr(key, &new_glyph); + + return new_glyph.sprite; +} + +/** + * Loads the TrueType font. + * If a CoreText font description is present, e.g. from the automatic font + * fallback search, use it. Otherwise, try to resolve it by font name. + * @param fs The font size to load. + */ +void LoadCoreTextFont(FontSize fs) +{ + static const char *SIZE_TO_NAME[] = { "medium", "small", "large", "mono" }; + + FreeTypeSubSetting *settings = nullptr; + switch (fs) { + default: NOT_REACHED(); + case FS_SMALL: settings = &_freetype.small; break; + case FS_NORMAL: settings = &_freetype.medium; break; + case FS_LARGE: settings = &_freetype.large; break; + case FS_MONO: settings = &_freetype.mono; break; + } + + if (StrEmpty(settings->font)) return; + + CFAutoRelease font_ref; + + if (settings->os_handle != nullptr) { + font_ref.reset(static_cast(const_cast(settings->os_handle))); + CFRetain(font_ref.get()); // Increase ref count to match a later release. + } + + if (!font_ref && MacOSVersionIsAtLeast(10, 6, 0)) { + /* Might be a font file name, try load it. Direct font loading is + * only supported starting on OSX 10.6. */ + CFAutoRelease path; + + /* See if this is an absolute path. */ + if (FileExists(settings->font)) { + path.reset(CFStringCreateWithCString(kCFAllocatorDefault, settings->font, kCFStringEncodingUTF8)); + } else { + /* Scan the search-paths to see if it can be found. */ + std::string full_font = FioFindFullPath(BASE_DIR, settings->font); + if (!full_font.empty()) { + path.reset(CFStringCreateWithCString(kCFAllocatorDefault, full_font.c_str(), kCFStringEncodingUTF8)); + } + } + + if (path) { + /* Try getting a font descriptor to see if the system can use it. */ + CFAutoRelease url(CFURLCreateWithFileSystemPath(kCFAllocatorDefault, path.get(), kCFURLPOSIXPathStyle, false)); + CFAutoRelease descs(CTFontManagerCreateFontDescriptorsFromURL(url.get())); + + if (descs && CFArrayGetCount(descs.get()) > 0) { + font_ref.reset((CTFontDescriptorRef)CFArrayGetValueAtIndex(descs.get(), 0)); + CFRetain(font_ref.get()); + } else { + ShowInfoF("Unable to load file '%s' for %s font, using default OS font selection instead", settings->font, SIZE_TO_NAME[fs]); + } + } + } + + if (!font_ref) { + CFAutoRelease name(CFStringCreateWithCString(kCFAllocatorDefault, settings->font, kCFStringEncodingUTF8)); + + /* Simply creating the font using CTFontCreateWithNameAndSize will *always* return + * something, no matter the name. As such, we can't use it to check for existence. + * We instead query the list of all font descriptors that match the given name which + * does not do this stupid name fallback. */ + CFAutoRelease name_desc(CTFontDescriptorCreateWithNameAndSize(name.get(), 0.0)); + CFAutoRelease mandatory_attribs(CFSetCreate(kCFAllocatorDefault, const_cast(reinterpret_cast(&kCTFontNameAttribute)), 1, &kCFTypeSetCallBacks)); + CFAutoRelease descs(CTFontDescriptorCreateMatchingFontDescriptors(name_desc.get(), mandatory_attribs.get())); + + /* Assume the first result is the one we want. */ + if (descs && CFArrayGetCount(descs.get()) > 0) { + font_ref.reset((CTFontDescriptorRef)CFArrayGetValueAtIndex(descs.get(), 0)); + CFRetain(font_ref.get()); + } + } + + if (!font_ref) { + ShowInfoF("Unable to use '%s' for %s font, using sprite font instead", settings->font, SIZE_TO_NAME[fs]); + return; + } + + new CoreTextFontCache(fs, std::move(font_ref), settings->size); +} diff --git a/src/os/macosx/font_osx.h b/src/os/macosx/font_osx.h new file mode 100644 index 0000000000..bdfd7316d0 --- /dev/null +++ b/src/os/macosx/font_osx.h @@ -0,0 +1,40 @@ +/* + * This file is part of OpenTTD. + * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. + * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . + */ + +/** @file font_osx.h Functions related to font handling on MacOS. */ + +#ifndef FONT_OSX_H +#define FONT_OSX_H + +#include "../../fontcache_internal.h" +#include "os/macosx/macos.h" + +#include + +class CoreTextFontCache : public TrueTypeFontCache { + CFAutoRelease font_desc; ///< Font descriptor exlcuding font size. + CFAutoRelease font; ///< CoreText font handle. + + std::string font_name; ///< Cached font name. + + void SetFontSize(int pixels); + const Sprite *InternalGetGlyph(GlyphID key, bool use_aa) override; + const void *InternalGetFontTable(uint32 tag, size_t &length) override; +public: + CoreTextFontCache(FontSize fs, CFAutoRelease &&font, int pixels); + ~CoreTextFontCache() {} + + void ClearFontCache() override; + GlyphID MapCharToGlyph(WChar key) override; + const char *GetFontName() override { return font_name.c_str(); } + bool IsBuiltInFont() override { return false; } + const void *GetOSHandle() override { return font.get(); } +}; + +void LoadCoreTextFont(FontSize fs); + +#endif /* FONT_OSX_H */ diff --git a/src/os/macosx/splash.cpp b/src/os/macosx/splash.cpp deleted file mode 100644 index ac2f86425b..0000000000 --- a/src/os/macosx/splash.cpp +++ /dev/null @@ -1,185 +0,0 @@ -/* - * This file is part of OpenTTD. - * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. - * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . - */ - -/** @file splash.cpp Splash screen support for OSX. */ - -#include "../../stdafx.h" -#include "../../openttd.h" -#include "../../debug.h" -#include "../../gfx_func.h" -#include "../../fileio_func.h" -#include "../../blitter/factory.hpp" -#include "../../core/mem_func.hpp" - -#include "splash.h" - -#ifdef WITH_PNG - -#include - -#include "../../safeguards.h" - -/** - * Handle pnglib error. - * - * @param png_ptr Pointer to png struct. - * @param message Error message text. - */ -static void PNGAPI png_my_error(png_structp png_ptr, png_const_charp message) -{ - DEBUG(misc, 0, "[libpng] error: %s - %s", message, (char *)png_get_error_ptr(png_ptr)); - longjmp(png_jmpbuf(png_ptr), 1); -} - -/** - * Handle warning in pnglib. - * - * @param png_ptr Pointer to png struct. - * @param message Warning message text. - */ -static void PNGAPI png_my_warning(png_structp png_ptr, png_const_charp message) -{ - DEBUG(misc, 1, "[libpng] warning: %s - %s", message, (char *)png_get_error_ptr(png_ptr)); -} - -/** - * Display a splash image shown on startup (WITH_PNG). - */ -void DisplaySplashImage() -{ - FILE *f = FioFOpenFile(SPLASH_IMAGE_FILE, "r", BASESET_DIR); - if (f == nullptr) return; - - png_byte header[8]; - fread(header, sizeof(png_byte), 8, f); - if (png_sig_cmp(header, 0, 8) != 0) { - fclose(f); - return; - } - - png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, (png_voidp) nullptr, png_my_error, png_my_warning); - - if (png_ptr == nullptr) { - fclose(f); - return; - } - - png_infop info_ptr = png_create_info_struct(png_ptr); - if (info_ptr == nullptr) { - png_destroy_read_struct(&png_ptr, (png_infopp)nullptr, (png_infopp)nullptr); - fclose(f); - return; - } - - png_infop end_info = png_create_info_struct(png_ptr); - if (end_info == nullptr) { - png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)nullptr); - fclose(f); - return; - } - - if (setjmp(png_jmpbuf(png_ptr))) { - png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); - fclose(f); - return; - } - - png_init_io(png_ptr, f); - png_set_sig_bytes(png_ptr, 8); - - png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, nullptr); - - uint width = png_get_image_width(png_ptr, info_ptr); - uint height = png_get_image_height(png_ptr, info_ptr); - uint bit_depth = png_get_bit_depth(png_ptr, info_ptr); - uint color_type = png_get_color_type(png_ptr, info_ptr); - - if (color_type != PNG_COLOR_TYPE_PALETTE || bit_depth != 8) { - png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); - fclose(f); - return; - } - - if (!png_get_valid(png_ptr, info_ptr, PNG_INFO_PLTE)) { - png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); - fclose(f); - return; - } - - png_colorp palette; - int num_palette; - png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette); - - png_bytep *row_pointers = png_get_rows(png_ptr, info_ptr); - - if (width > (uint) _screen.width) width = _screen.width; - if (height > (uint) _screen.height) height = _screen.height; - - uint xoff = (_screen.width - width) / 2; - uint yoff = (_screen.height - height) / 2; - - switch (BlitterFactory::GetCurrentBlitter()->GetScreenDepth()) { - case 8: { - uint8 *dst_ptr = (uint8 *)_screen.dst_ptr; - /* Initialize buffer */ - MemSetT(dst_ptr, 0xff, _screen.pitch * _screen.height); - - for (uint y = 0; y < height; y++) { - uint8 *src = row_pointers[y]; - uint8 *dst = dst_ptr + (yoff + y) * _screen.pitch + xoff; - - memcpy(dst, src, width); - } - - for (int i = 0; i < num_palette; i++) { - _cur_palette.palette[i].a = i == 0 ? 0 : 0xff; - _cur_palette.palette[i].r = palette[i].red; - _cur_palette.palette[i].g = palette[i].green; - _cur_palette.palette[i].b = palette[i].blue; - } - - _cur_palette.palette[0xff].a = 0xff; - _cur_palette.palette[0xff].r = 0; - _cur_palette.palette[0xff].g = 0; - _cur_palette.palette[0xff].b = 0; - - _cur_palette.first_dirty = 0; - _cur_palette.count_dirty = 256; - break; - } - case 32: { - uint32 *dst_ptr = (uint32 *)_screen.dst_ptr; - /* Initialize buffer */ - MemSetT(dst_ptr, 0, _screen.pitch * _screen.height); - - for (uint y = 0; y < height; y++) { - uint8 *src = row_pointers[y]; - uint32 *dst = dst_ptr + (yoff + y) * _screen.pitch + xoff; - - for (uint x = 0; x < width; x++) { - dst[x] = palette[src[x]].blue | (palette[src[x]].green << 8) | (palette[src[x]].red << 16) | 0xff000000; - } - } - break; - } - } - - png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); - fclose(f); - return; -} - - - -#else /* WITH_PNG */ - -/** - * Empty 'Display a splash image' routine (WITHOUT_PNG). - */ -void DisplaySplashImage() {} - -#endif /* WITH_PNG */ diff --git a/src/os/macosx/splash.h b/src/os/macosx/splash.h deleted file mode 100644 index 8ddb638578..0000000000 --- a/src/os/macosx/splash.h +++ /dev/null @@ -1,17 +0,0 @@ -/* - * This file is part of OpenTTD. - * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. - * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . - */ - -/** @file splash.h Functions to support splash screens for OSX. */ - -#ifndef SPLASH_H -#define SPLASH_H - -#define SPLASH_IMAGE_FILE "splash.png" - -void DisplaySplashImage(); - -#endif /* SPLASH_H */ diff --git a/src/os/macosx/string_osx.cpp b/src/os/macosx/string_osx.cpp index cbd3964f23..be01e1479d 100644 --- a/src/os/macosx/string_osx.cpp +++ b/src/os/macosx/string_osx.cpp @@ -175,12 +175,16 @@ static CTRunDelegateCallbacks _sprite_font_callback = { for (const auto &i : fontMapping) { if (i.first - last == 0) continue; - if (!_font_cache[i.second->fc->GetSize()]) { - /* Cache font information. */ - CFAutoRelease font_name(CFStringCreateWithCString(kCFAllocatorDefault, i.second->fc->GetFontName(), kCFStringEncodingUTF8)); - _font_cache[i.second->fc->GetSize()].reset(CTFontCreateWithName(font_name.get(), i.second->fc->GetFontSize(), nullptr)); + CTFontRef font = (CTFontRef)i.second->fc->GetOSHandle(); + if (font == nullptr) { + if (!_font_cache[i.second->fc->GetSize()]) { + /* Cache font information. */ + CFAutoRelease font_name(CFStringCreateWithCString(kCFAllocatorDefault, i.second->fc->GetFontName(), kCFStringEncodingUTF8)); + _font_cache[i.second->fc->GetSize()].reset(CTFontCreateWithName(font_name.get(), i.second->fc->GetFontSize(), nullptr)); + } + font = _font_cache[i.second->fc->GetSize()].get(); } - CFAttributedStringSetAttribute(str.get(), CFRangeMake(last, i.first - last), kCTFontAttributeName, _font_cache[i.second->fc->GetSize()].get()); + CFAttributedStringSetAttribute(str.get(), CFRangeMake(last, i.first - last), kCTFontAttributeName, font); CGColorRef color = CGColorCreateGenericGray((uint8)i.second->colour / 255.0f, 1.0f); // We don't care about the real colours, just that they are different. CFAttributedStringSetAttribute(str.get(), CFRangeMake(last, i.first - last), kCTForegroundColorAttributeName, color); @@ -301,7 +305,7 @@ void MacOSRegisterExternalFont(const char *file_path) CTFontManagerRegisterFontsForURL(url.get(), kCTFontManagerScopeProcess, nullptr); } -/** Store current language locale as a CoreFounation locale. */ +/** Store current language locale as a CoreFoundation locale. */ void MacOSSetCurrentLocaleName(const char *iso_code) { if (!MacOSVersionIsAtLeast(10, 5, 0)) return; diff --git a/src/os/unix/CMakeLists.txt b/src/os/unix/CMakeLists.txt index b548d3bb21..8e74f96643 100644 --- a/src/os/unix/CMakeLists.txt +++ b/src/os/unix/CMakeLists.txt @@ -7,3 +7,8 @@ add_files( unix.cpp CONDITION UNIX AND NOT OPTION_OS2 ) + +add_files( + font_unix.cpp + CONDITION Fontconfig_FOUND +) diff --git a/src/os/unix/font_unix.cpp b/src/os/unix/font_unix.cpp new file mode 100644 index 0000000000..b607db8f8a --- /dev/null +++ b/src/os/unix/font_unix.cpp @@ -0,0 +1,171 @@ +/* + * This file is part of OpenTTD. + * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. + * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . + */ + +/** @file font_unix.cpp Functions related to font handling on Unix/Fontconfig. */ + +#include "../../stdafx.h" +#include "../../debug.h" +#include "../../fontdetection.h" +#include "../../string_func.h" +#include "../../strings_func.h" + +#include + +#include "safeguards.h" + +#ifdef WITH_FREETYPE + +#include +#include FT_FREETYPE_H + +extern FT_Library _library; + + +FT_Error GetFontByFaceName(const char *font_name, FT_Face *face) +{ + FT_Error err = FT_Err_Cannot_Open_Resource; + + if (!FcInit()) { + ShowInfoF("Unable to load font configuration"); + } else { + FcPattern *match; + FcPattern *pat; + FcFontSet *fs; + FcResult result; + char *font_style; + char *font_family; + + /* Split & strip the font's style */ + font_family = stredup(font_name); + font_style = strchr(font_family, ','); + if (font_style != nullptr) { + font_style[0] = '\0'; + font_style++; + while (*font_style == ' ' || *font_style == '\t') font_style++; + } + + /* Resolve the name and populate the information structure */ + pat = FcNameParse((FcChar8 *)font_family); + if (font_style != nullptr) FcPatternAddString(pat, FC_STYLE, (FcChar8 *)font_style); + FcConfigSubstitute(0, pat, FcMatchPattern); + FcDefaultSubstitute(pat); + fs = FcFontSetCreate(); + match = FcFontMatch(0, pat, &result); + + if (fs != nullptr && match != nullptr) { + int i; + FcChar8 *family; + FcChar8 *style; + FcChar8 *file; + FcFontSetAdd(fs, match); + + for (i = 0; err != FT_Err_Ok && i < fs->nfont; i++) { + /* Try the new filename */ + if (FcPatternGetString(fs->fonts[i], FC_FILE, 0, &file) == FcResultMatch && + FcPatternGetString(fs->fonts[i], FC_FAMILY, 0, &family) == FcResultMatch && + FcPatternGetString(fs->fonts[i], FC_STYLE, 0, &style) == FcResultMatch) { + + /* The correct style? */ + if (font_style != nullptr && strcasecmp(font_style, (char *)style) != 0) continue; + + /* Font config takes the best shot, which, if the family name is spelled + * wrongly a 'random' font, so check whether the family name is the + * same as the supplied name */ + if (strcasecmp(font_family, (char *)family) == 0) { + err = FT_New_Face(_library, (char *)file, 0, face); + } + } + } + } + + free(font_family); + FcPatternDestroy(pat); + FcFontSetDestroy(fs); + FcFini(); + } + + return err; +} + +#endif /* WITH_FREETYPE */ + + +bool SetFallbackFont(FreeTypeSettings *settings, const char *language_isocode, int winlangid, MissingGlyphSearcher *callback) +{ + if (!FcInit()) return false; + + bool ret = false; + + /* Fontconfig doesn't handle full language isocodes, only the part + * before the _ of e.g. en_GB is used, so "remove" everything after + * the _. */ + char lang[16]; + seprintf(lang, lastof(lang), ":lang=%s", language_isocode); + char *split = strchr(lang, '_'); + if (split != nullptr) *split = '\0'; + + /* First create a pattern to match the wanted language. */ + FcPattern *pat = FcNameParse((FcChar8 *)lang); + /* We only want to know the filename. */ + FcObjectSet *os = FcObjectSetBuild(FC_FILE, FC_SPACING, FC_SLANT, FC_WEIGHT, nullptr); + /* Get the list of filenames matching the wanted language. */ + FcFontSet *fs = FcFontList(nullptr, pat, os); + + /* We don't need these anymore. */ + FcObjectSetDestroy(os); + FcPatternDestroy(pat); + + if (fs != nullptr) { + int best_weight = -1; + const char *best_font = nullptr; + + for (int i = 0; i < fs->nfont; i++) { + FcPattern *font = fs->fonts[i]; + + FcChar8 *file = nullptr; + FcResult res = FcPatternGetString(font, FC_FILE, 0, &file); + if (res != FcResultMatch || file == nullptr) { + continue; + } + + /* Get a font with the right spacing .*/ + int value = 0; + FcPatternGetInteger(font, FC_SPACING, 0, &value); + if (callback->Monospace() != (value == FC_MONO) && value != FC_DUAL) continue; + + /* Do not use those that explicitly say they're slanted. */ + FcPatternGetInteger(font, FC_SLANT, 0, &value); + if (value != 0) continue; + + /* We want the fatter font as they look better at small sizes. */ + FcPatternGetInteger(font, FC_WEIGHT, 0, &value); + if (value <= best_weight) continue; + + callback->SetFontNames(settings, (const char *)file); + + bool missing = callback->FindMissingGlyphs(); + DEBUG(freetype, 1, "Font \"%s\" misses%s glyphs", file, missing ? "" : " no"); + + if (!missing) { + best_weight = value; + best_font = (const char *)file; + } + } + + if (best_font != nullptr) { + ret = true; + callback->SetFontNames(settings, best_font); + InitFreeType(callback->Monospace()); + } + + /* Clean up the list of filenames. */ + FcFontSetDestroy(fs); + } + + FcFini(); + return ret; +} diff --git a/src/os/unix/unix.cpp b/src/os/unix/unix.cpp index 3fdc84009c..bca9220f41 100644 --- a/src/os/unix/unix.cpp +++ b/src/os/unix/unix.cpp @@ -28,6 +28,10 @@ #include #endif +#ifdef __EMSCRIPTEN__ +# include +#endif + #ifdef __APPLE__ # include #elif (defined(_POSIX_VERSION) && _POSIX_VERSION >= 200112L) || defined(__GLIBC__) @@ -288,7 +292,13 @@ bool GetClipboardContents(char *buffer, const char *last) #endif -#ifndef __APPLE__ +#if defined(__EMSCRIPTEN__) +void OSOpenBrowser(const char *url) +{ + /* Implementation in pre.js */ + EM_ASM({ if(window["openttd_open_url"]) window.openttd_open_url($0, $1) }, url, strlen(url)); +} +#elif !defined( __APPLE__) void OSOpenBrowser(const char *url) { pid_t child_pid = fork(); diff --git a/src/os/windows/CMakeLists.txt b/src/os/windows/CMakeLists.txt index 19d1bd46e3..8ac2de7acc 100644 --- a/src/os/windows/CMakeLists.txt +++ b/src/os/windows/CMakeLists.txt @@ -1,5 +1,7 @@ add_files( crashlog_win.cpp + font_win32.cpp + font_win32.h string_uniscribe.cpp string_uniscribe.h win32.cpp diff --git a/src/os/windows/crashlog_win.cpp b/src/os/windows/crashlog_win.cpp index eb6c5c8ebd..3a4654927a 100644 --- a/src/os/windows/crashlog_win.cpp +++ b/src/os/windows/crashlog_win.cpp @@ -27,6 +27,7 @@ #endif #include +#include #include #include "../../safeguards.h" @@ -617,6 +618,9 @@ void *_safe_esp = nullptr; static LONG WINAPI ExceptionHandler(EXCEPTION_POINTERS *ep) { + /* Restore system timer resolution. */ + timeEndPeriod(1); + /* Disable our event loop. */ SetWindowLongPtr(GetActiveWindow(), GWLP_WNDPROC, (LONG_PTR)&DefWindowProc); diff --git a/src/fontdetection.cpp b/src/os/windows/font_win32.cpp similarity index 50% rename from src/fontdetection.cpp rename to src/os/windows/font_win32.cpp index 570e1aa068..b77f9812ae 100644 --- a/src/fontdetection.cpp +++ b/src/os/windows/font_win32.cpp @@ -5,39 +5,37 @@ * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . */ -/** @file fontdetection.cpp Detection of the right font. */ +/** @file font_win32.cpp Functions related to font handling on Win32. */ + +#include "../../stdafx.h" +#include "../../debug.h" +#include "font_win32.h" +#include "../../blitter/factory.hpp" +#include "../../core/alloc_func.hpp" +#include "../../core/math_func.hpp" +#include "../../fileio_func.h" +#include "../../fontdetection.h" +#include "../../fontcache.h" +#include "../../string_func.h" +#include "../../strings_func.h" +#include "../../zoom_func.h" + +#include "../../table/control_codes.h" -#if defined(WITH_FREETYPE) || defined(_WIN32) - -#include "stdafx.h" -#include "debug.h" -#include "fontdetection.h" -#include "string_func.h" -#include "strings_func.h" - -#ifdef WITH_FREETYPE -extern FT_Library _library; -#endif /* WITH_FREETYPE */ - -/** - * Get the font loaded into a Freetype face by using a font-name. - * If no appropriate font is found, the function returns an error - */ - -/* ======================================================================================== - * Windows support - * ======================================================================================== */ - -#ifdef _WIN32 -#include "core/alloc_func.hpp" -#include "core/math_func.hpp" #include #include /* SHGetFolderPath */ #include "os/windows/win32.h" +#undef small // Say what, Windows? #include "safeguards.h" #ifdef WITH_FREETYPE + +#include +#include FT_FREETYPE_H + +extern FT_Library _library; + /** * Get the short DOS 8.3 format for paths. * FreeType doesn't support Unicode filenames and Windows' fopen (as used @@ -48,7 +46,7 @@ extern FT_Library _library; * @param long_path the path in system encoding. * @return the short path in ANSI (ASCII). */ -const char *GetShortPath(const TCHAR *long_path) +static const char *GetShortPath(const TCHAR *long_path) { static char short_path[MAX_PATH]; #ifdef UNICODE @@ -102,7 +100,7 @@ FT_Error GetFontByFaceName(const char *font_name, FT_Face *face) DWORD vbuflen = lengthof(vbuffer); DWORD dbuflen = lengthof(dbuffer); - ret = RegEnumValue(hKey, index, vbuffer, &vbuflen, nullptr, nullptr, (byte*)dbuffer, &dbuflen); + ret = RegEnumValue(hKey, index, vbuffer, &vbuflen, nullptr, nullptr, (byte *)dbuffer, &dbuflen); if (ret != ERROR_SUCCESS) goto registry_no_font_found; /* The font names in the registry are of the following 3 forms: @@ -222,12 +220,12 @@ static const char *GetEnglishFontName(const ENUMLOGFONTEX *logfont) offset += buf[pos++]; /* Don't buffer overflow */ - length = std::min(length, MAX_PATH - 1); + length = std::min(length, MAX_PATH - 1); for (uint j = 0; j < length; j++) font_name[j] = buf[stringOffset + offset + j]; font_name[length] = '\0'; if ((platformId == 1 && languageId == 0) || // Macintosh English - (platformId == 3 && languageId == 0x0409)) { // Microsoft English (US) + (platformId == 3 && languageId == 0x0409)) { // Microsoft English (US) ret_font_name = font_name; break; } @@ -240,7 +238,7 @@ err2: ReleaseDC(nullptr, dc); DeleteObject(font); err1: - return ret_font_name == nullptr ? WIDE_TO_MB((const TCHAR*)logfont->elfFullName) : ret_font_name; + return ret_font_name == nullptr ? WIDE_TO_MB((const TCHAR *)logfont->elfFullName) : ret_font_name; } #endif /* WITH_FREETYPE */ @@ -291,7 +289,7 @@ static int CALLBACK EnumFontCallback(const ENUMLOGFONTEX *logfont, const NEWTEXT EFCParam *info = (EFCParam *)lParam; /* Skip duplicates */ - if (!info->fonts.Add((const TCHAR*)logfont->elfFullName)) return 1; + if (!info->fonts.Add((const TCHAR *)logfont->elfFullName)) return 1; /* Only use TrueType fonts */ if (!(type & TRUETYPE_FONTTYPE)) return 1; /* Don't use SYMBOL fonts */ @@ -344,9 +342,7 @@ static int CALLBACK EnumFontCallback(const ENUMLOGFONTEX *logfont, const NEWTEXT const char *english_name = font_name; #endif /* WITH_FREETYPE */ - PLOGFONT os_data = MallocT(1); - *os_data = logfont->elfLogFont; - info->callback->SetFontNames(info->settings, font_name, os_data); + info->callback->SetFontNames(info->settings, font_name, &logfont->elfLogFont); if (info->callback->FindMissingGlyphs()) return 1; DEBUG(freetype, 1, "Fallback font: %s (%s)", font_name, english_name); return 0; // stop enumerating @@ -376,279 +372,306 @@ bool SetFallbackFont(FreeTypeSettings *settings, const char *language_isocode, i return ret == 0; } -#elif defined(__APPLE__) /* end ifdef Win32 */ -/* ======================================================================================== - * OSX support - * ======================================================================================== */ - -#include "os/macosx/macos.h" -#include "safeguards.h" +#ifndef ANTIALIASED_QUALITY +#define ANTIALIASED_QUALITY 4 +#endif -FT_Error GetFontByFaceName(const char *font_name, FT_Face *face) +/** + * Create a new Win32FontCache. + * @param fs The font size that is going to be cached. + * @param logfont The font that has to be loaded. + * @param pixels The number of pixels this font should be high. + */ +Win32FontCache::Win32FontCache(FontSize fs, const LOGFONT &logfont, int pixels) : TrueTypeFontCache(fs, pixels), logfont(logfont) { - FT_Error err = FT_Err_Cannot_Open_Resource; - - /* Get font reference from name. */ - UInt8 file_path[PATH_MAX]; - OSStatus os_err = -1; - CFAutoRelease name(CFStringCreateWithCString(kCFAllocatorDefault, font_name, kCFStringEncodingUTF8)); - - /* Simply creating the font using CTFontCreateWithNameAndSize will *always* return - * something, no matter the name. As such, we can't use it to check for existence. - * We instead query the list of all font descriptors that match the given name which - * does not do this stupid name fallback. */ - CFAutoRelease name_desc(CTFontDescriptorCreateWithNameAndSize(name.get(), 0.0)); - CFAutoRelease mandatory_attribs(CFSetCreate(kCFAllocatorDefault, const_cast(reinterpret_cast(&kCTFontNameAttribute)), 1, &kCFTypeSetCallBacks)); - CFAutoRelease descs(CTFontDescriptorCreateMatchingFontDescriptors(name_desc.get(), mandatory_attribs.get())); - - /* Loop over all matches until we can get a path for one of them. */ - for (CFIndex i = 0; descs.get() != nullptr && i < CFArrayGetCount(descs.get()) && os_err != noErr; i++) { - CFAutoRelease font(CTFontCreateWithFontDescriptor((CTFontDescriptorRef)CFArrayGetValueAtIndex(descs.get(), i), 0.0, nullptr)); - CFAutoRelease fontURL((CFURLRef)CTFontCopyAttribute(font.get(), kCTFontURLAttribute)); - if (CFURLGetFileSystemRepresentation(fontURL.get(), true, file_path, lengthof(file_path))) os_err = noErr; - } - - if (os_err == noErr) { - DEBUG(freetype, 3, "Font path for %s: %s", font_name, file_path); - err = FT_New_Face(_library, (const char *)file_path, 0, face); - } + this->dc = CreateCompatibleDC(nullptr); + this->SetFontSize(fs, pixels); +} - return err; +Win32FontCache::~Win32FontCache() +{ + this->ClearFontCache(); + DeleteDC(this->dc); + DeleteObject(this->font); } -bool SetFallbackFont(FreeTypeSettings *settings, const char *language_isocode, int winlangid, MissingGlyphSearcher *callback) +void Win32FontCache::SetFontSize(FontSize fs, int pixels) { - /* Determine fallback font using CoreText. This uses the language isocode - * to find a suitable font. CoreText is available from 10.5 onwards. */ - char lang[16]; - if (strcmp(language_isocode, "zh_TW") == 0) { - /* Traditional Chinese */ - strecpy(lang, "zh-Hant", lastof(lang)); - } else if (strcmp(language_isocode, "zh_CN") == 0) { - /* Simplified Chinese */ - strecpy(lang, "zh-Hans", lastof(lang)); + if (pixels == 0) { + /* Try to determine a good height based on the minimal height recommended by the font. */ + int scaled_height = ScaleFontTrad(this->GetDefaultFontHeight(this->fs)); + pixels = scaled_height; + + HFONT temp = CreateFontIndirect(&this->logfont); + if (temp != nullptr) { + HGDIOBJ old = SelectObject(this->dc, temp); + + UINT size = GetOutlineTextMetrics(this->dc, 0, nullptr); + LPOUTLINETEXTMETRIC otm = (LPOUTLINETEXTMETRIC)AllocaM(BYTE, size); + GetOutlineTextMetrics(this->dc, size, otm); + + /* Font height is minimum height plus the difference between the default + * height for this font size and the small size. */ + int diff = scaled_height - ScaleFontTrad(this->GetDefaultFontHeight(FS_SMALL)); + pixels = Clamp(std::min(otm->otmusMinimumPPEM, MAX_FONT_MIN_REC_SIZE) + diff, scaled_height, MAX_FONT_SIZE); + + SelectObject(dc, old); + DeleteObject(temp); + } } else { - /* Just copy the first part of the isocode. */ - strecpy(lang, language_isocode, lastof(lang)); - char *sep = strchr(lang, '_'); - if (sep != nullptr) *sep = '\0'; + pixels = ScaleFontTrad(pixels); } + this->used_size = pixels; - /* Create a font descriptor matching the wanted language and latin (english) glyphs. - * Can't use CFAutoRelease here for everything due to the way the dictionary has to be created. */ - CFStringRef lang_codes[2]; - lang_codes[0] = CFStringCreateWithCString(kCFAllocatorDefault, lang, kCFStringEncodingUTF8); - lang_codes[1] = CFSTR("en"); - CFArrayRef lang_arr = CFArrayCreate(kCFAllocatorDefault, (const void **)lang_codes, lengthof(lang_codes), &kCFTypeArrayCallBacks); - CFAutoRelease lang_attribs(CFDictionaryCreate(kCFAllocatorDefault, const_cast(reinterpret_cast(&kCTFontLanguagesAttribute)), (const void **)&lang_arr, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)); - CFAutoRelease lang_desc(CTFontDescriptorCreateWithAttributes(lang_attribs.get())); - CFRelease(lang_arr); - CFRelease(lang_codes[0]); - - /* Get array of all font descriptors for the wanted language. */ - CFAutoRelease mandatory_attribs(CFSetCreate(kCFAllocatorDefault, const_cast(reinterpret_cast(&kCTFontLanguagesAttribute)), 1, &kCFTypeSetCallBacks)); - CFAutoRelease descs(CTFontDescriptorCreateMatchingFontDescriptors(lang_desc.get(), mandatory_attribs.get())); - - bool result = false; - for (CFIndex i = 0; descs.get() != nullptr && i < CFArrayGetCount(descs.get()); i++) { - CTFontDescriptorRef font = (CTFontDescriptorRef)CFArrayGetValueAtIndex(descs.get(), i); - - /* Get font traits. */ - CFAutoRelease traits((CFDictionaryRef)CTFontDescriptorCopyAttribute(font, kCTFontTraitsAttribute)); - CTFontSymbolicTraits symbolic_traits; - CFNumberGetValue((CFNumberRef)CFDictionaryGetValue(traits.get(), kCTFontSymbolicTrait), kCFNumberIntType, &symbolic_traits); - - /* Skip symbol fonts and vertical fonts. */ - if ((symbolic_traits & kCTFontClassMaskTrait) == (CTFontStylisticClass)kCTFontSymbolicClass || (symbolic_traits & kCTFontVerticalTrait)) continue; - /* Skip bold fonts (especially Arial Bold, which looks worse than regular Arial). */ - if (symbolic_traits & kCTFontBoldTrait) continue; - /* Select monospaced fonts if asked for. */ - if (((symbolic_traits & kCTFontMonoSpaceTrait) == kCTFontMonoSpaceTrait) != callback->Monospace()) continue; - - /* Get font name. */ - char name[128]; - CFAutoRelease font_name((CFStringRef)CTFontDescriptorCopyAttribute(font, kCTFontDisplayNameAttribute)); - CFStringGetCString(font_name.get(), name, lengthof(name), kCFStringEncodingUTF8); - - /* There are some special fonts starting with an '.' and the last - * resort font that aren't usable. Skip them. */ - if (name[0] == '.' || strncmp(name, "LastResort", 10) == 0) continue; - - /* Save result. */ - callback->SetFontNames(settings, name); - if (!callback->FindMissingGlyphs()) { - DEBUG(freetype, 2, "CT-Font for %s: %s", language_isocode, name); - result = true; - break; - } - } + /* Create GDI font handle. */ + this->logfont.lfHeight = -pixels; + this->logfont.lfWidth = 0; + this->logfont.lfOutPrecision = ANTIALIASED_QUALITY; - if (!result) { - /* For some OS versions, the font 'Arial Unicode MS' does not report all languages it - * supports. If we didn't find any other font, just try it, maybe we get lucky. */ - callback->SetFontNames(settings, "Arial Unicode MS"); - result = !callback->FindMissingGlyphs(); + if (this->font != nullptr) { + SelectObject(dc, this->old_font); + DeleteObject(this->font); } + this->font = CreateFontIndirect(&this->logfont); + this->old_font = SelectObject(this->dc, this->font); - callback->FindMissingGlyphs(); - return result; -} + /* Query the font metrics we needed. */ + UINT otmSize = GetOutlineTextMetrics(this->dc, 0, nullptr); + POUTLINETEXTMETRIC otm = (POUTLINETEXTMETRIC)AllocaM(BYTE, otmSize); + GetOutlineTextMetrics(this->dc, otmSize, otm); -#elif defined(WITH_FONTCONFIG) /* end ifdef __APPLE__ */ + this->units_per_em = otm->otmEMSquare; + this->ascender = otm->otmTextMetrics.tmAscent; + this->descender = otm->otmTextMetrics.tmDescent; + this->height = this->ascender + this->descender; + this->glyph_size.cx = otm->otmTextMetrics.tmMaxCharWidth; + this->glyph_size.cy = otm->otmTextMetrics.tmHeight; -#include + font_height_cache[this->fs] = this->GetHeight(); -#include "safeguards.h" + DEBUG(freetype, 2, "Loaded font '%s' with size %d", FS2OTTD((LPTSTR)((BYTE *)otm + (ptrdiff_t)otm->otmpFullName)), pixels); +} -/* ======================================================================================== - * FontConfig (unix) support - * ======================================================================================== */ -FT_Error GetFontByFaceName(const char *font_name, FT_Face *face) +/** + * Reset cached glyphs. + */ +void Win32FontCache::ClearFontCache() { - FT_Error err = FT_Err_Cannot_Open_Resource; + /* GUI scaling might have changed, determine font size anew if it was automatically selected. */ + if (this->font != nullptr) this->SetFontSize(this->fs, this->req_size); - if (!FcInit()) { - ShowInfoF("Unable to load font configuration"); - } else { - FcPattern *match; - FcPattern *pat; - FcFontSet *fs; - FcResult result; - char *font_style; - char *font_family; - - /* Split & strip the font's style */ - font_family = stredup(font_name); - font_style = strchr(font_family, ','); - if (font_style != nullptr) { - font_style[0] = '\0'; - font_style++; - while (*font_style == ' ' || *font_style == '\t') font_style++; - } + this->TrueTypeFontCache::ClearFontCache(); +} + +/* virtual */ const Sprite *Win32FontCache::InternalGetGlyph(GlyphID key, bool aa) +{ + GLYPHMETRICS gm; + MAT2 mat = { {0, 1}, {0, 0}, {0, 0}, {0, 1} }; + + /* Make a guess for the needed memory size. */ + DWORD size = this->glyph_size.cy * Align(aa ? this->glyph_size.cx : std::max(this->glyph_size.cx / 8l, 1l), 4); // Bitmap data is DWORD-aligned rows. + byte *bmp = AllocaM(byte, size); + size = GetGlyphOutline(this->dc, key, GGO_GLYPH_INDEX | (aa ? GGO_GRAY8_BITMAP : GGO_BITMAP), &gm, size, bmp, &mat); + + if (size == GDI_ERROR) { + /* No dice with the guess. First query size of needed glyph memory, then allocate the + * memory and query again. This dance is necessary as some glyphs will only render with + * the exact matching size; e.g. the space glyph has no pixels and must be requested + * with size == 0, anything else fails. Unfortunately, a failed call doesn't return any + * info about the size and thus the triple GetGlyphOutline()-call. */ + size = GetGlyphOutline(this->dc, key, GGO_GLYPH_INDEX | (aa ? GGO_GRAY8_BITMAP : GGO_BITMAP), &gm, 0, nullptr, &mat); + if (size == GDI_ERROR) usererror("Unable to render font glyph"); + bmp = AllocaM(byte, size); + GetGlyphOutline(this->dc, key, GGO_GLYPH_INDEX | (aa ? GGO_GRAY8_BITMAP : GGO_BITMAP), &gm, size, bmp, &mat); + } - /* Resolve the name and populate the information structure */ - pat = FcNameParse((FcChar8*)font_family); - if (font_style != nullptr) FcPatternAddString(pat, FC_STYLE, (FcChar8*)font_style); - FcConfigSubstitute(0, pat, FcMatchPattern); - FcDefaultSubstitute(pat); - fs = FcFontSetCreate(); - match = FcFontMatch(0, pat, &result); - - if (fs != nullptr && match != nullptr) { - int i; - FcChar8 *family; - FcChar8 *style; - FcChar8 *file; - FcFontSetAdd(fs, match); - - for (i = 0; err != FT_Err_Ok && i < fs->nfont; i++) { - /* Try the new filename */ - if (FcPatternGetString(fs->fonts[i], FC_FILE, 0, &file) == FcResultMatch && - FcPatternGetString(fs->fonts[i], FC_FAMILY, 0, &family) == FcResultMatch && - FcPatternGetString(fs->fonts[i], FC_STYLE, 0, &style) == FcResultMatch) { - - /* The correct style? */ - if (font_style != nullptr && strcasecmp(font_style, (char*)style) != 0) continue; - - /* Font config takes the best shot, which, if the family name is spelled - * wrongly a 'random' font, so check whether the family name is the - * same as the supplied name */ - if (strcasecmp(font_family, (char*)family) == 0) { - err = FT_New_Face(_library, (char *)file, 0, face); + /* Add 1 pixel for the shadow on the medium font. Our sprite must be at least 1x1 pixel. */ + uint width = std::max(1U, (uint)gm.gmBlackBoxX + (this->fs == FS_NORMAL)); + uint height = std::max(1U, (uint)gm.gmBlackBoxY + (this->fs == FS_NORMAL)); + + /* Limit glyph size to prevent overflows later on. */ + if (width > MAX_GLYPH_DIM || height > MAX_GLYPH_DIM) usererror("Font glyph is too large"); + + /* GDI has rendered the glyph, now we allocate a sprite and copy the image into it. */ + SpriteLoader::Sprite sprite; + sprite.AllocateData(ZOOM_LVL_NORMAL, width * height); + sprite.type = ST_FONT; + sprite.width = width; + sprite.height = height; + sprite.x_offs = gm.gmptGlyphOrigin.x; + sprite.y_offs = this->ascender - gm.gmptGlyphOrigin.y; + + if (size > 0) { + /* All pixel data returned by GDI is in the form of DWORD-aligned rows. + * For a non anti-aliased glyph, the returned bitmap has one bit per pixel. + * For anti-aliased rendering, GDI uses the strange value range of 0 to 64, + * inclusively. To map this to 0 to 255, we shift left by two and then + * subtract one. */ + uint pitch = Align(aa ? gm.gmBlackBoxX : std::max(gm.gmBlackBoxX / 8u, 1u), 4); + + /* Draw shadow for medium size. */ + if (this->fs == FS_NORMAL && !aa) { + for (uint y = 0; y < gm.gmBlackBoxY; y++) { + for (uint x = 0; x < gm.gmBlackBoxX; x++) { + if (aa ? (bmp[x + y * pitch] > 0) : HasBit(bmp[(x / 8) + y * pitch], 7 - (x % 8))) { + sprite.data[1 + x + (1 + y) * sprite.width].m = SHADOW_COLOUR; + sprite.data[1 + x + (1 + y) * sprite.width].a = aa ? (bmp[x + y * pitch] << 2) - 1 : 0xFF; } } } } - free(font_family); - FcPatternDestroy(pat); - FcFontSetDestroy(fs); - FcFini(); + for (uint y = 0; y < gm.gmBlackBoxY; y++) { + for (uint x = 0; x < gm.gmBlackBoxX; x++) { + if (aa ? (bmp[x + y * pitch] > 0) : HasBit(bmp[(x / 8) + y * pitch], 7 - (x % 8))) { + sprite.data[x + y * sprite.width].m = FACE_COLOUR; + sprite.data[x + y * sprite.width].a = aa ? (bmp[x + y * pitch] << 2) - 1 : 0xFF; + } + } + } } - return err; + GlyphEntry new_glyph; + new_glyph.sprite = BlitterFactory::GetCurrentBlitter()->Encode(&sprite, AllocateFont); + new_glyph.width = gm.gmCellIncX; + + this->SetGlyphPtr(key, &new_glyph); + + return new_glyph.sprite; } -bool SetFallbackFont(FreeTypeSettings *settings, const char *language_isocode, int winlangid, MissingGlyphSearcher *callback) +/* virtual */ GlyphID Win32FontCache::MapCharToGlyph(WChar key) { - if (!FcInit()) return false; - - bool ret = false; - - /* Fontconfig doesn't handle full language isocodes, only the part - * before the _ of e.g. en_GB is used, so "remove" everything after - * the _. */ - char lang[16]; - seprintf(lang, lastof(lang), ":lang=%s", language_isocode); - char *split = strchr(lang, '_'); - if (split != nullptr) *split = '\0'; - - /* First create a pattern to match the wanted language. */ - FcPattern *pat = FcNameParse((FcChar8*)lang); - /* We only want to know the filename. */ - FcObjectSet *os = FcObjectSetBuild(FC_FILE, FC_SPACING, FC_SLANT, FC_WEIGHT, nullptr); - /* Get the list of filenames matching the wanted language. */ - FcFontSet *fs = FcFontList(nullptr, pat, os); - - /* We don't need these anymore. */ - FcObjectSetDestroy(os); - FcPatternDestroy(pat); - - if (fs != nullptr) { - int best_weight = -1; - const char *best_font = nullptr; - - for (int i = 0; i < fs->nfont; i++) { - FcPattern *font = fs->fonts[i]; - - FcChar8 *file = nullptr; - FcResult res = FcPatternGetString(font, FC_FILE, 0, &file); - if (res != FcResultMatch || file == nullptr) { - continue; - } + assert(IsPrintable(key)); + + if (key >= SCC_SPRITE_START && key <= SCC_SPRITE_END) { + return this->parent->MapCharToGlyph(key); + } + + /* Convert characters outside of the BMP into surrogate pairs. */ + WCHAR chars[2]; + if (key >= 0x010000U) { + chars[0] = (WCHAR)(((key - 0x010000U) >> 10) + 0xD800); + chars[1] = (WCHAR)(((key - 0x010000U) & 0x3FF) + 0xDC00); + } else { + chars[0] = (WCHAR)(key & 0xFFFF); + } + + WORD glyphs[2] = { 0, 0 }; + GetGlyphIndicesW(this->dc, chars, key >= 0x010000U ? 2 : 1, glyphs, GGI_MARK_NONEXISTING_GLYPHS); + + return glyphs[0] != 0xFFFF ? glyphs[0] : 0; +} + +/* virtual */ const void *Win32FontCache::InternalGetFontTable(uint32 tag, size_t &length) +{ + DWORD len = GetFontData(this->dc, tag, 0, nullptr, 0); + + void *result = nullptr; + if (len != GDI_ERROR && len > 0) { + result = MallocT(len); + GetFontData(this->dc, tag, 0, result, len); + } + + length = len; + return result; +} - /* Get a font with the right spacing .*/ - int value = 0; - FcPatternGetInteger(font, FC_SPACING, 0, &value); - if (callback->Monospace() != (value == FC_MONO) && value != FC_DUAL) continue; - /* Do not use those that explicitly say they're slanted. */ - FcPatternGetInteger(font, FC_SLANT, 0, &value); - if (value != 0) continue; +/** + * Loads the GDI font. + * If a GDI font description is present, e.g. from the automatic font + * fallback search, use it. Otherwise, try to resolve it by font name. + * @param fs The font size to load. + */ +void LoadWin32Font(FontSize fs) +{ + static const char *SIZE_TO_NAME[] = { "medium", "small", "large", "mono" }; + + FreeTypeSubSetting *settings = nullptr; + switch (fs) { + case FS_SMALL: settings = &_freetype.small; break; + case FS_NORMAL: settings = &_freetype.medium; break; + case FS_LARGE: settings = &_freetype.large; break; + case FS_MONO: settings = &_freetype.mono; break; + default: NOT_REACHED(); + } + + if (StrEmpty(settings->font)) return; - /* We want the fatter font as they look better at small sizes. */ - FcPatternGetInteger(font, FC_WEIGHT, 0, &value); - if (value <= best_weight) continue; + LOGFONT logfont; + MemSetT(&logfont, 0); + logfont.lfPitchAndFamily = fs == FS_MONO ? FIXED_PITCH : VARIABLE_PITCH; + logfont.lfCharSet = DEFAULT_CHARSET; + logfont.lfOutPrecision = OUT_OUTLINE_PRECIS; + logfont.lfClipPrecision = CLIP_DEFAULT_PRECIS; - callback->SetFontNames(settings, (const char*)file); + if (settings->os_handle != nullptr) { + logfont = *(const LOGFONT *)settings->os_handle; + } else if (strchr(settings->font, '.') != nullptr) { + /* Might be a font file name, try load it. */ - bool missing = callback->FindMissingGlyphs(); - DEBUG(freetype, 1, "Font \"%s\" misses%s glyphs", file, missing ? "" : " no"); + TCHAR fontPath[MAX_PATH] = {}; - if (!missing) { - best_weight = value; - best_font = (const char *)file; + /* See if this is an absolute path. */ + if (FileExists(settings->font)) { + convert_to_fs(settings->font, fontPath, lengthof(fontPath), false); + } else { + /* Scan the search-paths to see if it can be found. */ + std::string full_font = FioFindFullPath(BASE_DIR, settings->font); + if (!full_font.empty()) { + convert_to_fs(full_font.c_str(), fontPath, lengthof(fontPath), false); } } - if (best_font != nullptr) { - ret = true; - callback->SetFontNames(settings, best_font); - InitFreeType(callback->Monospace()); - } + if (fontPath[0] != 0) { + if (AddFontResourceEx(fontPath, FR_PRIVATE, 0) != 0) { + /* Try a nice little undocumented function first for getting the internal font name. + * Some documentation is found at: http://www.undocprint.org/winspool/getfontresourceinfo */ + typedef BOOL(WINAPI *PFNGETFONTRESOURCEINFO)(LPCTSTR, LPDWORD, LPVOID, DWORD); +#ifdef UNICODE + static PFNGETFONTRESOURCEINFO GetFontResourceInfo = (PFNGETFONTRESOURCEINFO)GetProcAddress(GetModuleHandle(_T("Gdi32")), "GetFontResourceInfoW"); +#else + static PFNGETFONTRESOURCEINFO GetFontResourceInfo = (PFNGETFONTRESOURCEINFO)GetProcAddress(GetModuleHandle(_T("Gdi32")), "GetFontResourceInfoA"); +#endif - /* Clean up the list of filenames. */ - FcFontSetDestroy(fs); + if (GetFontResourceInfo != nullptr) { + /* Try to query an array of LOGFONTs that describe the file. */ + DWORD len = 0; + if (GetFontResourceInfo(fontPath, &len, nullptr, 2) && len >= sizeof(LOGFONT)) { + LOGFONT *buf = (LOGFONT *)AllocaM(byte, len); + if (GetFontResourceInfo(fontPath, &len, buf, 2)) { + logfont = *buf; // Just use first entry. + } + } + } + + /* No dice yet. Use the file name as the font face name, hoping it matches. */ + if (logfont.lfFaceName[0] == 0) { + TCHAR fname[_MAX_FNAME]; + _tsplitpath(fontPath, nullptr, nullptr, fname, nullptr); + + _tcsncpy_s(logfont.lfFaceName, lengthof(logfont.lfFaceName), fname, _TRUNCATE); + logfont.lfWeight = strcasestr(settings->font, " bold") != nullptr || strcasestr(settings->font, "-bold") != nullptr ? FW_BOLD : FW_NORMAL; // Poor man's way to allow selecting bold fonts. + } + } else { + ShowInfoF("Unable to load file '%s' for %s font, using default windows font selection instead", settings->font, SIZE_TO_NAME[fs]); + } + } } - FcFini(); - return ret; -} + if (logfont.lfFaceName[0] == 0) { + logfont.lfWeight = strcasestr(settings->font, " bold") != nullptr ? FW_BOLD : FW_NORMAL; // Poor man's way to allow selecting bold fonts. + convert_to_fs(settings->font, logfont.lfFaceName, lengthof(logfont.lfFaceName), false); + } -#else /* without WITH_FONTCONFIG */ -FT_Error GetFontByFaceName(const char *font_name, FT_Face *face) {return FT_Err_Cannot_Open_Resource;} -bool SetFallbackFont(FreeTypeSettings *settings, const char *language_isocode, int winlangid, MissingGlyphSearcher *callback) { return false; } -#endif /* WITH_FONTCONFIG */ + HFONT font = CreateFontIndirect(&logfont); + if (font == nullptr) { + ShowInfoF("Unable to use '%s' for %s font, Win32 reported error 0x%lX, using sprite font instead", settings->font, SIZE_TO_NAME[fs], GetLastError()); + return; + } + DeleteObject(font); -#endif /* WITH_FREETYPE */ + new Win32FontCache(fs, logfont, settings->size); +} diff --git a/src/os/windows/font_win32.h b/src/os/windows/font_win32.h new file mode 100644 index 0000000000..9a552125fd --- /dev/null +++ b/src/os/windows/font_win32.h @@ -0,0 +1,42 @@ +/* + * This file is part of OpenTTD. + * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. + * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . + */ + +/** @file font_win32.h Functions related to font handling on Win32. */ + +#ifndef FONT_WIN32_H +#define FONT_WIN32_H + +#include "../../fontcache_internal.h" +#include "win32.h" + +/** Font cache for fonts that are based on a Win32 font. */ +class Win32FontCache : public TrueTypeFontCache { +private: + LOGFONT logfont; ///< Logical font information for selecting the font face. + HFONT font = nullptr; ///< The font face associated with this font. + HDC dc = nullptr; ///< Cached GDI device context. + HGDIOBJ old_font; ///< Old font selected into the GDI context. + SIZE glyph_size; ///< Maximum size of regular glyphs. + + void SetFontSize(FontSize fs, int pixels); + +protected: + const void *InternalGetFontTable(uint32 tag, size_t &length) override; + const Sprite *InternalGetGlyph(GlyphID key, bool aa) override; + +public: + Win32FontCache(FontSize fs, const LOGFONT &logfont, int pixels); + ~Win32FontCache(); + void ClearFontCache() override; + GlyphID MapCharToGlyph(WChar key) override; + const char *GetFontName() override { return WIDE_TO_MB(this->logfont.lfFaceName); } + const void *GetOSHandle() override { return &this->logfont; } +}; + +void LoadWin32Font(FontSize fs); + +#endif /* FONT_WIN32_H */ diff --git a/src/os/windows/string_uniscribe.cpp b/src/os/windows/string_uniscribe.cpp index ad37548662..6c0a9366e9 100644 --- a/src/os/windows/string_uniscribe.cpp +++ b/src/os/windows/string_uniscribe.cpp @@ -144,7 +144,7 @@ void UniscribeResetScriptCache(FontSize size) /** Load the matching native Windows font. */ static HFONT HFontFromFont(Font *font) { - if (font->fc->GetOSHandle() != nullptr) return CreateFontIndirect((PLOGFONT)font->fc->GetOSHandle()); + if (font->fc->GetOSHandle() != nullptr) return CreateFontIndirect((const PLOGFONT)font->fc->GetOSHandle()); LOGFONT logfont; ZeroMemory(&logfont, sizeof(LOGFONT)); diff --git a/src/os/windows/win32.cpp b/src/os/windows/win32.cpp index 18e92f934f..88c4867b13 100644 --- a/src/os/windows/win32.cpp +++ b/src/os/windows/win32.cpp @@ -14,6 +14,7 @@ #include "../../fileio_func.h" #include #include +#include #include #define NO_SHOBJIDL_SORTDIRECTION // Avoid multiple definition of SORT_ASCENDING #include /* SHGetFolderPath */ @@ -439,6 +440,9 @@ int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLi int argc; char *argv[64]; // max 64 command line arguments + /* Set system timer resolution to 1ms. */ + timeBeginPeriod(1); + CrashLog::InitialiseCrashLog(); #if defined(UNICODE) @@ -466,6 +470,10 @@ int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLi for (int i = 0; i < argc; i++) ValidateString(argv[i]); openttd_main(argc, argv); + + /* Restore system timer resolution. */ + timeEndPeriod(1); + free(cmdline); return 0; } diff --git a/src/road_cmd.cpp b/src/road_cmd.cpp index 78497d7c7d..e69283121b 100644 --- a/src/road_cmd.cpp +++ b/src/road_cmd.cpp @@ -2842,7 +2842,7 @@ static bool CanConvertUnownedRoadType(Owner owner, RoadTramType rtt) } /** - * Convert the ownership of the RoadType of the tile if applyable + * Convert the ownership of the RoadType of the tile if applicable * @param tile the tile of which convert ownership * @param num_pieces the count of the roadbits to assign to the new owner * @param owner the current owner of the RoadType diff --git a/src/script/api/game_changelog.hpp b/src/script/api/game_changelog.hpp index edc5884072..737adc7fc5 100644 --- a/src/script/api/game_changelog.hpp +++ b/src/script/api/game_changelog.hpp @@ -38,6 +38,7 @@ * * Other changes: * \li GSCompany::ChangeBankBalance takes one extra parameter to refer to a location to show text effect on + * \li GSGoal::Question and GSGoal::QuestionClient no longer require to have any buttons except for the window type GSGoal.QT_QUESTION * * \b 1.10.0 * diff --git a/src/script/api/script_goal.cpp b/src/script/api/script_goal.cpp index ca2b165740..f1b75b032f 100644 --- a/src/script/api/script_goal.cpp +++ b/src/script/api/script_goal.cpp @@ -115,9 +115,10 @@ EnforcePrecondition(false, question != nullptr); const char *text = question->GetEncodedText(); EnforcePreconditionEncodedText(false, text); - EnforcePrecondition(false, CountBits(buttons) >= 1 && CountBits(buttons) <= 3); + uint min_buttons = (type == QT_QUESTION ? 1 : 0); + EnforcePrecondition(false, CountBits(buttons) >= min_buttons && CountBits(buttons) <= 3); EnforcePrecondition(false, buttons < (1 << ::GOAL_QUESTION_BUTTON_COUNT)); - EnforcePrecondition(false, (int)type < ::GOAL_QUESTION_TYPE_COUNT); + EnforcePrecondition(false, (int)type < ::GQT_END); return ScriptObject::DoCommand(0, uniqueid | (target << 16), buttons | (type << 29) | (is_client ? (1 << 31) : 0), CMD_GOAL_QUESTION, text); } diff --git a/src/settings.cpp b/src/settings.cpp index 29f9aa5d3a..fe8692f378 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -38,7 +38,7 @@ #include "sound_func.h" #include "company_func.h" #include "rev.h" -#if defined(WITH_FREETYPE) || defined(_WIN32) +#if defined(WITH_FREETYPE) || defined(_WIN32) || defined(WITH_COCOA) #include "fontcache.h" #endif #include "textbuf_gui.h" @@ -77,7 +77,7 @@ #include "station_base.h" #include "infrastructure_func.h" -#if defined(WITH_FREETYPE) || defined(_WIN32) +#if defined(WITH_FREETYPE) || defined(_WIN32) || defined(WITH_COCOA) #define HAS_TRUETYPE_FONT #endif @@ -101,7 +101,7 @@ typedef std::list ErrorList; static ErrorList _settings_error_list; ///< Errors while loading minimal settings. -typedef void SettingDescProc(IniFile *ini, const SettingDesc *desc, const char *grpname, void *object); +typedef void SettingDescProc(IniFile *ini, const SettingDesc *desc, const char *grpname, void *object, bool only_startup); typedef void SettingDescProcList(IniFile *ini, const char *grpname, StringList &list); static bool IsSignedVarMemType(VarType vt); @@ -530,8 +530,9 @@ static void Write_ValidateSetting(void *ptr, const SettingDesc *sd, int32 val) * be given values * @param grpname the group of the IniFile to search in for the new values * @param object pointer to the object been loaded + * @param only_startup load only the startup settings set */ -static void IniLoadSettings(IniFile *ini, const SettingDesc *sd, const char *grpname, void *object) +static void IniLoadSettings(IniFile *ini, const SettingDesc *sd, const char *grpname, void *object, bool only_startup) { IniGroup *group; IniGroup *group_def = ini->GetGroup(grpname); @@ -541,6 +542,7 @@ static void IniLoadSettings(IniFile *ini, const SettingDesc *sd, const char *grp const SaveLoad *sld = &sd->save; if (!SlIsObjectCurrentlyValid(sld->version_from, sld->version_to, sld->ext_feature_test)) continue; + if (sd->desc.startup != only_startup) continue; IniItem *item; if (sdb->flags & SGF_NO_NEWGAME) { item = nullptr; @@ -645,7 +647,7 @@ static void IniLoadSettings(IniFile *ini, const SettingDesc *sd, const char *grp * values are reloaded when saving). If settings indeed have changed, we get * these and save them. */ -static void IniSaveSettings(IniFile *ini, const SettingDesc *sd, const char *grpname, void *object) +static void IniSaveSettings(IniFile *ini, const SettingDesc *sd, const char *grpname, void *object, bool) { IniGroup *group_def = nullptr, *group; IniItem *item; @@ -831,7 +833,7 @@ static void IniSaveSettingList(IniFile *ini, const char *grpname, StringList &li */ void IniLoadWindowSettings(IniFile *ini, const char *grpname, void *desc) { - IniLoadSettings(ini, _window_settings, grpname, desc); + IniLoadSettings(ini, _window_settings, grpname, desc, false); } /** @@ -842,7 +844,7 @@ void IniLoadWindowSettings(IniFile *ini, const char *grpname, void *desc) */ void IniSaveWindowSettings(IniFile *ini, const char *grpname, void *desc) { - IniSaveSettings(ini, _window_settings, grpname, desc); + IniSaveSettings(ini, _window_settings, grpname, desc, false); } /** @@ -2091,20 +2093,18 @@ static void GRFSaveConfig(IniFile *ini, const char *grpname, const GRFConfig *li } /* Common handler for saving/loading variables to the configuration file */ -static void HandleSettingDescs(IniFile *ini, SettingDescProc *proc, SettingDescProcList *proc_list, bool basic_settings = true, bool other_settings = true) +static void HandleSettingDescs(IniFile *ini, SettingDescProc *proc, SettingDescProcList *proc_list, bool only_startup = false) { - if (basic_settings) { - proc(ini, (const SettingDesc*)_misc_settings, "misc", nullptr); + proc(ini, (const SettingDesc*)_misc_settings, "misc", nullptr, only_startup); #if defined(_WIN32) && !defined(DEDICATED) - proc(ini, (const SettingDesc*)_win32_settings, "win32", nullptr); + proc(ini, (const SettingDesc*)_win32_settings, "win32", nullptr, only_startup); #endif /* _WIN32 */ - } - if (other_settings) { - proc(ini, _settings, "patches", &_settings_newgame); - proc(ini, _currency_settings,"currency", &_custom_currency); - proc(ini, _company_settings, "company", &_settings_client.company); + proc(ini, _settings, "patches", &_settings_newgame, only_startup); + proc(ini, _currency_settings,"currency", &_custom_currency, only_startup); + proc(ini, _company_settings, "company", &_settings_client.company, only_startup); + if (!only_startup) { proc_list(ini, "server_bind_addresses", _network_bind_list); proc_list(ini, "servers", _network_host_list); proc_list(ini, "bans", _network_ban_list); @@ -2120,24 +2120,24 @@ static IniFile *IniLoadConfig() /** * Load the values from the configuration files - * @param minimal Load the minimal amount of the configuration to "bootstrap" the blitter and such. + * @param startup Load the minimal amount of the configuration to "bootstrap" the blitter and such. */ -void LoadFromConfig(bool minimal) +void LoadFromConfig(bool startup) { IniFile *ini = IniLoadConfig(); - if (!minimal) ResetCurrencies(false); // Initialize the array of currencies, without preserving the custom one + if (!startup) ResetCurrencies(false); // Initialize the array of currencies, without preserving the custom one /* Load basic settings only during bootstrap, load other settings not during bootstrap */ - HandleSettingDescs(ini, IniLoadSettings, IniLoadSettingList, minimal, !minimal); + HandleSettingDescs(ini, IniLoadSettings, IniLoadSettingList, startup); - if (!minimal) { + if (!startup) { _grfconfig_newgame = GRFLoadConfig(ini, "newgrf", false); _grfconfig_static = GRFLoadConfig(ini, "newgrf-static", true); AILoadConfig(ini, "ai_players"); GameLoadConfig(ini, "game_scripts"); PrepareOldDiffCustom(); - IniLoadSettings(ini, _gameopt_settings, "gameopt", &_settings_newgame); + IniLoadSettings(ini, _gameopt_settings, "gameopt", &_settings_newgame, false); HandleOldDiffCustom(false); ValidateSettings(); diff --git a/src/settings_gui.cpp b/src/settings_gui.cpp index 5567ffaaab..2f80932196 100644 --- a/src/settings_gui.cpp +++ b/src/settings_gui.cpp @@ -16,7 +16,6 @@ #include "network/network.h" #include "town.h" #include "settings_internal.h" -#include "newgrf_townname.h" #include "strings_func.h" #include "window_func.h" #include "string_func.h" @@ -44,12 +43,6 @@ extern void FlushDeparturesWindowTextCaches(); -static const StringID _driveside_dropdown[] = { - STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_LEFT, - STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_RIGHT, - INVALID_STRING_ID -}; - static const StringID _autosave_dropdown[] = { STR_GAME_OPTIONS_AUTOSAVE_DROPDOWN_OFF, STR_GAME_OPTIONS_AUTOSAVE_DROPDOWN_EVERY_1_MONTH, @@ -60,6 +53,7 @@ static const StringID _autosave_dropdown[] = { }; static const StringID _gui_zoom_dropdown[] = { + STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_AUTO, STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_NORMAL, STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_2X_ZOOM, STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_4X_ZOOM, @@ -67,42 +61,17 @@ static const StringID _gui_zoom_dropdown[] = { }; static const StringID _font_zoom_dropdown[] = { + STR_GAME_OPTIONS_FONT_ZOOM_DROPDOWN_AUTO, STR_GAME_OPTIONS_FONT_ZOOM_DROPDOWN_NORMAL, STR_GAME_OPTIONS_FONT_ZOOM_DROPDOWN_2X_ZOOM, STR_GAME_OPTIONS_FONT_ZOOM_DROPDOWN_4X_ZOOM, INVALID_STRING_ID, }; -int _nb_orig_names = SPECSTR_TOWNNAME_LAST - SPECSTR_TOWNNAME_START + 1; ///< Number of original town names. -static StringID *_grf_names = nullptr; ///< Pointer to town names defined by NewGRFs. -static int _nb_grf_names = 0; ///< Number of town names defined by NewGRFs. - static Dimension _circle_size; ///< Dimension of the circle +/- icon. This is here as not all users are within the class of the settings window. static const void *ResolveVariableAddress(const GameSettings *settings_ptr, const SettingDesc *sd); -/** Allocate memory for the NewGRF town names. */ -void InitGRFTownGeneratorNames() -{ - free(_grf_names); - _grf_names = GetGRFTownNameList(); - _nb_grf_names = 0; - for (StringID *s = _grf_names; *s != INVALID_STRING_ID; s++) _nb_grf_names++; -} - -/** - * Get a town name. - * @param town_name Number of the wanted town name. - * @return Name of the town as string ID. - */ -static inline StringID TownName(int town_name) -{ - if (town_name < _nb_orig_names) return STR_GAME_OPTIONS_TOWN_NAME_ORIGINAL_ENGLISH + town_name; - town_name -= _nb_orig_names; - if (town_name < _nb_grf_names) return _grf_names[town_name]; - return STR_UNDEFINED; -} - /** * Get index of the current screen resolution. * @return Index of the current screen resolution if it is a known resolution, _resolutions.size() otherwise. @@ -220,51 +189,6 @@ struct GameOptionsWindow : Window { break; } - case WID_GO_ROADSIDE_DROPDOWN: { // Setup road-side dropdown - *selected_index = this->opt->vehicle.road_side; - const StringID *items = _driveside_dropdown; - uint disabled = 0; - - /* You can only change the drive side if you are in the menu or ingame with - * no vehicles present. In a networking game only the server can change it */ - extern bool RoadVehiclesAreBuilt(); - if ((_game_mode != GM_MENU && RoadVehiclesAreBuilt()) || (_networking && !(_network_server || _network_settings_access))) { - disabled = ~(1 << this->opt->vehicle.road_side); // disable the other value - } - - for (uint i = 0; *items != INVALID_STRING_ID; items++, i++) { - list.emplace_back(new DropDownListStringItem(*items, i, HasBit(disabled, i))); - } - break; - } - - case WID_GO_TOWNNAME_DROPDOWN: { // Setup townname dropdown - *selected_index = this->opt->game_creation.town_name; - - int enabled_item = (_game_mode == GM_MENU || Town::GetNumItems() == 0) ? -1 : *selected_index; - - /* Add and sort newgrf townnames generators */ - for (int i = 0; i < _nb_grf_names; i++) { - int result = _nb_orig_names + i; - list.emplace_back(new DropDownListStringItem(_grf_names[i], result, enabled_item != result && enabled_item >= 0)); - } - std::sort(list.begin(), list.end(), DropDownListStringItem::NatSortFunc); - - size_t newgrf_size = list.size(); - /* Insert newgrf_names at the top of the list */ - if (newgrf_size > 0) { - list.emplace_back(new DropDownListItem(-1, false)); // separator line - newgrf_size++; - } - - /* Add and sort original townnames generators */ - for (int i = 0; i < _nb_orig_names; i++) { - list.emplace_back(new DropDownListStringItem(STR_GAME_OPTIONS_TOWN_NAME_ORIGINAL_ENGLISH + i, i, enabled_item != i && enabled_item >= 0)); - } - std::sort(list.begin() + newgrf_size, list.end(), DropDownListStringItem::NatSortFunc); - break; - } - case WID_GO_AUTOSAVE_DROPDOWN: { // Setup autosave dropdown *selected_index = _settings_client.gui.autosave; const StringID *items = _autosave_dropdown; @@ -293,16 +217,16 @@ struct GameOptionsWindow : Window { break; case WID_GO_GUI_ZOOM_DROPDOWN: { - *selected_index = ZOOM_LVL_OUT_4X - _gui_zoom; + *selected_index = _gui_zoom_cfg != ZOOM_LVL_CFG_AUTO ? ZOOM_LVL_OUT_4X - _gui_zoom + 1 : 0; const StringID *items = _gui_zoom_dropdown; for (int i = 0; *items != INVALID_STRING_ID; items++, i++) { - list.emplace_back(new DropDownListStringItem(*items, i, _settings_client.gui.zoom_min > ZOOM_LVL_OUT_4X - i)); + list.emplace_back(new DropDownListStringItem(*items, i, i != 0 && _settings_client.gui.zoom_min > ZOOM_LVL_OUT_4X - i + 1)); } break; } case WID_GO_FONT_ZOOM_DROPDOWN: { - *selected_index = ZOOM_LVL_OUT_4X - _font_zoom; + *selected_index = _font_zoom_cfg != ZOOM_LVL_CFG_AUTO ? ZOOM_LVL_OUT_4X - _font_zoom + 1 : 0; const StringID *items = _font_zoom_dropdown; for (int i = 0; *items != INVALID_STRING_ID; items++, i++) { list.emplace_back(new DropDownListStringItem(*items, i, false)); @@ -330,13 +254,11 @@ struct GameOptionsWindow : Window { { switch (widget) { case WID_GO_CURRENCY_DROPDOWN: SetDParam(0, _currency_specs[this->opt->locale.currency].name); break; - case WID_GO_ROADSIDE_DROPDOWN: SetDParam(0, STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_LEFT + this->opt->vehicle.road_side); break; - case WID_GO_TOWNNAME_DROPDOWN: SetDParam(0, TownName(this->opt->game_creation.town_name)); break; case WID_GO_AUTOSAVE_DROPDOWN: SetDParam(0, _autosave_dropdown[_settings_client.gui.autosave]); break; case WID_GO_LANG_DROPDOWN: SetDParamStr(0, _current_language->own_name); break; case WID_GO_RESOLUTION_DROPDOWN: SetDParam(0, GetCurRes() == _resolutions.size() ? STR_GAME_OPTIONS_RESOLUTION_OTHER : SPECSTR_RESOLUTION_START + GetCurRes()); break; - case WID_GO_GUI_ZOOM_DROPDOWN: SetDParam(0, _gui_zoom_dropdown[ZOOM_LVL_OUT_4X - _gui_zoom]); break; - case WID_GO_FONT_ZOOM_DROPDOWN: SetDParam(0, _font_zoom_dropdown[ZOOM_LVL_OUT_4X - _font_zoom]); break; + case WID_GO_GUI_ZOOM_DROPDOWN: SetDParam(0, _gui_zoom_dropdown[_gui_zoom_cfg != ZOOM_LVL_CFG_AUTO ? ZOOM_LVL_OUT_4X - _gui_zoom_cfg + 1 : 0]); break; + case WID_GO_FONT_ZOOM_DROPDOWN: SetDParam(0, _font_zoom_dropdown[_font_zoom_cfg != ZOOM_LVL_CFG_AUTO ? ZOOM_LVL_OUT_4X - _font_zoom_cfg + 1 : 0]); break; case WID_GO_BASE_GRF_DROPDOWN: SetDParamStr(0, BaseGraphics::GetUsedSet()->name.c_str()); break; case WID_GO_BASE_GRF_STATUS: SetDParam(0, BaseGraphics::GetUsedSet()->GetNumInvalid()); break; case WID_GO_BASE_SFX_DROPDOWN: SetDParamStr(0, BaseSounds::GetUsedSet()->name.c_str()); break; @@ -502,22 +424,6 @@ struct GameOptionsWindow : Window { ReInitAllWindows(); break; - case WID_GO_ROADSIDE_DROPDOWN: // Road side - if (this->opt->vehicle.road_side != index) { // only change if setting changed - uint i; - if (GetSettingFromName("vehicle.road_side", &i) == nullptr) NOT_REACHED(); - SetSettingValue(i, index); - MarkWholeScreenDirty(); - } - break; - - case WID_GO_TOWNNAME_DROPDOWN: // Town names - if (_game_mode == GM_MENU || Town::GetNumItems() == 0) { - this->opt->game_creation.town_name = index; - SetWindowDirty(WC_GAME_OPTIONS, WN_GAME_OPTIONS_GAME_OPTIONS); - } - break; - case WID_GO_AUTOSAVE_DROPDOWN: // Autosave options _settings_client.gui.autosave = index; this->SetDirty(); @@ -539,26 +445,34 @@ struct GameOptionsWindow : Window { } break; - case WID_GO_GUI_ZOOM_DROPDOWN: - GfxClearSpriteCache(); - _gui_zoom = (ZoomLevel)(ZOOM_LVL_OUT_4X - index); - UpdateCursorSize(); - UpdateAllVirtCoords(); - FixTitleGameZoom(); - ReInitAllWindows(); - FlushDeparturesWindowTextCaches(); + case WID_GO_GUI_ZOOM_DROPDOWN: { + int8 new_zoom = index > 0 ? ZOOM_LVL_OUT_4X - index + 1 : ZOOM_LVL_CFG_AUTO; + if (new_zoom != _gui_zoom_cfg) { + GfxClearSpriteCache(); + _gui_zoom_cfg = new_zoom; + UpdateGUIZoom(); + UpdateCursorSize(); + UpdateAllVirtCoords(); + FixTitleGameZoom(); + ReInitAllWindows(); + FlushDeparturesWindowTextCaches(); + } break; + } - case WID_GO_FONT_ZOOM_DROPDOWN: - extern void UpdateFontHeightCache(); - GfxClearSpriteCache(); - _font_zoom = (ZoomLevel)(ZOOM_LVL_OUT_4X - index); - ClearFontCache(); - UpdateFontHeightCache(); - LoadStringWidthTable(); - UpdateAllVirtCoords(); - FlushDeparturesWindowTextCaches(); + case WID_GO_FONT_ZOOM_DROPDOWN: { + int8 new_zoom = index > 0 ? ZOOM_LVL_OUT_4X - index + 1 : ZOOM_LVL_CFG_AUTO; + if (new_zoom != _font_zoom_cfg) { + GfxClearSpriteCache(); + _font_zoom_cfg = new_zoom; + UpdateGUIZoom(); + ClearFontCache(); + LoadStringWidthTable(); + UpdateAllVirtCoords(); + FlushDeparturesWindowTextCaches(); + } break; + } case WID_GO_BASE_GRF_DROPDOWN: this->SetMediaSet(index); @@ -606,9 +520,6 @@ static const NWidgetPart _nested_game_options_widgets[] = { NWidget(WWT_PANEL, COLOUR_GREY, WID_GO_BACKGROUND), SetPIP(6, 6, 10), NWidget(NWID_HORIZONTAL), SetPIP(10, 10, 10), NWidget(NWID_VERTICAL), SetPIP(0, 6, 0), - NWidget(WWT_FRAME, COLOUR_GREY), SetDataTip(STR_GAME_OPTIONS_ROAD_VEHICLES_FRAME, STR_NULL), - NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_GO_ROADSIDE_DROPDOWN), SetMinimalSize(150, 12), SetDataTip(STR_BLACK_STRING, STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_TOOLTIP), SetFill(1, 0), - EndContainer(), NWidget(WWT_FRAME, COLOUR_GREY), SetDataTip(STR_GAME_OPTIONS_AUTOSAVE_FRAME, STR_NULL), NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_GO_AUTOSAVE_DROPDOWN), SetMinimalSize(150, 12), SetDataTip(STR_BLACK_STRING, STR_GAME_OPTIONS_AUTOSAVE_DROPDOWN_TOOLTIP), SetFill(1, 0), EndContainer(), @@ -625,9 +536,6 @@ static const NWidgetPart _nested_game_options_widgets[] = { EndContainer(), NWidget(NWID_VERTICAL), SetPIP(0, 6, 0), - NWidget(WWT_FRAME, COLOUR_GREY), SetDataTip(STR_GAME_OPTIONS_TOWN_NAMES_FRAME, STR_NULL), - NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_GO_TOWNNAME_DROPDOWN), SetMinimalSize(150, 12), SetDataTip(STR_BLACK_STRING, STR_GAME_OPTIONS_TOWN_NAMES_DROPDOWN_TOOLTIP), SetFill(1, 0), - EndContainer(), NWidget(WWT_FRAME, COLOUR_GREY), SetDataTip(STR_GAME_OPTIONS_LANGUAGE, STR_NULL), NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_GO_LANG_DROPDOWN), SetMinimalSize(150, 12), SetDataTip(STR_BLACK_RAW_STRING, STR_GAME_OPTIONS_LANGUAGE_TOOLTIP), SetFill(1, 0), EndContainer(), diff --git a/src/settings_internal.h b/src/settings_internal.h index d28e633e26..ff5f5e2133 100644 --- a/src/settings_internal.h +++ b/src/settings_internal.h @@ -114,6 +114,7 @@ struct SettingDescBase { OnChange *proc; ///< callback procedure for when the value is changed OnConvert *proc_cnvt; ///< callback procedure when loading value mechanism fails SettingCategory cat; ///< assigned categories of the setting + bool startup; ///< setting has to be loaded directly at startup? const SettingDescEnumEntry *enumlist; ///< For SGF_ENUM. The last entry must use STR_NULL }; diff --git a/src/settings_type.h b/src/settings_type.h index cc6f54a7c8..a8a19dc235 100644 --- a/src/settings_type.h +++ b/src/settings_type.h @@ -196,6 +196,7 @@ struct GUISettings : public TimeSettings { byte starting_colour; ///< default color scheme for the company to start a new game with bool show_newgrf_name; ///< Show the name of the NewGRF in the build vehicle window bool auto_remove_signals; ///< automatically remove signals when in the way during rail construction + uint16 refresh_rate; ///< How often we refresh the screen (time between draw-ticks). bool show_vehicle_route_steps; ///< when a window related to a specific vehicle is focused, show route steps bool show_vehicle_list_company_colour; ///< show the company colour of vehicles which have an owner different to the owner of the vehicle list bool enable_single_veh_shared_order_gui; ///< enable showing a single vehicle in the shared order GUI window diff --git a/src/settingsgen/CMakeLists.txt b/src/settingsgen/CMakeLists.txt index 69b2092017..43d5284e62 100644 --- a/src/settingsgen/CMakeLists.txt +++ b/src/settingsgen/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.9) if (NOT HOST_BINARY_DIR) project(settingsgen) diff --git a/src/ship_cmd.cpp b/src/ship_cmd.cpp index e158746ce3..c896b3ec09 100644 --- a/src/ship_cmd.cpp +++ b/src/ship_cmd.cpp @@ -340,11 +340,11 @@ void Ship::UpdateDeltaXY() } /** - * Test-procedure for HasVehicleOnPos to check for a ship. + * Test-procedure for HasVehicleOnPos to check for any ships which are visible and not stopped by the player. */ -static Vehicle *EnsureNoVisibleShipProc(Vehicle *v, void *data) +static Vehicle *EnsureNoMovingShipProc(Vehicle *v, void *data) { - return (v->vehstatus & VS_HIDDEN) == 0 ? v : nullptr; + return (v->vehstatus & (VS_HIDDEN | VS_STOPPED)) == 0 ? v : nullptr; } static bool CheckShipLeaveDepot(Ship *v) @@ -370,7 +370,7 @@ static bool CheckShipLeaveDepot(Ship *v) /* Don't leave depot if another vehicle is already entering/leaving */ /* This helps avoid CPU load if many ships are set to start at the same time */ - if (HasVehicleOnPos(v->tile, VEH_SHIP, nullptr, &EnsureNoVisibleShipProc)) return true; + if (HasVehicleOnPos(v->tile, VEH_SHIP, nullptr, &EnsureNoMovingShipProc)) return true; TileIndex tile = v->tile; Axis axis = GetShipDepotAxis(tile); diff --git a/src/station_cmd.cpp b/src/station_cmd.cpp index e3a1c21b25..3deb48c8c9 100644 --- a/src/station_cmd.cpp +++ b/src/station_cmd.cpp @@ -4379,7 +4379,7 @@ uint MoveGoodsToStation(CargoID type, uint amount, SourceType source_type, Sourc first_station = st; continue; } - if (used_stations.empty()) { + if (used_stations.empty()) { used_stations.reserve(2); used_stations.emplace_back(std::make_pair(first_station, 0)); } @@ -4424,9 +4424,9 @@ uint MoveGoodsToStation(CargoID type, uint amount, SourceType source_type, Sourc moving += p.second; } - /* If there is some cargo left due to rounding issues distribute it among the best rated stations. */ + /* If there is some cargo left due to rounding issues distribute it among the best rated stations. */ if (amount > moving) { - std::sort(used_stations.begin(), used_stations.end(), [type] (const StationInfo &a, const StationInfo &b) { + std::sort(used_stations.begin(), used_stations.end(), [type](const StationInfo &a, const StationInfo &b) { return b.first->goods[type].rating < a.first->goods[type].rating; }); diff --git a/src/stdafx.h b/src/stdafx.h index 2a0eb6af22..20c3efb9ce 100644 --- a/src/stdafx.h +++ b/src/stdafx.h @@ -153,54 +153,44 @@ /* Stuff for MSVC */ #if defined(_MSC_VER) # pragma once -# ifdef _WIN64 - /* No 64-bit Windows below XP, so we can safely assume it as the target platform. */ -# define NTDDI_VERSION NTDDI_WINXP // Windows XP -# define _WIN32_WINNT 0x501 // Windows XP -# define _WIN32_WINDOWS 0x501 // Windows XP -# define WINVER 0x0501 // Windows XP -# define _WIN32_IE_ 0x0600 // 6.0 (XP+) -# else - /* Define a win32 target platform, to override defaults of the SDK - * We need to define NTDDI version for Vista SDK, but win2k is minimum */ - #define NTDDI_VERSION NTDDI_WIN2K // Windows 2000 - #define _WIN32_WINNT 0x0500 // Windows 2000 - #define _WIN32_WINDOWS 0x400 // Windows 95 - #define WINVER 0x0400 // Windows NT 4.0 / Windows 95 - #define _WIN32_IE_ 0x0401 // 4.01 (win98 and NT4SP5+) - #endif - #define NOMINMAX // Disable min/max macros in windows.h. - - #pragma warning(disable: 4244) // 'conversion' conversion from 'type1' to 'type2', possible loss of data - #pragma warning(disable: 4761) // integral size mismatch in argument : conversion supplied - #pragma warning(disable: 4200) // nonstandard extension used : zero-sized array in struct/union - #pragma warning(disable: 4355) // 'this' : used in base member initializer list - - #if (_MSC_VER < 1400) // MSVC 2005 safety checks - #error "Only MSVC 2005 or higher are supported. MSVC 2003 and earlier are not! Upgrade your compiler." - #endif /* (_MSC_VER < 1400) */ - #pragma warning(disable: 4291) // no matching operator delete found; memory will not be freed if initialization throws an exception (reason: our overloaded functions never throw an exception) - #pragma warning(disable: 4996) // 'function': was declared deprecated - #pragma warning(disable: 6308) // code analyzer: 'realloc' might return null pointer: assigning null pointer to 't_ptr', which is passed as an argument to 'realloc', will cause the original memory block to be leaked - #pragma warning(disable: 6011) // code analyzer: Dereferencing nullptr pointer 'pfGetAddrInfo': Lines: 995, 996, 998, 999, 1001 - #pragma warning(disable: 6326) // code analyzer: potential comparison of a constant with another constant - #pragma warning(disable: 6031) // code analyzer: Return value ignored: 'ReadFile' - #pragma warning(disable: 6255) // code analyzer: _alloca indicates failure by raising a stack overflow exception. Consider using _malloca instead - #pragma warning(disable: 6246) // code analyzer: Local declaration of 'statspec' hides declaration of the same name in outer scope. For additional information, see previous declaration at ... - - #if (_MSC_VER == 1500) // Addresses item #13 on http://blogs.msdn.com/b/vcblog/archive/2008/08/11/tr1-fixes-in-vc9-sp1.aspx, for Visual Studio 2008 - #define _DO_NOT_DECLARE_INTERLOCKED_INTRINSICS_IN_MEMORY - #include - #endif - - #include // alloca() - #define NORETURN __declspec(noreturn) - #if (_MSC_VER < 1900) - #define inline __forceinline - #endif - - #define CDECL _cdecl - #define WARN_FORMAT(string, args) +# define NTDDI_VERSION NTDDI_WINXP // Windows XP +# define _WIN32_WINNT 0x501 // Windows XP +# define _WIN32_WINDOWS 0x501 // Windows XP +# define WINVER 0x0501 // Windows XP +# define _WIN32_IE_ 0x0600 // 6.0 (XP+) + +# define NOMINMAX // Disable min/max macros in windows.h. + +# pragma warning(disable: 4244) // 'conversion' conversion from 'type1' to 'type2', possible loss of data +# pragma warning(disable: 4761) // integral size mismatch in argument : conversion supplied +# pragma warning(disable: 4200) // nonstandard extension used : zero-sized array in struct/union +# pragma warning(disable: 4355) // 'this' : used in base member initializer list + +# if (_MSC_VER < 1400) // MSVC 2005 safety checks +# error "Only MSVC 2005 or higher are supported. MSVC 2003 and earlier are not! Upgrade your compiler." +# endif /* (_MSC_VER < 1400) */ +# pragma warning(disable: 4291) // no matching operator delete found; memory will not be freed if initialization throws an exception (reason: our overloaded functions never throw an exception) +# pragma warning(disable: 4996) // 'function': was declared deprecated +# pragma warning(disable: 6308) // code analyzer: 'realloc' might return null pointer: assigning null pointer to 't_ptr', which is passed as an argument to 'realloc', will cause the original memory block to be leaked +# pragma warning(disable: 6011) // code analyzer: Dereferencing NULL pointer 'pfGetAddrInfo': Lines: 995, 996, 998, 999, 1001 +# pragma warning(disable: 6326) // code analyzer: potential comparison of a constant with another constant +# pragma warning(disable: 6031) // code analyzer: Return value ignored: 'ReadFile' +# pragma warning(disable: 6255) // code analyzer: _alloca indicates failure by raising a stack overflow exception. Consider using _malloca instead +# pragma warning(disable: 6246) // code analyzer: Local declaration of 'statspec' hides declaration of the same name in outer scope. For additional information, see previous declaration at ... + +# if (_MSC_VER == 1500) // Addresses item #13 on http://blogs.msdn.com/b/vcblog/archive/2008/08/11/tr1-fixes-in-vc9-sp1.aspx, for Visual Studio 2008 +# define _DO_NOT_DECLARE_INTERLOCKED_INTRINSICS_IN_MEMORY +# include +# endif + +# include // alloca() +# define NORETURN __declspec(noreturn) +# if (_MSC_VER < 1900) +# define inline __forceinline +# endif + +# define CDECL _cdecl +# define WARN_FORMAT(string, args) # ifndef __clang__ # define FINAL sealed # else diff --git a/src/strgen/CMakeLists.txt b/src/strgen/CMakeLists.txt index 08c1223350..490f675f2e 100644 --- a/src/strgen/CMakeLists.txt +++ b/src/strgen/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.9) if (NOT HOST_BINARY_DIR) project(strgen) diff --git a/src/strings.cpp b/src/strings.cpp index 8ea36ca624..e215a38e89 100644 --- a/src/strings.cpp +++ b/src/strings.cpp @@ -2393,12 +2393,11 @@ class LanguagePackGlyphSearcher : public MissingGlyphSearcher { void SetFontNames(FreeTypeSettings *settings, const char *font_name, const void *os_data) override { -#if defined(WITH_FREETYPE) || defined(_WIN32) +#if defined(WITH_FREETYPE) || defined(_WIN32) || defined(WITH_COCOA) strecpy(settings->small.font, font_name, lastof(settings->small.font)); strecpy(settings->medium.font, font_name, lastof(settings->medium.font)); strecpy(settings->large.font, font_name, lastof(settings->large.font)); - free(settings->medium.os_handle); // Only free one, they are all the same pointer. settings->small.os_handle = os_data; settings->medium.os_handle = os_data; settings->large.os_handle = os_data; @@ -2424,7 +2423,7 @@ void CheckForMissingGlyphs(bool base_font, MissingGlyphSearcher *searcher) static LanguagePackGlyphSearcher pack_searcher; if (searcher == nullptr) searcher = &pack_searcher; bool bad_font = !base_font || searcher->FindMissingGlyphs(); -#if defined(WITH_FREETYPE) || defined(_WIN32) +#if defined(WITH_FREETYPE) || defined(_WIN32) || defined(WITH_COCOA) if (bad_font) { /* We found an unprintable character... lets try whether we can find * a fallback font that can print the characters in the current language. */ @@ -2436,9 +2435,6 @@ void CheckForMissingGlyphs(bool base_font, MissingGlyphSearcher *searcher) bad_font = !SetFallbackFont(&_freetype, _langpack.langpack->isocode, _langpack.langpack->winlangid, searcher); - free(_freetype.mono.os_handle); - free(_freetype.medium.os_handle); - memcpy(&_freetype, &backup, sizeof(backup)); if (!bad_font) { diff --git a/src/table/company_settings.ini b/src/table/company_settings.ini index 898bbf9077..0f5237860d 100644 --- a/src/table/company_settings.ini +++ b/src/table/company_settings.ini @@ -17,8 +17,8 @@ static const SettingDesc _company_settings[] = { [post-amble] }; [templates] -SDT_BOOL = SDT_BOOL($base, $var, $flags, $guiflags, $def, $str, $strhelp, $strval, $proc, $from, $to, $cat, $extver, $patxname), -SDT_VAR = SDT_VAR($base, $var, $type, $flags, $guiflags, $def, $min, $max, $interval, $str, $strhelp, $strval, $proc, $from, $to, $cat, $extver, $patxname, $orderproc), +SDT_BOOL = SDT_BOOL($base, $var, $flags, $guiflags, $def, $str, $strhelp, $strval, $proc, $from, $to, $cat, $startup, $extver, $patxname), +SDT_VAR = SDT_VAR($base, $var, $type, $flags, $guiflags, $def, $min, $max, $interval, $str, $strhelp, $strval, $proc, $from, $to, $cat, $startup, $extver, $patxname, $orderproc), SDT_NULL = SDT_NULL($length, $from, $to, $extver), SDT_END = SDT_END() @@ -34,6 +34,7 @@ load = nullptr from = SL_MIN_VERSION to = SL_MAX_VERSION cat = SC_ADVANCED +startup = false extver = SlXvFeatureTest() patxname = nullptr orderproc = nullptr diff --git a/src/table/currency_settings.ini b/src/table/currency_settings.ini index 99ada243ee..15c5d17e21 100644 --- a/src/table/currency_settings.ini +++ b/src/table/currency_settings.ini @@ -9,9 +9,9 @@ static const SettingDesc _currency_settings[] = { [post-amble] }; [templates] -SDT_VAR = SDT_VAR($base, $var, $type, $flags, $guiflags, $def, $min, $max, $interval, $str, $strhelp, $strval, $proc, $from, $to, $cat, $extver, nullptr, $orderproc), -SDT_CHR = SDT_CHR($base, $var, $flags, $guiflags, $def, $str, $strhelp, $strval, $proc, $from, $to, $cat, $extver, nullptr), -SDT_STR = SDT_STR($base, $var, $type, $flags, $guiflags, $def, $str, $strhelp, $strval, $proc, $from, $to, $cat, $extver, nullptr), +SDT_VAR = SDT_VAR($base, $var, $type, $flags, $guiflags, $def, $min, $max, $interval, $str, $strhelp, $strval, $proc, $from, $to, $cat, $startup, $extver, nullptr, $orderproc), +SDT_CHR = SDT_CHR($base, $var, $flags, $guiflags, $def, $str, $strhelp, $strval, $proc, $from, $to, $cat, $startup, $extver, nullptr), +SDT_STR = SDT_STR($base, $var, $type, $flags, $guiflags, $def, $str, $strhelp, $strval, $proc, $from, $to, $cat, $startup, $extver, nullptr), SDT_END = SDT_END() [defaults] @@ -26,6 +26,7 @@ load = nullptr from = SL_MIN_VERSION to = SL_MAX_VERSION cat = SC_ADVANCED +startup = false extver = SlXvFeatureTest() orderproc = nullptr diff --git a/src/table/gameopt_settings.ini b/src/table/gameopt_settings.ini index 1da9e4f871..76bf70ac4e 100644 --- a/src/table/gameopt_settings.ini +++ b/src/table/gameopt_settings.ini @@ -38,13 +38,13 @@ static const SettingDesc _gameopt_settings[] = { [post-amble] }; [templates] -SDTG_GENERAL = SDTG_GENERAL($name, $sdt_cmd, $sle_cmd, $type, $flags, $guiflags, $var, $length, $def, $min, $max, $interval, $full, $str, $strhelp, $strval, $proc, $from, $to, $cat, $extver, nullptr), -SDTG_VAR = SDTG_VAR($name, $type, $flags, $guiflags, $var, $def, $min, $max, $interval, $str, $strhelp, $strval, $proc, $from, $to, $cat, $extver, nullptr, $orderproc), +SDTG_GENERAL = SDTG_GENERAL($name, $sdt_cmd, $sle_cmd, $type, $flags, $guiflags, $var, $length, $def, $min, $max, $interval, $full, $str, $strhelp, $strval, $proc, $from, $to, $cat, $startup, $extver, nullptr), +SDTG_VAR = SDTG_VAR($name, $type, $flags, $guiflags, $var, $def, $min, $max, $interval, $str, $strhelp, $strval, $proc, $from, $to, $cat, $startup, $extver, nullptr, $orderproc), SDT_NULL = SDT_NULL($length, $from, $to, $extver), -SDTC_OMANY = SDTC_OMANY( $var, $type, $flags, $guiflags, $def, $max, $full, $str, $strhelp, $strval, $proc, $from, $to, $cat, $extver, nullptr), -SDTG_OMANY = SDTG_OMANY($name, $type, $flags, $guiflags, $var, $def, $max, $full, $str, $strhelp, $strval, $proc, $from, $to, $cat, $extver, nullptr), -SDT_OMANY = SDT_OMANY($base, $var, $type, $flags, $guiflags, $def, $max, $full, $str, $strhelp, $strval, $proc, $from, $to, $load, $cat, $extver, nullptr), -SDT_VAR = SDT_VAR($base, $var, $type, $flags, $guiflags, $def, $min, $max, $interval, $str, $strhelp, $strval, $proc, $from, $to, $cat, $extver, nullptr, $orderproc), +SDTC_OMANY = SDTC_OMANY( $var, $type, $flags, $guiflags, $def, $max, $full, $str, $strhelp, $strval, $proc, $from, $to, $cat, $startup, $extver, nullptr), +SDTG_OMANY = SDTG_OMANY($name, $type, $flags, $guiflags, $var, $def, $max, $full, $str, $strhelp, $strval, $proc, $from, $to, $cat, $startup, $extver, nullptr), +SDT_OMANY = SDT_OMANY($base, $var, $type, $flags, $guiflags, $def, $max, $full, $str, $strhelp, $strval, $proc, $from, $to, $load, $cat, $startup, $extver, nullptr), +SDT_VAR = SDT_VAR($base, $var, $type, $flags, $guiflags, $def, $min, $max, $interval, $str, $strhelp, $strval, $proc, $from, $to, $cat, $startup, $extver, nullptr, $orderproc), SDT_END = SDT_END() [defaults] @@ -59,6 +59,7 @@ load = nullptr from = SL_MIN_VERSION to = SL_MAX_VERSION cat = SC_ADVANCED +startup = false extver = SlXvFeatureTest() orderproc = nullptr diff --git a/src/table/genland.h b/src/table/genland.h index 53801e2f68..7bc6294ec7 100644 --- a/src/table/genland.h +++ b/src/table/genland.h @@ -10,121 +10,121 @@ #define M(x, y) {x, y} static const TileIndexDiffC _make_desert_or_rainforest_data[] = { - M(-5, -6), - M(-4, -6), - M(-3, -6), - M(-2, -6), - M(-1, -6), + M(-5, -5), + M(-4, -5), + M(-3, -5), + M(-2, -5), + M(-1, -5), M( 0, -5), M( 1, -5), M( 2, -5), M( 3, -5), M( 4, -5), M( 5, -5), - M(-5, -5), - M(-4, -5), - M(-3, -5), - M(-2, -5), - M(-1, -5), + M(-5, -4), + M(-4, -4), + M(-3, -4), + M(-2, -4), + M(-1, -4), M( 0, -4), M( 1, -4), M( 2, -4), M( 3, -4), M( 4, -4), M( 5, -4), - M(-5, -4), - M(-4, -4), - M(-3, -4), - M(-2, -4), - M(-1, -4), + M(-5, -3), + M(-4, -3), + M(-3, -3), + M(-2, -3), + M(-1, -3), M( 0, -3), M( 1, -3), M( 2, -3), M( 3, -3), M( 4, -3), M( 5, -3), - M(-5, -3), - M(-4, -3), - M(-3, -3), - M(-2, -3), - M(-1, -3), + M(-5, -2), + M(-4, -2), + M(-3, -2), + M(-2, -2), + M(-1, -2), M( 0, -2), M( 1, -2), M( 2, -2), M( 3, -2), M( 4, -2), M( 5, -2), - M(-5, -2), - M(-4, -2), - M(-3, -2), - M(-2, -2), - M(-1, -2), + M(-5, -1), + M(-4, -1), + M(-3, -1), + M(-2, -1), + M(-1, -1), M( 0, -1), M( 1, -1), M( 2, -1), M( 3, -1), M( 4, -1), M( 5, -1), - M(-5, -1), - M(-4, -1), - M(-3, -1), - M(-2, -1), - M(-1, -1), + M(-5, 0), + M(-4, 0), + M(-3, 0), + M(-2, 0), + M(-1, 0), M( 0, 0), M( 1, 0), M( 2, 0), M( 3, 0), M( 4, 0), M( 5, 0), - M(-5, 0), - M(-4, 0), - M(-3, 0), - M(-2, 0), - M(-1, 0), + M(-5, 1), + M(-4, 1), + M(-3, 1), + M(-2, 1), + M(-1, 1), M( 0, 1), M( 1, 1), M( 2, 1), M( 3, 1), M( 4, 1), M( 5, 1), - M(-5, 1), - M(-4, 1), - M(-3, 1), - M(-2, 1), - M(-1, 1), + M(-5, 2), + M(-4, 2), + M(-3, 2), + M(-2, 2), + M(-1, 2), M( 0, 2), M( 1, 2), M( 2, 2), M( 3, 2), M( 4, 2), M( 5, 2), - M(-5, 2), - M(-4, 2), - M(-3, 2), - M(-2, 2), - M(-1, 2), + M(-5, 3), + M(-4, 3), + M(-3, 3), + M(-2, 3), + M(-1, 3), M( 0, 3), M( 1, 3), M( 2, 3), M( 3, 3), M( 4, 3), M( 5, 3), - M(-5, 3), - M(-4, 3), - M(-3, 3), - M(-2, 3), - M(-1, 3), + M(-5, 4), + M(-4, 4), + M(-3, 4), + M(-2, 4), + M(-1, 4), M( 0, 4), M( 1, 4), M( 2, 4), M( 3, 4), M( 4, 4), M( 5, 4), - M(-5, 4), - M(-4, 4), - M(-3, 4), - M(-2, 4), - M(-1, 4), + M(-5, 5), + M(-4, 5), + M(-3, 5), + M(-2, 5), + M(-1, 5), M( 0, 5), M( 1, 5), M( 2, 5), @@ -132,31 +132,31 @@ static const TileIndexDiffC _make_desert_or_rainforest_data[] = { M( 4, 5), M( 5, 5), M( 6, -3), - M(-6, -4), - M(-3, 5), - M(-3, -7), - M( 6, -2), M(-6, -3), - M(-2, 5), - M(-2, -7), - M( 6, -1), + M(-3, 6), + M(-3, -6), + M( 6, -2), M(-6, -2), - M(-1, 5), - M(-1, -7), - M( 6, 0), + M(-2, 6), + M(-2, -6), + M( 6, -1), M(-6, -1), + M(-1, 6), + M(-1, -6), + M( 6, 0), + M(-6, 0), M( 0, 6), M( 0, -6), M( 6, 1), - M(-6, 0), + M(-6, 1), M( 1, 6), M( 1, -6), M( 6, 2), - M(-6, 1), + M(-6, 2), M( 2, 6), M( 2, -6), M( 6, 3), - M(-6, 2), + M(-6, 3), M( 3, 6), M( 3, -6) }; diff --git a/src/table/misc_settings.ini b/src/table/misc_settings.ini index dd6c49c0ca..6c52703390 100644 --- a/src/table/misc_settings.ini +++ b/src/table/misc_settings.ini @@ -9,17 +9,21 @@ extern std::string _config_language_file; static const char *_support8bppmodes = "no|system|hardware"; +#ifdef WITH_COCOA +extern bool _allow_hidpi_window; +#endif + static const SettingDescGlobVarList _misc_settings[] = { [post-amble] }; [templates] -SDTG_LIST = SDTG_LIST($name, $type, $length, $flags, $guiflags, $var, $def, $str, $strhelp, $strval, $proc, $from, $to, $cat, $extver, nullptr), -SDTG_MMANY = SDTG_MMANY($name, $type, $flags, $guiflags, $var, $def, $full, $str, $strhelp, $strval, $proc, $from, $to, $cat, $extver, nullptr), -SDTG_OMANY = SDTG_OMANY($name, $type, $flags, $guiflags, $var, $def, $max, $full, $str, $strhelp, $strval, $proc, $from, $to, $cat, $extver, nullptr), -SDTG_STR = SDTG_STR($name, $type, $flags, $guiflags, $var, $def, $str, $strhelp, $strval, $proc, $from, $to, $cat, $extver, nullptr), -SDTG_SSTR = SDTG_SSTR($name, $type, $flags, $guiflags, $var, $def, $str, $strhelp, $strval, $proc, $from, $to, $cat, $extver, nullptr), -SDTG_BOOL = SDTG_BOOL($name, $flags, $guiflags, $var, $def, $str, $strhelp, $strval, $proc, $from, $to, $cat, $extver, nullptr), -SDTG_VAR = SDTG_VAR($name, $type, $flags, $guiflags, $var, $def, $min, $max, $interval, $str, $strhelp, $strval, $proc, $from, $to, $cat, $extver, nullptr, $orderproc), +SDTG_LIST = SDTG_LIST($name, $type, $length, $flags, $guiflags, $var, $def, $str, $strhelp, $strval, $proc, $from, $to, $cat, $startup, $extver, nullptr), +SDTG_MMANY = SDTG_MMANY($name, $type, $flags, $guiflags, $var, $def, $full, $str, $strhelp, $strval, $proc, $from, $to, $cat, $startup, $extver, nullptr), +SDTG_OMANY = SDTG_OMANY($name, $type, $flags, $guiflags, $var, $def, $max, $full, $str, $strhelp, $strval, $proc, $from, $to, $cat, $startup, $extver, nullptr), +SDTG_STR = SDTG_STR($name, $type, $flags, $guiflags, $var, $def, $str, $strhelp, $strval, $proc, $from, $to, $cat, $startup, $extver, nullptr), +SDTG_SSTR = SDTG_SSTR($name, $type, $flags, $guiflags, $var, $def, $str, $strhelp, $strval, $proc, $from, $to, $cat, $startup, $extver, nullptr), +SDTG_BOOL = SDTG_BOOL($name, $flags, $guiflags, $var, $def, $str, $strhelp, $strval, $proc, $from, $to, $cat, $startup, $extver, nullptr), +SDTG_VAR = SDTG_VAR($name, $type, $flags, $guiflags, $var, $def, $min, $max, $interval, $str, $strhelp, $strval, $proc, $from, $to, $cat, $startup, $extver, nullptr, $orderproc), SDTG_END = SDTG_END() [defaults] @@ -34,6 +38,7 @@ load = nullptr from = SL_MIN_VERSION to = SL_MAX_VERSION cat = SC_ADVANCED +startup = true extver = SlXvFeatureTest() orderproc = nullptr @@ -309,22 +314,28 @@ cat = SC_EXPERT [SDTG_VAR] name = ""gui_zoom"" -type = SLE_UINT8 -var = _gui_zoom -def = ZOOM_LVL_OUT_4X -min = ZOOM_LVL_MIN +type = SLE_INT8 +var = _gui_zoom_cfg +def = ZOOM_LVL_CFG_AUTO +min = ZOOM_LVL_CFG_AUTO max = ZOOM_LVL_OUT_4X cat = SC_BASIC [SDTG_VAR] name = ""font_zoom"" -type = SLE_UINT8 -var = _font_zoom -def = ZOOM_LVL_OUT_4X -min = ZOOM_LVL_MIN +type = SLE_INT8 +var = _font_zoom_cfg +def = ZOOM_LVL_CFG_AUTO +min = ZOOM_LVL_CFG_AUTO max = ZOOM_LVL_OUT_4X cat = SC_BASIC +[SDTG_BOOL] +ifdef = WITH_COCOA +name = ""allow_hidpi"" +var = _allow_hidpi_window +def = true + [SDTG_VAR] name = ""zoning_overlay_inner"" type = SLE_UINT8 diff --git a/src/table/settings.h.preamble b/src/table/settings.h.preamble index 473ce99b44..cba64b7110 100644 --- a/src/table/settings.h.preamble +++ b/src/table/settings.h.preamble @@ -56,102 +56,102 @@ static size_t ConvertLandscape(const char *value); * on the appropriate macro. */ -#define NSD_GENERAL(name, def, cmd, guiflags, min, max, interval, many, str, strhelp, strval, proc, load, cat, enumlist)\ - {name, (const void*)(size_t)(def), cmd, guiflags, min, max, interval, many, str, strhelp, strval, proc, load, cat, enumlist} +#define NSD_GENERAL(name, def, cmd, guiflags, min, max, interval, many, str, strhelp, strval, proc, load, cat, startup, enumlist)\ + {name, (const void*)(size_t)(def), cmd, guiflags, min, max, interval, many, str, strhelp, strval, proc, load, cat, startup, enumlist} /* Macros for various objects to go in the configuration file. * This section is for global variables */ -#define SDTG_GENERAL2(name, sdt_cmd, sle_cmd, type, flags, guiflags, var, length, def, min, max, interval, full, str, strhelp, strval, proc, from, to, cat, extver, patxname, orderproc, enumlist)\ - {NSD_GENERAL(name, def, sdt_cmd, guiflags, min, max, interval, full, str, strhelp, strval, proc, nullptr, cat, enumlist), SLEG_GENERAL_X(sle_cmd, var, type | flags, length, from, to, extver), patxname, SettingsXref(), orderproc} +#define SDTG_GENERAL2(name, sdt_cmd, sle_cmd, type, flags, guiflags, var, length, def, min, max, interval, full, str, strhelp, strval, proc, from, to, cat, startup, extver, patxname, orderproc, enumlist)\ + {NSD_GENERAL(name, def, sdt_cmd, guiflags, min, max, interval, full, str, strhelp, strval, proc, nullptr, cat, startup, enumlist), SLEG_GENERAL_X(sle_cmd, var, type | flags, length, from, to, extver), patxname, SettingsXref(), orderproc} -#define SDTG_GENERAL(name, sdt_cmd, sle_cmd, type, flags, guiflags, var, length, def, min, max, interval, full, str, strhelp, strval, proc, from, to, cat, extver, patxname)\ - SDTG_GENERAL2(name, sdt_cmd, sle_cmd, type, flags, guiflags, var, length, def, min, max, interval, full, str, strhelp, strval, proc, from, to, cat, extver, patxname, nullptr, nullptr) +#define SDTG_GENERAL(name, sdt_cmd, sle_cmd, type, flags, guiflags, var, length, def, min, max, interval, full, str, strhelp, strval, proc, from, to, cat, startup, extver, patxname)\ + SDTG_GENERAL2(name, sdt_cmd, sle_cmd, type, flags, guiflags, var, length, def, min, max, interval, full, str, strhelp, strval, proc, from, to, cat, startup, extver, patxname, nullptr, nullptr) -#define SDTG_VAR(name, type, flags, guiflags, var, def, min, max, interval, str, strhelp, strval, proc, from, to, cat, extver, patxname, orderproc)\ - SDTG_GENERAL2(name, SDT_NUMX, SL_VAR, type, flags, guiflags, var, 0, def, min, max, interval, nullptr, str, strhelp, strval, proc, from, to, cat, extver, patxname, orderproc, nullptr) +#define SDTG_VAR(name, type, flags, guiflags, var, def, min, max, interval, str, strhelp, strval, proc, from, to, cat, startup, extver, patxname, orderproc)\ + SDTG_GENERAL2(name, SDT_NUMX, SL_VAR, type, flags, guiflags, var, 0, def, min, max, interval, nullptr, str, strhelp, strval, proc, from, to, cat, startup, extver, patxname, orderproc, nullptr) -#define SDTG_ENUM(name, type, flags, guiflags, var, def, str, strhelp, proc, from, to, cat, extver, patxname, enumlist)\ - SDTG_GENERAL2(name, SDT_NUMX, SL_VAR, type, flags, guiflags | SGF_ENUM, var, 0, def, 0, 0, 0, nullptr, str, strhelp, STR_NULL, proc, nullptr, from, to, cat, extver, patxname, nullptr, enumlist) +#define SDTG_ENUM(name, type, flags, guiflags, var, def, str, strhelp, proc, from, to, cat, startup, extver, patxname, enumlist)\ + SDTG_GENERAL2(name, SDT_NUMX, SL_VAR, type, flags, guiflags | SGF_ENUM, var, 0, def, 0, 0, 0, nullptr, str, strhelp, STR_NULL, proc, nullptr, from, to, cat, startup, extver, patxname, nullptr, enumlist) -#define SDTG_BOOL(name, flags, guiflags, var, def, str, strhelp, strval, proc, from, to, cat, extver, patxname)\ - SDTG_GENERAL(name, SDT_BOOLX, SL_VAR, SLE_BOOL, flags, guiflags, var, 0, def, 0, 1, 0, nullptr, str, strhelp, strval, proc, from, to, cat, extver, patxname) +#define SDTG_BOOL(name, flags, guiflags, var, def, str, strhelp, strval, proc, from, to, cat, startup, extver, patxname)\ + SDTG_GENERAL(name, SDT_BOOLX, SL_VAR, SLE_BOOL, flags, guiflags, var, 0, def, 0, 1, 0, nullptr, str, strhelp, strval, proc, from, to, cat, startup, extver, patxname) -#define SDTG_LIST(name, type, length, flags, guiflags, var, def, str, strhelp, strval, proc, from, to, cat, extver, patxname)\ - SDTG_GENERAL(name, SDT_INTLIST, SL_ARR, type, flags, guiflags, var, length, def, 0, 0, 0, nullptr, str, strhelp, strval, proc, from, to, cat, extver, patxname) +#define SDTG_LIST(name, type, length, flags, guiflags, var, def, str, strhelp, strval, proc, from, to, cat, startup, extver, patxname)\ + SDTG_GENERAL(name, SDT_INTLIST, SL_ARR, type, flags, guiflags, var, length, def, 0, 0, 0, nullptr, str, strhelp, strval, proc, from, to, cat, startup, extver, patxname) -#define SDTG_SSTR(name, type, flags, guiflags, var, def, str, strhelp, strval, proc, from, to, cat, extver, patxname)\ - SDTG_GENERAL(name, SDT_STDSTRING, SL_STDSTR, type, flags, guiflags, var, sizeof(var), def, 0, 0, 0, nullptr, str, strhelp, strval, proc, from, to, cat, extver, patxname) +#define SDTG_SSTR(name, type, flags, guiflags, var, def, str, strhelp, strval, proc, from, to, cat, startup, extver, patxname)\ + SDTG_GENERAL(name, SDT_STDSTRING, SL_STDSTR, type, flags, guiflags, var, sizeof(var), def, 0, 0, 0, nullptr, str, strhelp, strval, proc, from, to, cat, startup, extver, patxname) -#define SDTG_STR(name, type, flags, guiflags, var, def, str, strhelp, strval, proc, from, to, cat, extver, patxname)\ - SDTG_GENERAL(name, SDT_STRING, SL_STR, type, flags, guiflags, var, sizeof(var), def, 0, 0, 0, nullptr, str, strhelp, strval, proc, from, to, cat, extver, patxname) +#define SDTG_STR(name, type, flags, guiflags, var, def, str, strhelp, strval, proc, from, to, cat, startup, extver, patxname)\ + SDTG_GENERAL(name, SDT_STRING, SL_STR, type, flags, guiflags, var, sizeof(var), def, 0, 0, 0, nullptr, str, strhelp, strval, proc, from, to, cat, startup, extver, patxname) -#define SDTG_OMANY(name, type, flags, guiflags, var, def, max, full, str, strhelp, strval, proc, from, to, cat, extver, patxname)\ - SDTG_GENERAL(name, SDT_ONEOFMANY, SL_VAR, type, flags, guiflags, var, 0, def, 0, max, 0, full, str, strhelp, strval, proc, from, to, cat, extver, patxname) +#define SDTG_OMANY(name, type, flags, guiflags, var, def, max, full, str, strhelp, strval, proc, from, to, cat, startup, extver, patxname)\ + SDTG_GENERAL(name, SDT_ONEOFMANY, SL_VAR, type, flags, guiflags, var, 0, def, 0, max, 0, full, str, strhelp, strval, proc, from, to, cat, startup, extver, patxname) -#define SDTG_MMANY(name, type, flags, guiflags, var, def, full, str, strhelp, strval, proc, from, to, cat, extver, patxname)\ - SDTG_GENERAL(name, SDT_MANYOFMANY, SL_VAR, type, flags, guiflags, var, 0, def, 0, 0, 0, full, str, strhelp, strval, proc, from, to, cat, extver, patxname) +#define SDTG_MMANY(name, type, flags, guiflags, var, def, full, str, strhelp, strval, proc, from, to, cat, startup, extver, patxname)\ + SDTG_GENERAL(name, SDT_MANYOFMANY, SL_VAR, type, flags, guiflags, var, 0, def, 0, 0, 0, full, str, strhelp, strval, proc, from, to, cat, startup, extver, patxname) #define SDTG_NULL(length, from, to, extver)\ - {{"", nullptr, SDT_NUMX, SGF_NONE, 0, 0, 0, nullptr, STR_NULL, STR_NULL, STR_NULL, nullptr, nullptr, SC_NONE, nullptr}, SLEG_NULL_X(length, from, to, extver), nullptr, SettingsXref(), nullptr} + {{"", nullptr, SDT_NUMX, SGF_NONE, 0, 0, 0, nullptr, STR_NULL, STR_NULL, STR_NULL, nullptr, nullptr, SC_NONE, false, nullptr}, SLEG_NULL_X(length, from, to, extver), nullptr, SettingsXref(), nullptr} -#define SDTG_END() {{nullptr, nullptr, SDT_NUMX, SGF_NONE, 0, 0, 0, nullptr, STR_NULL, STR_NULL, STR_NULL, nullptr, nullptr, SC_NONE, nullptr}, SLEG_END(), nullptr, SettingsXref(), nullptr} +#define SDTG_END() {{nullptr, nullptr, SDT_NUMX, SGF_NONE, 0, 0, 0, nullptr, STR_NULL, STR_NULL, STR_NULL, nullptr, nullptr, SC_NONE, false, nullptr}, SLEG_END(), nullptr, SettingsXref(), nullptr} /* Macros for various objects to go in the configuration file. * This section is for structures where their various members are saved */ -#define SDT_GENERAL2(name, sdt_cmd, sle_cmd, type, flags, guiflags, base, var, length, def, min, max, interval, full, str, strhelp, strval, proc, load, from, to, cat, extver, patxname, orderproc, enumlist)\ - {NSD_GENERAL(name, def, sdt_cmd, guiflags, min, max, interval, full, str, strhelp, strval, proc, load, cat, enumlist), SLE_GENERAL_X(sle_cmd, base, var, type | flags, length, from, to, extver), patxname, SettingsXref(), orderproc} +#define SDT_GENERAL2(name, sdt_cmd, sle_cmd, type, flags, guiflags, base, var, length, def, min, max, interval, full, str, strhelp, strval, proc, load, from, to, cat, startup, extver, patxname, orderproc, enumlist)\ + {NSD_GENERAL(name, def, sdt_cmd, guiflags, min, max, interval, full, str, strhelp, strval, proc, load, cat, startup, enumlist), SLE_GENERAL_X(sle_cmd, base, var, type | flags, length, from, to, extver), patxname, SettingsXref(), orderproc} -#define SDT_GENERAL(name, sdt_cmd, sle_cmd, type, flags, guiflags, base, var, length, def, min, max, interval, full, str, strhelp, strval, proc, load, from, to, cat, extver, patxname)\ - SDT_GENERAL2(name, sdt_cmd, sle_cmd, type, flags, guiflags, base, var, length, def, min, max, interval, full, str, strhelp, strval, proc, load, from, to, cat, extver, patxname, nullptr, nullptr) +#define SDT_GENERAL(name, sdt_cmd, sle_cmd, type, flags, guiflags, base, var, length, def, min, max, interval, full, str, strhelp, strval, proc, load, from, to, cat, startup, extver, patxname)\ + SDT_GENERAL2(name, sdt_cmd, sle_cmd, type, flags, guiflags, base, var, length, def, min, max, interval, full, str, strhelp, strval, proc, load, from, to, cat, startup, extver, patxname, nullptr, nullptr) -#define SDT_VAR(base, var, type, flags, guiflags, def, min, max, interval, str, strhelp, strval, proc, from, to, cat, extver, patxname, orderproc)\ - SDT_GENERAL2(#var, SDT_NUMX, SL_VAR, type, flags, guiflags, base, var, 1, def, min, max, interval, nullptr, str, strhelp, strval, proc, nullptr, from, to, cat, extver, patxname, orderproc, nullptr) +#define SDT_VAR(base, var, type, flags, guiflags, def, min, max, interval, str, strhelp, strval, proc, from, to, cat, startup, extver, patxname, orderproc)\ + SDT_GENERAL2(#var, SDT_NUMX, SL_VAR, type, flags, guiflags, base, var, 1, def, min, max, interval, nullptr, str, strhelp, strval, proc, nullptr, from, to, cat, startup, extver, patxname, orderproc, nullptr) -#define SDT_ENUM(base, var, type, flags, guiflags, def, str, strhelp, proc, from, to, cat, extver, patxname, enumlist)\ - SDT_GENERAL2(#var, SDT_NUMX, SL_VAR, type, flags, guiflags | SGF_ENUM, base, var, 1, def, 0, 0, 0, nullptr, str, strhelp, STR_NULL, proc, nullptr, from, to, cat, extver, patxname, nullptr, enumlist) +#define SDT_ENUM(base, var, type, flags, guiflags, def, str, strhelp, proc, from, to, cat, startup, extver, patxname, enumlist)\ + SDT_GENERAL2(#var, SDT_NUMX, SL_VAR, type, flags, guiflags | SGF_ENUM, base, var, 1, def, 0, 0, 0, nullptr, str, strhelp, STR_NULL, proc, nullptr, from, to, cat, startup, extver, patxname, nullptr, enumlist) -#define SDT_BOOL(base, var, flags, guiflags, def, str, strhelp, strval, proc, from, to, cat, extver, patxname)\ - SDT_GENERAL(#var, SDT_BOOLX, SL_VAR, SLE_BOOL, flags, guiflags, base, var, 1, def, 0, 1, 0, nullptr, str, strhelp, strval, proc, nullptr, from, to, cat, extver, patxname) +#define SDT_BOOL(base, var, flags, guiflags, def, str, strhelp, strval, proc, from, to, cat, startup, extver, patxname)\ + SDT_GENERAL(#var, SDT_BOOLX, SL_VAR, SLE_BOOL, flags, guiflags, base, var, 1, def, 0, 1, 0, nullptr, str, strhelp, strval, proc, nullptr, from, to, cat, startup, extver, patxname) -#define SDT_LIST(base, var, type, flags, guiflags, def, str, strhelp, strval, proc, from, to, cat, extver, patxname)\ - SDT_GENERAL(#var, SDT_INTLIST, SL_ARR, type, flags, guiflags, base, var, lengthof(((base*)8)->var), def, 0, 0, 0, nullptr, str, strhelp, strval, proc, nullptr, from, to, cat, extver, patxname) +#define SDT_LIST(base, var, type, flags, guiflags, def, str, strhelp, strval, proc, from, to, cat, startup, extver, patxname)\ + SDT_GENERAL(#var, SDT_INTLIST, SL_ARR, type, flags, guiflags, base, var, lengthof(((base*)8)->var), def, 0, 0, 0, nullptr, str, strhelp, strval, proc, nullptr, from, to, cat, startup, extver, patxname) -#define SDT_STR(base, var, type, flags, guiflags, def, str, strhelp, strval, proc, from, to, cat, extver, patxname)\ - SDT_GENERAL(#var, SDT_STRING, SL_STR, type, flags, guiflags, base, var, sizeof(((base*)8)->var), def, 0, 0, 0, nullptr, str, strhelp, strval, proc, nullptr, from, to, cat, extver, patxname) +#define SDT_STR(base, var, type, flags, guiflags, def, str, strhelp, strval, proc, from, to, cat, startup, extver, patxname)\ + SDT_GENERAL(#var, SDT_STRING, SL_STR, type, flags, guiflags, base, var, sizeof(((base*)8)->var), def, 0, 0, 0, nullptr, str, strhelp, strval, proc, nullptr, from, to, cat, startup, extver, patxname) -#define SDT_CHR(base, var, flags, guiflags, def, str, strhelp, strval, proc, from, to, cat, extver, patxname)\ - SDT_GENERAL(#var, SDT_STRING, SL_VAR, SLE_CHAR, flags, guiflags, base, var, 1, def, 0, 0, 0, nullptr, str, strhelp, strval, proc, nullptr, from, to, cat, extver, patxname) +#define SDT_CHR(base, var, flags, guiflags, def, str, strhelp, strval, proc, from, to, cat, startup, extver, patxname)\ + SDT_GENERAL(#var, SDT_STRING, SL_VAR, SLE_CHAR, flags, guiflags, base, var, 1, def, 0, 0, 0, nullptr, str, strhelp, strval, proc, nullptr, from, to, cat, startup, extver, patxname) -#define SDT_OMANY(base, var, type, flags, guiflags, def, max, full, str, strhelp, strval, proc, from, to, load, cat, extver, patxname)\ - SDT_GENERAL(#var, SDT_ONEOFMANY, SL_VAR, type, flags, guiflags, base, var, 1, def, 0, max, 0, full, str, strhelp, strval, proc, load, from, to, cat, extver, patxname) +#define SDT_OMANY(base, var, type, flags, guiflags, def, max, full, str, strhelp, strval, proc, from, to, load, cat, startup, extver, patxname)\ + SDT_GENERAL(#var, SDT_ONEOFMANY, SL_VAR, type, flags, guiflags, base, var, 1, def, 0, max, 0, full, str, strhelp, strval, proc, load, from, to, cat, startup, extver, patxname) -#define SDT_MMANY(base, var, type, flags, guiflags, def, full, str, proc, strhelp, strval, from, to, cat, extver, patxname)\ - SDT_GENERAL(#var, SDT_MANYOFMANY, SL_VAR, type, flags, guiflags, base, var, 1, def, 0, 0, 0, full, str, strhelp, strval, proc, nullptr, from, to, cat, extver, patxname) +#define SDT_MMANY(base, var, type, flags, guiflags, def, full, str, proc, strhelp, strval, from, to, cat, startup, extver, patxname)\ + SDT_GENERAL(#var, SDT_MANYOFMANY, SL_VAR, type, flags, guiflags, base, var, 1, def, 0, 0, 0, full, str, strhelp, strval, proc, nullptr, from, to, cat, startup, extver, patxname) #define SDT_NULL(length, from, to, extver)\ - {{"", nullptr, SDT_NUMX, SGF_NONE, 0, 0, 0, nullptr, STR_NULL, STR_NULL, STR_NULL, nullptr, nullptr, SC_NONE, nullptr}, SLE_CONDNULL_X(length, from, to, extver), nullptr, SettingsXref(), nullptr} + {{"", nullptr, SDT_NUMX, SGF_NONE, 0, 0, 0, nullptr, STR_NULL, STR_NULL, STR_NULL, nullptr, nullptr, SC_NONE, false, nullptr}, SLE_CONDNULL_X(length, from, to, extver), nullptr, SettingsXref(), nullptr} -#define SDTC_VAR(var, type, flags, guiflags, def, min, max, interval, str, strhelp, strval, proc, from, to, cat, extver, patxname, orderproc)\ - SDTG_GENERAL2(#var, SDT_NUMX, SL_VAR, type, flags, guiflags, _settings_client.var, 1, def, min, max, interval, nullptr, str, strhelp, strval, proc, from, to, cat, extver, patxname, orderproc, nullptr) +#define SDTC_VAR(var, type, flags, guiflags, def, min, max, interval, str, strhelp, strval, proc, from, to, cat, startup, extver, patxname, orderproc)\ + SDTG_GENERAL2(#var, SDT_NUMX, SL_VAR, type, flags, guiflags, _settings_client.var, 1, def, min, max, interval, nullptr, str, strhelp, strval, proc, from, to, cat, startup, extver, patxname, orderproc, nullptr) -#define SDTC_ENUM(var, type, flags, guiflags, def, str, strhelp, proc, from, to, cat, extver, patxname, enumlist)\ - SDTG_GENERAL2(#var, SDT_NUMX, SL_VAR, type, flags, guiflags | SGF_ENUM, _settings_client.var, 1, def, 0, 0, 0, nullptr, str, strhelp, STR_NULL, proc, nullptr, from, to, cat, extver, patxname, nullptr, enumlist) +#define SDTC_ENUM(var, type, flags, guiflags, def, str, strhelp, proc, from, to, cat, startup, extver, patxname, enumlist)\ + SDTG_GENERAL2(#var, SDT_NUMX, SL_VAR, type, flags, guiflags | SGF_ENUM, _settings_client.var, 1, def, 0, 0, 0, nullptr, str, strhelp, STR_NULL, proc, nullptr, from, to, cat, startup, extver, patxname, nullptr, enumlist) -#define SDTC_BOOL(var, flags, guiflags, def, str, strhelp, strval, proc, from, to, cat, extver, patxname)\ - SDTG_GENERAL(#var, SDT_BOOLX, SL_VAR, SLE_BOOL, flags, guiflags, _settings_client.var, 1, def, 0, 1, 0, nullptr, str, strhelp, strval, proc, from, to, cat, extver, patxname) +#define SDTC_BOOL(var, flags, guiflags, def, str, strhelp, strval, proc, from, to, cat, startup, extver, patxname)\ + SDTG_GENERAL(#var, SDT_BOOLX, SL_VAR, SLE_BOOL, flags, guiflags, _settings_client.var, 1, def, 0, 1, 0, nullptr, str, strhelp, strval, proc, from, to, cat, startup, extver, patxname) -#define SDTC_LIST(var, type, flags, guiflags, def, str, strhelp, strval, proc, from, to, cat, extver, patxname)\ - SDTG_GENERAL(#var, SDT_INTLIST, SL_ARR, type, flags, guiflags, _settings_client.var, lengthof(_settings_client.var), def, 0, 0, 0, nullptr, str, strhelp, strval, proc, from, to, cat, extver, patxname) +#define SDTC_LIST(var, type, flags, guiflags, def, str, strhelp, strval, proc, from, to, cat, startup, extver, patxname)\ + SDTG_GENERAL(#var, SDT_INTLIST, SL_ARR, type, flags, guiflags, _settings_client.var, lengthof(_settings_client.var), def, 0, 0, 0, nullptr, str, strhelp, strval, proc, from, to, cat, startup, extver, patxname) -#define SDTC_STR(var, type, flags, guiflags, def, str, strhelp, strval, proc, from, to, cat, extver, patxname)\ - SDTG_GENERAL(#var, SDT_STRING, SL_STR, type, flags, guiflags, _settings_client.var, sizeof(_settings_client.var), def, 0, 0, 0, nullptr, str, strhelp, strval, proc, from, to, cat, extver, patxname) +#define SDTC_STR(var, type, flags, guiflags, def, str, strhelp, strval, proc, from, to, cat, startup, extver, patxname)\ + SDTG_GENERAL(#var, SDT_STRING, SL_STR, type, flags, guiflags, _settings_client.var, sizeof(_settings_client.var), def, 0, 0, 0, nullptr, str, strhelp, strval, proc, from, to, cat, startup, extver, patxname) -#define SDTC_OMANY(var, type, flags, guiflags, def, max, full, str, strhelp, strval, proc, from, to, cat, extver, patxname)\ - SDTG_GENERAL(#var, SDT_ONEOFMANY, SL_VAR, type, flags, guiflags, _settings_client.var, 1, def, 0, max, 0, full, str, strhelp, strval, proc, from, to, cat, extver, patxname) +#define SDTC_OMANY(var, type, flags, guiflags, def, max, full, str, strhelp, strval, proc, from, to, cat, startup, extver, patxname)\ + SDTG_GENERAL(#var, SDT_ONEOFMANY, SL_VAR, type, flags, guiflags, _settings_client.var, 1, def, 0, max, 0, full, str, strhelp, strval, proc, from, to, cat, startup, extver, patxname) #define SDT_XREF(from, to, extver, xref, xrefcvt)\ - {{"", nullptr, SDT_NUMX, SGF_NONE, 0, 0, 0, nullptr, STR_NULL, STR_NULL, STR_NULL, nullptr, nullptr, SC_NONE, nullptr}, SLE_CONDNULL_X(0, from, to, extver), nullptr, SettingsXref(xref, xrefcvt), nullptr} + {{"", nullptr, SDT_NUMX, SGF_NONE, 0, 0, 0, nullptr, STR_NULL, STR_NULL, STR_NULL, nullptr, nullptr, SC_NONE, false, nullptr}, SLE_CONDNULL_X(0, from, to, extver), nullptr, SettingsXref(xref, xrefcvt), nullptr} -#define SDT_END() {{nullptr, nullptr, SDT_NUMX, SGF_NONE, 0, 0, 0, nullptr, STR_NULL, STR_NULL, STR_NULL, nullptr, nullptr, SC_NONE, nullptr}, SLE_END(), nullptr, SettingsXref(), nullptr} +#define SDT_END() {{nullptr, nullptr, SDT_NUMX, SGF_NONE, 0, 0, 0, nullptr, STR_NULL, STR_NULL, STR_NULL, nullptr, nullptr, SC_NONE, false, nullptr}, SLE_END(), nullptr, SettingsXref(), nullptr} diff --git a/src/table/settings.ini b/src/table/settings.ini index a342e80357..ad93b78bf9 100644 --- a/src/table/settings.ini +++ b/src/table/settings.ini @@ -133,25 +133,25 @@ const SettingDesc _settings[] = { [post-amble] }; [templates] -SDTG_BOOL = SDTG_BOOL($name, $flags, $guiflags, $var, $def, $str, $strhelp, $strval, $proc, $from, $to, $cat, $extver, $patxname), -SDTG_VAR = SDTG_VAR($name, $type, $flags, $guiflags, $var, $def, $min, $max, $interval, $str, $strhelp, $strval, $proc, $from, $to, $cat, $extver, $patxname, $orderproc), -SDTG_ENUM = SDTG_ENUM($name, $type, $flags, $guiflags, $var, $def, $str, $strhelp, $proc, $from, $to, $cat, $extver, $patxname, $enumlist), -SDTG_OMANY = SDTG_OMANY($name, $type, $flags, $guiflags, $var, $def, $max, $full, $str, $strhelp, $strval, $proc, $from, $to, $cat, $extver, $patxname), -SDTC_BOOL = SDTC_BOOL( $var, $flags, $guiflags, $def, $str, $strhelp, $strval, $proc, $from, $to, $cat, $extver, $patxname), -SDTC_LIST = SDTC_LIST( $var, $type, $flags, $guiflags, $def, $str, $strhelp, $strval, $proc, $from, $to, $cat, $extver, $patxname), -SDTC_OMANY = SDTC_OMANY( $var, $type, $flags, $guiflags, $def, $max, $full, $str, $strhelp, $strval, $proc, $from, $to, $cat, $extver, $patxname), -SDTC_STR = SDTC_STR( $var, $type, $flags, $guiflags, $def, $str, $strhelp, $strval, $proc, $from, $to, $cat, $extver, $patxname), -SDTC_VAR = SDTC_VAR( $var, $type, $flags, $guiflags, $def, $min, $max, $interval, $str, $strhelp, $strval, $proc, $from, $to, $cat, $extver, $patxname, $orderproc), -SDT_BOOL = SDT_BOOL($base, $var, $flags, $guiflags, $def, $str, $strhelp, $strval, $proc, $from, $to, $cat, $extver, $patxname), -SDT_OMANY = SDT_OMANY($base, $var, $type, $flags, $guiflags, $def, $max, $full, $str, $strhelp, $strval, $proc, $from, $to, $load, $cat, $extver, $patxname), -SDT_STR = SDT_STR($base, $var, $type, $flags, $guiflags, $def, $str, $strhelp, $strval, $proc, $from, $to, $cat, $extver, $patxname), -SDT_VAR = SDT_VAR($base, $var, $type, $flags, $guiflags, $def, $min, $max, $interval, $str, $strhelp, $strval, $proc, $from, $to, $cat, $extver, $patxname, $orderproc), -SDT_ENUM = SDT_ENUM($base, $var, $type, $flags, $guiflags, $def, $str, $strhelp, $proc, $from, $to, $cat, $extver, $patxname, $enumlist), +SDTG_BOOL = SDTG_BOOL($name, $flags, $guiflags, $var, $def, $str, $strhelp, $strval, $proc, $from, $to, $cat, $startup, $extver, $patxname), +SDTG_VAR = SDTG_VAR($name, $type, $flags, $guiflags, $var, $def, $min, $max, $interval, $str, $strhelp, $strval, $proc, $from, $to, $cat, $startup, $extver, $patxname, $orderproc), +SDTG_ENUM = SDTG_ENUM($name, $type, $flags, $guiflags, $var, $def, $str, $strhelp, $proc, $from, $to, $cat, $startup, $extver, $patxname, $enumlist), +SDTG_OMANY = SDTG_OMANY($name, $type, $flags, $guiflags, $var, $def, $max, $full, $str, $strhelp, $strval, $proc, $from, $to, $cat, $startup, $extver, $patxname), +SDTC_BOOL = SDTC_BOOL( $var, $flags, $guiflags, $def, $str, $strhelp, $strval, $proc, $from, $to, $cat, $startup, $extver, $patxname), +SDTC_LIST = SDTC_LIST( $var, $type, $flags, $guiflags, $def, $str, $strhelp, $strval, $proc, $from, $to, $cat, $startup, $extver, $patxname), +SDTC_OMANY = SDTC_OMANY( $var, $type, $flags, $guiflags, $def, $max, $full, $str, $strhelp, $strval, $proc, $from, $to, $cat, $startup, $extver, $patxname), +SDTC_STR = SDTC_STR( $var, $type, $flags, $guiflags, $def, $str, $strhelp, $strval, $proc, $from, $to, $cat, $startup, $extver, $patxname), +SDTC_VAR = SDTC_VAR( $var, $type, $flags, $guiflags, $def, $min, $max, $interval, $str, $strhelp, $strval, $proc, $from, $to, $cat, $startup, $extver, $patxname, $orderproc), +SDT_BOOL = SDT_BOOL($base, $var, $flags, $guiflags, $def, $str, $strhelp, $strval, $proc, $from, $to, $cat, $startup, $extver, $patxname), +SDT_OMANY = SDT_OMANY($base, $var, $type, $flags, $guiflags, $def, $max, $full, $str, $strhelp, $strval, $proc, $from, $to, $load, $cat, $startup, $extver, $patxname), +SDT_STR = SDT_STR($base, $var, $type, $flags, $guiflags, $def, $str, $strhelp, $strval, $proc, $from, $to, $cat, $startup, $extver, $patxname), +SDT_VAR = SDT_VAR($base, $var, $type, $flags, $guiflags, $def, $min, $max, $interval, $str, $strhelp, $strval, $proc, $from, $to, $cat, $startup, $extver, $patxname, $orderproc), +SDT_ENUM = SDT_ENUM($base, $var, $type, $flags, $guiflags, $def, $str, $strhelp, $proc, $from, $to, $cat, $startup, $extver, $patxname, $enumlist), SDT_NULL = SDT_NULL($length, $from, $to, $extver), SDT_XREF = SDT_XREF( $from, $to, $extver, $xref, $xrefcvt), SDT_END = SDT_END() -SDT_LINKGRAPH_PER_CARGO = SDT_ENUM(GameSettings, linkgraph.distribution_per_cargo[$linkgraph_cargo], SLE_UINT8, $flags | SLF_NOT_IN_CONFIG, $guiflags | SGF_NO_NEWGAME, DT_PER_CARGO_DEFAULT, STR_CONFIG_SETTING_DISTRIBUTION_PER_CARGO, STR_CONFIG_SETTING_DISTRIBUTION_PER_CARGO_HELPTEXT, $proc, $from, $to, SC_EXPERT, SlXvFeatureTest(XSLFTO_AND, XSLFI_LINKGRAPH_MODES), nullptr, _linkgraph_mode_per_cargo), +SDT_LINKGRAPH_PER_CARGO = SDT_ENUM(GameSettings, linkgraph.distribution_per_cargo[$linkgraph_cargo], SLE_UINT8, $flags | SLF_NOT_IN_CONFIG, $guiflags | SGF_NO_NEWGAME, DT_PER_CARGO_DEFAULT, STR_CONFIG_SETTING_DISTRIBUTION_PER_CARGO, STR_CONFIG_SETTING_DISTRIBUTION_PER_CARGO_HELPTEXT, $proc, $from, $to, SC_EXPERT, false, SlXvFeatureTest(XSLFTO_AND, XSLFI_LINKGRAPH_MODES), nullptr, _linkgraph_mode_per_cargo), [defaults] flags = 0 @@ -165,6 +165,7 @@ load = nullptr from = SL_MIN_VERSION to = SL_MAX_VERSION cat = SC_ADVANCED +startup = false extver = SlXvFeatureTest() patxname = nullptr xref = @@ -508,6 +509,7 @@ str = STR_CONFIG_SETTING_ROAD_SIDE strhelp = STR_CONFIG_SETTING_ROAD_SIDE_HELPTEXT strval = STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_LEFT proc = CheckRoadSide +cat = SC_BASIC ; Construction @@ -1794,7 +1796,7 @@ extver = SlXvFeatureTest(XSLFTO_AND, XSLFI_JOKERPP, SL_JOKER_1_24) var = gui.sg_new_nonstop from = SLV_22 to = SLV_93 -def = true +def = false ; station.nonuniform_stations [SDT_NULL] @@ -3518,6 +3520,7 @@ max = 2 str = STR_CONFIG_SETTING_TREE_PLACER strhelp = STR_CONFIG_SETTING_TREE_PLACER_HELPTEXT strval = STR_CONFIG_SETTING_TREE_PLACER_NONE +cat = SC_BASIC [SDT_VAR] base = GameSettings @@ -4071,6 +4074,7 @@ str = STR_CONFIG_SETTING_ZOOM_MIN strhelp = STR_CONFIG_SETTING_ZOOM_MIN_HELPTEXT strval = STR_CONFIG_SETTING_ZOOM_LVL_MIN proc = ZoomMinMaxChanged +startup = true [SDTC_VAR] var = gui.zoom_max @@ -4084,6 +4088,7 @@ str = STR_CONFIG_SETTING_ZOOM_MAX strhelp = STR_CONFIG_SETTING_ZOOM_MAX_HELPTEXT strval = STR_CONFIG_SETTING_ZOOM_LVL_OUT_2X proc = ZoomMinMaxChanged +startup = true [SDTC_BOOL] var = gui.population_in_label @@ -4715,7 +4720,7 @@ strhelp = STR_CONFIG_SETTING_WARN_RESTRICTION_WAIT_VEHICLE_HELPTEXT [SDTC_BOOL] var = gui.new_nonstop flags = SLF_NOT_IN_SAVE | SLF_NO_NETWORK_SYNC -def = false +def = true str = STR_CONFIG_SETTING_NONSTOP_BY_DEFAULT strhelp = STR_CONFIG_SETTING_NONSTOP_BY_DEFAULT_HELPTEXT cat = SC_BASIC @@ -5178,6 +5183,16 @@ def = 100 min = 10 max = 65500 +[SDTC_VAR] +var = gui.refresh_rate +type = SLE_UINT16 +flags = SLF_NOT_IN_SAVE | SLF_NO_NETWORK_SYNC +def = 60 +min = 10 +max = 1000 +cat = SC_EXPERT +startup = true + [SDTC_BOOL] var = sound.news_ticker flags = SLF_NOT_IN_SAVE | SLF_NO_NETWORK_SYNC diff --git a/src/table/win32_settings.ini b/src/table/win32_settings.ini index 5ce56f73dd..dd8719eebc 100644 --- a/src/table/win32_settings.ini +++ b/src/table/win32_settings.ini @@ -15,8 +15,8 @@ static const SettingDescGlobVarList _win32_settings[] = { }; #endif /* _WIN32 */ [templates] -SDTG_BOOL = SDTG_BOOL($name, $flags, $guiflags, $var, $def, $str, $strhelp, $strval, $proc, $from, $to, $cat, $extver, nullptr), -SDTG_VAR = SDTG_VAR($name, $type, $flags, $guiflags, $var, $def, $min, $max, $interval, $str, $strhelp, $strval, $proc, $from, $to, $cat, $extver, nullptr, $orderproc), +SDTG_BOOL = SDTG_BOOL($name, $flags, $guiflags, $var, $def, $str, $strhelp, $strval, $proc, $from, $to, $cat, $startup, $extver, nullptr), +SDTG_VAR = SDTG_VAR($name, $type, $flags, $guiflags, $var, $def, $min, $max, $interval, $str, $strhelp, $strval, $proc, $from, $to, $cat, $startup, $extver, nullptr, $orderproc), SDTG_END = SDTG_END() [defaults] @@ -31,6 +31,7 @@ load = nullptr from = SL_MIN_VERSION to = SL_MAX_VERSION cat = SC_ADVANCED +startup = true extver = SlXvFeatureTest() orderproc = nullptr diff --git a/src/table/window_settings.ini b/src/table/window_settings.ini index 7994833b99..ec2d3ceac0 100644 --- a/src/table/window_settings.ini +++ b/src/table/window_settings.ini @@ -10,8 +10,8 @@ static const SettingDesc _window_settings[] = { [post-amble] }; [templates] -SDT_BOOL = SDT_BOOL($base, $var, $flags, $guiflags, $def, $str, $strhelp, $strval, $proc, $from, $to, $cat, $extver, nullptr), -SDT_VAR = SDT_VAR($base, $var, $type, $flags, $guiflags, $def, $min, $max, $interval, $str, $strhelp, $strval, $proc, $from, $to, $cat, $extver, nullptr, $orderproc), +SDT_BOOL = SDT_BOOL($base, $var, $flags, $guiflags, $def, $str, $strhelp, $strval, $proc, $from, $to, $cat, $startup, $extver, nullptr), +SDT_VAR = SDT_VAR($base, $var, $type, $flags, $guiflags, $def, $min, $max, $interval, $str, $strhelp, $strval, $proc, $from, $to, $cat, $startup, $extver, nullptr, $orderproc), SDT_END = SDT_END() [defaults] @@ -27,6 +27,7 @@ load = nullptr from = SL_MIN_VERSION to = SL_MAX_VERSION cat = SC_ADVANCED +startup = false extver = SlXvFeatureTest() orderproc = nullptr diff --git a/src/textfile_gui.cpp b/src/textfile_gui.cpp index b83e38187f..075898fd71 100644 --- a/src/textfile_gui.cpp +++ b/src/textfile_gui.cpp @@ -194,9 +194,8 @@ void TextfileWindow::SetupScrollbars() /* virtual */ void TextfileWindow::SetFontNames(FreeTypeSettings *settings, const char *font_name, const void *os_data) { -#if defined(WITH_FREETYPE) || defined(_WIN32) +#if defined(WITH_FREETYPE) || defined(_WIN32) || defined(WITH_COCOA) strecpy(settings->mono.font, font_name, lastof(settings->mono.font)); - free(settings->mono.os_handle); settings->mono.os_handle = os_data; #endif } diff --git a/src/town_cmd.cpp b/src/town_cmd.cpp index 817eb49926..00807a1a46 100644 --- a/src/town_cmd.cpp +++ b/src/town_cmd.cpp @@ -1272,7 +1272,7 @@ static bool GrowTownWithRoad(const Town *t, TileIndex tile, RoadBits rcmd) * @param road_dir The direction of the road * @return true if the road can be continued, else false */ -static bool CanRoadContinueIntoNextTile(const Town* t, const TileIndex tile, const DiagDirection road_dir) +static bool CanRoadContinueIntoNextTile(const Town *t, const TileIndex tile, const DiagDirection road_dir) { const int delta = TileOffsByDiagDir(road_dir); // +1 tile in the direction of the road TileIndex next_tile = tile + delta; // The tile beyond which must be connectable to the target tile @@ -1410,7 +1410,7 @@ static bool GrowTownWithBridge(const Town *t, const TileIndex tile, const DiagDi * @param tunnel_dir The valid direction in which to grow a tunnel * @return true if a tunnel has been built, else false */ -static bool GrowTownWithTunnel(const Town* t, const TileIndex tile, const DiagDirection tunnel_dir) +static bool GrowTownWithTunnel(const Town *t, const TileIndex tile, const DiagDirection tunnel_dir) { assert(tunnel_dir < DIAGDIR_END); @@ -2050,15 +2050,10 @@ static void DoCreateTown(Town *t, TileIndex tile, uint32 townnameparts, TownSize t->exclusive_counter = 0; t->statues = 0; - extern int _nb_orig_names; - if (_settings_game.game_creation.town_name < _nb_orig_names) { - /* Original town name */ - t->townnamegrfid = 0; - t->townnametype = SPECSTR_TOWNNAME_START + _settings_game.game_creation.town_name; - } else { - /* Newgrf town name */ - t->townnamegrfid = GetGRFTownNameId(_settings_game.game_creation.town_name - _nb_orig_names); - t->townnametype = GetGRFTownNameType(_settings_game.game_creation.town_name - _nb_orig_names); + { + TownNameParams tnp(_settings_game.game_creation.town_name); + t->townnamegrfid = tnp.grfid; + t->townnametype = tnp.type; } t->townnameparts = townnameparts; diff --git a/src/townname_type.h b/src/townname_type.h index dd95f8f70a..2dc23567f0 100644 --- a/src/townname_type.h +++ b/src/townname_type.h @@ -15,11 +15,14 @@ #include "newgrf_townname.h" #include "town_type.h" +#include "string_type.h" #include #include typedef std::set TownNames; +static constexpr uint BUILTIN_TOWNNAME_GENERATOR_COUNT = SPECSTR_TOWNNAME_LAST - SPECSTR_TOWNNAME_START + 1; ///< Number of built-in town name generators. + /** * Struct holding parameters used to generate town name. * Speeds things up a bit because these values are computed only once per name generation. @@ -34,10 +37,9 @@ struct TownNameParams { */ TownNameParams(byte town_name) { - extern int _nb_orig_names; - bool grf = town_name >= _nb_orig_names; - this->grfid = grf ? GetGRFTownNameId(town_name - _nb_orig_names) : 0; - this->type = grf ? GetGRFTownNameType(town_name - _nb_orig_names) : SPECSTR_TOWNNAME_START + town_name; + bool grf = town_name >= BUILTIN_TOWNNAME_GENERATOR_COUNT; + this->grfid = grf ? GetGRFTownNameId(town_name - BUILTIN_TOWNNAME_GENERATOR_COUNT) : 0; + this->type = grf ? GetGRFTownNameType(town_name - BUILTIN_TOWNNAME_GENERATOR_COUNT) : SPECSTR_TOWNNAME_START + town_name; } TownNameParams(const Town *t); diff --git a/src/tree_cmd.cpp b/src/tree_cmd.cpp index 9e2c1dfddc..a4a0549e7f 100644 --- a/src/tree_cmd.cpp +++ b/src/tree_cmd.cpp @@ -867,7 +867,7 @@ static void TileLoop_Trees(TileIndex tile) break; case 6: // final stage of tree destruction - if (!CanPlantExtraTrees(tile)) { + if (!CanPlantExtraTrees(tile)) { /* if trees can't spread just plant a new one to prevent deforestation */ SetTreeGrowth(tile, 0); } else if (GetTreeCount(tile) > 1) { diff --git a/src/vehicle_gui.cpp b/src/vehicle_gui.cpp index 70c01c273d..b03b90bf19 100644 --- a/src/vehicle_gui.cpp +++ b/src/vehicle_gui.cpp @@ -1984,7 +1984,6 @@ void BaseVehicleListWindow::DrawVehicleListItems(VehicleID selected_vehicle, int } case GB_SHARED_ORDERS: - assert(vehgroup.NumVehicles() > 0); for (int i = 0; i < static_cast(vehgroup.NumVehicles()); ++i) { diff --git a/src/video/allegro_v.cpp b/src/video/allegro_v.cpp index 86c8c25322..294e81a576 100644 --- a/src/video/allegro_v.cpp +++ b/src/video/allegro_v.cpp @@ -23,7 +23,9 @@ #include "../core/random_func.hpp" #include "../core/math_func.hpp" #include "../framerate_type.h" +#include "../progress.h" #include "../thread.h" +#include "../window_func.h" #include "allegro_v.h" #include @@ -235,7 +237,7 @@ bool VideoDriver_Allegro::ClaimMousePointer() return true; } -struct VkMapping { +struct AllegroVkMapping { uint16 vk_from; byte vk_count; byte map_to; @@ -244,7 +246,7 @@ struct VkMapping { #define AS(x, z) {x, 0, z} #define AM(x, y, z, w) {x, y - x, z} -static const VkMapping _vk_mapping[] = { +static const AllegroVkMapping _vk_mapping[] = { /* Pageup stuff + up/down */ AM(KEY_PGUP, KEY_PGDN, WKC_PAGEUP, WKC_PAGEDOWN), AS(KEY_UP, WKC_UP), @@ -302,7 +304,7 @@ static uint32 ConvertAllegroKeyIntoMy(WChar *character) int scancode; int unicode = ureadkey(&scancode); - const VkMapping *map; + const AllegroVkMapping *map; uint key = 0; for (map = _vk_mapping; map != endof(_vk_mapping); ++map) { @@ -445,34 +447,16 @@ void VideoDriver_Allegro::Stop() if (--_allegro_instance_count == 0) allegro_exit(); } -#if defined(UNIX) || defined(__OS2__) -# include /* gettimeofday */ - -static uint32 GetTime() -{ - struct timeval tim; - - gettimeofday(&tim, nullptr); - return tim.tv_usec / 1000 + tim.tv_sec * 1000; -} -#else -static uint32 GetTime() -{ - return GetTickCount(); -} -#endif - - void VideoDriver_Allegro::MainLoop() { - uint32 cur_ticks = GetTime(); - uint32 last_cur_ticks = cur_ticks; - uint32 next_tick = cur_ticks + MILLISECONDS_PER_TICK; + auto cur_ticks = std::chrono::steady_clock::now(); + auto last_realtime_tick = cur_ticks; + auto next_game_tick = cur_ticks; + auto next_draw_tick = cur_ticks; CheckPaletteAnim(); for (;;) { - uint32 prev_cur_ticks = cur_ticks; // to check for wrapping InteractiveRandom(); // randomness PollEvent(); @@ -491,11 +475,33 @@ void VideoDriver_Allegro::MainLoop() _fast_forward = 0; } - cur_ticks = GetTime(); - if (cur_ticks >= next_tick || (_fast_forward && !_pause_mode) || cur_ticks < prev_cur_ticks) { - _realtime_tick += cur_ticks - last_cur_ticks; - last_cur_ticks = cur_ticks; - next_tick = cur_ticks + MILLISECONDS_PER_TICK; + cur_ticks = std::chrono::steady_clock::now(); + + /* If more than a millisecond has passed, increase the _realtime_tick. */ + if (cur_ticks - last_realtime_tick > std::chrono::milliseconds(1)) { + auto delta = std::chrono::duration_cast(cur_ticks - last_realtime_tick); + IncreaseRealtimeTick(delta.count()); + last_realtime_tick += delta; + } + + if (cur_ticks >= next_game_tick || (_fast_forward && !_pause_mode)) { + if (_fast_forward && !_pause_mode) { + next_game_tick = cur_ticks + this->GetGameInterval(); + } else { + next_game_tick += this->GetGameInterval(); + /* Avoid next_game_tick getting behind more and more if it cannot keep up. */ + if (next_game_tick < cur_ticks - ALLOWED_DRIFT * this->GetGameInterval()) next_game_tick = cur_ticks; + } + + GameLoop(); + GameLoopPaletteAnimations(); + } + + /* Prevent drawing when switching mode, as windows can be removed when they should still appear. */ + if (cur_ticks >= next_draw_tick && (_switch_mode == SM_NONE || HasModalProgress())) { + next_draw_tick += this->GetDrawInterval(); + /* Avoid next_draw_tick getting behind more and more if it cannot keep up. */ + if (next_draw_tick < cur_ticks - ALLOWED_DRIFT * this->GetDrawInterval()) next_draw_tick = cur_ticks; bool old_ctrl_pressed = _ctrl_pressed; bool old_shift_pressed = _shift_pressed; @@ -513,17 +519,22 @@ void VideoDriver_Allegro::MainLoop() if (old_ctrl_pressed != _ctrl_pressed) HandleCtrlChanged(); if (old_shift_pressed != _shift_pressed) HandleShiftChanged(); - GameLoop(); - GameLoopPaletteAnimations(); - + InputLoop(); UpdateWindows(); CheckPaletteAnim(); + DrawSurfaceToScreen(); - } else { - CSleep(1); - NetworkDrawChatMessage(); - DrawMouseCursor(); - DrawSurfaceToScreen(); + } + + /* If we are not in fast-forward, create some time between calls to ease up CPU usage. */ + if (!_fast_forward || _pause_mode) { + /* See how much time there is till we have to process the next event, and try to hit that as close as possible. */ + auto next_tick = std::min(next_draw_tick, next_game_tick); + auto now = std::chrono::steady_clock::now(); + + if (next_tick > now) { + std::this_thread::sleep_for(next_tick - now); + } } } } diff --git a/src/video/cocoa/CMakeLists.txt b/src/video/cocoa/CMakeLists.txt index 8bcc6b2e03..968a98a91d 100644 --- a/src/video/cocoa/CMakeLists.txt +++ b/src/video/cocoa/CMakeLists.txt @@ -4,6 +4,5 @@ add_files( cocoa_v.mm cocoa_wnd.h cocoa_wnd.mm - event.mm CONDITION APPLE ) diff --git a/src/video/cocoa/cocoa_keys.h b/src/video/cocoa/cocoa_keys.h index def145d242..dff4f881a9 100644 --- a/src/video/cocoa/cocoa_keys.h +++ b/src/video/cocoa/cocoa_keys.h @@ -131,4 +131,126 @@ #define QZ_IBOOK_DOWN 0x3D #define QZ_IBOOK_UP 0x3E + +struct CocoaVkMapping { + unsigned short vk_from; + byte map_to; +}; + +#define AS(x, z) {x, z} + +static const CocoaVkMapping _vk_mapping[] = { + AS(QZ_BACKQUOTE, WKC_BACKQUOTE), // key left of '1' + AS(QZ_BACKQUOTE2, WKC_BACKQUOTE), // some keyboards have it on another scancode + + /* Pageup stuff + up/down */ + AS(QZ_PAGEUP, WKC_PAGEUP), + AS(QZ_PAGEDOWN, WKC_PAGEDOWN), + + AS(QZ_UP, WKC_UP), + AS(QZ_DOWN, WKC_DOWN), + AS(QZ_LEFT, WKC_LEFT), + AS(QZ_RIGHT, WKC_RIGHT), + + AS(QZ_HOME, WKC_HOME), + AS(QZ_END, WKC_END), + + AS(QZ_INSERT, WKC_INSERT), + AS(QZ_DELETE, WKC_DELETE), + + /* Letters. QZ_[a-z] is not in numerical order so we can't use AM(...) */ + AS(QZ_a, 'A'), + AS(QZ_b, 'B'), + AS(QZ_c, 'C'), + AS(QZ_d, 'D'), + AS(QZ_e, 'E'), + AS(QZ_f, 'F'), + AS(QZ_g, 'G'), + AS(QZ_h, 'H'), + AS(QZ_i, 'I'), + AS(QZ_j, 'J'), + AS(QZ_k, 'K'), + AS(QZ_l, 'L'), + AS(QZ_m, 'M'), + AS(QZ_n, 'N'), + AS(QZ_o, 'O'), + AS(QZ_p, 'P'), + AS(QZ_q, 'Q'), + AS(QZ_r, 'R'), + AS(QZ_s, 'S'), + AS(QZ_t, 'T'), + AS(QZ_u, 'U'), + AS(QZ_v, 'V'), + AS(QZ_w, 'W'), + AS(QZ_x, 'X'), + AS(QZ_y, 'Y'), + AS(QZ_z, 'Z'), + /* Same thing for digits */ + AS(QZ_0, '0'), + AS(QZ_1, '1'), + AS(QZ_2, '2'), + AS(QZ_3, '3'), + AS(QZ_4, '4'), + AS(QZ_5, '5'), + AS(QZ_6, '6'), + AS(QZ_7, '7'), + AS(QZ_8, '8'), + AS(QZ_9, '9'), + + AS(QZ_ESCAPE, WKC_ESC), + AS(QZ_PAUSE, WKC_PAUSE), + AS(QZ_BACKSPACE, WKC_BACKSPACE), + + AS(QZ_SPACE, WKC_SPACE), + AS(QZ_RETURN, WKC_RETURN), + AS(QZ_TAB, WKC_TAB), + + /* Function keys */ + AS(QZ_F1, WKC_F1), + AS(QZ_F2, WKC_F2), + AS(QZ_F3, WKC_F3), + AS(QZ_F4, WKC_F4), + AS(QZ_F5, WKC_F5), + AS(QZ_F6, WKC_F6), + AS(QZ_F7, WKC_F7), + AS(QZ_F8, WKC_F8), + AS(QZ_F9, WKC_F9), + AS(QZ_F10, WKC_F10), + AS(QZ_F11, WKC_F11), + AS(QZ_F12, WKC_F12), + + /* Numeric part */ + AS(QZ_KP0, '0'), + AS(QZ_KP1, '1'), + AS(QZ_KP2, '2'), + AS(QZ_KP3, '3'), + AS(QZ_KP4, '4'), + AS(QZ_KP5, '5'), + AS(QZ_KP6, '6'), + AS(QZ_KP7, '7'), + AS(QZ_KP8, '8'), + AS(QZ_KP9, '9'), + AS(QZ_KP_DIVIDE, WKC_NUM_DIV), + AS(QZ_KP_MULTIPLY, WKC_NUM_MUL), + AS(QZ_KP_MINUS, WKC_NUM_MINUS), + AS(QZ_KP_PLUS, WKC_NUM_PLUS), + AS(QZ_KP_ENTER, WKC_NUM_ENTER), + AS(QZ_KP_PERIOD, WKC_NUM_DECIMAL), + + /* Other non-letter keys */ + AS(QZ_SLASH, WKC_SLASH), + AS(QZ_SEMICOLON, WKC_SEMICOLON), + AS(QZ_EQUALS, WKC_EQUALS), + AS(QZ_LEFTBRACKET, WKC_L_BRACKET), + AS(QZ_BACKSLASH, WKC_BACKSLASH), + AS(QZ_RIGHTBRACKET, WKC_R_BRACKET), + + AS(QZ_QUOTE, WKC_SINGLEQUOTE), + AS(QZ_COMMA, WKC_COMMA), + AS(QZ_MINUS, WKC_MINUS), + AS(QZ_PERIOD, WKC_PERIOD) +}; + +#undef AS + #endif diff --git a/src/video/cocoa/cocoa_v.h b/src/video/cocoa/cocoa_v.h index 54ff53d156..5dd3d1d1f4 100644 --- a/src/video/cocoa/cocoa_v.h +++ b/src/video/cocoa/cocoa_v.h @@ -16,67 +16,80 @@ extern bool _cocoa_video_started; +@class OTTD_CocoaWindowDelegate; +@class OTTD_CocoaWindow; +@class OTTD_CocoaView; + class VideoDriver_Cocoa : public VideoDriver { private: - Dimension orig_res; ///< Saved window size for non-fullscreen mode. + Dimension orig_res; ///< Saved window size for non-fullscreen mode. + + int window_width; ///< Current window width in pixel + int window_height; ///< Current window height in pixel + int window_pitch; + + int buffer_depth; ///< Colour depth of used frame buffer + void *pixel_buffer; ///< used for direct pixel access + void *window_buffer; ///< Colour translation from palette to screen + + static const int MAX_DIRTY_RECTS = 100; + + Rect dirty_rects[MAX_DIRTY_RECTS]; ///< dirty rectangles + uint num_dirty_rects; ///< Number of dirty rectangles + uint32 palette[256]; ///< Colour Palette public: - const char *Start(const StringList ¶m) override; + bool setup; ///< Window is currently being created. - /** Stop the video driver */ - void Stop() override; + OTTD_CocoaWindow *window; ///< Pointer to window object + OTTD_CocoaView *cocoaview; ///< Pointer to view object + CGColorSpaceRef color_space; ///< Window color space + CGContextRef cgcontext; ///< Context reference for Quartz subdriver - /** Mark dirty a screen region - * @param left x-coordinate of left border - * @param top y-coordinate of top border - * @param width width or dirty rectangle - * @param height height of dirty rectangle - */ - void MakeDirty(int left, int top, int width, int height) override; + OTTD_CocoaWindowDelegate *delegate; //!< Window delegate object - /** Programme main loop */ +public: + VideoDriver_Cocoa(); + + const char *Start(const StringList ¶m) override; + void Stop() override; void MainLoop() override; - /** Change window resolution - * @param w New window width - * @param h New window height - * @return Whether change was successful - */ - bool ChangeResolution(int w, int h) override; + void MakeDirty(int left, int top, int width, int height) override; + bool AfterBlitterChange() override; - /** Set a new window mode - * @param fullscreen Whether to set fullscreen mode or not - * @return Whether changing the screen mode was successful - */ + bool ChangeResolution(int w, int h) override; bool ToggleFullscreen(bool fullscreen) override; - /** Callback invoked after the blitter was changed. - * @return True if no error. - */ - bool AfterBlitterChange() override; - - /** - * An edit box lost the input focus. Abort character compositing if necessary. - */ void EditBoxLostFocus() override; - /** Return driver name - * @return driver name - */ const char *GetName() const override { return "cocoa"; } /* --- The following methods should be private, but can't be due to Obj-C limitations. --- */ - /** Main game loop. */ - void GameLoop(); // In event.mm. + void GameLoop(); + + void AllocateBackingStore(); protected: Dimension GetScreenSize() const override; + float GetDPIScale() override; private: - friend class WindowQuartzSubdriver; + bool PollEvent(); + bool IsFullscreen(); void GameSizeChanged(); + + void UpdateVideoModes(); + + bool MakeWindow(int width, int height); + + void UpdatePalette(uint first_color, uint num_colors); + void CheckPaletteAnim(); + + void Draw(bool force_update = false); + void BlitIndexedToView32(int left, int top, int right, int bottom); }; class FVideoDriver_Cocoa : public DriverFactoryBase { @@ -85,129 +98,4 @@ public: Driver *CreateInstance() const override { return new VideoDriver_Cocoa(); } }; - -/** - * Generic display driver for cocoa - * On grounds to not duplicate some code, it contains a few variables - * which are not used by all device drivers. - */ -class CocoaSubdriver { -public: - int device_width; ///< Width of device in pixel - int device_height; ///< Height of device in pixel - int device_depth; ///< Colour depth of device in bit - - int window_width; ///< Current window width in pixel - int window_height; ///< Current window height in pixel - int window_pitch; - - int buffer_depth; ///< Colour depth of used frame buffer - void *pixel_buffer; ///< used for direct pixel access - void *window_buffer; ///< Colour translation from palette to screen - CGColorSpaceRef color_space; //< Window color space - id window; ///< Pointer to window object - -# define MAX_DIRTY_RECTS 100 - Rect dirty_rects[MAX_DIRTY_RECTS]; ///< dirty rectangles - int num_dirty_rects; ///< Number of dirty rectangles - uint32 palette[256]; ///< Colour Palette - - bool active; ///< Whether the window is visible - bool setup; - - id cocoaview; ///< Pointer to view object - - /* Separate driver vars for Quarz - * Needed here in order to avoid much code duplication */ - CGContextRef cgcontext; ///< Context reference for Quartz subdriver - - /* Driver methods */ - /** Initialize driver */ - virtual ~CocoaSubdriver() {} - - /** Draw window - * @param force_update Whether to redraw unconditionally - */ - virtual void Draw(bool force_update = false) = 0; - - /** Mark dirty a screen region - * @param left x-coordinate of left border - * @param top y-coordinate of top border - * @param width width or dirty rectangle - * @param height height of dirty rectangle - */ - virtual void MakeDirty(int left, int top, int width, int height) = 0; - - /** Update the palette */ - virtual void UpdatePalette(uint first_color, uint num_colors) = 0; - - virtual uint ListModes(OTTD_Point *modes, uint max_modes) = 0; - - /** Change window resolution - * @param w New window width - * @param h New window height - * @return Whether change was successful - */ - virtual bool ChangeResolution(int w, int h, int bpp) = 0; - - /** Are we in fullscreen mode - * @return whether fullscreen mode is currently used - */ - virtual bool IsFullscreen() = 0; - - /** Toggle between fullscreen and windowed mode - * @return whether switch was successful - */ - virtual bool ToggleFullscreen(bool fullscreen) { return false; }; - - /** Return the width of the current view - * @return width of the current view - */ - virtual int GetWidth() = 0; - - /** Return the height of the current view - * @return height of the current view - */ - virtual int GetHeight() = 0; - - /** Return the current pixel buffer - * @return pixelbuffer - */ - virtual void *GetPixelBuffer() = 0; - - /** Convert local coordinate to window server (CoreGraphics) coordinate - * @param p local coordinates - * @return window driver coordinates - */ - virtual CGPoint PrivateLocalToCG(NSPoint *p) = 0; - - /** Return the mouse location - * @param event UI event - * @return mouse location as NSPoint - */ - virtual NSPoint GetMouseLocation(NSEvent *event) = 0; - - /** Return whether the mouse is within our view - * @param pt Mouse coordinates - * @return Whether mouse coordinates are within view - */ - virtual bool MouseIsInsideView(NSPoint *pt) = 0; - - /** Return whether the window is active (visible) - * @return whether the window is visible or not - */ - virtual bool IsActive() = 0; - - /** Whether the window was successfully resized - * @return whether the window was successfully resized - */ - virtual bool WindowResized() { return false; }; -}; - -extern CocoaSubdriver *_cocoa_subdriver; - -CocoaSubdriver *QZ_CreateWindowQuartzSubdriver(int width, int height, int bpp); - -uint QZ_ListModes(OTTD_Point *modes, uint max_modes, CGDirectDisplayID display_id, int display_depth); - #endif /* VIDEO_COCOA_H */ diff --git a/src/video/cocoa/cocoa_v.mm b/src/video/cocoa/cocoa_v.mm index f09920fe20..0ee9472892 100644 --- a/src/video/cocoa/cocoa_v.mm +++ b/src/video/cocoa/cocoa_v.mm @@ -26,19 +26,23 @@ #include "../../openttd.h" #include "../../debug.h" -#include "../../rev.h" #include "../../core/geometry_type.hpp" +#include "../../core/math_func.hpp" #include "cocoa_v.h" #include "cocoa_wnd.h" #include "../../blitter/factory.hpp" +#include "../../framerate_type.h" +#include "../../network/network.h" #include "../../gfx_func.h" +#include "../../thread.h" +#include "../../core/random_func.hpp" +#include "../../progress.h" +#include "../../settings_type.h" #include "../../window_func.h" #include "../../window_gui.h" -#include "../../core/math_func.hpp" -#include "../../framerate_type.h" -#include #import /* for MAXPATHLEN */ +#import /* gettimeofday */ /** * Important notice regarding all modifications!!!!!!! @@ -56,140 +60,96 @@ #endif bool _cocoa_video_started = false; -CocoaSubdriver *_cocoa_subdriver = NULL; - - -static bool ModeSorter(const OTTD_Point &p1, const OTTD_Point &p2) -{ - if (p1.x < p2.x) return true; - if (p1.x > p2.x) return false; - if (p1.y < p2.y) return true; - if (p1.y > p2.y) return false; - return false; -} - -static void QZ_GetDisplayModeInfo(CFArrayRef modes, CFIndex i, int &bpp, uint16 &width, uint16 &height) -{ - CGDisplayModeRef mode = static_cast(const_cast(CFArrayGetValueAtIndex(modes, i))); - width = (uint16)CGDisplayModeGetWidth(mode); - height = (uint16)CGDisplayModeGetHeight(mode); +extern bool _tab_is_down; -#if (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_11) - /* Extract bit depth from mode string. */ - CFAutoRelease pixEnc(CGDisplayModeCopyPixelEncoding(mode)); - if (CFStringCompare(pixEnc.get(), CFSTR(IO32BitDirectPixels), kCFCompareCaseInsensitive) == kCFCompareEqualTo) bpp = 32; - if (CFStringCompare(pixEnc.get(), CFSTR(IO16BitDirectPixels), kCFCompareCaseInsensitive) == kCFCompareEqualTo) bpp = 16; - if (CFStringCompare(pixEnc.get(), CFSTR(IO8BitIndexedPixels), kCFCompareCaseInsensitive) == kCFCompareEqualTo) bpp = 8; -#else - /* CGDisplayModeCopyPixelEncoding is deprecated on OSX 10.11+, but there are no 8 bpp modes anyway... */ - bpp = 32; +#ifdef _DEBUG +static uint32 _tEvent; #endif -} - -uint QZ_ListModes(OTTD_Point *modes, uint max_modes, CGDirectDisplayID display_id, int device_depth) -{ - CFAutoRelease mode_list(CGDisplayCopyAllDisplayModes(display_id, nullptr)); - CFIndex num_modes = CFArrayGetCount(mode_list.get()); - - /* Build list of modes with the requested bpp */ - uint count = 0; - for (CFIndex i = 0; i < num_modes && count < max_modes; i++) { - int bpp; - uint16 width, height; - QZ_GetDisplayModeInfo(mode_list.get(), i, bpp, width, height); - - if (bpp != device_depth) continue; - - /* Check if mode is already in the list */ - bool hasMode = false; - for (uint i = 0; i < count; i++) { - if (modes[i].x == width && modes[i].y == height) { - hasMode = true; - break; - } - } - if (hasMode) continue; - - /* Add mode to the list */ - modes[count].x = width; - modes[count].y = height; - count++; - } +/** List of common display/window sizes. */ +static const Dimension _default_resolutions[] = { + { 640, 480 }, + { 800, 600 }, + { 1024, 768 }, + { 1152, 864 }, + { 1280, 800 }, + { 1280, 960 }, + { 1280, 1024 }, + { 1400, 1050 }, + { 1600, 1200 }, + { 1680, 1050 }, + { 1920, 1200 }, + { 2560, 1440 } +}; - /* Sort list smallest to largest */ - std::sort(modes, modes + count, ModeSorter); +static FVideoDriver_Cocoa iFVideoDriver_Cocoa; - return count; -} /** - * Update the video modus. - * - * @pre _cocoa_subdriver != NULL + * Get current realtime. + * @return Tick time in milliseconds. */ -static void QZ_UpdateVideoModes() +static uint32 GetTick() { - assert(_cocoa_subdriver != NULL); + struct timeval tim; - OTTD_Point modes[32]; - uint count = _cocoa_subdriver->ListModes(modes, lengthof(modes)); + gettimeofday(&tim, NULL); + return tim.tv_usec / 1000 + tim.tv_sec * 1000; +} - _resolutions.clear(); - for (uint i = 0; i < count; i++) { - _resolutions.emplace_back(modes[i].x, modes[i].y); - } + +/** Subclass of NSView for drawing to screen. */ +@interface OTTD_QuartzView : NSView { + VideoDriver_Cocoa *driver; } +- (instancetype)initWithFrame:(NSRect)frameRect andDriver:(VideoDriver_Cocoa *)drv; +@end -/** - * Find a suitable cocoa subdriver. - * - * @param width Width of display area. - * @param height Height of display area. - * @param bpp Colour depth of display area. - * @param fullscreen Whether a fullscreen mode is requested. - * @param fallback Whether we look for a fallback driver. - * @return Pointer to window subdriver. - */ -static CocoaSubdriver *QZ_CreateSubdriver(int width, int height, int bpp, bool fullscreen, bool fallback) + +VideoDriver_Cocoa::VideoDriver_Cocoa() { - CocoaSubdriver *ret = QZ_CreateWindowQuartzSubdriver(width, height, bpp); - if (ret != nullptr && fullscreen) ret->ToggleFullscreen(fullscreen); + this->window_width = 0; + this->window_height = 0; + this->window_pitch = 0; + this->buffer_depth = 0; + this->window_buffer = nullptr; + this->pixel_buffer = nullptr; + this->setup = false; - if (ret != nullptr) return ret; - if (!fallback) return nullptr; + this->window = nil; + this->cocoaview = nil; + this->delegate = nil; - /* Try again in 640x480 windowed */ - DEBUG(driver, 0, "Setting video mode failed, falling back to 640x480 windowed mode."); - ret = QZ_CreateWindowQuartzSubdriver(640, 480, bpp); - if (ret != nullptr) return ret; + this->color_space = nullptr; + this->cgcontext = nullptr; - return nullptr; + this->num_dirty_rects = lengthof(this->dirty_rects); } - -static FVideoDriver_Cocoa iFVideoDriver_Cocoa; - -/** - * Stop the cocoa video subdriver. - */ +/** Stop Cocoa video driver. */ void VideoDriver_Cocoa::Stop() { if (!_cocoa_video_started) return; CocoaExitApplication(); - delete _cocoa_subdriver; - _cocoa_subdriver = NULL; + /* Release window mode resources */ + if (this->window != nil) [ this->window close ]; + [ this->cocoaview release ]; + [ this->delegate release ]; + + CGContextRelease(this->cgcontext); + CGColorSpaceRelease(this->color_space); + + free(this->window_buffer); + free(this->pixel_buffer); _cocoa_video_started = false; } -/** - * Initialize a cocoa video subdriver. - */ +/** Try to start Cocoa video driver. */ const char *VideoDriver_Cocoa::Start(const StringList &parm) { if (!MacOSVersionIsAtLeast(10, 7, 0)) return "The Cocoa video driver requires Mac OS X 10.7 or later."; @@ -198,35 +158,33 @@ const char *VideoDriver_Cocoa::Start(const StringList &parm) _cocoa_video_started = true; /* Don't create a window or enter fullscreen if we're just going to show a dialog. */ - if (!CocoaSetupApplication()) return NULL; + if (!CocoaSetupApplication()) return nullptr; this->UpdateAutoResolution(); - this->orig_res = _cur_resolution; - int width = _cur_resolution.width; - int height = _cur_resolution.height; - int bpp = BlitterFactory::GetCurrentBlitter()->GetScreenDepth(); + int bpp = BlitterFactory::GetCurrentBlitter()->GetScreenDepth(); if (bpp != 8 && bpp != 32) { Stop(); return "The cocoa quartz subdriver only supports 8 and 32 bpp."; } - _cocoa_subdriver = QZ_CreateSubdriver(width, height, bpp, _fullscreen, true); - if (_cocoa_subdriver == NULL) { + bool fullscreen = _fullscreen; + if (!this->MakeWindow(_cur_resolution.width, _cur_resolution.height)) { Stop(); - return "Could not create subdriver"; + return "Could not create window"; } + if (fullscreen) this->ToggleFullscreen(fullscreen); + this->GameSizeChanged(); - QZ_UpdateVideoModes(); + this->UpdateVideoModes(); - return NULL; + return nullptr; } /** * Set dirty a rectangle managed by a cocoa video subdriver. - * * @param left Left x cooordinate of the dirty rectangle. * @param top Uppder y coordinate of the dirty rectangle. * @param width Width of the dirty rectangle. @@ -234,9 +192,13 @@ const char *VideoDriver_Cocoa::Start(const StringList &parm) */ void VideoDriver_Cocoa::MakeDirty(int left, int top, int width, int height) { - assert(_cocoa_subdriver != NULL); - - _cocoa_subdriver->MakeDirty(left, top, width, height); + if (this->num_dirty_rects < lengthof(this->dirty_rects)) { + dirty_rects[this->num_dirty_rects].left = left; + dirty_rects[this->num_dirty_rects].top = top; + dirty_rects[this->num_dirty_rects].right = left + width; + dirty_rects[this->num_dirty_rects].bottom = top + height; + } + this->num_dirty_rects++; } /** @@ -254,44 +216,64 @@ void VideoDriver_Cocoa::MainLoop() /** * Change the resolution when using a cocoa video driver. - * * @param w New window width. * @param h New window height. * @return Whether the video driver was successfully updated. */ bool VideoDriver_Cocoa::ChangeResolution(int w, int h) { - assert(_cocoa_subdriver != NULL); + NSSize screen_size = [ [ NSScreen mainScreen ] frame ].size; + w = std::min(w, (int)screen_size.width); + h = std::min(h, (int)screen_size.height); - bool ret = _cocoa_subdriver->ChangeResolution(w, h, BlitterFactory::GetCurrentBlitter()->GetScreenDepth()); + NSRect contentRect = NSMakeRect(0, 0, w, h); + [ this->window setContentSize:contentRect.size ]; - this->GameSizeChanged(); - QZ_UpdateVideoModes(); + /* Ensure frame height - title bar height >= view height */ + float content_height = [ this->window contentRectForFrameRect:[ this->window frame ] ].size.height; + contentRect.size.height = Clamp(h, 0, (int)content_height); + + if (this->cocoaview != nil) { + h = (int)contentRect.size.height; + [ this->cocoaview setFrameSize:contentRect.size ]; + } + + this->window_width = w; + this->window_height = h; + + [ (OTTD_CocoaWindow *)this->window center ]; + this->AllocateBackingStore(); - return ret; + return true; } /** * Toggle between windowed and full screen mode for cocoa display driver. - * * @param full_screen Whether to switch to full screen or not. * @return Whether the mode switch was successful. */ bool VideoDriver_Cocoa::ToggleFullscreen(bool full_screen) { - assert(_cocoa_subdriver != NULL); + if (this->IsFullscreen() == full_screen) return true; + + if ([ this->window respondsToSelector:@selector(toggleFullScreen:) ]) { + [ this->window performSelector:@selector(toggleFullScreen:) withObject:this->window ]; + this->UpdateVideoModes(); + return true; + } - return _cocoa_subdriver->ToggleFullscreen(full_screen); + return false; } /** * Callback invoked after the blitter was changed. - * * @return True if no error. */ bool VideoDriver_Cocoa::AfterBlitterChange() { - return this->ChangeResolution(_screen.width, _screen.height); + this->ChangeResolution(_cur_resolution.width, _cur_resolution.height); + this->UpdatePalette(0, 256); + return true; } /** @@ -299,7 +281,7 @@ bool VideoDriver_Cocoa::AfterBlitterChange() */ void VideoDriver_Cocoa::EditBoxLostFocus() { - if (_cocoa_subdriver != NULL) [ [ _cocoa_subdriver->cocoaview inputContext ] discardMarkedText ]; + [ [ this->cocoaview inputContext ] discardMarkedText ]; /* Clear any marked string from the current edit box. */ HandleTextInput(NULL, true); } @@ -313,323 +295,167 @@ Dimension VideoDriver_Cocoa::GetScreenSize() const return { static_cast(NSWidth(frame)), static_cast(NSHeight(frame)) }; } +/** Get DPI scale of our window. */ +float VideoDriver_Cocoa::GetDPIScale() +{ + return this->cocoaview != nil ? [ this->cocoaview getContentsScale ] : 1.0f; +} + +/** + * Are we in fullscreen mode? + * @return whether fullscreen mode is currently used + */ +bool VideoDriver_Cocoa::IsFullscreen() +{ + return this->window != nil && ([ this->window styleMask ] & NSWindowStyleMaskFullScreen) != 0; +} + /** * Handle a change of the display area. */ void VideoDriver_Cocoa::GameSizeChanged() { - if (_cocoa_subdriver == nullptr) return; - /* Tell the game that the resolution has changed */ - _screen.width = _cocoa_subdriver->GetWidth(); - _screen.height = _cocoa_subdriver->GetHeight(); - _screen.pitch = _cocoa_subdriver->GetWidth(); - _screen.dst_ptr = _cocoa_subdriver->GetPixelBuffer(); + _screen.width = this->window_width; + _screen.height = this->window_height; + _screen.pitch = this->buffer_depth == 8 ? this->window_width : this->window_pitch; + _screen.dst_ptr = this->buffer_depth == 8 ? this->pixel_buffer : this->window_buffer; /* Store old window size if we entered fullscreen mode. */ - bool fullscreen = _cocoa_subdriver->IsFullscreen(); + bool fullscreen = this->IsFullscreen(); if (fullscreen && !_fullscreen) this->orig_res = _cur_resolution; _fullscreen = fullscreen; BlitterFactory::GetCurrentBlitter()->PostResize(); ::GameSizeChanged(); -} - -class WindowQuartzSubdriver; - -/* Subclass of OTTD_CocoaView to fix Quartz rendering */ -@interface OTTD_QuartzView : OTTD_CocoaView -- (void)setDriver:(WindowQuartzSubdriver*)drv; -- (void)drawRect:(NSRect)invalidRect; -@end -class WindowQuartzSubdriver : public CocoaSubdriver { -private: - /** - * This function copies 8bpp pixels from the screen buffer in 32bpp windowed mode. - * - * @param left The x coord for the left edge of the box to blit. - * @param top The y coord for the top edge of the box to blit. - * @param right The x coord for the right edge of the box to blit. - * @param bottom The y coord for the bottom edge of the box to blit. - */ - void BlitIndexedToView32(int left, int top, int right, int bottom); - - virtual void GetDeviceInfo(); - virtual bool SetVideoMode(int width, int height, int bpp); - -public: - WindowQuartzSubdriver(); - virtual ~WindowQuartzSubdriver(); - - virtual void Draw(bool force_update); - virtual void MakeDirty(int left, int top, int width, int height); - virtual void UpdatePalette(uint first_color, uint num_colors); - - virtual uint ListModes(OTTD_Point *modes, uint max_modes); - - virtual bool ChangeResolution(int w, int h, int bpp); - - virtual bool IsFullscreen(); - virtual bool ToggleFullscreen(bool fullscreen); /* Full screen mode on OSX 10.7 */ - - virtual int GetWidth() { return window_width; } - virtual int GetHeight() { return window_height; } - virtual void *GetPixelBuffer() { return buffer_depth == 8 ? pixel_buffer : window_buffer; } - - /* Convert local coordinate to window server (CoreGraphics) coordinate */ - virtual CGPoint PrivateLocalToCG(NSPoint *p); - - virtual NSPoint GetMouseLocation(NSEvent *event); - virtual bool MouseIsInsideView(NSPoint *pt); - - virtual bool IsActive() { return active; } - - bool WindowResized(); -}; - - -@implementation OTTD_QuartzView - -- (void)setDriver:(WindowQuartzSubdriver*)drv -{ - driver = drv; + /* We need to store the window size as non-Retina size in + * the config file to get same windows size on next start. */ + _cur_resolution.width = [ this->cocoaview frame ].size.width; + _cur_resolution.height = [ this->cocoaview frame ].size.height; } -- (void)drawRect:(NSRect)invalidRect -{ - if (driver->cgcontext == NULL) return; - - CGContextRef viewContext = (CGContextRef)[ [ NSGraphicsContext currentContext ] graphicsPort ]; - CGContextSetShouldAntialias(viewContext, FALSE); - CGContextSetInterpolationQuality(viewContext, kCGInterpolationNone); - - /* The obtained 'rect' is actually a union of all dirty rects, let's ask for an explicit list of rects instead */ - const NSRect *dirtyRects; - NSInteger dirtyRectCount; - [ self getRectsBeingDrawn:&dirtyRects count:&dirtyRectCount ]; - /* We need an Image in order to do blitting, but as we don't touch the context between this call and drawing no copying will actually be done here */ - CGImageRef fullImage = CGBitmapContextCreateImage(driver->cgcontext); - - /* Calculate total area we are blitting */ - uint32 blitArea = 0; - for (int n = 0; n < dirtyRectCount; n++) { - blitArea += (uint32)(dirtyRects[n].size.width * dirtyRects[n].size.height); - } +/** + * Update the video mode. + */ +void VideoDriver_Cocoa::UpdateVideoModes() +{ + _resolutions.clear(); - /* - * This might be completely stupid, but in my extremely subjective opinion it feels faster - * The point is, if we're blitting less than 50% of the dirty rect union then it's still a good idea to blit each dirty - * rect separately but if we blit more than that, it's just cheaper to blit the entire union in one pass. - * Feel free to remove or find an even better value than 50% ... / blackis - */ - NSRect frameRect = [ self frame ]; - if (blitArea / (float)(invalidRect.size.width * invalidRect.size.height) > 0.5f) { - NSRect rect = invalidRect; - CGRect clipRect; - CGRect blitRect; - - blitRect.origin.x = rect.origin.x; - blitRect.origin.y = rect.origin.y; - blitRect.size.width = rect.size.width; - blitRect.size.height = rect.size.height; - - clipRect.origin.x = rect.origin.x; - clipRect.origin.y = frameRect.size.height - rect.origin.y - rect.size.height; - - clipRect.size.width = rect.size.width; - clipRect.size.height = rect.size.height; - - /* Blit dirty part of image */ - CGImageRef clippedImage = CGImageCreateWithImageInRect(fullImage, clipRect); - CGContextDrawImage(viewContext, blitRect, clippedImage); - CGImageRelease(clippedImage); + if (this->IsFullscreen()) { + /* Full screen, there is only one possible resolution. */ + NSSize screen = [ [ this->window screen ] frame ].size; + _resolutions.emplace_back((uint)screen.width, (uint)screen.height); } else { - for (int n = 0; n < dirtyRectCount; n++) { - NSRect rect = dirtyRects[n]; - CGRect clipRect; - CGRect blitRect; - - blitRect.origin.x = rect.origin.x; - blitRect.origin.y = rect.origin.y; - blitRect.size.width = rect.size.width; - blitRect.size.height = rect.size.height; - - clipRect.origin.x = rect.origin.x; - clipRect.origin.y = frameRect.size.height - rect.origin.y - rect.size.height; - - clipRect.size.width = rect.size.width; - clipRect.size.height = rect.size.height; - - /* Blit dirty part of image */ - CGImageRef clippedImage = CGImageCreateWithImageInRect(fullImage, clipRect); - CGContextDrawImage(viewContext, blitRect, clippedImage); - CGImageRelease(clippedImage); + /* Windowed; offer a selection of common window sizes up until the + * maximum usable screen space. This excludes the menu and dock areas. */ + NSSize maxSize = [ [ NSScreen mainScreen] visibleFrame ].size; + for (const auto &d : _default_resolutions) { + if (d.width < maxSize.width && d.height < maxSize.height) _resolutions.push_back(d); } + _resolutions.emplace_back((uint)maxSize.width, (uint)maxSize.height); } - - CGImageRelease(fullImage); -} - -@end - - -void WindowQuartzSubdriver::GetDeviceInfo() -{ - /* Initialize the video settings; this data persists between mode switches - * and gather some information that is useful to know about the display */ - - /* Use the new API when compiling for OSX 10.6 or later */ - CGDisplayModeRef cur_mode = CGDisplayCopyDisplayMode(kCGDirectMainDisplay); - if (cur_mode == NULL) { return; } - - this->device_width = CGDisplayModeGetWidth(cur_mode); - this->device_height = CGDisplayModeGetHeight(cur_mode); - - CGDisplayModeRelease(cur_mode); -} - -bool WindowQuartzSubdriver::IsFullscreen() -{ - return this->window != nil && ([ this->window styleMask ] & NSWindowStyleMaskFullScreen) != 0; } -/** Switch to full screen mode on OSX 10.7 - * @return Whether we switched to full screen +/** + * Build window and view with a given size. + * @param width Window width. + * @param height Window height. */ -bool WindowQuartzSubdriver::ToggleFullscreen(bool fullscreen) -{ - if (this->IsFullscreen() == fullscreen) return true; - - if ([ this->window respondsToSelector:@selector(toggleFullScreen:) ]) { - [ this->window performSelector:@selector(toggleFullScreen:) withObject:this->window ]; - return true; - } - - return false; -} - -bool WindowQuartzSubdriver::SetVideoMode(int width, int height, int bpp) +bool VideoDriver_Cocoa::MakeWindow(int width, int height) { this->setup = true; - this->GetDeviceInfo(); - if (width > this->device_width) width = this->device_width; - if (height > this->device_height) height = this->device_height; + /* Limit window size to screen frame. */ + NSSize screen_size = [ [ NSScreen mainScreen ] frame ].size; + if (width > screen_size.width) width = screen_size.width; + if (height > screen_size.height) height = screen_size.height; NSRect contentRect = NSMakeRect(0, 0, width, height); - /* Check if we should recreate the window */ + /* Create main window. */ + unsigned int style = NSTitledWindowMask | NSResizableWindowMask | NSMiniaturizableWindowMask | NSClosableWindowMask; + this->window = [ [ OTTD_CocoaWindow alloc ] initWithContentRect:contentRect styleMask:style backing:NSBackingStoreBuffered defer:NO driver:this ]; if (this->window == nil) { - OTTD_CocoaWindowDelegate *delegate; - - /* Set the window style */ - unsigned int style = NSTitledWindowMask; - style |= (NSMiniaturizableWindowMask | NSClosableWindowMask); - style |= NSResizableWindowMask; - - /* Manually create a window, avoids having a nib file resource */ - this->window = [ [ OTTD_CocoaWindow alloc ] - initWithContentRect:contentRect - styleMask:style - backing:NSBackingStoreBuffered - defer:NO ]; - - if (this->window == nil) { - DEBUG(driver, 0, "Could not create the Cocoa window."); - this->setup = false; - return false; - } - - /* Add built in full-screen support when available (OS X 10.7 and higher) - * This code actually compiles for 10.5 and later, but only makes sense in conjunction - * with the quartz fullscreen support as found only in 10.7 and later - */ - if ([ this->window respondsToSelector:@selector(toggleFullScreen:) ]) { - NSWindowCollectionBehavior behavior = [ this->window collectionBehavior ]; - behavior |= NSWindowCollectionBehaviorFullScreenPrimary; - [ this->window setCollectionBehavior:behavior ]; - - NSButton* fullscreenButton = [ this->window standardWindowButton:NSWindowFullScreenButton ]; - [ fullscreenButton setAction:@selector(toggleFullScreen:) ]; - [ fullscreenButton setTarget:this->window ]; - } - - [ this->window setDriver:this ]; - - char caption[50]; - snprintf(caption, sizeof(caption), "OpenTTD %s", _openttd_revision); - NSString *nsscaption = [ [ NSString alloc ] initWithUTF8String:caption ]; - [ this->window setTitle:nsscaption ]; - [ this->window setMiniwindowTitle:nsscaption ]; - [ nsscaption release ]; - - [ this->window setContentMinSize:NSMakeSize(64.0f, 64.0f) ]; - - [ this->window setAcceptsMouseMovedEvents:YES ]; - [ this->window setViewsNeedDisplay:NO ]; - - delegate = [ [ OTTD_CocoaWindowDelegate alloc ] init ]; - [ delegate setDriver:this ]; - [ this->window setDelegate:[ delegate autorelease ] ]; - } else { - /* We already have a window, just change its size */ - [ this->window setContentSize:contentRect.size ]; + DEBUG(driver, 0, "Could not create the Cocoa window."); + this->setup = false; + return false; + } - /* Ensure frame height - title bar height >= view height */ - float content_height = [ this->window contentRectForFrameRect:[ this->window frame ] ].size.height; - contentRect.size.height = Clamp(height, 0, (int)content_height); + /* Add built in full-screen support when available (OS X 10.7 and higher) + * This code actually compiles for 10.5 and later, but only makes sense in conjunction + * with the quartz fullscreen support as found only in 10.7 and later. */ + if ([ this->window respondsToSelector:@selector(toggleFullScreen:) ]) { + NSWindowCollectionBehavior behavior = [ this->window collectionBehavior ]; + behavior |= NSWindowCollectionBehaviorFullScreenPrimary; + [ this->window setCollectionBehavior:behavior ]; - if (this->cocoaview != nil) { - height = (int)contentRect.size.height; - [ this->cocoaview setFrameSize:contentRect.size ]; - } + NSButton* fullscreenButton = [ this->window standardWindowButton:NSWindowFullScreenButton ]; + [ fullscreenButton setAction:@selector(toggleFullScreen:) ]; + [ fullscreenButton setTarget:this->window ]; } - this->window_width = width; - this->window_height = height; - this->buffer_depth = bpp; + this->delegate = [ [ OTTD_CocoaWindowDelegate alloc ] initWithDriver:this ]; + [ this->window setDelegate:this->delegate ]; - [ (OTTD_CocoaWindow *)this->window center ]; + [ this->window center ]; + [ this->window makeKeyAndOrderFront:nil ]; - /* Only recreate the view if it doesn't already exist */ + /* Create wrapper view for input and event handling. */ + NSRect view_frame = [ this->window contentRectForFrameRect:[ this->window frame ] ]; + this->cocoaview = [ [ OTTD_CocoaView alloc ] initWithFrame:view_frame ]; if (this->cocoaview == nil) { - this->cocoaview = [ [ OTTD_QuartzView alloc ] initWithFrame:contentRect ]; - if (this->cocoaview == nil) { - DEBUG(driver, 0, "Could not create the Quartz view."); - this->setup = false; - return false; - } - - [ this->cocoaview setDriver:this ]; - - [ (NSView*)this->cocoaview setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable ]; - [ this->window setContentView:cocoaview ]; - [ this->cocoaview release ]; - [ this->window makeKeyAndOrderFront:nil ]; + DEBUG(driver, 0, "Could not create the event wrapper view."); + this->setup = false; + return false; } + [ this->cocoaview setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable ]; + + /* Create content view. */ + NSView *draw_view = [ [ OTTD_QuartzView alloc ] initWithFrame:[ this->cocoaview bounds ] andDriver:this ]; + if (draw_view == nil) { + DEBUG(driver, 0, "Could not create the drawing view."); + this->setup = false; + return false; + } + [ draw_view setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable ]; + + /* Create view chain: window -> input wrapper view -> content view. */ + [ this->window setContentView:this->cocoaview ]; + [ this->cocoaview addSubview:draw_view ]; + [ this->window makeFirstResponder:this->cocoaview ]; + [ draw_view release ]; [ this->window setColorSpace:[ NSColorSpace sRGBColorSpace ] ]; + CGColorSpaceRelease(this->color_space); this->color_space = CGColorSpaceCreateWithName(kCGColorSpaceSRGB); if (this->color_space == nullptr) this->color_space = CGColorSpaceCreateDeviceRGB(); if (this->color_space == nullptr) error("Could not get a valid colour space for drawing."); - bool ret = WindowResized(); - this->UpdatePalette(0, 256); - this->setup = false; - return ret; + this->UpdatePalette(0, 256); + this->AllocateBackingStore(); + + return true; } -void WindowQuartzSubdriver::BlitIndexedToView32(int left, int top, int right, int bottom) +/** + * This function copies 8bpp pixels to the screen buffer in 32bpp windowed mode. + * + * @param left The x coord for the left edge of the box to blit. + * @param top The y coord for the top edge of the box to blit. + * @param right The x coord for the right edge of the box to blit. + * @param bottom The y coord for the bottom edge of the box to blit. + */ +void VideoDriver_Cocoa::BlitIndexedToView32(int left, int top, int right, int bottom) { const uint32 *pal = this->palette; const uint8 *src = (uint8*)this->pixel_buffer; uint32 *dst = (uint32*)this->window_buffer; uint width = this->window_width; - uint pitch = this->window_width; + uint pitch = this->window_pitch; for (int y = top; y < bottom; y++) { for (int x = left; x < right; x++) { @@ -638,45 +464,18 @@ void WindowQuartzSubdriver::BlitIndexedToView32(int left, int top, int right, in } } - -WindowQuartzSubdriver::WindowQuartzSubdriver() -{ - this->window_width = 0; - this->window_height = 0; - this->buffer_depth = 0; - this->window_buffer = NULL; - this->pixel_buffer = NULL; - this->active = false; - this->setup = false; - - this->window = nil; - this->cocoaview = nil; - - this->cgcontext = NULL; - - this->num_dirty_rects = MAX_DIRTY_RECTS; -} - -WindowQuartzSubdriver::~WindowQuartzSubdriver() -{ - /* Release window mode resources */ - if (this->window != nil) [ this->window close ]; - - CGContextRelease(this->cgcontext); - - CGColorSpaceRelease(this->color_space); - free(this->window_buffer); - free(this->pixel_buffer); -} - -void WindowQuartzSubdriver::Draw(bool force_update) +/** + * Draw window. + * @param force_update Whether to redraw unconditionally + */ +void VideoDriver_Cocoa::Draw(bool force_update) { PerformanceMeasurer framerate(PFE_VIDEO); /* Check if we need to do anything */ if (this->num_dirty_rects == 0 || [ this->window isMiniaturized ]) return; - if (this->num_dirty_rects >= MAX_DIRTY_RECTS) { + if (this->num_dirty_rects >= lengthof(this->dirty_rects)) { this->num_dirty_rects = 1; this->dirty_rects[0].left = 0; this->dirty_rects[0].top = 0; @@ -685,7 +484,7 @@ void WindowQuartzSubdriver::Draw(bool force_update) } /* Build the region of dirty rectangles */ - for (int i = 0; i < this->num_dirty_rects; i++) { + for (uint i = 0; i < this->num_dirty_rects; i++) { /* We only need to blit in indexed mode since in 32bpp mode the game draws directly to the image. */ if (this->buffer_depth == 8) { BlitIndexedToView32( @@ -704,25 +503,15 @@ void WindowQuartzSubdriver::Draw(bool force_update) /* Normally drawRect will be automatically called by Mac OS X during next update cycle, * and then blitting will occur. If force_update is true, it will be done right now. */ - [ this->cocoaview setNeedsDisplayInRect:dirtyrect ]; + [ this->cocoaview setNeedsDisplayInRect:[ this->cocoaview getVirtualRect:dirtyrect ] ]; if (force_update) [ this->cocoaview displayIfNeeded ]; } this->num_dirty_rects = 0; } -void WindowQuartzSubdriver::MakeDirty(int left, int top, int width, int height) -{ - if (this->num_dirty_rects < MAX_DIRTY_RECTS) { - dirty_rects[this->num_dirty_rects].left = left; - dirty_rects[this->num_dirty_rects].top = top; - dirty_rects[this->num_dirty_rects].right = left + width; - dirty_rects[this->num_dirty_rects].bottom = top + height; - } - this->num_dirty_rects++; -} - -void WindowQuartzSubdriver::UpdatePalette(uint first_color, uint num_colors) +/** Update the palette. */ +void VideoDriver_Cocoa::UpdatePalette(uint first_color, uint num_colors) { if (this->buffer_depth != 8) return; @@ -734,61 +523,7 @@ void WindowQuartzSubdriver::UpdatePalette(uint first_color, uint num_colors) this->palette[i] = clr; } - this->num_dirty_rects = MAX_DIRTY_RECTS; -} - -uint WindowQuartzSubdriver::ListModes(OTTD_Point *modes, uint max_modes) -{ - return QZ_ListModes(modes, max_modes, kCGDirectMainDisplay, this->buffer_depth); -} - -bool WindowQuartzSubdriver::ChangeResolution(int w, int h, int bpp) -{ - int old_width = this->window_width; - int old_height = this->window_height; - int old_bpp = this->buffer_depth; - - if (this->SetVideoMode(w, h, bpp)) return true; - if (old_width != 0 && old_height != 0) this->SetVideoMode(old_width, old_height, old_bpp); - - return false; -} - -/* Convert local coordinate to window server (CoreGraphics) coordinate */ -CGPoint WindowQuartzSubdriver::PrivateLocalToCG(NSPoint *p) -{ - - p->y = this->window_height - p->y; - *p = [ this->cocoaview convertPoint:*p toView:nil ]; - *p = [ this->window convertRectToScreen:NSMakeRect(p->x, p->y, 0, 0) ].origin; - - p->y = this->device_height - p->y; - - CGPoint cgp; - cgp.x = p->x; - cgp.y = p->y; - - return cgp; -} - -NSPoint WindowQuartzSubdriver::GetMouseLocation(NSEvent *event) -{ - NSPoint pt; - - if ( [ event window ] == nil) { - pt = [ this->cocoaview convertPoint:[ [ this->cocoaview window ] convertRectFromScreen:NSMakeRect([ event locationInWindow ].x, [ event locationInWindow ].y, 0, 0) ].origin fromView:nil ]; - } else { - pt = [ event locationInWindow ]; - } - - pt.y = this->window_height - pt.y; - - return pt; -} - -bool WindowQuartzSubdriver::MouseIsInsideView(NSPoint *pt) -{ - return [ cocoaview mouse:*pt inRect:[ this->cocoaview bounds ] ]; + this->num_dirty_rects = lengthof(this->dirty_rects); } /** Clear buffer to opaque black. */ @@ -802,20 +537,23 @@ static void ClearWindowBuffer(uint32 *buffer, uint32 pitch, uint32 height) } } -bool WindowQuartzSubdriver::WindowResized() +/** Resize the window. */ +void VideoDriver_Cocoa::AllocateBackingStore() { - if (this->window == nil || this->cocoaview == nil) return true; + if (this->window == nil || this->cocoaview == nil || this->setup) return; - NSRect newframe = [ this->cocoaview frame ]; + NSRect newframe = [ this->cocoaview getRealRect:[ this->cocoaview frame ] ]; this->window_width = (int)newframe.size.width; this->window_height = (int)newframe.size.height; + this->window_pitch = Align(this->window_width, 16 / sizeof(uint32)); // Quartz likes lines that are multiple of 16-byte. + this->buffer_depth = BlitterFactory::GetCurrentBlitter()->GetScreenDepth(); /* Create Core Graphics Context */ free(this->window_buffer); - this->window_buffer = malloc(this->window_width * this->window_height * sizeof(uint32)); + this->window_buffer = malloc(this->window_pitch * this->window_height * sizeof(uint32)); /* Initialize with opaque black. */ - ClearWindowBuffer((uint32 *)this->window_buffer, this->window_width, this->window_height); + ClearWindowBuffer((uint32 *)this->window_buffer, this->window_pitch, this->window_height); CGContextRelease(this->cgcontext); this->cgcontext = CGBitmapContextCreate( @@ -823,7 +561,7 @@ bool WindowQuartzSubdriver::WindowResized() this->window_width, // width this->window_height, // height 8, // bits per component - this->window_width * 4, // bytes per row + this->window_pitch * 4, // bytes per row this->color_space, // color space kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host ); @@ -836,31 +574,201 @@ bool WindowQuartzSubdriver::WindowResized() if (this->buffer_depth == 8) { free(this->pixel_buffer); this->pixel_buffer = malloc(this->window_width * this->window_height); - if (this->pixel_buffer == NULL) { - DEBUG(driver, 0, "Failed to allocate pixel buffer"); - return false; + if (this->pixel_buffer == nullptr) usererror("Out of memory allocating pixel buffer"); + } else { + free(this->pixel_buffer); + this->pixel_buffer = nullptr; + } + + /* Redraw screen */ + this->num_dirty_rects = lengthof(this->dirty_rects); + this->GameSizeChanged(); +} + +/** Check if palette updates need to be performed. */ +void VideoDriver_Cocoa::CheckPaletteAnim() +{ + if (_cur_palette.count_dirty != 0) { + Blitter *blitter = BlitterFactory::GetCurrentBlitter(); + + switch (blitter->UsePaletteAnimation()) { + case Blitter::PALETTE_ANIMATION_VIDEO_BACKEND: + this->UpdatePalette(_cur_palette.first_dirty, _cur_palette.count_dirty); + break; + + case Blitter::PALETTE_ANIMATION_BLITTER: + blitter->PaletteAnimate(_cur_palette); + break; + + case Blitter::PALETTE_ANIMATION_NONE: + break; + + default: + NOT_REACHED(); } + _cur_palette.count_dirty = 0; } +} - static_cast(VideoDriver::GetInstance())->GameSizeChanged(); - /* Redraw screen */ - this->num_dirty_rects = MAX_DIRTY_RECTS; +/** + * Poll and handle a single event from the OS. + * @return True if there was an event to handle. + */ +bool VideoDriver_Cocoa::PollEvent() +{ +#ifdef _DEBUG + uint32 et0 = GetTick(); +#endif + NSEvent *event = [ NSApp nextEventMatchingMask:NSAnyEventMask untilDate:[ NSDate distantPast ] inMode:NSDefaultRunLoopMode dequeue:YES ]; +#ifdef _DEBUG + _tEvent += GetTick() - et0; +#endif + + if (event == nil) return false; + + [ NSApp sendEvent:event ]; return true; } +/** Main game loop. */ +void VideoDriver_Cocoa::GameLoop() +{ + auto cur_ticks = std::chrono::steady_clock::now(); + auto last_realtime_tick = cur_ticks; + auto next_game_tick = cur_ticks; + auto next_draw_tick = cur_ticks; + + for (;;) { + @autoreleasepool { + + InteractiveRandom(); // randomness + + while (this->PollEvent()) {} + + if (_exit_game) { + /* Restore saved resolution if in fullscreen mode. */ + if (this->IsFullscreen()) _cur_resolution = this->orig_res; + break; + } + + NSUInteger cur_mods = [ NSEvent modifierFlags ]; + +#if defined(_DEBUG) + if (cur_mods & NSShiftKeyMask) { +#else + if (_tab_is_down) { +#endif + if (!_networking && _game_mode != GM_MENU) _fast_forward |= 2; + } else if (_fast_forward & 2) { + _fast_forward = 0; + } + + cur_ticks = std::chrono::steady_clock::now(); + + /* If more than a millisecond has passed, increase the _realtime_tick. */ + if (cur_ticks - last_realtime_tick > std::chrono::milliseconds(1)) { + auto delta = std::chrono::duration_cast(cur_ticks - last_realtime_tick); + IncreaseRealtimeTick(delta.count()); + last_realtime_tick += delta; + } + + if (cur_ticks >= next_game_tick || (_fast_forward && !_pause_mode)) { + if (_fast_forward && !_pause_mode) { + next_game_tick = cur_ticks + this->GetGameInterval(); + } else { + next_game_tick += this->GetGameInterval(); + /* Avoid next_game_tick getting behind more and more if it cannot keep up. */ + if (next_game_tick < cur_ticks - ALLOWED_DRIFT * this->GetGameInterval()) next_game_tick = cur_ticks; + } + + ::GameLoop(); + } + + /* Prevent drawing when switching mode, as windows can be removed when they should still appear. */ + if (cur_ticks >= next_draw_tick && (_switch_mode == SM_NONE || HasModalProgress())) { + next_draw_tick += this->GetDrawInterval(); + /* Avoid next_draw_tick getting behind more and more if it cannot keep up. */ + if (next_draw_tick < cur_ticks - ALLOWED_DRIFT * this->GetDrawInterval()) next_draw_tick = cur_ticks; + + bool old_ctrl_pressed = _ctrl_pressed; + + _ctrl_pressed = (cur_mods & ( _settings_client.gui.right_mouse_btn_emulation != RMBE_CONTROL ? NSControlKeyMask : NSCommandKeyMask)) != 0; + _shift_pressed = (cur_mods & NSShiftKeyMask) != 0; + + if (old_ctrl_pressed != _ctrl_pressed) HandleCtrlChanged(); + + InputLoop(); + UpdateWindows(); + this->CheckPaletteAnim(); + + this->Draw(); + } + + /* If we are not in fast-forward, create some time between calls to ease up CPU usage. */ + if (!_fast_forward || _pause_mode) { + /* See how much time there is till we have to process the next event, and try to hit that as close as possible. */ + auto next_tick = std::min(next_draw_tick, next_game_tick); + auto now = std::chrono::steady_clock::now(); + + if (next_tick > now) { + std::this_thread::sleep_for(next_tick - now); + } + } + } + } +} + -CocoaSubdriver *QZ_CreateWindowQuartzSubdriver(int width, int height, int bpp) +@implementation OTTD_QuartzView + +- (instancetype)initWithFrame:(NSRect)frameRect andDriver:(VideoDriver_Cocoa *)drv { - WindowQuartzSubdriver *ret = new WindowQuartzSubdriver(); + if (self = [ super initWithFrame:frameRect ]) { + self->driver = drv; + + /* We manage our content updates ourselves. */ + self.layerContentsRedrawPolicy = NSViewLayerContentsRedrawOnSetNeedsDisplay; + self.wantsLayer = YES; - if (!ret->ChangeResolution(width, height, bpp)) { - delete ret; - return NULL; + self.layer.magnificationFilter = kCAFilterNearest; } + return self; +} + +- (BOOL)acceptsFirstResponder +{ + return NO; +} + +- (BOOL)isOpaque +{ + return YES; +} + +- (BOOL)wantsUpdateLayer +{ + return YES; +} - return ret; +- (void)updateLayer +{ + if (driver->cgcontext == nullptr) return; + + /* Set layer contents to our backing buffer, which avoids needless copying. */ + CGImageRef fullImage = CGBitmapContextCreateImage(driver->cgcontext); + self.layer.contents = (__bridge id)fullImage; + CGImageRelease(fullImage); +} + +- (void)viewDidChangeBackingProperties +{ + [ super viewDidChangeBackingProperties ]; + + self.layer.contentsScale = [ driver->cocoaview getContentsScale ]; } +@end + #endif /* WITH_COCOA */ diff --git a/src/video/cocoa/cocoa_wnd.h b/src/video/cocoa/cocoa_wnd.h index f2890ec0f0..55f0561b77 100644 --- a/src/video/cocoa/cocoa_wnd.h +++ b/src/video/cocoa/cocoa_wnd.h @@ -12,7 +12,14 @@ #import -class CocoaSubdriver; +class VideoDriver_Cocoa; + +/* Right Mouse Button Emulation enum */ +enum RightMouseButtonEmulationState { + RMBE_COMMAND = 0, + RMBE_CONTROL = 1, + RMBE_OFF = 2, +}; extern NSString *OTTDMainLaunchGameEngine; @@ -22,51 +29,28 @@ extern NSString *OTTDMainLaunchGameEngine; @end /** Subclass of NSWindow to cater our special needs */ -@interface OTTD_CocoaWindow : NSWindow { - CocoaSubdriver *driver; -} - -- (void)setDriver:(CocoaSubdriver*)drv; +@interface OTTD_CocoaWindow : NSWindow +- (instancetype)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)styleMask backing:(NSBackingStoreType)backingType defer:(BOOL)flag driver:(VideoDriver_Cocoa *)drv; -- (void)miniaturize:(id)sender; - (void)display; - (void)setFrame:(NSRect)frameRect display:(BOOL)flag; -- (void)appDidHide:(NSNotification*)note; -- (void)appDidUnhide:(NSNotification*)note; -- (id)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)styleMask backing:(NSBackingStoreType)backingType defer:(BOOL)flag; @end -/** Subclass of NSView to fix Quartz rendering and mouse awareness */ +/** Subclass of NSView to support mouse awareness and text input. */ @interface OTTD_CocoaView : NSView -{ - CocoaSubdriver *driver; - NSTrackingRectTag trackingtag; -} -- (void)setDriver:(CocoaSubdriver*)drv; -- (void)drawRect:(NSRect)rect; -- (BOOL)isOpaque; -- (BOOL)acceptsFirstResponder; -- (BOOL)becomeFirstResponder; -- (void)setTrackingRect; -- (void)clearTrackingRect; -- (void)resetCursorRects; -- (void)viewWillMoveToWindow:(NSWindow *)win; -- (void)viewDidMoveToWindow; -- (void)mouseEntered:(NSEvent *)theEvent; -- (void)mouseExited:(NSEvent *)theEvent; +- (NSRect)getRealRect:(NSRect)rect; +- (NSRect)getVirtualRect:(NSRect)rect; +- (CGFloat)getContentsScale; +- (NSPoint)mousePositionFromEvent:(NSEvent *)e; @end /** Delegate for our NSWindow to send ask for quit on close */ @interface OTTD_CocoaWindowDelegate : NSObject -{ - CocoaSubdriver *driver; -} - -- (void)setDriver:(CocoaSubdriver*)drv; +- (instancetype)initWithDriver:(VideoDriver_Cocoa *)drv; - (BOOL)windowShouldClose:(id)sender; - (void)windowDidEnterFullScreen:(NSNotification *)aNotification; -- (void)windowDidChangeScreenProfile:(NSNotification *)aNotification; +- (void)windowDidChangeBackingProperties:(NSNotification *)notification; - (NSApplicationPresentationOptions)window:(NSWindow *)window willUseFullScreenPresentationOptions:(NSApplicationPresentationOptions)proposedOptions; @end diff --git a/src/video/cocoa/cocoa_wnd.mm b/src/video/cocoa/cocoa_wnd.mm index 8c16dd1ef6..78e942c92f 100644 --- a/src/video/cocoa/cocoa_wnd.mm +++ b/src/video/cocoa/cocoa_wnd.mm @@ -24,14 +24,20 @@ #include "../../openttd.h" #include "../../debug.h" +#include "../../rev.h" #include "cocoa_v.h" #include "cocoa_wnd.h" +#include "../../settings_type.h" #include "../../string_func.h" #include "../../gfx_func.h" #include "../../window_func.h" #include "../../window_gui.h" +/* Table data for key mapping. */ +#include "cocoa_keys.h" + + /** * Important notice regarding all modifications!!!!!!! * There are certain limitations because the file is objective C++. @@ -40,14 +46,83 @@ * Read http://developer.apple.com/releasenotes/Cocoa/Objective-C++.html for more information. */ +bool _allow_hidpi_window = true; // Referenced from table/misc_settings.ini + @interface OTTDMain : NSObject @end NSString *OTTDMainLaunchGameEngine = @"ottdmain_launch_game_engine"; +bool _tab_is_down; + static bool _cocoa_video_dialog = false; static OTTDMain *_ottd_main; + +/** + * Count the number of UTF-16 code points in a range of an UTF-8 string. + * @param from Start of the range. + * @param to End of the range. + * @return Number of UTF-16 code points in the range. + */ +static NSUInteger CountUtf16Units(const char *from, const char *to) +{ + NSUInteger i = 0; + + while (from < to) { + WChar c; + size_t len = Utf8Decode(&c, from); + i += len < 4 ? 1 : 2; // Watch for surrogate pairs. + from += len; + } + + return i; +} + +/** + * Advance an UTF-8 string by a number of equivalent UTF-16 code points. + * @param str UTF-8 string. + * @param count Number of UTF-16 code points to advance the string by. + * @return Advanced string pointer. + */ +static const char *Utf8AdvanceByUtf16Units(const char *str, NSUInteger count) +{ + for (NSUInteger i = 0; i < count && *str != '\0'; ) { + WChar c; + size_t len = Utf8Decode(&c, str); + i += len < 4 ? 1 : 2; // Watch for surrogates. + str += len; + } + + return str; +} + +/** + * Convert a NSString to an UTF-32 encoded string. + * @param s String to convert. + * @return Vector of UTF-32 characters. + */ +static std::vector NSStringToUTF32(NSString *s) +{ + std::vector unicode_str; + + unichar lead = 0; + for (NSUInteger i = 0; i < s.length; i++) { + unichar c = [ s characterAtIndex:i ]; + if (Utf16IsLeadSurrogate(c)) { + lead = c; + continue; + } else if (Utf16IsTrailSurrogate(c)) { + if (lead != 0) unicode_str.push_back(Utf16DecodeSurrogate(lead, c)); + } else { + unicode_str.push_back(c); + } + } + + return unicode_str; +} + + /** * The main class of the application, the application's delegate. */ @@ -69,11 +144,15 @@ static OTTDMain *_ottd_main; */ - (void)launchGameEngine: (NSNotification*) note { + auto *drv = static_cast(VideoDriver::GetInstance()); + /* Setup cursor for the current _game_mode. */ - [ _cocoa_subdriver->cocoaview resetCursorRects ]; + NSEvent *e = [ [ NSEvent alloc ] init ]; + [ drv->cocoaview cursorUpdate:e ]; + [ e release ]; /* Hand off to main application code. */ - static_cast(VideoDriver::GetInstance())->GameLoop(); + drv->GameLoop(); /* We are done, thank you for playing. */ [ self performSelectorOnMainThread:@selector(stopEngine) withObject:nil waitUntilDone:FALSE ]; @@ -98,7 +177,7 @@ static OTTDMain *_ottd_main; { HandleExitGameRequest(); - return NSTerminateCancel; // NSTerminateLater ? + return NSTerminateCancel; } /** @@ -203,6 +282,7 @@ bool CocoaSetupApplication() } /* Become the front process, important when start from the command line. */ + [ [ NSApplication sharedApplication ] setActivationPolicy:NSApplicationActivationPolicyRegular ]; [ [ NSApplication sharedApplication ] activateIgnoringOtherApps:YES ]; /* Set up the menubar */ @@ -218,7 +298,7 @@ bool CocoaSetupApplication() } /** - * + * Deregister app delegate. */ void CocoaExitApplication() { @@ -247,15 +327,17 @@ void CocoaDialog(const char *title, const char *message, const char *buttonLabel return; } - NSAlert *alert = [ [ NSAlert alloc ] init ]; - [ alert setAlertStyle: NSCriticalAlertStyle ]; - [ alert setMessageText:[ NSString stringWithUTF8String:title ] ]; - [ alert setInformativeText:[ NSString stringWithUTF8String:message ] ]; - [ alert addButtonWithTitle: [ NSString stringWithUTF8String:buttonLabel ] ]; - [ alert runModal ]; - [ alert release ]; + @autoreleasepool { + NSAlert *alert = [ [ NSAlert alloc ] init ]; + [ alert setAlertStyle: NSCriticalAlertStyle ]; + [ alert setMessageText:[ NSString stringWithUTF8String:title ] ]; + [ alert setInformativeText:[ NSString stringWithUTF8String:message ] ]; + [ alert addButtonWithTitle: [ NSString stringWithUTF8String:buttonLabel ] ]; + [ alert runModal ]; + [ alert release ]; + } - if (!wasstarted && VideoDriver::GetInstance() != NULL) VideoDriver::GetInstance()->Stop(); + if (!wasstarted && VideoDriver::GetInstance() != nullptr) VideoDriver::GetInstance()->Stop(); _cocoa_video_dialog = false; } @@ -280,21 +362,28 @@ void CocoaDialog(const char *title, const char *message, const char *buttonLabel } @end -@implementation OTTD_CocoaWindow - -- (void)setDriver:(CocoaSubdriver*)drv -{ - driver = drv; +@implementation OTTD_CocoaWindow { + VideoDriver_Cocoa *driver; } + /** - * Minimize the window + * Initialize event system for the application rectangle */ -- (void)miniaturize:(id)sender +- (instancetype)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)styleMask backing:(NSBackingStoreType)backingType defer:(BOOL)flag driver:(VideoDriver_Cocoa *)drv { - /* window is hidden now */ - driver->active = false; + if (self = [ super initWithContentRect:contentRect styleMask:styleMask backing:backingType defer:flag ]) { + self->driver = drv; + + [ self setContentMinSize:NSMakeSize(64.0f, 64.0f) ]; + + std::string caption = std::string{"OpenTTD "} + _openttd_revision; + NSString *nsscaption = [ [ NSString alloc ] initWithUTF8String:caption.c_str() ]; + [ self setTitle:nsscaption ]; + [ self setMiniwindowTitle:nsscaption ]; + [ nsscaption release ]; + } - [ super miniaturize:sender ]; + return self; } /** @@ -313,9 +402,6 @@ void CocoaDialog(const char *title, const char *message, const char *buttonLabel /* restore visible surface */ [ self restoreCachedImage ]; - - /* window is visible again */ - driver->active = true; } /** * Define the rectangle we draw our window in @@ -324,175 +410,375 @@ void CocoaDialog(const char *title, const char *message, const char *buttonLabel { [ super setFrame:frameRect display:flag ]; - /* Don't do anything if the window is currently being created */ - if (driver->setup) return; + driver->AllocateBackingStore(); +} + +@end - if (!driver->WindowResized()) error("Cocoa: Failed to resize window."); +@implementation OTTD_CocoaView { + float _current_magnification; + NSUInteger _current_mods; + bool _emulated_down; + bool _use_hidpi; ///< Render content in native resolution? } -/** - * Handle hiding of the application - */ -- (void)appDidHide:(NSNotification*)note + +- (instancetype)initWithFrame:(NSRect)frameRect { - driver->active = false; + if (self = [ super initWithFrame:frameRect ]) { + self->_use_hidpi = _allow_hidpi_window && [ self respondsToSelector:@selector(convertRectToBacking:) ] && [ self respondsToSelector:@selector(convertRectFromBacking:) ]; + } + return self; } -/** - * Unhide and restore display plane and re-activate driver - */ -- (void)appDidUnhide:(NSNotification*)note + +- (NSRect)getRealRect:(NSRect)rect { - driver->active = true; + return _use_hidpi ? [ self convertRectToBacking:rect ] : rect; } -/** - * Initialize event system for the application rectangle - */ -- (id)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)styleMask backing:(NSBackingStoreType)backingType defer:(BOOL)flag -{ - /* Make our window subclass receive these application notifications */ - [ [ NSNotificationCenter defaultCenter ] addObserver:self - selector:@selector(appDidHide:) name:NSApplicationDidHideNotification object:NSApp ]; - [ [ NSNotificationCenter defaultCenter ] addObserver:self - selector:@selector(appDidUnhide:) name:NSApplicationDidUnhideNotification object:NSApp ]; - - return [ super initWithContentRect:contentRect styleMask:styleMask backing:backingType defer:flag ]; +- (NSRect)getVirtualRect:(NSRect)rect +{ + return _use_hidpi ? [ self convertRectFromBacking:rect ] : rect; } -@end - - +- (CGFloat)getContentsScale +{ + return _use_hidpi && self.window != nil ? [ self.window backingScaleFactor ] : 1.0f; +} /** - * Count the number of UTF-16 code points in a range of an UTF-8 string. - * @param from Start of the range. - * @param to End of the range. - * @return Number of UTF-16 code points in the range. + * Allow to handle events */ -static NSUInteger CountUtf16Units(const char *from, const char *to) +- (BOOL)acceptsFirstResponder { - NSUInteger i = 0; + return YES; +} - while (from < to) { - WChar c; - size_t len = Utf8Decode(&c, from); - i += len < 4 ? 1 : 2; // Watch for surrogate pairs. - from += len; +- (void)setNeedsDisplayInRect:(NSRect)invalidRect +{ + /* Drawing is handled by our sub-views. Just pass it along. */ + for ( NSView *v in [ self subviews ]) { + [ v setNeedsDisplayInRect:[ v convertRect:invalidRect fromView:self ] ]; } +} - return i; +/** Update mouse cursor to use for this view. */ +- (void)cursorUpdate:(NSEvent *)event +{ + [ (_game_mode == GM_BOOTSTRAP ? [ NSCursor arrowCursor ] : [ NSCursor clearCocoaCursor ]) set ]; } -/** - * Advance an UTF-8 string by a number of equivalent UTF-16 code points. - * @param str UTF-8 string. - * @param count Number of UTF-16 code points to advance the string by. - * @return Advanced string pointer. - */ -static const char *Utf8AdvanceByUtf16Units(const char *str, NSUInteger count) +- (void)viewWillMoveToWindow:(NSWindow *)win { - for (NSUInteger i = 0; i < count && *str != '\0'; ) { - WChar c; - size_t len = Utf8Decode(&c, str); - i += len < 4 ? 1 : 2; // Watch for surrogates. - str += len; + for (NSTrackingArea *a in [ self trackingAreas ]) { + [ self removeTrackingArea:a ]; } - - return str; } -@implementation OTTD_CocoaView -/** - * Initialize the driver - */ -- (void)setDriver:(CocoaSubdriver*)drv +- (void)viewDidMoveToWindow { - driver = drv; + /* Install mouse tracking area. */ + NSTrackingAreaOptions track_opt = NSTrackingInVisibleRect | NSTrackingActiveInActiveApp | NSTrackingMouseEnteredAndExited | NSTrackingMouseMoved | NSTrackingCursorUpdate; + NSTrackingArea *track = [ [ NSTrackingArea alloc ] initWithRect:[ self bounds ] options:track_opt owner:self userInfo:nil ]; + [ self addTrackingArea:track ]; + [ track release ]; } /** - * Define the opaqueness of the window / screen - * @return opaqueness of window / screen + * Make OpenTTD aware that it has control over the mouse */ -- (BOOL)isOpaque +- (void)mouseEntered:(NSEvent *)theEvent { - return YES; + _cursor.in_window = true; } /** - * Draws a rectangle on the screen. - * It's overwritten by the individual drivers but must be defined + * Make OpenTTD aware that it has NOT control over the mouse */ -- (void)drawRect:(NSRect)invalidRect +- (void)mouseExited:(NSEvent *)theEvent { - return; + if ([ self window ] != nil) UndrawMouseCursor(); + _cursor.in_window = false; } + /** - * Allow to handle events + * Return the mouse location + * @param event UI event + * @return mouse location as NSPoint */ -- (BOOL)acceptsFirstResponder +- (NSPoint)mousePositionFromEvent:(NSEvent *)e { - return YES; + NSPoint pt = e.locationInWindow; + if ([ e window ] == nil) pt = [ self.window convertRectFromScreen:NSMakeRect(pt.x, pt.y, 0, 0) ].origin; + pt = [ self convertPoint:pt fromView:nil ]; + + return [ self getRealRect:NSMakeRect(pt.x, self.bounds.size.height - pt.y, 0, 0) ].origin; } -/** - * Actually handle events - */ -- (BOOL)becomeFirstResponder + +- (void)internalMouseMoveEvent:(NSEvent *)event { - return YES; + if (_cursor.fix_at) { + _cursor.UpdateCursorPositionRelative(event.deltaX * self.getContentsScale, event.deltaY * self.getContentsScale); + } else { + NSPoint pt = [ self mousePositionFromEvent:event ]; + _cursor.UpdateCursorPosition(pt.x, pt.y, false); + } + + HandleMouseEvents(); } -/** - * Define the rectangle where we draw our application window - */ -- (void)setTrackingRect + +- (void)internalMouseButtonEvent { - NSPoint loc = [ self convertPoint:[ [ self window ] mouseLocationOutsideOfEventStream ] fromView:nil ]; - BOOL inside = ([ self hitTest:loc ]==self); - if (inside) [ [ self window ] makeFirstResponder:self ]; - trackingtag = [ self addTrackingRect:[ self visibleRect ] owner:self userData:nil assumeInside:inside ]; + bool cur_fix = _cursor.fix_at; + HandleMouseEvents(); + + /* Cursor fix mode was changed, synchronize with OS. */ + if (cur_fix != _cursor.fix_at) CGAssociateMouseAndMouseCursorPosition(!_cursor.fix_at); } -/** - * Return responsibility for the application window to system - */ -- (void)clearTrackingRect + +- (BOOL)emulateRightButton:(NSEvent *)event { - [ self removeTrackingRect:trackingtag ]; + uint32 keymask = 0; + if (_settings_client.gui.right_mouse_btn_emulation == RMBE_COMMAND) keymask |= NSCommandKeyMask; + if (_settings_client.gui.right_mouse_btn_emulation == RMBE_CONTROL) keymask |= NSControlKeyMask; + + return (event.modifierFlags & keymask) != 0; } -/** - * Declare responsibility for the cursor within our application rect - */ -- (void)resetCursorRects + +- (void)mouseMoved:(NSEvent *)event { - [ super resetCursorRects ]; - [ self clearTrackingRect ]; - [ self setTrackingRect ]; - [ self addCursorRect:[ self bounds ] cursor:(_game_mode == GM_BOOTSTRAP ? [ NSCursor arrowCursor ] : [ NSCursor clearCocoaCursor ]) ]; + [ self internalMouseMoveEvent:event ]; } -/** - * Prepare for moving the application window - */ -- (void)viewWillMoveToWindow:(NSWindow *)win + +- (void)mouseDragged:(NSEvent *)event { - if (!win && [ self window ]) [ self clearTrackingRect ]; + [ self internalMouseMoveEvent:event ]; } -/** - * Restore our responsibility for our application window after moving - */ -- (void)viewDidMoveToWindow +- (void)mouseDown:(NSEvent *)event { - if ([ self window ]) [ self setTrackingRect ]; + if ([ self emulateRightButton:event ]) { + self->_emulated_down = true; + [ self rightMouseDown:event ]; + } else { + _left_button_down = true; + [ self internalMouseButtonEvent ]; + } } -/** - * Make OpenTTD aware that it has control over the mouse - */ -- (void)mouseEntered:(NSEvent *)theEvent +- (void)mouseUp:(NSEvent *)event { - _cursor.in_window = true; + if (self->_emulated_down) { + self->_emulated_down = false; + [ self rightMouseUp:event ]; + } else { + _left_button_down = false; + _left_button_clicked = false; + [ self internalMouseButtonEvent ]; + } } -/** - * Make OpenTTD aware that it has NOT control over the mouse - */ -- (void)mouseExited:(NSEvent *)theEvent + +- (void)rightMouseDragged:(NSEvent *)event { - if (_cocoa_subdriver != NULL) UndrawMouseCursor(); - _cursor.in_window = false; + [ self internalMouseMoveEvent:event ]; +} +- (void)rightMouseDown:(NSEvent *)event +{ + _right_button_down = true; + _right_button_clicked = true; + [ self internalMouseButtonEvent ]; +} +- (void)rightMouseUp:(NSEvent *)event +{ + _right_button_down = false; + [ self internalMouseButtonEvent ]; +} + +- (void)scrollWheel:(NSEvent *)event +{ + if ([ event deltaY ] > 0.0) { /* Scroll up */ + _cursor.wheel--; + } else if ([ event deltaY ] < 0.0) { /* Scroll down */ + _cursor.wheel++; + } /* else: deltaY was 0.0 and we don't want to do anything */ + + /* Update the scroll count for 2D scrolling */ + CGFloat deltaX; + CGFloat deltaY; + + /* Use precise scrolling-specific deltas if they're supported. */ + if ([ event respondsToSelector:@selector(hasPreciseScrollingDeltas) ]) { + /* No precise deltas indicates a scroll wheel is being used, so we don't want 2D scrolling. */ + if (![ event hasPreciseScrollingDeltas ]) return; + + deltaX = [ event scrollingDeltaX ] * 0.5f; + deltaY = [ event scrollingDeltaY ] * 0.5f; + } else { + deltaX = [ event deltaX ] * 5; + deltaY = [ event deltaY ] * 5; + } + + _cursor.h_wheel -= (int)(deltaX * _settings_client.gui.scrollwheel_multiplier); + _cursor.v_wheel -= (int)(deltaY * _settings_client.gui.scrollwheel_multiplier); +} + +- (void)magnifyWithEvent:(NSEvent *)event +{ + /* Pinch open or close gesture. */ + self->_current_magnification += [ event magnification ] * 5.0f; + + while (self->_current_magnification >= 1.0f) { + self->_current_magnification -= 1.0f; + _cursor.wheel--; + HandleMouseEvents(); + } + while (self->_current_magnification <= -1.0f) { + self->_current_magnification += 1.0f; + _cursor.wheel++; + HandleMouseEvents(); + } +} + +- (void)endGestureWithEvent:(NSEvent *)event +{ + /* Gesture ended. */ + self->_current_magnification = 0.0f; +} + + +- (BOOL)internalHandleKeycode:(unsigned short)keycode unicode:(WChar)unicode pressed:(BOOL)down modifiers:(NSUInteger)modifiers +{ + switch (keycode) { + case QZ_UP: SB(_dirkeys, 1, 1, down); break; + case QZ_DOWN: SB(_dirkeys, 3, 1, down); break; + case QZ_LEFT: SB(_dirkeys, 0, 1, down); break; + case QZ_RIGHT: SB(_dirkeys, 2, 1, down); break; + + case QZ_TAB: _tab_is_down = down; break; + + case QZ_RETURN: + case QZ_f: + if (down && (modifiers & NSCommandKeyMask)) { + VideoDriver::GetInstance()->ToggleFullscreen(!_fullscreen); + } + break; + + case QZ_v: + if (down && EditBoxInGlobalFocus() && (modifiers & (NSCommandKeyMask | NSControlKeyMask))) { + HandleKeypress(WKC_CTRL | 'V', unicode); + } + break; + case QZ_u: + if (down && EditBoxInGlobalFocus() && (modifiers & (NSCommandKeyMask | NSControlKeyMask))) { + HandleKeypress(WKC_CTRL | 'U', unicode); + } + break; + } + + BOOL interpret_keys = YES; + if (down) { + /* Map keycode to OTTD code. */ + auto vk = std::find_if(std::begin(_vk_mapping), std::end(_vk_mapping), [=](const CocoaVkMapping &m) { return m.vk_from == keycode; }); + uint32 pressed_key = vk != std::end(_vk_mapping) ? vk->map_to : 0; + + if (modifiers & NSShiftKeyMask) pressed_key |= WKC_SHIFT; + if (modifiers & NSControlKeyMask) pressed_key |= (_settings_client.gui.right_mouse_btn_emulation != RMBE_CONTROL ? WKC_CTRL : WKC_META); + if (modifiers & NSAlternateKeyMask) pressed_key |= WKC_ALT; + if (modifiers & NSCommandKeyMask) pressed_key |= (_settings_client.gui.right_mouse_btn_emulation != RMBE_CONTROL ? WKC_META : WKC_CTRL); + + static bool console = false; + + /* The second backquote may have a character, which we don't want to interpret. */ + if (pressed_key == WKC_BACKQUOTE && (console || unicode == 0)) { + if (!console) { + /* Backquote is a dead key, require a double press for hotkey behaviour (i.e. console). */ + console = true; + return YES; + } else { + /* Second backquote, don't interpret as text input. */ + interpret_keys = NO; + } + } + console = false; + + /* Don't handle normal characters if an edit box has the focus. */ + if (!EditBoxInGlobalFocus() || IsInsideMM(pressed_key & ~WKC_SPECIAL_KEYS, WKC_F1, WKC_PAUSE + 1)) { + HandleKeypress(pressed_key, unicode); + } + DEBUG(driver, 2, "cocoa_v: QZ_KeyEvent: %x (%x), down, mapping: %x", keycode, unicode, pressed_key); + } else { + DEBUG(driver, 2, "cocoa_v: QZ_KeyEvent: %x (%x), up", keycode, unicode); + } + + return interpret_keys; +} + +- (void)keyDown:(NSEvent *)event +{ + /* Quit, hide and minimize */ + switch (event.keyCode) { + case QZ_q: + case QZ_h: + case QZ_m: + if (event.modifierFlags & NSCommandKeyMask) { + [ self interpretKeyEvents:[ NSArray arrayWithObject:event ] ]; + } + break; + } + + /* Convert UTF-16 characters to UCS-4 chars. */ + std::vector unicode_str = NSStringToUTF32([ event characters ]); + if (unicode_str.empty()) unicode_str.push_back(0); + + if (EditBoxInGlobalFocus()) { + if ([ self internalHandleKeycode:event.keyCode unicode:unicode_str[0] pressed:YES modifiers:event.modifierFlags ]) { + [ self interpretKeyEvents:[ NSArray arrayWithObject:event ] ]; + } + } else { + [ self internalHandleKeycode:event.keyCode unicode:unicode_str[0] pressed:YES modifiers:event.modifierFlags ]; + for (size_t i = 1; i < unicode_str.size(); i++) { + [ self internalHandleKeycode:0 unicode:unicode_str[i] pressed:YES modifiers:event.modifierFlags ]; + } + } +} + +- (void)keyUp:(NSEvent *)event +{ + /* Quit, hide and minimize */ + switch (event.keyCode) { + case QZ_q: + case QZ_h: + case QZ_m: + if (event.modifierFlags & NSCommandKeyMask) { + [ self interpretKeyEvents:[ NSArray arrayWithObject:event ] ]; + } + break; + } + + /* Convert UTF-16 characters to UCS-4 chars. */ + std::vector unicode_str = NSStringToUTF32([ event characters ]); + if (unicode_str.empty()) unicode_str.push_back(0); + + [ self internalHandleKeycode:event.keyCode unicode:unicode_str[0] pressed:NO modifiers:event.modifierFlags ]; +} + +- (void)flagsChanged:(NSEvent *)event +{ + const int mapping[] = { QZ_CAPSLOCK, QZ_LSHIFT, QZ_LCTRL, QZ_LALT, QZ_LMETA }; + + NSUInteger newMods = event.modifierFlags; + if (self->_current_mods == newMods) return; + + /* Iterate through the bits, testing each against the current modifiers */ + for (unsigned int i = 0, bit = NSAlphaShiftKeyMask; bit <= NSCommandKeyMask; bit <<= 1, ++i) { + unsigned int currentMask, newMask; + + currentMask = self->_current_mods & bit; + newMask = newMods & bit; + + if (currentMask && currentMask != newMask) { // modifier up event + [ self internalHandleKeycode:mapping[i] unicode:0 pressed:NO modifiers:newMods ]; + } else if (newMask && currentMask != newMask) { // modifier down event + [ self internalHandleKeycode:mapping[i] unicode:0 pressed:YES modifiers:newMods ]; + } + } + + _current_mods = newMods; } @@ -555,7 +841,7 @@ static const char *Utf8AdvanceByUtf16Units(const char *str, NSUInteger count) /** Unmark the current marked text. */ - (void)unmarkText { - HandleTextInput(NULL, true); + HandleTextInput(nullptr, true); } /** Get the caret position. */ @@ -574,7 +860,7 @@ static const char *Utf8AdvanceByUtf16Units(const char *str, NSUInteger count) size_t mark_len; const char *mark = _focused_window->GetMarkedText(&mark_len); - if (mark != NULL) { + if (mark != nullptr) { NSUInteger start = CountUtf16Units(_focused_window->GetFocusedText(), mark); NSUInteger len = CountUtf16Units(mark, mark + mark_len); @@ -590,7 +876,7 @@ static const char *Utf8AdvanceByUtf16Units(const char *str, NSUInteger count) if (!EditBoxInGlobalFocus()) return NO; size_t len; - return _focused_window->GetMarkedText(&len) != NULL; + return _focused_window->GetMarkedText(&len) != nullptr; } /** Get a string corresponding to the given range. */ @@ -601,7 +887,7 @@ static const char *Utf8AdvanceByUtf16Units(const char *str, NSUInteger count) NSString *s = [ NSString stringWithUTF8String:_focused_window->GetFocusedText() ]; NSRange valid_range = NSIntersectionRange(NSMakeRange(0, [ s length ]), theRange); - if (actualRange != NULL) *actualRange = valid_range; + if (actualRange != nullptr) *actualRange = valid_range; if (valid_range.length == 0) return nil; return [ [ [ NSAttributedString alloc ] initWithString:[ s substringWithRange:valid_range ] ] autorelease ]; @@ -610,7 +896,7 @@ static const char *Utf8AdvanceByUtf16Units(const char *str, NSUInteger count) /** Get a string corresponding to the given range. */ - (NSAttributedString *)attributedSubstringFromRange:(NSRange)theRange { - return [ self attributedSubstringForProposedRange:theRange actualRange:NULL ]; + return [ self attributedSubstringForProposedRange:theRange actualRange:nil ]; } /** Get the current edit box string. */ @@ -631,7 +917,7 @@ static const char *Utf8AdvanceByUtf16Units(const char *str, NSUInteger count) Point pt = { (int)view_pt.x, (int)[ self frame ].size.height - (int)view_pt.y }; const char *ch = _focused_window->GetTextCharacterAtPosition(pt); - if (ch == NULL) return NSNotFound; + if (ch == nullptr) return NSNotFound; return CountUtf16Units(_focused_window->GetFocusedText(), ch); } @@ -807,12 +1093,17 @@ static const char *Utf8AdvanceByUtf16Units(const char *str, NSUInteger count) @end +@implementation OTTD_CocoaWindowDelegate { + VideoDriver_Cocoa *driver; +} -@implementation OTTD_CocoaWindowDelegate /** Initialize the video driver */ -- (void)setDriver:(CocoaSubdriver*)drv +- (instancetype)initWithDriver:(VideoDriver_Cocoa *)drv { - driver = drv; + if (self = [ super init ]) { + self->driver = drv; + } + return self; } /** Handle closure requests */ - (BOOL)windowShouldClose:(id)sender @@ -821,26 +1112,6 @@ static const char *Utf8AdvanceByUtf16Units(const char *str, NSUInteger count) return NO; } -/** Handle key acceptance */ -- (void)windowDidBecomeKey:(NSNotification*)aNotification -{ - driver->active = true; -} -/** Resign key acceptance */ -- (void)windowDidResignKey:(NSNotification*)aNotification -{ - driver->active = false; -} -/** Handle becoming main window */ -- (void)windowDidBecomeMain:(NSNotification*)aNotification -{ - driver->active = true; -} -/** Resign being main window */ -- (void)windowDidResignMain:(NSNotification*)aNotification -{ - driver->active = false; -} /** Window entered fullscreen mode (10.7). */ - (void)windowDidEnterFullScreen:(NSNotification *)aNotification { @@ -854,10 +1125,11 @@ static const char *Utf8AdvanceByUtf16Units(const char *str, NSUInteger count) [ e release ]; } } -/** The colour profile of the screen the window is on changed. */ -- (void)windowDidChangeScreenProfile:(NSNotification *)aNotification +/** Screen the window is on changed. */ +- (void)windowDidChangeBackingProperties:(NSNotification *)notification { - if (!driver->setup) driver->WindowResized(); + /* Reallocate screen buffer if necessary. */ + driver->AllocateBackingStore(); } /** Presentation options to use for fullsreen mode. */ @@ -867,4 +1139,5 @@ static const char *Utf8AdvanceByUtf16Units(const char *str, NSUInteger count) } @end + #endif /* WITH_COCOA */ diff --git a/src/video/cocoa/event.mm b/src/video/cocoa/event.mm deleted file mode 100644 index aad049541e..0000000000 --- a/src/video/cocoa/event.mm +++ /dev/null @@ -1,720 +0,0 @@ -/* - * This file is part of OpenTTD. - * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. - * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . - */ - -/****************************************************************************** - * Cocoa video driver * - * Known things left to do: * - * Nothing at the moment. * - ******************************************************************************/ - -#ifdef WITH_COCOA - -#include "../../stdafx.h" - -#define Rect OTTDRect -#define Point OTTDPoint -#import -#undef Rect -#undef Point - -#include "../../openttd.h" -#include "../../debug.h" -#include "../../os/macosx/splash.h" -#include "../../settings_type.h" -#include "../../core/geometry_type.hpp" -#include "cocoa_v.h" -#include "cocoa_keys.h" -#include "../../blitter/factory.hpp" -#include "../../gfx_func.h" -#include "../../network/network.h" -#include "../../core/random_func.hpp" -#include "../../core/math_func.hpp" -#include "../../texteff.hpp" -#include "../../window_func.h" -#include "../../thread.h" - -#import /* gettimeofday */ - -/** - * Important notice regarding all modifications!!!!!!! - * There are certain limitations because the file is objective C++. - * gdb has limitations. - * C++ and objective C code can't be joined in all cases (classes stuff). - * Read http://developer.apple.com/releasenotes/Cocoa/Objective-C++.html for more information. - */ - - -/* Right Mouse Button Emulation enum */ -enum RightMouseButtonEmulationState { - RMBE_COMMAND, - RMBE_CONTROL, - RMBE_OFF, -}; - - -static unsigned int _current_mods; -static bool _tab_is_down; -static bool _emulating_right_button; -static float _current_magnification; -#ifdef _DEBUG -static uint32 _tEvent; -#endif - - -static uint32 GetTick() -{ - struct timeval tim; - - gettimeofday(&tim, nullptr); - return tim.tv_usec / 1000 + tim.tv_sec * 1000; -} - -static void QZ_WarpCursor(int x, int y) -{ - assert(_cocoa_subdriver != nullptr); - - /* Only allow warping when in foreground */ - if (![ NSApp isActive ]) return; - - NSPoint p = NSMakePoint(x, y); - CGPoint cgp = _cocoa_subdriver->PrivateLocalToCG(&p); - - /* Do the actual warp */ - CGWarpMouseCursorPosition(cgp); - /* this is the magic call that fixes cursor "freezing" after warp */ - CGAssociateMouseAndMouseCursorPosition(true); -} - - -static void QZ_CheckPaletteAnim() -{ - if (_cur_palette.count_dirty != 0) { - Blitter *blitter = BlitterFactory::GetCurrentBlitter(); - - switch (blitter->UsePaletteAnimation()) { - case Blitter::PALETTE_ANIMATION_VIDEO_BACKEND: - _cocoa_subdriver->UpdatePalette(_cur_palette.first_dirty, _cur_palette.count_dirty); - break; - - case Blitter::PALETTE_ANIMATION_BLITTER: - blitter->PaletteAnimate(_cur_palette); - break; - - case Blitter::PALETTE_ANIMATION_NONE: - break; - - default: - NOT_REACHED(); - } - _cur_palette.count_dirty = 0; - } -} - - - -struct VkMapping { - unsigned short vk_from; - byte map_to; -}; - -#define AS(x, z) {x, z} - -static const VkMapping _vk_mapping[] = { - AS(QZ_BACKQUOTE, WKC_BACKQUOTE), // key left of '1' - AS(QZ_BACKQUOTE2, WKC_BACKQUOTE), // some keyboards have it on another scancode - - /* Pageup stuff + up/down */ - AS(QZ_PAGEUP, WKC_PAGEUP), - AS(QZ_PAGEDOWN, WKC_PAGEDOWN), - - AS(QZ_UP, WKC_UP), - AS(QZ_DOWN, WKC_DOWN), - AS(QZ_LEFT, WKC_LEFT), - AS(QZ_RIGHT, WKC_RIGHT), - - AS(QZ_HOME, WKC_HOME), - AS(QZ_END, WKC_END), - - AS(QZ_INSERT, WKC_INSERT), - AS(QZ_DELETE, WKC_DELETE), - - /* Letters. QZ_[a-z] is not in numerical order so we can't use AM(...) */ - AS(QZ_a, 'A'), - AS(QZ_b, 'B'), - AS(QZ_c, 'C'), - AS(QZ_d, 'D'), - AS(QZ_e, 'E'), - AS(QZ_f, 'F'), - AS(QZ_g, 'G'), - AS(QZ_h, 'H'), - AS(QZ_i, 'I'), - AS(QZ_j, 'J'), - AS(QZ_k, 'K'), - AS(QZ_l, 'L'), - AS(QZ_m, 'M'), - AS(QZ_n, 'N'), - AS(QZ_o, 'O'), - AS(QZ_p, 'P'), - AS(QZ_q, 'Q'), - AS(QZ_r, 'R'), - AS(QZ_s, 'S'), - AS(QZ_t, 'T'), - AS(QZ_u, 'U'), - AS(QZ_v, 'V'), - AS(QZ_w, 'W'), - AS(QZ_x, 'X'), - AS(QZ_y, 'Y'), - AS(QZ_z, 'Z'), - /* Same thing for digits */ - AS(QZ_0, '0'), - AS(QZ_1, '1'), - AS(QZ_2, '2'), - AS(QZ_3, '3'), - AS(QZ_4, '4'), - AS(QZ_5, '5'), - AS(QZ_6, '6'), - AS(QZ_7, '7'), - AS(QZ_8, '8'), - AS(QZ_9, '9'), - - AS(QZ_ESCAPE, WKC_ESC), - AS(QZ_PAUSE, WKC_PAUSE), - AS(QZ_BACKSPACE, WKC_BACKSPACE), - - AS(QZ_SPACE, WKC_SPACE), - AS(QZ_RETURN, WKC_RETURN), - AS(QZ_TAB, WKC_TAB), - - /* Function keys */ - AS(QZ_F1, WKC_F1), - AS(QZ_F2, WKC_F2), - AS(QZ_F3, WKC_F3), - AS(QZ_F4, WKC_F4), - AS(QZ_F5, WKC_F5), - AS(QZ_F6, WKC_F6), - AS(QZ_F7, WKC_F7), - AS(QZ_F8, WKC_F8), - AS(QZ_F9, WKC_F9), - AS(QZ_F10, WKC_F10), - AS(QZ_F11, WKC_F11), - AS(QZ_F12, WKC_F12), - - /* Numeric part */ - AS(QZ_KP0, '0'), - AS(QZ_KP1, '1'), - AS(QZ_KP2, '2'), - AS(QZ_KP3, '3'), - AS(QZ_KP4, '4'), - AS(QZ_KP5, '5'), - AS(QZ_KP6, '6'), - AS(QZ_KP7, '7'), - AS(QZ_KP8, '8'), - AS(QZ_KP9, '9'), - AS(QZ_KP_DIVIDE, WKC_NUM_DIV), - AS(QZ_KP_MULTIPLY, WKC_NUM_MUL), - AS(QZ_KP_MINUS, WKC_NUM_MINUS), - AS(QZ_KP_PLUS, WKC_NUM_PLUS), - AS(QZ_KP_ENTER, WKC_NUM_ENTER), - AS(QZ_KP_PERIOD, WKC_NUM_DECIMAL), - - /* Other non-letter keys */ - AS(QZ_SLASH, WKC_SLASH), - AS(QZ_SEMICOLON, WKC_SEMICOLON), - AS(QZ_EQUALS, WKC_EQUALS), - AS(QZ_LEFTBRACKET, WKC_L_BRACKET), - AS(QZ_BACKSLASH, WKC_BACKSLASH), - AS(QZ_RIGHTBRACKET, WKC_R_BRACKET), - - AS(QZ_QUOTE, WKC_SINGLEQUOTE), - AS(QZ_COMMA, WKC_COMMA), - AS(QZ_MINUS, WKC_MINUS), - AS(QZ_PERIOD, WKC_PERIOD) -}; - - -static uint32 QZ_MapKey(unsigned short sym) -{ - uint32 key = 0; - - for (const VkMapping *map = _vk_mapping; map != endof(_vk_mapping); ++map) { - if (sym == map->vk_from) { - key = map->map_to; - break; - } - } - - if (_current_mods & NSShiftKeyMask) key |= WKC_SHIFT; - if (_current_mods & NSControlKeyMask) key |= (_settings_client.gui.right_mouse_btn_emulation != RMBE_CONTROL ? WKC_CTRL : WKC_META); - if (_current_mods & NSAlternateKeyMask) key |= WKC_ALT; - if (_current_mods & NSCommandKeyMask) key |= (_settings_client.gui.right_mouse_btn_emulation != RMBE_CONTROL ? WKC_META : WKC_CTRL); - - return key; -} - -static bool QZ_KeyEvent(unsigned short keycode, unsigned short unicode, BOOL down) -{ - bool interpret_keys = true; - - switch (keycode) { - case QZ_UP: SB(_dirkeys, 1, 1, down); break; - case QZ_DOWN: SB(_dirkeys, 3, 1, down); break; - case QZ_LEFT: SB(_dirkeys, 0, 1, down); break; - case QZ_RIGHT: SB(_dirkeys, 2, 1, down); break; - - case QZ_TAB: _tab_is_down = down; break; - - case QZ_RETURN: - case QZ_f: - if (down && (_current_mods & NSCommandKeyMask)) { - VideoDriver::GetInstance()->ToggleFullscreen(!_fullscreen); - } - break; - - case QZ_v: - if (down && EditBoxInGlobalFocus() && (_current_mods & (NSCommandKeyMask | NSControlKeyMask))) { - HandleKeypress(WKC_CTRL | 'V', unicode); - } - break; - case QZ_u: - if (down && EditBoxInGlobalFocus() && (_current_mods & (NSCommandKeyMask | NSControlKeyMask))) { - HandleKeypress(WKC_CTRL | 'U', unicode); - } - break; - } - - if (down) { - uint32 pressed_key = QZ_MapKey(keycode); - - static bool console = false; - - /* The second backquote may have a character, which we don't want to interpret. */ - if (pressed_key == WKC_BACKQUOTE && (console || unicode == 0)) { - if (!console) { - /* Backquote is a dead key, require a double press for hotkey behaviour (i.e. console). */ - console = true; - return true; - } else { - /* Second backquote, don't interpret as text input. */ - interpret_keys = false; - } - } - console = false; - - /* Don't handle normal characters if an edit box has the focus. */ - if (!EditBoxInGlobalFocus() || IsInsideMM(pressed_key & ~WKC_SPECIAL_KEYS, WKC_F1, WKC_PAUSE + 1)) { - HandleKeypress(pressed_key, unicode); - } - DEBUG(driver, 2, "cocoa_v: QZ_KeyEvent: %x (%x), down, mapping: %x", keycode, unicode, pressed_key); - } else { - DEBUG(driver, 2, "cocoa_v: QZ_KeyEvent: %x (%x), up", keycode, unicode); - } - - return interpret_keys; -} - -static void QZ_DoUnsidedModifiers(unsigned int newMods) -{ - const int mapping[] = { QZ_CAPSLOCK, QZ_LSHIFT, QZ_LCTRL, QZ_LALT, QZ_LMETA }; - - if (_current_mods == newMods) return; - - /* Iterate through the bits, testing each against the current modifiers */ - for (unsigned int i = 0, bit = NSAlphaShiftKeyMask; bit <= NSCommandKeyMask; bit <<= 1, ++i) { - unsigned int currentMask, newMask; - - currentMask = _current_mods & bit; - newMask = newMods & bit; - - if (currentMask && currentMask != newMask) { // modifier up event - /* If this was Caps Lock, we need some additional voodoo to make SDL happy (is this needed in ottd?) */ - if (bit == NSAlphaShiftKeyMask) QZ_KeyEvent(mapping[i], 0, YES); - QZ_KeyEvent(mapping[i], 0, NO); - } else if (newMask && currentMask != newMask) { // modifier down event - QZ_KeyEvent(mapping[i], 0, YES); - /* If this was Caps Lock, we need some additional voodoo to make SDL happy (is this needed in ottd?) */ - if (bit == NSAlphaShiftKeyMask) QZ_KeyEvent(mapping[i], 0, NO); - } - } - - _current_mods = newMods; -} - -static void QZ_MouseMovedEvent(int x, int y) -{ - if (_cursor.UpdateCursorPosition(x, y, false)) { - QZ_WarpCursor(_cursor.pos.x, _cursor.pos.y); - } - HandleMouseEvents(); -} - - -static void QZ_MouseButtonEvent(int button, BOOL down) -{ - switch (button) { - case 0: - if (down) { - _left_button_down = true; - } else { - _left_button_down = false; - _left_button_clicked = false; - } - HandleMouseEvents(); - break; - - case 1: - if (down) { - _right_button_down = true; - _right_button_clicked = true; - } else { - _right_button_down = false; - } - HandleMouseEvents(); - break; - } -} - - - - -static bool QZ_PollEvent() -{ - assert(_cocoa_subdriver != nullptr); - -#ifdef _DEBUG - uint32 et0 = GetTick(); -#endif - NSEvent *event = [ NSApp nextEventMatchingMask:NSAnyEventMask - untilDate:[ NSDate distantPast ] - inMode:NSDefaultRunLoopMode dequeue:YES ]; -#ifdef _DEBUG - _tEvent += GetTick() - et0; -#endif - - if (event == nil) return false; - if (!_cocoa_subdriver->IsActive()) { - [ NSApp sendEvent:event ]; - return true; - } - - QZ_DoUnsidedModifiers( [ event modifierFlags ] ); - - NSString *chars; - NSPoint pt; - switch ([ event type ]) { - case NSMouseMoved: - case NSOtherMouseDragged: - case NSLeftMouseDragged: - pt = _cocoa_subdriver->GetMouseLocation(event); - if (!_cocoa_subdriver->MouseIsInsideView(&pt) && !_emulating_right_button) { - [ NSApp sendEvent:event ]; - break; - } - - QZ_MouseMovedEvent((int)pt.x, (int)pt.y); - break; - - case NSRightMouseDragged: - pt = _cocoa_subdriver->GetMouseLocation(event); - QZ_MouseMovedEvent((int)pt.x, (int)pt.y); - break; - - case NSLeftMouseDown: - { - uint32 keymask = 0; - if (_settings_client.gui.right_mouse_btn_emulation == RMBE_COMMAND) keymask |= NSCommandKeyMask; - if (_settings_client.gui.right_mouse_btn_emulation == RMBE_CONTROL) keymask |= NSControlKeyMask; - - pt = _cocoa_subdriver->GetMouseLocation(event); - - if (!([ event modifierFlags ] & keymask) || !_cocoa_subdriver->MouseIsInsideView(&pt)) { - [ NSApp sendEvent:event ]; - } - - QZ_MouseMovedEvent((int)pt.x, (int)pt.y); - - /* Right mouse button emulation */ - if ([ event modifierFlags ] & keymask) { - _emulating_right_button = true; - QZ_MouseButtonEvent(1, YES); - } else { - QZ_MouseButtonEvent(0, YES); - } - break; - } - case NSLeftMouseUp: - [ NSApp sendEvent:event ]; - - pt = _cocoa_subdriver->GetMouseLocation(event); - - QZ_MouseMovedEvent((int)pt.x, (int)pt.y); - - /* Right mouse button emulation */ - if (_emulating_right_button) { - _emulating_right_button = false; - QZ_MouseButtonEvent(1, NO); - } else { - QZ_MouseButtonEvent(0, NO); - } - break; - - case NSRightMouseDown: - pt = _cocoa_subdriver->GetMouseLocation(event); - if (!_cocoa_subdriver->MouseIsInsideView(&pt)) { - [ NSApp sendEvent:event ]; - break; - } - - QZ_MouseMovedEvent((int)pt.x, (int)pt.y); - QZ_MouseButtonEvent(1, YES); - break; - - case NSRightMouseUp: - pt = _cocoa_subdriver->GetMouseLocation(event); - if (!_cocoa_subdriver->MouseIsInsideView(&pt)) { - [ NSApp sendEvent:event ]; - break; - } - - QZ_MouseMovedEvent((int)pt.x, (int)pt.y); - QZ_MouseButtonEvent(1, NO); - break; - -#if 0 - /* This is not needed since openttd currently only use two buttons */ - case NSOtherMouseDown: - pt = QZ_GetMouseLocation(event); - if (!QZ_MouseIsInsideView(&pt)) { - [ NSApp sendEvent:event ]; - break; - } - - QZ_MouseMovedEvent((int)pt.x, (int)pt.y); - QZ_MouseButtonEvent([ event buttonNumber ], YES); - break; - - case NSOtherMouseUp: - pt = QZ_GetMouseLocation(event); - if (!QZ_MouseIsInsideView(&pt)) { - [ NSApp sendEvent:event ]; - break; - } - - QZ_MouseMovedEvent((int)pt.x, (int)pt.y); - QZ_MouseButtonEvent([ event buttonNumber ], NO); - break; -#endif - - case NSKeyDown: { - /* Quit, hide and minimize */ - switch ([ event keyCode ]) { - case QZ_q: - case QZ_h: - case QZ_m: - if ([ event modifierFlags ] & NSCommandKeyMask) { - [ NSApp sendEvent:event ]; - } - break; - } - - chars = [ event characters ]; - unsigned short unicode = [ chars length ] > 0 ? [ chars characterAtIndex:0 ] : 0; - if (EditBoxInGlobalFocus()) { - if (QZ_KeyEvent([ event keyCode ], unicode, YES)) { - [ _cocoa_subdriver->cocoaview interpretKeyEvents:[ NSArray arrayWithObject:event ] ]; - } - } else { - QZ_KeyEvent([ event keyCode ], unicode, YES); - for (uint i = 1; i < [ chars length ]; i++) { - QZ_KeyEvent(0, [ chars characterAtIndex:i ], YES); - } - } - break; - } - - case NSKeyUp: - /* Quit, hide and minimize */ - switch ([ event keyCode ]) { - case QZ_q: - case QZ_h: - case QZ_m: - if ([ event modifierFlags ] & NSCommandKeyMask) { - [ NSApp sendEvent:event ]; - } - break; - } - - chars = [ event characters ]; - QZ_KeyEvent([ event keyCode ], [ chars length ] ? [ chars characterAtIndex:0 ] : 0, NO); - break; - - case NSScrollWheel: - if ([ event deltaY ] > 0.0) { /* Scroll up */ - _cursor.wheel--; - } else if ([ event deltaY ] < 0.0) { /* Scroll down */ - _cursor.wheel++; - } /* else: deltaY was 0.0 and we don't want to do anything */ - - /* Update the scroll count for 2D scrolling */ - CGFloat deltaX; - CGFloat deltaY; - - /* Use precise scrolling-specific deltas if they're supported. */ - if ([event respondsToSelector:@selector(hasPreciseScrollingDeltas)]) { - /* No precise deltas indicates a scroll wheel is being used, so we don't want 2D scrolling. */ - if (![ event hasPreciseScrollingDeltas ]) break; - - deltaX = [ event scrollingDeltaX ] * 0.5f; - deltaY = [ event scrollingDeltaY ] * 0.5f; - } else { - deltaX = [ event deltaX ] * 5; - deltaY = [ event deltaY ] * 5; - } - - _cursor.h_wheel -= (int)(deltaX * _settings_client.gui.scrollwheel_multiplier); - _cursor.v_wheel -= (int)(deltaY * _settings_client.gui.scrollwheel_multiplier); - - break; - - case NSEventTypeMagnify: - /* Pinch open or close gesture. */ - _current_magnification += [ event magnification ] * 5.0f; - - while (_current_magnification >= 1.0f) { - _current_magnification -= 1.0f; - _cursor.wheel--; - HandleMouseEvents(); - } - while (_current_magnification <= -1.0f) { - _current_magnification += 1.0f; - _cursor.wheel++; - HandleMouseEvents(); - } - break; - - case NSEventTypeEndGesture: - /* Gesture ended. */ - _current_magnification = 0.0f; - break; - - case NSCursorUpdate: - case NSMouseEntered: - case NSMouseExited: - /* Catch these events if the cursor is dragging. During dragging, we reset - * the mouse position programmatically, which would trigger OS X to show - * the default arrow cursor if the events are propagated. */ - if (_cursor.fix_at) break; - FALLTHROUGH; - - default: - [ NSApp sendEvent:event ]; - } - - return true; -} - - -void VideoDriver_Cocoa::GameLoop() -{ - uint32 cur_ticks = GetTick(); - uint32 last_cur_ticks = cur_ticks; - uint32 next_tick = cur_ticks + MILLISECONDS_PER_TICK; - -#ifdef _DEBUG - uint32 et0 = GetTick(); - uint32 st = 0; -#endif - - DisplaySplashImage(); - QZ_CheckPaletteAnim(); - _cocoa_subdriver->Draw(true); - CSleep(1); - - for (int i = 0; i < 2; i++) { - ::GameLoop(); - ::GameLoopPaletteAnimations(); - } - - UpdateWindows(); - QZ_CheckPaletteAnim(); - _cocoa_subdriver->Draw(); - CSleep(1); - - /* Set the proper OpenTTD palette which got spoilt by the splash - * image when using 8bpp blitter */ - GfxInitPalettes(); - QZ_CheckPaletteAnim(); - _cocoa_subdriver->Draw(true); - - for (;;) { - uint32 prev_cur_ticks = cur_ticks; // to check for wrapping - InteractiveRandom(); // randomness - - while (QZ_PollEvent()) {} - - if (_exit_game) { - /* Restore saved resolution if in fullscreen mode. */ - if (_cocoa_subdriver->IsFullscreen()) _cur_resolution = this->orig_res; - break; - } - -#if defined(_DEBUG) - if (_current_mods & NSShiftKeyMask) -#else - if (_tab_is_down) -#endif - { - if (!_networking && _game_mode != GM_MENU) _fast_forward |= 2; - } else if (_fast_forward & 2) { - _fast_forward = 0; - } - - cur_ticks = GetTick(); - if (cur_ticks >= next_tick || (_fast_forward && !_pause_mode) || cur_ticks < prev_cur_ticks) { - _realtime_tick += cur_ticks - last_cur_ticks; - last_cur_ticks = cur_ticks; - next_tick = cur_ticks + MILLISECONDS_PER_TICK; - - bool old_ctrl_pressed = _ctrl_pressed; - bool old_shift_pressed = _shift_pressed; - - _ctrl_pressed = !!(_current_mods & ( _settings_client.gui.right_mouse_btn_emulation != RMBE_CONTROL ? NSControlKeyMask : NSCommandKeyMask)) != _invert_ctrl; - _shift_pressed = !!(_current_mods & NSShiftKeyMask) != _invert_shift; - - if (old_ctrl_pressed != _ctrl_pressed) HandleCtrlChanged(); - if (old_shift_pressed != _shift_pressed) HandleShiftChanged(); - - ::GameLoop(); - ::GameLoopPaletteAnimations(); - - UpdateWindows(); - QZ_CheckPaletteAnim(); - _cocoa_subdriver->Draw(); - } else { -#ifdef _DEBUG - uint32 st0 = GetTick(); -#endif - CSleep(1); -#ifdef _DEBUG - st += GetTick() - st0; -#endif - NetworkDrawChatMessage(); - DrawMouseCursor(); - _cocoa_subdriver->Draw(); - } - } - -#ifdef _DEBUG - uint32 et = GetTick(); - - DEBUG(driver, 1, "cocoa_v: nextEventMatchingMask took %i ms total", _tEvent); - DEBUG(driver, 1, "cocoa_v: game loop took %i ms total (%i ms without sleep)", et - et0, et - et0 - st); - DEBUG(driver, 1, "cocoa_v: (nextEventMatchingMask total)/(game loop total) is %f%%", (double)_tEvent / (double)(et - et0) * 100); - DEBUG(driver, 1, "cocoa_v: (nextEventMatchingMask total)/(game loop without sleep total) is %f%%", (double)_tEvent / (double)(et - et0 - st) * 100); -#endif -} - -#endif /* WITH_COCOA */ diff --git a/src/video/dedicated_v.cpp b/src/video/dedicated_v.cpp index b9a7a6d6e1..28553b900b 100644 --- a/src/video/dedicated_v.cpp +++ b/src/video/dedicated_v.cpp @@ -21,6 +21,7 @@ #include "../core/random_func.hpp" #include "../saveload/saveload.h" #include "../thread.h" +#include "../window_func.h" #include "dedicated_v.h" #ifdef __OS2__ @@ -195,14 +196,6 @@ static bool InputWaiting() return select(STDIN + 1, &readfds, nullptr, nullptr, &tv) > 0; } -static uint32 GetTime() -{ - struct timeval tim; - - gettimeofday(&tim, nullptr); - return tim.tv_usec / 1000 + tim.tv_sec * 1000; -} - #else static bool InputWaiting() @@ -210,11 +203,6 @@ static bool InputWaiting() return WaitForSingleObject(_hInputReady, 1) == WAIT_OBJECT_0; } -static uint32 GetTime() -{ - return GetTickCount(); -} - #endif static void DedicatedHandleKeyInput() @@ -248,8 +236,9 @@ static void DedicatedHandleKeyInput() void VideoDriver_Dedicated::MainLoop() { - uint32 cur_ticks = GetTime(); - uint32 next_tick = cur_ticks + MILLISECONDS_PER_TICK; + auto cur_ticks = std::chrono::steady_clock::now(); + auto last_realtime_tick = cur_ticks; + auto next_game_tick = cur_ticks; /* Signal handlers */ #if defined(UNIX) @@ -290,18 +279,31 @@ void VideoDriver_Dedicated::MainLoop() } while (!_exit_game) { - uint32 prev_cur_ticks = cur_ticks; // to check for wrapping InteractiveRandom(); // randomness if (!_dedicated_forks) DedicatedHandleKeyInput(); - cur_ticks = GetTime(); - _realtime_tick += cur_ticks - prev_cur_ticks; - if (cur_ticks >= next_tick || cur_ticks < prev_cur_ticks || _ddc_fastforward) { - next_tick = cur_ticks + MILLISECONDS_PER_TICK; + cur_ticks = std::chrono::steady_clock::now(); + + /* If more than a millisecond has passed, increase the _realtime_tick. */ + if (cur_ticks - last_realtime_tick > std::chrono::milliseconds(1)) { + auto delta = std::chrono::duration_cast(cur_ticks - last_realtime_tick); + IncreaseRealtimeTick(delta.count()); + last_realtime_tick += delta; + } + + if (cur_ticks >= next_game_tick || _ddc_fastforward) { + if (_ddc_fastforward) { + next_game_tick = cur_ticks + this->GetGameInterval(); + } else { + next_game_tick += this->GetGameInterval(); + /* Avoid next_game_tick getting behind more and more if it cannot keep up. */ + if (next_game_tick < cur_ticks - ALLOWED_DRIFT * this->GetGameInterval()) next_game_tick = cur_ticks; + } GameLoop(); GameLoopPaletteAnimations(); + InputLoop(); UpdateWindows(); } @@ -310,9 +312,14 @@ void VideoDriver_Dedicated::MainLoop() /* Sleep longer on a dedicated server, if the game is paused and no clients connected. * That can allow the CPU to better use deep sleep states. */ if (_pause_mode != 0 && !HasClients()) { - CSleep(100); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); } else { - CSleep(1); + /* See how much time there is till we have to process the next event, and try to hit that as close as possible. */ + auto now = std::chrono::steady_clock::now(); + + if (next_game_tick > now) { + std::this_thread::sleep_for(next_game_tick - now); + } } } } diff --git a/src/video/null_v.cpp b/src/video/null_v.cpp index 4132effa94..9094a49319 100644 --- a/src/video/null_v.cpp +++ b/src/video/null_v.cpp @@ -10,6 +10,7 @@ #include "../stdafx.h" #include "../gfx_func.h" #include "../blitter/factory.hpp" +#include "../window_func.h" #include "null_v.h" #include "../safeguards.h" @@ -51,12 +52,14 @@ void VideoDriver_Null::MainLoop() while (!_exit_game) { GameLoop(); GameLoopPaletteAnimations(); + InputLoop(); UpdateWindows(); } } else { for (int i = 0; i < this->ticks; i++) { GameLoop(); GameLoopPaletteAnimations(); + InputLoop(); UpdateWindows(); } } diff --git a/src/video/sdl2_v.cpp b/src/video/sdl2_v.cpp index f0c2ee8e62..6c2511a33a 100644 --- a/src/video/sdl2_v.cpp +++ b/src/video/sdl2_v.cpp @@ -17,6 +17,8 @@ #include "../progress.h" #include "../core/random_func.hpp" #include "../core/math_func.hpp" +#include "../core/mem_func.hpp" +#include "../core/geometry_func.hpp" #include "../fileio_func.h" #include "../framerate_type.h" #include "../scope.h" @@ -69,13 +71,7 @@ static SDL_Palette *_sdl_palette; static bool _cursor_new_in_window = false; #endif -#define MAX_DIRTY_RECTS 100 -static SDL_Rect _dirty_rects[MAX_DIRTY_RECTS]; -static int _num_dirty_rects; - -/* Size of window */ -static int _window_size_w; -static int _window_size_h; +static Rect _dirty_rect; static std::string _editing_text; @@ -85,6 +81,7 @@ Point GetFocusedWindowCaret(); Point GetFocusedWindowTopLeft(); bool FocusedWindowIsConsole(); bool EditBoxInGlobalFocus(); +void InputLoop(); #if defined(WITH_FCITX) static bool _fcitx_mode = false; @@ -277,13 +274,8 @@ const static bool _suppress_text_event = false; void VideoDriver_SDL::MakeDirty(int left, int top, int width, int height) { - if (_num_dirty_rects < MAX_DIRTY_RECTS) { - _dirty_rects[_num_dirty_rects].x = left; - _dirty_rects[_num_dirty_rects].y = top; - _dirty_rects[_num_dirty_rects].w = width; - _dirty_rects[_num_dirty_rects].h = height; - } - _num_dirty_rects++; + Rect r = {left, top, left + width, top + height}; + _dirty_rect = BoundingRect(_dirty_rect, r); } static void UpdatePalette() @@ -308,6 +300,8 @@ static void MakePalette() if (_sdl_palette == nullptr) usererror("SDL2: Couldn't allocate palette: %s", SDL_GetError()); } + _cur_palette.first_dirty = 0; + _cur_palette.count_dirty = 256; _local_palette = _cur_palette; UpdatePalette(); @@ -344,11 +338,11 @@ void VideoDriver_SDL::CheckPaletteAnim() this->MakeDirty(0, 0, _screen.width, _screen.height); } -static void DrawSurfaceToScreen() +static void Paint() { PerformanceMeasurer framerate(PFE_VIDEO); - if (_num_dirty_rects == 0) return; + if (IsEmptyRect(_dirty_rect) && _cur_palette.count_dirty == 0) return; if (_cur_palette.count_dirty != 0) { Blitter *blitter = BlitterFactory::GetCurrentBlitter(); @@ -371,51 +365,17 @@ static void DrawSurfaceToScreen() _cur_palette.count_dirty = 0; } - int n = _num_dirty_rects; - if (n == 0) return; - - _num_dirty_rects = 0; - - bool update_whole_screen = n > MAX_DIRTY_RECTS; - - if (!update_whole_screen) { - int area = 0; - for (int i = 0; i < n; i++) { - area += _dirty_rects[i].w * _dirty_rects[i].h; - } - // Rectangles cover more than 80% of screen area, just do the whole screen - if ((area * 5) / 4 >= _sdl_surface->w * _sdl_surface->h) update_whole_screen = true; - } - - if (update_whole_screen) { - if (_sdl_surface != _sdl_real_surface) { - SDL_BlitSurface(_sdl_surface, nullptr, _sdl_real_surface, nullptr); - } - - SDL_UpdateWindowSurface(_sdl_window); - } else { - if (_sdl_surface != _sdl_real_surface) { - for (int i = 0; i < _num_dirty_rects; i++) { - SDL_BlitSurface(_sdl_surface, &_dirty_rects[i], _sdl_real_surface, &_dirty_rects[i]); - } - } - SDL_Rect update_rect = { _sdl_surface->w, _sdl_surface->h, 0, 0 }; - for (int i = 0; i < n; i++) { - if (_dirty_rects[i].x < update_rect.x) update_rect.x = _dirty_rects[i].x; - if (_dirty_rects[i].y < update_rect.y) update_rect.y = _dirty_rects[i].y; - if (_dirty_rects[i].x + _dirty_rects[i].w > update_rect.w) update_rect.w = _dirty_rects[i].x + _dirty_rects[i].w; - if (_dirty_rects[i].y + _dirty_rects[i].h > update_rect.h) update_rect.h = _dirty_rects[i].y + _dirty_rects[i].h; - } - update_rect.w -= update_rect.x; - update_rect.h -= update_rect.y; + SDL_Rect r = { _dirty_rect.left, _dirty_rect.top, _dirty_rect.right - _dirty_rect.left, _dirty_rect.bottom - _dirty_rect.top }; - SDL_UpdateWindowSurfaceRects(_sdl_window, &update_rect, 1); + if (_sdl_surface != _sdl_real_surface) { + SDL_BlitSurface(_sdl_surface, &r, _sdl_real_surface, &r); } + SDL_UpdateWindowSurfaceRects(_sdl_window, &r, 1); - _num_dirty_rects = 0; + MemSetT(&_dirty_rect, 0); } -static void DrawSurfaceToScreenThread() +static void PaintThread() { /* First tell the main thread we're started */ std::unique_lock lock(*_draw_mutex); @@ -426,7 +386,7 @@ static void DrawSurfaceToScreenThread() while (_draw_continue) { /* Then just draw and wait till we stop */ - DrawSurfaceToScreen(); + Paint(); _draw_signal->wait(lock); } } @@ -591,8 +551,12 @@ bool VideoDriver_SDL::CreateMainSurface(uint w, uint h, bool resize) _sdl_surface = _sdl_real_surface; } - /* Delay drawing for this cycle; the next cycle will redraw the whole screen */ - _num_dirty_rects = 0; + /* X11 doesn't appreciate it if we invalidate areas outside the window + * if shared memory is enabled (read: it crashes). So, as we might have + * gotten smaller, reset our dirty rects. GameSizeChanged() a bit lower + * will mark the whole screen dirty again anyway, but this time with the + * new dimensions. */ + MemSetT(&_dirty_rect, 0); _screen.width = _sdl_surface->w; _screen.height = _sdl_surface->h; @@ -699,7 +663,7 @@ void VideoDriver_SDL::EditBoxLostFocus() HandleTextInput(nullptr, true); } -struct VkMapping { +struct SDLVkMapping { SDL_Keycode vk_from; byte vk_count; byte map_to; @@ -711,7 +675,7 @@ struct VkMapping { #define AS_UP(x, z) {x, 0, z, true} #define AM_UP(x, y, z, w) {x, (byte)(y - x), z, true} -static const VkMapping _vk_mapping[] = { +static const SDLVkMapping _vk_mapping[] = { /* Pageup stuff + up/down */ AS_UP(SDLK_PAGEUP, WKC_PAGEUP), AS_UP(SDLK_PAGEDOWN, WKC_PAGEDOWN), @@ -767,7 +731,7 @@ static const VkMapping _vk_mapping[] = { static uint ConvertSdlKeyIntoMy(SDL_Keysym *sym, WChar *character) { - const VkMapping *map; + const SDLVkMapping *map; uint key = 0; bool unprintable = false; @@ -807,7 +771,7 @@ static uint ConvertSdlKeyIntoMy(SDL_Keysym *sym, WChar *character) */ static uint ConvertSdlKeycodeIntoMy(SDL_Keycode kc) { - const VkMapping *map; + const SDLVkMapping *map; uint key = 0; for (map = _vk_mapping; map != endof(_vk_mapping); ++map) { @@ -818,7 +782,7 @@ static uint ConvertSdlKeycodeIntoMy(SDL_Keycode kc) } /* check scancode for BACKQUOTE key, because we want the key left - of "1", not anything else (on non-US keyboards) */ + * of "1", not anything else (on non-US keyboards) */ SDL_Scancode sc = SDL_GetScancodeFromKey(kc); if (sc == SDL_SCANCODE_GRAVE) key = WKC_BACKQUOTE; @@ -839,10 +803,10 @@ int VideoDriver_SDL::PollEvent() #ifdef __EMSCRIPTEN__ if (_cursor_new_in_window) { /* The cursor just moved into the window; this means we don't - * know the absolutely position yet to move relative from. - * Before this time, SDL didn't know it either, and this is - * why we postpone it till now. Update the absolute position - * for this once, and work relative after. */ + * know the absolutely position yet to move relative from. + * Before this time, SDL didn't know it either, and this is + * why we postpone it till now. Update the absolute position + * for this once, and work relative after. */ _cursor.pos.x = ev.motion.x; _cursor.pos.y = ev.motion.y; _cursor.dirty = true; @@ -1062,6 +1026,17 @@ const char *VideoDriver_SDL::Start(const StringList &parm) MarkWholeScreenDirty(); _draw_threaded = !GetDriverParamBool(parm, "no_threads") && !GetDriverParamBool(parm, "no_thread"); + /* Wayland SDL video driver uses EGL to render the game. SDL created the + * EGL context from the main-thread, and with EGL you are not allowed to + * draw in another thread than the context was created. The function of + * _draw_threaded is to do exactly this: draw in another thread than the + * window was created, and as such, this fails on Wayland SDL video + * driver. So, we disable threading by default if Wayland SDL video + * driver is detected. + */ + if (strcmp(dname, "wayland") == 0) { + _draw_threaded = false; + } SDL_StopTextInput(); this->edit_box_focused = false; @@ -1089,7 +1064,6 @@ void VideoDriver_SDL::LoopOnce() uint32 mod; int numkeys; const Uint8 *keys; - uint32 prev_cur_ticks = cur_ticks; // to check for wrapping InteractiveRandom(); // randomness while (PollEvent() == -1) {} @@ -1123,11 +1097,37 @@ void VideoDriver_SDL::LoopOnce() _fast_forward = 0; } - cur_ticks = SDL_GetTicks(); - if (SDL_TICKS_PASSED(cur_ticks, next_tick) || (_fast_forward && !_pause_mode) || cur_ticks < prev_cur_ticks) { - _realtime_tick += cur_ticks - last_cur_ticks; - last_cur_ticks = cur_ticks; - next_tick = cur_ticks + MILLISECONDS_PER_TICK; + cur_ticks = std::chrono::steady_clock::now(); + + /* If more than a millisecond has passed, increase the _realtime_tick. */ + if (cur_ticks - last_realtime_tick > std::chrono::milliseconds(1)) { + auto delta = std::chrono::duration_cast(cur_ticks - last_realtime_tick); + IncreaseRealtimeTick(delta.count()); + last_realtime_tick += delta; + } + + if (cur_ticks >= next_game_tick || (_fast_forward && !_pause_mode)) { + if (_fast_forward && !_pause_mode) { + next_game_tick = cur_ticks + this->GetGameInterval(); + } else { + next_game_tick += this->GetGameInterval(); + /* Avoid next_game_tick getting behind more and more if it cannot keep up. */ + if (next_game_tick < cur_ticks - ALLOWED_DRIFT * this->GetGameInterval()) next_game_tick = cur_ticks; + } + + /* The gameloop is the part that can run asynchronously. The rest + * except sleeping can't. */ + if (_draw_mutex != nullptr) draw_lock.unlock(); + GameLoop(); + if (_draw_mutex != nullptr) draw_lock.lock(); + GameLoopPaletteAnimations(); + } + + /* Prevent drawing when switching mode, as windows can be removed when they should still appear. */ + if (cur_ticks >= next_draw_tick && (_switch_mode == SM_NONE || HasModalProgress())) { + next_draw_tick += this->GetDrawInterval(); + /* Avoid next_draw_tick getting behind more and more if it cannot keep up. */ + if (next_draw_tick < cur_ticks - ALLOWED_DRIFT * this->GetDrawInterval()) next_draw_tick = cur_ticks; bool old_ctrl_pressed = _ctrl_pressed; bool old_shift_pressed = _shift_pressed; @@ -1144,50 +1144,40 @@ void VideoDriver_SDL::LoopOnce() if (old_ctrl_pressed != _ctrl_pressed) HandleCtrlChanged(); if (old_shift_pressed != _shift_pressed) HandleShiftChanged(); - /* The gameloop is the part that can run asynchronously. The rest - * except sleeping can't. */ - if (_draw_mutex != nullptr) draw_lock.unlock(); - - GameLoop(); - - if (_draw_mutex != nullptr) draw_lock.lock(); - - GameLoopPaletteAnimations(); - + InputLoop(); UpdateWindows(); this->CheckPaletteAnim(); - } else { - /* Release the thread while sleeping */ - if (_draw_mutex != nullptr) { - draw_lock.unlock(); - CSleep(1); - draw_lock.lock(); + + if (_draw_mutex != nullptr && !HasModalProgress()) { + _draw_signal->notify_one(); } else { + Paint(); + } + } + /* Emscripten is running an event-based mainloop; there is already some * downtime between each iteration, so no need to sleep. */ #ifndef __EMSCRIPTEN__ - CSleep(1); -#endif + /* If we are not in fast-forward, create some time between calls to ease up CPU usage. */ + if (!_fast_forward || _pause_mode) { + /* See how much time there is till we have to process the next event, and try to hit that as close as possible. */ + auto next_tick = std::min(next_draw_tick, next_game_tick); + auto now = std::chrono::steady_clock::now(); + + if (next_tick > now) { + if (_draw_mutex != nullptr) draw_lock.unlock(); + std::this_thread::sleep_for(next_tick - now); + if (_draw_mutex != nullptr) draw_lock.lock(); } - - NetworkDrawChatMessage(); - DrawMouseCursor(); - } - - /* End of the critical part. */ - if (_draw_mutex != nullptr && !HasModalProgress()) { - _draw_signal->notify_one(); - } else { - /* Oh, we didn't have threads, then just draw unthreaded */ - DrawSurfaceToScreen(); } +#endif } void VideoDriver_SDL::MainLoop() { - cur_ticks = SDL_GetTicks(); - last_cur_ticks = cur_ticks; - next_tick = cur_ticks + MILLISECONDS_PER_TICK; + cur_ticks = std::chrono::steady_clock::now(); + last_realtime_tick = cur_ticks; + next_game_tick = cur_ticks; this->CheckPaletteAnim(); @@ -1202,7 +1192,7 @@ void VideoDriver_SDL::MainLoop() _draw_signal = new std::condition_variable_any(); _draw_continue = true; - _draw_threaded = StartNewThread(&draw_thread, "ottd:draw-sdl", &DrawSurfaceToScreenThread); + _draw_threaded = StartNewThread(&draw_thread, "ottd:draw-sdl", &PaintThread); /* Free the mutex if we won't be able to use it. */ if (!_draw_threaded) { @@ -1274,9 +1264,11 @@ bool VideoDriver_SDL::ToggleFullscreen(bool fullscreen) std::unique_lock lock; if (_draw_mutex != nullptr) lock = std::unique_lock(*_draw_mutex); + int w, h; + /* Remember current window size */ if (fullscreen) { - SDL_GetWindowSize(_sdl_window, &_window_size_w, &_window_size_h); + SDL_GetWindowSize(_sdl_window, &w, &h); /* Find fullscreen window size */ SDL_DisplayMode dm; @@ -1292,7 +1284,7 @@ bool VideoDriver_SDL::ToggleFullscreen(bool fullscreen) if (ret == 0) { /* Switching resolution succeeded, set fullscreen value of window. */ _fullscreen = fullscreen; - if (!fullscreen) SDL_SetWindowSize(_sdl_window, _window_size_w, _window_size_h); + if (!fullscreen) SDL_SetWindowSize(_sdl_window, w, h); } else { DEBUG(driver, 0, "SDL_SetWindowFullscreen() failed: %s", SDL_GetError()); } diff --git a/src/video/sdl2_v.h b/src/video/sdl2_v.h index d1c4d957ca..b1afb6d962 100644 --- a/src/video/sdl2_v.h +++ b/src/video/sdl2_v.h @@ -62,9 +62,10 @@ private: */ bool edit_box_focused; - uint32 cur_ticks; - uint32 last_cur_ticks; - uint32 next_tick; + std::chrono::steady_clock::time_point cur_ticks; + std::chrono::steady_clock::time_point last_realtime_tick; + std::chrono::steady_clock::time_point next_game_tick; + std::chrono::steady_clock::time_point next_draw_tick; int startup_display; std::thread draw_thread; diff --git a/src/video/sdl_v.cpp b/src/video/sdl_v.cpp index ef5d64f96c..e954dde274 100644 --- a/src/video/sdl_v.cpp +++ b/src/video/sdl_v.cpp @@ -21,6 +21,7 @@ #include "../core/math_func.hpp" #include "../fileio_func.h" #include "../framerate_type.h" +#include "../window_func.h" #include "sdl_v.h" #include #include @@ -408,7 +409,7 @@ bool VideoDriver_SDL::ClaimMousePointer() return true; } -struct VkMapping { +struct SDLVkMapping { #if SDL_VERSION_ATLEAST(1, 3, 0) SDL_Keycode vk_from; #else @@ -421,7 +422,7 @@ struct VkMapping { #define AS(x, z) {x, 0, z} #define AM(x, y, z, w) {x, (byte)(y - x), z} -static const VkMapping _vk_mapping[] = { +static const SDLVkMapping _vk_mapping[] = { /* Pageup stuff + up/down */ AM(SDLK_PAGEUP, SDLK_PAGEDOWN, WKC_PAGEUP, WKC_PAGEDOWN), AS(SDLK_UP, WKC_UP), @@ -476,7 +477,7 @@ static const VkMapping _vk_mapping[] = { static uint ConvertSdlKeyIntoMy(SDL_keysym *sym, WChar *character) { - const VkMapping *map; + const SDLVkMapping *map; uint key = 0; for (map = _vk_mapping; map != endof(_vk_mapping); ++map) { @@ -654,9 +655,10 @@ void VideoDriver_SDL::Stop() void VideoDriver_SDL::MainLoop() { - uint32 cur_ticks = SDL_GetTicks(); - uint32 last_cur_ticks = cur_ticks; - uint32 next_tick = cur_ticks + MILLISECONDS_PER_TICK; + auto cur_ticks = std::chrono::steady_clock::now(); + auto last_realtime_tick = cur_ticks; + auto next_game_tick = cur_ticks; + auto next_draw_tick = cur_ticks; uint32 mod; int numkeys; Uint8 *keys; @@ -696,7 +698,6 @@ void VideoDriver_SDL::MainLoop() DEBUG(driver, 1, "SDL: using %sthreads", _draw_threaded ? "" : "no "); for (;;) { - uint32 prev_cur_ticks = cur_ticks; // to check for wrapping InteractiveRandom(); // randomness while (PollEvent() == -1) {} @@ -725,11 +726,37 @@ void VideoDriver_SDL::MainLoop() _fast_forward = 0; } - cur_ticks = SDL_GetTicks(); - if (cur_ticks >= next_tick || (_fast_forward && !_pause_mode) || cur_ticks < prev_cur_ticks) { - _realtime_tick += cur_ticks - last_cur_ticks; - last_cur_ticks = cur_ticks; - next_tick = cur_ticks + MILLISECONDS_PER_TICK; + cur_ticks = std::chrono::steady_clock::now(); + + /* If more than a millisecond has passed, increase the _realtime_tick. */ + if (cur_ticks - last_realtime_tick > std::chrono::milliseconds(1)) { + auto delta = std::chrono::duration_cast(cur_ticks - last_realtime_tick); + IncreaseRealtimeTick(delta.count()); + last_realtime_tick += delta; + } + + if (cur_ticks >= next_game_tick || (_fast_forward && !_pause_mode)) { + if (_fast_forward && !_pause_mode) { + next_game_tick = cur_ticks + this->GetGameInterval(); + } else { + next_game_tick += this->GetGameInterval(); + /* Avoid next_game_tick getting behind more and more if it cannot keep up. */ + if (next_game_tick < cur_ticks - ALLOWED_DRIFT * this->GetGameInterval()) next_game_tick = cur_ticks; + } + + /* The gameloop is the part that can run asynchronously. The rest + * except sleeping can't. */ + if (_draw_mutex != nullptr) draw_lock.unlock(); + GameLoop(); + if (_draw_mutex != nullptr) draw_lock.lock(); + GameLoopPaletteAnimations(); + } + + /* Prevent drawing when switching mode, as windows can be removed when they should still appear. */ + if (cur_ticks >= next_draw_tick && (_switch_mode == SM_NONE || HasModalProgress())) { + next_draw_tick += this->GetDrawInterval(); + /* Avoid next_draw_tick getting behind more and more if it cannot keep up. */ + if (next_draw_tick < cur_ticks - ALLOWED_DRIFT * this->GetDrawInterval()) next_draw_tick = cur_ticks; bool old_ctrl_pressed = _ctrl_pressed; bool old_shift_pressed = _shift_pressed; @@ -753,35 +780,29 @@ void VideoDriver_SDL::MainLoop() if (old_ctrl_pressed != _ctrl_pressed) HandleCtrlChanged(); if (old_shift_pressed != _shift_pressed) HandleShiftChanged(); - /* The gameloop is the part that can run asynchronously. The rest - * except sleeping can't. */ - if (_draw_mutex != nullptr) draw_lock.unlock(); - - GameLoop(); - - if (_draw_mutex != nullptr) draw_lock.lock(); - - GameLoopPaletteAnimations(); - + InputLoop(); UpdateWindows(); _local_palette = _cur_palette; - } else { - /* Release the thread while sleeping */ - if (_draw_mutex != nullptr) draw_lock.unlock(); - CSleep(1); - if (_draw_mutex != nullptr) draw_lock.lock(); - NetworkDrawChatMessage(); - DrawMouseCursor(); + if (_draw_mutex != nullptr && !HasModalProgress()) { + _draw_signal->notify_one(); + } else { + CheckPaletteAnim(); + DrawSurfaceToScreen(); + } } - /* End of the critical part. */ - if (_draw_mutex != nullptr && !HasModalProgress()) { - _draw_signal->notify_one(); - } else { - /* Oh, we didn't have threads, then just draw unthreaded */ - CheckPaletteAnim(); - DrawSurfaceToScreen(); + /* If we are not in fast-forward, create some time between calls to ease up CPU usage. */ + if (!_fast_forward || _pause_mode) { + /* See how much time there is till we have to process the next event, and try to hit that as close as possible. */ + auto next_tick = std::min(next_draw_tick, next_game_tick); + auto now = std::chrono::steady_clock::now(); + + if (next_tick > now) { + if (_draw_mutex != nullptr) draw_lock.unlock(); + std::this_thread::sleep_for(next_tick - now); + if (_draw_mutex != nullptr) draw_lock.lock(); + } } } diff --git a/src/video/video_driver.hpp b/src/video/video_driver.hpp index 88ce43a42b..1cfb268753 100644 --- a/src/video/video_driver.hpp +++ b/src/video/video_driver.hpp @@ -13,6 +13,9 @@ #include "../driver.h" #include "../core/geometry_type.hpp" #include "../core/math_func.hpp" +#include "../settings_type.h" +#include "../zoom_type.h" +#include #include extern std::string _ini_videodriver; @@ -105,6 +108,18 @@ public: */ virtual void EditBoxLostFocus() {} + /** + * Get a suggested default GUI zoom taking screen DPI into account. + */ + virtual ZoomLevel GetSuggestedUIZoom() + { + float dpi_scale = this->GetDPIScale(); + + if (dpi_scale >= 3.0f) return ZOOM_LVL_NORMAL; + if (dpi_scale >= 1.5f) return ZOOM_LVL_OUT_2X; + return ZOOM_LVL_OUT_4X; + } + /** * Get the currently active instance of the video driver. */ @@ -113,11 +128,19 @@ public: } protected: - /* + const uint ALLOWED_DRIFT = 5; ///< How many times videodriver can miss deadlines without it being overly compensated. + + /** * Get the resolution of the main screen. */ virtual Dimension GetScreenSize() const { return { DEFAULT_WINDOW_WIDTH, DEFAULT_WINDOW_HEIGHT }; } + /** + * Get DPI scaling factor of the screen OTTD is displayed on. + * @return 1.0 for default platform DPI, > 1.0 for higher DPI values, and < 1.0 for smaller DPI values. + */ + virtual float GetDPIScale() { return 1.0f; } + /** * Apply resolution auto-detection and clamp to sensible defaults. */ @@ -132,6 +155,16 @@ protected: _cur_resolution.height = ClampU(res.height * 3 / 4, DEFAULT_WINDOW_HEIGHT, UINT16_MAX / 2); } } + + std::chrono::steady_clock::duration GetGameInterval() + { + return std::chrono::milliseconds(MILLISECONDS_PER_TICK); + } + + std::chrono::steady_clock::duration GetDrawInterval() + { + return std::chrono::microseconds(1000000 / _settings_client.gui.refresh_rate); + } }; #endif /* VIDEO_VIDEO_DRIVER_HPP */ diff --git a/src/video/win32_v.cpp b/src/video/win32_v.cpp index b487608708..898c7c3bfc 100644 --- a/src/video/win32_v.cpp +++ b/src/video/win32_v.cpp @@ -119,7 +119,7 @@ bool VideoDriver_Win32::ClaimMousePointer() return true; } -struct VkMapping { +struct Win32VkMapping { byte vk_from; byte vk_count; byte map_to; @@ -128,7 +128,7 @@ struct VkMapping { #define AS(x, z) {x, 0, z} #define AM(x, y, z, w) {x, y - x, z} -static const VkMapping _vk_mapping[] = { +static const Win32VkMapping _vk_mapping[] = { /* Pageup stuff + up/down */ AM(VK_PRIOR, VK_DOWN, WKC_PAGEUP, WKC_DOWN), /* Map letters & digits */ @@ -171,7 +171,7 @@ static const VkMapping _vk_mapping[] = { static uint MapWindowsKey(uint sym) { - const VkMapping *map; + const Win32VkMapping *map; uint key = 0; for (map = _vk_mapping; map != endof(_vk_mapping); ++map) { @@ -1138,9 +1138,10 @@ static void CheckPaletteAnim() void VideoDriver_Win32::MainLoop() { MSG mesg; - uint32 cur_ticks = GetTickCount(); - uint32 last_cur_ticks = cur_ticks; - uint32 next_tick = cur_ticks + MILLISECONDS_PER_TICK; + auto cur_ticks = std::chrono::steady_clock::now(); + auto last_realtime_tick = cur_ticks; + auto next_game_tick = cur_ticks; + auto next_draw_tick = cur_ticks; std::thread draw_thread; std::unique_lock draw_lock; @@ -1181,8 +1182,6 @@ void VideoDriver_Win32::MainLoop() CheckPaletteAnim(); for (;;) { - uint32 prev_cur_ticks = cur_ticks; // to check for wrapping - while (PeekMessage(&mesg, nullptr, 0, 0, PM_REMOVE)) { InteractiveRandom(); // randomness /* Convert key messages to char messages if we want text input. */ @@ -1203,11 +1202,40 @@ void VideoDriver_Win32::MainLoop() _fast_forward = 0; } - cur_ticks = GetTickCount(); - if (cur_ticks >= next_tick || (_fast_forward && !_pause_mode) || cur_ticks < prev_cur_ticks) { - _realtime_tick += cur_ticks - last_cur_ticks; - last_cur_ticks = cur_ticks; - next_tick = cur_ticks + MILLISECONDS_PER_TICK; + cur_ticks = std::chrono::steady_clock::now(); + + /* If more than a millisecond has passed, increase the _realtime_tick. */ + if (cur_ticks - last_realtime_tick > std::chrono::milliseconds(1)) { + auto delta = std::chrono::duration_cast(cur_ticks - last_realtime_tick); + IncreaseRealtimeTick(delta.count()); + last_realtime_tick += delta; + } + + if (cur_ticks >= next_game_tick || (_fast_forward && !_pause_mode)) { + if (_fast_forward && !_pause_mode) { + next_game_tick = cur_ticks + this->GetGameInterval(); + } else { + next_game_tick += this->GetGameInterval(); + /* Avoid next_game_tick getting behind more and more if it cannot keep up. */ + if (next_game_tick < cur_ticks - ALLOWED_DRIFT * this->GetGameInterval()) next_game_tick = cur_ticks; + } + + /* Flush GDI buffer to ensure we don't conflict with the drawing thread. */ + GdiFlush(); + + /* The game loop is the part that can run asynchronously. + * The rest except sleeping can't. */ + if (_draw_threaded) draw_lock.unlock(); + GameLoop(); + if (_draw_threaded) draw_lock.lock(); + GameLoopPaletteAnimations(); + } + + /* Prevent drawing when switching mode, as windows can be removed when they should still appear. */ + if (cur_ticks >= next_draw_tick && (_switch_mode == SM_NONE || HasModalProgress())) { + next_draw_tick += this->GetDrawInterval(); + /* Avoid next_draw_tick getting behind more and more if it cannot keep up. */ + if (next_draw_tick < cur_ticks - ALLOWED_DRIFT * this->GetDrawInterval()) next_draw_tick = cur_ticks; bool old_ctrl_pressed = _ctrl_pressed; bool old_shift_pressed = _shift_pressed; @@ -1229,31 +1257,30 @@ void VideoDriver_Win32::MainLoop() if (old_ctrl_pressed != _ctrl_pressed) HandleCtrlChanged(); if (old_shift_pressed != _shift_pressed) HandleShiftChanged(); + if (_force_full_redraw) MarkWholeScreenDirty(); + /* Flush GDI buffer to ensure we don't conflict with the drawing thread. */ GdiFlush(); - /* The game loop is the part that can run asynchronously. - * The rest except sleeping can't. */ - if (_draw_threaded) draw_lock.unlock(); - GameLoop(); - if (_draw_threaded) draw_lock.lock(); - GameLoopPaletteAnimations(); - - if (_force_full_redraw) MarkWholeScreenDirty(); - + InputLoop(); UpdateWindows(); CheckPaletteAnim(); - } else { - /* Flush GDI buffer to ensure we don't conflict with the drawing thread. */ - GdiFlush(); + } - /* Release the thread while sleeping */ - if (_draw_threaded) draw_lock.unlock(); - CSleep(1); - if (_draw_threaded) draw_lock.lock(); + /* If we are not in fast-forward, create some time between calls to ease up CPU usage. */ + if (!_fast_forward || _pause_mode) { + /* See how much time there is till we have to process the next event, and try to hit that as close as possible. */ + auto next_tick = std::min(next_draw_tick, next_game_tick); + auto now = std::chrono::steady_clock::now(); + + if (next_tick > now) { + /* Flush GDI buffer to ensure we don't conflict with the drawing thread. */ + GdiFlush(); - NetworkDrawChatMessage(); - DrawMouseCursor(); + if (_draw_mutex != nullptr) draw_lock.unlock(); + std::this_thread::sleep_for(next_tick - now); + if (_draw_mutex != nullptr) draw_lock.lock(); + } } } @@ -1324,3 +1351,43 @@ Dimension VideoDriver_Win32::GetScreenSize() const { return { static_cast(GetSystemMetrics(SM_CXSCREEN)), static_cast(GetSystemMetrics(SM_CYSCREEN)) }; } + +float VideoDriver_Win32::GetDPIScale() +{ + typedef UINT (WINAPI *PFNGETDPIFORWINDOW)(HWND hwnd); + typedef UINT (WINAPI *PFNGETDPIFORSYSTEM)(VOID); + typedef HRESULT (WINAPI *PFNGETDPIFORMONITOR)(HMONITOR hMonitor, int dpiType, UINT *dpiX, UINT *dpiY); + + static PFNGETDPIFORWINDOW _GetDpiForWindow = nullptr; + static PFNGETDPIFORSYSTEM _GetDpiForSystem = nullptr; + static PFNGETDPIFORMONITOR _GetDpiForMonitor = nullptr; + + static bool init_done = false; + if (!init_done) { + init_done = true; + + _GetDpiForWindow = (PFNGETDPIFORWINDOW)GetProcAddress(GetModuleHandle(_T("User32")), "GetDpiForWindow"); + _GetDpiForSystem = (PFNGETDPIFORSYSTEM)GetProcAddress(GetModuleHandle(_T("User32")), "GetDpiForSystem"); + _GetDpiForMonitor = (PFNGETDPIFORMONITOR)GetProcAddress(LoadLibrary(_T("Shcore.dll")), "GetDpiForMonitor"); + } + + UINT cur_dpi = 0; + + if (cur_dpi == 0 && _GetDpiForWindow != nullptr && _wnd.main_wnd != nullptr) { + /* Per window DPI is supported since Windows 10 Ver 1607. */ + cur_dpi = _GetDpiForWindow(_wnd.main_wnd); + } + if (cur_dpi == 0 && _GetDpiForMonitor != nullptr && _wnd.main_wnd != nullptr) { + /* Per monitor is supported since Windows 8.1. */ + UINT dpiX, dpiY; + if (SUCCEEDED(_GetDpiForMonitor(MonitorFromWindow(_wnd.main_wnd, MONITOR_DEFAULTTOPRIMARY), 0 /* MDT_EFFECTIVE_DPI */, &dpiX, &dpiY))) { + cur_dpi = dpiX; // X and Y are always identical. + } + } + if (cur_dpi == 0 && _GetDpiForSystem != nullptr) { + /* Fall back to system DPI. */ + cur_dpi = _GetDpiForSystem(); + } + + return cur_dpi > 0 ? cur_dpi / 96.0f : 1.0f; // Default Windows DPI value is 96. +} diff --git a/src/video/win32_v.h b/src/video/win32_v.h index ef02e132e6..1849a9ebc9 100644 --- a/src/video/win32_v.h +++ b/src/video/win32_v.h @@ -43,6 +43,8 @@ public: protected: Dimension GetScreenSize() const override; + + float GetDPIScale() override; }; /** The factory for Windows' video driver. */ diff --git a/src/widgets/genworld_widget.h b/src/widgets/genworld_widget.h index f7bf20e950..11226b5fa1 100644 --- a/src/widgets/genworld_widget.h +++ b/src/widgets/genworld_widget.h @@ -21,6 +21,7 @@ enum GenerateLandscapeWidgets { WID_GL_MAPSIZE_Y_PULLDOWN, ///< Dropdown 'map Y size'. WID_GL_TOWN_PULLDOWN, ///< Dropdown 'No. of towns'. + WID_GL_TOWNNAME_DROPDOWN, ///< Dropdown 'Townnames'. WID_GL_INDUSTRY_PULLDOWN, ///< Dropdown 'No. of industries'. WID_GL_GENERATE_BUTTON, ///< 'Generate' button. @@ -37,7 +38,6 @@ enum GenerateLandscapeWidgets { WID_GL_SNOW_LEVEL_TEXT, ///< Snow level. WID_GL_SNOW_LEVEL_UP, ///< Increase snow level. - WID_GL_TREE_PULLDOWN, ///< Dropdown 'Tree algorithm'. WID_GL_LANDSCAPE_PULLDOWN, ///< Dropdown 'Land generator'. WID_GL_HEIGHTMAP_NAME_TEXT, ///< Heightmap name. diff --git a/src/widgets/goal_widget.h b/src/widgets/goal_widget.h index e5da620510..ad530210ba 100644 --- a/src/widgets/goal_widget.h +++ b/src/widgets/goal_widget.h @@ -20,12 +20,13 @@ enum GoalListWidgets { /** Widgets of the #GoalQuestionWindow class. */ enum GoalQuestionWidgets { - WID_GQ_CAPTION, ///< Caption of the window. - WID_GQ_QUESTION, ///< Question text. - WID_GQ_BUTTONS, ///< Buttons selection (between 1, 2 or 3). - WID_GQ_BUTTON_1, ///< First button. - WID_GQ_BUTTON_2, ///< Second button. - WID_GQ_BUTTON_3, ///< Third button. + WID_GQ_CAPTION, ///< Caption of the window. + WID_GQ_QUESTION, ///< Question text. + WID_GQ_BUTTONS, ///< Buttons selection (between 1, 2 or 3). + WID_GQ_BUTTON_1, ///< First button. + WID_GQ_BUTTON_2, ///< Second button. + WID_GQ_BUTTON_3, ///< Third button. + WID_GQ_BUTTON_SPACER, ///< Selection to hide extra padding if there are no buttons }; #endif /* WIDGETS_GOAL_WIDGET_H */ diff --git a/src/widgets/industry_widget.h b/src/widgets/industry_widget.h index e9fb2b1148..5e3a656c1f 100644 --- a/src/widgets/industry_widget.h +++ b/src/widgets/industry_widget.h @@ -12,11 +12,14 @@ /** Widgets of the #BuildIndustryWindow class. */ enum DynamicPlaceIndustriesWidgets { - WID_DPI_MATRIX_WIDGET, ///< Matrix of the industries. - WID_DPI_SCROLLBAR, ///< Scrollbar of the matrix. - WID_DPI_INFOPANEL, ///< Info panel about the industry. - WID_DPI_DISPLAY_WIDGET, ///< Display chain button. - WID_DPI_FUND_WIDGET, ///< Fund button. + WID_DPI_SCENARIO_EDITOR_PANE, ///< Pane containing SE-only widgets. + WID_DPI_REMOVE_ALL_INDUSTRIES_WIDGET, ///< Remove all industries button. + WID_DPI_CREATE_RANDOM_INDUSTRIES_WIDGET, ///< Create random industries button. + WID_DPI_MATRIX_WIDGET, ///< Matrix of the industries. + WID_DPI_SCROLLBAR, ///< Scrollbar of the matrix. + WID_DPI_INFOPANEL, ///< Info panel about the industry. + WID_DPI_DISPLAY_WIDGET, ///< Display chain button. + WID_DPI_FUND_WIDGET, ///< Fund button. }; /** Widgets of the #IndustryViewWindow class. */ diff --git a/src/widgets/object_widget.h b/src/widgets/object_widget.h index 9ca06e2fea..9afb783c56 100644 --- a/src/widgets/object_widget.h +++ b/src/widgets/object_widget.h @@ -12,6 +12,7 @@ /** Widgets of the #BuildObjectWindow class. */ enum BuildObjectWidgets { + WID_BO_FILTER, ///< The filter text box for the object list. WID_BO_CLASS_LIST, ///< The list with classes. WID_BO_SCROLLBAR, ///< The scrollbar associated with the list. WID_BO_OBJECT_MATRIX, ///< The matrix with preview sprites. diff --git a/src/widgets/settings_widget.h b/src/widgets/settings_widget.h index 5ad981f4dc..2eb6180f5c 100644 --- a/src/widgets/settings_widget.h +++ b/src/widgets/settings_widget.h @@ -15,8 +15,6 @@ enum GameOptionsWidgets { WID_GO_BACKGROUND, ///< Background of the window. WID_GO_CURRENCY_DROPDOWN, ///< Currency dropdown. WID_GO_DISTANCE_DROPDOWN, ///< Measuring unit dropdown. - WID_GO_ROADSIDE_DROPDOWN, ///< Dropdown to select the road side (to set the right side ;)). - WID_GO_TOWNNAME_DROPDOWN, ///< Town name dropdown. WID_GO_AUTOSAVE_DROPDOWN, ///< Dropdown to say how often to autosave. WID_GO_LANG_DROPDOWN, ///< Language dropdown. WID_GO_RESOLUTION_DROPDOWN, ///< Dropdown for the resolution. diff --git a/src/window.cpp b/src/window.cpp index 26a394a802..39922d4f8f 100644 --- a/src/window.cpp +++ b/src/window.cpp @@ -2182,8 +2182,8 @@ void ResizeWindow(Window *w, int delta_x, int delta_y, bool clamp_to_screen) * the resolution clamp it in such a manner that it stays within the bounds. */ int new_right = w->left + w->width + delta_x; int new_bottom = w->top + w->height + delta_y; - if (new_right >= (int)_cur_resolution.width) delta_x -= Ceil(new_right - _cur_resolution.width, std::max(1U, w->nested_root->resize_x)); - if (new_bottom >= (int)_cur_resolution.height) delta_y -= Ceil(new_bottom - _cur_resolution.height, std::max(1U, w->nested_root->resize_y)); + if (new_right >= (int)_screen.width) delta_x -= Ceil(new_right - _screen.width, std::max(1U, w->nested_root->resize_x)); + if (new_bottom >= (int)_screen.height) delta_y -= Ceil(new_bottom - _screen.height, std::max(1U, w->nested_root->resize_y)); } w->SetDirtyAsBlocks(); @@ -3541,7 +3541,7 @@ void ReInitAllWindows() NetworkReInitChatBoxSize(); /* Make sure essential parts of all windows are visible */ - RelocateAllWindows(_cur_resolution.width, _cur_resolution.height); + RelocateAllWindows(_screen.width, _screen.height); MarkWholeScreenDirty(); } diff --git a/src/zoom_type.h b/src/zoom_type.h index b498d651aa..2e7959a7bd 100644 --- a/src/zoom_type.h +++ b/src/zoom_type.h @@ -15,6 +15,8 @@ static uint const ZOOM_LVL_SHIFT = 2; static int const ZOOM_LVL_BASE = 1 << ZOOM_LVL_SHIFT; +static const int8 ZOOM_LVL_CFG_AUTO = -1; + /** All zoom levels we know. */ enum ZoomLevel : byte { /* Our possible zoom-levels */ @@ -53,6 +55,9 @@ enum ZoomLevel : byte { }; DECLARE_POSTFIX_INCREMENT(ZoomLevel) +extern int8 _gui_zoom_cfg; +extern int8 _font_zoom_cfg; + extern ZoomLevel _gui_zoom; extern ZoomLevel _font_zoom; #define ZOOM_LVL_GUI (_gui_zoom)